Skip to content

Commit f9237ee

Browse files
committed
FIX: 修复合并后 TTS 宿主测试在并发/dispose/跨平台下的失败
- word_dictionary_sheet_test 注入桩 TtsController,避免弹窗内嵌发音按钮 与查词完成自动预热触发真实平台 TTS 合成,在并发下拖住 pumpAndSettle 挂起 - tts_voice_preview_row_test 的 _FakeTtsController 补 stop() override, 避免 TtsSettingsScreen.dispose 调 stop() 触碰未初始化的 late _coordinator - tts_settings_screen_test 平台引擎显示名改为按宿主推导(Apple 宿主 Apple Speech / 其余 System Speech),不再硬编码「Apple Speech」,修 Linux CI 找不到该文案的失败
1 parent ad30b47 commit f9237ee

3 files changed

Lines changed: 32 additions & 3 deletions

File tree

test/screens/tts_settings_screen_test.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:io';
2+
13
import 'package:echo_loop/l10n/app_localizations.dart';
24
import 'package:echo_loop/providers/tts/kokoro_model_provider.dart';
35
import 'package:echo_loop/providers/tts/tts_settings_provider.dart';
@@ -11,6 +13,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
1113
import 'package:flutter_test/flutter_test.dart';
1214
import 'package:shared_preferences/shared_preferences.dart';
1315

16+
/// 平台语音引擎的显示名随宿主而变(见 [platformSpeechEngineName]):
17+
/// Apple 宿主(macOS/iOS)显「Apple Speech」,其余(Linux CI 等)显「System Speech」。
18+
/// 测试断言须与运行宿主一致,否则在 Linux CI 上找不到「Apple Speech」而失败。
19+
final String _platformEngineLabel =
20+
(Platform.isIOS || Platform.isMacOS) ? 'Apple Speech' : 'System Speech';
21+
1422
/// 受控的 Kokoro 模型 notifier:build 返回注入初值,方法仅按变体计数(不做真实 IO)。
1523
class _TestKokoroNotifier extends KokoroModelNotifier {
1624
_TestKokoroNotifier(this._initial);
@@ -81,8 +89,8 @@ void main() {
8189
await tester.pumpWidget(_wrap(const TtsSettings()));
8290
await tester.pumpAndSettle();
8391

84-
// 测试运行于 macOS 宿主 → 平台引擎显「Apple Speech
85-
expect(find.text('Apple Speech'), findsOneWidget);
92+
// 平台引擎显示名随宿主而变(Apple → Apple Speech,其余 → System Speech
93+
expect(find.text(_platformEngineLabel), findsOneWidget);
8694
// Echo Loop 现拆为两档可选:Balanced(Piper) / Advanced(Kokoro)。
8795
expect(find.text('Echo Loop Speech (Balanced)'), findsOneWidget);
8896
expect(find.text('Echo Loop Speech (Advanced)'), findsOneWidget);
@@ -344,7 +352,7 @@ void main() {
344352
);
345353
await tester.pumpAndSettle();
346354

347-
await tester.tap(find.text('Apple Speech'));
355+
await tester.tap(find.text(_platformEngineLabel));
348356
await tester.pumpAndSettle();
349357

350358
expect(

test/screens/tts_voice_preview_row_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class _FakeTtsController extends TtsController {
4242
Future<void> prewarmAccentPreviews() async {}
4343
@override
4444
void cancelVoicePreviewPrewarm() {}
45+
@override
46+
Future<void> stop() async {}
4547
}
4648

4749
Widget _wrap(

test/widgets/word_dictionary_sheet_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
1010
import 'package:flutter_test/flutter_test.dart';
1111
import 'package:echo_loop/features/onboarding_survey/providers/onboarding_survey_provider.dart';
1212
import 'package:echo_loop/l10n/app_localizations.dart';
13+
import 'package:echo_loop/providers/tts/tts_controller_provider.dart';
1314
import 'package:echo_loop/services/dictionary_service.dart';
1415
import 'package:echo_loop/theme/app_theme.dart';
1516
import 'package:echo_loop/widgets/intensive_listen/word_dictionary_sheet.dart';
@@ -21,6 +22,23 @@ import '../helpers/mock_providers.dart';
2122
/// 词典设置读取的 SharedPreferences(在 setUp 注入),供 [_buildTestPage] override
2223
late SharedPreferences _prefs;
2324

25+
/// 桩 [TtsController]:弹窗内嵌发音按钮、查词完成会自动触发例句预热,
26+
/// 真实控制器会经平台 TTS 引擎/method channel 异步合成,在 widget 测试中
27+
/// 永不完成而拖住 pumpAndSettle(并发跑时确定性挂起)。这里把预热/发音/停止
28+
/// 全部置空,使本测试只验证弹窗 UI、不触碰真实 TTS 栈。
29+
class _StubTtsController extends TtsController {
30+
@override
31+
TtsControllerState build() => const TtsControllerState();
32+
@override
33+
Future<void> speak(String text, {String? key}) async {}
34+
@override
35+
Future<void> prewarmTexts(List<String> texts) async {}
36+
@override
37+
void cancelTextsPrewarm() {}
38+
@override
39+
Future<void> stop() async {}
40+
}
41+
2442
/// 创建测试用内存词典数据库
2543
Database _createTestDb() {
2644
final db = sqlite3.openInMemory();
@@ -50,6 +68,7 @@ Widget _buildTestPage(String word, {String? sentenceText}) {
5068
analyticsOverride(),
5169
dictionaryOverride(),
5270
sharedPreferencesProvider.overrideWithValue(_prefs),
71+
ttsControllerProvider.overrideWith(_StubTtsController.new),
5372
],
5473
child: MaterialApp(
5574
locale: const Locale('en'),

0 commit comments

Comments
 (0)