From 9590f555c9336dc3411ec98536f4b7d50ca2f45d Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Sat, 23 May 2026 10:41:17 +0200 Subject: [PATCH] share a generous test I/O timeout constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flaky-gate fixes kept hand-tuning magic seconds in each real-I/O test. Pull them into one Flutter-free constant — ioTimeout (20s) in test/helpers/timeouts.dart, importable by both the dart-test (pty) and flutter-test suites — and route the real-external-wait timeouts through it: PTY output (session + registry), and fs-watcher events (timeout + poll ceiling). Tune in one place instead of scattering durations. The ipc socket round-trip timeouts (2s) are left as-is — they haven't flaked and local sockets respond in ms; they can adopt the constant later if needed. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/files/watcher_test.dart | 14 ++++++++------ test/helpers/timeouts.dart | 10 ++++++++++ test/panes/registry_test.dart | 6 ++++-- test/pty/session_test.dart | 16 +++++++++------- 4 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 test/helpers/timeouts.dart diff --git a/test/files/watcher_test.dart b/test/files/watcher_test.dart index 211adf59..f01a5ba2 100644 --- a/test/files/watcher_test.dart +++ b/test/files/watcher_test.dart @@ -8,6 +8,8 @@ import 'package:clide/src/files/ignore.dart'; import 'package:clide/src/files/watcher.dart'; import 'package:test/test.dart'; +import '../helpers/timeouts.dart'; + void main() { group('FileChangeKind', () { test('fromEvent maps every FileSystemEvent.type to a wire kind', () { @@ -69,8 +71,8 @@ void main() { ); await File('${sandbox.path}/new.txt').writeAsString('hi'); final change = await saw.timeout( - const Duration(seconds: 5), - onTimeout: () => fail('no `new.txt` event within 5s'), + ioTimeout, + onTimeout: () => fail('no `new.txt` event within ${ioTimeout.inSeconds}s'), ); expect(change.path, 'new.txt'); }); @@ -115,13 +117,13 @@ void main() { /// Wait until [received] satisfies [predicate]. Polls the list (it /// gets mutated by the listener subscription) every 25 ms with a -/// generous 8 s ceiling; fails loudly on miss instead of silently -/// continuing as the old fixed-sleep tests did. +/// generous [ioTimeout] ceiling; fails loudly on miss instead of +/// silently continuing as the old fixed-sleep tests did. Future _expectReceived(List received, bool Function(T) predicate) async { - final deadline = DateTime.now().add(const Duration(seconds: 8)); + final deadline = DateTime.now().add(ioTimeout); while (!received.any(predicate)) { if (DateTime.now().isAfter(deadline)) { - fail('expected event never arrived within 8s; received=${received.length} entries'); + fail('expected event never arrived within ${ioTimeout.inSeconds}s; received=${received.length} entries'); } await Future.delayed(const Duration(milliseconds: 25)); } diff --git a/test/helpers/timeouts.dart b/test/helpers/timeouts.dart new file mode 100644 index 00000000..f93fcd26 --- /dev/null +++ b/test/helpers/timeouts.dart @@ -0,0 +1,10 @@ +/// Shared test timeouts. Flutter-free on purpose, so both `dart test` +/// (the pty / ipc suites) and `flutter test` files can import it. +library; + +/// Generous timeout for waiting on real external I/O — PTY output, fs +/// watcher events, socket round-trips. A working path responds in well +/// under a second; this is pure headroom so transient scheduling latency +/// doesn't flake the gate. A genuinely broken path still fails — just +/// later. Tune here, once, rather than scattering magic seconds. +const Duration ioTimeout = Duration(seconds: 20); diff --git a/test/panes/registry_test.dart b/test/panes/registry_test.dart index b6a46c70..994d4241 100644 --- a/test/panes/registry_test.dart +++ b/test/panes/registry_test.dart @@ -13,6 +13,8 @@ import 'package:clide/clide.dart'; import 'package:clide/src/panes/registry.dart'; import 'package:test/test.dart'; +import '../helpers/timeouts.dart'; + void main() { if (!Platform.isLinux && !Platform.isMacOS) return; @@ -63,8 +65,8 @@ void main() { ); final decoded = await got.future.timeout( - const Duration(seconds: 5), - onTimeout: () => fail('pane.output never carried "hello-panes" within 5s'), + ioTimeout, + onTimeout: () => fail('pane.output never carried "hello-panes" within ${ioTimeout.inSeconds}s'), ); expect(decoded, contains('hello-panes')); expect(sink.ofKind('pane.output'), isNotEmpty); diff --git a/test/pty/session_test.dart b/test/pty/session_test.dart index 5e74371a..22dbb2b5 100644 --- a/test/pty/session_test.dart +++ b/test/pty/session_test.dart @@ -20,6 +20,8 @@ import 'package:clide/src/pty/errors.dart'; import 'package:clide/src/pty/native_pty.dart'; import 'package:test/test.dart'; +import '../helpers/timeouts.dart'; + void main() { if (!Platform.isLinux && !Platform.isMacOS) return; @@ -38,7 +40,7 @@ void main() { ); addTearDown(s.close); - final got = await _readUntil(s, 'hello-pty', const Duration(seconds: 20)); + final got = await _readUntil(s, 'hello-pty', ioTimeout); expect(got, contains('hello-pty')); }); @@ -67,13 +69,13 @@ void main() { addTearDown(sub.cancel); await firstByte.future.timeout( - const Duration(seconds: 20), - onTimeout: () => fail('shell never produced its first byte within 20s'), + ioTimeout, + onTimeout: () => fail('shell never produced its first byte within ${ioTimeout.inSeconds}s'), ); s.write(utf8.encode('echo write-test-ok\n')); - final result = await _waitForBuffer(buf, 'write-test-ok', const Duration(seconds: 20)); + final result = await _waitForBuffer(buf, 'write-test-ok', ioTimeout); expect(result, contains('write-test-ok')); }); @@ -95,8 +97,8 @@ void main() { await s.close(); await done.future.timeout( - const Duration(seconds: 10), - onTimeout: () => fail('output stream did not close within 10s after s.close()'), + ioTimeout, + onTimeout: () => fail('output stream did not close within ${ioTimeout.inSeconds}s after s.close()'), ); expect(s.isClosed, isTrue); }); @@ -116,7 +118,7 @@ void main() { ); addTearDown(s.close); - final got = await _readUntil(s, 'path-resolution-ok', const Duration(seconds: 20)); + final got = await _readUntil(s, 'path-resolution-ok', ioTimeout); expect(got, contains('path-resolution-ok')); });