Completed
Push — master ( 95fe89...6b0ea2 )
by Dave
19s queued 16s
created

testRelativePathFlagHasNoAffectWhenUsingAbsolutPaths()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 14
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 27
rs 9.7998
1
<?php
2
3
declare(strict_types=1);
4
5
namespace DaveLiddament\StaticAnalysisResultsBaseliner\Tests\Integration;
6
7
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\ProjectRoot;
8
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\RelativeFileName;
9
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\StringUtils;
10
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Command\CreateBaseLineCommand;
11
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Command\ListHistoryAnalysersCommand;
12
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Command\ListResultsParsesCommand;
13
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Command\RemoveBaseLineFromResultsCommand;
14
use DaveLiddament\StaticAnalysisResultsBaseliner\Framework\Container\Container;
15
use DaveLiddament\StaticAnalysisResultsBaseliner\Plugins\GitDiffHistoryAnalyser\internal\GitCliWrapper;
16
use DaveLiddament\StaticAnalysisResultsBaseliner\Tests\Helpers\ResourceLoaderTrait;
17
use PHPUnit\Framework\TestCase;
18
use Symfony\Component\Console\Application;
19
use Symfony\Component\Console\Tester\CommandTester;
20
use Symfony\Component\Filesystem\Filesystem;
21
use Webmozart\PathUtil\Path;
22
23
// TODO this is getting a bit big. Split into multiple files.
24
class EndToEndTest extends TestCase
25
{
26
    use ResourceLoaderTrait;
27
    use TestDirectoryTrait;
28
29
    private const COMMIT_1_DIRECTORY = 'integration/commit1';
30
    private const COMMIT_1_RESULTS = 'commit1.json';
31
32
    private const COMMIT_2_DIRECTORY = 'integration/commit2';
33
    private const COMMIT_2_RESULTS = 'commit2.json';
34
    private const COMMIT_2_BASELINE_REMOVED_EXPECTED_RESULTS = 'baseline-removed.json';
35
36
    private const COMMIT_3_DIRECTORY = 'integration/commit3';
37
    private const COMMIT_3_RESULTS = 'commit3.json';
38
39
    private const INVALID_RESULTS = 'invalid_analysis_results.json';
40
41
    /**
42
     * @var Filesystem
43
     */
44
    private $fileSystem;
45
46
    /**
47
     * @var GitCliWrapper
48
     */
49
    private $gitWrapper;
50
51
    /**
52
     * @var ProjectRoot
53
     */
54
    private $projectRoot;
55
56
    /**
57
     * @var Application
58
     */
59
    private $application;
60
61
    protected function setUp(): void
62
    {
63
        $this->fileSystem = new Filesystem();
64
        $this->gitWrapper = new GitCliWrapper();
65
        $container = new Container();
66
        $this->application = $container->getApplication();
67
    }
68
69
    public function testInvalidConfig(): void
70
    {
71
        $this->createTestDirectory();
72
73
        $arguments = [
74
            '--input-format' => 'rubbish',
75
            'baseline-file' => $this->getBaselineFilePath(),
76
        ];
77
78
        $this->runCommand(
79
            CreateBaseLineCommand::COMMAND_NAME,
80
            $arguments,
81
            11,
82
            self::COMMIT_1_RESULTS
83
        );
84
85
        // Only delete test directory if tests passed. Keep to investigate test failures
86
        $this->removeTestDirectory();
87
    }
88
89
    public function testInvalidAnalysisResults(): void
90
    {
91
        $this->createTestDirectory();
92
        $this->gitWrapper->init($this->projectRoot);
93
        $this->commit(self::COMMIT_1_DIRECTORY);
94
95
        $arguments = [
96
            'baseline-file' => $this->getBaselineFilePath(),
97
            '--project-root' => (string) $this->projectRoot,
98
        ];
99
100
        $this->runCommand(
101
            CreateBaseLineCommand::COMMAND_NAME,
102
            $arguments,
103
            13,
104
            self::INVALID_RESULTS
105
        );
106
107
        // Only delete test directory if tests passed. Keep to investigate test failures
108
        $this->removeTestDirectory();
109
    }
110
111
    public function testInvalidProjectRoot(): void
112
    {
113
        $this->createTestDirectory();
114
        $this->gitWrapper->init($this->projectRoot);
115
        $this->commit(self::COMMIT_1_DIRECTORY);
116
117
        $arguments = [
118
            'baseline-file' => $this->getProjectRootFilename('InvalidFileName.json'),
119
            '--project-root' => '/tmp/foo/bar',
120
        ];
121
122
        $this->runCommand(
123
            CreateBaseLineCommand::COMMAND_NAME,
124
            $arguments,
125
            15,
126
            self::COMMIT_1_RESULTS
127
        );
128
129
        // Only delete test directory if tests passed. Keep to investigate test failures
130
        $this->removeTestDirectory();
131
    }
132
133
    public function testInvalidBaselineFileNameSupplied(): void
134
    {
135
        $this->createTestDirectory();
136
        $arguments = [
137
            'baseline-file' => $this->getProjectRootFilename('InvalidFileName.json'),
138
        ];
139
140
        $this->runCommand(
141
            RemoveBaseLineFromResultsCommand::COMMAND_NAME,
142
            $arguments,
143
            14,
144
            self::COMMIT_1_RESULTS);
145
146
        // Only delete test directory if tests passed. Keep to investigate test failures
147
        $this->removeTestDirectory();
148
    }
149
150
    public function testInvalidBaselineContents(): void
151
    {
152
        $this->createTestDirectory();
153
        $this->gitWrapper->init($this->projectRoot);
154
        $this->commit(self::COMMIT_1_DIRECTORY);
155
        $arguments = [
156
            'baseline-file' => $this->getProjectRootFilename('src/Person.php'),
157
        ];
158
159
        $this->runCommand(
160
            RemoveBaseLineFromResultsCommand::COMMAND_NAME,
161
            $arguments,
162
            12,
163
            self::COMMIT_1_RESULTS);
164
165
        // Only delete test directory if tests passed. Keep to investigate test failures
166
        $this->removeTestDirectory();
167
    }
168
169
    public function testHappyPath(): void
170
    {
171
        $this->createTestDirectory();
172
        $this->gitWrapper->init($this->projectRoot);
173
174
        $this->commit(self::COMMIT_1_DIRECTORY);
175
        $this->runCreateBaseLineCommand();
176
177
        // Now create commit 2. THis introduces some new errors
178
        $this->commit(self::COMMIT_2_DIRECTORY);
179
        $this->runStripBaseLineFromResultsCommand(
180
            self::COMMIT_2_RESULTS,
181
            1,
182
            $this->getStaticAnalysisResultsAsString(self::COMMIT_2_BASELINE_REMOVED_EXPECTED_RESULTS)
183
        );
184
185
        // Now create commit 3. This has errors that were only in the baseline.
186
        $this->commit(self::COMMIT_3_DIRECTORY);
187
        $this->runStripBaseLineFromResultsCommand(
188
            self::COMMIT_3_RESULTS,
189
            0,
190
        ''
191
        );
192
193
        // Only delete test directory if tests passed. Keep to investigate test failures
194
        $this->removeTestDirectory();
195
    }
196
197
    public function testRelativePathFlagHasNoAffectWhenUsingAbsolutPaths(): void
198
    {
199
        $this->createTestDirectory();
200
        $this->gitWrapper->init($this->projectRoot);
201
202
        $this->commit(self::COMMIT_1_DIRECTORY);
203
        $this->runCreateBaseLineCommand('code');
204
205
        // Now create commit 2. THis introduces some new errors
206
        $this->commit(self::COMMIT_2_DIRECTORY);
207
        $this->runStripBaseLineFromResultsCommand(
208
            self::COMMIT_2_RESULTS,
209
            1,
210
            $this->getStaticAnalysisResultsAsString(self::COMMIT_2_BASELINE_REMOVED_EXPECTED_RESULTS),
211
            'code'
212
        );
213
214
        // Now create commit 3. This has errors that were only in the baseline.
215
        $this->commit(self::COMMIT_3_DIRECTORY);
216
        $this->runStripBaseLineFromResultsCommand(
217
            self::COMMIT_3_RESULTS,
218
            0,
219
            ''
220
        );
221
222
        // Only delete test directory if tests passed. Keep to investigate test failures
223
        $this->removeTestDirectory();
224
    }
225
226
    public function testAttemptToCreateBaselineWithNonCleanGitStatus(): void
227
    {
228
        $this->createTestDirectory();
229
        $this->gitWrapper->init($this->projectRoot);
230
        $this->commit(self::COMMIT_1_DIRECTORY);
231
        $this->addNonCheckedInFile();
232
233
        $arguments = [
234
            'baseline-file' => $this->getProjectRootFilename('baseline.json'),
235
            '--project-root' => (string) $this->projectRoot,
236
        ];
237
238
        $this->runCommand(
239
            CreateBaseLineCommand::COMMAND_NAME,
240
            $arguments,
241
            15,
242
            self::COMMIT_1_RESULTS
243
        );
244
245
        $this->removeTestDirectory();
246
    }
247
248
    public function testForceCreateBaselineWithNonCleanGitStatus(): void
249
    {
250
        $this->createTestDirectory();
251
        $this->gitWrapper->init($this->projectRoot);
252
        $this->commit(self::COMMIT_1_DIRECTORY);
253
        $this->addNonCheckedInFile();
254
255
        $arguments = [
256
            'baseline-file' => $this->getProjectRootFilename('baseline.json'),
257
            '--project-root' => (string) $this->projectRoot,
258
            '--force' => null,
259
        ];
260
261
        $this->runCommand(
262
            CreateBaseLineCommand::COMMAND_NAME,
263
            $arguments,
264
            0,
265
            self::COMMIT_1_RESULTS
266
        );
267
268
        // Only delete test directory if tests passed. Keep to investigate test failures
269
        $this->removeTestDirectory();
270
    }
271
272
    /**
273
     * This is just a smoke test.
274
     */
275
    public function testListSupportedStaticAnalysisTools(): void
276
    {
277
        $this->runCommand(ListResultsParsesCommand::COMMAND_NAME, [], 0, null);
278
    }
279
280
    /**
281
     * This is just a smoke test.
282
     */
283
    public function testListSupportedHistoryAnalysers(): void
284
    {
285
        $this->runCommand(ListHistoryAnalysersCommand::COMMAND_NAME, [], 0, null);
286
    }
287
288
    private function commit(string $directory): void
289
    {
290
        $source = $this->getPath($directory);
291
        $this->fileSystem->mirror($source, (string) $this->projectRoot, null, ['override' => true]);
292
        $this->updatePathsInJsonFiles($this->projectRoot);
293
        $this->gitWrapper->addAndCommit("Updating code to $directory", $this->projectRoot);
294
    }
295
296
    private function runCreateBaseLineCommand(?string $relativePathToCode = null): void
297
    {
298
        $arguments = [
299
            'baseline-file' => $this->getBaselineFilePath(),
300
            '--project-root' => (string) $this->projectRoot,
301
        ];
302
303
        if (null !== $relativePathToCode) {
304
            $arguments['--relative-path-to-code'] = $relativePathToCode;
305
        }
306
307
        $this->runCommand(
308
            CreateBaseLineCommand::COMMAND_NAME,
309
            $arguments,
310
            0,
311
            self::COMMIT_1_RESULTS
312
        );
313
    }
314
315
    private function runStripBaseLineFromResultsCommand(
316
        string $psalmResults,
317
        int $expectedExitCode,
318
        string $expectedResultsJson,
319
        ?string $relativePathToCode = null
320
    ): void {
321
        $arguments = [
322
            'baseline-file' => $this->getBaselineFilePath(),
323
            '--output-format' => 'json',
324
            '--project-root' => (string) $this->projectRoot,
325
        ];
326
327
        if (null !== $relativePathToCode) {
328
            $arguments['--relative-path-to-code'] = $relativePathToCode;
329
        }
330
331
        $output = $this->runCommand(
332
            RemoveBaseLineFromResultsCommand::COMMAND_NAME,
333
            $arguments,
334
            $expectedExitCode,
335
            $psalmResults
336
        );
337
338
        $output = str_replace('\/', '/', $output);
339
340
        $this->assertStringContainsString($expectedResultsJson, $output);
341
    }
342
343
    /**
344
     * @param array<string, string|null> $arguments
345
     */
346
    private function runCommand(
347
        string $commandName,
348
        array $arguments,
349
        int $expectedExitCode,
350
        ?string $resourceContainStdinContents
351
    ): string {
352
        $command = $this->application->find($commandName);
353
        $commandTester = new CommandTester($command);
354
        $arguments['command'] = $command->getName();
355
356
        if (null !== $resourceContainStdinContents) {
357
            $stdin = $this->getStaticAnalysisResultsAsString($resourceContainStdinContents);
358
            $commandTester->setInputs([$stdin]);
359
        }
360
361
        $actualExitCode = $commandTester->execute($arguments);
362
        $output = $commandTester->getDisplay();
363
        $this->assertEquals($expectedExitCode, $actualExitCode, $output);
364
365
        return $output;
366
    }
367
368
    private function getBaselineFilePath(): string
369
    {
370
        return "{$this->projectRoot}/baseline.json";
371
    }
372
373
    private function getStaticAnalysisResultsAsString(string $resourceName): string
374
    {
375
        $fileName = __DIR__.'/../resources/integration/staticAnalysisOutput/'.$resourceName;
376
        $rawResults = file_get_contents($fileName);
377
        $this->assertNotFalse($rawResults);
378
        $projectRootDirectory = (string) $this->projectRoot;
379
        $resultsWithPathsCorrected = str_replace('__SCRATCH_PAD_PATH__', $projectRootDirectory, $rawResults);
380
381
        return $resultsWithPathsCorrected;
382
    }
383
384
    private function getProjectRootFilename(string $resourceName): string
385
    {
386
        return $this->projectRoot->getAbsoluteFileName(new RelativeFileName($resourceName))->getFileName();
387
    }
388
389
    private function updatePathsInJsonFiles(ProjectRoot $projectRoot): void
390
    {
391
        $directory = $projectRoot->getProjectRootDirectory();
392
393
        $files = scandir($directory);
394
        $this->assertNotFalse($files);
395
        foreach ($files as $file) {
396
            if (StringUtils::endsWith('.json', $file)) {
397
                $fullPath = Path::makeAbsolute($file, $directory);
398
                $contents = file_get_contents($fullPath);
399
                $this->assertNotFalse($contents);
400
                $newContents = str_replace('__SCRATCH_PAD_PATH__', $directory, $contents);
401
                file_put_contents($fullPath, $newContents);
402
            }
403
        }
404
    }
405
406
    private function addNonCheckedInFile(): void
407
    {
408
        // Add a new file that is not checked in
409
        $newFile = new RelativeFileName('new.php');
410
        $this->fileSystem->dumpFile($this->projectRoot->getAbsoluteFileName($newFile)->getFileName(), 'new');
411
    }
412
}
413