From eb5066df8856be735b3ea3dc751fb8dd6b453e50 Mon Sep 17 00:00:00 2001 From: RuiAlonso Date: Sun, 8 May 2022 21:47:26 +0200 Subject: [PATCH] fix: added behaviors to ramp and some tests --- lib/bootstrap.dart | 2 +- .../android_acres/spaceship_ramp_game.dart | 15 +- .../ramp_arrow_blinking_behavior_test.dart | 213 ++++++++++++++++++ ..._ball_ascending_contact_behavior_test.dart | 71 ++++-- .../cubit/spaceship_ramp_cubit_test.dart | 130 ++++++++++- .../cubit/spaceship_ramp_state_test.dart | 54 ++++- .../spaceship_ramp/spaceship_ramp_test.dart | 8 +- .../behaviors/ramp_bonus_behavior_test.dart | 60 ++--- .../ramp_multiplier_behavior_test.dart | 184 +++++++++++++++ .../behaviors/ramp_reset_behavior_test.dart | 131 +++++++++++ .../behaviors/ramp_shot_behavior_test.dart | 96 ++------ 11 files changed, 796 insertions(+), 168 deletions(-) create mode 100644 packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_arrow_blinking_behavior_test.dart create mode 100644 test/game/components/android_acres/behaviors/ramp_multiplier_behavior_test.dart create mode 100644 test/game/components/android_acres/behaviors/ramp_reset_behavior_test.dart diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 9dea2208..f4028ee1 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -10,7 +10,7 @@ class AppBlocObserver extends BlocObserver { @override void onChange(BlocBase bloc, Change change) { super.onChange(bloc, change); - //log('onChange(${bloc.runtimeType}, $change)'); + log('onChange(${bloc.runtimeType}, $change)'); } @override diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart index 1027002a..7a425917 100644 --- a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart @@ -38,15 +38,26 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents { Color backgroundColor() => Colors.white; late final SpaceshipRamp _spaceshipRamp; + late final SpaceshipRampCubit _bloc; @override Future onLoad() async { await super.onLoad(); camera.followVector2(Vector2(-12, -50)); + _spaceshipRamp = SpaceshipRamp(); + /* await add( - _spaceshipRamp = SpaceshipRamp(), + FlameBlocProvider( + create: SpaceshipRampCubit.new, + children: [ + SpaceshipRamp( + children: [_spaceshipRamp], + ), + ], + ), ); + */ await traceAllBodies(); } @@ -57,7 +68,7 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents { ) { if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.space) { - _spaceshipRamp.bloc.onAscendingBallEntered(); + _bloc.onProgressed(); return KeyEventResult.handled; } diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_arrow_blinking_behavior_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_arrow_blinking_behavior_test.dart new file mode 100644 index 00000000..d259a170 --- /dev/null +++ b/packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_arrow_blinking_behavior_test.dart @@ -0,0 +1,213 @@ +// ignore_for_file: prefer_const_constructors, cascade_invocations + +import 'dart:async'; + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/spaceship_ramp/behavior/ramp_arrow_blinking_behavior.dart'; +import 'package:pinball_flame/pinball_flame.dart'; +import 'package:pinball_components/src/components/multiball/behaviors/behaviors.dart'; + +class _TestGame extends Forge2DGame { + @override + Future onLoad() async { + images.prefix = ''; + await images.loadAll([ + Assets.images.multiball.lit.keyName, + Assets.images.android.ramp.railingForeground.keyName, + Assets.images.android.ramp.railingBackground.keyName, + Assets.images.android.ramp.main.keyName, + Assets.images.android.ramp.arrow.inactive.keyName, + Assets.images.android.ramp.arrow.active1.keyName, + Assets.images.android.ramp.arrow.active2.keyName, + Assets.images.android.ramp.arrow.active3.keyName, + Assets.images.android.ramp.arrow.active4.keyName, + Assets.images.android.ramp.arrow.active5.keyName, + ]); + } + + Future pump( + SpaceshipRamp child, { + required SpaceshipRampCubit spaceshipRampCubit, + }) async { + await ensureAdd( + FlameBlocProvider.value( + value: spaceshipRampCubit, + children: [ + ZCanvasComponent(children: [child]), + ], + ), + ); + } +} + +class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final flameTester = FlameTester(_TestGame.new); + + group( + 'RampArrowBlinkingBehavior', + () { + flameTester.testGameWidget( + 'calls onBlink every 0.05 seconds when animation state is animated', + setUp: (game, tester) async { + final behavior = RampArrowBlinkingBehavior(); + final bloc = _MockSpaceshipRampCubit(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: SpaceshipRampState.initial(), + ); + + final spaceshipRamp = SpaceshipRamp.test(); + await game.pump( + spaceshipRamp, + spaceshipRampCubit: bloc, + ); + await spaceshipRamp.add(behavior); + + streamController.add( + SpaceshipRampState( + hits: 1, + animationState: ArrowAnimationState.blinking, + lightState: ArrowLightState.active1, + ), + ); + await tester.pump(); + game.update(0); + + verify(bloc.onBlink).called(1); + + await tester.pump(); + game.update(0.05); + + await streamController.close(); + verify(bloc.onBlink).called(1); + }, + ); + + flameTester.testGameWidget( + 'calls onStop when animation state is stopped', + setUp: (game, tester) async { + final behavior = RampArrowBlinkingBehavior(); + final bloc = _MockSpaceshipRampCubit(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: SpaceshipRampState.initial(), + ); + when(bloc.onBlink).thenAnswer((_) async {}); + + final spaceshipRamp = SpaceshipRamp.test(); + await game.pump( + spaceshipRamp, + spaceshipRampCubit: bloc, + ); + await spaceshipRamp.add(behavior); + + streamController.add( + SpaceshipRampState( + hits: 1, + animationState: ArrowAnimationState.blinking, + lightState: ArrowLightState.active1, + ), + ); + await tester.pump(); + + streamController.add( + SpaceshipRampState( + hits: 1, + animationState: ArrowAnimationState.idle, + lightState: ArrowLightState.active1, + ), + ); + + await streamController.close(); + verify(bloc.onStop).called(1); + }, + ); + + flameTester.testGameWidget( + 'onTick stops when there is no animation', + setUp: (game, tester) async { + final behavior = RampArrowBlinkingBehavior(); + final bloc = _MockSpaceshipRampCubit(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: SpaceshipRampState.initial(), + ); + when(bloc.onBlink).thenAnswer((_) async {}); + + final spaceshipRamp = SpaceshipRamp.test(); + await game.pump( + spaceshipRamp, + spaceshipRampCubit: bloc, + ); + await spaceshipRamp.add(behavior); + + streamController.add( + SpaceshipRampState( + hits: 1, + animationState: ArrowAnimationState.idle, + lightState: ArrowLightState.active1, + ), + ); + await tester.pump(); + + behavior.onTick(); + + expect(behavior.timer.isRunning(), false); + }, + ); + + flameTester.testGameWidget( + 'onTick stops after 10 blinks repetitions', + setUp: (game, tester) async { + final behavior = RampArrowBlinkingBehavior(); + final bloc = _MockSpaceshipRampCubit(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: SpaceshipRampState.initial(), + ); + when(bloc.onBlink).thenAnswer((_) async {}); + + final spaceshipRamp = SpaceshipRamp.test(); + await game.pump( + spaceshipRamp, + spaceshipRampCubit: bloc, + ); + await spaceshipRamp.add(behavior); + + streamController.add( + SpaceshipRampState( + hits: 1, + animationState: ArrowAnimationState.blinking, + lightState: ArrowLightState.inactive, + ), + ); + await tester.pump(); + + for (var i = 0; i < 10; i++) { + behavior.onTick(); + } + + expect(behavior.timer.isRunning(), false); + }, + ); + }, + ); +} diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_ball_ascending_contact_behavior_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_ball_ascending_contact_behavior_test.dart index d1f03ce7..3ea10085 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_ball_ascending_contact_behavior_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp/behavior/ramp_ball_ascending_contact_behavior_test.dart @@ -1,14 +1,47 @@ // ignore_for_file: cascade_invocations import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/src/components/spaceship_ramp/behavior/behavior.dart'; - -import '../../../../helpers/helpers.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +class _TestGame extends Forge2DGame { + @override + Future onLoad() async { + images.prefix = ''; + await images.loadAll([ + Assets.images.android.ramp.boardOpening.keyName, + Assets.images.android.ramp.railingForeground.keyName, + Assets.images.android.ramp.railingBackground.keyName, + Assets.images.android.ramp.main.keyName, + Assets.images.android.ramp.arrow.inactive.keyName, + Assets.images.android.ramp.arrow.active1.keyName, + Assets.images.android.ramp.arrow.active2.keyName, + Assets.images.android.ramp.arrow.active3.keyName, + Assets.images.android.ramp.arrow.active4.keyName, + Assets.images.android.ramp.arrow.active5.keyName, + ]); + } + + Future pump( + SpaceshipRamp child, { + required SpaceshipRampCubit spaceshipRampCubit, + }) async { + await ensureAdd( + FlameBlocProvider.value( + value: spaceshipRampCubit, + children: [ + ZCanvasComponent(children: [child]), + ], + ), + ); + } +} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} @@ -20,20 +53,8 @@ class _MockContact extends Mock implements Contact {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final assets = [ - Assets.images.android.ramp.boardOpening.keyName, - Assets.images.android.ramp.railingForeground.keyName, - Assets.images.android.ramp.railingBackground.keyName, - Assets.images.android.ramp.main.keyName, - Assets.images.android.ramp.arrow.inactive.keyName, - Assets.images.android.ramp.arrow.active1.keyName, - Assets.images.android.ramp.arrow.active2.keyName, - Assets.images.android.ramp.arrow.active3.keyName, - Assets.images.android.ramp.arrow.active4.keyName, - Assets.images.android.ramp.arrow.active5.keyName, - ]; - - final flameTester = FlameTester(() => TestGame(assets)); + + final flameTester = FlameTester(_TestGame.new); group( 'RampBallAscendingContactBehavior', @@ -68,14 +89,15 @@ void main() { ); final parent = SpaceshipRampBoardOpening.test(); - final spaceshipRamp = SpaceshipRamp.test( - bloc: bloc, - ); + final spaceshipRamp = SpaceshipRamp.test(); when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); await spaceshipRamp.add(parent); - await game.ensureAddAll([spaceshipRamp, ball]); + await game.pump( + spaceshipRamp, + spaceshipRampCubit: bloc, + ); await parent.add(behavior); behavior.beginContact(ball, _MockContact()); @@ -96,14 +118,15 @@ void main() { ); final parent = SpaceshipRampBoardOpening.test(); - final spaceshipRamp = SpaceshipRamp.test( - bloc: bloc, - ); + final spaceshipRamp = SpaceshipRamp.test(); when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); await spaceshipRamp.add(parent); - await game.ensureAddAll([spaceshipRamp, ball]); + await game.pump( + spaceshipRamp, + spaceshipRampCubit: bloc, + ); await parent.add(behavior); behavior.beginContact(ball, _MockContact()); diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart index b7e899fe..32826cd6 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart @@ -15,9 +15,133 @@ void main() { ..onAscendingBallEntered() ..onAscendingBallEntered(), expect: () => [ - SpaceshipRampState(hits: 1), - SpaceshipRampState(hits: 2), - SpaceshipRampState(hits: 3), + isA().having((state) => state.hits, 'hits', 1), + isA().having((state) => state.hits, 'hits', 2), + isA().having((state) => state.hits, 'hits', 3), + ], + ); + }); + + group('onReset', () { + blocTest( + 'emits state reset to initial values', + build: SpaceshipRampCubit.new, + seed: () => SpaceshipRampState( + hits: 100, + lightState: ArrowLightState.active3, + animationState: ArrowAnimationState.blinking, + ), + act: (bloc) => bloc.onReset(), + expect: () => [ + isA() + ..having((state) => state.hits, 'hits', 0) + ..having( + (state) => state.lightState, + 'lightState', + ArrowLightState.inactive, + ) + ..having( + (state) => state.animationState, + 'animationState', + ArrowAnimationState.idle, + ), + ], + ); + }); + + group('onAnimate', () { + blocTest( + 'emits animationState blinking', + build: SpaceshipRampCubit.new, + seed: () => SpaceshipRampState( + hits: 100, + lightState: ArrowLightState.active3, + animationState: ArrowAnimationState.idle, + ), + act: (bloc) => bloc.onAnimate(), + expect: () => [ + isA().having( + (state) => state.animationState, + 'animationState', + ArrowAnimationState.blinking, + ), + ], + ); + }); + + group('onStop', () { + blocTest( + 'emits animationState idle and lightState inactive', + build: SpaceshipRampCubit.new, + seed: () => SpaceshipRampState( + hits: 100, + lightState: ArrowLightState.active3, + animationState: ArrowAnimationState.blinking, + ), + act: (bloc) => bloc.onStop(), + expect: () => [ + isA() + ..having( + (state) => state.lightState, + 'lightState', + ArrowLightState.inactive, + ) + ..having( + (state) => state.animationState, + 'animationState', + ArrowAnimationState.idle, + ), + ], + ); + }); + + group('onBlink', () { + blocTest( + 'emits next lit state at lightState', + build: SpaceshipRampCubit.new, + seed: () => SpaceshipRampState( + hits: 100, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.blinking, + ), + act: (bloc) => bloc + ..onBlink() + ..onBlink() + ..onBlink() + ..onBlink() + ..onBlink() + ..onBlink(), + expect: () => [ + isA().having( + (state) => state.lightState, + 'lightState', + ArrowLightState.active1, + ), + isA().having( + (state) => state.lightState, + 'lightState', + ArrowLightState.active2, + ), + isA().having( + (state) => state.lightState, + 'lightState', + ArrowLightState.active3, + ), + isA().having( + (state) => state.lightState, + 'lightState', + ArrowLightState.active4, + ), + isA().having( + (state) => state.lightState, + 'lightState', + ArrowLightState.active5, + ), + isA().having( + (state) => state.lightState, + 'lightState', + ArrowLightState.inactive, + ), ], ); }); diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart index 536f4e8e..3ad7d067 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart @@ -7,9 +7,17 @@ void main() { group('SpaceshipRampState', () { test('supports value equality', () { expect( - SpaceshipRampState(hits: 0), + SpaceshipRampState( + hits: 0, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.idle, + ), equals( - SpaceshipRampState(hits: 0), + SpaceshipRampState( + hits: 0, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.idle, + ), ), ); }); @@ -17,7 +25,11 @@ void main() { group('constructor', () { test('can be instantiated', () { expect( - SpaceshipRampState(hits: 0), + SpaceshipRampState( + hits: 0, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.idle, + ), isNotNull, ); }); @@ -28,7 +40,11 @@ void main() { 'when hits is negative', () { expect( - () => SpaceshipRampState(hits: -1), + () => SpaceshipRampState( + hits: -1, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.idle, + ), throwsAssertionError, ); }, @@ -39,7 +55,11 @@ void main() { 'throws AssertionError ' 'when hits is decreased', () { - const rampState = SpaceshipRampState(hits: 0); + const rampState = SpaceshipRampState( + hits: 0, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.idle, + ); expect( () => rampState.copyWith(hits: rampState.hits - 1), throwsAssertionError, @@ -51,7 +71,11 @@ void main() { 'copies correctly ' 'when no argument specified', () { - const rampState = SpaceshipRampState(hits: 0); + const rampState = SpaceshipRampState( + hits: 0, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.idle, + ); expect( rampState.copyWith(), equals(rampState), @@ -63,12 +87,24 @@ void main() { 'copies correctly ' 'when all arguments specified', () { - const rampState = SpaceshipRampState(hits: 0); - final otherRampState = SpaceshipRampState(hits: rampState.hits + 1); + const rampState = SpaceshipRampState( + hits: 0, + lightState: ArrowLightState.inactive, + animationState: ArrowAnimationState.idle, + ); + final otherRampState = SpaceshipRampState( + hits: rampState.hits + 1, + lightState: ArrowLightState.active1, + animationState: ArrowAnimationState.blinking, + ); expect(rampState, isNot(equals(otherRampState))); expect( - rampState.copyWith(hits: rampState.hits + 1), + rampState.copyWith( + hits: otherRampState.hits, + lightState: otherRampState.lightState, + animationState: otherRampState.animationState, + ), equals(otherRampState), ); }, diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart index 1c9c968d..614ad718 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart @@ -263,9 +263,7 @@ void main() { ); when(bloc.close).thenAnswer((_) async {}); - final ramp = SpaceshipRamp.test( - bloc: bloc, - ); + final ramp = SpaceshipRamp.test(); await game.ensureAdd(ramp); game.remove(ramp); @@ -332,7 +330,7 @@ void main() { }); flameTester.test('can be loaded', (game) async { - final parent = SpaceshipRamp.test(bloc: _MockSpaceshipRampCubit()); + final parent = SpaceshipRamp.test(); final component = SpaceshipRampBoardOpening(); await game.ensureAdd(parent); await parent.ensureAdd(component); @@ -340,7 +338,7 @@ void main() { }); flameTester.test('adds a RampBallAscendingContactBehavior', (game) async { - final parent = SpaceshipRamp.test(bloc: _MockSpaceshipRampCubit()); + final parent = SpaceshipRamp.test(); final component = SpaceshipRampBoardOpening(); await game.ensureAdd(parent); await parent.ensureAdd(component); 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 cb6c2784..6df83e2a 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 @@ -37,13 +37,19 @@ class _TestGame extends Forge2DGame { Future pump( SpaceshipRamp child, { + required SpaceshipRampCubit spaceshipRampCubit, required GameBloc gameBloc, }) async { await ensureAdd( FlameBlocProvider.value( value: gameBloc, children: [ - ZCanvasComponent(children: [child]), + FlameBlocProvider.value( + value: spaceshipRampCubit, + children: [ + ZCanvasComponent(children: [child]), + ], + ), ], ), ); @@ -54,9 +60,6 @@ class _MockGameBloc extends Mock implements GameBloc {} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} -class _MockStreamSubscription extends Mock - implements StreamSubscription {} - void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -75,22 +78,24 @@ void main() { 'when hits are multiples of 10 times adds a ScoringBehavior', (game) async { final bloc = _MockSpaceshipRampCubit(); + final state = SpaceshipRampState.initial(); final streamController = StreamController(); whenListen( bloc, streamController.stream, - initialState: SpaceshipRampState(hits: 9), + initialState: state, ); final behavior = RampBonusBehavior(points: shotPoints); - final parent = SpaceshipRamp.test(bloc: bloc); + final parent = SpaceshipRamp.test(); await game.pump( parent, gameBloc: gameBloc, + spaceshipRampCubit: bloc, ); await parent.ensureAdd(behavior); - streamController.add(SpaceshipRampState(hits: 10)); + streamController.add(state.copyWith(hits: 10)); final scores = game.descendants().whereType(); await game.ready(); @@ -103,22 +108,24 @@ void main() { "when hits are not multiple of 10 times doesn't add any ScoringBehavior", (game) async { final bloc = _MockSpaceshipRampCubit(); + final state = SpaceshipRampState.initial(); final streamController = StreamController(); whenListen( bloc, streamController.stream, - initialState: SpaceshipRampState.initial(), + initialState: state, ); final behavior = RampBonusBehavior(points: shotPoints); - final parent = SpaceshipRamp.test(bloc: bloc); + final parent = SpaceshipRamp.test(); await game.pump( parent, gameBloc: gameBloc, + spaceshipRampCubit: bloc, ); await parent.ensureAdd(behavior); - streamController.add(SpaceshipRampState(hits: 1)); + streamController.add(state.copyWith(hits: 9)); final scores = game.descendants().whereType(); await game.ready(); @@ -126,38 +133,5 @@ void main() { expect(scores.length, 0); }, ); - - flameTester.test( - 'closes subscription when removed', - (game) 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.pump( - parent, - gameBloc: gameBloc, - ); - 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_multiplier_behavior_test.dart b/test/game/components/android_acres/behaviors/ramp_multiplier_behavior_test.dart new file mode 100644 index 00000000..507ece72 --- /dev/null +++ b/test/game/components/android_acres/behaviors/ramp_multiplier_behavior_test.dart @@ -0,0 +1,184 @@ +// ignore_for_file: cascade_invocations, prefer_const_constructors + +import 'dart:async'; + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +class _TestGame extends Forge2DGame { + @override + Future onLoad() async { + images.prefix = ''; + await images.loadAll([ + Assets.images.android.ramp.boardOpening.keyName, + Assets.images.android.ramp.railingForeground.keyName, + Assets.images.android.ramp.railingBackground.keyName, + Assets.images.android.ramp.main.keyName, + Assets.images.android.ramp.arrow.inactive.keyName, + Assets.images.android.ramp.arrow.active1.keyName, + Assets.images.android.ramp.arrow.active2.keyName, + Assets.images.android.ramp.arrow.active3.keyName, + Assets.images.android.ramp.arrow.active4.keyName, + Assets.images.android.ramp.arrow.active5.keyName, + Assets.images.android.rail.main.keyName, + Assets.images.android.rail.exit.keyName, + Assets.images.score.fiveThousand.keyName, + ]); + } + + Future pump( + SpaceshipRamp child, { + required SpaceshipRampCubit spaceshipRampCubit, + required GameBloc gameBloc, + }) async { + await ensureAdd( + FlameBlocProvider.value( + value: gameBloc, + children: [ + FlameBlocProvider.value( + value: spaceshipRampCubit, + children: [ + ZCanvasComponent(children: [child]), + ], + ), + ], + ), + ); + } +} + +class _MockGameBloc extends Mock implements GameBloc {} + +class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} + +class _FakeGameState extends Fake implements GameState {} + +class _FakeGameEvent extends Fake implements GameEvent {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('RampMultiplierBehavior', () { + late GameBloc gameBloc; + + setUp(() { + registerFallbackValue(_FakeGameState()); + registerFallbackValue(_FakeGameEvent()); + gameBloc = _MockGameBloc(); + }); + + final flameTester = FlameTester(_TestGame.new); + + flameTester.test( + 'adds MultiplierIncreased ' + 'when hits are multiples of 5 times and multiplier is less than 6', + (game) async { + final bloc = _MockSpaceshipRampCubit(); + final event = MultiplierIncreased(); + final state = SpaceshipRampState.initial(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: state, + ); + when(() => gameBloc.state).thenReturn( + GameState.initial().copyWith( + multiplier: 5, + ), + ); + when(() => gameBloc.add(event)).thenAnswer((_) async {}); + + final behavior = RampMultiplierBehavior(); + final parent = SpaceshipRamp.test(); + + await game.pump( + parent, + gameBloc: gameBloc, + spaceshipRampCubit: bloc, + ); + await parent.ensureAdd(behavior); + + streamController.add(state.copyWith(hits: 5)); + + verify(() => gameBloc.add(event)).called(1); + }, + ); + + flameTester.test( + "doesn't add MultiplierIncreased " + 'when hits are multiples of 5 times but multiplier is 6', + (game) async { + final bloc = _MockSpaceshipRampCubit(); + final state = SpaceshipRampState.initial(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: state, + ); + when(() => gameBloc.state).thenReturn( + GameState.initial().copyWith( + multiplier: 6, + ), + ); + + final behavior = RampMultiplierBehavior(); + final parent = SpaceshipRamp.test(); + + await game.pump( + parent, + gameBloc: gameBloc, + spaceshipRampCubit: bloc, + ); + await parent.ensureAdd(behavior); + + streamController.add(state.copyWith(hits: 5)); + + verifyNever(() => gameBloc.add(const MultiplierIncreased())); + }, + ); + + flameTester.test( + "doesn't add MultiplierIncreased " + "when hits aren't multiples of 5 times", + (game) async { + final bloc = _MockSpaceshipRampCubit(); + final state = SpaceshipRampState.initial(); + final streamController = StreamController(); + whenListen( + bloc, + streamController.stream, + initialState: state, + ); + when(() => gameBloc.state).thenReturn( + GameState.initial().copyWith( + multiplier: 5, + ), + ); + + final behavior = RampMultiplierBehavior(); + final parent = SpaceshipRamp.test(); + + await game.pump( + parent, + gameBloc: gameBloc, + spaceshipRampCubit: bloc, + ); + await parent.ensureAdd(behavior); + + streamController.add(state.copyWith(hits: 1)); + + verifyNever(() => gameBloc.add(const MultiplierIncreased())); + }, + ); + }); +} diff --git a/test/game/components/android_acres/behaviors/ramp_reset_behavior_test.dart b/test/game/components/android_acres/behaviors/ramp_reset_behavior_test.dart new file mode 100644 index 00000000..b5174509 --- /dev/null +++ b/test/game/components/android_acres/behaviors/ramp_reset_behavior_test.dart @@ -0,0 +1,131 @@ +// ignore_for_file: cascade_invocations, prefer_const_constructors + +import 'dart:async'; + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +class _TestGame extends Forge2DGame { + @override + Future onLoad() async { + images.prefix = ''; + await images.loadAll([ + Assets.images.android.ramp.boardOpening.keyName, + Assets.images.android.ramp.railingForeground.keyName, + Assets.images.android.ramp.railingBackground.keyName, + Assets.images.android.ramp.main.keyName, + Assets.images.android.ramp.arrow.inactive.keyName, + Assets.images.android.ramp.arrow.active1.keyName, + Assets.images.android.ramp.arrow.active2.keyName, + Assets.images.android.ramp.arrow.active3.keyName, + Assets.images.android.ramp.arrow.active4.keyName, + Assets.images.android.ramp.arrow.active5.keyName, + Assets.images.android.rail.main.keyName, + Assets.images.android.rail.exit.keyName, + Assets.images.score.fiveThousand.keyName, + ]); + } + + Future pump( + SpaceshipRamp child, { + required SpaceshipRampCubit spaceshipRampCubit, + required GameBloc gameBloc, + }) async { + await ensureAdd( + FlameBlocProvider.value( + value: gameBloc, + children: [ + FlameBlocProvider.value( + value: spaceshipRampCubit, + children: [ + ZCanvasComponent(children: [child]), + ], + ), + ], + ), + ); + } +} + +class _MockGameBloc extends Mock implements GameBloc {} + +class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('RampResetBehavior', () { + late GameBloc gameBloc; + + setUp(() { + gameBloc = _MockGameBloc(); + }); + + final flameTester = FlameTester(_TestGame.new); + + flameTester.test( + 'when round lost call onReset', + (game) async { + final bloc = _MockSpaceshipRampCubit(); + final state = GameState.initial(); + final streamController = StreamController(); + whenListen( + gameBloc, + streamController.stream, + initialState: state, + ); + final behavior = RampResetBehavior(); + final parent = SpaceshipRamp.test(); + + await game.pump( + parent, + gameBloc: gameBloc, + spaceshipRampCubit: bloc, + ); + await parent.ensureAdd(behavior); + + streamController.add(state.copyWith(rounds: state.rounds - 1)); + await game.ready(); + + verify(bloc.onReset).called(1); + }, + ); + + flameTester.test( + "when round doesn't change never call onReset", + (game) async { + final bloc = _MockSpaceshipRampCubit(); + final state = GameState.initial(); + final streamController = StreamController(); + whenListen( + gameBloc, + streamController.stream, + initialState: state, + ); + final behavior = RampResetBehavior(); + final parent = SpaceshipRamp.test(); + + await game.pump( + parent, + gameBloc: gameBloc, + spaceshipRampCubit: bloc, + ); + await parent.ensureAdd(behavior); + + streamController + .add(state.copyWith(roundScore: state.roundScore + 100)); + await game.ready(); + + verifyNever(bloc.onReset); + }, + ); + }); +} 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 ae072ea4..3198e8c6 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 @@ -37,13 +37,19 @@ class _TestGame extends Forge2DGame { Future pump( SpaceshipRamp child, { + required SpaceshipRampCubit spaceshipRampCubit, required GameBloc gameBloc, }) async { await ensureAdd( FlameBlocProvider.value( value: gameBloc, children: [ - ZCanvasComponent(children: [child]), + FlameBlocProvider.value( + value: spaceshipRampCubit, + children: [ + ZCanvasComponent(children: [child]), + ], + ), ], ), ); @@ -54,9 +60,6 @@ class _MockGameBloc extends Mock implements GameBloc {} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} -class _MockStreamSubscription extends Mock - implements StreamSubscription {} - void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -69,105 +72,36 @@ void main() { gameBloc = _MockGameBloc(); }); - final flameBlocTester = FlameTester(_TestGame.new); + final flameTester = FlameTester(_TestGame.new); - flameBlocTester.test( - 'when hits are not multiple of 10 times ' - 'increases multiplier and adds a ScoringBehavior', + flameTester.test( + 'when hits adds a ScoringBehavior', (game) async { final bloc = _MockSpaceshipRampCubit(); + final state = SpaceshipRampState.initial(); final streamController = StreamController(); whenListen( bloc, streamController.stream, - initialState: SpaceshipRampState.initial(), + initialState: state, ); final behavior = RampShotBehavior(points: shotPoints); - final parent = SpaceshipRamp.test(bloc: bloc); + final parent = SpaceshipRamp.test(); await game.pump( parent, gameBloc: gameBloc, + spaceshipRampCubit: bloc, ); await parent.ensureAdd(behavior); - streamController.add(SpaceshipRampState(hits: 1)); + streamController.add(state.copyWith(hits: state.hits + 1)); final scores = game.descendants().whereType(); await game.ready(); - verify(() => gameBloc.add(MultiplierIncreased())).called(1); expect(scores.length, 1); }, ); - - flameBlocTester.test( - 'when hits multiple of 10 times ' - "doesn't increase multiplier, neither ScoringBehavior", - (game) async { - final bloc = _MockSpaceshipRampCubit(); - final streamController = StreamController(); - whenListen( - bloc, - streamController.stream, - initialState: SpaceshipRampState(hits: 9), - ); - final behavior = RampShotBehavior( - points: shotPoints, - ); - final parent = SpaceshipRamp.test( - bloc: bloc, - ); - - await game.pump( - parent, - gameBloc: gameBloc, - ); - await parent.ensureAdd(behavior); - - streamController.add(SpaceshipRampState(hits: 10)); - - final scores = game.children.whereType(); - await game.ready(); - - verifyNever(() => gameBloc.add(MultiplierIncreased())); - expect(scores.length, 0); - }, - ); - - flameBlocTester.test( - 'closes subscription when removed', - (game) 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.pump( - parent, - gameBloc: gameBloc, - ); - await parent.ensureAdd(behavior); - - parent.remove(behavior); - await game.ready(); - - verify(subscription.cancel).called(1); - }, - ); }); }