From 04118891f52af6dc2da9182c2f342f856336a361 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Wed, 4 May 2022 20:02:34 +0200 Subject: [PATCH] feat: cancel subscription on ramp behavior remove --- .../behaviors/ramp_bonus_behavior.dart | 44 ++++++++++++------ .../behaviors/ramp_shot_behavior.dart | 46 ++++++++++++------- .../behaviors/ramp_bonus_behavior_test.dart | 42 +++++++++++++++-- .../behaviors/ramp_shot_behavior_test.dart | 39 +++++++++++++++- 4 files changed, 134 insertions(+), 37 deletions(-) diff --git a/lib/game/components/android_acres/behaviors/ramp_bonus_behavior.dart b/lib/game/components/android_acres/behaviors/ramp_bonus_behavior.dart index 598c9cc2..218ad8b4 100644 --- a/lib/game/components/android_acres/behaviors/ramp_bonus_behavior.dart +++ b/lib/game/components/android_acres/behaviors/ramp_bonus_behavior.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -17,32 +18,45 @@ class RampBonusBehavior extends Component }) : _points = points, super(); + /// Creates a [RampBonusBehavior]. + /// + /// This can be used for testing [RampBonusBehavior] in isolation. + @visibleForTesting + RampBonusBehavior.test({ + required Points points, + required this.subscription, + }) : _points = points, + super(); + final Points _points; - StreamSubscription? _subscription; + /// Subscription to [SpaceshipRampState] at [SpaceshipRamp]. + @visibleForTesting + StreamSubscription? subscription; @override void onMount() { super.onMount(); - _subscription = parent.bloc.stream.listen((state) { - final achievedOneMillionPoints = state.hits % 10 == 0; - - if (achievedOneMillionPoints) { - parent.add( - ScoringBehavior( - points: _points, - position: Vector2(0, -60), - duration: 2, - ), - ); - } - }); + subscription = subscription ?? + parent.bloc.stream.listen((state) { + final achievedOneMillionPoints = state.hits % 10 == 0; + + if (achievedOneMillionPoints) { + parent.add( + ScoringBehavior( + points: _points, + position: Vector2(0, -60), + duration: 2, + ), + ); + } + }); } @override void onRemove() { - _subscription?.cancel(); + subscription?.cancel(); super.onRemove(); } } diff --git a/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart b/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart index f8aa8581..8a9c1a9c 100644 --- a/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart +++ b/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flame/components.dart'; +import 'package:flutter/cupertino.dart'; import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -17,33 +18,46 @@ class RampShotBehavior extends Component }) : _points = points, super(); + /// Creates a [RampShotBehavior]. + /// + /// This can be used for testing [RampShotBehavior] in isolation. + @visibleForTesting + RampShotBehavior.test({ + required Points points, + required this.subscription, + }) : _points = points, + super(); + final Points _points; - StreamSubscription? _subscription; + /// Subscription to [SpaceshipRampState] at [SpaceshipRamp]. + @visibleForTesting + StreamSubscription? subscription; @override void onMount() { super.onMount(); - _subscription = parent.bloc.stream.listen((state) { - final achievedOneMillionPoints = state.hits % 10 == 0; - - if (!achievedOneMillionPoints) { - gameRef.read().add(const MultiplierIncreased()); - - parent.add( - ScoringBehavior( - points: _points, - position: Vector2(0, -45), - ), - ); - } - }); + subscription = subscription ?? + parent.bloc.stream.listen((state) { + final achievedOneMillionPoints = state.hits % 10 == 0; + + if (!achievedOneMillionPoints) { + gameRef.read().add(const MultiplierIncreased()); + + parent.add( + ScoringBehavior( + points: _points, + position: Vector2(0, -45), + ), + ); + } + }); } @override void onRemove() { - _subscription?.cancel(); + subscription?.cancel(); super.onRemove(); } } diff --git a/test/game/components/android_acres/behaviors/ramp_bonus_behavior_test.dart b/test/game/components/android_acres/behaviors/ramp_bonus_behavior_test.dart index fe1f8382..b14b5d03 100644 --- a/test/game/components/android_acres/behaviors/ramp_bonus_behavior_test.dart +++ b/test/game/components/android_acres/behaviors/ramp_bonus_behavior_test.dart @@ -1,6 +1,7 @@ // ignore_for_file: cascade_invocations, prefer_const_constructors import 'dart:async'; +import 'dart:io'; import 'package:bloc_test/bloc_test.dart'; import 'package:flame_test/flame_test.dart'; @@ -18,6 +19,9 @@ class _MockGameBloc extends Mock implements GameBloc {} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} +class _MockStreamSubscription extends Mock + implements StreamSubscription {} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); final assets = [ @@ -57,8 +61,7 @@ void main() { ); flameBlocTester.testGameWidget( - 'when hits are multiples of 10 times ' - 'add score and show score points', + 'when hits are multiples of 10 times adds a ScoringBehavior', setUp: (game, tester) async { final bloc = _MockSpaceshipRampCubit(); final streamController = StreamController(); @@ -87,8 +90,7 @@ void main() { ); flameBlocTester.testGameWidget( - 'when hits are not multiple of 10 times ' - "doesn't add score neither show score points", + "when hits are not multiple of 10 times doesn't add any ScoringBehavior", setUp: (game, tester) async { final bloc = _MockSpaceshipRampCubit(); final streamController = StreamController(); @@ -115,5 +117,37 @@ void main() { expect(scores.length, 0); }, ); + + flameBlocTester.testGameWidget( + 'closes subscription when removed', + setUp: (game, tester) async { + final bloc = _MockSpaceshipRampCubit(); + whenListen( + bloc, + const Stream.empty(), + initialState: SpaceshipRampState.initial(), + ); + when(bloc.close).thenAnswer((_) async {}); + + final subscription = _MockStreamSubscription(); + when(subscription.cancel).thenAnswer((_) async {}); + + final behavior = RampBonusBehavior.test( + points: shotPoints, + subscription: subscription, + ); + final parent = SpaceshipRamp.test( + bloc: bloc, + ); + + await game.ensureAdd(ZCanvasComponent(children: [parent])); + await parent.ensureAdd(behavior); + + parent.remove(behavior); + await game.ready(); + + verify(subscription.cancel).called(1); + }, + ); }); } diff --git a/test/game/components/android_acres/behaviors/ramp_shot_behavior_test.dart b/test/game/components/android_acres/behaviors/ramp_shot_behavior_test.dart index 47e83e1e..23f02220 100644 --- a/test/game/components/android_acres/behaviors/ramp_shot_behavior_test.dart +++ b/test/game/components/android_acres/behaviors/ramp_shot_behavior_test.dart @@ -18,6 +18,9 @@ class _MockGameBloc extends Mock implements GameBloc {} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} +class _MockStreamSubscription extends Mock + implements StreamSubscription {} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); final assets = [ @@ -58,7 +61,7 @@ void main() { flameBlocTester.testGameWidget( 'when hits are not multiple of 10 times ' - 'increase multiplier, add score and show score points', + 'increases multiplier and adds a ScoringBehavior', setUp: (game, tester) async { final bloc = _MockSpaceshipRampCubit(); final streamController = StreamController(); @@ -89,7 +92,7 @@ void main() { flameBlocTester.testGameWidget( 'when hits multiple of 10 times ' - "doesn't increase multiplier, neither add score or show score points", + "doesn't increase multiplier, neither ScoringBehavior", setUp: (game, tester) async { final bloc = _MockSpaceshipRampCubit(); final streamController = StreamController(); @@ -117,5 +120,37 @@ void main() { expect(scores.length, 0); }, ); + + flameBlocTester.testGameWidget( + 'closes subscription when removed', + setUp: (game, tester) async { + final bloc = _MockSpaceshipRampCubit(); + whenListen( + bloc, + const Stream.empty(), + initialState: SpaceshipRampState.initial(), + ); + when(bloc.close).thenAnswer((_) async {}); + + final subscription = _MockStreamSubscription(); + when(subscription.cancel).thenAnswer((_) async {}); + + final behavior = RampShotBehavior.test( + points: shotPoints, + subscription: subscription, + ); + final parent = SpaceshipRamp.test( + bloc: bloc, + ); + + await game.ensureAdd(ZCanvasComponent(children: [parent])); + await parent.ensureAdd(behavior); + + parent.remove(behavior); + await game.ready(); + + verify(subscription.cancel).called(1); + }, + ); }); }