Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace core_cache;
18
 
19
use cache;
20
use cache_application;
21
use cache_config;
22
use cache_config_disabled;
23
use cache_config_testing;
24
use cache_definition;
25
use cache_disabled;
26
use cache_factory;
27
use cache_factory_disabled;
28
use cache_helper;
29
use cache_loader;
30
use cache_phpunit_application;
31
use cache_phpunit_cache;
32
use cache_phpunit_dummy_object;
33
use cache_phpunit_dummy_overrideclass;
34
use cache_phpunit_factory;
35
use cache_phpunit_request;
36
use cache_phpunit_session;
37
use cache_request;
38
use cache_session;
39
use cache_store;
40
use cacheable_object_array;
41
 
42
/**
43
 * PHPunit tests for the cache API
44
 *
45
 * This file is part of Moodle's cache API, affectionately called MUC.
46
 * It contains the components that are requried in order to use caching.
47
 *
48
 * @package    core
49
 * @category   cache
50
 * @copyright  2012 Sam Hemelryk
51
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
52
 * @coversDefaultClass \cache
53
 * @covers \cache
54
 */
55
class cache_test extends \advanced_testcase {
56
 
57
    /**
58
     * Load required libraries and fixtures.
59
     */
60
    public static function setUpBeforeClass(): void {
61
        global $CFG;
62
 
63
        require_once($CFG->dirroot . '/cache/locallib.php');
64
        require_once($CFG->dirroot . '/cache/tests/fixtures/lib.php');
65
        require_once($CFG->dirroot . '/cache/tests/fixtures/cache_phpunit_dummy_datasource_versionable.php');
66
    }
67
 
68
    /**
69
     * Set things back to the default before each test.
70
     */
71
    public function setUp(): void {
72
        parent::setUp();
73
        cache_factory::reset();
74
        cache_config_testing::create_default_configuration();
75
    }
76
 
77
    /**
78
     * Final task is to reset the cache system
79
     */
80
    public static function tearDownAfterClass(): void {
81
        parent::tearDownAfterClass();
82
        cache_factory::reset();
83
    }
84
 
85
    /**
86
     * Returns the expected application cache store.
87
     * @return string
88
     */
89
    protected function get_expected_application_cache_store() {
90
        global $CFG;
91
        $expected = 'cachestore_file';
92
 
93
        // Verify if we are using any of the available ways to use a different application store within tests.
94
        if (defined('TEST_CACHE_USING_APPLICATION_STORE') && preg_match('#[a-zA-Z][a-zA-Z0-9_]*#', TEST_CACHE_USING_APPLICATION_STORE)) {
95
            // 1st way. Using some of the testing servers.
96
            $expected = 'cachestore_'.(string)TEST_CACHE_USING_APPLICATION_STORE;
97
 
98
        } else if (defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH && !empty($CFG->altcacheconfigpath)) {
99
            // 2nd way. Using an alternative configuration.
100
            $defaultstores = cache_helper::get_stores_suitable_for_mode_default();
101
            $instance = cache_config::instance();
102
            // Iterate over defined mode mappings until we get an application one not being the default.
103
            foreach ($instance->get_mode_mappings() as $mapping) {
104
                // If the store is not for application mode, ignore.
105
                if ($mapping['mode'] !== cache_store::MODE_APPLICATION) {
106
                    continue;
107
                }
108
                // If the store matches some default mapping store name, ignore.
109
                if (array_key_exists($mapping['store'], $defaultstores) && !empty($defaultstores[$mapping['store']]['default'])) {
110
                    continue;
111
                }
112
                // Arrived here, have found an application mode store not being the default mapped one (file),
113
                // that's the one we are using in the configuration for sure.
114
                $expected = 'cachestore_'.$mapping['store'];
115
            }
116
        }
117
 
118
        return $expected;
119
    }
120
 
121
    /**
122
     * Tests cache configuration
123
     */
11 efrain 124
    public function test_cache_config(): void {
1 efrain 125
        global $CFG;
126
 
127
        if (defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH &&
128
            !empty($CFG->altcacheconfigpath)) {
129
            // We need to skip this test - it checks the default config structure, but very likely we arn't using the
130
            // default config structure here so theres no point in running the test.
131
            $this->markTestSkipped('Skipped testing default cache config structure as alt cache path is being used.');
132
        }
133
 
134
        if (defined('TEST_CACHE_USING_APPLICATION_STORE')) {
135
            // We need to skip this test - it checks the default config structure, but very likely we arn't using the
136
            // default config structure here because we are testing against an alternative application store.
137
            $this->markTestSkipped('Skipped testing default cache config structure as alt application store is being used.');
138
        }
139
 
140
        $instance = cache_config::instance();
141
        $this->assertInstanceOf(cache_config_testing::class, $instance);
142
 
143
        $this->assertTrue(cache_config_testing::config_file_exists());
144
 
145
        $stores = $instance->get_all_stores();
146
        $this->assertCount(3, $stores);
147
        foreach ($stores as $name => $store) {
148
            // Check its an array.
149
            $this->assertIsArray($store);
150
            // Check the name is the key.
151
            $this->assertEquals($name, $store['name']);
152
            // Check that it has been declared default.
153
            $this->assertTrue($store['default']);
154
            // Required attributes = name + plugin + configuration + modes + features.
155
            $this->assertArrayHasKey('name', $store);
156
            $this->assertArrayHasKey('plugin', $store);
157
            $this->assertArrayHasKey('configuration', $store);
158
            $this->assertArrayHasKey('modes', $store);
159
            $this->assertArrayHasKey('features', $store);
160
        }
161
 
162
        $modemappings = $instance->get_mode_mappings();
163
        $this->assertCount(3, $modemappings);
164
        $modes = array(
165
            cache_store::MODE_APPLICATION => false,
166
            cache_store::MODE_SESSION => false,
167
            cache_store::MODE_REQUEST => false,
168
        );
169
        foreach ($modemappings as $mapping) {
170
            // We expect 3 properties.
171
            $this->assertCount(3, $mapping);
172
            // Required attributes = mode + store.
173
            $this->assertArrayHasKey('mode', $mapping);
174
            $this->assertArrayHasKey('store', $mapping);
175
            // Record the mode.
176
            $modes[$mapping['mode']] = true;
177
        }
178
 
179
        // Must have the default 3 modes and no more.
180
        $this->assertCount(3, $mapping);
181
        foreach ($modes as $mode) {
182
            $this->assertTrue($mode);
183
        }
184
 
185
        $definitions = $instance->get_definitions();
186
        // The event invalidation definition is required for the cache API and must be there.
187
        $this->assertArrayHasKey('core/eventinvalidation', $definitions);
188
 
189
        $definitionmappings = $instance->get_definition_mappings();
190
        foreach ($definitionmappings as $mapping) {
191
            // Required attributes = definition + store.
192
            $this->assertArrayHasKey('definition', $mapping);
193
            $this->assertArrayHasKey('store', $mapping);
194
        }
195
    }
196
 
197
    /**
198
     * Tests for cache keys that would break on windows.
199
     */
11 efrain 200
    public function test_windows_nasty_keys(): void {
1 efrain 201
        $instance = cache_config_testing::instance();
202
        $instance->phpunit_add_definition('phpunit/windowskeytest', array(
203
            'mode' => cache_store::MODE_APPLICATION,
204
            'component' => 'phpunit',
205
            'area' => 'windowskeytest',
206
            'simplekeys' => true,
207
            'simpledata' => true
208
        ));
209
        $cache = cache::make('phpunit', 'windowskeytest');
210
        $this->assertTrue($cache->set('contest', 'test data 1'));
211
        $this->assertEquals('test data 1', $cache->get('contest'));
212
    }
213
 
214
    /**
215
     * Tests set_identifiers fails post cache creation.
216
     *
217
     * set_identifiers cannot be called after initial cache instantiation, as you need to create a difference cache.
218
     */
11 efrain 219
    public function test_set_identifiers(): void {
1 efrain 220
        $instance = cache_config_testing::instance();
221
        $instance->phpunit_add_definition('phpunit/identifier', array(
222
            'mode' => cache_store::MODE_APPLICATION,
223
            'component' => 'phpunit',
224
            'area' => 'identifier',
225
            'simplekeys' => true,
226
            'simpledata' => true,
227
            'staticacceleration' => true
228
        ));
229
        $cache = cache::make('phpunit', 'identifier', array('area'));
230
        $this->assertTrue($cache->set('contest', 'test data 1'));
231
        $this->assertEquals('test data 1', $cache->get('contest'));
232
 
233
        $this->expectException('coding_exception');
234
        $cache->set_identifiers(array());
235
    }
236
 
237
    /**
238
     * Tests the default application cache
239
     */
11 efrain 240
    public function test_default_application_cache(): void {
1 efrain 241
        $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'applicationtest');
242
        $this->assertInstanceOf(cache_application::class, $cache);
243
        $this->run_on_cache($cache);
244
 
245
        $instance = cache_config_testing::instance(true);
246
        $instance->phpunit_add_definition('phpunit/test_default_application_cache', array(
247
            'mode' => cache_store::MODE_APPLICATION,
248
            'component' => 'phpunit',
249
            'area' => 'test_default_application_cache',
250
            'staticacceleration' => true,
251
            'staticaccelerationsize' => 1
252
        ));
253
        $cache = cache::make('phpunit', 'test_default_application_cache');
254
        $this->assertInstanceOf(cache_application::class, $cache);
255
        $this->run_on_cache($cache);
256
    }
257
 
258
    /**
259
     * Tests the default session cache
260
     */
11 efrain 261
    public function test_default_session_cache(): void {
1 efrain 262
        $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'applicationtest');
263
        $this->assertInstanceOf(cache_session::class, $cache);
264
        $this->run_on_cache($cache);
265
    }
266
 
267
    /**
268
     * Tests the default request cache
269
     */
11 efrain 270
    public function test_default_request_cache(): void {
1 efrain 271
        $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'applicationtest');
272
        $this->assertInstanceOf(cache_request::class, $cache);
273
        $this->run_on_cache($cache);
274
    }
275
 
276
    /**
277
     * Tests using a cache system when there are no stores available (who knows what the admin did to achieve this).
278
     */
11 efrain 279
    public function test_on_cache_without_store(): void {
1 efrain 280
        $instance = cache_config_testing::instance(true);
281
        $instance->phpunit_add_definition('phpunit/nostoretest1', array(
282
            'mode' => cache_store::MODE_APPLICATION,
283
            'component' => 'phpunit',
284
            'area' => 'nostoretest1',
285
        ));
286
        $instance->phpunit_add_definition('phpunit/nostoretest2', array(
287
            'mode' => cache_store::MODE_APPLICATION,
288
            'component' => 'phpunit',
289
            'area' => 'nostoretest2',
290
            'staticacceleration' => true
291
        ));
292
        $instance->phpunit_remove_stores();
293
 
294
        $cache = cache::make('phpunit', 'nostoretest1');
295
        $this->run_on_cache($cache);
296
 
297
        $cache = cache::make('phpunit', 'nostoretest2');
298
        $this->run_on_cache($cache);
299
    }
300
 
301
    /**
302
     * Runs a standard series of access and use tests on a cache instance.
303
     *
304
     * This function is great because we can use it to ensure all of the loaders perform exactly the same way.
305
     *
306
     * @param cache_loader $cache
307
     */
308
    protected function run_on_cache(cache_loader $cache) {
309
        $key = 'contestkey';
310
        $datascalars = array('test data', null);
311
        $dataarray = array('contest' => 'data', 'part' => 'two');
312
        $dataobject = (object)$dataarray;
313
 
314
        foreach ($datascalars as $datascalar) {
315
            $this->assertTrue($cache->purge());
316
 
317
            // Check all read methods.
318
            $this->assertFalse($cache->get($key));
319
            $this->assertFalse($cache->has($key));
320
            $result = $cache->get_many(array($key));
321
            $this->assertCount(1, $result);
322
            $this->assertFalse(reset($result));
323
            $this->assertFalse($cache->has_any(array($key)));
324
            $this->assertFalse($cache->has_all(array($key)));
325
 
326
            // Set the data.
327
            $this->assertTrue($cache->set($key, $datascalar));
328
            // Setting it more than once should be permitted.
329
            $this->assertTrue($cache->set($key, $datascalar));
330
 
331
            // Recheck the read methods.
332
            $this->assertEquals($datascalar, $cache->get($key));
333
            $this->assertTrue($cache->has($key));
334
            $result = $cache->get_many(array($key));
335
            $this->assertCount(1, $result);
336
            $this->assertEquals($datascalar, reset($result));
337
            $this->assertTrue($cache->has_any(array($key)));
338
            $this->assertTrue($cache->has_all(array($key)));
339
 
340
            // Delete it.
341
            $this->assertTrue($cache->delete($key));
342
 
343
            // Check its gone.
344
            $this->assertFalse($cache->get($key));
345
            $this->assertFalse($cache->has($key));
346
        }
347
 
348
        // Test arrays.
349
        $this->assertTrue($cache->set($key, $dataarray));
350
        $this->assertEquals($dataarray, $cache->get($key));
351
 
352
        // Test objects.
353
        $this->assertTrue($cache->set($key, $dataobject));
354
        $this->assertEquals($dataobject, $cache->get($key));
355
 
356
        $starttime = microtime(true);
357
        $specobject = new cache_phpunit_dummy_object('red', 'blue', $starttime);
358
        $this->assertTrue($cache->set($key, $specobject));
359
        $result = $cache->get($key);
360
        $this->assertInstanceOf(cache_phpunit_dummy_object::class, $result);
361
        $this->assertEquals('red_ptc_wfc', $result->property1);
362
        $this->assertEquals('blue_ptc_wfc', $result->property2);
363
        $this->assertGreaterThan($starttime, $result->propertytime);
364
 
365
        // Test array of objects.
366
        $specobject = new cache_phpunit_dummy_object('red', 'blue', $starttime);
367
        $data = new cacheable_object_array(array(
368
            clone($specobject),
369
            clone($specobject),
370
            clone($specobject))
371
        );
372
        $this->assertTrue($cache->set($key, $data));
373
        $result = $cache->get($key);
374
        $this->assertInstanceOf(cacheable_object_array::class, $result);
375
        $this->assertCount(3, $data);
376
        foreach ($result as $item) {
377
            $this->assertInstanceOf(cache_phpunit_dummy_object::class, $item);
378
            $this->assertEquals('red_ptc_wfc', $item->property1);
379
            $this->assertEquals('blue_ptc_wfc', $item->property2);
380
            // Ensure that wake from cache is called in all cases.
381
            $this->assertGreaterThan($starttime, $item->propertytime);
382
        }
383
 
384
        // Test set many.
385
        $cache->set_many(array('key1' => 'data1', 'key2' => 'data2', 'key3' => null));
386
        $this->assertEquals('data1', $cache->get('key1'));
387
        $this->assertEquals('data2', $cache->get('key2'));
388
        $this->assertEquals(null, $cache->get('key3'));
389
        $this->assertTrue($cache->delete('key1'));
390
        $this->assertTrue($cache->delete('key2'));
391
        $this->assertTrue($cache->delete('key3'));
392
 
393
        $cache->set_many(array(
394
            'key1' => array(1, 2, 3),
395
            'key2' => array(3, 2, 1),
396
        ));
397
        $this->assertIsArray($cache->get('key1'));
398
        $this->assertIsArray($cache->get('key2'));
399
        $this->assertCount(3, $cache->get('key1'));
400
        $this->assertCount(3, $cache->get('key2'));
401
        $this->assertIsArray($cache->get_many(array('key1', 'key2')));
402
        $this->assertCount(2, $cache->get_many(array('key1', 'key2')));
403
        $this->assertEquals(2, $cache->delete_many(array('key1', 'key2')));
404
 
405
        // Test delete many.
406
        $this->assertTrue($cache->set('key1', 'data1'));
407
        $this->assertTrue($cache->set('key2', 'data2'));
408
        $this->assertTrue($cache->set('key3', null));
409
 
410
        $this->assertEquals('data1', $cache->get('key1'));
411
        $this->assertEquals('data2', $cache->get('key2'));
412
        $this->assertEquals(null, $cache->get('key3'));
413
 
414
        $this->assertEquals(3, $cache->delete_many(array('key1', 'key2', 'key3')));
415
 
416
        $this->assertFalse($cache->get('key1'));
417
        $this->assertFalse($cache->get('key2'));
418
        $this->assertFalse($cache->get('key3'));
419
 
420
        // Quick reference test.
421
        $obj = new \stdClass;
422
        $obj->key = 'value';
423
        $ref =& $obj;
424
        $this->assertTrue($cache->set('obj', $obj));
425
 
426
        $obj->key = 'eulav';
427
        $var = $cache->get('obj');
428
        $this->assertInstanceOf(\stdClass::class, $var);
429
        $this->assertEquals('value', $var->key);
430
 
431
        $ref->key = 'eulav';
432
        $var = $cache->get('obj');
433
        $this->assertInstanceOf(\stdClass::class, $var);
434
        $this->assertEquals('value', $var->key);
435
 
436
        $this->assertTrue($cache->delete('obj'));
437
 
438
        // Deep reference test.
439
        $obj1 = new \stdClass;
440
        $obj1->key = 'value';
441
        $obj2 = new \stdClass;
442
        $obj2->key = 'test';
443
        $obj3 = new \stdClass;
444
        $obj3->key = 'pork';
445
        $obj1->subobj =& $obj2;
446
        $obj2->subobj =& $obj3;
447
        $this->assertTrue($cache->set('obj', $obj1));
448
 
449
        $obj1->key = 'eulav';
450
        $obj2->key = 'tset';
451
        $obj3->key = 'krop';
452
        $var = $cache->get('obj');
453
        $this->assertInstanceOf(\stdClass::class, $var);
454
        $this->assertEquals('value', $var->key);
455
        $this->assertInstanceOf(\stdClass::class, $var->subobj);
456
        $this->assertEquals('test', $var->subobj->key);
457
        $this->assertInstanceOf(\stdClass::class, $var->subobj->subobj);
458
        $this->assertEquals('pork', $var->subobj->subobj->key);
459
        $this->assertTrue($cache->delete('obj'));
460
 
461
        // Death reference test... basically we don't want this to die.
462
        $obj = new \stdClass;
463
        $obj->key = 'value';
464
        $obj->self =& $obj;
465
        $this->assertTrue($cache->set('obj', $obj));
466
        $var = $cache->get('obj');
467
        $this->assertInstanceOf(\stdClass::class, $var);
468
        $this->assertEquals('value', $var->key);
469
 
470
        // Reference test after retrieve.
471
        $obj = new \stdClass;
472
        $obj->key = 'value';
473
        $this->assertTrue($cache->set('obj', $obj));
474
 
475
        $var1 = $cache->get('obj');
476
        $this->assertInstanceOf(\stdClass::class, $var1);
477
        $this->assertEquals('value', $var1->key);
478
        $var1->key = 'eulav';
479
        $this->assertEquals('eulav', $var1->key);
480
 
481
        $var2 = $cache->get('obj');
482
        $this->assertInstanceOf(\stdClass::class, $var2);
483
        $this->assertEquals('value', $var2->key);
484
 
485
        $this->assertTrue($cache->delete('obj'));
486
 
487
        // Death reference test on get_many... basically we don't want this to die.
488
        $obj = new \stdClass;
489
        $obj->key = 'value';
490
        $obj->self =& $obj;
491
        $this->assertEquals(1, $cache->set_many(array('obj' => $obj)));
492
        $var = $cache->get_many(array('obj'));
493
        $this->assertInstanceOf(\stdClass::class, $var['obj']);
494
        $this->assertEquals('value', $var['obj']->key);
495
 
496
        // Reference test after retrieve.
497
        $obj = new \stdClass;
498
        $obj->key = 'value';
499
        $this->assertEquals(1, $cache->set_many(array('obj' => $obj)));
500
 
501
        $var1 = $cache->get_many(array('obj'));
502
        $this->assertInstanceOf(\stdClass::class, $var1['obj']);
503
        $this->assertEquals('value', $var1['obj']->key);
504
        $var1['obj']->key = 'eulav';
505
        $this->assertEquals('eulav', $var1['obj']->key);
506
 
507
        $var2 = $cache->get_many(array('obj'));
508
        $this->assertInstanceOf(\stdClass::class, $var2['obj']);
509
        $this->assertEquals('value', $var2['obj']->key);
510
 
511
        $this->assertTrue($cache->delete('obj'));
512
 
513
        // Test strictness exceptions.
514
        try {
515
            $cache->get('exception', MUST_EXIST);
516
            $this->fail('Exception expected from cache::get using MUST_EXIST');
517
        } catch (\Exception $e) {
518
            $this->assertTrue(true);
519
        }
520
        try {
521
            $cache->get_many(array('exception1', 'exception2'), MUST_EXIST);
522
            $this->fail('Exception expected from cache::get_many using MUST_EXIST');
523
        } catch (\Exception $e) {
524
            $this->assertTrue(true);
525
        }
526
        $cache->set('test', 'test');
527
        try {
528
            $cache->get_many(array('test', 'exception'), MUST_EXIST);
529
            $this->fail('Exception expected from cache::get_many using MUST_EXIST');
530
        } catch (\Exception $e) {
531
            $this->assertTrue(true);
532
        }
533
    }
534
 
535
    /**
536
     * Tests a definition using a data loader
537
     */
11 efrain 538
    public function test_definition_data_loader(): void {
1 efrain 539
        $instance = cache_config_testing::instance(true);
540
        $instance->phpunit_add_definition('phpunit/datasourcetest', array(
541
            'mode' => cache_store::MODE_APPLICATION,
542
            'component' => 'phpunit',
543
            'area' => 'datasourcetest',
544
            'datasource' => 'cache_phpunit_dummy_datasource',
545
            'datasourcefile' => 'cache/tests/fixtures/lib.php'
546
        ));
547
 
548
        $cache = cache::make('phpunit', 'datasourcetest');
549
        $this->assertInstanceOf(cache_application::class, $cache);
550
 
551
        // Purge it to be sure.
552
        $this->assertTrue($cache->purge());
553
        // It won't be there yet.
554
        $this->assertFalse($cache->has('Test'));
555
        // It should load it ;).
556
        $this->assertTrue($cache->has('Test', true));
557
 
558
        // Purge it to be sure.
559
        $this->assertTrue($cache->purge());
560
        $this->assertEquals('Test has no value really.', $cache->get('Test'));
561
 
562
        // Test multiple values.
563
        $this->assertTrue($cache->purge());
564
        $this->assertTrue($cache->set('b', 'B'));
565
        $result = $cache->get_many(array('a', 'b', 'c'));
566
        $this->assertIsArray($result);
567
        $this->assertCount(3, $result);
568
        $this->assertArrayHasKey('a', $result);
569
        $this->assertArrayHasKey('b', $result);
570
        $this->assertArrayHasKey('c', $result);
571
        $this->assertEquals('a has no value really.', $result['a']);
572
        $this->assertEquals('B', $result['b']);
573
        $this->assertEquals('c has no value really.', $result['c']);
574
    }
575
 
576
    /**
577
     * Tests a definition using a data loader with versioned keys.
578
     *
579
     * @covers ::get_versioned
580
     * @covers ::set_versioned
581
     */
11 efrain 582
    public function test_definition_data_loader_versioned(): void {
1 efrain 583
        // Create two definitions, one using a non-versionable data source and the other using
584
        // a versionable one.
585
        $instance = cache_config_testing::instance(true);
586
        $instance->phpunit_add_definition('phpunit/datasourcetest1', array(
587
            'mode' => cache_store::MODE_APPLICATION,
588
            'component' => 'phpunit',
589
            'area' => 'datasourcetest1',
590
            'datasource' => 'cache_phpunit_dummy_datasource',
591
            'datasourcefile' => 'cache/tests/fixtures/lib.php'
592
        ));
593
        $instance->phpunit_add_definition('phpunit/datasourcetest2', array(
594
            'mode' => cache_store::MODE_APPLICATION,
595
            'component' => 'phpunit',
596
            'area' => 'datasourcetest2',
597
            'datasource' => 'cache_phpunit_dummy_datasource_versionable',
598
            'datasourcefile' => 'cache/tests/fixtures/lib.php'
599
        ));
600
 
601
        // The first data source works for normal 'get'.
602
        $cache1 = cache::make('phpunit', 'datasourcetest1');
603
        $this->assertEquals('Frog has no value really.', $cache1->get('Frog'));
604
 
605
        // But it doesn't work for get_versioned.
606
        try {
607
            $cache1->get_versioned('zombie', 1);
608
            $this->fail();
609
        } catch (\coding_exception $e) {
610
            $this->assertStringContainsString('Data source is not versionable', $e->getMessage());
611
        }
612
 
613
        // The second data source works for get_versioned. Set up the datasource first.
614
        $cache2 = cache::make('phpunit', 'datasourcetest2');
615
 
616
        $datasource = \cache_phpunit_dummy_datasource_versionable::get_last_instance();
617
        $datasource->has_value('frog', 3, 'Kermit');
618
 
619
        // Check data with no value.
620
        $this->assertFalse($cache2->get_versioned('zombie', 1));
621
 
622
        // Check data with value in datastore of required version.
623
        $result = $cache2->get_versioned('frog', 3, IGNORE_MISSING, $actualversion);
624
        $this->assertEquals('Kermit', $result);
625
        $this->assertEquals(3, $actualversion);
626
 
627
        // Check when the datastore doesn't have required version.
628
        $this->assertFalse($cache2->get_versioned('frog', 4));
629
    }
630
 
631
    /**
632
     * Tests a definition using an overridden loader
633
     */
11 efrain 634
    public function test_definition_overridden_loader(): void {
1 efrain 635
        $instance = cache_config_testing::instance(true);
636
        $instance->phpunit_add_definition('phpunit/overridetest', array(
637
            'mode' => cache_store::MODE_APPLICATION,
638
            'component' => 'phpunit',
639
            'area' => 'overridetest',
640
            'overrideclass' => 'cache_phpunit_dummy_overrideclass',
641
            'overrideclassfile' => 'cache/tests/fixtures/lib.php'
642
        ));
643
        $cache = cache::make('phpunit', 'overridetest');
644
        $this->assertInstanceOf(cache_phpunit_dummy_overrideclass::class, $cache);
645
        $this->assertInstanceOf(cache_application::class, $cache);
646
        // Purge it to be sure.
647
        $this->assertTrue($cache->purge());
648
        // It won't be there yet.
649
        $this->assertFalse($cache->has('Test'));
650
        // Add it.
651
        $this->assertTrue($cache->set('Test', 'Test has no value really.'));
652
        // Check its there.
653
        $this->assertEquals('Test has no value really.', $cache->get('Test'));
654
    }
655
 
656
    /**
657
     * Test the mappingsonly setting.
658
     */
11 efrain 659
    public function test_definition_mappings_only(): void {
1 efrain 660
        /** @var cache_config_testing $instance */
661
        $instance = cache_config_testing::instance(true);
662
        $instance->phpunit_add_definition('phpunit/mappingsonly', array(
663
            'mode' => cache_store::MODE_APPLICATION,
664
            'component' => 'phpunit',
665
            'area' => 'mappingsonly',
666
            'mappingsonly' => true
667
        ), false);
668
        $instance->phpunit_add_definition('phpunit/nonmappingsonly', array(
669
            'mode' => cache_store::MODE_APPLICATION,
670
            'component' => 'phpunit',
671
            'area' => 'nonmappingsonly',
672
            'mappingsonly' => false
673
        ), false);
674
 
675
        $cacheonly = cache::make('phpunit', 'mappingsonly');
676
        $this->assertInstanceOf(cache_application::class, $cacheonly);
677
        $this->assertEquals('cachestore_dummy', $cacheonly->phpunit_get_store_class());
678
 
679
        $expected = $this->get_expected_application_cache_store();
680
        $cachenon = cache::make('phpunit', 'nonmappingsonly');
681
        $this->assertInstanceOf(cache_application::class, $cachenon);
682
        $this->assertEquals($expected, $cachenon->phpunit_get_store_class());
683
    }
684
 
685
    /**
686
     * Test a very basic definition.
687
     */
11 efrain 688
    public function test_definition(): void {
1 efrain 689
        $instance = cache_config_testing::instance();
690
        $instance->phpunit_add_definition('phpunit/test', array(
691
            'mode' => cache_store::MODE_APPLICATION,
692
            'component' => 'phpunit',
693
            'area' => 'test',
694
        ));
695
        $cache = cache::make('phpunit', 'test');
696
 
697
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
698
        $this->assertEquals('test data 1', $cache->get('testkey1'));
699
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
700
        $this->assertEquals('test data 2', $cache->get('testkey2'));
701
    }
702
 
703
    /**
704
     * Test a definition using the simple keys.
705
     */
11 efrain 706
    public function test_definition_simplekeys(): void {
1 efrain 707
        $instance = cache_config_testing::instance();
708
        $instance->phpunit_add_definition('phpunit/simplekeytest', array(
709
            'mode' => cache_store::MODE_APPLICATION,
710
            'component' => 'phpunit',
711
            'area' => 'simplekeytest',
712
            'simplekeys' => true
713
        ));
714
        $cache = cache::make('phpunit', 'simplekeytest');
715
 
716
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
717
        $this->assertEquals('test data 1', $cache->get('testkey1'));
718
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
719
        $this->assertEquals('test data 2', $cache->get('testkey2'));
720
 
721
        $cache->purge();
722
 
723
        $this->assertTrue($cache->set('1', 'test data 1'));
724
        $this->assertEquals('test data 1', $cache->get('1'));
725
        $this->assertTrue($cache->set('2', 'test data 2'));
726
        $this->assertEquals('test data 2', $cache->get('2'));
727
    }
728
 
729
    /**
730
     * Test a negative TTL on an application cache.
731
     */
11 efrain 732
    public function test_application_ttl_negative(): void {
1 efrain 733
        $instance = cache_config_testing::instance(true);
734
        $instance->phpunit_add_definition('phpunit/ttltest', array(
735
            'mode' => cache_store::MODE_APPLICATION,
736
            'component' => 'phpunit',
737
            'area' => 'ttltest',
738
            'ttl' => -86400 // Set to a day in the past to be extra sure.
739
        ));
740
        $cache = cache::make('phpunit', 'ttltest');
741
        $this->assertInstanceOf(cache_application::class, $cache);
742
 
743
        // Purge it to be sure.
744
        $this->assertTrue($cache->purge());
745
        // It won't be there yet.
746
        $this->assertFalse($cache->has('Test'));
747
        // Set it now.
748
        $this->assertTrue($cache->set('Test', 'Test'));
749
        // Check its not there.
750
        $this->assertFalse($cache->has('Test'));
751
        // Double check by trying to get it.
752
        $this->assertFalse($cache->get('Test'));
753
 
754
        // Test with multiple keys.
755
        $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C')));
756
        $result = $cache->get_many(array('a', 'b', 'c'));
757
        $this->assertIsArray($result);
758
        $this->assertCount(3, $result);
759
        $this->assertArrayHasKey('a', $result);
760
        $this->assertArrayHasKey('b', $result);
761
        $this->assertArrayHasKey('c', $result);
762
        $this->assertFalse($result['a']);
763
        $this->assertFalse($result['b']);
764
        $this->assertFalse($result['c']);
765
 
766
        // Test with multiple keys including missing ones.
767
        $result = $cache->get_many(array('a', 'c', 'e'));
768
        $this->assertIsArray($result);
769
        $this->assertCount(3, $result);
770
        $this->assertArrayHasKey('a', $result);
771
        $this->assertArrayHasKey('c', $result);
772
        $this->assertArrayHasKey('e', $result);
773
        $this->assertFalse($result['a']);
774
        $this->assertFalse($result['c']);
775
        $this->assertFalse($result['e']);
776
    }
777
 
778
    /**
779
     * Test a positive TTL on an application cache.
780
     */
11 efrain 781
    public function test_application_ttl_positive(): void {
1 efrain 782
        $instance = cache_config_testing::instance(true);
783
        $instance->phpunit_add_definition('phpunit/ttltest', array(
784
            'mode' => cache_store::MODE_APPLICATION,
785
            'component' => 'phpunit',
786
            'area' => 'ttltest',
787
            'ttl' => 86400 // Set to a day in the future to be extra sure.
788
        ));
789
        $cache = cache::make('phpunit', 'ttltest');
790
        $this->assertInstanceOf(cache_application::class, $cache);
791
 
792
        // Purge it to be sure.
793
        $this->assertTrue($cache->purge());
794
        // It won't be there yet.
795
        $this->assertFalse($cache->has('Test'));
796
        // Set it now.
797
        $this->assertTrue($cache->set('Test', 'Test'));
798
        // Check its there.
799
        $this->assertTrue($cache->has('Test'));
800
        // Double check by trying to get it.
801
        $this->assertEquals('Test', $cache->get('Test'));
802
 
803
        // Test with multiple keys.
804
        $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C')));
805
        $result = $cache->get_many(array('a', 'b', 'c'));
806
        $this->assertIsArray($result);
807
        $this->assertCount(3, $result);
808
        $this->assertArrayHasKey('a', $result);
809
        $this->assertArrayHasKey('b', $result);
810
        $this->assertArrayHasKey('c', $result);
811
        $this->assertEquals('A', $result['a']);
812
        $this->assertEquals('B', $result['b']);
813
        $this->assertEquals('C', $result['c']);
814
 
815
        // Test with multiple keys including missing ones.
816
        $result = $cache->get_many(array('a', 'c', 'e'));
817
        $this->assertIsArray($result);
818
        $this->assertCount(3, $result);
819
        $this->assertArrayHasKey('a', $result);
820
        $this->assertArrayHasKey('c', $result);
821
        $this->assertArrayHasKey('e', $result);
822
        $this->assertEquals('A', $result['a']);
823
        $this->assertEquals('C', $result['c']);
824
        $this->assertEquals(false, $result['e']);
825
    }
826
 
827
    /**
828
     * Test a negative TTL on an session cache.
829
     */
11 efrain 830
    public function test_session_ttl_positive(): void {
1 efrain 831
        $instance = cache_config_testing::instance(true);
832
        $instance->phpunit_add_definition('phpunit/ttltest', array(
833
            'mode' => cache_store::MODE_SESSION,
834
            'component' => 'phpunit',
835
            'area' => 'ttltest',
836
            'ttl' => 86400 // Set to a day in the future to be extra sure.
837
        ));
838
        $cache = cache::make('phpunit', 'ttltest');
839
        $this->assertInstanceOf(cache_session::class, $cache);
840
 
841
        // Purge it to be sure.
842
        $this->assertTrue($cache->purge());
843
        // It won't be there yet.
844
        $this->assertFalse($cache->has('Test'));
845
        // Set it now.
846
        $this->assertTrue($cache->set('Test', 'Test'));
847
        // Check its there.
848
        $this->assertTrue($cache->has('Test'));
849
        // Double check by trying to get it.
850
        $this->assertEquals('Test', $cache->get('Test'));
851
 
852
        // Test with multiple keys.
853
        $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C')));
854
        $result = $cache->get_many(array('a', 'b', 'c'));
855
        $this->assertIsArray($result);
856
        $this->assertCount(3, $result);
857
        $this->assertArrayHasKey('a', $result);
858
        $this->assertArrayHasKey('b', $result);
859
        $this->assertArrayHasKey('c', $result);
860
        $this->assertEquals('A', $result['a']);
861
        $this->assertEquals('B', $result['b']);
862
        $this->assertEquals('C', $result['c']);
863
 
864
        // Test with multiple keys including missing ones.
865
        $result = $cache->get_many(array('a', 'c', 'e'));
866
        $this->assertIsArray($result);
867
        $this->assertCount(3, $result);
868
        $this->assertArrayHasKey('a', $result);
869
        $this->assertArrayHasKey('c', $result);
870
        $this->assertArrayHasKey('e', $result);
871
        $this->assertEquals('A', $result['a']);
872
        $this->assertEquals('C', $result['c']);
873
        $this->assertEquals(false, $result['e']);
874
    }
875
 
876
    /**
877
     * Tests manual locking operations on an application cache
878
     */
11 efrain 879
    public function test_application_manual_locking(): void {
1 efrain 880
        $instance = cache_config_testing::instance();
881
        $instance->phpunit_add_definition('phpunit/lockingtest', array(
882
            'mode' => cache_store::MODE_APPLICATION,
883
            'component' => 'phpunit',
884
            'area' => 'lockingtest'
885
        ));
886
        // Configure the lock timeout so the test doesn't take too long to run.
887
        $instance->phpunit_edit_store_config('default_application', ['lockwait' => 2]);
888
        $cache1 = cache::make('phpunit', 'lockingtest');
889
        $cache2 = clone($cache1);
890
 
891
        $this->assertTrue($cache1->set('testkey', 'test data'));
892
        $this->assertTrue($cache2->set('testkey', 'test data'));
893
 
894
        $cache1->acquire_lock('testkey');
895
        try {
896
            $cache2->acquire_lock('testkey');
897
            $this->fail();
898
        } catch (\moodle_exception $e) {
899
            // Check the right exception message, and debug info mentions the store type.
900
            $this->assertMatchesRegularExpression('~Unable to acquire a lock.*cachestore_file.*~',
901
                    $e->getMessage());
902
        }
903
 
904
        $this->assertTrue($cache1->check_lock_state('testkey'));
905
        $this->assertFalse($cache2->check_lock_state('testkey'));
906
 
907
        $this->assertTrue($cache1->release_lock('testkey'));
908
        $this->assertFalse($cache2->release_lock('testkey'));
909
 
910
        $this->assertTrue($cache1->set('testkey', 'test data'));
911
        $this->assertTrue($cache2->set('testkey', 'test data'));
912
    }
913
 
914
    /**
915
     * Tests application cache event invalidation
916
     */
11 efrain 917
    public function test_application_event_invalidation(): void {
1 efrain 918
        $instance = cache_config_testing::instance();
919
        $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
920
            'mode' => cache_store::MODE_APPLICATION,
921
            'component' => 'phpunit',
922
            'area' => 'eventinvalidationtest',
923
            'invalidationevents' => array(
924
                'crazyevent'
925
            )
926
        ));
927
        $cache = cache::make('phpunit', 'eventinvalidationtest');
928
 
929
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
930
        $this->assertEquals('test data 1', $cache->get('testkey1'));
931
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
932
        $this->assertEquals('test data 2', $cache->get('testkey2'));
933
 
934
        // Test invalidating a single entry.
935
        cache_helper::invalidate_by_event('crazyevent', array('testkey1'));
936
 
937
        $this->assertFalse($cache->get('testkey1'));
938
        $this->assertEquals('test data 2', $cache->get('testkey2'));
939
 
940
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
941
 
942
        // Test invalidating both entries.
943
        cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2'));
944
 
945
        $this->assertFalse($cache->get('testkey1'));
946
        $this->assertFalse($cache->get('testkey2'));
947
    }
948
 
949
    /**
950
     * Tests session cache event invalidation
951
     */
11 efrain 952
    public function test_session_event_invalidation(): void {
1 efrain 953
        $instance = cache_config_testing::instance();
954
        $instance->phpunit_add_definition('phpunit/test_session_event_invalidation', array(
955
            'mode' => cache_store::MODE_SESSION,
956
            'component' => 'phpunit',
957
            'area' => 'test_session_event_invalidation',
958
            'invalidationevents' => array(
959
                'crazyevent'
960
            )
961
        ));
962
        $cache = cache::make('phpunit', 'test_session_event_invalidation');
963
        $this->assertInstanceOf(cache_session::class, $cache);
964
 
965
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
966
        $this->assertEquals('test data 1', $cache->get('testkey1'));
967
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
968
        $this->assertEquals('test data 2', $cache->get('testkey2'));
969
 
970
        // Test invalidating a single entry.
971
        cache_helper::invalidate_by_event('crazyevent', array('testkey1'));
972
 
973
        $this->assertFalse($cache->get('testkey1'));
974
        $this->assertEquals('test data 2', $cache->get('testkey2'));
975
 
976
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
977
 
978
        // Test invalidating both entries.
979
        cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2'));
980
 
981
        $this->assertFalse($cache->get('testkey1'));
982
        $this->assertFalse($cache->get('testkey2'));
983
    }
984
 
985
    /**
986
     * Tests application cache definition invalidation
987
     */
11 efrain 988
    public function test_application_definition_invalidation(): void {
1 efrain 989
        $instance = cache_config_testing::instance();
990
        $instance->phpunit_add_definition('phpunit/definitioninvalidation', array(
991
            'mode' => cache_store::MODE_APPLICATION,
992
            'component' => 'phpunit',
993
            'area' => 'definitioninvalidation'
994
        ));
995
        $cache = cache::make('phpunit', 'definitioninvalidation');
996
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
997
        $this->assertEquals('test data 1', $cache->get('testkey1'));
998
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
999
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1000
 
1001
        cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), 'testkey1');
1002
 
1003
        $this->assertFalse($cache->get('testkey1'));
1004
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1005
 
1006
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1007
 
1008
        cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1'));
1009
 
1010
        $this->assertFalse($cache->get('testkey1'));
1011
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1012
 
1013
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1014
 
1015
        cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1', 'testkey2'));
1016
 
1017
        $this->assertFalse($cache->get('testkey1'));
1018
        $this->assertFalse($cache->get('testkey2'));
1019
    }
1020
 
1021
    /**
1022
     * Tests session cache definition invalidation
1023
     */
11 efrain 1024
    public function test_session_definition_invalidation(): void {
1 efrain 1025
        $instance = cache_config_testing::instance();
1026
        $instance->phpunit_add_definition('phpunit/test_session_definition_invalidation', array(
1027
            'mode' => cache_store::MODE_SESSION,
1028
            'component' => 'phpunit',
1029
            'area' => 'test_session_definition_invalidation'
1030
        ));
1031
        $cache = cache::make('phpunit', 'test_session_definition_invalidation');
1032
        $this->assertInstanceOf(cache_session::class, $cache);
1033
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1034
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1035
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
1036
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1037
 
1038
        cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 'testkey1');
1039
 
1040
        $this->assertFalse($cache->get('testkey1'));
1041
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1042
 
1043
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1044
 
1045
        cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(),
1046
                array('testkey1'));
1047
 
1048
        $this->assertFalse($cache->get('testkey1'));
1049
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1050
 
1051
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1052
 
1053
        cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(),
1054
                array('testkey1', 'testkey2'));
1055
 
1056
        $this->assertFalse($cache->get('testkey1'));
1057
        $this->assertFalse($cache->get('testkey2'));
1058
    }
1059
 
1060
    /**
1061
     * Tests application cache event invalidation over a distributed setup.
1062
     */
11 efrain 1063
    public function test_distributed_application_event_invalidation(): void {
1 efrain 1064
        global $CFG;
1065
        // This is going to be an intense wee test.
1066
        // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a
1067
        // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally
1068
        // check that it is not picked up.
1069
        $instance = cache_config_testing::instance();
1070
        $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
1071
            'mode' => cache_store::MODE_APPLICATION,
1072
            'component' => 'phpunit',
1073
            'area' => 'eventinvalidationtest',
1074
            'simplekeys' => true,
1075
            'simpledata' => true,
1076
            'invalidationevents' => array(
1077
                'crazyevent'
1078
            )
1079
        ));
1080
        $cache = cache::make('phpunit', 'eventinvalidationtest');
1081
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1082
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1083
 
1084
        cache_helper::invalidate_by_event('crazyevent', array('testkey1'));
1085
 
1086
        $this->assertFalse($cache->get('testkey1'));
1087
 
1088
        // OK data added, data invalidated, and invalidation time has been set.
1089
        // Now we need to manually add back the data and adjust the invalidation time.
1090
        $hash = md5(cache_store::MODE_APPLICATION.'/phpunit/eventinvalidationtest/'.$CFG->wwwroot.'phpunit');
1091
        $timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-$hash.cache";
1092
        // Make sure the file is correct.
1093
        $this->assertTrue(file_exists($timefile));
1094
        $timecont = serialize(cache::now(true) - 60); // Back 60sec in the past to force it to re-invalidate.
1095
        make_writable_directory(dirname($timefile));
1096
        file_put_contents($timefile, $timecont);
1097
        $this->assertTrue(file_exists($timefile));
1098
 
1099
        $datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-$hash.cache";
1100
        $datacont = serialize("test data 1");
1101
        make_writable_directory(dirname($datafile));
1102
        file_put_contents($datafile, $datacont);
1103
        $this->assertTrue(file_exists($datafile));
1104
 
1105
        // Test 1: Rebuild without the event and test its there.
1106
        cache_factory::reset();
1107
        $instance = cache_config_testing::instance();
1108
        $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
1109
            'mode' => cache_store::MODE_APPLICATION,
1110
            'component' => 'phpunit',
1111
            'area' => 'eventinvalidationtest',
1112
            'simplekeys' => true,
1113
            'simpledata' => true,
1114
        ));
1115
        $cache = cache::make('phpunit', 'eventinvalidationtest');
1116
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1117
 
1118
        // Test 2: Rebuild and test the invalidation of the event via the invalidation cache.
1119
        cache_factory::reset();
1120
 
1121
        $instance = cache_config_testing::instance();
1122
        $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
1123
            'mode' => cache_store::MODE_APPLICATION,
1124
            'component' => 'phpunit',
1125
            'area' => 'eventinvalidationtest',
1126
            'simplekeys' => true,
1127
            'simpledata' => true,
1128
            'invalidationevents' => array(
1129
                'crazyevent'
1130
            )
1131
        ));
1132
 
1133
        $cache = cache::make('phpunit', 'eventinvalidationtest');
1134
        $this->assertFalse($cache->get('testkey1'));
1135
 
1136
        // Test 3: Verify that an existing lastinvalidation cache file is updated when needed.
1137
 
1138
        // Make a new cache class.  This should should invalidate testkey2.
1139
        $cache = cache::make('phpunit', 'eventinvalidationtest');
1140
 
1141
        // Invalidation token should have been reset.
1142
        $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
1143
 
1144
        // Set testkey2 data.
1145
        $cache->set('testkey2', 'test data 2');
1146
 
1147
        // Backdate the event invalidation time by 30 seconds.
1148
        $invalidationcache = cache::make('core', 'eventinvalidation');
1149
        $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 30));
1150
 
1151
        // Lastinvalidation should already be cache::now().
1152
        $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
1153
 
1154
        // Set it to 15 seconds ago so that we know if it changes.
1155
        $pasttime = cache::now(true) - 15;
1156
        $cache->set('lastinvalidation', $pasttime);
1157
 
1158
        // Make a new cache class.  This should not invalidate anything.
1159
        cache_factory::instance()->reset_cache_instances();
1160
        $cache = cache::make('phpunit', 'eventinvalidationtest');
1161
 
1162
        // Lastinvalidation shouldn't change since it was already newer than invalidation event.
1163
        $this->assertEquals($pasttime, $cache->get('lastinvalidation'));
1164
 
1165
        // Now set the event invalidation to newer than the lastinvalidation time.
1166
        $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 5));
1167
        // Make a new cache class.  This should should invalidate testkey2.
1168
        cache_factory::instance()->reset_cache_instances();
1169
        $cache = cache::make('phpunit', 'eventinvalidationtest');
1170
        // Lastinvalidation timestamp should have updated to cache::now().
1171
        $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
1172
 
1173
        // Now simulate a purge_by_event 5 seconds ago.
1174
        $invalidationcache = cache::make('core', 'eventinvalidation');
1175
        $invalidationcache->set('crazyevent', array('purged' => cache::now(true) - 5));
1176
        // Set our lastinvalidation timestamp to 15 seconds ago.
1177
        $cache->set('lastinvalidation', cache::now(true) - 15);
1178
        // Make a new cache class.  This should invalidate the cache.
1179
        cache_factory::instance()->reset_cache_instances();
1180
        $cache = cache::make('phpunit', 'eventinvalidationtest');
1181
        // Lastinvalidation timestamp should have updated to cache::now().
1182
        $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation'));
1183
 
1184
    }
1185
 
1186
    /**
1187
     * Tests application cache event purge
1188
     */
11 efrain 1189
    public function test_application_event_purge(): void {
1 efrain 1190
        $instance = cache_config_testing::instance();
1191
        $instance->phpunit_add_definition('phpunit/eventpurgetest', array(
1192
            'mode' => cache_store::MODE_APPLICATION,
1193
            'component' => 'phpunit',
1194
            'area' => 'eventpurgetest',
1195
            'invalidationevents' => array(
1196
                'crazyevent'
1197
            )
1198
        ));
1199
        $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array(
1200
            'mode' => cache_store::MODE_APPLICATION,
1201
            'component' => 'phpunit',
1202
            'area' => 'eventpurgetestaccelerated',
1203
            'staticacceleration' => true,
1204
            'invalidationevents' => array(
1205
                'crazyevent'
1206
            )
1207
        ));
1208
        $cache = cache::make('phpunit', 'eventpurgetest');
1209
 
1210
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1211
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1212
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
1213
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1214
 
1215
        // Purge the event.
1216
        cache_helper::purge_by_event('crazyevent');
1217
 
1218
        // Check things have been removed.
1219
        $this->assertFalse($cache->get('testkey1'));
1220
        $this->assertFalse($cache->get('testkey2'));
1221
 
1222
        // Now test the static acceleration array.
1223
        $cache = cache::make('phpunit', 'eventpurgetestaccelerated');
1224
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1225
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1226
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
1227
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1228
 
1229
        // Purge the event.
1230
        cache_helper::purge_by_event('crazyevent');
1231
 
1232
        // Check things have been removed.
1233
        $this->assertFalse($cache->get('testkey1'));
1234
        $this->assertFalse($cache->get('testkey2'));
1235
    }
1236
 
1237
    /**
1238
     * Tests session cache event purge
1239
     */
11 efrain 1240
    public function test_session_event_purge(): void {
1 efrain 1241
        $instance = cache_config_testing::instance();
1242
        $instance->phpunit_add_definition('phpunit/eventpurgetest', array(
1243
            'mode' => cache_store::MODE_SESSION,
1244
            'component' => 'phpunit',
1245
            'area' => 'eventpurgetest',
1246
            'invalidationevents' => array(
1247
                'crazyevent'
1248
            )
1249
        ));
1250
        $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array(
1251
            'mode' => cache_store::MODE_SESSION,
1252
            'component' => 'phpunit',
1253
            'area' => 'eventpurgetestaccelerated',
1254
            'staticacceleration' => true,
1255
            'invalidationevents' => array(
1256
                'crazyevent'
1257
            )
1258
        ));
1259
        $cache = cache::make('phpunit', 'eventpurgetest');
1260
 
1261
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1262
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1263
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
1264
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1265
 
1266
        // Purge the event.
1267
        cache_helper::purge_by_event('crazyevent');
1268
 
1269
        // Check things have been removed.
1270
        $this->assertFalse($cache->get('testkey1'));
1271
        $this->assertFalse($cache->get('testkey2'));
1272
 
1273
        // Now test the static acceleration array.
1274
        $cache = cache::make('phpunit', 'eventpurgetestaccelerated');
1275
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1276
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1277
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
1278
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1279
 
1280
        // Purge the event.
1281
        cache_helper::purge_by_event('crazyevent');
1282
 
1283
        // Check things have been removed.
1284
        $this->assertFalse($cache->get('testkey1'));
1285
        $this->assertFalse($cache->get('testkey2'));
1286
    }
1287
 
1288
    /**
1289
     * Tests application cache definition purge
1290
     */
11 efrain 1291
    public function test_application_definition_purge(): void {
1 efrain 1292
        $instance = cache_config_testing::instance();
1293
        $instance->phpunit_add_definition('phpunit/definitionpurgetest', array(
1294
            'mode' => cache_store::MODE_APPLICATION,
1295
            'component' => 'phpunit',
1296
            'area' => 'definitionpurgetest',
1297
            'invalidationevents' => array(
1298
                'crazyevent'
1299
            )
1300
        ));
1301
        $cache = cache::make('phpunit', 'definitionpurgetest');
1302
 
1303
        $this->assertTrue($cache->set('testkey1', 'test data 1'));
1304
        $this->assertEquals('test data 1', $cache->get('testkey1'));
1305
        $this->assertTrue($cache->set('testkey2', 'test data 2'));
1306
        $this->assertEquals('test data 2', $cache->get('testkey2'));
1307
 
1308
        // Purge the event.
1309
        cache_helper::purge_by_definition('phpunit', 'definitionpurgetest');
1310
 
1311
        // Check things have been removed.
1312
        $this->assertFalse($cache->get('testkey1'));
1313
        $this->assertFalse($cache->get('testkey2'));
1314
    }
1315
 
1316
    /**
1317
     * Test the use of an alt path.
1318
     * If we can generate a config instance we are done :)
1319
     */
11 efrain 1320
    public function test_alt_cache_path(): void {
1 efrain 1321
        global $CFG;
1322
        if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) {
1323
            $this->markTestSkipped('Skipped testing alt cache path as it is already being used.');
1324
        }
1325
        $this->resetAfterTest();
1326
        $CFG->altcacheconfigpath = $CFG->dataroot.'/cache/altcacheconfigpath';
1327
        $instance = cache_config_testing::instance();
1328
        $this->assertInstanceOf(cache_config::class, $instance);
1329
    }
1330
 
1331
    /**
1332
     * Test disabling the cache stores.
1333
     */
11 efrain 1334
    public function test_disable_stores(): void {
1 efrain 1335
        $instance = cache_config_testing::instance();
1336
        $instance->phpunit_add_definition('phpunit/disabletest1', array(
1337
            'mode' => cache_store::MODE_APPLICATION,
1338
            'component' => 'phpunit',
1339
            'area' => 'disabletest1'
1340
        ));
1341
        $instance->phpunit_add_definition('phpunit/disabletest2', array(
1342
            'mode' => cache_store::MODE_SESSION,
1343
            'component' => 'phpunit',
1344
            'area' => 'disabletest2'
1345
        ));
1346
        $instance->phpunit_add_definition('phpunit/disabletest3', array(
1347
            'mode' => cache_store::MODE_REQUEST,
1348
            'component' => 'phpunit',
1349
            'area' => 'disabletest3'
1350
        ));
1351
 
1352
        $caches = array(
1353
            'disabletest1' => cache::make('phpunit', 'disabletest1'),
1354
            'disabletest2' => cache::make('phpunit', 'disabletest2'),
1355
            'disabletest3' => cache::make('phpunit', 'disabletest3')
1356
        );
1357
 
1358
        $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']);
1359
        $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']);
1360
        $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']);
1361
 
1362
        $this->assertEquals('cachestore_file', $caches['disabletest1']->phpunit_get_store_class());
1363
        $this->assertEquals('cachestore_session', $caches['disabletest2']->phpunit_get_store_class());
1364
        $this->assertEquals('cachestore_static', $caches['disabletest3']->phpunit_get_store_class());
1365
 
1366
        foreach ($caches as $cache) {
1367
            $this->assertFalse($cache->get('test'));
1368
            $this->assertTrue($cache->set('test', 'test'));
1369
            $this->assertEquals('test', $cache->get('test'));
1370
        }
1371
 
1372
        cache_factory::disable_stores();
1373
 
1374
        $caches = array(
1375
            'disabletest1' => cache::make('phpunit', 'disabletest1'),
1376
            'disabletest2' => cache::make('phpunit', 'disabletest2'),
1377
            'disabletest3' => cache::make('phpunit', 'disabletest3')
1378
        );
1379
 
1380
        $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']);
1381
        $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']);
1382
        $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']);
1383
 
1384
        $this->assertEquals('cachestore_dummy', $caches['disabletest1']->phpunit_get_store_class());
1385
        $this->assertEquals('cachestore_dummy', $caches['disabletest2']->phpunit_get_store_class());
1386
        $this->assertEquals('cachestore_dummy', $caches['disabletest3']->phpunit_get_store_class());
1387
 
1388
        foreach ($caches as $cache) {
1389
            $this->assertFalse($cache->get('test'));
1390
            $this->assertTrue($cache->set('test', 'test'));
1391
            $this->assertEquals('test', $cache->get('test'));
1392
        }
1393
    }
1394
 
1395
    /**
1396
     * Test disabling the cache.
1397
     */
11 efrain 1398
    public function test_disable(): void {
1 efrain 1399
        global $CFG;
1400
 
1401
        if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) {
1402
            // We can't run this test as it requires us to delete the cache configuration script which we just
1403
            // cant do with a custom path in play.
1404
            $this->markTestSkipped('Skipped testing cache disable functionality as alt cache path is being used.');
1405
        }
1406
 
1407
        $configfile = $CFG->dataroot.'/muc/config.php';
1408
 
1409
        // The config file will not exist yet as we've not done anything with the cache.
1410
        // reset_all_data removes the file and without a call to create a configuration it doesn't exist
1411
        // as yet.
1412
        $this->assertFileDoesNotExist($configfile);
1413
 
1414
        // Disable the cache
1415
        cache_phpunit_factory::phpunit_disable();
1416
 
1417
        // Check we get the expected disabled factory.
1418
        $factory = cache_factory::instance();
1419
        $this->assertInstanceOf(cache_factory_disabled::class, $factory);
1420
 
1421
        // Check we get the expected disabled config.
1422
        $config = $factory->create_config_instance();
1423
        $this->assertInstanceOf(cache_config_disabled::class, $config);
1424
 
1425
        // Check we get the expected disabled caches.
1426
        $cache = cache::make('core', 'string');
1427
        $this->assertInstanceOf(cache_disabled::class, $cache);
1428
 
1429
        // Test an application cache.
1430
        $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'disable');
1431
        $this->assertInstanceOf(cache_disabled::class, $cache);
1432
 
1433
        $this->assertFalse($cache->get('test'));
1434
        $this->assertFalse($cache->get_versioned('v', 1));
1435
        $this->assertFalse($cache->set('test', 'test'));
1436
        $this->assertFalse($cache->set_versioned('v', 1, 'data'));
1437
        $this->assertFalse($cache->delete('test'));
1438
        $this->assertTrue($cache->purge());
1439
        // Checking a lock should always report that we have one.
1440
        // Acquiring or releasing a lock should always report success.
1441
        $this->assertTrue($cache->check_lock_state('test'));
1442
        $this->assertTrue($cache->acquire_lock('test'));
1443
        $this->assertTrue($cache->acquire_lock('test'));
1444
        $this->assertTrue($cache->check_lock_state('test'));
1445
        $this->assertTrue($cache->release_lock('test'));
1446
        $this->assertTrue($cache->release_lock('test'));
1447
        $this->assertTrue($cache->check_lock_state('test'));
1448
 
1449
        // Test a session cache.
1450
        $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'disable');
1451
        $this->assertInstanceOf(cache_disabled::class, $cache);
1452
 
1453
        $this->assertFalse($cache->get('test'));
1454
        $this->assertFalse($cache->get_versioned('v', 1));
1455
        $this->assertFalse($cache->set('test', 'test'));
1456
        $this->assertFalse($cache->set_versioned('v', 1, 'data'));
1457
        $this->assertFalse($cache->delete('test'));
1458
        $this->assertTrue($cache->purge());
1459
 
1460
        // Finally test a request cache.
1461
        $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'disable');
1462
        $this->assertInstanceOf(cache_disabled::class, $cache);
1463
 
1464
        $this->assertFalse($cache->get('test'));
1465
        $this->assertFalse($cache->get_versioned('v', 1));
1466
        $this->assertFalse($cache->set('test', 'test'));
1467
        $this->assertFalse($cache->set_versioned('v', 1, 'data'));
1468
        $this->assertFalse($cache->delete('test'));
1469
        $this->assertTrue($cache->purge());
1470
 
1471
        cache_factory::reset();
1472
 
1473
        $factory = cache_factory::instance(true);
1474
        $config = $factory->create_config_instance();
1475
        $this->assertEquals('cache_config_testing', get_class($config));
1476
    }
1477
 
1478
    /**
1479
     * Test that multiple application loaders work ok.
1480
     */
11 efrain 1481
    public function test_multiple_application_loaders(): void {
1 efrain 1482
        $instance = cache_config_testing::instance(true);
1483
        $instance->phpunit_add_file_store('phpunittest1');
1484
        $instance->phpunit_add_file_store('phpunittest2');
1485
        $instance->phpunit_add_definition('phpunit/multi_loader', array(
1486
            'mode' => cache_store::MODE_APPLICATION,
1487
            'component' => 'phpunit',
1488
            'area' => 'multi_loader'
1489
        ));
1490
        $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3);
1491
        $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2);
1492
 
1493
        $cache = cache::make('phpunit', 'multi_loader');
1494
        $this->assertInstanceOf(cache_application::class, $cache);
1495
        $this->assertFalse($cache->get('test'));
1496
        $this->assertTrue($cache->set('test', 'test'));
1497
        $this->assertEquals('test', $cache->get('test'));
1498
        $this->assertTrue($cache->delete('test'));
1499
        $this->assertFalse($cache->get('test'));
1500
        $this->assertTrue($cache->set('test', 'test'));
1501
        $this->assertTrue($cache->purge());
1502
        $this->assertFalse($cache->get('test'));
1503
 
1504
        // Test the many commands.
1505
        $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C')));
1506
        $result = $cache->get_many(array('a', 'b', 'c'));
1507
        $this->assertIsArray($result);
1508
        $this->assertCount(3, $result);
1509
        $this->assertArrayHasKey('a', $result);
1510
        $this->assertArrayHasKey('b', $result);
1511
        $this->assertArrayHasKey('c', $result);
1512
        $this->assertEquals('A', $result['a']);
1513
        $this->assertEquals('B', $result['b']);
1514
        $this->assertEquals('C', $result['c']);
1515
        $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c')));
1516
        $this->assertEquals(2, $cache->delete_many(array('a', 'c')));
1517
        $result = $cache->get_many(array('a', 'b', 'c'));
1518
        $this->assertIsArray($result);
1519
        $this->assertCount(3, $result);
1520
        $this->assertArrayHasKey('a', $result);
1521
        $this->assertArrayHasKey('b', $result);
1522
        $this->assertArrayHasKey('c', $result);
1523
        $this->assertFalse($result['a']);
1524
        $this->assertEquals('B', $result['b']);
1525
        $this->assertFalse($result['c']);
1526
 
1527
        // Test non-recursive deletes.
1528
        $this->assertTrue($cache->set('test', 'test'));
1529
        $this->assertSame('test', $cache->get('test'));
1530
        $this->assertTrue($cache->delete('test', false));
1531
        // We should still have it on a deeper loader.
1532
        $this->assertSame('test', $cache->get('test'));
1533
        // Test non-recusive with many functions.
1534
        $this->assertSame(3, $cache->set_many(array(
1535
            'one' => 'one',
1536
            'two' => 'two',
1537
            'three' => 'three'
1538
        )));
1539
        $this->assertSame('one', $cache->get('one'));
1540
        $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
1541
        $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false));
1542
        $this->assertSame('one', $cache->get('one'));
1543
        $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
1544
    }
1545
 
1546
    /**
1547
     * Data provider to try using a TTL or non-TTL cache.
1548
     *
1549
     * @return array
1550
     */
1551
    public function ttl_or_not(): array {
1552
        return [[false], [true]];
1553
    }
1554
 
1555
    /**
1556
     * Data provider to try using a TTL or non-TTL cache, and static acceleration or not.
1557
     *
1558
     * @return array
1559
     */
1560
    public function ttl_and_static_acceleration_or_not(): array {
1561
        return [[false, false], [false, true], [true, false], [true, true]];
1562
    }
1563
 
1564
    /**
1565
     * Data provider to try using a TTL or non-TTL cache, and simple data on or off.
1566
     *
1567
     * @return array
1568
     */
1569
    public function ttl_and_simple_data_or_not(): array {
1570
        // Same values as for ttl and static acceleration (two booleans).
1571
        return $this->ttl_and_static_acceleration_or_not();
1572
    }
1573
 
1574
    /**
1575
     * Shared code to set up a two or three-layer versioned cache for testing.
1576
     *
1577
     * @param bool $ttl If true, sets TTL in the definition
1578
     * @param bool $threelayer If true, uses a 3-layer instead of 2-layer cache
1579
     * @param bool $staticacceleration If true, enables static acceleration
1580
     * @param bool $simpledata If true, enables simple data
1581
     * @return \cache_application Cache
1582
     */
1583
    protected function create_versioned_cache(bool $ttl, bool $threelayer = false,
1584
            bool $staticacceleration = false, bool $simpledata = false): \cache_application {
1585
        $instance = cache_config_testing::instance(true);
1586
        $instance->phpunit_add_file_store('a', false);
1587
        $instance->phpunit_add_file_store('b', false);
1588
        if ($threelayer) {
1589
            $instance->phpunit_add_file_store('c', false);
1590
        }
1591
        $defarray = [
1592
            'mode' => cache_store::MODE_APPLICATION,
1593
            'component' => 'phpunit',
1594
            'area' => 'multi_loader'
1595
        ];
1596
        if ($ttl) {
1597
            $defarray['ttl'] = '600';
1598
        }
1599
        if ($staticacceleration) {
1600
            $defarray['staticacceleration'] = true;
1601
            $defarray['staticaccelerationsize'] = 10;
1602
        }
1603
        if ($simpledata) {
1604
            $defarray['simpledata'] = true;
1605
        }
1606
        $instance->phpunit_add_definition('phpunit/multi_loader', $defarray, false);
1607
        $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'a', 1);
1608
        $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'b', 2);
1609
        if ($threelayer) {
1610
            $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'c', 3);
1611
        }
1612
 
1613
        $multicache = cache::make('phpunit', 'multi_loader');
1614
        return $multicache;
1615
    }
1616
 
1617
    /**
1618
     * Tests basic use of versioned cache.
1619
     *
1620
     * @dataProvider ttl_and_simple_data_or_not
1621
     * @param bool $ttl If true, uses a TTL cache.
1622
     * @param bool $simpledata If true, turns on simple data flag
1623
     * @covers ::set_versioned
1624
     * @covers ::get_versioned
1625
     */
1626
    public function test_versioned_cache_basic(bool $ttl, bool $simpledata): void {
1627
        $multicache = $this->create_versioned_cache($ttl, false, false, $simpledata);
1628
 
1629
        $this->assertTrue($multicache->set_versioned('game', 1, 'Pooh-sticks'));
1630
 
1631
        $result = $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion);
1632
        $this->assertEquals('Pooh-sticks', $result);
1633
        $this->assertEquals(1, $actualversion);
1634
    }
1635
 
1636
    /**
1637
     * Tests versioned cache with objects.
1638
     *
1639
     * @dataProvider ttl_and_static_acceleration_or_not
1640
     * @param bool $ttl If true, uses a TTL cache.
1641
     * @param bool $staticacceleration If true, enables static acceleration
1642
     * @covers ::set_versioned
1643
     * @covers ::get_versioned
1644
     */
1645
    public function test_versioned_cache_objects(bool $ttl, bool $staticacceleration): void {
1646
        $multicache = $this->create_versioned_cache($ttl, false, $staticacceleration);
1647
 
1648
        // Set an object value.
1649
        $data = (object)['game' => 'Pooh-sticks'];
1650
        $this->assertTrue($multicache->set_versioned('game', 1, $data));
1651
 
1652
        // Get it.
1653
        $result = $multicache->get_versioned('game', 1);
1654
        $this->assertEquals('Pooh-sticks', $result->game);
1655
 
1656
        // Mess about with the value in the returned object.
1657
        $result->game = 'Tag';
1658
 
1659
        // Get it again and confirm the cached object has not been affected.
1660
        $result = $multicache->get_versioned('game', 1);
1661
        $this->assertEquals('Pooh-sticks', $result->game);
1662
    }
1663
 
1664
    /**
1665
     * Tests requesting a version that doesn't exist.
1666
     *
1667
     * @dataProvider ttl_or_not
1668
     * @param bool $ttl If true, uses a TTL cache.
1669
     * @covers ::set_versioned
1670
     * @covers ::get_versioned
1671
     */
1672
    public function test_versioned_cache_not_exist(bool $ttl): void {
1673
        $multicache = $this->create_versioned_cache($ttl);
1674
 
1675
        $multicache->set_versioned('game', 1, 'Pooh-sticks');
1676
 
1677
        // Exists but with wrong version.
1678
        $this->assertFalse($multicache->get_versioned('game', 2));
1679
 
1680
        // Doesn't exist at all.
1681
        $this->assertFalse($multicache->get_versioned('frog', 0));
1682
    }
1683
 
1684
    /**
1685
     * Tests attempts to use get after set_version or get_version after set.
1686
     *
1687
     * @dataProvider ttl_or_not
1688
     * @param bool $ttl If true, uses a TTL cache.
1689
     * @covers ::set_versioned
1690
     * @covers ::get_versioned
1691
     */
1692
    public function test_versioned_cache_incompatible_versioning(bool $ttl): void {
1693
        $multicache = $this->create_versioned_cache($ttl);
1694
 
1695
        // What if you use get on a get_version cache?
1696
        $multicache->set_versioned('game', 1, 'Pooh-sticks');
1697
        try {
1698
            $multicache->get('game');
1699
            $this->fail();
1700
        } catch (\coding_exception $e) {
1701
            $this->assertStringContainsString('Unexpectedly found versioned cache entry', $e->getMessage());
1702
        }
1703
 
1704
        // Or get_version on a get cache?
1705
        $multicache->set('toy', 'Train set');
1706
        try {
1707
            $multicache->get_versioned('toy', 1);
1708
            $this->fail();
1709
        } catch (\coding_exception $e) {
1710
            $this->assertStringContainsString('Unexpectedly found non-versioned cache entry', $e->getMessage());
1711
        }
1712
    }
1713
 
1714
    /**
1715
     * Versions are only stored once, so if you set a newer version you will always get it even
1716
     * if you ask for the lower version number.
1717
     *
1718
     * @dataProvider ttl_or_not
1719
     * @param bool $ttl If true, uses a TTL cache.
1720
     * @covers ::set_versioned
1721
     * @covers ::get_versioned
1722
     */
1723
    public function test_versioned_cache_single_copy(bool $ttl): void {
1724
        $multicache = $this->create_versioned_cache($ttl);
1725
 
1726
        $multicache->set_versioned('game', 1, 'Pooh-sticks');
1727
        $multicache->set_versioned('game', 2, 'Tag');
1728
        $this->assertEquals('Tag', $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion));
1729
 
1730
        // The reported version number matches the one returned, not requested.
1731
        $this->assertEquals(2, $actualversion);
1732
    }
1733
 
1734
    /**
1735
     * If the first (local) store has an outdated copy but the second (shared) store has a newer
1736
     * one, then it should automatically be retrieved.
1737
     *
1738
     * @dataProvider ttl_or_not
1739
     * @param bool $ttl If true, uses a TTL cache.
1740
     * @covers ::set_versioned
1741
     * @covers ::get_versioned
1742
     */
1743
    public function test_versioned_cache_outdated_local(bool $ttl): void {
1744
        $multicache = $this->create_versioned_cache($ttl);
1745
 
1746
        // Set initial value to version 2, 'Tag', in both stores.
1747
        $multicache->set_versioned('game', 2, 'Tag');
1748
 
1749
        // Get the two separate cache stores for the multi-level cache.
1750
        $factory = cache_factory::instance();
1751
        $definition = $factory->create_definition('phpunit', 'multi_loader');
1752
        [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition);
1753
 
1754
        // Simulate what happens if the shared cache is updated with a new version but the
1755
        // local one still has an old version.
1756
        $hashgame = cache_helper::hash_key('game', $definition);
1757
        $data = 'British Bulldog';
1758
        if ($ttl) {
1759
            $data = new \cache_ttl_wrapper($data, 600);
1760
        }
1761
        $storeb->set($hashgame, new \core_cache\version_wrapper($data, 3));
1762
 
1763
        // If we ask for the old one we'll get it straight off from local cache.
1764
        $this->assertEquals('Tag', $multicache->get_versioned('game', 2));
1765
 
1766
        // But if we ask for the new one it will still get it via the shared cache.
1767
        $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3));
1768
 
1769
        // Also, now it will have been updated in the local cache as well.
1770
        $localvalue = $storea->get($hashgame);
1771
        if ($ttl) {
1772
            // In case the time has changed slightly since the first set, we can't do an exact
1773
            // compare, so check it ignoring the time field.
1774
            $this->assertEquals(3, $localvalue->version);
1775
            $ttldata = $localvalue->data;
1776
            $this->assertInstanceOf('cache_ttl_wrapper', $ttldata);
1777
            $this->assertEquals('British Bulldog', $ttldata->data);
1778
        } else {
1779
            $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $localvalue);
1780
        }
1781
    }
1782
 
1783
    /**
1784
     * When we request a newer version, older ones are automatically deleted in every level of the
1785
     * cache (to save I/O if there are multiple requests, as if there is another request it will
1786
     * not have to retrieve the values to find out that they're old).
1787
     *
1788
     * @dataProvider ttl_or_not
1789
     * @param bool $ttl If true, uses a TTL cache.
1790
     * @covers ::set_versioned
1791
     * @covers ::get_versioned
1792
     */
1793
    public function test_versioned_cache_deleting_outdated(bool $ttl): void {
1794
        $multicache = $this->create_versioned_cache($ttl);
1795
 
1796
        // Set initial value to version 2, 'Tag', in both stores.
1797
        $multicache->set_versioned('game', 2, 'Tag');
1798
 
1799
        // Get the two separate cache stores for the multi-level cache.
1800
        $factory = cache_factory::instance();
1801
        $definition = $factory->create_definition('phpunit', 'multi_loader');
1802
        [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition);
1803
 
1804
        // If we request a newer version, then any older version should be deleted in each
1805
        // cache level.
1806
        $this->assertFalse($multicache->get_versioned('game', 4));
1807
        $hashgame = cache_helper::hash_key('game', $definition);
1808
        $this->assertFalse($storea->get($hashgame));
1809
        $this->assertFalse($storeb->get($hashgame));
1810
    }
1811
 
1812
    /**
1813
     * Tests a versioned cache when using static cache.
1814
     *
1815
     * @covers ::set_versioned
1816
     * @covers ::get_versioned
1817
     */
1818
    public function test_versioned_cache_static(): void {
1819
        $staticcache = $this->create_versioned_cache(false, false, true);
1820
 
1821
        // Set a value in the cache, version 1. This will store it in static acceleration.
1822
        $staticcache->set_versioned('game', 1, 'Pooh-sticks');
1823
 
1824
        // Get the first cache store (we don't need the second one for this test).
1825
        $factory = cache_factory::instance();
1826
        $definition = $factory->create_definition('phpunit', 'multi_loader');
1827
        [0 => $storea] = $factory->get_store_instances_in_use($definition);
1828
 
1829
        // Hack a newer version into cache store without directly calling set (now the static
1830
        // has v1, store has v2). This simulates another client updating the cache.
1831
        $hashgame = cache_helper::hash_key('game', $definition);
1832
        $storea->set($hashgame, new \core_cache\version_wrapper('Tag', 2));
1833
 
1834
        // Get the key from the cache, v1. This will use static acceleration.
1835
        $this->assertEquals('Pooh-sticks', $staticcache->get_versioned('game', 1));
1836
 
1837
        // Now if we ask for a newer version, it should not use the static cached one.
1838
        $this->assertEquals('Tag', $staticcache->get_versioned('game', 2));
1839
 
1840
        // This get should have updated static acceleration, so it will be used next time without
1841
        // a store request.
1842
        $storea->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3));
1843
        $this->assertEquals('Tag', $staticcache->get_versioned('game', 2));
1844
 
1845
        // Requesting the higher version will get rid of static acceleration again.
1846
        $this->assertEquals('British Bulldog', $staticcache->get_versioned('game', 3));
1847
 
1848
        // Finally ask for a version that doesn't exist anywhere, just to confirm it returns null.
1849
        $this->assertFalse($staticcache->get_versioned('game', 4));
1850
    }
1851
 
1852
    /**
1853
     * Tests basic use of 3-layer versioned caches.
1854
     *
1855
     * @covers ::set_versioned
1856
     * @covers ::get_versioned
1857
     */
1858
    public function test_versioned_cache_3_layers_basic(): void {
1859
        $multicache = $this->create_versioned_cache(false, true);
1860
 
1861
        // Basic use of set_versioned and get_versioned.
1862
        $multicache->set_versioned('game', 1, 'Pooh-sticks');
1863
        $this->assertEquals('Pooh-sticks', $multicache->get_versioned('game', 1));
1864
 
1865
        // What if you ask for a version that doesn't exist?
1866
        $this->assertFalse($multicache->get_versioned('game', 2));
1867
 
1868
        // Setting a new version wipes out the old version; if you request it, you get the new one.
1869
        $multicache->set_versioned('game', 2, 'Tag');
1870
        $this->assertEquals('Tag', $multicache->get_versioned('game', 1));
1871
    }
1872
 
1873
    /**
1874
     * Tests use of 3-layer versioned caches where the 3 layers currently have different versions.
1875
     *
1876
     * @covers ::set_versioned
1877
     * @covers ::get_versioned
1878
     */
1879
    public function test_versioned_cache_3_layers_different_data(): void {
1880
        // Set version 2 using normal method.
1881
        $multicache = $this->create_versioned_cache(false, true);
1882
        $multicache->set_versioned('game', 2, 'Tag');
1883
 
1884
        // Get the three separate cache stores for the multi-level cache.
1885
        $factory = cache_factory::instance();
1886
        $definition = $factory->create_definition('phpunit', 'multi_loader');
1887
        [0 => $storea, 1 => $storeb, 2 => $storec] = $factory->get_store_instances_in_use($definition);
1888
 
1889
        // Set up two other versions so every level has a different version.
1890
        $hashgame = cache_helper::hash_key('game', $definition);
1891
        $storeb->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3));
1892
        $storec->set($hashgame, new \core_cache\version_wrapper('Hopscotch', 4));
1893
 
1894
        // First request can be satisfied from A; second request requires B...
1895
        $this->assertEquals('Tag', $multicache->get_versioned('game', 2));
1896
        $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3));
1897
 
1898
        // And should update the data in A.
1899
        $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $storea->get($hashgame));
1900
        $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 1));
1901
 
1902
        // But newer data should still be in C.
1903
        $this->assertEquals('Hopscotch', $multicache->get_versioned('game', 4));
1904
        // Now it's stored in A and B too.
1905
        $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storea->get($hashgame));
1906
        $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storeb->get($hashgame));
1907
    }
1908
 
1909
    /**
1910
     * Test that multiple application loaders work ok.
1911
     */
11 efrain 1912
    public function test_multiple_session_loaders(): void {
1 efrain 1913
        /* @var cache_config_testing $instance */
1914
        $instance = cache_config_testing::instance(true);
1915
        $instance->phpunit_add_session_store('phpunittest1');
1916
        $instance->phpunit_add_session_store('phpunittest2');
1917
        $instance->phpunit_add_definition('phpunit/multi_loader', array(
1918
            'mode' => cache_store::MODE_SESSION,
1919
            'component' => 'phpunit',
1920
            'area' => 'multi_loader'
1921
        ));
1922
        $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3);
1923
        $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2);
1924
 
1925
        $cache = cache::make('phpunit', 'multi_loader');
1926
        $this->assertInstanceOf(cache_session::class, $cache);
1927
        $this->assertFalse($cache->get('test'));
1928
        $this->assertTrue($cache->set('test', 'test'));
1929
        $this->assertEquals('test', $cache->get('test'));
1930
        $this->assertTrue($cache->delete('test'));
1931
        $this->assertFalse($cache->get('test'));
1932
        $this->assertTrue($cache->set('test', 'test'));
1933
        $this->assertTrue($cache->purge());
1934
        $this->assertFalse($cache->get('test'));
1935
 
1936
        // Test the many commands.
1937
        $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C')));
1938
        $result = $cache->get_many(array('a', 'b', 'c'));
1939
        $this->assertIsArray($result);
1940
        $this->assertCount(3, $result);
1941
        $this->assertArrayHasKey('a', $result);
1942
        $this->assertArrayHasKey('b', $result);
1943
        $this->assertArrayHasKey('c', $result);
1944
        $this->assertEquals('A', $result['a']);
1945
        $this->assertEquals('B', $result['b']);
1946
        $this->assertEquals('C', $result['c']);
1947
        $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c')));
1948
        $this->assertEquals(2, $cache->delete_many(array('a', 'c')));
1949
        $result = $cache->get_many(array('a', 'b', 'c'));
1950
        $this->assertIsArray($result);
1951
        $this->assertCount(3, $result);
1952
        $this->assertArrayHasKey('a', $result);
1953
        $this->assertArrayHasKey('b', $result);
1954
        $this->assertArrayHasKey('c', $result);
1955
        $this->assertFalse($result['a']);
1956
        $this->assertEquals('B', $result['b']);
1957
        $this->assertFalse($result['c']);
1958
 
1959
        // Test non-recursive deletes.
1960
        $this->assertTrue($cache->set('test', 'test'));
1961
        $this->assertSame('test', $cache->get('test'));
1962
        $this->assertTrue($cache->delete('test', false));
1963
        // We should still have it on a deeper loader.
1964
        $this->assertSame('test', $cache->get('test'));
1965
        // Test non-recusive with many functions.
1966
        $this->assertSame(3, $cache->set_many(array(
1967
            'one' => 'one',
1968
            'two' => 'two',
1969
            'three' => 'three'
1970
        )));
1971
        $this->assertSame('one', $cache->get('one'));
1972
        $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
1973
        $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false));
1974
        $this->assertSame('one', $cache->get('one'));
1975
        $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
1976
    }
1977
 
1978
    /**
1979
     * Test switching users with session caches.
1980
     */
11 efrain 1981
    public function test_session_cache_switch_user(): void {
1 efrain 1982
        $this->resetAfterTest(true);
1983
        $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache');
1984
        $user1 = $this->getDataGenerator()->create_user();
1985
        $user2 = $this->getDataGenerator()->create_user();
1986
 
1987
        // Log in as the first user.
1988
        $this->setUser($user1);
1989
        $sesskey1 = sesskey();
1990
 
1991
        // Set a basic value in the cache.
1992
        $cache->set('var', 1);
1993
        $this->assertTrue($cache->has('var'));
1994
        $this->assertEquals(1, $cache->get('var'));
1995
 
1996
        // Change to the second user.
1997
        $this->setUser($user2);
1998
        $sesskey2 = sesskey();
1999
 
2000
        // Make sure the cache doesn't give us the data for the last user.
2001
        $this->assertNotEquals($sesskey1, $sesskey2);
2002
        $this->assertFalse($cache->has('var'));
2003
        $this->assertEquals(false, $cache->get('var'));
2004
    }
2005
 
2006
    /**
2007
     * Test switching users with session caches.
2008
     */
11 efrain 2009
    public function test_session_cache_switch_user_application_mapping(): void {
1 efrain 2010
        $this->resetAfterTest(true);
2011
        $instance = cache_config_testing::instance(true);
2012
        $instance->phpunit_add_file_store('testfilestore');
2013
        $instance->phpunit_add_definition('phpunit/testappsession', array(
2014
            'mode' => cache_store::MODE_SESSION,
2015
            'component' => 'phpunit',
2016
            'area' => 'testappsession'
2017
        ));
2018
        $instance->phpunit_add_definition_mapping('phpunit/testappsession', 'testfilestore', 3);
2019
        $cache = cache::make('phpunit', 'testappsession');
2020
        $user1 = $this->getDataGenerator()->create_user();
2021
        $user2 = $this->getDataGenerator()->create_user();
2022
 
2023
        // Log in as the first user.
2024
        $this->setUser($user1);
2025
        $sesskey1 = sesskey();
2026
 
2027
        // Set a basic value in the cache.
2028
        $cache->set('var', 1);
2029
        $this->assertTrue($cache->has('var'));
2030
        $this->assertEquals(1, $cache->get('var'));
2031
 
2032
        // Change to the second user.
2033
        $this->setUser($user2);
2034
        $sesskey2 = sesskey();
2035
 
2036
        // Make sure the cache doesn't give us the data for the last user.
2037
        $this->assertNotEquals($sesskey1, $sesskey2);
2038
        $this->assertFalse($cache->has('var'));
2039
        $this->assertEquals(false, $cache->get('var'));
2040
    }
2041
 
2042
    /**
2043
     * Test two session caches being used at once to confirm collisions don't occur.
2044
     */
11 efrain 2045
    public function test_dual_session_caches(): void {
1 efrain 2046
        $instance = cache_config_testing::instance(true);
2047
        $instance->phpunit_add_definition('phpunit/testsess1', array(
2048
            'mode' => cache_store::MODE_SESSION,
2049
            'component' => 'phpunit',
2050
            'area' => 'testsess1'
2051
        ));
2052
        $instance->phpunit_add_definition('phpunit/testsess2', array(
2053
            'mode' => cache_store::MODE_SESSION,
2054
            'component' => 'phpunit',
2055
            'area' => 'testsess2'
2056
        ));
2057
        $cache1 = cache::make('phpunit', 'testsess1');
2058
        $cache2 = cache::make('phpunit', 'testsess2');
2059
 
2060
        $this->assertFalse($cache1->has('test'));
2061
        $this->assertFalse($cache2->has('test'));
2062
 
2063
        $this->assertTrue($cache1->set('test', '1'));
2064
 
2065
        $this->assertTrue($cache1->has('test'));
2066
        $this->assertFalse($cache2->has('test'));
2067
 
2068
        $this->assertTrue($cache2->set('test', '2'));
2069
 
2070
        $this->assertEquals(1, $cache1->get('test'));
2071
        $this->assertEquals(2, $cache2->get('test'));
2072
 
2073
        $this->assertTrue($cache1->delete('test'));
2074
    }
2075
 
2076
    /**
2077
     * Test multiple session caches when switching user.
2078
     */
11 efrain 2079
    public function test_session_cache_switch_user_multiple(): void {
1 efrain 2080
        $this->resetAfterTest(true);
2081
        $cache1 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache1');
2082
        $cache2 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache2');
2083
        $user1 = $this->getDataGenerator()->create_user();
2084
        $user2 = $this->getDataGenerator()->create_user();
2085
 
2086
        // Log in as the first user.
2087
        $this->setUser($user1);
2088
        $sesskey1 = sesskey();
2089
 
2090
        // Set a basic value in the caches.
2091
        $cache1->set('var', 1);
2092
        $cache2->set('var', 2);
2093
        $this->assertEquals(1, $cache1->get('var'));
2094
        $this->assertEquals(2, $cache2->get('var'));
2095
 
2096
        // Change to the second user.
2097
        $this->setUser($user2);
2098
        $sesskey2 = sesskey();
2099
 
2100
        // Make sure the cache doesn't give us the data for the last user.
2101
        // Also make sure that switching the user has lead to both caches being purged.
2102
        $this->assertNotEquals($sesskey1, $sesskey2);
2103
        $this->assertEquals(false, $cache1->get('var'));
2104
        $this->assertEquals(false, $cache2->get('var'));
2105
    }
2106
 
2107
    /**
2108
     * The application locking feature should work with caches that support multiple identifiers
2109
     * (static cache and MongoDB with a specific setting).
2110
     *
2111
     * @covers \cache_application
2112
     */
11 efrain 2113
    public function test_application_locking_multiple_identifier_cache(): void {
1 efrain 2114
        // Get an arbitrary definition (modinfo).
2115
        $instance = cache_config_testing::instance(true);
2116
        $definitions = $instance->get_definitions();
2117
        $definition = \cache_definition::load('phpunit', $definitions['core/coursemodinfo']);
2118
 
2119
        // Set up a static cache using that definition, wrapped in cache_application so we can do
2120
        // locking.
2121
        $store = new \cachestore_static('test');
2122
        $store->initialise($definition);
2123
        $cache = new cache_application($definition, $store);
2124
 
2125
        // Test the three locking functions.
2126
        $cache->acquire_lock('frog');
2127
        $this->assertTrue($cache->check_lock_state('frog'));
2128
        $cache->release_lock('frog');
2129
    }
2130
 
2131
    /**
2132
     * Test requiring a lock before attempting to set a key.
2133
     *
2134
     * @covers ::set_implementation
2135
     */
11 efrain 2136
    public function test_application_locking_before_write(): void {
1 efrain 2137
        $instance = cache_config_testing::instance(true);
2138
        $instance->phpunit_add_definition('phpunit/test_application_locking', array(
2139
            'mode' => cache_store::MODE_APPLICATION,
2140
            'component' => 'phpunit',
2141
            'area' => 'test_application_locking',
2142
            'staticacceleration' => true,
2143
            'staticaccelerationsize' => 1,
2144
            'requirelockingbeforewrite' => true
2145
        ));
2146
        $cache = cache::make('phpunit', 'test_application_locking');
2147
        $this->assertInstanceOf(cache_application::class, $cache);
2148
 
2149
        $cache->acquire_lock('a');
2150
        try {
2151
            // Set with lock.
2152
            $this->assertTrue($cache->set('a', 'A'));
2153
 
2154
            // Set without lock.
2155
            try {
2156
                $cache->set('b', 'B');
2157
                $this->fail();
2158
            } catch (\coding_exception $e) {
2159
                $this->assertStringContainsString(
2160
                        'Attempted to set cache key "b" without a lock. ' .
2161
                        'Locking before writes is required for phpunit/test_application_locking',
2162
                        $e->getMessage());
2163
            }
2164
 
2165
            // Set many without full lock.
2166
            try {
2167
                $cache->set_many(['a' => 'AA', 'b' => 'BB']);
2168
                $this->fail();
2169
            } catch (\coding_exception $e) {
2170
                $this->assertStringContainsString(
2171
                        'Attempted to set cache key "b" without a lock.',
2172
                        $e->getMessage());
2173
            }
2174
 
2175
            // Check it didn't set key a either.
2176
            $this->assertEquals('A', $cache->get('a'));
2177
 
2178
            // Set many with full lock.
2179
            $cache->acquire_lock('b');
2180
            try {
2181
                $this->assertEquals(2, $cache->set_many(['a' => 'AA', 'b' => 'BB']));
2182
                $this->assertEquals('AA', $cache->get('a'));
2183
                $this->assertEquals('BB', $cache->get('b'));
2184
            } finally {
2185
                $cache->release_lock('b');
2186
            }
2187
 
2188
            // Delete key with lock.
2189
            $this->assertTrue($cache->delete('a'));
2190
            $this->assertFalse($cache->get('a'));
2191
 
2192
            // Delete key without lock.
2193
            try {
2194
                $cache->delete('b');
2195
                $this->fail();
2196
            } catch (\coding_exception $e) {
2197
                $this->assertStringContainsString(
2198
                        'Attempted to delete cache key "b" without a lock.',
2199
                        $e->getMessage());
2200
            }
2201
 
2202
            // Delete many without full lock.
2203
            $cache->set('a', 'AAA');
2204
            try {
2205
                $cache->delete_many(['a', 'b']);
2206
                $this->fail();
2207
            } catch (\coding_exception $e) {
2208
                $this->assertStringContainsString(
2209
                        'Attempted to delete cache key "b" without a lock.',
2210
                        $e->getMessage());
2211
            }
2212
            // Nothing was deleted.
2213
            $this->assertEquals('AAA', $cache->get('a'));
2214
 
2215
            // Delete many with full lock.
2216
            $cache->acquire_lock('b');
2217
            try {
2218
                $this->assertEquals(2, $cache->delete_many(['a', 'b']));
2219
            } finally {
2220
                $cache->release_lock('b');
2221
            }
2222
            $this->assertFalse($cache->get('a'));
2223
            $this->assertFalse($cache->get('b'));
2224
        } finally {
2225
            $cache->release_lock('a');
2226
        }
2227
    }
2228
 
2229
    /**
2230
     * Test that locking before write works when writing across multiple layers.
2231
     *
2232
     * @covers \cache_loader
2233
     * @return void
2234
     */
11 efrain 2235
    public function test_application_locking_multiple_layers(): void {
1 efrain 2236
 
2237
        $instance = cache_config_testing::instance(true);
2238
        $instance->phpunit_add_definition('phpunit/test_application_locking', array(
2239
            'mode' => cache_store::MODE_APPLICATION,
2240
            'component' => 'phpunit',
2241
            'area' => 'test_application_locking',
2242
            'staticacceleration' => true,
2243
            'staticaccelerationsize' => 1,
2244
            'requirelockingbeforewrite' => true
2245
        ), false);
2246
        $instance->phpunit_add_file_store('phpunittest1');
2247
        $instance->phpunit_add_file_store('phpunittest2');
2248
        $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1);
2249
        $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2);
2250
 
2251
        $cache = cache::make('phpunit', 'test_application_locking');
2252
        $this->assertInstanceOf(cache_application::class, $cache);
2253
 
2254
        // Check that we can set a key across multiple layers.
2255
        $cache->acquire_lock('a');
2256
        $this->assertTrue($cache->set('a', 'A'));
2257
 
2258
        // Delete from the current layer.
2259
        $cache->delete('a', false);
2260
        $cache->release_lock('a');
2261
 
2262
        // Check that we can get the value from the deeper layer, which will also re-set it in the current one.
2263
        $this->assertEquals('A', $cache->get('a'));
2264
 
2265
        // Try set/delete/get_many.
2266
        $cache->acquire_lock('x');
2267
        $cache->acquire_lock('y');
2268
        $cache->acquire_lock('z');
2269
        $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z']));
2270
        $cache->delete_many(['x', 'y', 'z'], false);
2271
        $cache->release_lock('x');
2272
        $cache->release_lock('y');
2273
        $cache->release_lock('z');
2274
 
2275
        $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z']));
2276
 
2277
        $cache->purge();
2278
 
2279
        // Try the tests again with a third layer.
2280
        $instance->phpunit_add_file_store('phpunittest3');
2281
        $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest3', 3);
2282
        $cache = cache::make('phpunit', 'test_application_locking');
2283
        $this->assertInstanceOf(cache_application::class, $cache);
2284
 
2285
        // Check that we can set a key across multiple layers.
2286
        $cache->acquire_lock('a');
2287
        $this->assertTrue($cache->set('a', 'A'));
2288
 
2289
        // Delete from the current layer.
2290
        $cache->delete('a', false);
2291
        $cache->release_lock('a');
2292
 
2293
        // Check that we can get the value from the deeper layer, which will also re-set it in the current one.
2294
        $this->assertEquals('A', $cache->get('a'));
2295
 
2296
        // Try set/delete/get_many.
2297
        $cache->acquire_lock('x');
2298
        $cache->acquire_lock('y');
2299
        $cache->acquire_lock('z');
2300
        $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z']));
2301
        $cache->delete_many(['x', 'y', 'z'], false);
2302
        $cache->release_lock('x');
2303
        $cache->release_lock('y');
2304
        $cache->release_lock('z');
2305
 
2306
        $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z']));
2307
    }
2308
 
2309
    /**
2310
     * Tests that locking fails correctly when either layer of a 2-layer cache has a lock already.
2311
     *
2312
     * @covers \cache_loader
2313
     */
2314
    public function test_application_locking_multiple_layers_failures(): void {
2315
 
2316
        $instance = cache_config_testing::instance(true);
2317
        $instance->phpunit_add_definition('phpunit/test_application_locking', array(
2318
            'mode' => cache_store::MODE_APPLICATION,
2319
            'component' => 'phpunit',
2320
            'area' => 'test_application_locking',
2321
            'staticacceleration' => true,
2322
            'staticaccelerationsize' => 1,
2323
            'requirelockingbeforewrite' => true
2324
        ), false);
2325
        $instance->phpunit_add_file_store('phpunittest1');
2326
        $instance->phpunit_add_file_store('phpunittest2');
2327
        $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1);
2328
        $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2);
2329
 
2330
        $cache = cache::make('phpunit', 'test_application_locking');
2331
 
2332
        // We need to get the individual stores so as to set up the right behaviour here.
2333
        $ref = new \ReflectionClass('\cache');
2334
        $definitionprop = $ref->getProperty('definition');
2335
        $storeprop = $ref->getProperty('store');
2336
        $loaderprop = $ref->getProperty('loader');
2337
 
2338
        $definition = $definitionprop->getValue($cache);
2339
        $localstore = $storeprop->getValue($cache);
2340
        $sharedcache = $loaderprop->getValue($cache);
2341
        $sharedstore = $storeprop->getValue($sharedcache);
2342
 
2343
        // Set the lock waiting time to 1 second so it doesn't take forever to run the test.
2344
        $ref = new \ReflectionClass('\cachestore_file');
2345
        $lockwaitprop = $ref->getProperty('lockwait');
2346
 
2347
        $lockwaitprop->setValue($localstore, 1);
2348
        $lockwaitprop->setValue($sharedstore, 1);
2349
 
2350
        // Get key details and the cache identifier.
2351
        $hashedkey = cache_helper::hash_key('apple', $definition);
2352
        $localidentifier = $cache->get_identifier();
2353
        $sharedidentifier = $sharedcache->get_identifier();
2354
 
2355
        // 1. Local cache is not locked but parent cache is locked.
2356
        $sharedstore->acquire_lock($hashedkey, 'somebodyelse');
2357
        try {
2358
            try {
2359
                $cache->acquire_lock('apple');
2360
                $this->fail();
2361
            } catch (\moodle_exception $e) {
2362
            }
2363
 
2364
            // Neither store is locked by us, shared store still locked.
2365
            $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier));
2366
            $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier));
2367
            $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, 'somebodyelse'));
2368
        } finally {
2369
            $sharedstore->release_lock($hashedkey, 'somebodyelse');
2370
        }
2371
 
2372
        // 2. Local cache is locked, parent cache is not locked.
2373
        $localstore->acquire_lock($hashedkey, 'somebodyelse');
2374
        try {
2375
            try {
2376
                $cache->acquire_lock('apple');
2377
                $this->fail();
2378
            } catch (\moodle_exception $e) {
2379
 
2380
            }
2381
 
2382
            // Neither store is locked by us, local store still locked.
2383
            $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier));
2384
            $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier));
2385
            $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, 'somebodyelse'));
2386
        } finally {
2387
            $localstore->release_lock($hashedkey, 'somebodyelse');
2388
        }
2389
 
2390
        // 3. Just for completion, test what happens if we do lock it.
2391
        $this->assertTrue($cache->acquire_lock('apple'));
2392
        try {
2393
            $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, $localidentifier));
2394
            $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier));
2395
        } finally {
2396
            $cache->release_lock('apple');
2397
        }
2398
    }
2399
 
2400
    /**
2401
     * Test the static cache_helper method purge_stores_used_by_definition.
2402
     */
11 efrain 2403
    public function test_purge_stores_used_by_definition(): void {
1 efrain 2404
        $instance = cache_config_testing::instance(true);
2405
        $instance->phpunit_add_definition('phpunit/test_purge_stores_used_by_definition', array(
2406
            'mode' => cache_store::MODE_APPLICATION,
2407
            'component' => 'phpunit',
2408
            'area' => 'test_purge_stores_used_by_definition'
2409
        ));
2410
        $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition');
2411
        $this->assertInstanceOf(cache_application::class, $cache);
2412
        $this->assertTrue($cache->set('test', 'test'));
2413
        unset($cache);
2414
 
2415
        cache_helper::purge_stores_used_by_definition('phpunit', 'test_purge_stores_used_by_definition');
2416
 
2417
        $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition');
2418
        $this->assertInstanceOf(cache_application::class, $cache);
2419
        $this->assertFalse($cache->get('test'));
2420
    }
2421
 
2422
    /**
2423
     * Test purge routines.
2424
     */
11 efrain 2425
    public function test_purge_routines(): void {
1 efrain 2426
        $instance = cache_config_testing::instance(true);
2427
        $instance->phpunit_add_definition('phpunit/purge1', array(
2428
            'mode' => cache_store::MODE_APPLICATION,
2429
            'component' => 'phpunit',
2430
            'area' => 'purge1'
2431
        ));
2432
        $instance->phpunit_add_definition('phpunit/purge2', array(
2433
            'mode' => cache_store::MODE_APPLICATION,
2434
            'component' => 'phpunit',
2435
            'area' => 'purge2',
2436
            'requireidentifiers' => array(
2437
                'id'
2438
            )
2439
        ));
2440
 
2441
        $factory = cache_factory::instance();
2442
        $definition = $factory->create_definition('phpunit', 'purge1');
2443
        $this->assertFalse($definition->has_required_identifiers());
2444
        $cache = $factory->create_cache($definition);
2445
        $this->assertInstanceOf(cache_application::class, $cache);
2446
        $this->assertTrue($cache->set('test', 'test'));
2447
        $this->assertTrue($cache->has('test'));
2448
        cache_helper::purge_by_definition('phpunit', 'purge1');
2449
        $this->assertFalse($cache->has('test'));
2450
 
2451
        $factory = cache_factory::instance();
2452
        $definition = $factory->create_definition('phpunit', 'purge2');
2453
        $this->assertTrue($definition->has_required_identifiers());
2454
        $cache = $factory->create_cache($definition);
2455
        $this->assertInstanceOf(cache_application::class, $cache);
2456
        $this->assertTrue($cache->set('test', 'test'));
2457
        $this->assertTrue($cache->has('test'));
2458
        cache_helper::purge_stores_used_by_definition('phpunit', 'purge2');
2459
        $this->assertFalse($cache->has('test'));
2460
 
2461
        try {
2462
            cache_helper::purge_by_definition('phpunit', 'purge2');
2463
            $this->fail('Should not be able to purge a definition required identifiers without providing them.');
2464
        } catch (\coding_exception $ex) {
2465
            $this->assertStringContainsString('Identifier required for cache has not been provided', $ex->getMessage());
2466
        }
2467
    }
2468
 
2469
    /**
2470
     * Tests that ad-hoc caches are correctly purged with a purge_all call.
2471
     */
11 efrain 2472
    public function test_purge_all_with_adhoc_caches(): void {
1 efrain 2473
        $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'core_cache', 'test');
2474
        $cache->set('test', 123);
2475
        cache_helper::purge_all();
2476
        $this->assertFalse($cache->get('test'));
2477
    }
2478
 
2479
    /**
2480
     * Test that the default stores all support searching.
2481
     */
11 efrain 2482
    public function test_defaults_support_searching(): void {
1 efrain 2483
        $instance = cache_config_testing::instance(true);
2484
        $instance->phpunit_add_definition('phpunit/search1', array(
2485
            'mode' => cache_store::MODE_APPLICATION,
2486
            'component' => 'phpunit',
2487
            'area' => 'search1',
2488
            'requiresearchable' => true
2489
        ));
2490
        $instance->phpunit_add_definition('phpunit/search2', array(
2491
            'mode' => cache_store::MODE_SESSION,
2492
            'component' => 'phpunit',
2493
            'area' => 'search2',
2494
            'requiresearchable' => true
2495
        ));
2496
        $instance->phpunit_add_definition('phpunit/search3', array(
2497
            'mode' => cache_store::MODE_REQUEST,
2498
            'component' => 'phpunit',
2499
            'area' => 'search3',
2500
            'requiresearchable' => true
2501
        ));
2502
        $factory = cache_factory::instance();
2503
 
2504
        // Test application cache is searchable.
2505
        $definition = $factory->create_definition('phpunit', 'search1');
2506
        $this->assertInstanceOf(cache_definition::class, $definition);
2507
        $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE);
2508
        $cache = $factory->create_cache($definition);
2509
        $this->assertInstanceOf(cache_application::class, $cache);
2510
        $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements());
2511
 
2512
        // Test session cache is searchable.
2513
        $definition = $factory->create_definition('phpunit', 'search2');
2514
        $this->assertInstanceOf(cache_definition::class, $definition);
2515
        $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE);
2516
        $cache = $factory->create_cache($definition);
2517
        $this->assertInstanceOf(cache_session::class, $cache);
2518
        $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements());
2519
 
2520
        // Test request cache is searchable.
2521
        $definition = $factory->create_definition('phpunit', 'search3');
2522
        $this->assertInstanceOf(cache_definition::class, $definition);
2523
        $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE);
2524
        $cache = $factory->create_cache($definition);
2525
        $this->assertInstanceOf(cache_request::class, $cache);
2526
        $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements());
2527
    }
2528
 
2529
    /**
2530
     * Test static acceleration
2531
     *
2532
     * Note: All the assertGreaterThanOrEqual() in this test should be assertGreaterThan() be because of some microtime()
2533
     * resolution problems under some OSs / PHP versions, we are accepting equal as valid outcome. For more info see MDL-57147.
2534
     */
11 efrain 2535
    public function test_static_acceleration(): void {
1 efrain 2536
        $instance = cache_config_testing::instance();
2537
        $instance->phpunit_add_definition('phpunit/accelerated', array(
2538
            'mode' => cache_store::MODE_APPLICATION,
2539
            'component' => 'phpunit',
2540
            'area' => 'accelerated',
2541
            'staticacceleration' => true,
2542
            'staticaccelerationsize' => 3,
2543
        ));
2544
        $instance->phpunit_add_definition('phpunit/accelerated2', array(
2545
            'mode' => cache_store::MODE_APPLICATION,
2546
            'component' => 'phpunit',
2547
            'area' => 'accelerated2',
2548
            'staticacceleration' => true,
2549
            'staticaccelerationsize' => 3,
2550
        ));
2551
        $instance->phpunit_add_definition('phpunit/accelerated3', array(
2552
            'mode' => cache_store::MODE_APPLICATION,
2553
            'component' => 'phpunit',
2554
            'area' => 'accelerated3',
2555
            'staticacceleration' => true,
2556
            'staticaccelerationsize' => 3,
2557
        ));
2558
        $instance->phpunit_add_definition('phpunit/accelerated4', array(
2559
            'mode' => cache_store::MODE_APPLICATION,
2560
            'component' => 'phpunit',
2561
            'area' => 'accelerated4',
2562
            'staticacceleration' => true,
2563
            'staticaccelerationsize' => 4,
2564
        ));
2565
        $instance->phpunit_add_definition('phpunit/simpledataarea1', array(
2566
            'mode' => cache_store::MODE_APPLICATION,
2567
            'component' => 'phpunit',
2568
            'area' => 'simpledataarea1',
2569
            'staticacceleration' => true,
2570
            'simpledata' => false
2571
        ));
2572
        $instance->phpunit_add_definition('phpunit/simpledataarea2', array(
2573
            'mode' => cache_store::MODE_APPLICATION,
2574
            'component' => 'phpunit',
2575
            'area' => 'simpledataarea2',
2576
            'staticacceleration' => true,
2577
            'simpledata' => true
2578
        ));
2579
 
2580
        $cache = cache::make('phpunit', 'accelerated');
2581
        $this->assertInstanceOf(cache_phpunit_application::class, $cache);
2582
 
2583
        // Set and get three elements.
2584
        $this->assertTrue($cache->set('a', 'A'));
2585
        $this->assertTrue($cache->set('b', 'B'));
2586
        $this->assertTrue($cache->set('c', 'C'));
2587
        $this->assertEquals('A', $cache->get('a'));
2588
        $this->assertEquals(array('b' => 'B', 'c' => 'C'), $cache->get_many(array('b', 'c')));
2589
 
2590
        // Make sure all items are in static acceleration array.
2591
        $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a'));
2592
        $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b'));
2593
        $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c'));
2594
 
2595
        // Add new value and make sure it is in cache and it is in array.
2596
        $this->assertTrue($cache->set('d', 'D'));
2597
        $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d'));
2598
        $this->assertEquals('D', $cache->get('d'));
2599
 
2600
        // Now the least recent accessed item (a) is no longer in acceleration array.
2601
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2602
        $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b'));
2603
        $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c'));
2604
 
2605
        // Adding and deleting element.
2606
        $this->assertTrue($cache->set('a', 'A'));
2607
        $this->assertTrue($cache->delete('a'));
2608
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2609
        $this->assertFalse($cache->has('a'));
2610
 
2611
        // Make sure "purge" deletes from the array as well.
2612
        $cache->purge();
2613
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2614
        $this->assertFalse($cache->phpunit_static_acceleration_get('b'));
2615
        $this->assertFalse($cache->phpunit_static_acceleration_get('c'));
2616
        $this->assertFalse($cache->phpunit_static_acceleration_get('d'));
2617
        $this->assertFalse($cache->phpunit_static_acceleration_get('e'));
2618
 
2619
        // Check that the array holds the last accessed items by get/set.
2620
        $this->assertTrue($cache->set('a', 'A'));
2621
        $this->assertTrue($cache->set('b', 'B'));
2622
        $this->assertTrue($cache->set('c', 'C'));
2623
        $this->assertTrue($cache->set('d', 'D'));
2624
        $this->assertTrue($cache->set('e', 'E'));
2625
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2626
        $this->assertFalse($cache->phpunit_static_acceleration_get('b'));
2627
        $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c'));
2628
        $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d'));
2629
        $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e'));
2630
 
2631
        // Store a cacheable_object, get many times and ensure each time wake_for_cache is used.
2632
        // Both get and get_many are tested.  Two cache entries are used to ensure the times aren't
2633
        // confused with multiple calls to get()/get_many().
2634
        $startmicrotime = microtime(true);
2635
        $cacheableobject = new cache_phpunit_dummy_object(1, 1, $startmicrotime);
2636
        $cacheableobject2 = new cache_phpunit_dummy_object(2, 2, $startmicrotime);
2637
        $this->assertTrue($cache->set('a', $cacheableobject));
2638
        $this->assertTrue($cache->set('b', $cacheableobject2));
2639
        $staticaccelerationreturntime = $cache->phpunit_static_acceleration_get('a')->propertytime;
2640
        $staticaccelerationreturntimeb = $cache->phpunit_static_acceleration_get('b')->propertytime;
2641
        $this->assertGreaterThanOrEqual($startmicrotime, $staticaccelerationreturntime, 'Restore time of static must be newer.');
2642
 
2643
        // Reset the static cache without resetting backing store.
2644
        $cache->phpunit_static_acceleration_purge();
2645
 
2646
        // Get the value from the backend store, populating the static cache.
2647
        $cachevalue = $cache->get('a');
2648
        $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue);
2649
        $this->assertGreaterThanOrEqual($staticaccelerationreturntime, $cachevalue->propertytime);
2650
        $backingstorereturntime = $cachevalue->propertytime;
2651
 
2652
        $results = $cache->get_many(array('b'));
2653
        $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']);
2654
        $this->assertGreaterThanOrEqual($staticaccelerationreturntimeb, $results['b']->propertytime);
2655
        $backingstorereturntimeb = $results['b']->propertytime;
2656
 
2657
        // Obtain the value again and confirm that static cache is using wake_from_cache.
2658
        // Upon failure, the times are not adjusted as wake_from_cache is skipped as the
2659
        // value is stored serialized in the static acceleration cache.
2660
        $cachevalue = $cache->phpunit_static_acceleration_get('a');
2661
        $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue);
2662
        $this->assertGreaterThanOrEqual($backingstorereturntime, $cachevalue->propertytime);
2663
 
2664
        $results = $cache->get_many(array('b'));
2665
        $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']);
2666
        $this->assertGreaterThanOrEqual($backingstorereturntimeb, $results['b']->propertytime);
2667
 
2668
        /** @var cache_phpunit_application $cache */
2669
        $cache = cache::make('phpunit', 'accelerated2');
2670
        $this->assertInstanceOf(cache_phpunit_application::class, $cache);
2671
 
2672
        // Check that the array holds the last accessed items by get/set.
2673
        $this->assertTrue($cache->set('a', 'A'));
2674
        $this->assertTrue($cache->set('b', 'B'));
2675
        $this->assertTrue($cache->set('c', 'C'));
2676
        $this->assertTrue($cache->set('d', 'D'));
2677
        $this->assertTrue($cache->set('e', 'E'));
2678
        // Current keys in the array: c, d, e.
2679
        $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c'));
2680
        $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d'));
2681
        $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e'));
2682
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2683
        $this->assertFalse($cache->phpunit_static_acceleration_get('b'));
2684
 
2685
        $this->assertEquals('A', $cache->get('a'));
2686
        // Current keys in the array: d, e, a.
2687
        $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d'));
2688
        $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e'));
2689
        $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a'));
2690
        $this->assertFalse($cache->phpunit_static_acceleration_get('b'));
2691
        $this->assertFalse($cache->phpunit_static_acceleration_get('c'));
2692
 
2693
        // Current keys in the array: d, e, a.
2694
        $this->assertEquals(array('c' => 'C'), $cache->get_many(array('c')));
2695
        // Current keys in the array: e, a, c.
2696
        $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e'));
2697
        $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a'));
2698
        $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c'));
2699
        $this->assertFalse($cache->phpunit_static_acceleration_get('b'));
2700
        $this->assertFalse($cache->phpunit_static_acceleration_get('d'));
2701
 
2702
 
2703
        $cache = cache::make('phpunit', 'accelerated3');
2704
        $this->assertInstanceOf(cache_phpunit_application::class, $cache);
2705
 
2706
        // Check that the array holds the last accessed items by get/set.
2707
        $this->assertTrue($cache->set('a', 'A'));
2708
        $this->assertTrue($cache->set('b', 'B'));
2709
        $this->assertTrue($cache->set('c', 'C'));
2710
        $this->assertTrue($cache->set('d', 'D'));
2711
        $this->assertTrue($cache->set('e', 'E'));
2712
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2713
        $this->assertFalse($cache->phpunit_static_acceleration_get('b'));
2714
        $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c'));
2715
        $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d'));
2716
        $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e'));
2717
 
2718
        $this->assertTrue($cache->set('b', 'B2'));
2719
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2720
        $this->assertEquals('B2', $cache->phpunit_static_acceleration_get('b'));
2721
        $this->assertFalse($cache->phpunit_static_acceleration_get('c'));
2722
        $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d'));
2723
        $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e'));
2724
 
2725
        $this->assertEquals(2, $cache->set_many(array('b' => 'B3', 'c' => 'C3')));
2726
        $this->assertFalse($cache->phpunit_static_acceleration_get('a'));
2727
        $this->assertEquals('B3', $cache->phpunit_static_acceleration_get('b'));
2728
        $this->assertEquals('C3', $cache->phpunit_static_acceleration_get('c'));
2729
        $this->assertFalse($cache->phpunit_static_acceleration_get('d'));
2730
        $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e'));
2731
 
2732
        $cache = cache::make('phpunit', 'accelerated4');
2733
        $this->assertInstanceOf(cache_phpunit_application::class, $cache);
2734
        $this->assertTrue($cache->set('a', 'A'));
2735
        $this->assertTrue($cache->set('a', 'A'));
2736
        $this->assertTrue($cache->set('a', 'A'));
2737
        $this->assertTrue($cache->set('a', 'A'));
2738
        $this->assertTrue($cache->set('a', 'A'));
2739
        $this->assertTrue($cache->set('a', 'A'));
2740
        $this->assertTrue($cache->set('a', 'A'));
2741
        $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a'));
2742
        $this->assertEquals('A', $cache->get('a'));
2743
 
2744
        // Setting simpledata to false objects are cloned when retrieving data.
2745
        $cache = cache::make('phpunit', 'simpledataarea1');
2746
        $notreallysimple = new \stdClass();
2747
        $notreallysimple->name = 'a';
2748
        $cache->set('a', $notreallysimple);
2749
        $returnedinstance1 = $cache->get('a');
2750
        $returnedinstance2 = $cache->get('a');
2751
        $returnedinstance1->name = 'b';
2752
        $this->assertEquals('a', $returnedinstance2->name);
2753
 
2754
        // Setting simpledata to true we assume that data does not contain references.
2755
        $cache = cache::make('phpunit', 'simpledataarea2');
2756
        $notreallysimple = new \stdClass();
2757
        $notreallysimple->name = 'a';
2758
        $cache->set('a', $notreallysimple);
2759
        $returnedinstance1 = $cache->get('a');
2760
        $returnedinstance2 = $cache->get('a');
2761
        $returnedinstance1->name = 'b';
2762
        $this->assertEquals('b', $returnedinstance2->name);
2763
    }
2764
 
11 efrain 2765
    public function test_identifiers_have_separate_caches(): void {
1 efrain 2766
        $cachepg = cache::make('core', 'databasemeta', array('dbfamily' => 'pgsql'));
2767
        $cachepg->set(1, 'here');
2768
        $cachemy = cache::make('core', 'databasemeta', array('dbfamily' => 'mysql'));
2769
        $cachemy->set(2, 'there');
2770
        $this->assertEquals('here', $cachepg->get(1));
2771
        $this->assertEquals('there', $cachemy->get(2));
2772
        $this->assertFalse($cachemy->get(1));
2773
    }
2774
 
11 efrain 2775
    public function test_performance_debug(): void {
1 efrain 2776
        global $CFG;
2777
        $this->resetAfterTest(true);
2778
        $CFG->perfdebug = 15;
2779
 
2780
        $instance = cache_config_testing::instance();
2781
        $applicationid = 'phpunit/applicationperf';
2782
        $instance->phpunit_add_definition($applicationid, array(
2783
            'mode' => cache_store::MODE_APPLICATION,
2784
            'component' => 'phpunit',
2785
            'area' => 'applicationperf'
2786
        ));
2787
        $sessionid = 'phpunit/sessionperf';
2788
        $instance->phpunit_add_definition($sessionid, array(
2789
            'mode' => cache_store::MODE_SESSION,
2790
            'component' => 'phpunit',
2791
            'area' => 'sessionperf'
2792
        ));
2793
        $requestid = 'phpunit/requestperf';
2794
        $instance->phpunit_add_definition($requestid, array(
2795
            'mode' => cache_store::MODE_REQUEST,
2796
            'component' => 'phpunit',
2797
            'area' => 'requestperf'
2798
        ));
2799
 
2800
        $application = cache::make('phpunit', 'applicationperf');
2801
        $session = cache::make('phpunit', 'sessionperf');
2802
        $request = cache::make('phpunit', 'requestperf');
2803
 
2804
        // Check that no stats are recorded for these definitions yet.
2805
        $stats = cache_helper::get_stats();
2806
        $this->assertArrayNotHasKey($applicationid, $stats);
2807
        $this->assertArrayHasKey($sessionid, $stats);       // Session cache sets a key on construct.
2808
        $this->assertArrayNotHasKey($requestid, $stats);
2809
 
2810
        // Check that stores register misses.
2811
        $this->assertFalse($application->get('missMe'));
2812
        $this->assertFalse($application->get('missMe'));
2813
        $this->assertFalse($session->get('missMe'));
2814
        $this->assertFalse($session->get('missMe'));
2815
        $this->assertFalse($session->get('missMe'));
2816
        $this->assertFalse($request->get('missMe'));
2817
        $this->assertFalse($request->get('missMe'));
2818
        $this->assertFalse($request->get('missMe'));
2819
        $this->assertFalse($request->get('missMe'));
2820
 
2821
        $endstats = cache_helper::get_stats();
2822
        $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['misses']);
2823
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits']);
2824
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets']);
2825
        $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['misses']);
2826
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits']);
2827
        $this->assertEquals(1, $endstats[$sessionid]['stores']['default_session']['sets']);
2828
        $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['misses']);
2829
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits']);
2830
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets']);
2831
 
2832
        $startstats = cache_helper::get_stats();
2833
 
2834
        // Check that stores register sets.
2835
        $this->assertTrue($application->set('setMe1', 1));
2836
        $this->assertTrue($application->set('setMe2', 2));
2837
        $this->assertTrue($session->set('setMe1', 1));
2838
        $this->assertTrue($session->set('setMe2', 2));
2839
        $this->assertTrue($session->set('setMe3', 3));
2840
        $this->assertTrue($request->set('setMe1', 1));
2841
        $this->assertTrue($request->set('setMe2', 2));
2842
        $this->assertTrue($request->set('setMe3', 3));
2843
        $this->assertTrue($request->set('setMe4', 4));
2844
 
2845
        $endstats = cache_helper::get_stats();
2846
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] -
2847
                             $startstats[$applicationid]['stores']['default_application']['misses']);
2848
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] -
2849
                             $startstats[$applicationid]['stores']['default_application']['hits']);
2850
        $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] -
2851
                             $startstats[$applicationid]['stores']['default_application']['sets']);
2852
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] -
2853
                             $startstats[$sessionid]['stores']['default_session']['misses']);
2854
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] -
2855
                             $startstats[$sessionid]['stores']['default_session']['hits']);
2856
        $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] -
2857
                             $startstats[$sessionid]['stores']['default_session']['sets']);
2858
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] -
2859
                             $startstats[$requestid]['stores']['default_request']['misses']);
2860
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] -
2861
                             $startstats[$requestid]['stores']['default_request']['hits']);
2862
        $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] -
2863
                             $startstats[$requestid]['stores']['default_request']['sets']);
2864
 
2865
        $startstats = cache_helper::get_stats();
2866
 
2867
        // Check that stores register hits.
2868
        $this->assertEquals($application->get('setMe1'), 1);
2869
        $this->assertEquals($application->get('setMe2'), 2);
2870
        $this->assertEquals($session->get('setMe1'), 1);
2871
        $this->assertEquals($session->get('setMe2'), 2);
2872
        $this->assertEquals($session->get('setMe3'), 3);
2873
        $this->assertEquals($request->get('setMe1'), 1);
2874
        $this->assertEquals($request->get('setMe2'), 2);
2875
        $this->assertEquals($request->get('setMe3'), 3);
2876
        $this->assertEquals($request->get('setMe4'), 4);
2877
 
2878
        $endstats = cache_helper::get_stats();
2879
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] -
2880
                             $startstats[$applicationid]['stores']['default_application']['misses']);
2881
        $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] -
2882
                             $startstats[$applicationid]['stores']['default_application']['hits']);
2883
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] -
2884
                             $startstats[$applicationid]['stores']['default_application']['sets']);
2885
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] -
2886
                             $startstats[$sessionid]['stores']['default_session']['misses']);
2887
        $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] -
2888
                             $startstats[$sessionid]['stores']['default_session']['hits']);
2889
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] -
2890
                             $startstats[$sessionid]['stores']['default_session']['sets']);
2891
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] -
2892
                             $startstats[$requestid]['stores']['default_request']['misses']);
2893
        $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] -
2894
                             $startstats[$requestid]['stores']['default_request']['hits']);
2895
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] -
2896
                             $startstats[$requestid]['stores']['default_request']['sets']);
2897
 
2898
        $startstats = cache_helper::get_stats();
2899
 
2900
        // Check that stores register through get_many.
2901
        $application->get_many(array('setMe1', 'setMe2'));
2902
        $session->get_many(array('setMe1', 'setMe2', 'setMe3'));
2903
        $request->get_many(array('setMe1', 'setMe2', 'setMe3', 'setMe4'));
2904
 
2905
        $endstats = cache_helper::get_stats();
2906
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] -
2907
                             $startstats[$applicationid]['stores']['default_application']['misses']);
2908
        $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] -
2909
                             $startstats[$applicationid]['stores']['default_application']['hits']);
2910
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] -
2911
                             $startstats[$applicationid]['stores']['default_application']['sets']);
2912
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] -
2913
                             $startstats[$sessionid]['stores']['default_session']['misses']);
2914
        $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] -
2915
                             $startstats[$sessionid]['stores']['default_session']['hits']);
2916
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] -
2917
                             $startstats[$sessionid]['stores']['default_session']['sets']);
2918
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] -
2919
                             $startstats[$requestid]['stores']['default_request']['misses']);
2920
        $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] -
2921
                             $startstats[$requestid]['stores']['default_request']['hits']);
2922
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] -
2923
                             $startstats[$requestid]['stores']['default_request']['sets']);
2924
 
2925
        $startstats = cache_helper::get_stats();
2926
 
2927
        // Check that stores register through set_many.
2928
        $this->assertEquals(2, $application->set_many(['setMe1' => 1, 'setMe2' => 2]));
2929
        $this->assertEquals(3, $session->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3]));
2930
        $this->assertEquals(4, $request->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3, 'setMe4' => 4]));
2931
 
2932
        $endstats = cache_helper::get_stats();
2933
 
2934
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] -
2935
            $startstats[$applicationid]['stores']['default_application']['misses']);
2936
        $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] -
2937
            $startstats[$applicationid]['stores']['default_application']['hits']);
2938
        $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] -
2939
            $startstats[$applicationid]['stores']['default_application']['sets']);
2940
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] -
2941
            $startstats[$sessionid]['stores']['default_session']['misses']);
2942
        $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] -
2943
            $startstats[$sessionid]['stores']['default_session']['hits']);
2944
        $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] -
2945
            $startstats[$sessionid]['stores']['default_session']['sets']);
2946
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] -
2947
            $startstats[$requestid]['stores']['default_request']['misses']);
2948
        $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] -
2949
            $startstats[$requestid]['stores']['default_request']['hits']);
2950
        $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] -
2951
            $startstats[$requestid]['stores']['default_request']['sets']);
2952
    }
2953
 
2954
    /**
2955
     * Data provider for static acceleration performance tests.
2956
     *
2957
     * @return array
2958
     */
2959
    public function static_acceleration_performance_provider(): array {
2960
        // Note: These are the delta values, not the absolute values.
2961
        // Also note that the set will actually store the valuein the static cache immediately.
2962
        $validfirst = [
2963
            'default_application' => [
2964
                'hits' => 1,
2965
                'misses' => 0,
2966
            ],
2967
            cache_store::STATIC_ACCEL => [
2968
                'hits' => 0,
2969
                'misses' => 1,
2970
            ],
2971
        ];
2972
 
2973
        $validsecond = [
2974
            'default_application' => [
2975
                'hits' => 0,
2976
                'misses' => 0,
2977
            ],
2978
            cache_store::STATIC_ACCEL => [
2979
                'hits' => 1,
2980
                'misses' => 0,
2981
            ],
2982
        ];
2983
 
2984
        $invalidfirst = [
2985
            'default_application' => [
2986
                'hits' => 0,
2987
                'misses' => 1,
2988
            ],
2989
            cache_store::STATIC_ACCEL => [
2990
                'hits' => 0,
2991
                'misses' => 1,
2992
            ],
2993
        ];
2994
        $invalidsecond = [
2995
            'default_application' => [
2996
                'hits' => 0,
2997
                'misses' => 1,
2998
            ],
2999
            cache_store::STATIC_ACCEL => [
3000
                'hits' => 0,
3001
                'misses' => 1,
3002
            ],
3003
        ];;
3004
 
3005
        return [
3006
            'Truthy' => [
3007
                true,
3008
                $validfirst,
3009
                $validsecond,
3010
            ],
3011
            'Null' => [
3012
                null,
3013
                $validfirst,
3014
                $validsecond,
3015
            ],
3016
            'Empty Array' => [
3017
                [],
3018
                $validfirst,
3019
                $validsecond,
3020
            ],
3021
            'Empty String' => [
3022
                '',
3023
                $validfirst,
3024
                $validsecond,
3025
            ],
3026
            'False' => [
3027
                false,
3028
                $invalidfirst,
3029
                $invalidsecond,
3030
            ],
3031
        ];
3032
    }
3033
 
3034
    /**
3035
     * Test performance of static acceleration caches with values which are frequently confused with missing values.
3036
     *
3037
     * @dataProvider static_acceleration_performance_provider
3038
     * @param mixed $value The value to test
3039
     * @param array $firstfetchstats The expected stats on the first fetch
3040
     * @param array $secondfetchstats The expected stats on the subsequent fetch
3041
     */
3042
    public function test_static_acceleration_values_performance(
3043
        $value,
3044
        array $firstfetchstats,
3045
        array $secondfetchstats,
3046
    ): void {
3047
        // Note: We need to modify perfdebug to test this.
3048
        global $CFG;
3049
        $this->resetAfterTest(true);
3050
        $CFG->perfdebug = 15;
3051
 
3052
        $instance = cache_config_testing::instance();
3053
        $instance->phpunit_add_definition('phpunit/accelerated', [
3054
            'mode' => cache_store::MODE_APPLICATION,
3055
            'component' => 'phpunit',
3056
            'area' => 'accelerated',
3057
            'staticacceleration' => true,
3058
            'staticaccelerationsize' => 1,
3059
        ]);
3060
 
3061
        $cache = cache::make('phpunit', 'accelerated');
3062
        $this->assertInstanceOf(cache_phpunit_application::class, $cache);
3063
 
3064
        $this->assertTrue($cache->set('value', $value));
3065
 
3066
        $checkstats = function(
3067
            array $start,
3068
            array $expectedstats,
3069
        ): array {
3070
            $applicationid = 'phpunit/accelerated';
3071
            $endstats = cache_helper::get_stats();
3072
 
3073
            $start = $start[$applicationid]['stores'];
3074
            $end = $endstats[$applicationid]['stores'];
3075
 
3076
            foreach ($expectedstats as $cachename => $expected) {
3077
                foreach ($expected as $type => $value) {
3078
                    $startvalue = array_key_exists($cachename, $start) ? $start[$cachename][$type] : 0;
3079
                    $endvalue = array_key_exists($cachename, $end) ? $end[$cachename][$type] : 0;
3080
                    $diff = $endvalue - $startvalue;
3081
                    $this->assertEquals(
3082
                        $value,
3083
                        $diff,
3084
                        "Expected $cachename $type to be $value, got $diff",
3085
                    );
3086
                }
3087
            }
3088
 
3089
            return $endstats;
3090
        };
3091
 
3092
        // Reset the cache factory so that we can get the stats from a fresh instance.
3093
        $factory = cache_factory::instance();
3094
        $factory->reset_cache_instances();
3095
        $cache = cache::make('phpunit', 'accelerated');
3096
 
3097
        // Get the initial stats.
3098
        $startstats = cache_helper::get_stats();
3099
 
3100
        // Fetching the value the first time should seed the static cache from the application cache.
3101
        $this->assertEquals($value, $cache->get('value'));
3102
        $startstats = $checkstats($startstats, $firstfetchstats);
3103
 
3104
        // Fetching the value should only hit the static cache.
3105
        $this->assertEquals($value, $cache->get('value'));
3106
        $checkstats($startstats, $secondfetchstats);
3107
    }
3108
 
3109
 
11 efrain 3110
    public function test_static_cache(): void {
1 efrain 3111
        global $CFG;
3112
        $this->resetAfterTest(true);
3113
        $CFG->perfdebug = 15;
3114
 
3115
        // Create cache store with static acceleration.
3116
        $instance = cache_config_testing::instance();
3117
        $applicationid = 'phpunit/applicationperf';
3118
        $instance->phpunit_add_definition($applicationid, array(
3119
            'mode' => cache_store::MODE_APPLICATION,
3120
            'component' => 'phpunit',
3121
            'area' => 'applicationperf',
3122
            'simplekeys' => true,
3123
            'staticacceleration' => true,
3124
            'staticaccelerationsize' => 3
3125
        ));
3126
 
3127
        $application = cache::make('phpunit', 'applicationperf');
3128
 
3129
        // Check that stores register sets.
3130
        $this->assertTrue($application->set('setMe1', 1));
3131
        $this->assertTrue($application->set('setMe2', 0));
3132
        $this->assertTrue($application->set('setMe3', array()));
3133
        $this->assertTrue($application->get('setMe1') !== false);
3134
        $this->assertTrue($application->get('setMe2') !== false);
3135
        $this->assertTrue($application->get('setMe3') !== false);
3136
 
3137
        // Check that the static acceleration worked, even on empty arrays and the number 0.
3138
        $endstats = cache_helper::get_stats();
3139
        $this->assertEquals(0, $endstats[$applicationid]['stores']['** static accel. **']['misses']);
3140
        $this->assertEquals(3, $endstats[$applicationid]['stores']['** static accel. **']['hits']);
3141
    }
3142
 
11 efrain 3143
    public function test_performance_debug_off(): void {
1 efrain 3144
        global $CFG;
3145
        $this->resetAfterTest(true);
3146
        $CFG->perfdebug = 7;
3147
 
3148
        $instance = cache_config_testing::instance();
3149
        $applicationid = 'phpunit/applicationperfoff';
3150
        $instance->phpunit_add_definition($applicationid, array(
3151
            'mode' => cache_store::MODE_APPLICATION,
3152
            'component' => 'phpunit',
3153
            'area' => 'applicationperfoff'
3154
        ));
3155
        $sessionid = 'phpunit/sessionperfoff';
3156
        $instance->phpunit_add_definition($sessionid, array(
3157
            'mode' => cache_store::MODE_SESSION,
3158
            'component' => 'phpunit',
3159
            'area' => 'sessionperfoff'
3160
        ));
3161
        $requestid = 'phpunit/requestperfoff';
3162
        $instance->phpunit_add_definition($requestid, array(
3163
            'mode' => cache_store::MODE_REQUEST,
3164
            'component' => 'phpunit',
3165
            'area' => 'requestperfoff'
3166
        ));
3167
 
3168
        $application = cache::make('phpunit', 'applicationperfoff');
3169
        $session = cache::make('phpunit', 'sessionperfoff');
3170
        $request = cache::make('phpunit', 'requestperfoff');
3171
 
3172
        // Check that no stats are recorded for these definitions yet.
3173
        $stats = cache_helper::get_stats();
3174
        $this->assertArrayNotHasKey($applicationid, $stats);
3175
        $this->assertArrayNotHasKey($sessionid, $stats);
3176
        $this->assertArrayNotHasKey($requestid, $stats);
3177
 
3178
        // Trigger cache misses, cache sets and cache hits.
3179
        $this->assertFalse($application->get('missMe'));
3180
        $this->assertTrue($application->set('setMe', 1));
3181
        $this->assertEquals(1, $application->get('setMe'));
3182
        $this->assertFalse($session->get('missMe'));
3183
        $this->assertTrue($session->set('setMe', 3));
3184
        $this->assertEquals(3, $session->get('setMe'));
3185
        $this->assertFalse($request->get('missMe'));
3186
        $this->assertTrue($request->set('setMe', 4));
3187
        $this->assertEquals(4, $request->get('setMe'));
3188
 
3189
        // Check that no stats are being recorded for these definitions.
3190
        $endstats = cache_helper::get_stats();
3191
        $this->assertArrayNotHasKey($applicationid, $endstats);
3192
        $this->assertArrayNotHasKey($sessionid, $endstats);
3193
        $this->assertArrayNotHasKey($requestid, $endstats);
3194
    }
3195
 
3196
    /**
3197
     * Tests session cache event purge and subsequent visit in the same request.
3198
     *
3199
     * This test simulates a cache being created, a value being set, then the value being purged.
3200
     * A subsequent use of the same cache is started in the same request which fills the cache.
3201
     * A new request is started a short time later.
3202
     * The cache should be filled.
3203
     */
11 efrain 3204
    public function test_session_event_purge_same_second(): void {
1 efrain 3205
        $instance = cache_config_testing::instance();
3206
        $instance->phpunit_add_definition('phpunit/eventpurgetest', array(
3207
            'mode' => cache_store::MODE_SESSION,
3208
            'component' => 'phpunit',
3209
            'area' => 'eventpurgetest',
3210
            'invalidationevents' => array(
3211
                'crazyevent',
3212
            )
3213
        ));
3214
 
3215
        // Create the cache, set a value, and immediately purge it by event.
3216
        $cache = cache::make('phpunit', 'eventpurgetest');
3217
        $cache->set('testkey1', 'test data 1');
3218
        $this->assertEquals('test data 1', $cache->get('testkey1'));
3219
        cache_helper::purge_by_event('crazyevent');
3220
        $this->assertFalse($cache->get('testkey1'));
3221
 
3222
        // Set up the cache again in the same request and add a new value back in.
3223
        $factory = cache_factory::instance();
3224
        $factory->reset_cache_instances();
3225
        $cache = cache::make('phpunit', 'eventpurgetest');
3226
        $cache->set('testkey1', 'test data 2');
3227
        $this->assertEquals('test data 2', $cache->get('testkey1'));
3228
 
3229
        // Trick the cache into thinking that this is a new request.
3230
        cache_phpunit_cache::simulate_new_request();
3231
        $factory = cache_factory::instance();
3232
        $factory->reset_cache_instances();
3233
 
3234
        // Set up the cache again.
3235
        // This is a subsequent request at a new time, so we instead the invalidation time will be checked.
3236
        // The invalidation time should match the last purged time and the cache will not be re-purged.
3237
        $cache = cache::make('phpunit', 'eventpurgetest');
3238
        $this->assertEquals('test data 2', $cache->get('testkey1'));
3239
    }
3240
 
3241
    /**
3242
     * Test that values set in different sessions are stored with different key prefixes.
3243
     */
11 efrain 3244
    public function test_session_distinct_storage_key(): void {
1 efrain 3245
        $this->resetAfterTest();
3246
 
3247
        // Prepare a dummy session cache configuration.
3248
        $config = cache_config_testing::instance();
3249
        $config->phpunit_add_definition('phpunit/test_session_distinct_storage_key', array(
3250
            'mode' => cache_store::MODE_SESSION,
3251
            'component' => 'phpunit',
3252
            'area' => 'test_session_distinct_storage_key'
3253
        ));
3254
 
3255
        // First anonymous user's session cache.
3256
        cache_phpunit_session::phpunit_mockup_session_id('foo');
3257
        $this->setUser(0);
3258
        $cache1 = cache::make('phpunit', 'test_session_distinct_storage_key');
3259
 
3260
        // Reset cache instances to emulate a new request.
3261
        cache_factory::instance()->reset_cache_instances();
3262
 
3263
        // Another anonymous user's session cache.
3264
        cache_phpunit_session::phpunit_mockup_session_id('bar');
3265
        $this->setUser(0);
3266
        $cache2 = cache::make('phpunit', 'test_session_distinct_storage_key');
3267
 
3268
        cache_factory::instance()->reset_cache_instances();
3269
 
3270
        // Guest user's session cache.
3271
        cache_phpunit_session::phpunit_mockup_session_id('baz');
3272
        $this->setGuestUser();
3273
        $cache3 = cache::make('phpunit', 'test_session_distinct_storage_key');
3274
 
3275
        cache_factory::instance()->reset_cache_instances();
3276
 
3277
        // Same guest user's session cache but in another browser window.
3278
        cache_phpunit_session::phpunit_mockup_session_id('baz');
3279
        $this->setGuestUser();
3280
        $cache4 = cache::make('phpunit', 'test_session_distinct_storage_key');
3281
 
3282
        // Assert that different PHP session implies different key prefix for storing values.
3283
        $this->assertNotEquals($cache1->phpunit_get_key_prefix(), $cache2->phpunit_get_key_prefix());
3284
 
3285
        // Assert that same PHP session implies same key prefix for storing values.
3286
        $this->assertEquals($cache3->phpunit_get_key_prefix(), $cache4->phpunit_get_key_prefix());
3287
    }
3288
}