AssertBackwardsCompatibleTest   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 124
c 5
b 0
f 1
dl 0
loc 278
rs 10
wmc 9

8 Methods

Rating   Name   Duplication   Size   Complexity  
A testWillRunSuccessfullyOnNoBcBreaks() 0 16 1
A testWillPickLatestTaggedVersionOnNoGivenFrom() 0 26 1
A testWillAllowSpecifyingGitRevision() 0 20 1
A testWillPickTaggedVersionOnNoGivenFrom() 0 25 1
A testWillNotRunWithoutTagsNorSpecifiedVersions() 0 11 1
A tagOnVersion() 0 21 1
A setUp() 0 31 2
A tearDown() 0 9 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace RoaveE2ETest\BackwardCompatibility\Command;
6
7
use PHPUnit\Framework\TestCase;
8
use Symfony\Component\Process\Process;
9
10
/**
11
 * @coversNothing
12
 */
13
final class AssertBackwardsCompatibleTest extends TestCase
14
{
15
    private const COMPOSER_MANIFEST = <<<'JSON'
16
{
17
    "autoload": {
18
        "classmap": [
19
            "src/"
20
        ]
21
    },
22
    "repositories": [
23
        {
24
            "packagist.org": false
25
        }
26
    ]
27
}
28
29
JSON;
30
31
    private const CLASS_VERSIONS = [
32
        <<<'PHP'
33
<?php
34
35
namespace TestArtifact;
36
37
interface A {}
38
interface B {}
39
interface C {}
40
41
final class TheClass
42
{
43
    public function method(A $a)
44
    {
45
    }
46
}
47
48
PHP
49
        ,
50
        <<<'PHP'
51
<?php
52
53
namespace TestArtifact;
54
55
interface A {}
56
interface B {}
57
interface C {}
58
59
final class TheClass
60
{
61
    public function method(B $a)
62
    {
63
    }
64
}
65
66
PHP
67
        ,
68
        <<<'PHP'
69
<?php
70
71
namespace TestArtifact;
72
73
interface A {}
74
interface B {}
75
interface C {}
76
77
final class TheClass
78
{
79
    public function method(C $a)
80
    {
81
    }
82
}
83
84
PHP
85
        ,
86
        // The last version resets the class to its initial state
87
        <<<'PHP'
88
<?php
89
90
namespace TestArtifact;
91
92
interface A {}
93
interface B {}
94
interface C {}
95
96
final class TheClass
97
{
98
    public function method(A $a)
99
    {
100
    }
101
}
102
103
PHP
104
    ];
105
106
    /** @var string path to the sources that should be checked */
107
    private $sourcesRepository;
108
109
    /** @var string[] sha1 of the source versions */
110
    private $versions = [];
111
112
    protected function setUp() : void
113
    {
114
        parent::setUp();
115
116
        $this->sourcesRepository = tempnam(sys_get_temp_dir(), 'roave-backward-compatibility-e2e-test');
117
118
        self::assertIsString($this->sourcesRepository);
119
        self::assertNotEmpty($this->sourcesRepository);
120
        self::assertFileExists($this->sourcesRepository);
121
122
        unlink($this->sourcesRepository);
123
        mkdir($this->sourcesRepository);
124
        mkdir($this->sourcesRepository . '/src');
125
126
        self::assertDirectoryExists($this->sourcesRepository);
127
        self::assertDirectoryExists($this->sourcesRepository . '/src');
128
129
        (new Process(['git', 'init'], $this->sourcesRepository))->mustRun();
130
131
        file_put_contents($this->sourcesRepository . '/composer.json', self::COMPOSER_MANIFEST);
132
133
        (new Process(['git', 'add', '-A'], $this->sourcesRepository))->mustRun();
134
        (new Process(['git', 'commit', '-am', 'Initial commit with composer manifest'], $this->sourcesRepository))->mustRun();
135
136
        foreach (self::CLASS_VERSIONS as $key => $classCode) {
137
            file_put_contents($this->sourcesRepository . '/src/TheClass.php', $classCode);
138
139
            (new Process(['git', 'add', '-A'], $this->sourcesRepository))->mustRun();
140
            (new Process(['git', 'commit', '-am', sprintf('Class sources v%d', $key + 1)], $this->sourcesRepository))->mustRun();
141
            $this->versions[$key] = trim((new Process(['git', 'rev-parse', 'HEAD'], $this->sourcesRepository))->mustRun()
142
                                                                                                      ->getOutput());
143
        }
144
    }
145
146
    protected function tearDown() : void
147
    {
148
        self::assertNotEmpty($this->sourcesRepository);
149
        self::assertDirectoryExists($this->sourcesRepository);
150
151
        // Need to be extremely careful with this stuff - skipping it for now
152
        (new Process(['rm', '-r', $this->sourcesRepository]))->mustRun();
153
154
        parent::tearDown();
155
    }
156
157
    public function testWillAllowSpecifyingGitRevision() : void
158
    {
159
        $check = new Process(
160
            [
161
                __DIR__ . '/../../../bin/roave-backward-compatibility-check',
162
                '--from=' . $this->versions[0],
163
                '--to=' . $this->versions[1],
164
            ],
165
            $this->sourcesRepository
166
        );
167
168
        self::assertSame(3, $check->run());
169
        self::assertStringEndsWith(
170
            <<<'EXPECTED'
171
[BC] CHANGED: The parameter $a of TestArtifact\TheClass#method() changed from TestArtifact\A to a non-contravariant TestArtifact\B
172
1 backwards-incompatible changes detected
173
174
EXPECTED
175
            ,
176
            $check->getErrorOutput() // @TODO https://github.com/Roave/BackwardCompatibilityCheck/issues/79 this looks like a symfony bug - we shouldn't check STDERR, but STDOUT
177
        );
178
    }
179
180
    public function testWillNotRunWithoutTagsNorSpecifiedVersions() : void
181
    {
182
        $check = new Process(
183
            [__DIR__ . '/../../../bin/roave-backward-compatibility-check'],
184
            $this->sourcesRepository
185
        );
186
187
        self::assertSame(212, $check->run());
188
        self::assertStringContainsString(
189
            'Could not detect any released versions for the given repository',
190
            $check->getErrorOutput()
191
        );
192
    }
193
194
    public function testWillRunSuccessfullyOnNoBcBreaks() : void
195
    {
196
        $check = new Process(
197
            [
198
                __DIR__ . '/../../../bin/roave-backward-compatibility-check',
199
                '--from=' . $this->versions[0],
200
                '--to=' . $this->versions[3],
201
                '-vvv',
202
            ],
203
            $this->sourcesRepository
204
        );
205
206
        self::assertSame(0, $check->run());
207
        self::assertStringContainsString(
208
            'No backwards-incompatible changes detected',
209
            $check->getErrorOutput()
210
        );
211
    }
212
213
    public function testWillPickTaggedVersionOnNoGivenFrom() : void
214
    {
215
        $this->tagOnVersion('1.2.3', 1);
216
217
        $check = new Process(
218
            [
219
                __DIR__ . '/../../../bin/roave-backward-compatibility-check',
220
                '--to=' . $this->versions[2],
221
            ],
222
            $this->sourcesRepository
223
        );
224
225
        self::assertSame(3, $check->run());
226
227
        $errorOutput = $check->getErrorOutput();
228
229
        self::assertStringContainsString('Detected last minor version: 1.2.3', $errorOutput);
230
        self::assertStringEndsWith(
231
            <<<'EXPECTED'
232
[BC] CHANGED: The parameter $a of TestArtifact\TheClass#method() changed from TestArtifact\B to a non-contravariant TestArtifact\C
233
1 backwards-incompatible changes detected
234
235
EXPECTED
236
            ,
237
            $errorOutput // @TODO https://github.com/Roave/BackwardCompatibilityCheck/issues/79 this looks like a symfony bug - we shouldn't check STDERR, but STDOUT
238
        );
239
    }
240
241
    public function testWillPickLatestTaggedVersionOnNoGivenFrom() : void
242
    {
243
        $this->tagOnVersion('2.2.3', 1);
244
        $this->tagOnVersion('1.2.3', 3);
245
246
        $check = new Process(
247
            [
248
                __DIR__ . '/../../../bin/roave-backward-compatibility-check',
249
                '--to=' . $this->versions[2],
250
            ],
251
            $this->sourcesRepository
252
        );
253
254
        self::assertSame(3, $check->run());
255
256
        $errorOutput = $check->getErrorOutput();
257
258
        self::assertStringContainsString('Detected last minor version: 2.2.3', $errorOutput);
259
        self::assertStringEndsWith(
260
            <<<'EXPECTED'
261
[BC] CHANGED: The parameter $a of TestArtifact\TheClass#method() changed from TestArtifact\B to a non-contravariant TestArtifact\C
262
1 backwards-incompatible changes detected
263
264
EXPECTED
265
            ,
266
            $errorOutput // @TODO https://github.com/Roave/BackwardCompatibilityCheck/issues/79 this looks like a symfony bug - we shouldn't check STDERR, but STDOUT
267
        );
268
    }
269
270
    private function tagOnVersion(string $tagName, int $version) : void
271
    {
272
        (new Process(
273
            [
274
                'git',
275
                'checkout',
276
                $this->versions[$version],
277
            ],
278
            $this->sourcesRepository
279
        ))->mustRun();
280
281
        (new Process(
282
            [
283
                'git',
284
                'tag',
285
                $tagName,
286
                '-m',
287
                'A tag for version ' . $version,
288
            ],
289
            $this->sourcesRepository
290
        ))->mustRun();
291
    }
292
}
293