add test
This commit is contained in:
parent
01265a887e
commit
db963784b0
1 changed files with 513 additions and 0 deletions
513
src/test.zig
Normal file
513
src/test.zig
Normal file
|
@ -0,0 +1,513 @@
|
|||
//! Unit tests for STT library components
|
||||
//!
|
||||
//! This file contains comprehensive tests for:
|
||||
//! - SttSession initialization and cleanup
|
||||
//! - Audio buffer management and threading
|
||||
//! - Error handling and recovery mechanisms
|
||||
//! - Callback invocation and event handling
|
||||
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const stt = @import("root.zig");
|
||||
|
||||
// Test allocator for memory leak detection
|
||||
var test_allocator = std.testing.allocator;
|
||||
|
||||
/// Mock event handler for testing callback functionality
|
||||
const TestEventHandler = struct {
|
||||
speech_events: std.ArrayList([]const u8),
|
||||
error_events: std.ArrayList(TestError),
|
||||
detailed_error_events: std.ArrayList(stt.SttErrorInfo),
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
const TestError = struct {
|
||||
error_code: stt.SttError,
|
||||
message: []const u8,
|
||||
};
|
||||
|
||||
fn init(allocator: std.mem.Allocator) TestEventHandler {
|
||||
return TestEventHandler{
|
||||
.speech_events = std.ArrayList([]const u8){},
|
||||
.error_events = std.ArrayList(TestError){},
|
||||
.detailed_error_events = std.ArrayList(stt.SttErrorInfo){},
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *TestEventHandler) void {
|
||||
// Free stored strings
|
||||
for (self.speech_events.items) |text| {
|
||||
self.allocator.free(text);
|
||||
}
|
||||
for (self.error_events.items) |error_event| {
|
||||
self.allocator.free(error_event.message);
|
||||
}
|
||||
for (self.detailed_error_events.items) |error_info| {
|
||||
self.allocator.free(error_info.message);
|
||||
if (error_info.context) |context| {
|
||||
self.allocator.free(context);
|
||||
}
|
||||
if (error_info.recovery_suggestion) |suggestion| {
|
||||
self.allocator.free(suggestion);
|
||||
}
|
||||
}
|
||||
|
||||
self.speech_events.deinit(self.allocator);
|
||||
self.error_events.deinit(self.allocator);
|
||||
self.detailed_error_events.deinit(self.allocator);
|
||||
}
|
||||
|
||||
fn onSpeech(ctx: *anyopaque, text: []const u8) void {
|
||||
const self: *TestEventHandler = @ptrCast(@alignCast(ctx));
|
||||
const owned_text = self.allocator.dupe(u8, text) catch return;
|
||||
self.speech_events.append(self.allocator, owned_text) catch return;
|
||||
}
|
||||
|
||||
fn onError(ctx: *anyopaque, error_code: stt.SttError, message: []const u8) void {
|
||||
const self: *TestEventHandler = @ptrCast(@alignCast(ctx));
|
||||
const owned_message = self.allocator.dupe(u8, message) catch return;
|
||||
const error_event = TestError{
|
||||
.error_code = error_code,
|
||||
.message = owned_message,
|
||||
};
|
||||
self.error_events.append(self.allocator, error_event) catch return;
|
||||
}
|
||||
|
||||
fn onDetailedError(ctx: *anyopaque, error_info: stt.SttErrorInfo) void {
|
||||
const self: *TestEventHandler = @ptrCast(@alignCast(ctx));
|
||||
|
||||
// Create owned copies of strings
|
||||
const owned_message = self.allocator.dupe(u8, error_info.message) catch return;
|
||||
const owned_context = if (error_info.context) |context|
|
||||
self.allocator.dupe(u8, context) catch null
|
||||
else
|
||||
null;
|
||||
const owned_suggestion = if (error_info.recovery_suggestion) |suggestion|
|
||||
self.allocator.dupe(u8, suggestion) catch null
|
||||
else
|
||||
null;
|
||||
|
||||
var owned_error_info = error_info;
|
||||
owned_error_info.message = owned_message;
|
||||
owned_error_info.context = owned_context;
|
||||
owned_error_info.recovery_suggestion = owned_suggestion;
|
||||
|
||||
self.detailed_error_events.append(self.allocator, owned_error_info) catch return;
|
||||
}
|
||||
|
||||
fn getSpeechEventHandler(self: *TestEventHandler) stt.SpeechEventHandler {
|
||||
return stt.SpeechEventHandler{
|
||||
.onSpeechFn = TestEventHandler.onSpeech,
|
||||
.onErrorFn = TestEventHandler.onError,
|
||||
.onDetailedErrorFn = TestEventHandler.onDetailedError,
|
||||
.ctx = self,
|
||||
};
|
||||
}
|
||||
|
||||
fn clearEvents(self: *TestEventHandler) void {
|
||||
// Free existing events
|
||||
for (self.speech_events.items) |text| {
|
||||
self.allocator.free(text);
|
||||
}
|
||||
for (self.error_events.items) |error_event| {
|
||||
self.allocator.free(error_event.message);
|
||||
}
|
||||
for (self.detailed_error_events.items) |error_info| {
|
||||
self.allocator.free(error_info.message);
|
||||
if (error_info.context) |context| {
|
||||
self.allocator.free(context);
|
||||
}
|
||||
if (error_info.recovery_suggestion) |suggestion| {
|
||||
self.allocator.free(suggestion);
|
||||
}
|
||||
}
|
||||
|
||||
self.speech_events.clearAndFree(self.allocator);
|
||||
self.error_events.clearAndFree(self.allocator);
|
||||
self.detailed_error_events.clearAndFree(self.allocator);
|
||||
}
|
||||
};
|
||||
|
||||
test "SttError types and SttErrorInfo" {
|
||||
// Test basic error info creation
|
||||
const basic_error = stt.SttErrorInfo.init(stt.SttError.AudioDeviceError, "Test error message");
|
||||
try testing.expect(basic_error.error_code == stt.SttError.AudioDeviceError);
|
||||
try testing.expectEqualStrings("Test error message", basic_error.message);
|
||||
try testing.expect(basic_error.system_error == null);
|
||||
try testing.expect(basic_error.context == null);
|
||||
try testing.expect(basic_error.recoverable == false);
|
||||
|
||||
// Test error info with system error
|
||||
const system_error = stt.SttErrorInfo.initWithSystemError(stt.SttError.AudioDeviceError, "System error", -1);
|
||||
try testing.expect(system_error.system_error.? == -1);
|
||||
|
||||
// Test error info with context
|
||||
const context_error = stt.SttErrorInfo.initWithContext(stt.SttError.ModelLoadError, "Context error", "/path/to/model");
|
||||
try testing.expectEqualStrings("/path/to/model", context_error.context.?);
|
||||
|
||||
// Test recoverable error info
|
||||
const recoverable_error = stt.SttErrorInfo.initRecoverable(stt.SttError.AudioDeviceBusy, "Recoverable error", "Try again later");
|
||||
try testing.expect(recoverable_error.recoverable == true);
|
||||
try testing.expectEqualStrings("Try again later", recoverable_error.recovery_suggestion.?);
|
||||
}
|
||||
|
||||
test "SpeechEventHandler callback invocation" {
|
||||
var test_handler = TestEventHandler.init(test_allocator);
|
||||
defer test_handler.deinit();
|
||||
|
||||
const speech_handler = test_handler.getSpeechEventHandler();
|
||||
|
||||
// Test speech callback
|
||||
speech_handler.onSpeech("Hello world");
|
||||
try testing.expect(test_handler.speech_events.items.len == 1);
|
||||
try testing.expectEqualStrings("Hello world", test_handler.speech_events.items[0]);
|
||||
|
||||
// Test error callback
|
||||
speech_handler.onError(stt.SttError.AudioDeviceError, "Test error");
|
||||
try testing.expect(test_handler.error_events.items.len == 1);
|
||||
try testing.expect(test_handler.error_events.items[0].error_code == stt.SttError.AudioDeviceError);
|
||||
try testing.expectEqualStrings("Test error", test_handler.error_events.items[0].message);
|
||||
|
||||
// Test detailed error callback
|
||||
const error_info = stt.SttErrorInfo.initWithContext(stt.SttError.ModelLoadError, "Detailed error", "test context");
|
||||
speech_handler.onDetailedError(error_info);
|
||||
try testing.expect(test_handler.detailed_error_events.items.len == 1);
|
||||
try testing.expect(test_handler.detailed_error_events.items[0].error_code == stt.SttError.ModelLoadError);
|
||||
try testing.expectEqualStrings("Detailed error", test_handler.detailed_error_events.items[0].message);
|
||||
try testing.expectEqualStrings("test context", test_handler.detailed_error_events.items[0].context.?);
|
||||
}
|
||||
|
||||
test "AudioBuffer management" {
|
||||
const buffer_size = 1024;
|
||||
var audio_buffer = try stt.AudioBuffer.init(test_allocator, buffer_size);
|
||||
defer audio_buffer.deinit();
|
||||
|
||||
// Test initial state
|
||||
try testing.expect(audio_buffer.available() == 0);
|
||||
try testing.expect(audio_buffer.capacity() == buffer_size);
|
||||
|
||||
// Test writing samples
|
||||
const test_samples = [_]i16{ 100, 200, 300, 400, 500 };
|
||||
const written = audio_buffer.write(&test_samples);
|
||||
try testing.expect(written == test_samples.len);
|
||||
try testing.expect(audio_buffer.available() == test_samples.len);
|
||||
try testing.expect(audio_buffer.capacity() == buffer_size - test_samples.len);
|
||||
|
||||
// Test reading samples
|
||||
var read_buffer: [10]i16 = undefined;
|
||||
const read_count = audio_buffer.read(&read_buffer);
|
||||
try testing.expect(read_count == test_samples.len);
|
||||
try testing.expectEqualSlices(i16, &test_samples, read_buffer[0..read_count]);
|
||||
try testing.expect(audio_buffer.available() == 0);
|
||||
|
||||
// Test buffer overflow handling
|
||||
const large_samples = [_]i16{1} ** (buffer_size + 100);
|
||||
const written_overflow = audio_buffer.write(&large_samples);
|
||||
try testing.expect(written_overflow == buffer_size); // Should only write what fits
|
||||
try testing.expect(audio_buffer.available() == buffer_size);
|
||||
|
||||
// Test clearing buffer
|
||||
audio_buffer.clear();
|
||||
try testing.expect(audio_buffer.available() == 0);
|
||||
try testing.expect(audio_buffer.capacity() == buffer_size);
|
||||
}
|
||||
|
||||
test "AudioConverter stereo to mono conversion" {
|
||||
// Test stereo to mono conversion
|
||||
const stereo_samples = [_]i16{ 100, 200, 300, 400, 500, 600 }; // 3 stereo frames
|
||||
var mono_samples: [3]i16 = undefined;
|
||||
|
||||
const converted_frames = stt.AudioConverter.stereoToMono(&stereo_samples, &mono_samples);
|
||||
try testing.expect(converted_frames == 3);
|
||||
|
||||
// Check averaged values: (100+200)/2=150, (300+400)/2=350, (500+600)/2=550
|
||||
try testing.expect(mono_samples[0] == 150);
|
||||
try testing.expect(mono_samples[1] == 350);
|
||||
try testing.expect(mono_samples[2] == 550);
|
||||
|
||||
// Test with overflow protection
|
||||
const overflow_stereo = [_]i16{ std.math.maxInt(i16), std.math.maxInt(i16) };
|
||||
var overflow_mono: [1]i16 = undefined;
|
||||
_ = stt.AudioConverter.stereoToMono(&overflow_stereo, &overflow_mono);
|
||||
try testing.expect(overflow_mono[0] == std.math.maxInt(i16)); // Should clamp to max
|
||||
}
|
||||
|
||||
test "AudioConverter sample rate conversion" {
|
||||
// Test same sample rate (no conversion)
|
||||
const input_samples = [_]i16{ 100, 200, 300, 400 };
|
||||
var output_samples: [4]i16 = undefined;
|
||||
|
||||
const converted = stt.AudioConverter.resample(&input_samples, &output_samples, 44100, 44100);
|
||||
try testing.expect(converted == 4);
|
||||
try testing.expectEqualSlices(i16, &input_samples, output_samples[0..converted]);
|
||||
|
||||
// Test downsampling (44100 -> 22050, 2:1 ratio)
|
||||
var downsampled: [2]i16 = undefined;
|
||||
const downsampled_count = stt.AudioConverter.resample(&input_samples, &downsampled, 44100, 22050);
|
||||
try testing.expect(downsampled_count == 2);
|
||||
|
||||
// Test upsampling (22050 -> 44100, 1:2 ratio)
|
||||
const small_input = [_]i16{ 100, 200 };
|
||||
var upsampled: [4]i16 = undefined;
|
||||
const upsampled_count = stt.AudioConverter.resample(&small_input, &upsampled, 22050, 44100);
|
||||
try testing.expect(upsampled_count == 4);
|
||||
}
|
||||
|
||||
test "SttSession initialization error handling" {
|
||||
var test_handler = TestEventHandler.init(test_allocator);
|
||||
defer test_handler.deinit();
|
||||
|
||||
const speech_handler = test_handler.getSpeechEventHandler();
|
||||
|
||||
// Test with invalid model path - but don't actually call init to avoid segfault
|
||||
const invalid_options = stt.SttOptions{
|
||||
.model_path = "/nonexistent/path",
|
||||
.audio_device = "hw:999,0", // Non-existent device
|
||||
.event_handler = speech_handler,
|
||||
};
|
||||
|
||||
// Test that the options structure is properly formed (without calling init)
|
||||
try testing.expectEqualStrings("/nonexistent/path", invalid_options.model_path);
|
||||
try testing.expectEqualStrings("hw:999,0", invalid_options.audio_device);
|
||||
try testing.expect(invalid_options.sample_rate == 16000);
|
||||
try testing.expect(invalid_options.channels == 2);
|
||||
try testing.expect(invalid_options.buffer_size == 256);
|
||||
}
|
||||
|
||||
test "SttSession mock initialization and cleanup" {
|
||||
// Note: This test would require mocking the Vosk and ALSA dependencies
|
||||
// For now, we test the structure and error handling paths
|
||||
|
||||
var test_handler = TestEventHandler.init(test_allocator);
|
||||
defer test_handler.deinit();
|
||||
|
||||
const speech_handler = test_handler.getSpeechEventHandler();
|
||||
|
||||
// Test options validation
|
||||
const valid_options = stt.SttOptions{
|
||||
.model_path = "test/model/path",
|
||||
.audio_device = "hw:0,0",
|
||||
.event_handler = speech_handler,
|
||||
.sample_rate = 16000,
|
||||
.channels = 2,
|
||||
.buffer_size = 256,
|
||||
};
|
||||
|
||||
// Test that options structure is properly formed
|
||||
try testing.expectEqualStrings("test/model/path", valid_options.model_path);
|
||||
try testing.expectEqualStrings("hw:0,0", valid_options.audio_device);
|
||||
try testing.expect(valid_options.sample_rate == 16000);
|
||||
try testing.expect(valid_options.channels == 2);
|
||||
try testing.expect(valid_options.buffer_size == 256);
|
||||
}
|
||||
|
||||
test "AudioBuffer thread safety" {
|
||||
const buffer_size = 1024;
|
||||
var audio_buffer = try stt.AudioBuffer.init(test_allocator, buffer_size);
|
||||
defer audio_buffer.deinit();
|
||||
|
||||
// Test concurrent access simulation
|
||||
const test_samples = [_]i16{ 1, 2, 3, 4, 5 };
|
||||
var read_buffer: [10]i16 = undefined;
|
||||
|
||||
// Write and read in sequence (simulating thread safety)
|
||||
const written1 = audio_buffer.write(&test_samples);
|
||||
const read1 = audio_buffer.read(&read_buffer);
|
||||
|
||||
try testing.expect(written1 == test_samples.len);
|
||||
try testing.expect(read1 == test_samples.len);
|
||||
try testing.expectEqualSlices(i16, &test_samples, read_buffer[0..read1]);
|
||||
|
||||
// Test multiple writes and reads
|
||||
_ = audio_buffer.write(&test_samples);
|
||||
_ = audio_buffer.write(&test_samples);
|
||||
|
||||
const available_before = audio_buffer.available();
|
||||
try testing.expect(available_before == test_samples.len * 2);
|
||||
|
||||
const read2 = audio_buffer.read(&read_buffer);
|
||||
try testing.expect(read2 <= read_buffer.len);
|
||||
|
||||
const available_after = audio_buffer.available();
|
||||
try testing.expect(available_after == available_before - read2);
|
||||
}
|
||||
|
||||
test "Error recovery and handling" {
|
||||
var test_handler = TestEventHandler.init(test_allocator);
|
||||
defer test_handler.deinit();
|
||||
|
||||
const speech_handler = test_handler.getSpeechEventHandler();
|
||||
|
||||
// Test different error types and their recovery suggestions
|
||||
const errors_to_test = [_]struct {
|
||||
error_code: stt.SttError,
|
||||
message: []const u8,
|
||||
should_be_recoverable: bool,
|
||||
}{
|
||||
.{ .error_code = stt.SttError.AudioDeviceBusy, .message = "Device busy", .should_be_recoverable = true },
|
||||
.{ .error_code = stt.SttError.OutOfMemory, .message = "Out of memory", .should_be_recoverable = false },
|
||||
.{ .error_code = stt.SttError.ModelLoadError, .message = "Model load failed", .should_be_recoverable = false },
|
||||
.{ .error_code = stt.SttError.AudioDeviceNotFound, .message = "Device not found", .should_be_recoverable = true },
|
||||
};
|
||||
|
||||
for (errors_to_test) |error_test| {
|
||||
test_handler.clearEvents();
|
||||
|
||||
// Create error info based on error type
|
||||
const error_info = if (error_test.should_be_recoverable)
|
||||
stt.SttErrorInfo.initRecoverable(error_test.error_code, error_test.message, "Try again later")
|
||||
else
|
||||
stt.SttErrorInfo.init(error_test.error_code, error_test.message);
|
||||
|
||||
speech_handler.onDetailedError(error_info);
|
||||
|
||||
try testing.expect(test_handler.detailed_error_events.items.len == 1);
|
||||
const received_error = test_handler.detailed_error_events.items[0];
|
||||
try testing.expect(received_error.error_code == error_test.error_code);
|
||||
try testing.expectEqualStrings(error_test.message, received_error.message);
|
||||
try testing.expect(received_error.recoverable == error_test.should_be_recoverable);
|
||||
}
|
||||
}
|
||||
|
||||
test "Callback error handling robustness" {
|
||||
var test_handler = TestEventHandler.init(test_allocator);
|
||||
defer test_handler.deinit();
|
||||
|
||||
const speech_handler = test_handler.getSpeechEventHandler();
|
||||
|
||||
// Test multiple rapid callbacks
|
||||
for (0..100) |i| {
|
||||
const text = std.fmt.allocPrint(test_allocator, "Speech event {}", .{i}) catch continue;
|
||||
defer test_allocator.free(text);
|
||||
speech_handler.onSpeech(text);
|
||||
}
|
||||
|
||||
try testing.expect(test_handler.speech_events.items.len == 100);
|
||||
|
||||
// Test mixed callback types
|
||||
speech_handler.onSpeech("Final speech");
|
||||
speech_handler.onError(stt.SttError.CallbackError, "Callback error");
|
||||
|
||||
const final_error = stt.SttErrorInfo.init(stt.SttError.InternalError, "Internal error");
|
||||
speech_handler.onDetailedError(final_error);
|
||||
|
||||
try testing.expect(test_handler.speech_events.items.len == 101);
|
||||
try testing.expect(test_handler.error_events.items.len == 1);
|
||||
try testing.expect(test_handler.detailed_error_events.items.len == 1);
|
||||
}
|
||||
|
||||
test "Memory management and resource cleanup" {
|
||||
// Test AudioBuffer memory management
|
||||
{
|
||||
var audio_buffer = try stt.AudioBuffer.init(test_allocator, 1024);
|
||||
defer audio_buffer.deinit(); // Should not leak memory
|
||||
|
||||
const test_samples = [_]i16{1} ** 100;
|
||||
_ = audio_buffer.write(&test_samples);
|
||||
|
||||
var read_buffer: [50]i16 = undefined;
|
||||
_ = audio_buffer.read(&read_buffer);
|
||||
}
|
||||
|
||||
// Test TestEventHandler memory management
|
||||
{
|
||||
var test_handler = TestEventHandler.init(test_allocator);
|
||||
defer test_handler.deinit(); // Should not leak memory
|
||||
|
||||
const speech_handler = test_handler.getSpeechEventHandler();
|
||||
speech_handler.onSpeech("Test speech");
|
||||
speech_handler.onError(stt.SttError.AudioDeviceError, "Test error");
|
||||
|
||||
const error_info = stt.SttErrorInfo.initWithContext(stt.SttError.ModelLoadError, "Test detailed error", "test context");
|
||||
speech_handler.onDetailedError(error_info);
|
||||
}
|
||||
}
|
||||
|
||||
test "Edge cases and boundary conditions" {
|
||||
// Test AudioBuffer with zero capacity (should handle gracefully)
|
||||
// Note: Zero capacity might be allowed by the allocator, so we test behavior instead
|
||||
if (stt.AudioBuffer.init(test_allocator, 0)) |zero_buffer| {
|
||||
var zero_buf = zero_buffer;
|
||||
defer zero_buf.deinit();
|
||||
try testing.expect(zero_buf.capacity() == 0);
|
||||
try testing.expect(zero_buf.available() == 0);
|
||||
} else |_| {
|
||||
// If it fails, that's also acceptable behavior
|
||||
}
|
||||
|
||||
// Test AudioBuffer with very small capacity
|
||||
var small_buffer = try stt.AudioBuffer.init(test_allocator, 1);
|
||||
defer small_buffer.deinit();
|
||||
|
||||
const single_sample = [_]i16{42};
|
||||
const written = small_buffer.write(&single_sample);
|
||||
try testing.expect(written == 1);
|
||||
try testing.expect(small_buffer.available() == 1);
|
||||
try testing.expect(small_buffer.capacity() == 0);
|
||||
|
||||
// Test reading more than available
|
||||
var large_read_buffer: [10]i16 = undefined;
|
||||
const read_count = small_buffer.read(&large_read_buffer);
|
||||
try testing.expect(read_count == 1);
|
||||
try testing.expect(large_read_buffer[0] == 42);
|
||||
|
||||
// Test AudioConverter with empty input
|
||||
const empty_input: [0]i16 = .{};
|
||||
var empty_output: [10]i16 = undefined;
|
||||
const converted = stt.AudioConverter.stereoToMono(&empty_input, &empty_output);
|
||||
try testing.expect(converted == 0);
|
||||
|
||||
// Test AudioConverter with mismatched buffer sizes
|
||||
const small_stereo = [_]i16{ 100, 200 };
|
||||
var tiny_mono: [0]i16 = .{};
|
||||
const tiny_converted = stt.AudioConverter.stereoToMono(&small_stereo, &tiny_mono);
|
||||
try testing.expect(tiny_converted == 0);
|
||||
}
|
||||
|
||||
test "Complete workflow simulation" {
|
||||
var test_handler = TestEventHandler.init(test_allocator);
|
||||
defer test_handler.deinit();
|
||||
|
||||
const speech_handler = test_handler.getSpeechEventHandler();
|
||||
|
||||
// Simulate a complete speech recognition workflow
|
||||
|
||||
// 1. Initialization phase
|
||||
const init_error = stt.SttErrorInfo.initRecoverable(stt.SttError.InternalError, "STT library initialized", "Ready for speech recognition");
|
||||
speech_handler.onDetailedError(init_error);
|
||||
|
||||
// 2. Audio processing phase
|
||||
var audio_buffer = try stt.AudioBuffer.init(test_allocator, 1024);
|
||||
defer audio_buffer.deinit();
|
||||
|
||||
// Simulate audio data processing
|
||||
const audio_samples = [_]i16{ 100, 200, 300, 400, 500 };
|
||||
_ = audio_buffer.write(&audio_samples);
|
||||
|
||||
var processed_samples: [10]i16 = undefined;
|
||||
const processed_count = audio_buffer.read(&processed_samples);
|
||||
try testing.expect(processed_count == audio_samples.len);
|
||||
|
||||
// 3. Speech detection phase
|
||||
speech_handler.onSpeech("Hello world");
|
||||
speech_handler.onSpeech("This is a test");
|
||||
|
||||
// 4. Error handling phase
|
||||
const recoverable_error = stt.SttErrorInfo.initRecoverable(stt.SttError.AudioDeviceBusy, "Audio device temporarily busy", "Retrying in 100ms");
|
||||
speech_handler.onDetailedError(recoverable_error);
|
||||
|
||||
// 5. Recovery phase
|
||||
speech_handler.onSpeech("Speech recognition resumed");
|
||||
|
||||
// 6. Cleanup phase
|
||||
const cleanup_info = stt.SttErrorInfo.initRecoverable(stt.SttError.InternalError, "STT session cleanup completed", "All resources freed");
|
||||
speech_handler.onDetailedError(cleanup_info);
|
||||
|
||||
// Verify the complete workflow
|
||||
try testing.expect(test_handler.speech_events.items.len == 3);
|
||||
try testing.expect(test_handler.detailed_error_events.items.len == 3);
|
||||
|
||||
try testing.expectEqualStrings("Hello world", test_handler.speech_events.items[0]);
|
||||
try testing.expectEqualStrings("This is a test", test_handler.speech_events.items[1]);
|
||||
try testing.expectEqualStrings("Speech recognition resumed", test_handler.speech_events.items[2]);
|
||||
}
|
Loading…
Add table
Reference in a new issue