Completed
Pull Request — master (#102)
by Marco
23:26
created

E2EInstallerTest::assertPackageVersionsIsUsable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PackageVersionsTest;
6
7
use PHPUnit\Framework\TestCase;
8
use RecursiveCallbackFilterIterator;
9
use RecursiveDirectoryIterator;
10
use RecursiveIteratorIterator;
11
use SplFileInfo;
12
use ZipArchive;
13
use const JSON_PRETTY_PRINT;
14
use const JSON_UNESCAPED_SLASHES;
15
use function array_filter;
16
use function array_map;
17
use function array_walk;
18
use function chdir;
19
use function exec;
20
use function file_get_contents;
21
use function file_put_contents;
22
use function getcwd;
23
use function in_array;
24
use function is_dir;
25
use function iterator_to_array;
26
use function json_decode;
27
use function json_encode;
28
use function mkdir;
29
use function putenv;
30
use function realpath;
31
use function rmdir;
32
use function scandir;
33
use function strlen;
34
use function substr;
35
use function sys_get_temp_dir;
36
use function uniqid;
37
use function unlink;
38
39
/**
40
 * @coversNothing
41
 */
42
class E2EInstallerTest extends TestCase
43
{
44
    /** @var string */
45
    private $tempGlobalComposerHome;
46
47
    /** @var string */
48
    private $tempLocalComposerHome;
49
50
    /** @var string */
51
    private $tempArtifact;
52
53
    public function setUp() : void
54
    {
55
        $this->tempGlobalComposerHome = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/global';
56
        $this->tempLocalComposerHome  = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/local';
57
        $this->tempArtifact           = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/artifacts';
58
        mkdir($this->tempGlobalComposerHome, 0700, true);
59
        mkdir($this->tempLocalComposerHome, 0700, true);
60
        mkdir($this->tempArtifact, 0700, true);
61
62
        putenv('COMPOSER_HOME=' . $this->tempGlobalComposerHome);
63
    }
64
65
    public function tearDown() : void
66
    {
67
        $this->rmDir($this->tempGlobalComposerHome);
68
        $this->rmDir($this->tempLocalComposerHome);
69
        $this->rmDir($this->tempArtifact);
70
71
        putenv('COMPOSER_HOME');
72
    }
73
74
    public function testGloballyInstalledPluginDoesNotGenerateVersionsForLocalProject() : void
75
    {
76
        $this->createPackageVersionsArtifact();
77
78
        $this->writeComposerJsonFile(
79
            [
80
                'name'         => 'package-versions/e2e-global',
81
                'require'      => ['ocramius/package-versions' => '1.0.0'],
82
                'repositories' => [
83
                    ['packagist' => false],
84
                    [
85
                        'type' => 'artifact',
86
                        'url' => $this->tempArtifact,
87
                    ],
88
                ],
89
            ],
90
            $this->tempGlobalComposerHome
91
        );
92
93
        $this->execComposerInDir('global update', $this->tempGlobalComposerHome);
94
95
        $this->createArtifact();
96
        $this->writeComposerJsonFile(
97
            [
98
                'name'         => 'package-versions/e2e-local',
99
                'require'      => ['test/package' => '2.0.0'],
100
                'repositories' => [
101
                    ['packagist' => false],
102
                    [
103
                        'type' => 'artifact',
104
                        'url' => $this->tempArtifact,
105
                    ],
106
                ],
107
            ],
108
            $this->tempLocalComposerHome
109
        );
110
111
        $this->execComposerInDir('update', $this->tempLocalComposerHome);
112
        $this->assertFileNotExists(
113
            $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
114
        );
115
    }
116
117
    public function testRemovingPluginDoesNotAttemptToGenerateVersions() : void
118
    {
119
        $this->createPackageVersionsArtifact();
120
        $this->createArtifact();
121
122
        $this->writeComposerJsonFile(
123
            [
124
                'name'         => 'package-versions/e2e-local',
125
                'require'      => [
126
                    'test/package' => '2.0.0',
127
                    'ocramius/package-versions' => '1.0.0',
128
                ],
129
                'repositories' => [
130
                    ['packagist' => false],
131
                    [
132
                        'type' => 'artifact',
133
                        'url' => $this->tempArtifact,
134
                    ],
135
                ],
136
            ],
137
            $this->tempLocalComposerHome
138
        );
139
140
        $this->execComposerInDir('update', $this->tempLocalComposerHome);
141
        $this->assertFileExists(
142
            $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
143
        );
144
145
        $this->execComposerInDir('remove ocramius/package-versions', $this->tempLocalComposerHome);
146
147
        $this->assertFileNotExists(
148
            $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
149
        );
150
    }
151
152
    /**
153
     * @group #41
154
     * @group #46
155
     */
156
    public function testRemovingPluginWithNoDevDoesNotAttemptToGenerateVersions() : void
157
    {
158
        $this->createPackageVersionsArtifact();
159
        $this->createArtifact();
160
161
        $this->writeComposerJsonFile(
162
            [
163
                'name'         => 'package-versions/e2e-local',
164
                'require-dev'      => ['ocramius/package-versions' => '1.0.0'],
165
                'repositories' => [
166
                    ['packagist' => false],
167
                    [
168
                        'type' => 'artifact',
169
                        'url' => $this->tempArtifact,
170
                    ],
171
                ],
172
            ],
173
            $this->tempLocalComposerHome
174
        );
175
176
        $this->execComposerInDir('update', $this->tempLocalComposerHome);
177
        $this->assertFileExists(
178
            $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
179
        );
180
181
        $this->execComposerInDir('install --no-dev', $this->tempLocalComposerHome);
182
183
        $this->assertFileNotExists(
184
            $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
185
        );
186
    }
187
188
    /**
189
     * @group 101
190
     */
191
    public function testInstallingPluginWithNoScriptsLeadsToUsableVersionsClass() : void
192
    {
193
        $this->createPackageVersionsArtifact();
194
        $this->createArtifact();
195
196
        $this->writeComposerJsonFile(
197
            [
198
                'name'         => 'package-versions/e2e-local',
199
                'require'      => ['ocramius/package-versions' => '1.0.0'],
200
                'repositories' => [
201
                    ['packagist' => false],
202
                    [
203
                        'type' => 'artifact',
204
                        'url' => $this->tempArtifact,
205
                    ],
206
                ],
207
            ],
208
            $this->tempLocalComposerHome
209
        );
210
211
        $this->execComposerInDir('install --no-scripts', $this->tempLocalComposerHome);
212
        $this->assertFileExists(
213
            $this->tempLocalComposerHome . '/vendor/ocramius/package-versions/src/PackageVersions/Versions.php'
214
        );
215
216
        $this->writePackageVersionUsingFile($this->tempLocalComposerHome);
217
        $this->assertPackageVersionsIsUsable($this->tempLocalComposerHome);
218
    }
219
220
    private function createPackageVersionsArtifact() : void
221
    {
222
        $zip = new ZipArchive();
223
224
        $zip->open($this->tempArtifact . '/ocramius-package-versions-1.0.0.zip', ZipArchive::CREATE);
225
226
        $files = array_filter(
227
            iterator_to_array(new RecursiveIteratorIterator(
228
                new RecursiveCallbackFilterIterator(
229
                    new RecursiveDirectoryIterator(realpath(__DIR__ . '/../../'), RecursiveDirectoryIterator::SKIP_DOTS),
230
                    static function (SplFileInfo $file, string $key, RecursiveDirectoryIterator $iterator) {
231
                        return $iterator->getSubPathname()[0]  !== '.' && $iterator->getSubPathname() !== 'vendor';
232
                    }
233
                ),
234
                RecursiveIteratorIterator::LEAVES_ONLY
235
            )),
236
            static function (SplFileInfo $file) {
237
                return ! $file->isDir();
238
            }
239
        );
240
241
        array_walk(
242
            $files,
243
            static function (SplFileInfo $file) use ($zip) {
244
                if ($file->getFilename() === 'composer.json') {
245
                    $contents            = json_decode(file_get_contents($file->getRealPath()), true);
246
                    $contents['version'] = '1.0.0';
247
248
                    return $zip->addFromString('composer.json', json_encode($contents));
249
                }
250
251
                $zip->addFile(
252
                    $file->getRealPath(),
253
                    substr($file->getRealPath(), strlen(realpath(__DIR__ . '/../../')) + 1)
254
                );
255
            }
256
        );
257
258
        $zip->close();
259
    }
260
261
    private function createArtifact() : void
262
    {
263
        $zip = new ZipArchive();
264
265
        $zip->open($this->tempArtifact . '/test-package-2.0.0.zip', ZipArchive::CREATE);
266
        $zip->addFromString(
267
            'composer.json',
268
            json_encode(
269
                [
270
                    'name'    => 'test/package',
271
                    'version' => '2.0.0',
272
                ],
273
                JSON_PRETTY_PRINT
274
            )
275
        );
276
        $zip->close();
277
    }
278
279
    /**
280
     * @param mixed[] $config
281
     */
282
    private function writeComposerJsonFile(array $config, string $directory) : void
283
    {
284
        file_put_contents(
285
            $directory . '/composer.json',
286
            json_encode($config, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
287
        );
288
    }
289
290
    private function writePackageVersionUsingFile(string $directory) : void
291
    {
292
        file_put_contents(
293
            $directory . '/use-package-versions.php',
294
            <<<'PHP'
295
<?php
296
297
require_once __DIR__ . '/vendor/autoload.php';
298
299
echo \PackageVersions\Versions::getVersion('ocramius/package-versions');
300
PHP
301
302
        );
303
    }
304
305
    private function assertPackageVersionsIsUsable(string $directory) : void
306
    {
307
        exec(PHP_BINARY . ' ' . escapeshellarg($directory . '/use-package-versions.php'), $output, $exitCode);
308
309
        self::assertSame(0, $exitCode);
310
        self::assertCount(1, $output);
0 ignored issues
show
Documentation introduced by
$output is of type null|array, but the function expects a object<Countable>|object...nit\Framework\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
311
        self::assertRegExp('/^1\\..*\\@[a-f0-9]*$/', $output[0]);
312
    }
313
314
    /**
315
     * @return mixed[]
316
     */
317
    private function execComposerInDir(string $command, string $dir) : array
318
    {
319
        $currentDir = getcwd();
320
        chdir($dir);
321
        exec(__DIR__ . '/../../vendor/bin/composer ' . $command . ' 2> /dev/null', $output, $exitCode);
322
        $this->assertEquals(0, $exitCode);
323
        chdir($currentDir);
324
325
        return $output;
326
    }
327
328
    private function rmDir(string $directory) : void
329
    {
330
        if (! is_dir($directory)) {
331
            unlink($directory);
332
333
            return;
334
        }
335
336
        array_map(
337
            function ($item) use ($directory) : void {
338
                $this->rmDir($directory . '/' . $item);
339
            },
340
            array_filter(
341
                scandir($directory),
342
                static function (string $dirItem) {
343
                    return ! in_array($dirItem, ['.', '..'], true);
344
                }
345
            )
346
        );
347
348
        rmdir($directory);
349
    }
350
}
351