Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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  /**
  18   * Redis cache test.
  19   *
  20   * If you wish to use these unit tests all you need to do is add the following definition to
  21   * your config.php file.
  22   *
  23   * define('TEST_CACHESTORE_REDIS_TESTSERVERS', '127.0.0.1');
  24   *
  25   * @package   cachestore_redis
  26   * @copyright 2018 Catalyst IT Australia {@link http://www.catalyst-au.net}
  27   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  
  30  defined('MOODLE_INTERNAL') || die();
  31  
  32  require_once (__DIR__.'/../../../tests/fixtures/stores.php');
  33  require_once (__DIR__.'/../lib.php');
  34  
  35  /**
  36   * Redis cache test - compressor settings.
  37   *
  38   * @package   cachestore_redis
  39   * @author    Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
  40   * @copyright 2018 Catalyst IT Australia {@link http://www.catalyst-au.net}
  41   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  class cachestore_redis_compressor_test extends advanced_testcase {
  44  
  45      /**
  46       * Test set up
  47       */
  48      public function setUp(): void {
  49          if (!cachestore_redis::are_requirements_met() || !defined('TEST_CACHESTORE_REDIS_TESTSERVERS')) {
  50              $this->markTestSkipped('Could not test cachestore_redis. Requirements are not met.');
  51          }
  52  
  53          parent::setUp();
  54      }
  55  
  56      /**
  57       * Create a cachestore.
  58       *
  59       * @param int $compressor
  60       * @param int $serializer
  61       * @return cachestore_redis
  62       */
  63      public function create_store($compressor, $serializer) {
  64          /** @var cache_definition $definition */
  65          $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_redis', 'phpunit_test');
  66          $config = cachestore_redis::unit_test_configuration();
  67          $config['compressor'] = $compressor;
  68          $config['serializer'] = $serializer;
  69          $store = new cachestore_redis('Test', $config);
  70          $store->initialise($definition);
  71  
  72          return $store;
  73      }
  74  
  75      /**
  76       * It misses a value.
  77       */
  78      public function test_it_can_miss_one() {
  79          $store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, Redis::SERIALIZER_PHP);
  80  
  81          self::assertFalse($store->get('missme'));
  82      }
  83  
  84      /**
  85       * It misses many values.
  86       */
  87      public function test_it_can_miss_many() {
  88          $store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, Redis::SERIALIZER_PHP);
  89  
  90          $expected = ['missme' => false, 'missmetoo' => false];
  91          $actual = $store->get_many(array_keys($expected));
  92          self::assertSame($expected, $actual);
  93      }
  94  
  95      /**
  96       * It misses some values.
  97       */
  98      public function test_it_can_miss_some() {
  99          $store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, Redis::SERIALIZER_PHP);
 100          $store->set('iamhere', 'youfoundme');
 101  
 102          $expected = ['missme' => false, 'missmetoo' => false, 'iamhere' => 'youfoundme'];
 103          $actual = $store->get_many(array_keys($expected));
 104          self::assertSame($expected, $actual);
 105      }
 106  
 107      /**
 108       * A provider for test_works_with_different_types
 109       *
 110       * @return array
 111       */
 112      public function provider_for_test_it_works_with_different_types() {
 113          $object = new stdClass();
 114          $object->field = 'value';
 115  
 116          return [
 117              ['string', 'Abc Def'],
 118              ['string_empty', ''],
 119              ['string_binary', gzencode('some binary data')],
 120              ['int', 123],
 121              ['int_zero', 0],
 122              ['int_negative', -100],
 123              ['int_huge', PHP_INT_MAX],
 124              ['float', 3.14],
 125              ['boolean_true', true],
 126              // Boolean 'false' is not tested as it is not allowed in Moodle.
 127              ['array', [1, 'b', 3.4]],
 128              ['array_map', ['a' => 'b', 'c' => 'd']],
 129              ['object_stdClass', $object],
 130              ['null', null],
 131          ];
 132      }
 133  
 134      /**
 135       * It works with different types.
 136       *
 137       * @dataProvider provider_for_test_it_works_with_different_types
 138       * @param string $key
 139       * @param mixed $value
 140       */
 141      public function test_it_works_with_different_types($key, $value) {
 142          $store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, Redis::SERIALIZER_PHP);
 143          $store->set($key, $value);
 144  
 145          self::assertEquals($value, $store->get($key), "Failed set/get for: {$key}");
 146      }
 147  
 148      /**
 149       * Test it works with different types for many.
 150       */
 151      public function test_it_works_with_different_types_for_many() {
 152          $store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, Redis::SERIALIZER_PHP);
 153  
 154          $provider = $this->provider_for_test_it_works_with_different_types();
 155          $keys = [];
 156          $values = [];
 157          $expected = [];
 158          foreach ($provider as $item) {
 159              $keys[] = $item[0];
 160              $values[] = ['key' => $item[0], 'value' => $item[1]];
 161              $expected[$item[0]] = $item[1];
 162          }
 163          $store->set_many($values);
 164          $actual = $store->get_many($keys);
 165          self::assertEquals($expected, $actual);
 166      }
 167  
 168      /**
 169       * Provider for set/get combination tests.
 170       *
 171       * @return array
 172       */
 173      public function provider_for_tests_setget() {
 174          if (!cachestore_redis::are_requirements_met()) {
 175              // Even though we skip all tests in this case, this provider can still show warnings about non-existing class.
 176              return [];
 177          }
 178  
 179          $data = [
 180              ['none, none',
 181                  Redis::SERIALIZER_NONE, cachestore_redis::COMPRESSOR_NONE,
 182                  'value1', 'value2'],
 183              ['none, gzip',
 184                  Redis::SERIALIZER_NONE, cachestore_redis::COMPRESSOR_PHP_GZIP,
 185                  gzencode('value1'), gzencode('value2')],
 186              ['php, none',
 187                  Redis::SERIALIZER_PHP, cachestore_redis::COMPRESSOR_NONE,
 188                  serialize('value1'), serialize('value2')],
 189              ['php, gzip',
 190                  Redis::SERIALIZER_PHP, cachestore_redis::COMPRESSOR_PHP_GZIP,
 191                  gzencode(serialize('value1')), gzencode(serialize('value2'))],
 192          ];
 193  
 194          if (defined('Redis::SERIALIZER_IGBINARY')) {
 195              $data[] = [
 196                  'igbinary, none',
 197                      Redis::SERIALIZER_IGBINARY, cachestore_redis::COMPRESSOR_NONE,
 198                      igbinary_serialize('value1'), igbinary_serialize('value2'),
 199              ];
 200              $data[] = [
 201                  'igbinary, gzip',
 202                      Redis::SERIALIZER_IGBINARY, cachestore_redis::COMPRESSOR_PHP_GZIP,
 203                      gzencode(igbinary_serialize('value1')), gzencode(igbinary_serialize('value2')),
 204              ];
 205          }
 206  
 207          if (extension_loaded('zstd')) {
 208              $data[] = [
 209                  'none, zstd',
 210                  Redis::SERIALIZER_NONE, cachestore_redis::COMPRESSOR_PHP_ZSTD,
 211                  zstd_compress('value1'), zstd_compress('value2'),
 212              ];
 213              $data[] = [
 214                  'php, zstd',
 215                  Redis::SERIALIZER_PHP, cachestore_redis::COMPRESSOR_PHP_ZSTD,
 216                  zstd_compress(serialize('value1')), zstd_compress(serialize('value2')),
 217              ];
 218  
 219              if (defined('Redis::SERIALIZER_IGBINARY')) {
 220                  $data[] = [
 221                      'igbinary, zstd',
 222                      Redis::SERIALIZER_IGBINARY, cachestore_redis::COMPRESSOR_PHP_ZSTD,
 223                      zstd_compress(igbinary_serialize('value1')), zstd_compress(igbinary_serialize('value2')),
 224                  ];
 225              }
 226          }
 227  
 228          return $data;
 229      }
 230  
 231      /**
 232       * Test we can use get and set with all combinations.
 233       *
 234       * @dataProvider provider_for_tests_setget
 235       * @param string $name
 236       * @param int $serializer
 237       * @param int $compressor
 238       * @param string $rawexpected1
 239       * @param string $rawexpected2
 240       */
 241      public function test_it_can_use_getset($name, $serializer, $compressor, $rawexpected1, $rawexpected2) {
 242          // Create a connection with the desired serialisation.
 243          $store = $this->create_store($compressor, $serializer);
 244          $store->set('key', 'value1');
 245  
 246          // Disable compressor and serializer to check the actual stored value.
 247          $rawstore = $this->create_store(cachestore_redis::COMPRESSOR_NONE, Redis::SERIALIZER_NONE);
 248  
 249          $data = $store->get('key');
 250          $rawdata = $rawstore->get('key');
 251          self::assertSame('value1', $data, "Invalid serialisation/unserialisation for: {$name}");
 252          self::assertSame($rawexpected1, $rawdata, "Invalid rawdata for: {$name}");
 253      }
 254  
 255      /**
 256       * Test we can use get and set many with all combinations.
 257       *
 258       * @dataProvider provider_for_tests_setget
 259       * @param string $name
 260       * @param int $serializer
 261       * @param int $compressor
 262       * @param string $rawexpected1
 263       * @param string $rawexpected2
 264       */
 265      public function test_it_can_use_getsetmany($name, $serializer, $compressor, $rawexpected1, $rawexpected2) {
 266          $many = [
 267              ['key' => 'key1', 'value' => 'value1'],
 268              ['key' => 'key2', 'value' => 'value2'],
 269          ];
 270          $keys = ['key1', 'key2'];
 271          $expectations = ['key1' => 'value1', 'key2' => 'value2'];
 272          $rawexpectations = ['key1' => $rawexpected1, 'key2' => $rawexpected2];
 273  
 274          // Create a connection with the desired serialisation.
 275          $store = $this->create_store($compressor, $serializer);
 276          $store->set_many($many);
 277  
 278          // Disable compressor and serializer to check the actual stored value.
 279          $rawstore = $this->create_store(cachestore_redis::COMPRESSOR_NONE, Redis::SERIALIZER_NONE);
 280  
 281          $data = $store->get_many($keys);
 282          $rawdata = $rawstore->get_many($keys);
 283          foreach ($keys as $key) {
 284              self::assertSame($expectations[$key],
 285                               $data[$key],
 286                               "Invalid serialisation/unserialisation for {$key} with serializer {$name}");
 287              self::assertSame($rawexpectations[$key],
 288                               $rawdata[$key],
 289                               "Invalid rawdata for {$key} with serializer {$name}");
 290          }
 291      }
 292  }