Passed
Pull Request — master (#75)
by Dave
02:21 queued 11s
created

EndToEndTest::setUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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