import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:yx_async_throttle_flutter/yx_async_throttle_flutter.dart'; /// 商用级全面测试用例 /// 覆盖所有核心功能、边界条件、并发场景、异常处理、内存安全等 void main() { late AsyncThrottle throttle; setUp(() { throttle = AsyncThrottle.instance; throttle.clearAllLocks(); }); tearDown(() { throttle.clearAllLocks(); }); // ============================================================ // 1. 单例模式测试 // ============================================================ group('Singleton Pattern Tests', () { test('多次获取实例应返回同一对象', () { final instance1 = AsyncThrottle.instance; final instance2 = AsyncThrottle.instance; final instance3 = AsyncThrottle.instance; expect(identical(instance1, instance2), isTrue); expect(identical(instance2, instance3), isTrue); }); test('实例不为null', () { expect(AsyncThrottle.instance, isNotNull); }); test('实例类型正确', () { expect(AsyncThrottle.instance, isA()); }); }); // ============================================================ // 2. 节流模式 (Throttle) 核心功能测试 // ============================================================ group('Throttle Mode - Core Functionality', () { test('首次调用应立即执行', () async { int counter = 0; final stopwatch = Stopwatch()..start(); await throttle.execute( 'throttle_immediate', () async { counter++; }, enableDebounce: false, duration: const Duration(milliseconds: 100), ); stopwatch.stop(); expect(counter, 1); // 应该几乎立即执行(小于50ms) expect(stopwatch.elapsedMilliseconds, lessThan(50)); }); test('duration内的后续调用应被忽略', () async { int counter = 0; const duration = Duration(milliseconds: 200); // 第一次调用 await throttle.execute( 'throttle_ignore', () async { counter++; }, enableDebounce: false, duration: duration, ); // 立即第二次调用(应被忽略) await throttle.execute( 'throttle_ignore', () async { counter++; }, enableDebounce: false, duration: duration, ); expect(counter, 1); }); test('duration过后应可以再次执行', () async { int counter = 0; const duration = Duration(milliseconds: 50); await throttle.execute( 'throttle_after_duration', () async { counter++; }, enableDebounce: false, duration: duration, ); expect(counter, 1); // 等待 duration 过去 await Future.delayed(duration + const Duration(milliseconds: 20)); await throttle.execute( 'throttle_after_duration', () async { counter++; }, enableDebounce: false, duration: duration, ); expect(counter, 2); }); test('异步任务执行中新调用应被忽略(锁机制)', () async { int counter = 0; final taskCompleter = Completer(); // 启动长时间任务 final future1 = throttle.execute( 'throttle_lock', () async { counter++; await taskCompleter.future; }, enableDebounce: false, ); // 确保任务已开始 await Future.delayed(const Duration(milliseconds: 10)); expect(throttle.isExecuting('throttle_lock'), isTrue); // 尝试再次调用(应被锁阻止) await throttle.execute( 'throttle_lock', () async { counter++; }, enableDebounce: false, ); expect(counter, 1); // 完成任务 taskCompleter.complete(); await future1; expect(throttle.isExecuting('throttle_lock'), isFalse); }); test('异常后锁应正确释放', () async { const duration = Duration(milliseconds: 50); bool exceptionThrown = false; try { await throttle.execute( 'throttle_exception_lock', () async { throw Exception('Test exception'); }, enableDebounce: false, duration: duration, ); } catch (e) { exceptionThrown = true; } expect(exceptionThrown, isTrue); expect(throttle.isExecuting('throttle_exception_lock'), isFalse); // 等待 duration await Future.delayed(duration + const Duration(milliseconds: 20)); // 应该可以再次执行 int counter = 0; await throttle.execute( 'throttle_exception_lock', () async { counter++; }, enableDebounce: false, duration: duration, ); expect(counter, 1); }); test('异常应正确传播', () async { expect( () => throttle.execute( 'throttle_exception_propagate', () async { throw StateError('Propagated error'); }, enableDebounce: false, ), throwsA(isA().having((e) => e.message, 'message', 'Propagated error')), ); }); test('不同tagId应互不影响', () async { int counter1 = 0; int counter2 = 0; final future1 = throttle.execute( 'tag_a', () async { await Future.delayed(const Duration(milliseconds: 50)); counter1++; }, enableDebounce: false, ); final future2 = throttle.execute( 'tag_b', () async { await Future.delayed(const Duration(milliseconds: 50)); counter2++; }, enableDebounce: false, ); await Future.wait([future1, future2]); expect(counter1, 1); expect(counter2, 1); }); test('节流期间多次调用全部被忽略', () async { int counter = 0; const duration = Duration(milliseconds: 100); // 第一次调用 await throttle.execute( 'throttle_multiple_ignore', () async { counter++; }, enableDebounce: false, duration: duration, ); // 快速连续多次调用 for (int i = 0; i < 10; i++) { await throttle.execute( 'throttle_multiple_ignore', () async { counter++; }, enableDebounce: false, duration: duration, ); } expect(counter, 1); }); }); // ============================================================ // 3. 防抖模式 (Debounce) 核心功能测试 // ============================================================ group('Debounce Mode - Core Functionality', () { test('单次调用应延迟执行', () async { int counter = 0; const duration = Duration(milliseconds: 50); final future = throttle.execute( 'debounce_delay', () async { counter++; }, enableDebounce: true, duration: duration, ); // 调用后立即检查 expect(counter, 0); await future; expect(counter, 1); }); test('快速连续调用只应执行最后一次', () async { int counter = 0; final executedValues = []; const duration = Duration(milliseconds: 50); final futures = >[]; for (int i = 1; i <= 5; i++) { final value = i; futures.add( throttle.execute( 'debounce_last_only', () async { counter++; executedValues.add(value); }, enableDebounce: true, duration: duration, ), ); } await Future.delayed(const Duration(milliseconds: 150)); await Future.wait(futures); expect(counter, 1); expect(executedValues, [5]); // 只执行了最后一次 }); test('被取消调用的Future应正确完成(不挂起)', () async { const duration = Duration(milliseconds: 50); final completedFutures = []; final f1 = throttle.execute( 'debounce_cancel_complete', () async {}, enableDebounce: true, duration: duration, ).then((_) => completedFutures.add(1)); final f2 = throttle.execute( 'debounce_cancel_complete', () async {}, enableDebounce: true, duration: duration, ).then((_) => completedFutures.add(2)); final f3 = throttle.execute( 'debounce_cancel_complete', () async {}, enableDebounce: true, duration: duration, ).then((_) => completedFutures.add(3)); await Future.wait([f1, f2, f3]).timeout(const Duration(seconds: 2)); // 所有 Future 都应该完成 expect(completedFutures.length, 3); }); test('任务执行中新调用应被阻止', () async { int counter = 0; final taskCompleter = Completer(); const duration = Duration(milliseconds: 10); // 启动第一个任务 final f1 = throttle.execute( 'debounce_lock_test', () async { counter++; await taskCompleter.future; }, enableDebounce: true, duration: duration, ); // 等待防抖触发并开始执行 await Future.delayed(const Duration(milliseconds: 50)); expect(throttle.isExecuting('debounce_lock_test'), isTrue); // 尝试新调用(应被阻止) final f2 = throttle.execute( 'debounce_lock_test', () async { counter++; }, enableDebounce: true, duration: duration, ); await Future.delayed(const Duration(milliseconds: 50)); // 完成原任务 taskCompleter.complete(); await f1; await f2; expect(counter, 1); }); test('防抖异常应正确传播到最后一个调用者', () async { const duration = Duration(milliseconds: 30); final f1 = throttle.execute( 'debounce_error_propagate', () async { // 不会执行 }, enableDebounce: true, duration: duration, ); final f2 = throttle.execute( 'debounce_error_propagate', () async { throw ArgumentError('Last call error'); }, enableDebounce: true, duration: duration, ); // f1 应该正常完成(被取消) await expectLater(f1, completes); // f2 应该抛出错误 await expectLater(f2, throwsA(isA())); }); test('防抖异常后锁应正确释放', () async { const duration = Duration(milliseconds: 30); try { await throttle.execute( 'debounce_error_lock', () async { throw Exception('Test'); }, enableDebounce: true, duration: duration, ); } catch (_) {} expect(throttle.isExecuting('debounce_error_lock'), isFalse); // 应该可以再次执行 int counter = 0; await throttle.execute( 'debounce_error_lock', () async { counter++; }, enableDebounce: true, duration: duration, ); await Future.delayed(const Duration(milliseconds: 80)); expect(counter, 1); }); }); // ============================================================ // 4. executeSafe 安全模式测试 // ============================================================ group('ExecuteSafe - UI Safe Mode', () { test('节流模式异常不应向外抛', () async { Object? capturedError; // 不应该抛出异常 await throttle.executeSafe( 'safe_throttle', () async { throw StateError('Should be caught'); }, enableDebounce: false, onError: (e, s) { capturedError = e; }, ); expect(capturedError, isA()); }); test('防抖模式异常不应向外抛', () async { Object? capturedError; StackTrace? capturedStack; await throttle.executeSafe( 'safe_debounce', () async { throw FormatException('Safe debounce error'); }, enableDebounce: true, duration: const Duration(milliseconds: 30), onError: (e, s) { capturedError = e; capturedStack = s; }, ); await Future.delayed(const Duration(milliseconds: 80)); expect(capturedError, isA()); expect(capturedStack, isNotNull); }); test('onError回调正确接收错误和堆栈', () async { Object? error; StackTrace? stack; await throttle.executeSafe( 'safe_error_callback', () async { throw UnsupportedError('Test error'); }, enableDebounce: false, onError: (e, s) { error = e; stack = s; }, ); expect(error, isA()); expect(stack, isNotNull); expect(stack.toString(), isNotEmpty); }); test('无异常时onError不应被调用', () async { bool errorCalled = false; await throttle.executeSafe( 'safe_no_error', () async { // 正常执行 }, enableDebounce: false, onError: (e, s) { errorCalled = true; }, ); expect(errorCalled, isFalse); }); test('onError为null时异常被静默吞掉', () async { // 不应该抛出任何异常 await throttle.executeSafe( 'safe_null_callback', () async { throw Exception('Silent error'); }, enableDebounce: false, onError: null, ); // 如果到达这里,测试通过 expect(true, isTrue); }); }); // ============================================================ // 5. isExecuting 状态检查测试 // ============================================================ group('IsExecuting - State Check', () { test('执行前应返回false', () { expect(throttle.isExecuting('not_started'), isFalse); }); test('节流执行中应返回true', () async { final completer = Completer(); final future = throttle.execute( 'executing_throttle', () async { await completer.future; }, enableDebounce: false, ); await Future.delayed(const Duration(milliseconds: 10)); expect(throttle.isExecuting('executing_throttle'), isTrue); completer.complete(); await future; expect(throttle.isExecuting('executing_throttle'), isFalse); }); test('防抖执行中应返回true', () async { final completer = Completer(); final future = throttle.execute( 'executing_debounce', () async { await completer.future; }, enableDebounce: true, duration: const Duration(milliseconds: 10), ); // 等待防抖触发 await Future.delayed(const Duration(milliseconds: 30)); expect(throttle.isExecuting('executing_debounce'), isTrue); completer.complete(); await future; expect(throttle.isExecuting('executing_debounce'), isFalse); }); test('异常后应返回false', () async { try { await throttle.execute( 'executing_error', () async { throw Exception('Test'); }, enableDebounce: false, ); } catch (_) {} expect(throttle.isExecuting('executing_error'), isFalse); }); test('不存在的tagId应返回false', () { expect(throttle.isExecuting('non_existent_tag_12345'), isFalse); }); }); // ============================================================ // 6. clearAllLocks 测试 // ============================================================ group('ClearAllLocks - Lock Cleanup', () { test('应清除所有锁', () async { final completer1 = Completer(); final completer2 = Completer(); throttle.execute('lock1', () async { await completer1.future; }, enableDebounce: false); throttle.execute('lock2', () async { await completer2.future; }, enableDebounce: false); await Future.delayed(const Duration(milliseconds: 10)); expect(throttle.isExecuting('lock1'), isTrue); expect(throttle.isExecuting('lock2'), isTrue); throttle.clearAllLocks(); expect(throttle.isExecuting('lock1'), isFalse); expect(throttle.isExecuting('lock2'), isFalse); completer1.complete(); completer2.complete(); }); test('应释放等待中的防抖Completer', () async { final future = throttle.execute( 'pending_debounce', () async {}, enableDebounce: true, duration: const Duration(milliseconds: 100), ); // 立即清除 throttle.clearAllLocks(); // Future 应该正常完成而不是挂起 await future.timeout(const Duration(seconds: 1)); }); test('清除后异步锁应解除(配合duration过期后可重新执行)', () async { final completer = Completer(); const duration = Duration(milliseconds: 30); throttle.execute('reexecute', () async { await completer.future; }, enableDebounce: false, duration: duration); await Future.delayed(const Duration(milliseconds: 10)); // 此时异步锁生效,clearAllLocks 清除异步锁 throttle.clearAllLocks(); expect(throttle.isExecuting('reexecute'), isFalse); // 等待 EasyThrottle 的 duration 过期 await Future.delayed(duration + const Duration(milliseconds: 20)); int counter = 0; await throttle.execute('reexecute', () async { counter++; }, enableDebounce: false, duration: duration); expect(counter, 1); completer.complete(); }); test('多次调用clearAllLocks应安全', () { throttle.clearAllLocks(); throttle.clearAllLocks(); throttle.clearAllLocks(); expect(true, isTrue); // 无异常则通过 }); }); // ============================================================ // 7. 并发场景测试 // ============================================================ group('Concurrency Tests', () { test('多个不同tagId并发执行', () async { final results = []; final futures = >[]; for (int i = 0; i < 10; i++) { final tag = 'concurrent_$i'; futures.add( throttle.execute(tag, () async { await Future.delayed(const Duration(milliseconds: 10)); results.add(tag); }, enableDebounce: false), ); } await Future.wait(futures); expect(results.length, 10); }); test('大量快速调用压力测试 - 节流', () async { int counter = 0; const iterations = 100; final futures = >[]; for (int i = 0; i < iterations; i++) { futures.add( throttle.execute('stress_throttle', () async { counter++; }, enableDebounce: false, duration: const Duration(milliseconds: 500)), ); } await Future.wait(futures); expect(counter, 1); // 只执行一次 }); test('大量快速调用压力测试 - 防抖', () async { int counter = 0; const iterations = 100; final futures = >[]; for (int i = 0; i < iterations; i++) { futures.add( throttle.execute('stress_debounce', () async { counter++; }, enableDebounce: true, duration: const Duration(milliseconds: 50)), ); } await Future.delayed(const Duration(milliseconds: 200)); await Future.wait(futures).timeout(const Duration(seconds: 2)); expect(counter, 1); // 只执行最后一次 }); test('混合模式并发', () async { int throttleCounter = 0; int debounceCounter = 0; final futures = >[]; // 节流调用 for (int i = 0; i < 5; i++) { futures.add( throttle.execute('mixed_throttle', () async { throttleCounter++; }, enableDebounce: false), ); } // 防抖调用 for (int i = 0; i < 5; i++) { futures.add( throttle.execute('mixed_debounce', () async { debounceCounter++; }, enableDebounce: true, duration: const Duration(milliseconds: 30)), ); } await Future.delayed(const Duration(milliseconds: 100)); await Future.wait(futures); expect(throttleCounter, 1); expect(debounceCounter, 1); }); }); // ============================================================ // 8. 边界条件测试 // ============================================================ group('Edge Cases', () { test('duration为零应正常工作', () async { int counter = 0; await throttle.execute( 'zero_duration', () async { counter++; }, enableDebounce: false, duration: Duration.zero, ); expect(counter, 1); }); test('极短duration应正常工作', () async { int counter = 0; await throttle.execute( 'tiny_duration', () async { counter++; }, enableDebounce: false, duration: const Duration(microseconds: 1), ); expect(counter, 1); }); test('空字符串tagId应正常工作', () async { int counter = 0; await throttle.execute( '', () async { counter++; }, enableDebounce: false, ); expect(counter, 1); }); test('特殊字符tagId应正常工作', () async { int counter = 0; const specialTag = '!@#\$%^&*()_+-=[]{}|;:,.<>?/~`中文🎉'; await throttle.execute( specialTag, () async { counter++; }, enableDebounce: false, ); expect(counter, 1); expect(throttle.isExecuting(specialTag), isFalse); }); test('长字符串tagId应正常工作', () async { int counter = 0; final longTag = 'a' * 10000; await throttle.execute( longTag, () async { counter++; }, enableDebounce: false, ); expect(counter, 1); }); test('同步完成的异步任务应正常工作', () async { int counter = 0; await throttle.execute( 'sync_async', () async { counter++; // 同步代码 }, enableDebounce: false, ); expect(counter, 1); }); test('嵌套异步任务应正常工作', () async { int counter = 0; await throttle.execute( 'nested_async', () async { await Future.delayed(const Duration(milliseconds: 10)); await Future.delayed(const Duration(milliseconds: 10)); await Future.delayed(const Duration(milliseconds: 10)); counter++; }, enableDebounce: false, ); expect(counter, 1); }); }); // ============================================================ // 9. 重入测试 // ============================================================ group('Reentrancy Tests', () { test('在回调中调用同一tagId应被阻止(无死锁)', () async { int outerCounter = 0; int innerCounter = 0; await throttle.execute( 'reentrant_same', () async { outerCounter++; // 尝试重入 await throttle .execute('reentrant_same', () async { innerCounter++; }, enableDebounce: false) .timeout(const Duration(seconds: 1)); }, enableDebounce: false, ); expect(outerCounter, 1); expect(innerCounter, 0); // 内部调用被阻止 }); test('在回调中调用不同tagId应正常执行', () async { int outerCounter = 0; int innerCounter = 0; await throttle.execute( 'reentrant_outer', () async { outerCounter++; await throttle.execute('reentrant_inner', () async { innerCounter++; }, enableDebounce: false); }, enableDebounce: false, ); expect(outerCounter, 1); expect(innerCounter, 1); }); test('防抖模式重入应安全', () async { int outerCounter = 0; int innerCounter = 0; final future = throttle.execute( 'debounce_reentrant_outer', () async { outerCounter++; await throttle .execute('debounce_reentrant_outer', () async { innerCounter++; }, enableDebounce: true, duration: const Duration(milliseconds: 10)) .timeout(const Duration(seconds: 1)); }, enableDebounce: true, duration: const Duration(milliseconds: 10), ); await Future.delayed(const Duration(milliseconds: 100)); await future; expect(outerCounter, 1); expect(innerCounter, 0); }); }); // ============================================================ // 10. 内存安全测试 // ============================================================ group('Memory Safety Tests', () { test('大量防抖调用后Completer应正确清理', () async { const iterations = 200; for (int batch = 0; batch < 5; batch++) { final futures = >[]; for (int i = 0; i < iterations; i++) { futures.add( throttle.execute('memory_test_$batch', () async { // empty }, enableDebounce: true, duration: const Duration(milliseconds: 20)), ); } await Future.delayed(const Duration(milliseconds: 100)); await Future.wait(futures).timeout(const Duration(seconds: 2)); } // 如果到达这里没有超时或内存问题,测试通过 expect(true, isTrue); }); test('异常场景下资源应正确释放', () async { for (int i = 0; i < 50; i++) { try { await throttle.execute( 'exception_cleanup_$i', () async { throw Exception('Error $i'); }, enableDebounce: i % 2 == 0, duration: const Duration(milliseconds: 10), ); } catch (_) {} } // 所有锁应该被释放 for (int i = 0; i < 50; i++) { expect(throttle.isExecuting('exception_cleanup_$i'), isFalse); } }); test('clearAllLocks后状态应完全重置', () async { // 创建一些状态 for (int i = 0; i < 10; i++) { throttle.execute('cleanup_$i', () async { await Future.delayed(const Duration(seconds: 10)); }, enableDebounce: false); } await Future.delayed(const Duration(milliseconds: 50)); // 清除所有 throttle.clearAllLocks(); // 验证状态重置 for (int i = 0; i < 10; i++) { expect(throttle.isExecuting('cleanup_$i'), isFalse); } }); }); // ============================================================ // 11. 时间精度测试 // ============================================================ group('Timing Precision Tests', () { test('节流duration应基本准确', () async { const duration = Duration(milliseconds: 100); int counter = 0; await throttle.execute('timing_throttle', () async { counter++; }, enableDebounce: false, duration: duration); expect(counter, 1); // 在 duration 之前调用应被忽略 await Future.delayed(const Duration(milliseconds: 50)); await throttle.execute('timing_throttle', () async { counter++; }, enableDebounce: false, duration: duration); expect(counter, 1); // 在 duration 之后调用应执行 await Future.delayed(const Duration(milliseconds: 80)); await throttle.execute('timing_throttle', () async { counter++; }, enableDebounce: false, duration: duration); expect(counter, 2); }); test('防抖duration应基本准确', () async { const duration = Duration(milliseconds: 100); int counter = 0; final stopwatch = Stopwatch()..start(); await throttle.execute('timing_debounce', () async { counter++; }, enableDebounce: true, duration: duration); stopwatch.stop(); expect(counter, 1); // 执行时间应该大于等于 duration expect(stopwatch.elapsedMilliseconds, greaterThanOrEqualTo(90)); }); }); // ============================================================ // 12. 异常类型测试 // ============================================================ group('Exception Type Tests', () { test('Error类型应正确传播', () async { await expectLater( throttle.execute('error_type', () async { throw AssertionError('Test assertion'); }, enableDebounce: false), throwsA(isA()), ); }); test('String异常应正确传播', () async { await expectLater( throttle.execute('string_exception', () async { throw 'String exception'; }, enableDebounce: false), throwsA(equals('String exception')), ); }); test('自定义异常应正确传播', () async { await expectLater( throttle.execute('custom_exception', () async { throw CustomTestException('Custom error'); }, enableDebounce: false), throwsA(isA()), ); }); test('防抖模式异常类型应正确传播', () async { await expectLater( throttle.execute('debounce_exception_type', () async { throw RangeError('Out of range'); }, enableDebounce: true, duration: const Duration(milliseconds: 10)), throwsA(isA()), ); }); }); // ============================================================ // 13. 默认参数测试 // ============================================================ group('Default Parameters Tests', () { test('默认duration应为300ms', () async { int counter = 0; await throttle.execute('default_duration', () async { counter++; }); expect(counter, 1); // 在默认 300ms 内再次调用应被忽略 await throttle.execute('default_duration', () async { counter++; }); expect(counter, 1); }); test('默认enableDebounce应为false(节流模式)', () async { int counter = 0; final stopwatch = Stopwatch()..start(); await throttle.execute( 'default_mode', () async { counter++; }, duration: const Duration(milliseconds: 100), ); stopwatch.stop(); // 节流模式应立即执行 expect(counter, 1); expect(stopwatch.elapsedMilliseconds, lessThan(50)); }); }); // ============================================================ // 14. 顺序保证测试 // ============================================================ group('Order Guarantee Tests', () { test('防抖模式应保证只执行最后一次', () async { final executedValues = []; const duration = Duration(milliseconds: 30); final futures = >[]; for (int i = 1; i <= 10; i++) { final value = i; futures.add( throttle.execute( 'order_test', () async { executedValues.add(value); }, enableDebounce: true, duration: duration, ), ); await Future.delayed(const Duration(milliseconds: 5)); } await Future.delayed(const Duration(milliseconds: 100)); await Future.wait(futures); expect(executedValues.length, 1); expect(executedValues.first, 10); }); }); // ============================================================ // 15. 复杂场景集成测试 // ============================================================ group('Integration Tests', () { test('模拟真实按钮快速点击场景', () async { int apiCallCount = 0; final results = []; // 模拟用户快速点击按钮5次 for (int i = 0; i < 5; i++) { throttle.executeSafe( 'button_click', () async { apiCallCount++; await Future.delayed(const Duration(milliseconds: 100)); // 模拟API调用 results.add('success'); }, enableDebounce: false, duration: const Duration(milliseconds: 300), ); await Future.delayed(const Duration(milliseconds: 50)); } await Future.delayed(const Duration(milliseconds: 500)); expect(apiCallCount, 1); expect(results.length, 1); }); test('模拟搜索输入防抖场景', () async { final searchQueries = []; Future search(String query) async { await throttle.execute( 'search_input', () async { await Future.delayed(const Duration(milliseconds: 50)); // 模拟网络请求 searchQueries.add(query); }, enableDebounce: true, duration: const Duration(milliseconds: 100), ); } // 模拟用户输入 "flutter" search('f'); await Future.delayed(const Duration(milliseconds: 30)); search('fl'); await Future.delayed(const Duration(milliseconds: 30)); search('flu'); await Future.delayed(const Duration(milliseconds: 30)); search('flut'); await Future.delayed(const Duration(milliseconds: 30)); search('flutt'); await Future.delayed(const Duration(milliseconds: 30)); await search('flutter'); await Future.delayed(const Duration(milliseconds: 200)); // 只有最后一个搜索词被执行 expect(searchQueries.length, 1); expect(searchQueries.first, 'flutter'); }); test('模拟弱网环境重复点击场景', () async { int requestCount = 0; final taskCompleter = Completer(); // 第一次点击,请求开始但未返回 final f1 = throttle.execute( 'weak_network', () async { requestCount++; await taskCompleter.future; // 模拟弱网,请求迟迟不返回 }, enableDebounce: false, ); await Future.delayed(const Duration(milliseconds: 50)); // 用户不耐烦,继续点击 for (int i = 0; i < 10; i++) { await throttle.execute( 'weak_network', () async { requestCount++; }, enableDebounce: false, ); } // 请求返回 taskCompleter.complete(); await f1; // 应该只有一次请求 expect(requestCount, 1); }); test('混合使用节流和防抖应互不干扰', () async { int throttleCount = 0; int debounceCount = 0; // 同一个tag,先节流后防抖 await throttle.execute('mixed_tag', () async { throttleCount++; }, enableDebounce: false); await Future.delayed(const Duration(milliseconds: 350)); await throttle.execute('mixed_tag', () async { debounceCount++; }, enableDebounce: true, duration: const Duration(milliseconds: 30)); await Future.delayed(const Duration(milliseconds: 100)); expect(throttleCount, 1); expect(debounceCount, 1); }); }); } /// 自定义测试异常类 class CustomTestException implements Exception { final String message; CustomTestException(this.message); @override String toString() => 'CustomTestException: $message'; }