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
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
//log('onChange(${bloc.runtimeType}, $change)');
log('onChange(${bloc.runtimeType}, $change)');
}
@override

@ -38,15 +38,26 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents {
Color backgroundColor() => Colors.white;
late final SpaceshipRamp _spaceshipRamp;
late final SpaceshipRampCubit _bloc;
@override
Future<void> onLoad() async {
await super.onLoad();
camera.followVector2(Vector2(-12, -50));
_spaceshipRamp = SpaceshipRamp();
/*
await add(
_spaceshipRamp = SpaceshipRamp(),
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>(
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;
}

@ -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
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 '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 {}
@ -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());

@ -15,9 +15,133 @@ void main() {
..onAscendingBallEntered()
..onAscendingBallEntered(),
expect: () => [
SpaceshipRampState(hits: 1),
SpaceshipRampState(hits: 2),
SpaceshipRampState(hits: 3),
isA<SpaceshipRampState>().having((state) => state.hits, 'hits', 1),
isA<SpaceshipRampState>().having((state) => state.hits, 'hits', 2),
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', () {
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),
);
},

@ -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);

@ -37,15 +37,21 @@ class _TestGame extends Forge2DGame {
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]),
],
),
],
),
);
}
}
@ -54,9 +60,6 @@ class _MockGameBloc extends Mock implements GameBloc {}
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
class _MockStreamSubscription extends Mock
implements StreamSubscription<SpaceshipRampState> {}
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<SpaceshipRampState>();
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<ScoringBehavior>();
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<SpaceshipRampState>();
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<ScoringBehavior>();
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<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(
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]),
],
),
],
),
);
}
}
@ -54,9 +60,6 @@ class _MockGameBloc extends Mock implements GameBloc {}
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
class _MockStreamSubscription extends Mock
implements StreamSubscription<SpaceshipRampState> {}
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<SpaceshipRampState>();
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<ScoringBehavior>();
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<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