Completed
Pull Request — develop (#168)
by
unknown
08:57 queued 17s
created

TestCase::myAssertMatchesRegularExpression()   A

Complexity

Conditions 2
Paths 2

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 2
nc 2
nop 3
1
<?php
2
3
/**
4
 * This file is part of the GitElephant package.
5
 *
6
 * (c) Matteo Giachino <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * Just for fun...
12
 */
13
14
namespace GitElephant;
15
16
use GitElephant\Command\Caller\Caller;
17
use GitElephant\Command\Caller\CallerInterface;
18
use GitElephant\Command\MvCommand;
19
use GitElephant\Objects\Author;
20
use GitElephant\Objects\Commit;
21
use Mockery as m;
22
use PHPUnit\Framework\MockObject\MockObject;
23
use Symfony\Component\Filesystem\Filesystem;
24
use Symfony\Component\Finder\Finder;
25
26
/**
27
 * Class TestCase
28
 *
29
 * @package GitElephant
30
 */
31
class TestCase extends \PHPUnit\Framework\TestCase
32
{
33
    /**
34
     * @var \GitElephant\Command\Caller\CallerInterface
35
     */
36
    protected $caller;
37
38
    /**
39
     * @var Repository|array<Repository>
40
     */
41
    protected $repository;
42
43
    /**
44
     * @var string
45
     */
46
    protected $path;
47
48
    /**
49
     * @var Finder
50
     */
51
    protected $finder;
52
53
    /**
54
     * @param string|int|null $name the name or index of the repository
55
     *
56
     * @return \GitElephant\Repository
57
     */
58
    protected function getRepository($name = null)
59
    {
60
        if ($this->repository == null) {
61
            $this->initRepository($name);
62
        }
63
        if (is_null($name)) {
64
            return $this->repository;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->repository; of type GitElephant\Repository|GitElephant\Repository[] adds the type GitElephant\Repository[] to the return on line 64 which is incompatible with the return type documented by GitElephant\TestCase::getRepository of type GitElephant\Repository.
Loading history...
65
        } else {
66
            return $this->repository[$name];
67
        }
68
    }
69
70
    /**
71
     * @return CallerInterface the real/not mocked caller
72
     */
73
    protected function getCaller(): CallerInterface
74
    {
75
        if ($this->caller == null) {
76
            $this->initRepository();
77
        }
78
79
        return $this->caller;
80
    }
81
82
    /**
83
     * @param null|string $name  the folder name
84
     * @param int         $index the repository index (for getting them back)
85
     *
86
     * @return void
87
     */
88
    protected function initRepository(string $name = null, int $index = null): void
89
    {
90
        $tempDir = realpath(sys_get_temp_dir());
91
        $tempName = null === $name
92
            ? tempnam($tempDir, 'gitelephant')
93
            : $tempDir . DIRECTORY_SEPARATOR . $name;
94
        $this->path = $tempName;
95
        @unlink($this->path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
96
        $fs = new Filesystem();
97
        $fs->mkdir($this->path);
98
        $this->caller = new Caller(null, $this->path);
99
        if (is_null($index)) {
100
            $this->repository = Repository::open($this->path);
101
            $this->assertInstanceOf(Repository::class, $this->repository);
102
        } else {
103
            if (!is_array($this->repository)) {
104
                $this->repository = [];
105
            }
106
            $this->repository[$index] = Repository::open($this->path);
107
            $this->assertInstanceOf(Repository::class, $this->repository[$index]);
108
        }
109
    }
110
111
    protected function tearDown(): void
112
    {
113
        $fs = new Filesystem();
114
        if (is_array($this->repository)) {
115
            array_map(function (Repository $repo) use ($fs) {
116
                $fs->remove($repo->getPath());
117
            }, $this->repository);
118
        } else {
119
            $fs->remove($this->path);
120
        }
121
        m::close();
122
    }
123
124
    /**
125
     * Write to file. Creates the file if not existing.
126
     * Overwrites content if already existing.
127
     *
128
     * @param string      $name       file name
129
     * @param string|null $folder     folder name
130
     * @param string|null        $content    content
131
     * @param Repository  $repository repository to add file to
132
     *
133
     * @return void
134
     */
135
    protected function addFile(
136
        string $name,
137
        string $folder = null,
138
        string $content = null,
139
        Repository $repository = null
140
    ): void {
141
        $path = is_null($repository) ? $this->path : $repository->getPath();
142
        $filename = $folder == null
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $folder of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
143
            ? $path . DIRECTORY_SEPARATOR . $name
144
            : $path . DIRECTORY_SEPARATOR . $folder . DIRECTORY_SEPARATOR . $name;
145
        $handle = fopen($filename, 'w');
146
        $fileContent = $content === null ? 'test content' : $content;
147
        $this->assertTrue(false !== fwrite($handle, $fileContent), sprintf('unable to write the file %s', $name));
148
        fclose($handle);
149
    }
150
151
    /**
152
     * remove file from repo
153
     *
154
     * @param string $name
155
     */
156
    protected function removeFile(string $name): void
157
    {
158
        $filename = $this->path . DIRECTORY_SEPARATOR . $name;
159
        $this->assertTrue(unlink($filename));
160
    }
161
162
    /**
163
     * update a file in the repository
164
     *
165
     * @param string $name    file name
166
     * @param string $content content
167
     */
168
    protected function updateFile(string $name, string $content): void
169
    {
170
        $filename = $this->path . DIRECTORY_SEPARATOR . $name;
171
        $this->assertTrue(false !== file_put_contents($filename, $content));
172
    }
173
174
    /**
175
     * rename a file in the repository
176
     *
177
     * @param string $originName file name
178
     * @param string $targetName new file name
179
     * @param bool   $gitMv      use git mv, otherwise uses php rename function (with the Filesystem component)
180
     */
181
    protected function renameFile(string $originName, string $targetName, bool $gitMv = true): void
182
    {
183
        if ($gitMv) {
184
            $this->getRepository()
185
                ->getCaller()
186
                ->execute(MvCommand::getInstance()->rename($originName, $targetName));
187
188
            return;
189
        }
190
        $origin = $this->path . DIRECTORY_SEPARATOR . $originName;
191
        $target = $this->path . DIRECTORY_SEPARATOR . $targetName;
192
        $fs = new Filesystem();
193
        $fs->rename($origin, $target);
194
    }
195
196
    /**
197
     * @param string $name name
198
     *
199
     * @return void
200
     */
201
    protected function addFolder($name): void
202
    {
203
        $fs = new Filesystem();
204
        $fs->mkdir($this->path . DIRECTORY_SEPARATOR . $name);
205
    }
206
207
    protected function addSubmodule(string $url, string $path): void
208
    {
209
        $this->getRepository()->addSubmodule($url, $path);
210
    }
211
212
    /**
213
     * @param string $classname
214
     *
215
     * @return MockObject
216
     */
217
    protected function getMock(string $classname): MockObject
218
    {
219
        return $this
220
            ->getMockBuilder($classname)
221
            ->disableOriginalConstructor()
222
            ->getMock();
223
    }
224
225
    /**
226
     * mock the caller
227
     *
228
     * @param string $command command
229
     * @param string $output  output
230
     *
231
     * @return MockObject
232
     */
233
    protected function getMockCaller($command, $output): MockObject
0 ignored issues
show
Unused Code introduced by
The parameter $command is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
234
    {
235
        $mock = $this->createMock(CallerInterface::class);
236
        $mock
237
            ->expects($this->any())
238
            ->method('execute')
239
            ->willReturn($mock);
240
        $mock
241
            ->expects($this->any())
242
            ->method('getOutputLines')
243
            ->willReturn($output);
244
245
        return $mock;
246
    }
247
248
    protected function addCommandToMockContainer(MockObject $container, string $commandName): void
249
    {
250
        $container
251
            ->expects($this->any())
252
            ->method('get')
253
            ->with($this->equalTo($commandName))
254
            ->willReturn($this->getMockCommand());
255
    }
256
257
    /**
258
     *
259
     * @param MockObject $repo
260
     * @param string|array $output
261
     * @return void
262
     */
263
    protected function addOutputToMockRepo(MockObject $repo, $output): void
264
    {
265
        $repo
266
            ->expects($this->any())
267
            ->method('getCaller')
268
            ->willReturn($this->getMockCaller('', $output));
0 ignored issues
show
Bug introduced by
It seems like $output defined by parameter $output on line 263 can also be of type array; however, GitElephant\TestCase::getMockCaller() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
269
    }
270
271
    protected function getMockCommand(): MockObject
272
    {
273
        $command = $this->getMock('Command', ['showCommit']);
0 ignored issues
show
Unused Code introduced by
The call to TestCase::getMock() has too many arguments starting with array('showCommit').

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
274
        $command
275
            ->expects($this->any())
276
            ->method('showCommit')
277
            ->willReturn('');
278
279
        return $command;
280
    }
281
282
    protected function getMockRepository(): MockObject
283
    {
284
        return $this->getMock(
285
            Repository::class,
286
            [],
0 ignored issues
show
Unused Code introduced by
The call to TestCase::getMock() has too many arguments starting with array().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
287
            [
288
                $this->repository->getPath(),
289
                null,
290
            ]
291
        );
292
    }
293
294
    /**
295
     * Do a test on a certain commit
296
     *
297
     * @param Commit $commit the commit to test
298
     * @param string $sha
299
     * @param string $tree
300
     * @param string $author the name of the author
301
     * @param string $committer the name of the committer
302
     * @param string $emailAuthor
303
     * @param string $emailCommitter
304
     * @param integer $datetimeAuthor
305
     * @param integer $datetimeCommitter
306
     * @param string $message
307
     * @return void
308
     */
309
    protected function doCommitTest(
310
        Commit $commit,
311
        string $sha,
312
        string $tree,
313
        string $author,
314
        string $committer,
315
        string $emailAuthor,
316
        string $emailCommitter,
317
        int $datetimeAuthor,
318
        int $datetimeCommitter,
319
        string $message
320
    ): void {
321
        $this->assertInstanceOf(Commit::class, $commit);
322
        $this->assertEquals($sha, $commit->getSha());
323
        $this->assertEquals($tree, $commit->getTree());
324
        $this->assertInstanceOf(Author::class, $commit->getAuthor());
325
        $this->assertEquals($author, $commit->getAuthor()->getName());
326
        $this->assertEquals($emailAuthor, $commit->getAuthor()->getEmail());
327
        $this->assertInstanceOf(Author::class, $commit->getCommitter());
328
        $this->assertEquals($committer, $commit->getCommitter()->getName());
329
        $this->assertEquals($emailCommitter, $commit->getCommitter()->getEmail());
330
        $this->assertInstanceOf(\DateTime::class, $commit->getDatetimeAuthor());
331
        $this->assertEquals($datetimeAuthor, $commit->getDatetimeAuthor()->format('U'));
332
        $this->assertInstanceOf(\DateTime::class, $commit->getDatetimeCommitter());
333
        $this->assertEquals($datetimeCommitter, $commit->getDatetimeCommitter()->format('U'));
334
        $this->assertEquals($message, $commit->getMessage()->getShortMessage());
335
    }
336
337
    /**
338
     * Compatibility function for PHP 7.2:
339
     * PHPUnit 9. only supports PHP >= 7.3,
340
     * but has a different compatibility regarding the assertRegex function
341
     *
342
     * @param string $pattern
343
     * @param string $string
344
     * @param string $message
345
     * @return void
346
     */
347
    protected function myAssertMatchesRegularExpression($pattern, $string, $message = '')
348
    {
349
        if (method_exists($this, 'assertMatchesRegularExpression')) {
350
            $this->assertMatchesRegularExpression($pattern, $string, $message);
351
        } else {
352
            $this->assertRegExp($pattern, $string, $message);
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertRegExp() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/4086

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
353
        }
354
    }
355
}
356