1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace ArpTest\DominoGame; |
||||
6 | |||||
7 | use Arp\DominoGame\DominoGame; |
||||
8 | use Arp\DominoGame\Exception\DominoGameException; |
||||
9 | use Arp\DominoGame\Value\DominoCollection; |
||||
10 | use Arp\DominoGame\Value\Player; |
||||
11 | use Arp\DominoGame\Value\PlayerCollection; |
||||
12 | use PHPUnit\Framework\MockObject\MockObject; |
||||
13 | use PHPUnit\Framework\TestCase; |
||||
14 | use Psr\Log\LoggerInterface; |
||||
15 | |||||
16 | /** |
||||
17 | * @author Alex Patterson <[email protected]> |
||||
18 | * @package ArpTest\DominoGame |
||||
19 | */ |
||||
20 | final class DominoGameTest extends TestCase |
||||
21 | { |
||||
22 | /** |
||||
23 | * @var PlayerCollection|MockObject |
||||
24 | */ |
||||
25 | private $players; |
||||
26 | |||||
27 | /** |
||||
28 | * @var LoggerInterface|MockObject |
||||
29 | */ |
||||
30 | private $logger; |
||||
31 | |||||
32 | /** |
||||
33 | * Prepare the test case dependencies |
||||
34 | */ |
||||
35 | public function setUp(): void |
||||
36 | { |
||||
37 | $this->players = $this->createMock(PlayerCollection::class); |
||||
38 | |||||
39 | $this->logger = $this->createMock(LoggerInterface::class); |
||||
40 | } |
||||
41 | |||||
42 | /** |
||||
43 | * Assert that if we provide a collection with an invalid number of players to the DominoGame class that a |
||||
44 | * DominoGameException will be thrown with the correct exception message. |
||||
45 | * |
||||
46 | * @param int $playerCount |
||||
47 | * |
||||
48 | * @covers \Arp\DominoGame\DominoGame::__construct |
||||
49 | * @covers \Arp\DominoGame\DominoGame::reset |
||||
50 | * |
||||
51 | * @dataProvider getInvalidPlayerCountWillThrowDominoGameExceptionData |
||||
52 | * |
||||
53 | * @throws DominoGameException |
||||
54 | */ |
||||
55 | public function testInvalidPlayerCountWillThrowDominoGameException(int $playerCount): void |
||||
56 | { |
||||
57 | $this->players->expects($this->once()) |
||||
58 | ->method('count') |
||||
59 | ->willReturn($playerCount); |
||||
60 | |||||
61 | $this->expectException(DominoGameException::class); |
||||
62 | $this->expectExceptionMessage( |
||||
63 | sprintf('There must be a minimum of 1 and a maximum of 4 players; %d provided', $playerCount) |
||||
64 | ); |
||||
65 | |||||
66 | new DominoGame($this->players, $this->logger, 6); |
||||
67 | } |
||||
68 | |||||
69 | /** |
||||
70 | * @return array |
||||
71 | */ |
||||
72 | public function getInvalidPlayerCountWillThrowDominoGameExceptionData(): array |
||||
73 | { |
||||
74 | return [ |
||||
75 | [0], |
||||
76 | [1], |
||||
77 | [5], |
||||
78 | [-12], |
||||
79 | [1000], |
||||
80 | ]; |
||||
81 | } |
||||
82 | |||||
83 | /** |
||||
84 | * Assert that the internal collection of dominoes (the deck) is correctly reset and a new one configured |
||||
85 | * when calling reset(). |
||||
86 | * |
||||
87 | * @param int $expectedCount |
||||
88 | * @param int $maxTileSize |
||||
89 | * |
||||
90 | * @covers \Arp\DominoGame\DominoGame::__construct |
||||
91 | * @covers \Arp\DominoGame\DominoGame::reset |
||||
92 | * @covers \Arp\DominoGame\DominoGame::createDominoCollection |
||||
93 | * |
||||
94 | * @dataProvider getPopulationOfNewBoneyardCollectionWithTheCorrectNumberOfDominoesData |
||||
95 | * |
||||
96 | * @throws DominoGameException |
||||
97 | */ |
||||
98 | public function testPopulationOfNewDeckCollectionWithTheCorrectNumberOfDominoes( |
||||
99 | int $expectedCount, |
||||
100 | int $maxTileSize |
||||
101 | ): void { |
||||
102 | $players = $this->createMockPlayersArray(2); |
||||
103 | $playersCollection = $this->createMockPlayersCollection($players); |
||||
104 | |||||
105 | foreach ($players as $player) { |
||||
106 | /** @var DominoCollection|MockObject $dominoCollection */ |
||||
107 | $dominoCollection = $this->createMock(DominoCollection::class); |
||||
108 | |||||
109 | $player->expects($this->once()) |
||||
110 | ->method('getHand') |
||||
111 | ->willReturn($dominoCollection); |
||||
112 | |||||
113 | $dominoCollection->expects($this->once()) |
||||
114 | ->method('removeElements') |
||||
115 | ->with(null); |
||||
116 | } |
||||
117 | |||||
118 | $game = new DominoGame($playersCollection, $this->logger, $maxTileSize); |
||||
119 | |||||
120 | $this->assertSame($expectedCount, $game->getDeck()->count()); |
||||
121 | } |
||||
122 | |||||
123 | /** |
||||
124 | * @return array |
||||
125 | */ |
||||
126 | public function getPopulationOfNewBoneyardCollectionWithTheCorrectNumberOfDominoesData(): array |
||||
127 | { |
||||
128 | return [ |
||||
129 | [28, 6], |
||||
130 | ]; |
||||
131 | } |
||||
132 | |||||
133 | /** |
||||
134 | * Assert that a $handSize less than 1 will result in a DominoGameException being thrown when calling deal(). |
||||
135 | * |
||||
136 | * @param int $handSize The hand size that should be tested. |
||||
137 | * |
||||
138 | * @covers \Arp\DominoGame\DominoGame::deal |
||||
139 | * @dataProvider getDealWillThrowDominoGameExceptionIfProvidedAHandSizeLessThanOneData |
||||
140 | * |
||||
141 | * @throws DominoGameException |
||||
142 | */ |
||||
143 | public function testDealWillThrowDominoGameExceptionIfProvidedAHandSizeLessThanOne(int $handSize): void |
||||
144 | { |
||||
145 | $players = $this->createMockPlayersArray(2); |
||||
146 | $playersCollection = $this->createMockPlayersCollection($players); |
||||
147 | |||||
148 | $game = new DominoGame($playersCollection, $this->logger, 6); |
||||
149 | |||||
150 | $this->expectException(DominoGameException::class); |
||||
151 | $this->expectExceptionMessage('Hand sizes must be a minimum of 1'); |
||||
152 | |||||
153 | $game->deal($handSize); |
||||
154 | } |
||||
155 | |||||
156 | /** |
||||
157 | * @return array |
||||
158 | */ |
||||
159 | public function getDealWillThrowDominoGameExceptionIfProvidedAHandSizeLessThanOneData(): array |
||||
160 | { |
||||
161 | return [ |
||||
162 | [0], |
||||
163 | [-1], |
||||
164 | [-100], |
||||
165 | [-1212], |
||||
166 | ]; |
||||
167 | } |
||||
168 | |||||
169 | /** |
||||
170 | * Assert that the deal method will throw a DominoGameException if the provided $handSize exceeds the maximum |
||||
171 | * number that is available |
||||
172 | * |
||||
173 | * @param int $handSize The invalid hand size to test |
||||
174 | * |
||||
175 | * @dataProvider getDealWillThrowDominoGameExceptionIfProvidedAHandSizeExceedsAvailableDeckSizeData |
||||
176 | * |
||||
177 | * @throws DominoGameException |
||||
178 | */ |
||||
179 | public function testDealWillThrowDominoGameExceptionIfProvidedAHandSizeExceedsAvailableDeckSize(int $handSize): void |
||||
180 | { |
||||
181 | $players = $this->createMockPlayersArray(2); |
||||
182 | $collection = $this->createMockPlayersCollection($players); |
||||
183 | |||||
184 | $game = new DominoGame($collection, $this->logger, 6); |
||||
185 | |||||
186 | $expectedDeckCount = 28; |
||||
187 | |||||
188 | $this->expectException(DominoGameException::class); |
||||
189 | $this->expectExceptionMessage( |
||||
190 | sprintf( |
||||
191 | 'The hand size %d exceeds the maximum permissible for current deck size of %d', |
||||
192 | $handSize, |
||||
193 | $expectedDeckCount |
||||
194 | ) |
||||
195 | ); |
||||
196 | |||||
197 | $game->deal($handSize); |
||||
198 | } |
||||
199 | |||||
200 | /** |
||||
201 | * @return array |
||||
202 | */ |
||||
203 | public function getDealWillThrowDominoGameExceptionIfProvidedAHandSizeExceedsAvailableDeckSizeData(): array |
||||
204 | { |
||||
205 | return [ |
||||
206 | [28], |
||||
207 | [100], |
||||
208 | ]; |
||||
209 | } |
||||
210 | |||||
211 | /** |
||||
212 | * Assert that when providing a varied $playerCount we are able to correct deal the number of expected |
||||
213 | * dominoes to each one |
||||
214 | * |
||||
215 | * @covers \Arp\DominoGame\DominoGame::deal |
||||
216 | * |
||||
217 | * @param int $playerCount The number of players to test |
||||
218 | * |
||||
219 | * @dataProvider getDealingProvidesTheCorrectNumberOfDominoesToEachPlayerData |
||||
220 | * |
||||
221 | * @throws DominoGameException |
||||
222 | */ |
||||
223 | public function testDealingProvidesTheCorrectNumberOfDominoesToEachPlayer(int $playerCount): void |
||||
224 | { |
||||
225 | $deckSize = 28; |
||||
226 | $handSize = 7; |
||||
227 | |||||
228 | $players = []; |
||||
229 | for ($x = 0; $x < $playerCount; $x++) { |
||||
230 | $players[] = new Player('Player ' . $x); |
||||
231 | } |
||||
232 | |||||
233 | $game = new DominoGame(new PlayerCollection($players), $this->logger, 6); |
||||
234 | |||||
235 | $game->deal($handSize); |
||||
236 | |||||
237 | $deckResult = $game->getDeck(); |
||||
238 | $playersResult = $game->getPlayers(); |
||||
239 | |||||
240 | $expectedDeckCount = $deckSize - ($playerCount * $handSize); |
||||
241 | |||||
242 | $this->assertSame($expectedDeckCount, $deckResult->count()); |
||||
243 | |||||
244 | /** @var Player $player */ |
||||
245 | foreach ($playersResult as $player) { |
||||
246 | $this->assertSame(7, $player->getHandCount()); |
||||
247 | } |
||||
248 | } |
||||
249 | |||||
250 | /** |
||||
251 | * @return array |
||||
252 | */ |
||||
253 | public function getDealingProvidesTheCorrectNumberOfDominoesToEachPlayerData(): array |
||||
254 | { |
||||
255 | return [ |
||||
256 | [2], |
||||
257 | [3], |
||||
258 | [4], |
||||
259 | ]; |
||||
260 | } |
||||
261 | |||||
262 | /** |
||||
263 | * Create an array of player mock objects. |
||||
264 | * |
||||
265 | * @param int $numberOfPlayers |
||||
266 | * |
||||
267 | * @return Player[]|MockObject[] |
||||
268 | */ |
||||
269 | private function createMockPlayersArray(int $numberOfPlayers): array |
||||
270 | { |
||||
271 | /** @var Player[]|MockObject $players */ |
||||
272 | $players = []; |
||||
273 | for ($x = 0; $x < $numberOfPlayers; $x++) { |
||||
274 | $players[] = $this->createMock(Player::class); |
||||
275 | } |
||||
276 | return $players; |
||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||
277 | } |
||||
278 | |||||
279 | /** |
||||
280 | * Create a new player collection mock object with iterable players. |
||||
281 | * |
||||
282 | * @param array|iterable $players |
||||
283 | * |
||||
284 | * @return PlayerCollection|MockObject |
||||
285 | */ |
||||
286 | private function createMockPlayersCollection(iterable $players = []): object |
||||
287 | { |
||||
288 | /** @var PlayerCollection|MockObject $collection */ |
||||
289 | $collection = $this->createMock(PlayerCollection::class); |
||||
290 | |||||
291 | $collection->method('getIterator')->willReturn(new \ArrayIterator($players)); |
||||
0 ignored issues
–
show
It seems like
$players can also be of type iterable ; however, parameter $array of ArrayIterator::__construct() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
292 | $collection->method('count')->willReturn(count($players)); |
||||
293 | |||||
294 | return $collection; |
||||
295 | } |
||||
296 | } |
||||
297 |