testFirstPieceIsPlacedAtBeginningOfCollection()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 10
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArpTest\DominoGame\Value;
6
7
use Arp\DominoGame\Exception\DominoGameException;
8
use Arp\DominoGame\Exception\InvalidArgumentException;
9
use Arp\DominoGame\Value\Board;
10
use Arp\DominoGame\Value\Domino;
11
use PHPUnit\Framework\MockObject\MockObject;
12
use PHPUnit\Framework\TestCase;
13
14
/**
15
 * @author  Alex Patterson <[email protected]>
16
 * @package ArpTest\Value\PlayerTest
17
 */
18
class BoardTest extends TestCase
19
{
20
    /**
21
     * Assert that the board will be created with an empty collection of placed pieces.
22
     *
23
     * @covers \Arp\DominoGame\Value\Board::__construct
24
     */
25
    public function testBoardStartsWithZeroPlacedPieces(): void
26
    {
27
        $board = new Board();
28
29
        $this->assertSame(0, $board->getPlaced()->count());
30
    }
31
32
    /**
33
     * Assert that when placing a new domino to an empty collection that is not a 'double' a new
34
     * DominoGameException will be thrown.
35
     *
36
     * @throws DominoGameException
37
     */
38
    public function testPlaceWillThrowDominoGameExceptionIfFirstPieceIsNotADouble(): void
39
    {
40
        $board = new Board();
41
42
        $domino = $this->createDominoMock(1, 2);
43
        $dominoName = '1-2';
44
45
        $domino->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Arp\DominoGame\Value\Domino. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

45
        $domino->/** @scrutinizer ignore-call */ 
46
                 expects($this->once())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
46
            ->method('getName')
47
            ->willReturn($dominoName);
48
49
        $this->expectException(DominoGameException::class);
50
        $this->expectExceptionMessage(
51
            sprintf('The first domino place must be a double \'%s\' provided', $dominoName)
52
        );
53
54
        $board->place($domino);
55
    }
56
57
58
    /**
59
     * Assert that when placing the first piece then the provided domino is added to the start of the collection.
60
     *
61
     * @covers \Arp\DominoGame\Value\Board::place
62
     *
63
     * @throws DominoGameException
64
     */
65
    public function testFirstPieceIsPlacedAtBeginningOfCollection(): void
66
    {
67
        $board = new Board();
68
69
        /** @var Domino|MockObject $domino */
70
        $domino = $this->createDominoMock(5, 5);
71
72
        $board->place($domino);
73
74
        $this->assertSame($domino, $board->getPlaced()->first());
75
    }
76
77
    /**
78
     * Assert that when calling getLeft() or getRight() after placing our first piece then the expected
79
     * placed domino is returned.
80
     *
81
     * @covers \Arp\DominoGame\Value\Board::place
82
     * @covers \Arp\DominoGame\Value\Board::getRight
83
     * @covers \Arp\DominoGame\Value\Board::getRightTile
84
     * @covers \Arp\DominoGame\Value\Board::getLeft
85
     * @covers \Arp\DominoGame\Value\Board::getLeftTile
86
     *
87
     * @throws DominoGameException
88
     */
89
    public function testGetLeftAndGetRightValuesAreCorrectForFirstPlacedPiece(): void
90
    {
91
        $board = new Board();
92
93
        /** @var Domino|MockObject $domino */
94
        $domino = $this->createDominoMock(5, 5);
95
96
        $board->place($domino);
97
98
        $this->assertSame($domino, $board->getLeft());
99
        $this->assertSame($domino, $board->getRight());
100
101
        $this->assertSame(5, $board->getLeftTile());
102
        $this->assertSame(5, $board->getRightTile());
103
    }
104
105
    /**
106
     * Assert that if we attempt to place() a domino to the board that doesn't match an exposed tile then a new
107
     * DominoGameException is thrown.
108
     *
109
     * @covers \Arp\DominoGame\Value\Board::place
110
     *
111
     * @throws DominoGameException
112
     */
113
    public function testPlacementOfNonMatchingPieceThrowsInvalidArgumentException(): void
114
    {
115
        $board = new Board();
116
117
        $firstDomino = $this->createDominoMock(1, 1);
118
119
        $board->place($firstDomino);
120
121
        $nonMatchDomino = $this->createDominoMock(2, 2);
122
123
        $nonMatchDomino->expects($this->once())
124
            ->method('getName')
125
            ->willReturn('2-2');
126
127
        $this->expectException(InvalidArgumentException::class);
128
        $this->expectExceptionMessage(
129
            sprintf(
130
                'The domino \'%s\' cannot be placed on the board; tiles must match values \'%d\' or \'%d\'',
131
                '2-2',
132
                1,
133
                1
134
            )
135
        );
136
137
        $board->place($nonMatchDomino);
138
    }
139
140
    /**
141
     * Assert that when we call place() on a non-empty board we expect the provided domino to be added to the top/left.
142
     *
143
     * @param int $start  The starting double that should be placed.
144
     * @param int $top    The value of the top tile to place.
145
     * @param int $bottom The value of the bottom tile to place.
146
     *
147
     * @dataProvider getLeftPlacedDominoIsAddedToTheBeginningOfANonEmptyDominoCollectionData
148
     *
149
     * @covers       \Arp\DominoGame\Value\Board::place
150
     * @covers       \Arp\DominoGame\Value\Board::placeLeft
151
     *
152
     * @throws DominoGameException
153
     */
154
    public function testLeftPlacedDominoIsAddedToTheBeginningOfANonEmptyDominoCollection(
155
        int $start,
156
        int $top,
157
        int $bottom
158
    ): void {
159
        $board = new Board();
160
161
        // We need to first make the collection non-empty
162
        $firstDomino = new Domino($start, $start);
163
        $board->place($firstDomino);
164
165
        // We expect to add this to the left of the placed dominoes...
166
        $leftPlaceDomino = new Domino($top, $bottom);
167
        $board->place($leftPlaceDomino);
168
169
        $leftTile = $board->getLeftTile();
170
        if ($top === $start) {
171
            $this->assertSame($leftTile, $bottom);
172
        } elseif ($bottom === $start) {
173
            $this->assertSame($leftTile, $top);
174
        } else {
175
            $this->fail(sprintf('The $start value \'%d\' should match either $top or $bottom', $start));
176
        }
177
178
        $this->assertSame($board->getLeft(), $leftPlaceDomino);
179
    }
180
181
    /**
182
     * @return array
183
     */
184
    public function getLeftPlacedDominoIsAddedToTheBeginningOfANonEmptyDominoCollectionData(): array
185
    {
186
        return [
187
            [5, 5, 3],
188
            [5, 3, 5],
189
190
            [1, 1, 3],
191
            [1, 3, 1],
192
193
            [0, 0, 3],
194
            [0, 3, 0],
195
        ];
196
    }
197
198
    /**
199
     * Assert that we can place matching tiles to the 'right' of the collection.
200
     *
201
     * @covers \Arp\DominoGame\Value\Board::place
202
     * @covers \Arp\DominoGame\Value\Board::placeRight
203
     *
204
     * @throws InvalidArgumentException
205
     */
206
    public function testPlaceRightTileWithNonEmptyDeck(): void
207
    {
208
        $board = new Board();
209
210
        /**
211
         * @var Domino $domino1
212
         * @var Domino $domino2
213
         */
214
        $domino1 = new Domino(2, 2);
215
        $domino2 = new Domino(2, 3);
216
        $domino3 = new Domino(2, 5);
217
218
        $board->place($domino1);
219
        $board->place($domino2);
220
        $board->place($domino3);
221
222
        $rightDomino = new Domino(1, 5);
223
224
        $board->place($rightDomino);
225
226
        $this->assertSame(3, $board->getLeftTile());
227
        $this->assertSame(1, $board->getRightTile());
228
    }
229
230
    /**
231
     * Create a mock domino instance that will return the provided tile values.
232
     *
233
     * @param int $top
234
     * @param int $bottom
235
     *
236
     * @return Domino|MockObject
237
     */
238
    private function createDominoMock(int $top, int $bottom): Domino
239
    {
240
        /** @var Domino|MockObject $domino */
241
        $domino = $this->createMock(Domino::class);
242
243
        $domino->method('getTopTile')->willReturn($top);
244
        $domino->method('getBottomTile')->willReturn($bottom);
245
        $domino->method('isDouble')->willReturn($top === $bottom);
246
247
        return $domino;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $domino returns the type PHPUnit\Framework\MockObject\MockObject which is incompatible with the type-hinted return Arp\DominoGame\Value\Domino.
Loading history...
248
    }
249
}
250