Completed
Pull Request — master (#15)
by
unknown
03:57
created

FinderTest::testIfNotHiddenLetsSubpathsThrough()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
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
    /** @var Finder */
30
    private $fixture;
31
32
    /**
33
     * Initializes the fixture for this test.
34
     */
35
    public function setUp()
36
    {
37
        $this->fixture = new Finder();
38
    }
39
40
    public function tearDown()
41
    {
42
        m::close();
43
    }
44
45
    /**
46
     * @covers ::getMethod
47
     */
48
    public function testGetMethod()
49
    {
50
        $this->assertSame('find', $this->fixture->getMethod());
51
    }
52
53
    public function testIfNotHiddenLetsSubpathsThrough()
54
    {
55
        $files = [ 'foo/bar/.hidden/baz/not-hidden.txt' ];
56
        $this->fixture->setFilesystem($this->mockFileSystem($files));
57
        $notHidden = (new IsHidden())->notSpecification();
58
        $this->assertEquals(
59
            $files,
60
            $this->generatorToFileList($this->fixture->handle($notHidden))
61
        );
62
    }
63
64
    public function testIfDoubleNotHiddenLetsSubpathsThrough()
65
    {
66
        $files = [ '.foo/.bar/not-hidden/.baz/.hidden.txt' ];
67
        $this->fixture->setFilesystem($this->mockFileSystem($files));
68
        $notHidden = (new IsHidden())->notSpecification()->notSpecification();
69
        $this->assertEquals(
70
            $files,
71
            $this->generatorToFileList($this->fixture->handle($notHidden))
72
        );
73
    }
74
75
    public function testIfNeitherHiddenNorExtLetsSubpathsThrough()
76
    {
77
        $files = [ 'foo/bar/.hidden/baz.ext/neither-hidden-nor.ext.zzz' ];
78
        $this->fixture->setFilesystem($this->mockFileSystem($files));
79
80
        $neitherHiddenNorExt =
81
            (new IsHidden())->notSpecification()
82
                ->andSpecification((new HasExtension(['ext']))->notSpecification());
83
        $this->assertEquals(
84
            $files,
85
            $this->generatorToFileList($this->fixture->handle($neitherHiddenNorExt))
86
        );
87
88
        $neitherHiddenNorExtDeMorgan = (new IsHidden())->orSpecification(new HasExtension(['ext']))->notSpecification();
89
        $this->assertEquals(
90
            $files,
91
            $this->generatorToFileList($this->fixture->handle($neitherHiddenNorExtDeMorgan))
92
        );
93
    }
94
95
    public function testIfNegatedOrCullsExactMatches()
96
    {
97
        $files = [
98
            'foo/bar/baz/whatever.txt',
99
            'foo/gen/pics/bottle.jpg',
100
            'foo/lou/time.txt'
101
        ];
102
        $this->fixture->setFilesystem($this->mockFileSystem($files,['foo/bar','foo/gen']));
103
104
        $negatedOr =
105
            (new InPath(new Path("foo/gen")))
106
                ->orSpecification(new InPath(new Path("foo/bar")))
107
                ->notSpecification();
108
109
        $this->assertEquals(
110
            ['foo/lou/time.txt'],
111
            $this->generatorToFileList($this->fixture->handle($negatedOr))
112
        );
113
114
        $negatedOrDeMorgan =
115
            (new InPath(new Path("foo/gen")))->notSpecification()
116
            ->andSpecification((new InPath(new Path("foo/bar")))->notSpecification());
117
118
        $this->assertEquals(
119
            ['foo/lou/time.txt'],
120
            $this->generatorToFileList($this->fixture->handle($negatedOrDeMorgan))
121
        );
122
    }
123
124
    public function testIfNegatedAndCullsExactMatches()
125
    {
126
        $files = [
127
            'foo/bar/baz/whatever.txt',
128
            'foo/gen/pics/bottle.jpg',
129
            'foo/lou/time.txt'
130
        ];
131
        $expected = [
132
            'foo/gen/pics/bottle.jpg',
133
            'foo/lou/time.txt'
134
        ];
135
        $this->fixture->setFilesystem($this->mockFileSystem($files,['foo/bar']));
136
137
        $negatedAnd =
138
            (new InPath(new Path("foo/*")))
139
                ->andSpecification(new InPath(new Path("*/bar")))
140
                ->notSpecification();
141
142
        $this->assertEquals(
143
            $expected,
144
            $this->generatorToFileList($this->fixture->handle($negatedAnd))
145
        );
146
147
        $negatedAndDeMorgan =
148
            (new InPath(new Path("foo/*")))->notSpecification()
149
                ->orSpecification((new InPath(new Path("*/bar")))->notSpecification());
150
151
        $this->assertEquals(
152
            $expected,
153
            $this->generatorToFileList($this->fixture->handle($negatedAndDeMorgan))
154
        );
155
    }
156
157
158
    /**
159
     * @covers ::handle
160
     * @covers ::setFilesystem
161
     * @covers ::<private>
162
     */
163
    public function testIfCorrectFilesAreBeingYielded()
164
    {
165
        $isHidden = m::mock(IsHidden::class);
166
        $filesystem = m::mock(Filesystem::class);
167
168
        $listContents1 = [
169
            0 => [
170
                'type' => 'dir',
171
                'path' => '.hiddendir',
172
                'dirname' => '',
173
                'basename' => '.hiddendir',
174
                'filename' => '.hiddendir',
175
            ],
176
            1 => [
177
                'type' => 'file',
178
                'path' => 'test.txt',
179
                'basename' => 'test.txt',
180
            ],
181
        ];
182
183
        $listContents2 = [
184
            0 => [
185
                'type' => 'file',
186
                'path' => '.hiddendir/.test.txt',
187
                'dirname' => '.hiddendir',
188
                'basename' => '.test.txt',
189
                'filename' => '.test',
190
                'extension' => 'txt',
191
            ],
192
        ];
193
194
        $filesystem->shouldReceive('listContents')
195
            ->with('')
196
            ->andReturn($listContents1);
197
198
        $filesystem->shouldReceive('listContents')
199
            ->with('.hiddendir')
200
            ->andReturn($listContents2);
201
202
        $isHidden->shouldReceive('isSatisfiedBy')
203
            ->with($listContents1[0])
204
            ->andReturn(true);
205
206
        $isHidden->shouldReceive('canBeSatisfiedByAnythingBelow')
207
            ->with($listContents1[0])
208
            ->andReturn(true);
209
210
        $isHidden->shouldReceive('isSatisfiedBy')
211
            ->with($listContents1[1])
212
            ->andReturn(false);
213
214
        $isHidden->shouldReceive('isSatisfiedBy')
215
            ->with($listContents2[0])
216
            ->andReturn(true);
217
218
        $this->fixture->setFilesystem($filesystem);
219
        $generator = $this->fixture->handle($isHidden);
220
221
        $result = [];
222
223
        foreach ($generator as $value) {
224
            $result[] = $value;
225
        }
226
227
        $expected = [
228
            0 => [
229
                'type' => 'file',
230
                'path' => '.hiddendir/.test.txt',
231
                'dirname' => '.hiddendir',
232
                'basename' => '.test.txt',
233
                'filename' => '.test',
234
                'extension' => 'txt',
235
            ],
236
        ];
237
238
        $this->assertSame($expected, $result);
239
    }
240
241
    public function testSubtreeCullingOptimization()
242
    {
243
        $filesystem = $this->mockFileSystem(
244
            [
245
                'foo/bar/baz/file.txt',
246
                'foo/bar/baz/file2.txt',
247
                'foo/bar/baz/excluded/excluded.txt',
248
                'foo/bar/baz/excluded/culled/culled.txt',
249
                'foo/bar/baz/excluded/important/reincluded.txt',
250
                'foo/bar/file3.txt',
251
                'foo/lou/someSubdir/file4.txt',
252
                'foo/irrelevant1/',
253
                'irrelevant2/irrelevant3/irrelevantFile.txt'
254
            ],
255
            [
256
                'foo/irrelevant1',
257
                'irrelevant2',
258
                'foo/bar/baz/excluded/culled'
259
            ]
260
        );
261
262
        $inFooBar = new InPath(new Path("foo/bar"));
263
        $inFooLou = new InPath(new Path("foo/lou"));
264
        $inExcl = new InPath(new Path("foo/bar/baz/excl*"));
265
        $inReincl = new InPath(new Path("foo/bar/baz/*/important"));
266
        $spec =
267
            $inFooBar
268
                ->orSpecification($inFooLou)
269
                ->andSpecification($inExcl->notSpecification())
270
                ->orSpecification($inReincl);
271
272
        $finder = $this->fixture;
273
        $finder->setFilesystem($filesystem);
274
        $generator = $finder->handle($spec);
275
276
        $expected = [
277
            'foo/bar/baz/file.txt',
278
            'foo/bar/baz/file2.txt',
279
            'foo/bar/file3.txt',
280
            'foo/bar/baz/excluded/important/reincluded.txt',
281
            'foo/lou/someSubdir/file4.txt',
282
        ];
283
        sort($expected);
284
285
        $this->assertEquals($expected, $this->generatorToFileList($generator));
286
287
        $this->addToAssertionCount(1);
288
    }
289
290
    protected function generatorToFileList(\Generator $generator) : array
291
    {
292
        $actual = array_values(array_map(function($v) {return $v['path']; }, iterator_to_array($generator)));
293
        sort($actual);
294
        return $actual;
295
    }
296
297
    protected function mockFileTree(array $pathList) : array
298
    {
299
        $result = [
300
            "." => [
301
                'type' => 'dir',
302
                'path' => '',
303
                'dirname' => '.',
304
                'basename' => '.',
305
                'filename' => '.',
306
                'contents' => []
307
            ]
308
        ];
309
        foreach($pathList as $path) {
310
311
            $isFile = "/" !== substr($path,-1);
312
            $child = null;
313
            while(true) {
314
                $info = pathinfo($path);
315
                if ($isFile) {
316
                    $isFile = false;
317
                    $result[$path] = [
318
                        'type' => 'file',
319
                        'path' => $path,
320
                        'dirname' => $info['dirname'],
321
                        'basename' => $info['basename'],
322
                        'filename' => $info['filename'],
323
                        'extension' => $info['extension']
324
                    ];
325
                }
326
                else {
327
                    $existed = true;
328
                    if (!isset($result[$path])) {
329
                        $existed = false;
330
                        $result[$path] = [
331
                            'type' => 'dir',
332
                            'path' => $path,
333
                            'basename' => $info['basename'],
334
                            'filename' => $info['filename'],
335
                            'contents' => []
336
                        ];
337
                    }
338
                    if (null!==$child) {
339
                        $result[$path]['contents'][] = $child;
340
                    }
341
                    if ($existed) break;
342
                }
343
                $child = $info['basename'];
344
                $path = $info['dirname'];
345
            }
346
        }
347
        return $result;
348
    }
349
350
    protected function mockListContents(array $fileTreeMock, string $path) : array
351
    {
352
        $path = trim($path,"/");
353
        if (substr($path."  ",0,2)==="./") $path = substr($path,2);
354
        if ($path==="") $path = ".";
355
356
        if (!isset($fileTreeMock[$path]) || 'file' === $fileTreeMock[$path]['type']) {
357
            return [];
358
        }
359
        $result = [];
360
        foreach($fileTreeMock[$path]['contents'] as $basename) {
361
            $childPath = ($path==="." ? "" : $path."/").$basename;
362
            if (isset($fileTreeMock[$childPath])) {
363
                $result[$basename] = $fileTreeMock[$childPath];
364
            }
365
        }
366
        return $result;
367
    }
368
369
    protected function mockFileSystem(array $paths, array $pathsThatShouldNotBeListed = []) : FilesystemInterface
370
    {
371
        $fsData = $this->mockFileTree($paths);
372
        $filesystem = m::mock(Filesystem::class);
373
        $filesystem->shouldReceive('listContents')
374
            ->zeroOrMoreTimes()
375
            ->andReturnUsing(function(string $path) use ($fsData, $pathsThatShouldNotBeListed) : array {
376
377
                $this->assertNotContains($path, $pathsThatShouldNotBeListed);
378
                return array_values($this->mockListContents($fsData, $path));
379
            });
380
        return $filesystem;
381
    }
382
}
383