getInvalidPlayerCountWillThrowDominoGameExceptionData()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 8
rs 10
cc 1
nc 1
nop 0
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
The expression return $players returns the type Arp\DominoGame\Value\Pla...k\MockObject\MockObject which is incompatible with the type-hinted return array.
Loading history...
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
Bug introduced by
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 ignore-type  annotation

291
        $collection->method('getIterator')->willReturn(new \ArrayIterator(/** @scrutinizer ignore-type */ $players));
Loading history...
292
        $collection->method('count')->willReturn(count($players));
293
294
        return $collection;
295
    }
296
}
297