Passed
Pull Request — master (#30)
by Alexander
01:27
created

FileCacheTest::testExpire()

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

144
    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...
145
    {
146
        $cache = $this->createCacheInstance();
147
        $cache = $this->prepare($cache);
148
149
        $this->assertTrue($cache->clear());
150
        $this->assertNull($cache->get($key));
151
    }
152
153
    /**
154
     * @dataProvider dataProviderSetMultiple
155
     * @param int|null $ttl
156
     * @throws InvalidArgumentException
157
     */
158
    public function testSetMultiple(?int $ttl): void
159
    {
160
        $cache = $this->createCacheInstance();
161
        $cache->clear();
162
163
        $data = $this->getDataProviderData();
164
165
        $cache->setMultiple($data, $ttl);
166
167
        foreach ($data as $key => $value) {
168
            $this->assertSameExceptObject($value, $cache->get($key));
169
        }
170
    }
171
172
    /**
173
     * @return array testing multiSet with and without expiry
174
     */
175
    public function dataProviderSetMultiple(): array
176
    {
177
        return [
178
            [null],
179
            [2],
180
        ];
181
    }
182
183
    public function testGetMultiple(): void
184
    {
185
        /** @var Cache $cache */
186
        $cache = $this->createCacheInstance();
187
        $cache->clear();
188
189
        $data = $this->getDataProviderData();
190
191
        $cache->setMultiple($data);
192
193
        $this->assertSameExceptObject($data, $cache->getMultiple(array_keys($data)));
194
    }
195
196
    public function testDeleteMultiple(): void
197
    {
198
        /** @var Cache $cache */
199
        $cache = $this->createCacheInstance();
200
        $cache->clear();
201
202
        $data = $this->getDataProviderData();
203
204
        $cache->setMultiple($data);
205
206
        $this->assertSameExceptObject($data, $cache->getMultiple(array_keys($data)));
207
208
        $cache->deleteMultiple(array_keys($data));
209
210
        $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

210
        $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...
211
            return null;
212
        }, $data);
213
214
        $this->assertSameExceptObject($emptyData, $cache->getMultiple(array_keys($data)));
215
    }
216
217
    public function testZeroAndNegativeTtl()
218
    {
219
        $cache = $this->createCacheInstance();
220
        $cache->clear();
221
        $cache->setMultiple([
222
            'a' => 1,
223
            'b' => 2,
224
        ]);
225
226
        $this->assertTrue($cache->has('a'));
227
        $this->assertTrue($cache->has('b'));
228
229
        $cache->set('a', 11, -1);
230
231
        $this->assertFalse($cache->has('a'));
232
233
        $cache->set('b', 22, 0);
234
235
        $this->assertFalse($cache->has('b'));
236
    }
237
238
    /**
239
     * @dataProvider dataProviderNormalizeTtl
240
     * @param mixed $ttl
241
     * @param mixed $expectedResult
242
     * @throws ReflectionException
243
     */
244
    public function testNormalizeTtl($ttl, $expectedResult): void
245
    {
246
        $cache = $this->createCacheInstance();
247
        $this->assertSameExceptObject($expectedResult, $this->invokeMethod($cache, 'normalizeTtl', [$ttl]));
248
    }
249
250
    /**
251
     * Data provider for {@see testNormalizeTtl()}
252
     * @return array test data
253
     *
254
     * @throws \Exception
255
     */
256
    public function dataProviderNormalizeTtl(): array
257
    {
258
        return [
259
            [123, 123],
260
            ['123', 123],
261
            [null, null],
262
            [0, 0],
263
            [new DateInterval('PT6H8M'), 6 * 3600 + 8 * 60],
264
            [new DateInterval('P2Y4D'), 2 * 365 * 24 * 3600 + 4 * 24 * 3600],
265
        ];
266
    }
267
268
    /**
269
     * @dataProvider ttlToExpirationProvider
270
     * @param mixed $ttl
271
     * @param mixed $expected
272
     * @throws ReflectionException
273
     */
274
    public function testTtlToExpiration($ttl, $expected): void
275
    {
276
        if ($expected === 'calculate_expiration') {
277
            MockHelper::$time = \time();
278
            $expected = MockHelper::$time + $ttl;
279
        }
280
        if ($expected === 'calculate_max_expiration') {
281
            MockHelper::$time = \time();
282
            $expected = MockHelper::$time + 31536000;
283
        }
284
        $cache = new FileCache(static::CACHE_DIRECTORY);
285
        $this->assertSameExceptObject($expected, $this->invokeMethod($cache, 'ttlToExpiration', [$ttl]));
286
    }
287
288
    public function ttlToExpirationProvider(): array
289
    {
290
        return [
291
            [3, 'calculate_expiration'],
292
            [null, 'calculate_max_expiration'],
293
            [-5, -1],
294
        ];
295
    }
296
297
    /**
298
     * @dataProvider iterableProvider
299
     * @param array $array
300
     * @param iterable $iterable
301
     * @throws InvalidArgumentException
302
     */
303
    public function testValuesAsIterable(array $array, iterable $iterable): void
304
    {
305
        $cache = $this->createCacheInstance();
306
        $cache->clear();
307
308
        $cache->setMultiple($iterable);
309
310
        $this->assertSameExceptObject($array, $cache->getMultiple(array_keys($array)));
311
    }
312
313
    public function iterableProvider(): array
314
    {
315
        return [
316
            'array' => [
317
                ['a' => 1, 'b' => 2,],
318
                ['a' => 1, 'b' => 2,],
319
            ],
320
            'ArrayIterator' => [
321
                ['a' => 1, 'b' => 2,],
322
                new \ArrayIterator(['a' => 1, 'b' => 2,]),
323
            ],
324
            'IteratorAggregate' => [
325
                ['a' => 1, 'b' => 2,],
326
                new class() implements \IteratorAggregate
327
                {
328
                    public function getIterator()
329
                    {
330
                        return new \ArrayIterator(['a' => 1, 'b' => 2,]);
331
                    }
332
                }
333
            ],
334
            'generator' => [
335
                ['a' => 1, 'b' => 2,],
336
                (static function () {
337
                    yield 'a' => 1;
338
                    yield 'b' => 2;
339
                })()
340
            ]
341
        ];
342
    }
343
344
    public function testExpire(): void
345
    {
346
        $cache = $this->createCacheInstance();
347
        $cache->clear();
348
349
        MockHelper::$time = \time();
350
        $this->assertTrue($cache->set('expire_test', 'expire_test', 2));
351
        MockHelper::$time++;
352
        $this->assertEquals('expire_test', $cache->get('expire_test'));
353
        MockHelper::$time++;
354
        $this->assertNull($cache->get('expire_test'));
355
    }
356
357
    /**
358
     * We have to on separate process because of PHPMock not being able to mock a function that
359
     * was already called.
360
     * @runInSeparateProcess
361
     */
362
    public function testCacheRenewalOnDifferentOwnership(): void
363
    {
364
        if (!function_exists('posix_geteuid')) {
365
            $this->markTestSkipped('Can not test without posix extension installed.');
366
        }
367
368
        $cache = $this->createCacheInstance();
369
        $cache->clear();
370
371
        $cacheValue = uniqid('value_', false);
372
        $cacheKey = uniqid('key_', false);
373
374
        MockHelper::$time = \time();
375
        $this->assertTrue($cache->set($cacheKey, $cacheValue, 2));
376
        $this->assertSame($cacheValue, $cache->get($cacheKey));
377
378
        // Override fileowner method so it always returns something not equal to the current user
379
        $notCurrentEuid = posix_geteuid() + 15;
380
        $this->getFunctionMock('Yiisoft\Cache', 'fileowner')->expects($this->any())->willReturn($notCurrentEuid);
381
        $this->getFunctionMock('Yiisoft\Cache', 'unlink')->expects($this->once());
382
383
        $this->assertTrue($cache->set($cacheKey, uniqid('value_2_', false), 2), 'Cannot rebuild cache on different file ownership');
384
    }
385
}
386