Proyectos de Subversion Moodle

Rev

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