fix: re-anchor effects clock when ticker drains (stale-clock freeze)
After Ticker.stop(), elapsed resets to zero on the next start(). _now was left frozen at the old elapsed, so effects added after a drain captured a stale start time and their progress() clamped to 0 forever — ticker never stopped, second batch broken. Fix: reset _now = Duration.zero on drain. Adds @visibleForTesting getters and a regression test that catches the stale value directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import 'package:block_seasons/game/engine/game_engine.dart';
|
||||
import 'package:block_seasons/ui/widgets/effects_overlay.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
PlacementResult _clearResult() => const PlacementResult(
|
||||
events: [],
|
||||
pointsGained: 250,
|
||||
linesCleared: 1,
|
||||
gemsCleared: 0,
|
||||
clearedRows: [3],
|
||||
clearedCols: [],
|
||||
comboStreak: 2,
|
||||
);
|
||||
|
||||
void main() {
|
||||
testWidgets('second batch after drain completes within its own duration',
|
||||
(tester) async {
|
||||
final key = GlobalKey<EffectsOverlayState>();
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home:
|
||||
Stack(children: [Positioned.fill(child: EffectsOverlay(key: key))]),
|
||||
));
|
||||
|
||||
const board = Rect.fromLTWH(0, 0, 320, 320);
|
||||
|
||||
// ── First batch ──────────────────────────────────────────────────────────
|
||||
key.currentState!.onPlacement(_clearResult(), boardRect: board);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Ticker must be stopped after the first drain.
|
||||
expect(
|
||||
key.currentState!.ticker.isActive,
|
||||
isFalse,
|
||||
reason: 'ticker should be idle after first batch drains',
|
||||
);
|
||||
|
||||
// _now must be Duration.zero after drain (regression for stale-clock bug).
|
||||
// Without the fix, _now stays frozen at the elapsed value from the last
|
||||
// tick of the first batch (~1000 ms).
|
||||
expect(
|
||||
key.currentState!.now,
|
||||
Duration.zero,
|
||||
reason:
|
||||
'_now must be reset to zero when the list drains so the next '
|
||||
'batch starts from a clean clock',
|
||||
);
|
||||
|
||||
// ── Second batch ─────────────────────────────────────────────────────────
|
||||
// With the stale-clock bug the effects get start: ~1000ms, but the
|
||||
// restarted ticker delivers elapsed from 0, so progress stays 0 forever
|
||||
// and they never drain. We verify the second batch completes via
|
||||
// pumpAndSettle (which would time out if the ticker ran forever).
|
||||
key.currentState!.onPlacement(_clearResult(), boardRect: board);
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
tester.hasRunningAnimations,
|
||||
isFalse,
|
||||
reason:
|
||||
'second batch must finish; stale clock would keep effects frozen '
|
||||
'and the ticker running indefinitely',
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user