From 4340dc38d056da66f3b6e7f30d58bb398184fad8 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Sun, 31 May 2026 16:15:51 +0200 Subject: [PATCH] =?UTF-8?q?add=20pumpAsync=20test=20helper=20=E2=80=94=20b?= =?UTF-8?q?ounded=20settle=20for=20async=20widget=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A reusable helper in the shared harness that drains microtasks + advances one short fake-time tick, replacing the two patterns that have repeatedly wedged the suite (and the pre-push gate) for ~10 minutes each: pumpAndSettle (loops until quiescent — hangs on perpetual animation / overlapping async) and `await Future.delayed(Duration.zero)` inside testWidgets (a real timer that never fires under fake-async). Bounded by construction — cannot hang. Co-Authored-By: Claude --- test/helpers/widget_harness.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/helpers/widget_harness.dart b/test/helpers/widget_harness.dart index 345c426d..bdd09e98 100644 --- a/test/helpers/widget_harness.dart +++ b/test/helpers/widget_harness.dart @@ -1,5 +1,6 @@ import 'package:clide/kernel/kernel.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'kernel_fixture.dart'; @@ -33,3 +34,23 @@ Widget harness(KernelFixture fixture, Widget child) { ), ); } + +/// Settle async-driven UI in a widget test WITHOUT the two patterns that have +/// repeatedly wedged this suite: +/// +/// - **Never `pumpAndSettle()`** — it loops until the frame queue is quiescent, +/// so a perpetual animation or overlapping async loads hang it for its +/// ~10-minute default timeout (which wedged the pre-push gate). +/// - **Never `await Future.delayed(Duration.zero)`** — inside the fake-async +/// `testWidgets` zone a real timer never fires unless fake time is advanced, +/// so that line wedges the test until timeout (and even defeats `--timeout`). +/// +/// Instead: pump one frame (draining the microtask queue — broadcast-stream and +/// async-IPC deliveries resolve here), then advance a tiny fake-time tick to +/// flush any follow-up `setState`. Bounded by construction — it cannot hang. +/// Use this after publishing a message / triggering a load in a reader/panel +/// widget test, in place of `pumpAndSettle`. +Future pumpAsync(WidgetTester tester) async { + await tester.pump(); + await tester.pump(const Duration(milliseconds: 20)); +}