mirror of https://github.com/flutter/pinball.git
parent
ae687c4913
commit
96cc57b233
@ -1,28 +0,0 @@
|
|||||||
// ignore_for_file: public_member_api_docs
|
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:pinball_components/pinball_components.dart';
|
|
||||||
|
|
||||||
part 'ramp_sensor_state.dart';
|
|
||||||
|
|
||||||
class RampSensorCubit extends Cubit<RampSensorState> {
|
|
||||||
RampSensorCubit() : super(const RampSensorState.initial());
|
|
||||||
|
|
||||||
void onDoor(Ball ball) {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
type: RampSensorType.door,
|
|
||||||
ball: ball,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onInside(Ball ball) {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
type: RampSensorType.inside,
|
|
||||||
ball: ball,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
// ignore_for_file: public_member_api_docs
|
|
||||||
|
|
||||||
part of 'ramp_sensor_cubit.dart';
|
|
||||||
|
|
||||||
/// Used to know when a [Ball] gets into the [SpaceshipRamp] against every ball
|
|
||||||
/// that crosses the opening.
|
|
||||||
enum RampSensorType {
|
|
||||||
/// Sensor at the entrance of the opening.
|
|
||||||
door,
|
|
||||||
|
|
||||||
/// Sensor inside the [SpaceshipRamp].
|
|
||||||
inside,
|
|
||||||
}
|
|
||||||
|
|
||||||
class RampSensorState {
|
|
||||||
const RampSensorState({
|
|
||||||
required this.type,
|
|
||||||
this.ball,
|
|
||||||
});
|
|
||||||
|
|
||||||
const RampSensorState.initial() : this(type: RampSensorType.door);
|
|
||||||
|
|
||||||
final RampSensorType type;
|
|
||||||
final Ball? ball;
|
|
||||||
|
|
||||||
RampSensorState copyWith({
|
|
||||||
RampSensorType? type,
|
|
||||||
Ball? ball,
|
|
||||||
}) {
|
|
||||||
return RampSensorState(
|
|
||||||
type: type ?? this.type,
|
|
||||||
ball: ball ?? this.ball,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,43 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
part 'spaceship_ramp_state.dart';
|
||||||
|
|
||||||
|
class SpaceshipRampCubit extends Cubit<SpaceshipRampState> {
|
||||||
|
SpaceshipRampCubit() : super(SpaceshipRampState.initial());
|
||||||
|
|
||||||
|
void onDoor(Ball ball) {
|
||||||
|
if (!state.balls.contains(ball)) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
balls: state.balls..add(ball),
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onInside(Ball ball) {
|
||||||
|
if (state.balls.contains(ball)) {
|
||||||
|
final hits = state.hits + 1;
|
||||||
|
final bonus = (hits % 10 == 0)
|
||||||
|
? SpaceshipRampStatus.withBonus
|
||||||
|
: SpaceshipRampStatus.withoutBonus;
|
||||||
|
final shot = hits % 10 != 0;
|
||||||
|
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
hits: hits,
|
||||||
|
balls: state.balls..remove(ball),
|
||||||
|
shot: shot,
|
||||||
|
status: bonus,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
part of 'spaceship_ramp_cubit.dart';
|
||||||
|
|
||||||
|
/// Used to know when a [Ball] gets into the [SpaceshipRamp] against every ball
|
||||||
|
/// that crosses the opening.
|
||||||
|
enum RampSensorType {
|
||||||
|
/// Sensor at the entrance of the opening.
|
||||||
|
door,
|
||||||
|
|
||||||
|
/// Sensor inside the [SpaceshipRamp].
|
||||||
|
inside,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SpaceshipRampStatus {
|
||||||
|
withoutBonus,
|
||||||
|
withBonus,
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpaceshipRampState extends Equatable {
|
||||||
|
const SpaceshipRampState({
|
||||||
|
required this.hits,
|
||||||
|
required this.balls,
|
||||||
|
required this.shot,
|
||||||
|
required this.status,
|
||||||
|
}) : assert(hits >= 0, "Hits can't be negative");
|
||||||
|
|
||||||
|
SpaceshipRampState.initial()
|
||||||
|
: this(
|
||||||
|
hits: 0,
|
||||||
|
balls: HashSet(),
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
);
|
||||||
|
|
||||||
|
final int hits;
|
||||||
|
final Set<Ball> balls;
|
||||||
|
final bool shot;
|
||||||
|
final SpaceshipRampStatus status;
|
||||||
|
|
||||||
|
SpaceshipRampState copyWith({
|
||||||
|
int? hits,
|
||||||
|
Set<Ball>? balls,
|
||||||
|
bool? shot,
|
||||||
|
SpaceshipRampStatus? status,
|
||||||
|
}) {
|
||||||
|
return SpaceshipRampState(
|
||||||
|
hits: hits ?? this.hits,
|
||||||
|
balls: balls ?? this.balls,
|
||||||
|
shot: shot ?? this.shot,
|
||||||
|
status: status ?? this.status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [hits, balls, shot, status];
|
||||||
|
}
|
@ -1,66 +0,0 @@
|
|||||||
// ignore_for_file: prefer_const_constructors
|
|
||||||
|
|
||||||
import 'package:bloc_test/bloc_test.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:pinball_components/pinball_components.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
group('RampSensorCubit', () {
|
|
||||||
final ball = Ball(baseColor: Colors.red);
|
|
||||||
|
|
||||||
blocTest<RampSensorCubit, RampSensorState>(
|
|
||||||
'onDoor emits [door]',
|
|
||||||
build: RampSensorCubit.new,
|
|
||||||
act: (bloc) => bloc.onDoor(ball),
|
|
||||||
expect: () => [
|
|
||||||
isA<RampSensorState>()
|
|
||||||
..having((state) => state.type, 'type', RampSensorType.door)
|
|
||||||
..having((state) => state.ball, 'ball', ball),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
blocTest<RampSensorCubit, RampSensorState>(
|
|
||||||
'onDoor twice emits [door, door]',
|
|
||||||
build: RampSensorCubit.new,
|
|
||||||
act: (bloc) => bloc
|
|
||||||
..onDoor(ball)
|
|
||||||
..onDoor(ball),
|
|
||||||
expect: () => [
|
|
||||||
isA<RampSensorState>()
|
|
||||||
..having((state) => state.type, 'type', RampSensorType.door)
|
|
||||||
..having((state) => state.ball, 'ball', ball),
|
|
||||||
isA<RampSensorState>()
|
|
||||||
..having((state) => state.type, 'type', RampSensorType.door)
|
|
||||||
..having((state) => state.ball, 'ball', ball),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
blocTest<RampSensorCubit, RampSensorState>(
|
|
||||||
'onInside emits [inside]',
|
|
||||||
build: RampSensorCubit.new,
|
|
||||||
act: (bloc) => bloc.onInside(ball),
|
|
||||||
expect: () => [
|
|
||||||
isA<RampSensorState>()
|
|
||||||
..having((state) => state.type, 'type', RampSensorType.inside)
|
|
||||||
..having((state) => state.ball, 'ball', ball),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
blocTest<RampSensorCubit, RampSensorState>(
|
|
||||||
'onInside twice emits [inside,inside]',
|
|
||||||
build: RampSensorCubit.new,
|
|
||||||
act: (bloc) => bloc
|
|
||||||
..onInside(ball)
|
|
||||||
..onInside(ball),
|
|
||||||
expect: () => [
|
|
||||||
isA<RampSensorState>()
|
|
||||||
..having((state) => state.type, 'type', RampSensorType.inside)
|
|
||||||
..having((state) => state.ball, 'ball', ball),
|
|
||||||
isA<RampSensorState>()
|
|
||||||
..having((state) => state.type, 'type', RampSensorType.inside)
|
|
||||||
..having((state) => state.ball, 'ball', ball),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
// ignore_for_file: prefer_const_constructors
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:pinball_components/pinball_components.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
group('RampSensorState', () {
|
|
||||||
test('same states are different even though they have same content', () {
|
|
||||||
final ball = Ball(baseColor: Colors.red);
|
|
||||||
|
|
||||||
final rampSensorState = RampSensorState(
|
|
||||||
type: RampSensorType.door,
|
|
||||||
ball: ball,
|
|
||||||
);
|
|
||||||
final otherRampSensorState = RampSensorState(
|
|
||||||
type: RampSensorType.door,
|
|
||||||
ball: ball,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
rampSensorState,
|
|
||||||
isNot(equals(otherRampSensorState)),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
rampSensorState.type,
|
|
||||||
equals(otherRampSensorState.type),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
rampSensorState.ball,
|
|
||||||
equals(otherRampSensorState.ball),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
group('constructor', () {
|
|
||||||
test('can be instantiated', () {
|
|
||||||
expect(
|
|
||||||
RampSensorState(
|
|
||||||
type: RampSensorType.door,
|
|
||||||
ball: Ball(baseColor: Colors.red),
|
|
||||||
),
|
|
||||||
isNotNull,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('copyWith', () {
|
|
||||||
test(
|
|
||||||
'copies correctly '
|
|
||||||
'when no argument specified',
|
|
||||||
() {
|
|
||||||
final rampSensorState = RampSensorState(
|
|
||||||
type: RampSensorType.door,
|
|
||||||
ball: Ball(baseColor: Colors.red),
|
|
||||||
);
|
|
||||||
|
|
||||||
final copiedRampSensorState = rampSensorState.copyWith();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
copiedRampSensorState.type,
|
|
||||||
equals(rampSensorState.type),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
copiedRampSensorState.ball,
|
|
||||||
equals(rampSensorState.ball),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
test(
|
|
||||||
'copies correctly '
|
|
||||||
'when all arguments specified',
|
|
||||||
() {
|
|
||||||
final ball = Ball(baseColor: Colors.blue);
|
|
||||||
final rampSensorState = RampSensorState(
|
|
||||||
type: RampSensorType.door,
|
|
||||||
ball: Ball(baseColor: Colors.red),
|
|
||||||
);
|
|
||||||
final otherRampSensorState = RampSensorState(
|
|
||||||
type: RampSensorType.inside,
|
|
||||||
ball: ball,
|
|
||||||
);
|
|
||||||
|
|
||||||
final copiedRampSensorState = rampSensorState.copyWith(
|
|
||||||
type: RampSensorType.inside,
|
|
||||||
ball: ball,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
rampSensorState,
|
|
||||||
isNot(equals(otherRampSensorState)),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
copiedRampSensorState.type,
|
|
||||||
equals(otherRampSensorState.type),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
copiedRampSensorState.ball,
|
|
||||||
equals(otherRampSensorState.ball),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -0,0 +1,98 @@
|
|||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SpaceshipRampCubit', () {
|
||||||
|
final ball = Ball(baseColor: Colors.red);
|
||||||
|
|
||||||
|
group('onDoor', () {
|
||||||
|
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
|
||||||
|
'emits nothing if contains ball',
|
||||||
|
build: SpaceshipRampCubit.new,
|
||||||
|
seed: () => SpaceshipRampState.initial().copyWith(
|
||||||
|
balls: {ball},
|
||||||
|
),
|
||||||
|
act: (bloc) => bloc.onDoor(ball),
|
||||||
|
expect: () => <SpaceshipRampState>[],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
|
||||||
|
'emits [{ball}, withoutBonus] if not contains ball',
|
||||||
|
build: SpaceshipRampCubit.new,
|
||||||
|
act: (bloc) => bloc.onDoor(ball),
|
||||||
|
expect: () => [
|
||||||
|
isA<SpaceshipRampState>()
|
||||||
|
..having(
|
||||||
|
(state) => state.balls,
|
||||||
|
'balls',
|
||||||
|
contains(ball),
|
||||||
|
)
|
||||||
|
..having(
|
||||||
|
(state) => state.status,
|
||||||
|
'status',
|
||||||
|
SpaceshipRampStatus.withoutBonus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('onInside', () {
|
||||||
|
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
|
||||||
|
'emits nothing if not contains ball',
|
||||||
|
build: SpaceshipRampCubit.new,
|
||||||
|
seed: () => SpaceshipRampState.initial().copyWith(
|
||||||
|
balls: {},
|
||||||
|
),
|
||||||
|
act: (bloc) => bloc.onInside(ball),
|
||||||
|
expect: () => <SpaceshipRampState>[],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
|
||||||
|
'emits withoutBonus if contains ball '
|
||||||
|
'and hits less than 10 times',
|
||||||
|
build: SpaceshipRampCubit.new,
|
||||||
|
seed: () => SpaceshipRampState.initial().copyWith(
|
||||||
|
hits: 5,
|
||||||
|
balls: {ball},
|
||||||
|
),
|
||||||
|
act: (bloc) => bloc.onInside(ball),
|
||||||
|
expect: () => [
|
||||||
|
isA<SpaceshipRampState>()
|
||||||
|
..having((state) => state.hits, 'hits', 6)
|
||||||
|
..having((state) => state.balls, 'balls', isNot(contains(ball)))
|
||||||
|
..having((state) => state.shot, 'shot', true)
|
||||||
|
..having(
|
||||||
|
(state) => state.status,
|
||||||
|
'status',
|
||||||
|
SpaceshipRampStatus.withoutBonus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
|
||||||
|
'emits withBonus if contains ball and hits 10 times',
|
||||||
|
build: SpaceshipRampCubit.new,
|
||||||
|
seed: () => SpaceshipRampState.initial().copyWith(
|
||||||
|
hits: 9,
|
||||||
|
balls: {ball},
|
||||||
|
),
|
||||||
|
act: (bloc) => bloc.onInside(ball),
|
||||||
|
expect: () => [
|
||||||
|
isA<SpaceshipRampState>()
|
||||||
|
..having((state) => state.hits, 'hits', 10)
|
||||||
|
..having((state) => state.balls, 'balls', contains(ball))
|
||||||
|
..having((state) => state.shot, 'shot', false)
|
||||||
|
..having(
|
||||||
|
(state) => state.status,
|
||||||
|
'status',
|
||||||
|
SpaceshipRampStatus.withBonus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/src/components/components.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SpaceshipRampState', () {
|
||||||
|
test('supports value equality', () {
|
||||||
|
expect(
|
||||||
|
SpaceshipRampState(
|
||||||
|
hits: 0,
|
||||||
|
balls: const {},
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
),
|
||||||
|
equals(
|
||||||
|
SpaceshipRampState(
|
||||||
|
hits: 0,
|
||||||
|
balls: const {},
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('constructor', () {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
SpaceshipRampState(
|
||||||
|
hits: 0,
|
||||||
|
balls: const {},
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
),
|
||||||
|
isNotNull,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'throws AssertionError '
|
||||||
|
'when hits is negative',
|
||||||
|
() {
|
||||||
|
expect(
|
||||||
|
() => SpaceshipRampState(
|
||||||
|
hits: -1,
|
||||||
|
balls: const {},
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
),
|
||||||
|
throwsAssertionError,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
group('copyWith', () {
|
||||||
|
test(
|
||||||
|
'throws AssertionError '
|
||||||
|
'when hits is decreased',
|
||||||
|
() {
|
||||||
|
const rampState = SpaceshipRampState(
|
||||||
|
hits: 0,
|
||||||
|
balls: {},
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
() => rampState.copyWith(hits: rampState.hits - 1),
|
||||||
|
throwsAssertionError,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'copies correctly '
|
||||||
|
'when no argument specified',
|
||||||
|
() {
|
||||||
|
const rampState = SpaceshipRampState(
|
||||||
|
hits: 0,
|
||||||
|
balls: {},
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
rampState.copyWith(),
|
||||||
|
equals(rampState),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'copies correctly '
|
||||||
|
'when all arguments specified',
|
||||||
|
() {
|
||||||
|
final ball = Ball(baseColor: Colors.black);
|
||||||
|
|
||||||
|
const rampState = SpaceshipRampState(
|
||||||
|
hits: 0,
|
||||||
|
balls: {},
|
||||||
|
shot: false,
|
||||||
|
status: SpaceshipRampStatus.withoutBonus,
|
||||||
|
);
|
||||||
|
final otherRampState = SpaceshipRampState(
|
||||||
|
hits: rampState.hits + 1,
|
||||||
|
balls: {ball},
|
||||||
|
shot: true,
|
||||||
|
status: SpaceshipRampStatus.withBonus,
|
||||||
|
);
|
||||||
|
expect(rampState, isNot(equals(otherRampState)));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rampState.copyWith(
|
||||||
|
hits: rampState.hits + 1,
|
||||||
|
balls: {ball},
|
||||||
|
shot: true,
|
||||||
|
status: SpaceshipRampStatus.withBonus,
|
||||||
|
),
|
||||||
|
equals(otherRampState),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue