fix(audio): reject stale replies during voice startup

This commit is contained in:
chenjintao
2026-10-13 10:04:31 +08:00
parent 00e9509dd0
commit 109a95497a
2 changed files with 170 additions and 20 deletions
+36 -4
View File
@@ -24,10 +24,11 @@ harness = r'''
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
typedef void *ql_mutex_t;
static int g_mic_working, recording, shine_init, fota_start;
static int tts_playing, tts_play_status, audio_packet_type, no_speech_timeout;
static int queued, disconnected, flags_reset, mutex_locked;
static int queued, disconnected, flags_reset, mutex_locked, player_mq_couter, vad_starts;
static uint64_t now;
static unsigned int gSequenceNumber;
static int end_result;
@@ -42,6 +43,7 @@ static void vad_set_speech_timeout_flag(int value) { no_speech_timeout = value;
static void vad_reset_conversation_flags(void) { no_speech_timeout = 0; flags_reset++; }
static int build_random_tts_error_url(char *p, size_t n) { (void)p; (void)n; return 1; }
static void add_file_to_play_player_queue(char *p, int type) { (void)p; assert(type == 3); queued++; }
static void vad_start_func(void) { vad_starts++; recording = 1; g_mic_working = 0; }
static void app_play_tips_file(int n) { (void)n; assert(0); }
static int conversation_try_begin(void);
static void talkingq_ws_disconnect(void) {
@@ -52,7 +54,7 @@ static void talkingq_ws_disconnect(void) {
static int sendRecordingAudioPacket(AudioPacket *p) { assert(p->packetType == 2); return end_result; }
'''
harness += source[state_start:state_end]
for name in ['conversation_try_begin', 'sendEndPacket', 'conversation_check_timeout']:
for name in ['conversation_try_begin', 'sendEndPacket', 'conversation_check_timeout', 'conversation_accept_voice_message', 'conversation_finish_startup_prompt', 'conversation_find_session_id']:
harness += function(name)
harness += r'''
static void reset(void) {
@@ -60,9 +62,16 @@ static void reset(void) {
conversation_round = wait_round = sender_round = 0;
conversation_wait = WAIT_NONE; timeout_action_busy = round_response_received = 0;
queued = disconnected = flags_reset = no_speech_timeout = mutex_locked = 0;
now = 1000; end_result = 0;
now = 1000; end_result = 0; player_mq_couter = vad_starts = 0; active_session_valid = 0; startup_start_ms = 0; tts_playing = 0;
}
int main(void) {
char id[33];
const char *tagged = "PROMPT_SOUND_URL:http://host/26101309423304_EFGHIJKLMNOPQRSTUV.mp3";
const char *plain = "PROMPT_SOUND_URL:http://host/tts_error.mp3";
assert(conversation_find_session_id(tagged, (unsigned int)strlen(tagged), id));
assert(strcmp(id, "26101309423304EFGHIJKLMNOPQRSTUV") == 0);
assert(!conversation_find_session_id(plain, (unsigned int)strlen(plain), id));
assert(!conversation_find_session_id(tagged, 20, id));
/* No confirmed speech: prompt remains, without disconnect or reply wait. */
reset(); assert(conversation_try_begin() == 0); g_mic_working = 0;
conversation_no_speech(); conversation_check_timeout();
@@ -94,7 +103,30 @@ int main(void) {
reset(); conversation_no_speech(); conversation_cancel_wait(); conversation_check_timeout(); assert(!queued);
reset(); conversation_wait = WAIT_REPLY; wait_round = 9; now += 20000;
conversation_check_timeout(); assert(!queued && !disconnected);
puts("PASS: no-speech prompt, new-round cancellation, ignored recording keys, END deadline, stale rounds, early reply, send failure, timeout/key race, auto-listening");
/* Reproduce ai_active followed by a late untagged backend error. */
reset(); active_session_valid = 1; active_session_round = 0;
assert(conversation_try_begin() == 0);
assert(!conversation_accept_voice_message("PROMPT_SOUND_URL:http://host/tts_error.mp3"));
assert(!conversation_accept_voice_message("TTS_START"));
assert(conversation_accept_voice_message("NFC_SOUND_URL:http://host/card.mp3"));
/* A following queue entry cannot consume the startup action. */
player_mq_couter = 1;
conversation_finish_startup_prompt(conversation_round, 0);
assert(vad_starts == 1 && recording && !g_mic_working);
assert(!conversation_accept_voice_message("PROMPT_SOUND_URL:http://host/tts_error.mp3"));
active_session_valid = 1; active_session_round = conversation_round;
assert(conversation_accept_voice_message("PROMPT_SOUND_URL:http://host/tts_error.mp3"));
/* Stale prompt completion must not start another round. */
reset(); assert(conversation_try_begin() == 0);
conversation_finish_startup_prompt(conversation_round - 1, 0); assert(!vad_starts);
conversation_finish_startup_prompt(conversation_round, -1); assert(vad_starts == 1);
/* Lost startup releases reservation; active work is never recovered. */
reset(); assert(conversation_try_begin() == 0); now += 30001;
conversation_check_timeout(); assert(!g_mic_working);
assert(conversation_try_begin() == 0);
now += 30001; player_mq_couter = 1; conversation_check_timeout(); assert(g_mic_working);
player_mq_couter = 0; recording = 1; conversation_check_timeout(); assert(g_mic_working);
puts("PASS: late backend error admission, prompt completion with queued audio, stale prompt, failed playback, stalled-startup recovery; no-speech prompt, new-round cancellation, ignored recording keys, END deadline, stale rounds, early reply, send failure, timeout/key race, auto-listening");
}
'''
with tempfile.TemporaryDirectory(prefix='banban-timeout-') as directory: