Passed
Push — master ( 524477...8e7758 )
by Alexander
01:21
created

php$0 ➔ testDeleteMultipleInvalidKeys()   A

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 5
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((string)$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
        $keys = array_map('strval', array_keys($data));
188
189
        $cache->setMultiple($data);
190
191
        $this->assertSameExceptObject($data, $cache->getMultiple($keys));
192
    }
193
194
    public function testDeleteMultiple(): void
195
    {
196
        $cache = $this->createCacheInstance();
197
        $cache->clear();
198
199
        $data = $this->getDataProviderData();
200
        $keys = array_map('strval', array_keys($data));
201
202
        $cache->setMultiple($data);
203
204
        $this->assertSameExceptObject($data, $cache->getMultiple($keys));
205
206
        $cache->deleteMultiple($keys);
207
208
        $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

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