Passed
Push — master ( 599ef6...524477 )
by Alexander
01:29
created

FileCacheTest::testValuesAsIterable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Yiisoft\Cache\File\Tests;
4
5
require_once __DIR__ . '/MockHelper.php';
6
7
use DateInterval;
8
use phpmock\phpunit\PHPMock;
9
use Psr\SimpleCache\CacheInterface;
10
use Psr\SimpleCache\InvalidArgumentException;
11
use ReflectionException;
12
use Yiisoft\Cache\File\FileCache;
13
use Yiisoft\Cache\File\MockHelper;
14
15
class FileCacheTest extends TestCase
16
{
17
    use PHPMock;
18
19
    protected const CACHE_DIRECTORY = __DIR__ . '/runtime/cache';
20
21
    protected function tearDown(): void
22
    {
23
        MockHelper::$time = null;
24
    }
25
26
    protected function createCacheInstance(): CacheInterface
27
    {
28
        return new FileCache(static::CACHE_DIRECTORY);
29
    }
30
31
    /**
32
     * @dataProvider dataProvider
33
     * @param $key
34
     * @param $value
35
     * @throws InvalidArgumentException
36
     */
37
    public function testSet($key, $value): void
38
    {
39
        $cache = $this->createCacheInstance();
40
        $cache->clear();
41
42
        for ($i = 0; $i < 2; $i++) {
43
            $this->assertTrue($cache->set($key, $value));
44
        }
45
    }
46
47
    /**
48
     * @dataProvider dataProvider
49
     * @param $key
50
     * @param $value
51
     * @throws InvalidArgumentException
52
     */
53
    public function testGet($key, $value): void
54
    {
55
        $cache = $this->createCacheInstance();
56
        $cache->clear();
57
58
        $cache->set($key, $value);
59
        $valueFromCache = $cache->get($key, 'default');
60
61
        $this->assertSameExceptObject($value, $valueFromCache);
62
    }
63
64
    /**
65
     * @dataProvider dataProvider
66
     * @param $key
67
     * @param $value
68
     * @throws InvalidArgumentException
69
     */
70
    public function testValueInCacheCannotBeChanged($key, $value): void
71
    {
72
        $cache = $this->createCacheInstance();
73
        $cache->clear();
74
75
        $cache->set($key, $value);
76
        $valueFromCache = $cache->get($key, 'default');
77
78
        $this->assertSameExceptObject($value, $valueFromCache);
79
80
        if (is_object($value)) {
81
            $originalValue = clone $value;
82
            $valueFromCache->test_field = 'changed';
83
            $value->test_field = 'changed';
84
            $valueFromCacheNew = $cache->get($key, 'default');
85
            $this->assertSameExceptObject($originalValue, $valueFromCacheNew);
86
        }
87
    }
88
89
    /**
90
     * @dataProvider dataProvider
91
     * @param $key
92
     * @param $value
93
     * @throws InvalidArgumentException
94
     */
95
    public function testHas($key, $value): void
96
    {
97
        $cache = $this->createCacheInstance();
98
        $cache->clear();
99
100
        $cache->set($key, $value);
101
102
        $this->assertTrue($cache->has($key));
103
        // check whether exists affects the value
104
        $this->assertSameExceptObject($value, $cache->get($key));
105
106
        $this->assertTrue($cache->has($key));
107
        $this->assertFalse($cache->has('not_exists'));
108
    }
109
110
    public function testGetNonExistent(): void
111
    {
112
        $cache = $this->createCacheInstance();
113
        $cache->clear();
114
115
        $this->assertNull($cache->get('non_existent_key'));
116
    }
117
118
    /**
119
     * @dataProvider dataProvider
120
     * @param $key
121
     * @param $value
122
     * @throws InvalidArgumentException
123
     */
124
    public function testDelete($key, $value): void
125
    {
126
        $cache = $this->createCacheInstance();
127
        $cache->clear();
128
129
        $cache->set($key, $value);
130
131
        $this->assertSameExceptObject($value, $cache->get($key));
132
        $this->assertTrue($cache->delete($key));
133
        $this->assertNull($cache->get($key));
134
    }
135
136
    /**
137
     * @dataProvider dataProvider
138
     * @param $key
139
     * @param $value
140
     * @throws InvalidArgumentException
141
     */
142
    public function testClear($key, $value): void
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

142
    public function testClear($key, /** @scrutinizer ignore-unused */ $value): void

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

Loading history...
143
    {
144
        $cache = $this->createCacheInstance();
145
        $cache = $this->prepare($cache);
146
147
        $this->assertTrue($cache->clear());
148
        $this->assertNull($cache->get($key));
149
    }
150
151
    /**
152
     * @dataProvider dataProviderSetMultiple
153
     * @param int|null $ttl
154
     * @throws InvalidArgumentException
155
     */
156
    public function testSetMultiple(?int $ttl): void
157
    {
158
        $cache = $this->createCacheInstance();
159
        $cache->clear();
160
161
        $data = $this->getDataProviderData();
162
163
        $cache->setMultiple($data, $ttl);
164
165
        foreach ($data as $key => $value) {
166
            $this->assertSameExceptObject($value, $cache->get($key));
167
        }
168
    }
169
170
    /**
171
     * @return array testing multiSet with and without expiry
172
     */
173
    public function dataProviderSetMultiple(): array
174
    {
175
        return [
176
            [null],
177
            [2],
178
        ];
179
    }
180
181
    public function testGetMultiple(): void
182
    {
183
        $cache = $this->createCacheInstance();
184
        $cache->clear();
185
186
        $data = $this->getDataProviderData();
187
188
        $cache->setMultiple($data);
189
190
        $this->assertSameExceptObject($data, $cache->getMultiple(array_keys($data)));
191
    }
192
193
    public function testDeleteMultiple(): void
194
    {
195
        $cache = $this->createCacheInstance();
196
        $cache->clear();
197
198
        $data = $this->getDataProviderData();
199
200
        $cache->setMultiple($data);
201
202
        $this->assertSameExceptObject($data, $cache->getMultiple(array_keys($data)));
203
204
        $cache->deleteMultiple(array_keys($data));
205
206
        $emptyData = array_map(static function ($v) {
0 ignored issues
show
Unused Code introduced by
The parameter $v is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

206
        $emptyData = array_map(static function (/** @scrutinizer ignore-unused */ $v) {

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

Loading history...
207
            return null;
208
        }, $data);
209
210
        $this->assertSameExceptObject($emptyData, $cache->getMultiple(array_keys($data)));
211
    }
212
213
    public function testZeroAndNegativeTtl(): void
214
    {
215
        $cache = $this->createCacheInstance();
216
        $cache->clear();
217
        $cache->setMultiple([
218
            'a' => 1,
219
            'b' => 2,
220
        ]);
221
222
        $this->assertTrue($cache->has('a'));
223
        $this->assertTrue($cache->has('b'));
224
225
        $cache->set('a', 11, -1);
226
227
        $this->assertFalse($cache->has('a'));
228
229
        $cache->set('b', 22, 0);
230
231
        $this->assertFalse($cache->has('b'));
232
    }
233
234
    /**
235
     * @dataProvider dataProviderNormalizeTtl
236
     * @param mixed $ttl
237
     * @param mixed $expectedResult
238
     * @throws ReflectionException
239
     */
240
    public function testNormalizeTtl($ttl, $expectedResult): void
241
    {
242
        $cache = new FileCache(static::CACHE_DIRECTORY);
243
        $this->assertSameExceptObject($expectedResult, $this->invokeMethod($cache, 'normalizeTtl', [$ttl]));
244
    }
245
246
    /**
247
     * Data provider for {@see testNormalizeTtl()}
248
     * @return array test data
249
     *
250
     * @throws \Exception
251
     */
252
    public function dataProviderNormalizeTtl(): array
253
    {
254
        return [
255
            [123, 123],
256
            ['123', 123],
257
            [null, null],
258
            [0, 0],
259
            [new DateInterval('PT6H8M'), 6 * 3600 + 8 * 60],
260
            [new DateInterval('P2Y4D'), 2 * 365 * 24 * 3600 + 4 * 24 * 3600],
261
        ];
262
    }
263
264
    /**
265
     * @dataProvider ttlToExpirationProvider
266
     * @param mixed $ttl
267
     * @param mixed $expected
268
     * @throws ReflectionException
269
     */
270
    public function testTtlToExpiration($ttl, $expected): void
271
    {
272
        if ($expected === 'calculate_expiration') {
273
            MockHelper::$time = \time();
274
            $expected = MockHelper::$time + $ttl;
275
        }
276
        if ($expected === 'calculate_max_expiration') {
277
            MockHelper::$time = \time();
278
            $expected = MockHelper::$time + 31536000;
279
        }
280
        $cache = new FileCache(static::CACHE_DIRECTORY);
281
        $this->assertSameExceptObject($expected, $this->invokeMethod($cache, 'ttlToExpiration', [$ttl]));
282
    }
283
284
    public function ttlToExpirationProvider(): array
285
    {
286
        return [
287
            [3, 'calculate_expiration'],
288
            [null, 'calculate_max_expiration'],
289
            [-5, -1],
290
        ];
291
    }
292
293
    /**
294
     * @dataProvider iterableProvider
295
     * @param array $array
296
     * @param iterable $iterable
297
     * @throws InvalidArgumentException
298
     */
299
    public function testValuesAsIterable(array $array, iterable $iterable): void
300
    {
301
        $cache = $this->createCacheInstance();
302
        $cache->clear();
303
304
        $cache->setMultiple($iterable);
305
306
        $this->assertSameExceptObject($array, $cache->getMultiple(array_keys($array)));
307
    }
308
309
    public function iterableProvider(): array
310
    {
311
        return [
312
            'array' => [
313
                ['a' => 1, 'b' => 2,],
314
                ['a' => 1, 'b' => 2,],
315
            ],
316
            'ArrayIterator' => [
317
                ['a' => 1, 'b' => 2,],
318
                new \ArrayIterator(['a' => 1, 'b' => 2,]),
319
            ],
320
            'IteratorAggregate' => [
321
                ['a' => 1, 'b' => 2,],
322
                new class() implements \IteratorAggregate
323
                {
324
                    public function getIterator()
325
                    {
326
                        return new \ArrayIterator(['a' => 1, 'b' => 2,]);
327
                    }
328
                }
329
            ],
330
            'generator' => [
331
                ['a' => 1, 'b' => 2,],
332
                (static function () {
333
                    yield 'a' => 1;
334
                    yield 'b' => 2;
335
                })()
336
            ]
337
        ];
338
    }
339
340
    public function testExpire(): void
341
    {
342
        $cache = $this->createCacheInstance();
343
        $cache->clear();
344
345
        MockHelper::$time = \time();
346
        $this->assertTrue($cache->set('expire_test', 'expire_test', 2));
347
        MockHelper::$time++;
348
        $this->assertEquals('expire_test', $cache->get('expire_test'));
349
        MockHelper::$time++;
350
        $this->assertNull($cache->get('expire_test'));
351
    }
352
353
    /**
354
     * We have to on separate process because of PHPMock not being able to mock a function that
355
     * was already called.
356
     * @runInSeparateProcess
357
     */
358
    public function testCacheRenewalOnDifferentOwnership(): void
359
    {
360
        if (!function_exists('posix_geteuid')) {
361
            $this->markTestSkipped('Can not test without posix extension installed.');
362
        }
363
364
        $cache = $this->createCacheInstance();
365
        $cache->clear();
366
367
        $cacheValue = uniqid('value_', false);
368
        $cacheKey = uniqid('key_', false);
369
370
        MockHelper::$time = \time();
371
        $this->assertTrue($cache->set($cacheKey, $cacheValue, 2));
372
        $this->assertSame($cacheValue, $cache->get($cacheKey));
373
374
        // Override fileowner method so it always returns something not equal to the current user
375
        $notCurrentEuid = posix_geteuid() + 15;
376
        $this->getFunctionMock('Yiisoft\Cache\File', 'fileowner')->expects($this->any())->willReturn($notCurrentEuid);
377
        $this->getFunctionMock('Yiisoft\Cache\File', 'unlink')->expects($this->once());
378
379
        $this->assertTrue($cache->set($cacheKey, uniqid('value_2_', false), 2), 'Cannot rebuild cache on different file ownership');
380
    }
381
382
    public function testSetWithDateIntervalTtl(): void
383
    {
384
        $cache = $this->createCacheInstance();
385
        $cache->clear();
386
387
        $cache->set('a', 1, new DateInterval('PT1H'));
388
        $this->assertSameExceptObject(1, $cache->get('a'));
389
390
        $cache->setMultiple(['b' => 2]);
391
        $this->assertSameExceptObject(['b' => 2], $cache->getMultiple(['b']));
392
    }
393
394
    public function testCacheFileSuffix(): void
395
    {
396
        /** @var FileCache $cache */
397
        $cache = $this->createCacheInstance();
398
        $cache->clear();
399
        $cache->setCacheFileSuffix('.test');
400
401
        $cache->set('a', 1);
402
        $this->assertSameExceptObject(1, $cache->get('a'));
403
404
        $cacheFile = $this->invokeMethod($cache, 'getCacheFile', ['a']);
405
406
        $this->assertEquals('.test', substr($cacheFile, -5));
407
    }
408
409
    public function testDirectoryLevel(): void
410
    {
411
        /** @var FileCache $cache */
412
        $cache = $this->createCacheInstance();
413
        $cache->clear();
414
        $cache->setDirectoryLevel(0);
415
416
        $cache->set('a', 1);
417
        $this->assertSameExceptObject(1, $cache->get('a'));
418
419
        $cacheFile = $this->invokeMethod($cache, 'getCacheFile', ['a']);
420
421
        $this->assertEquals(__DIR__ . '/runtime/cache/a.bin', $cacheFile);
422
    }
423
424
    public function testFileMode(): void
425
    {
426
        $cache = new FileCache('/tmp/test_file_cache');
427
        $cache->clear();
428
        $cache->setFileMode(0755);
429
430
        $cache->set('a', 1);
431
        $this->assertSameExceptObject(1, $cache->get('a'));
432
433
        $cacheFile = $this->invokeMethod($cache, 'getCacheFile', ['a']);
434
435
        $permissions = substr(sprintf('%o', fileperms($cacheFile)), -4);
436
437
        $this->assertEquals('0755', $permissions);
438
    }
439
440
    public function testDirMode(): void
441
    {
442
        $cache = new FileCache('/tmp/test_file_cache');
443
        $cache->clear();
444
        $cache->setDirMode(0755);
445
446
        $cache->set('a', 1);
447
        $this->assertSameExceptObject(1, $cache->get('a'));
448
449
        $cacheFile = $this->invokeMethod($cache, 'getCacheFile', ['a']);
450
451
        $permissions = substr(sprintf('%o', fileperms(dirname($cacheFile))), -4);
452
453
        $this->assertEquals('0755', $permissions);
454
    }
455
456
    public function testGcProbability(): void
457
    {
458
        /** @var FileCache $cache */
459
        $cache = $this->createCacheInstance();
460
        $cache->clear();
461
        $cache->setGcProbability(1000000);
462
463
        $key = 'gc_probability_test';
464
465
        MockHelper::$time = \time();
466
467
        $cache->set($key, 1, 1);
468
469
        $this->assertSameExceptObject(1, $cache->get($key));
470
471
        $cacheFile = $this->invokeMethod($cache, 'getCacheFile', [$key]);
472
473
        $this->assertFileExists($cacheFile);
474
475
        MockHelper::$time++;
476
        MockHelper::$time++;
477
478
        $cache->set('b', 2);
479
480
        $this->assertFileNotExists($cacheFile);
481
    }
482
}
483