Completed
Pull Request — master (#15)
by
unknown
04:30
created

FinderTest::testGetMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * This file is part of phpDocumentor.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @copyright 2010-2018 Mike van Riel<[email protected]>
9
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
10
 * @link      http://phpdoc.org
11
 */
12
13
namespace Flyfinder;
14
15
use Flyfinder\Specification\HasExtension;
16
use Flyfinder\Specification\InPath;
17
use Flyfinder\Specification\IsHidden;
18
use League\Flysystem\Filesystem;
19
use League\Flysystem\FilesystemInterface;
20
use Mockery as m;
21
use PHPUnit\Framework\TestCase;
22
23
/**
24
 * Test case for Finder
25
 * @coversDefaultClass Flyfinder\Finder
26
 */
27
class FinderTest extends TestCase
28
{
29
    use TestsBothAlgorithms;
30
31
    /** @var Finder */
32
    private $fixture;
33
34
    /**
35
     * Initializes the fixture for this test.
36
     */
37
    public function setUp()
38
    {
39
        $this->fixture = new Finder();
40
    }
41
42
    public function tearDown()
43
    {
44
        m::close();
45
    }
46
47
    /**
48
     * @covers ::getMethod
49
     */
50
    public function testGetMethod()
51
    {
52
        $this->assertSame('find', $this->fixture->getMethod());
53
    }
54
55
    const DEEPLY_UNDER_HIDDEN_DIRECTORY = [ 'foo/bar/.hidden/baz/not-hidden.txt' ];
56
    const DEEPLY_UNDER_DIRECTORY_WITH_EXTENSION = [ 'foo/bar/directory.ext/baz/not-hidden.txt' ];
57
58
    public function testIfNotHiddenLetsSubpathsThrough()
59
    {
60
        $files = [ 'foo/bar/.hidden/baz/not-hidden.txt' ];
61
        $this->fixture->setAlgorithm(Finder::ALGORITHM_OPTIMIZED);
62
        $this->fixture->setFilesystem($this->mockFileSystem($files));
63
        $notHidden = (new IsHidden())->notSpecification();
64
        $this->assertEquals(
65
            $files,
66
            $this->generatorToFileList($this->fixture->handle($notHidden))
67
        );
68
    }
69
70
    public function testIfDoubleNotHiddenLetsSubpathsThrough()
71
    {
72
        $files = [ '.foo/.bar/not-hidden/.baz/.hidden.txt' ];
73
        $this->fixture->setAlgorithm(Finder::ALGORITHM_OPTIMIZED);
74
        $this->fixture->setFilesystem($this->mockFileSystem($files));
75
        $notHidden = (new IsHidden())->notSpecification()->notSpecification();
76
        $this->assertEquals(
77
            $files,
78
            $this->generatorToFileList($this->fixture->handle($notHidden))
79
        );
80
    }
81
82
    public function testIfNeitherHiddenNorExtLetsSubpathsThrough()
83
    {
84
        $files = [ 'foo/bar/.hidden/baz.ext/neither-hidden-nor.ext.zzz' ];
85
        $this->fixture->setAlgorithm(Finder::ALGORITHM_OPTIMIZED);
86
        $this->fixture->setFilesystem($this->mockFileSystem($files));
87
88
        $neitherHiddenNorExt =
89
            (new IsHidden())->notSpecification()
90
                ->andSpecification((new HasExtension(['ext']))->notSpecification());
91
        $this->assertEquals(
92
            $files,
93
            $this->generatorToFileList($this->fixture->handle($neitherHiddenNorExt))
94
        );
95
96
        $neitherHiddenNorExtDeMorgan = (new IsHidden())->orSpecification(new HasExtension(['ext']))->notSpecification();
97
        $this->assertEquals(
98
            $files,
99
            $this->generatorToFileList($this->fixture->handle($neitherHiddenNorExtDeMorgan))
100
        );
101
    }
102
103
    /**
104
     * @covers ::handle
105
     * @covers ::setFilesystem
106
     * @covers ::<private>
107
     * @dataProvider algorithms
108
     * @param int $finderAlgorithm
109
     */
110
    public function testIfCorrectFilesAreBeingYielded(int $finderAlgorithm)
111
    {
112
        $this->fixture->setAlgorithm($finderAlgorithm);
113
114
        $isHidden = m::mock(IsHidden::class);
115
        $filesystem = m::mock(Filesystem::class);
116
117
        $listContents1 = [
118
            0 => [
119
                'type' => 'dir',
120
                'path' => '.hiddendir',
121
                'dirname' => '',
122
                'basename' => '.hiddendir',
123
                'filename' => '.hiddendir',
124
            ],
125
            1 => [
126
                'type' => 'file',
127
                'path' => 'test.txt',
128
                'basename' => 'test.txt',
129
            ],
130
        ];
131
132
        $listContents2 = [
133
            0 => [
134
                'type' => 'file',
135
                'path' => '.hiddendir/.test.txt',
136
                'dirname' => '.hiddendir',
137
                'basename' => '.test.txt',
138
                'filename' => '.test',
139
                'extension' => 'txt',
140
            ],
141
        ];
142
143
        $filesystem->shouldReceive('listContents')
144
            ->with('')
145
            ->andReturn($listContents1);
146
147
        $filesystem->shouldReceive('listContents')
148
            ->with('.hiddendir')
149
            ->andReturn($listContents2);
150
151
        $isHidden->shouldReceive('isSatisfiedBy')
152
            ->with($listContents1[0])
153
            ->andReturn(true);
154
155
        if (Finder::ALGORITHM_OPTIMIZED === $finderAlgorithm) {
156
            $isHidden->shouldReceive('canBeSatisfiedByAnythingBelow')
157
                ->with($listContents1[0])
158
                ->andReturn(true);
159
        }
160
161
        $isHidden->shouldReceive('isSatisfiedBy')
162
            ->with($listContents1[1])
163
            ->andReturn(false);
164
165
        $isHidden->shouldReceive('isSatisfiedBy')
166
            ->with($listContents2[0])
167
            ->andReturn(true);
168
169
        $this->fixture->setFilesystem($filesystem);
170
        $generator = $this->fixture->handle($isHidden);
171
172
        $result = [];
173
174
        foreach ($generator as $value) {
175
            $result[] = $value;
176
        }
177
178
        $expected = [
179
            0 => [
180
                'type' => 'file',
181
                'path' => '.hiddendir/.test.txt',
182
                'dirname' => '.hiddendir',
183
                'basename' => '.test.txt',
184
                'filename' => '.test',
185
                'extension' => 'txt',
186
            ],
187
        ];
188
189
        $this->assertSame($expected, $result);
190
    }
191
192
    /**
193
     * @param int $finderAlgorithm
194
     * @dataProvider algorithms
195
     */
196
    public function testPrefixCullingOptimization(int $finderAlgorithm)
197
    {
198
        $this->fixture->setAlgorithm($finderAlgorithm);
199
200
        $filesystem = $this->mockFileSystem(
201
            [
202
                'foo/bar/baz/file.txt',
203
                'foo/bar/baz/file2.txt',
204
                'foo/bar/baz/excluded/excluded.txt',
205
                'foo/bar/baz/excluded/culled/culled.txt',
206
                'foo/bar/baz/excluded/important/reincluded.txt',
207
                'foo/bar/file3.txt',
208
                'foo/lou/someSubdir/file4.txt',
209
                'foo/irrelevant1/',
210
                'irrelevant2/irrelevant3/irrelevantFile.txt'
211
            ],
212
            Finder::ALGORITHM_OPTIMIZED === $finderAlgorithm
213
                ? [
214
                    'foo/irrelevant1',
215
                    'irrelevant2',
216
                    'foo/bar/baz/excluded/culled'
217
                ]
218
                : []
219
        );
220
221
        $inFooBar = new InPath(new Path("foo/bar"));
222
        $inFooLou = new InPath(new Path("foo/lou"));
223
        $inExcl = new InPath(new Path("foo/bar/baz/excl*"));
224
        $inReincl = new InPath(new Path("foo/bar/baz/*/important"));
225
        $spec =
226
            $inFooBar
227
                ->orSpecification($inFooLou)
228
                ->andSpecification($inExcl->notSpecification())
229
                ->orSpecification($inReincl);
230
231
        $finder = $this->fixture;
232
        $finder->setFilesystem($filesystem);
233
        $generator = $finder->handle($spec);
234
235
        $expected = [
236
            'foo/bar/baz/file.txt',
237
            'foo/bar/baz/file2.txt',
238
            'foo/bar/file3.txt',
239
            'foo/bar/baz/excluded/important/reincluded.txt',
240
            'foo/lou/someSubdir/file4.txt',
241
        ];
242
        sort($expected);
243
244
        $this->assertEquals($expected, $this->generatorToFileList($generator));
245
246
        $this->addToAssertionCount(1);
247
    }
248
249
    protected function generatorToFileList(\Generator $generator) : array
250
    {
251
        $actual = array_values(array_map(function($v) {return $v['path']; }, iterator_to_array($generator)));
252
        sort($actual);
253
        return $actual;
254
    }
255
256
    protected function mockFileTree(array $pathList) : array
257
    {
258
        $result = [
259
            "." => [
260
                'type' => 'dir',
261
                'path' => '',
262
                'dirname' => '.',
263
                'basename' => '.',
264
                'filename' => '.',
265
                'contents' => []
266
            ]
267
        ];
268
        foreach($pathList as $path) {
269
270
            $isFile = "/" !== substr($path,-1);
271
            $child = null;
272
            while(true) {
273
                $info = pathinfo($path);
274
                if ($isFile) {
275
                    $isFile = false;
276
                    $result[$path] = [
277
                        'type' => 'file',
278
                        'path' => $path,
279
                        'dirname' => $info['dirname'],
280
                        'basename' => $info['basename'],
281
                        'filename' => $info['filename'],
282
                        'extension' => $info['extension']
283
                    ];
284
                }
285
                else {
286
                    $existed = true;
287
                    if (!isset($result[$path])) {
288
                        $existed = false;
289
                        $result[$path] = [
290
                            'type' => 'dir',
291
                            'path' => $path,
292
                            'basename' => $info['basename'],
293
                            'filename' => $info['filename'],
294
                            'contents' => []
295
                        ];
296
                    }
297
                    if (null!==$child) {
298
                        $result[$path]['contents'][] = $child;
299
                    }
300
                    if ($existed) break;
301
                }
302
                $child = $info['basename'];
303
                $path = $info['dirname'];
304
            }
305
        }
306
        return $result;
307
    }
308
309
    protected function mockListContents(array $fileTreeMock, string $path) : array
310
    {
311
        $path = trim($path,"/");
312
        if (substr($path."  ",0,2)==="./") $path = substr($path,2);
313
        if ($path==="") $path = ".";
314
315
        if (!isset($fileTreeMock[$path]) || 'file' === $fileTreeMock[$path]['type']) {
316
            return [];
317
        }
318
        $result = [];
319
        foreach($fileTreeMock[$path]['contents'] as $basename) {
320
            $childPath = ($path==="." ? "" : $path."/").$basename;
321
            if (isset($fileTreeMock[$childPath])) {
322
                $result[$basename] = $fileTreeMock[$childPath];
323
            }
324
        }
325
        return $result;
326
    }
327
328
    protected function mockFileSystem(array $paths, array $pathsThatShouldNotBeListed = []) : FilesystemInterface
329
    {
330
        $fsData = $this->mockFileTree($paths);
331
        $filesystem = m::mock(Filesystem::class);
332
        $filesystem->shouldReceive('listContents')
333
            ->zeroOrMoreTimes()
334
            ->andReturnUsing(function(string $path) use ($fsData, $pathsThatShouldNotBeListed) : array {
335
336
                $this->assertNotContains($path, $pathsThatShouldNotBeListed);
337
                return array_values($this->mockListContents($fsData, $path));
338
            });
339
        return $filesystem;
340
    }
341
}
342