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