fix: added behaviors to ramp and some tests

pull/416/head
RuiAlonso 3 years ago
parent 615361324e
commit eb5066df88

@ -10,7 +10,7 @@ class AppBlocObserver extends BlocObserver {
@override @override
void onChange(BlocBase bloc, Change change) { void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change); super.onChange(bloc, change);
//log('onChange(${bloc.runtimeType}, $change)'); log('onChange(${bloc.runtimeType}, $change)');
} }
@override @override

@ -38,15 +38,26 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents {
Color backgroundColor() => Colors.white; Color backgroundColor() => Colors.white;
late final SpaceshipRamp _spaceshipRamp; late final SpaceshipRamp _spaceshipRamp;
late final SpaceshipRampCubit _bloc;
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
camera.followVector2(Vector2(-12, -50)); camera.followVector2(Vector2(-12, -50));
_spaceshipRamp = SpaceshipRamp();
/*
await add( await add(
_spaceshipRamp = SpaceshipRamp(), FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>(
create: SpaceshipRampCubit.new,
children: [
SpaceshipRamp(
children: [_spaceshipRamp],
),
],
),
); );
*/
await traceAllBodies(); await traceAllBodies();
} }
@ -57,7 +68,7 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents {
) { ) {
if (event is RawKeyDownEvent && if (event is RawKeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.space) { event.logicalKey == LogicalKeyboardKey.space) {
_spaceshipRamp.bloc.onAscendingBallEntered(); _bloc.onProgressed();
return KeyEventResult.handled; return KeyEventResult.handled;
} }

@ -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<void> 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<void> pump(
SpaceshipRamp child, {
required SpaceshipRampCubit spaceshipRampCubit,
}) async {
await ensureAdd(
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.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<SpaceshipRampState>();
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<SpaceshipRampState>();
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<SpaceshipRampState>();
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<SpaceshipRampState>();
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);
},
);
},
);
}

@ -1,14 +1,47 @@
// ignore_for_file: cascade_invocations // ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart'; import 'package:bloc_test/bloc_test.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_components/src/components/spaceship_ramp/behavior/behavior.dart'; import 'package:pinball_components/src/components/spaceship_ramp/behavior/behavior.dart';
import 'package:pinball_flame/pinball_flame.dart';
import '../../../../helpers/helpers.dart'; class _TestGame extends Forge2DGame {
@override
Future<void> 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<void> pump(
SpaceshipRamp child, {
required SpaceshipRampCubit spaceshipRampCubit,
}) async {
await ensureAdd(
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.value(
value: spaceshipRampCubit,
children: [
ZCanvasComponent(children: [child]),
],
),
);
}
}
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
@ -20,20 +53,8 @@ class _MockContact extends Mock implements Contact {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); 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( group(
'RampBallAscendingContactBehavior', 'RampBallAscendingContactBehavior',
@ -68,14 +89,15 @@ void main() {
); );
final parent = SpaceshipRampBoardOpening.test(); final parent = SpaceshipRampBoardOpening.test();
final spaceshipRamp = SpaceshipRamp.test( final spaceshipRamp = SpaceshipRamp.test();
bloc: bloc,
);
when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); when(() => body.linearVelocity).thenReturn(Vector2(0, -1));
await spaceshipRamp.add(parent); await spaceshipRamp.add(parent);
await game.ensureAddAll([spaceshipRamp, ball]); await game.pump(
spaceshipRamp,
spaceshipRampCubit: bloc,
);
await parent.add(behavior); await parent.add(behavior);
behavior.beginContact(ball, _MockContact()); behavior.beginContact(ball, _MockContact());
@ -96,14 +118,15 @@ void main() {
); );
final parent = SpaceshipRampBoardOpening.test(); final parent = SpaceshipRampBoardOpening.test();
final spaceshipRamp = SpaceshipRamp.test( final spaceshipRamp = SpaceshipRamp.test();
bloc: bloc,
);
when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); when(() => body.linearVelocity).thenReturn(Vector2(0, 1));
await spaceshipRamp.add(parent); await spaceshipRamp.add(parent);
await game.ensureAddAll([spaceshipRamp, ball]); await game.pump(
spaceshipRamp,
spaceshipRampCubit: bloc,
);
await parent.add(behavior); await parent.add(behavior);
behavior.beginContact(ball, _MockContact()); behavior.beginContact(ball, _MockContact());

@ -15,9 +15,133 @@ void main() {
..onAscendingBallEntered() ..onAscendingBallEntered()
..onAscendingBallEntered(), ..onAscendingBallEntered(),
expect: () => [ expect: () => [
SpaceshipRampState(hits: 1), isA<SpaceshipRampState>().having((state) => state.hits, 'hits', 1),
SpaceshipRampState(hits: 2), isA<SpaceshipRampState>().having((state) => state.hits, 'hits', 2),
SpaceshipRampState(hits: 3), isA<SpaceshipRampState>().having((state) => state.hits, 'hits', 3),
],
);
});
group('onReset', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'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<SpaceshipRampState>()
..having((state) => state.hits, 'hits', 0)
..having(
(state) => state.lightState,
'lightState',
ArrowLightState.inactive,
)
..having(
(state) => state.animationState,
'animationState',
ArrowAnimationState.idle,
),
],
);
});
group('onAnimate', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'emits animationState blinking',
build: SpaceshipRampCubit.new,
seed: () => SpaceshipRampState(
hits: 100,
lightState: ArrowLightState.active3,
animationState: ArrowAnimationState.idle,
),
act: (bloc) => bloc.onAnimate(),
expect: () => [
isA<SpaceshipRampState>().having(
(state) => state.animationState,
'animationState',
ArrowAnimationState.blinking,
),
],
);
});
group('onStop', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'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<SpaceshipRampState>()
..having(
(state) => state.lightState,
'lightState',
ArrowLightState.inactive,
)
..having(
(state) => state.animationState,
'animationState',
ArrowAnimationState.idle,
),
],
);
});
group('onBlink', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'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<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active1,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active2,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active3,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active4,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.active5,
),
isA<SpaceshipRampState>().having(
(state) => state.lightState,
'lightState',
ArrowLightState.inactive,
),
], ],
); );
}); });

@ -7,9 +7,17 @@ void main() {
group('SpaceshipRampState', () { group('SpaceshipRampState', () {
test('supports value equality', () { test('supports value equality', () {
expect( expect(
SpaceshipRampState(hits: 0), SpaceshipRampState(
hits: 0,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
),
equals( equals(
SpaceshipRampState(hits: 0), SpaceshipRampState(
hits: 0,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
),
), ),
); );
}); });
@ -17,7 +25,11 @@ void main() {
group('constructor', () { group('constructor', () {
test('can be instantiated', () { test('can be instantiated', () {
expect( expect(
SpaceshipRampState(hits: 0), SpaceshipRampState(
hits: 0,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
),
isNotNull, isNotNull,
); );
}); });
@ -28,7 +40,11 @@ void main() {
'when hits is negative', 'when hits is negative',
() { () {
expect( expect(
() => SpaceshipRampState(hits: -1), () => SpaceshipRampState(
hits: -1,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
),
throwsAssertionError, throwsAssertionError,
); );
}, },
@ -39,7 +55,11 @@ void main() {
'throws AssertionError ' 'throws AssertionError '
'when hits is decreased', 'when hits is decreased',
() { () {
const rampState = SpaceshipRampState(hits: 0); const rampState = SpaceshipRampState(
hits: 0,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
);
expect( expect(
() => rampState.copyWith(hits: rampState.hits - 1), () => rampState.copyWith(hits: rampState.hits - 1),
throwsAssertionError, throwsAssertionError,
@ -51,7 +71,11 @@ void main() {
'copies correctly ' 'copies correctly '
'when no argument specified', 'when no argument specified',
() { () {
const rampState = SpaceshipRampState(hits: 0); const rampState = SpaceshipRampState(
hits: 0,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
);
expect( expect(
rampState.copyWith(), rampState.copyWith(),
equals(rampState), equals(rampState),
@ -63,12 +87,24 @@ void main() {
'copies correctly ' 'copies correctly '
'when all arguments specified', 'when all arguments specified',
() { () {
const rampState = SpaceshipRampState(hits: 0); const rampState = SpaceshipRampState(
final otherRampState = SpaceshipRampState(hits: rampState.hits + 1); 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, isNot(equals(otherRampState)));
expect( expect(
rampState.copyWith(hits: rampState.hits + 1), rampState.copyWith(
hits: otherRampState.hits,
lightState: otherRampState.lightState,
animationState: otherRampState.animationState,
),
equals(otherRampState), equals(otherRampState),
); );
}, },

@ -263,9 +263,7 @@ void main() {
); );
when(bloc.close).thenAnswer((_) async {}); when(bloc.close).thenAnswer((_) async {});
final ramp = SpaceshipRamp.test( final ramp = SpaceshipRamp.test();
bloc: bloc,
);
await game.ensureAdd(ramp); await game.ensureAdd(ramp);
game.remove(ramp); game.remove(ramp);
@ -332,7 +330,7 @@ void main() {
}); });
flameTester.test('can be loaded', (game) async { flameTester.test('can be loaded', (game) async {
final parent = SpaceshipRamp.test(bloc: _MockSpaceshipRampCubit()); final parent = SpaceshipRamp.test();
final component = SpaceshipRampBoardOpening(); final component = SpaceshipRampBoardOpening();
await game.ensureAdd(parent); await game.ensureAdd(parent);
await parent.ensureAdd(component); await parent.ensureAdd(component);
@ -340,7 +338,7 @@ void main() {
}); });
flameTester.test('adds a RampBallAscendingContactBehavior', (game) async { flameTester.test('adds a RampBallAscendingContactBehavior', (game) async {
final parent = SpaceshipRamp.test(bloc: _MockSpaceshipRampCubit()); final parent = SpaceshipRamp.test();
final component = SpaceshipRampBoardOpening(); final component = SpaceshipRampBoardOpening();
await game.ensureAdd(parent); await game.ensureAdd(parent);
await parent.ensureAdd(component); await parent.ensureAdd(component);

@ -37,15 +37,21 @@ class _TestGame extends Forge2DGame {
Future<void> pump( Future<void> pump(
SpaceshipRamp child, { SpaceshipRamp child, {
required SpaceshipRampCubit spaceshipRampCubit,
required GameBloc gameBloc, required GameBloc gameBloc,
}) async { }) async {
await ensureAdd( await ensureAdd(
FlameBlocProvider<GameBloc, GameState>.value( FlameBlocProvider<GameBloc, GameState>.value(
value: gameBloc, value: gameBloc,
children: [
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.value(
value: spaceshipRampCubit,
children: [ children: [
ZCanvasComponent(children: [child]), ZCanvasComponent(children: [child]),
], ],
), ),
],
),
); );
} }
} }
@ -54,9 +60,6 @@ class _MockGameBloc extends Mock implements GameBloc {}
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
class _MockStreamSubscription extends Mock
implements StreamSubscription<SpaceshipRampState> {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
@ -75,22 +78,24 @@ void main() {
'when hits are multiples of 10 times adds a ScoringBehavior', 'when hits are multiples of 10 times adds a ScoringBehavior',
(game) async { (game) async {
final bloc = _MockSpaceshipRampCubit(); final bloc = _MockSpaceshipRampCubit();
final state = SpaceshipRampState.initial();
final streamController = StreamController<SpaceshipRampState>(); final streamController = StreamController<SpaceshipRampState>();
whenListen( whenListen(
bloc, bloc,
streamController.stream, streamController.stream,
initialState: SpaceshipRampState(hits: 9), initialState: state,
); );
final behavior = RampBonusBehavior(points: shotPoints); final behavior = RampBonusBehavior(points: shotPoints);
final parent = SpaceshipRamp.test(bloc: bloc); final parent = SpaceshipRamp.test();
await game.pump( await game.pump(
parent, parent,
gameBloc: gameBloc, gameBloc: gameBloc,
spaceshipRampCubit: bloc,
); );
await parent.ensureAdd(behavior); await parent.ensureAdd(behavior);
streamController.add(SpaceshipRampState(hits: 10)); streamController.add(state.copyWith(hits: 10));
final scores = game.descendants().whereType<ScoringBehavior>(); final scores = game.descendants().whereType<ScoringBehavior>();
await game.ready(); await game.ready();
@ -103,22 +108,24 @@ void main() {
"when hits are not multiple of 10 times doesn't add any ScoringBehavior", "when hits are not multiple of 10 times doesn't add any ScoringBehavior",
(game) async { (game) async {
final bloc = _MockSpaceshipRampCubit(); final bloc = _MockSpaceshipRampCubit();
final state = SpaceshipRampState.initial();
final streamController = StreamController<SpaceshipRampState>(); final streamController = StreamController<SpaceshipRampState>();
whenListen( whenListen(
bloc, bloc,
streamController.stream, streamController.stream,
initialState: SpaceshipRampState.initial(), initialState: state,
); );
final behavior = RampBonusBehavior(points: shotPoints); final behavior = RampBonusBehavior(points: shotPoints);
final parent = SpaceshipRamp.test(bloc: bloc); final parent = SpaceshipRamp.test();
await game.pump( await game.pump(
parent, parent,
gameBloc: gameBloc, gameBloc: gameBloc,
spaceshipRampCubit: bloc,
); );
await parent.ensureAdd(behavior); await parent.ensureAdd(behavior);
streamController.add(SpaceshipRampState(hits: 1)); streamController.add(state.copyWith(hits: 9));
final scores = game.descendants().whereType<ScoringBehavior>(); final scores = game.descendants().whereType<ScoringBehavior>();
await game.ready(); await game.ready();
@ -126,38 +133,5 @@ void main() {
expect(scores.length, 0); expect(scores.length, 0);
}, },
); );
flameTester.test(
'closes subscription when removed',
(game) async {
final bloc = _MockSpaceshipRampCubit();
whenListen(
bloc,
const Stream<SpaceshipRampState>.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);
},
);
}); });
} }

@ -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<void> 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<void> pump(
SpaceshipRamp child, {
required SpaceshipRampCubit spaceshipRampCubit,
required GameBloc gameBloc,
}) async {
await ensureAdd(
FlameBlocProvider<GameBloc, GameState>.value(
value: gameBloc,
children: [
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.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<SpaceshipRampState>();
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<SpaceshipRampState>();
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<SpaceshipRampState>();
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()));
},
);
});
}

@ -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<void> 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<void> pump(
SpaceshipRamp child, {
required SpaceshipRampCubit spaceshipRampCubit,
required GameBloc gameBloc,
}) async {
await ensureAdd(
FlameBlocProvider<GameBloc, GameState>.value(
value: gameBloc,
children: [
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.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<GameState>();
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<GameState>();
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);
},
);
});
}

@ -37,15 +37,21 @@ class _TestGame extends Forge2DGame {
Future<void> pump( Future<void> pump(
SpaceshipRamp child, { SpaceshipRamp child, {
required SpaceshipRampCubit spaceshipRampCubit,
required GameBloc gameBloc, required GameBloc gameBloc,
}) async { }) async {
await ensureAdd( await ensureAdd(
FlameBlocProvider<GameBloc, GameState>.value( FlameBlocProvider<GameBloc, GameState>.value(
value: gameBloc, value: gameBloc,
children: [
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.value(
value: spaceshipRampCubit,
children: [ children: [
ZCanvasComponent(children: [child]), ZCanvasComponent(children: [child]),
], ],
), ),
],
),
); );
} }
} }
@ -54,9 +60,6 @@ class _MockGameBloc extends Mock implements GameBloc {}
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {} class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
class _MockStreamSubscription extends Mock
implements StreamSubscription<SpaceshipRampState> {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
@ -69,105 +72,36 @@ void main() {
gameBloc = _MockGameBloc(); gameBloc = _MockGameBloc();
}); });
final flameBlocTester = FlameTester(_TestGame.new); final flameTester = FlameTester(_TestGame.new);
flameBlocTester.test( flameTester.test(
'when hits are not multiple of 10 times ' 'when hits adds a ScoringBehavior',
'increases multiplier and adds a ScoringBehavior',
(game) async { (game) async {
final bloc = _MockSpaceshipRampCubit(); final bloc = _MockSpaceshipRampCubit();
final state = SpaceshipRampState.initial();
final streamController = StreamController<SpaceshipRampState>(); final streamController = StreamController<SpaceshipRampState>();
whenListen( whenListen(
bloc, bloc,
streamController.stream, streamController.stream,
initialState: SpaceshipRampState.initial(), initialState: state,
); );
final behavior = RampShotBehavior(points: shotPoints); final behavior = RampShotBehavior(points: shotPoints);
final parent = SpaceshipRamp.test(bloc: bloc); final parent = SpaceshipRamp.test();
await game.pump( await game.pump(
parent, parent,
gameBloc: gameBloc, gameBloc: gameBloc,
spaceshipRampCubit: bloc,
); );
await parent.ensureAdd(behavior); await parent.ensureAdd(behavior);
streamController.add(SpaceshipRampState(hits: 1)); streamController.add(state.copyWith(hits: state.hits + 1));
final scores = game.descendants().whereType<ScoringBehavior>(); final scores = game.descendants().whereType<ScoringBehavior>();
await game.ready(); await game.ready();
verify(() => gameBloc.add(MultiplierIncreased())).called(1);
expect(scores.length, 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<SpaceshipRampState>();
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<ScoringBehavior>();
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<SpaceshipRampState>.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);
},
);
}); });
} }

Loading…
Cancel
Save