Passed
Pull Request — master (#50)
by Marco
02:46
created

testExecuteFailsIfCheckedOutRepositoryDoesNotExist()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 42
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 33
nc 1
nop 0
dl 0
loc 42
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace RoaveTest\BackwardCompatibility\Command;
6
7
use Assert\InvalidArgumentException;
8
use PHPUnit\Framework\MockObject\MockObject;
9
use PHPUnit\Framework\TestCase;
10
use Roave\BackwardCompatibility\Change;
11
use Roave\BackwardCompatibility\Changes;
12
use Roave\BackwardCompatibility\Command\AssertBackwardsCompatible;
13
use Roave\BackwardCompatibility\Comparator;
14
use Roave\BackwardCompatibility\Factory\DirectoryReflectorFactory;
15
use Roave\BackwardCompatibility\Git\CheckedOutRepository;
16
use Roave\BackwardCompatibility\Git\GetVersionCollection;
17
use Roave\BackwardCompatibility\Git\ParseRevision;
18
use Roave\BackwardCompatibility\Git\PerformCheckoutOfRevision;
19
use Roave\BackwardCompatibility\Git\PickVersionFromVersionCollection;
20
use Roave\BackwardCompatibility\Git\Revision;
21
use Roave\BackwardCompatibility\LocateDependencies\LocateDependencies;
22
use Roave\BetterReflection\BetterReflection;
23
use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
24
use Symfony\Component\Console\Input\InputInterface;
25
use Symfony\Component\Console\Output\ConsoleOutputInterface;
26
use Symfony\Component\Console\Output\OutputInterface;
27
use Version\Version;
28
use Version\VersionsCollection;
29
use function chdir;
30
use function realpath;
31
use function sha1;
32
use function uniqid;
33
34
/**
35
 * @covers \Roave\BackwardCompatibility\Command\AssertBackwardsCompatible
36
 */
37
final class AssertBackwardsCompatibleTest extends TestCase
38
{
39
    /** @var CheckedOutRepository */
40
    private $sourceRepository;
41
42
    /** @var InputInterface|MockObject */
43
    private $input;
44
45
    /** @var ConsoleOutputInterface|MockObject */
46
    private $output;
47
48
    /** @var OutputInterface|MockObject */
49
    private $stdErr;
50
51
    /** @var PerformCheckoutOfRevision|MockObject */
52
    private $performCheckout;
53
54
    /** @var ParseRevision|MockObject */
55
    private $parseRevision;
56
57
    /** @var GetVersionCollection|MockObject */
58
    private $getVersions;
59
60
    /** @var PickVersionFromVersionCollection|MockObject */
61
    private $pickVersion;
62
63
    /** @var LocateDependencies|MockObject */
64
    private $locateDependencies;
65
66
    /** @var Comparator|MockObject */
67
    private $comparator;
68
69
    /** @var AggregateSourceLocator */
70
    private $dependencies;
71
72
    /** @var AssertBackwardsCompatible */
73
    private $compare;
74
75
    public function setUp() : void
76
    {
77
        $this->sourceRepository = CheckedOutRepository::fromPath(realpath(__DIR__ . '/../../../'));
78
        chdir((string) $this->sourceRepository);
79
80
        $this->input              = $this->createMock(InputInterface::class);
81
        $this->output             = $this->createMock(ConsoleOutputInterface::class);
82
        $this->stdErr             = $this->createMock(OutputInterface::class);
83
        $this->performCheckout    = $this->createMock(PerformCheckoutOfRevision::class);
84
        $this->parseRevision      = $this->createMock(ParseRevision::class);
85
        $this->getVersions        = $this->createMock(GetVersionCollection::class);
86
        $this->pickVersion        = $this->createMock(PickVersionFromVersionCollection::class);
87
        $this->locateDependencies = $this->createMock(LocateDependencies::class);
88
        $this->dependencies       = new AggregateSourceLocator();
89
        $this->comparator         = $this->createMock(Comparator::class);
90
        $this->compare            = new AssertBackwardsCompatible(
91
            $this->performCheckout,
92
            new DirectoryReflectorFactory((new BetterReflection())->astLocator()),
93
            $this->parseRevision,
94
            $this->getVersions,
95
            $this->pickVersion,
96
            $this->locateDependencies,
97
            $this->comparator
98
        );
99
100
        $this
101
            ->output
102
            ->expects(self::any())
103
            ->method('getErrorOutput')
104
            ->willReturn($this->stdErr);
105
    }
106
107
    public function testExecuteWhenRevisionsAreProvidedAsOptions() : void
108
    {
109
        $fromSha = sha1('fromRevision', false);
110
        $toSha   = sha1('toRevision', false);
111
112
        $this->input->expects(self::any())->method('hasOption')->willReturn(true);
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Symfony\Component\Console\Input\InputInterface. ( Ignorable by Annotation )

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

112
        $this->input->/** @scrutinizer ignore-call */ 
113
                      expects(self::any())->method('hasOption')->willReturn(true);

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...
113
        $this->input->expects(self::any())->method('getOption')->willReturnMap([
114
            ['from', $fromSha],
115
            ['to', $toSha],
116
        ]);
117
        $this->input->expects(self::any())->method('getArgument')->willReturnMap([
118
            ['sources-path', 'src'],
119
        ]);
120
121
        $this->performCheckout->expects(self::at(0))
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Roave\BackwardCompatibil...rformCheckoutOfRevision. ( Ignorable by Annotation )

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

121
        $this->performCheckout->/** @scrutinizer ignore-call */ 
122
                                expects(self::at(0))

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...
122
            ->method('checkout')
123
            ->with($this->sourceRepository, $fromSha)
124
            ->willReturn($this->sourceRepository);
125
        $this->performCheckout->expects(self::at(1))
126
            ->method('checkout')
127
            ->with($this->sourceRepository, $toSha)
128
            ->willReturn($this->sourceRepository);
129
        $this->performCheckout->expects(self::at(2))
130
            ->method('remove')
131
            ->with($this->sourceRepository);
132
        $this->performCheckout->expects(self::at(3))
133
            ->method('remove')
134
            ->with($this->sourceRepository);
135
136
        $this->parseRevision->expects(self::at(0))
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Roave\BackwardCompatibility\Git\ParseRevision. ( Ignorable by Annotation )

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

136
        $this->parseRevision->/** @scrutinizer ignore-call */ 
137
                              expects(self::at(0))

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...
137
            ->method('fromStringForRepository')
138
            ->with($fromSha)
139
            ->willReturn(Revision::fromSha1($fromSha));
140
        $this->parseRevision->expects(self::at(1))
141
            ->method('fromStringForRepository')
142
            ->with($toSha)
143
            ->willReturn(Revision::fromSha1($toSha));
144
145
        $this
146
            ->locateDependencies
147
            ->expects(self::any())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Roave\BackwardCompatibil...cies\LocateDependencies. ( Ignorable by Annotation )

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

147
            ->/** @scrutinizer ignore-call */ 
148
              expects(self::any())

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...
148
            ->method('__invoke')
149
            ->with((string) $this->sourceRepository)
150
            ->willReturn($this->dependencies);
151
152
        $this->comparator->expects(self::once())->method('compare')->willReturn(Changes::empty());
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Roave\BackwardCompatibility\Comparator. ( Ignorable by Annotation )

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

152
        $this->comparator->/** @scrutinizer ignore-call */ 
153
                           expects(self::once())->method('compare')->willReturn(Changes::empty());

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...
153
154
        self::assertSame(0, $this->compare->execute($this->input, $this->output));
155
    }
156
157
    public function testExecuteReturnsNonZeroExitCodeWhenChangesAreDetected() : void
158
    {
159
        $fromSha = sha1('fromRevision', false);
160
        $toSha   = sha1('toRevision', false);
161
162
        $this->input->expects(self::any())->method('hasOption')->willReturn(true);
163
        $this->input->expects(self::any())->method('getOption')->willReturnMap([
164
            ['from', $fromSha],
165
            ['to', $toSha],
166
        ]);
167
        $this->input->expects(self::any())->method('getArgument')->willReturnMap([
168
            ['sources-path', 'src'],
169
        ]);
170
171
        $this->performCheckout->expects(self::at(0))
172
            ->method('checkout')
173
            ->with($this->sourceRepository, $fromSha)
174
            ->willReturn($this->sourceRepository);
175
        $this->performCheckout->expects(self::at(1))
176
            ->method('checkout')
177
            ->with($this->sourceRepository, $toSha)
178
            ->willReturn($this->sourceRepository);
179
        $this->performCheckout->expects(self::at(2))
180
            ->method('remove')
181
            ->with($this->sourceRepository);
182
        $this->performCheckout->expects(self::at(3))
183
            ->method('remove')
184
            ->with($this->sourceRepository);
185
186
        $this->parseRevision->expects(self::at(0))
187
            ->method('fromStringForRepository')
188
            ->with($fromSha)
189
            ->willReturn(Revision::fromSha1($fromSha));
190
        $this->parseRevision->expects(self::at(1))
191
            ->method('fromStringForRepository')
192
            ->with($toSha)
193
            ->willReturn(Revision::fromSha1($toSha));
194
195
        $this
196
            ->locateDependencies
197
            ->expects(self::any())
198
            ->method('__invoke')
199
            ->with((string) $this->sourceRepository)
200
            ->willReturn($this->dependencies);
201
202
        $this->comparator->expects(self::once())->method('compare')->willReturn(Changes::fromList(
203
            Change::added(uniqid('added', true), true)
204
        ));
205
206
        $this
207
            ->stdErr
208
            ->expects(self::exactly(3))
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Symfony\Component\Console\Output\OutputInterface. ( Ignorable by Annotation )

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

208
            ->/** @scrutinizer ignore-call */ 
209
              expects(self::exactly(3))

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...
209
            ->method('writeln')
210
            ->with(self::logicalOr(
211
                self::matches('Comparing from %a to %a...'),
212
                self::matches('[BC] ADDED: added%a'),
213
                self::matches('<error>1 backwards-incompatible changes detected</error>')
214
            ));
215
216
        self::assertSame(1, $this->compare->execute($this->input, $this->output));
217
    }
218
219
    public function testProvidingMarkdownOptionWritesMarkdownOutput() : void
220
    {
221
        $fromSha = sha1('fromRevision', false);
222
        $toSha   = sha1('toRevision', false);
223
224
        $this->input->expects(self::any())->method('hasOption')->willReturn(true);
225
        $this->input->expects(self::any())->method('getOption')->willReturnMap([
226
            ['from', $fromSha],
227
            ['to', $toSha],
228
            ['format', ['markdown']],
229
        ]);
230
        $this->input->expects(self::any())->method('getArgument')->willReturnMap([
231
            ['sources-path', 'src'],
232
        ]);
233
234
        $this->performCheckout->expects(self::at(0))
235
            ->method('checkout')
236
            ->with($this->sourceRepository, $fromSha)
237
            ->willReturn($this->sourceRepository);
238
        $this->performCheckout->expects(self::at(1))
239
            ->method('checkout')
240
            ->with($this->sourceRepository, $toSha)
241
            ->willReturn($this->sourceRepository);
242
        $this->performCheckout->expects(self::at(2))
243
            ->method('remove')
244
            ->with($this->sourceRepository);
245
        $this->performCheckout->expects(self::at(3))
246
            ->method('remove')
247
            ->with($this->sourceRepository);
248
249
        $this->parseRevision->expects(self::at(0))
250
            ->method('fromStringForRepository')
251
            ->with($fromSha)
252
            ->willReturn(Revision::fromSha1($fromSha));
253
        $this->parseRevision->expects(self::at(1))
254
            ->method('fromStringForRepository')
255
            ->with($toSha)
256
            ->willReturn(Revision::fromSha1($toSha));
257
258
        $this
259
            ->locateDependencies
260
            ->expects(self::any())
261
            ->method('__invoke')
262
            ->with((string) $this->sourceRepository)
263
            ->willReturn($this->dependencies);
264
265
        $changeToExpect = uniqid('changeToExpect', true);
266
        $this->comparator->expects(self::once())->method('compare')->willReturn(Changes::fromList(
267
            Change::removed($changeToExpect, true)
268
        ));
269
270
        $this->compare->execute($this->input, $this->output);
271
272
        $this->output->expects(self::any())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Symfony\Component\Consol...\ConsoleOutputInterface. ( Ignorable by Annotation )

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

272
        $this->output->/** @scrutinizer ignore-call */ 
273
                       expects(self::any())

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...
273
            ->method('writeln')
274
            ->willReturnCallback(function (string $output) use ($changeToExpect) : void {
275
                self::assertContains($changeToExpect, $output);
276
            });
277
    }
278
279
    public function testExecuteWithDefaultRevisionsNotProvided() : void
280
    {
281
        $fromSha       = sha1('fromRevision', false);
282
        $toSha         = sha1('toRevision', false);
283
        $versions      = VersionsCollection::fromArray(['1.0.0', '1.0.1']);
284
        $pickedVersion = Version::fromString('1.0.0');
285
286
        $this->input->expects(self::any())->method('hasOption')->willReturn(false);
287
        $this->input->expects(self::any())->method('getOption')->willReturnMap([
288
            ['from', null],
289
            ['to', 'HEAD'],
290
        ]);
291
        $this->input->expects(self::any())->method('getArgument')->willReturnMap([
292
            ['sources-path', 'src'],
293
        ]);
294
295
        $this->performCheckout->expects(self::at(0))
296
            ->method('checkout')
297
            ->with($this->sourceRepository, $fromSha)
298
            ->willReturn($this->sourceRepository);
299
        $this->performCheckout->expects(self::at(1))
300
            ->method('checkout')
301
            ->with($this->sourceRepository, $toSha)
302
            ->willReturn($this->sourceRepository);
303
        $this->performCheckout->expects(self::at(2))
304
            ->method('remove')
305
            ->with($this->sourceRepository);
306
        $this->performCheckout->expects(self::at(3))
307
            ->method('remove')
308
            ->with($this->sourceRepository);
309
310
        $this->parseRevision->expects(self::at(0))
311
            ->method('fromStringForRepository')
312
            ->with((string) $pickedVersion)
313
            ->willReturn(Revision::fromSha1($fromSha));
314
        $this->parseRevision->expects(self::at(1))
315
            ->method('fromStringForRepository')
316
            ->with('HEAD')
317
            ->willReturn(Revision::fromSha1($toSha));
318
319
        $this->getVersions->expects(self::once())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Roave\BackwardCompatibil...it\GetVersionCollection. ( Ignorable by Annotation )

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

319
        $this->getVersions->/** @scrutinizer ignore-call */ 
320
                            expects(self::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...
320
            ->method('fromRepository')
321
            ->with(self::callback(function (CheckedOutRepository $checkedOutRepository) : bool {
322
                self::assertEquals($this->sourceRepository, $checkedOutRepository);
323
                return true;
324
            }))
325
            ->willReturn($versions);
326
        $this->pickVersion->expects(self::once())
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Roave\BackwardCompatibil...onFromVersionCollection. ( Ignorable by Annotation )

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

326
        $this->pickVersion->/** @scrutinizer ignore-call */ 
327
                            expects(self::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...
327
            ->method('forVersions')
328
            ->with($versions)
329
            ->willReturn($pickedVersion);
330
331
        $this
332
            ->stdErr
333
            ->expects(self::exactly(3))
334
            ->method('writeln')
335
            ->with(self::logicalOr(
336
                'Detected last minor version: 1.0.0',
337
                self::matches('Comparing from %a to %a...'),
338
                self::matches('<info>No backwards-incompatible changes detected</info>')
339
            ));
340
341
        $this->comparator->expects(self::once())->method('compare')->willReturn(Changes::empty());
342
343
        self::assertSame(0, $this->compare->execute($this->input, $this->output));
344
    }
345
346
    public function testExecuteFailsIfCheckedOutRepositoryDoesNotExist() : void
347
    {
348
        $fromSha = sha1('fromRevision', false);
349
        $toSha   = sha1('toRevision', false);
350
351
        $this->input->expects(self::any())->method('hasOption')->willReturn(true);
352
        $this->input->expects(self::any())->method('getOption')->willReturnMap([
353
            ['from', $fromSha],
354
            ['to', $toSha],
355
        ]);
356
        $this->input->expects(self::any())->method('getArgument')->willReturnMap([
357
            ['sources-path', uniqid('src', true)],
358
        ]);
359
360
        $this->performCheckout->expects(self::at(0))
361
            ->method('checkout')
362
            ->with($this->sourceRepository, $fromSha)
363
            ->willReturn($this->sourceRepository);
364
        $this->performCheckout->expects(self::at(1))
365
            ->method('checkout')
366
            ->with($this->sourceRepository, $toSha)
367
            ->willReturn($this->sourceRepository);
368
        $this->performCheckout->expects(self::at(2))
369
            ->method('remove')
370
            ->with($this->sourceRepository);
371
        $this->performCheckout->expects(self::at(3))
372
            ->method('remove')
373
            ->with($this->sourceRepository);
374
375
        $this->parseRevision->expects(self::at(0))
376
            ->method('fromStringForRepository')
377
            ->with($fromSha)
378
            ->willReturn(Revision::fromSha1($fromSha));
379
        $this->parseRevision->expects(self::at(1))
380
            ->method('fromStringForRepository')
381
            ->with($toSha)
382
            ->willReturn(Revision::fromSha1($toSha));
383
384
        $this->comparator->expects(self::never())->method('compare');
385
386
        $this->expectException(InvalidArgumentException::class);
387
        $this->compare->execute($this->input, $this->output);
388
    }
389
}
390