Commit ade0944f authored by mrweiner's avatar mrweiner
Browse files

Issue #3296253 by mrweiner: Improve tests

parent da95512d
Loading
Loading
Loading
Loading
+6 −1
Changes for src/Object/SlotBase.php: 6 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -118,7 +118,12 @@ abstract class SlotBase implements SlotBaseInterface {
    /** @var array $existing_tags */
    $existing_tags = $cache_item->tags;
    $updated_tags = Cache::mergeTags($new_tags, $existing_tags);
    $this->setCache($cache_item->data, $cache_item->expire, $updated_tags);

    // Do not use $this->setCache() here.
    //  - We do not need to worry about setting the register.
    //  - setCache() causes tests to fail, because the method
    //    is abstract on BaseSlot and nothing will happen.
    $this->doSetCache($cache_item->data, $cache_item->expire, $updated_tags);
  }

  /**
+145 −11
Changes for tests/src/Kernel/CacheRegisterKernelTestBase.php: 145 added lines, 11 removed lines.
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@ use Drupal\cache_register\Object\RegisterInterface;
use Drupal\cache_register\Object\Slot;
use Drupal\cache_register\Object\SlotBaseInterface;
use Drupal\cache_register\Object\SlotInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\KernelTests\Core\Cache\DatabaseBackendTest;

/**
@@ -82,9 +84,9 @@ abstract class CacheRegisterKernelTestBase extends DatabaseBackendTest {
  /**
   * To help us test protected/private methods.
   *
   * @param int $obj
   * @param object $obj
   *   The object instance.
   * @param int $method_name
   * @param string $method_name
   *   The Method name.
   * @param array $args
   *   The args to pass into the method.
@@ -94,7 +96,7 @@ abstract class CacheRegisterKernelTestBase extends DatabaseBackendTest {
   *
   * @throws \ReflectionException
   */
  public static function callMethod($obj, $method_name, array $args = []) {
  public static function callMethod(object $obj, string $method_name, array $args = []) {
    $class = new \ReflectionClass($obj);
    $method = $class->getMethod($method_name);
    $method->setAccessible(TRUE);
@@ -145,7 +147,7 @@ abstract class CacheRegisterKernelTestBase extends DatabaseBackendTest {
   * @return array[]
   *   Returns the array values.
   */
  public function cacheDataProvider() {
  public function cacheDataProvider(): array {
    return [
      ['string data'],
      [['array', 'of', 'data']],
@@ -154,6 +156,66 @@ abstract class CacheRegisterKernelTestBase extends DatabaseBackendTest {
    ];
  }

  /**
   * Provides cache tags bool value.
   *
   * @return array
   *   Returns the array values.
   */
  public function cacheTagsProvider(): array {
    return [
      // Valid.
      ['tag:1', TRUE],
      [['tag:1', 'tag:2'], TRUE],
      // Invalid.
      [new \stdClass(), FALSE],
      [1, FALSE],
      [['tag:1', new \stdClass()], FALSE],
      [['tag:1', 1], FALSE],
    ];
  }

  /**
   * Common method for testing addCacheTags.
   *
   * @param \Drupal\cache_register\Object\SlotBaseInterface $slot
   *   The slot.
   * @param mixed $tags_to_add
   *   The tags.
   * @param bool $is_valid
   *   Whether the provider-provided tags are valid.
   *
   * @return void
   * @throws \ReflectionException
   * @throws \Throwable
   */
  protected function doTestAddCacheTags(SlotBaseInterface $slot, $tags_to_add, bool $is_valid) {
    if (!$is_valid) {
      $this->expectException('AssertionError');
    }
    $initial_data_source = 'some_data';
    $base_tags = ['tag_1', 'tag_2'];
    $this->doSetCache($slot, $initial_data_source, CacheBackendInterface::CACHE_PERMANENT, $base_tags);

    // Ensure the initial cache data was set as expected.
    $this->assertEquals($this->doGetCacheData($slot), $initial_data_source);
    $this->assertSlotTagsMatchExpected($slot, $base_tags);

    $slot->addCacheTags($tags_to_add);

    // Ensure the data didn't change as a result of setting the tags.
    $this->assertEquals($this->doGetCacheData($slot), $initial_data_source);
    $all_tags = Cache::mergeTags($base_tags, is_array($tags_to_add) ? $tags_to_add : [$tags_to_add]);
    $this->assertSlotTagsMatchExpected($slot, $all_tags);

    // Make sure that we test this at the end. If we test this
    // any earlier then the exception stops further execution and
    // subsequent tests will not run.
    $this->callMethod($slot, 'doSetCache', [NULL, 0, []]);
    $this->expectException('Drupal\cache_register\Exception\CacheNotSetError');
    $slot->addCacheTags($tags_to_add);
  }

  /**
   * Common method for testing setCacheData.
   */
@@ -162,11 +224,7 @@ abstract class CacheRegisterKernelTestBase extends DatabaseBackendTest {

    switch ($method_name) {
      case 'doSetCache':
        $this->callMethod(
          $slot,
          'doSetCache',
          [$data, $expire, $tags]
        );
        $this->doSetCache($slot, $data, $expire, $tags);
        break;

      case 'setCache':
@@ -177,9 +235,84 @@ abstract class CacheRegisterKernelTestBase extends DatabaseBackendTest {
    $cache_entry = $slot->getCacheBackend()->get($cid);
    $this->assertEquals($cache_entry->data, $data);
    $this->assertEquals($cache_entry->expire, $expire);
    foreach ($tags as $tag) {
      $this->assertContains($tag, $cache_entry->tags);
    $this->assertSlotTagsMatchExpected($slot, $tags);
  }

  /**
   * Shortcut to callMethod doSetCache on a slot via reflection.
   *
   * @param \Drupal\cache_register\Object\SlotBaseInterface $slot
   *   The slot.
   * @param $data
   *   The data to cache.
   * @param int $expire
   *   The cache expiration.
   * @param array $tags
   *   The cache tags.
   *
   * @return void
   * @throws \ReflectionException
   */
  protected function doSetCache(SlotBaseInterface $slot, $data, int $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = []) {
    $this->callMethod(
      $slot,
      'doSetCache',
      [$data, $expire, $tags]
    );
  }

  /**
   * Shortcut to callMethod doGetCacheData() on a slot via reflection.
   *
   * @param \Drupal\cache_register\Object\SlotBaseInterface $slot
   *   The slot.
   * @param bool $allow_invalid
   *   Whether to allow invalidated entries.
   *
   * @return mixed|null
   *   The cache data or NULL.
   * @throws \ReflectionException
   */
  protected function doGetCacheData(SlotBaseInterface $slot, bool $allow_invalid = FALSE) {
    return $this->callMethod($slot, 'doGetCacheData', [$allow_invalid]);
  }

  /**
   * Whether two items contain all the same items.
   *
   * This does not account for keys.
   *
   * @param array $a
   *   Array 1.
   * @param array $b
   *   Array 2.
   *
   * @return bool
   *   Whether two items contain all the same items.
   */
  protected function arraysContainSameItems(array $a, array $b): bool {
    return !array_diff($a, $b) && !array_diff($b, $a);
  }

  /**
   * Asserts that a slots tags match an expected set.
   *
   * @param \Drupal\cache_register\Object\SlotBaseInterface $slot
   *   The slot.
   * @param array $expected_tags
   *   The expected tags.
   *
   * @return void
   */
  protected function assertSlotTagsMatchExpected(SlotBaseInterface $slot, array $expected_tags) {
    $cache_entry = $slot->getCache();
    // The first tag will be based on the drawer, so we need
    // to append it to the expected tags in order to match
    // the two arrays.
    $provided_tags_plus_default = array_merge($expected_tags, [$cache_entry->tags[0]]);

    $this->assertTrue(!array_diff($provided_tags_plus_default, $cache_entry->tags));
    $this->assertTrue(!array_diff($cache_entry->tags, $provided_tags_plus_default));
  }

  /**
@@ -237,4 +370,5 @@ abstract class CacheRegisterKernelTestBase extends DatabaseBackendTest {
    return $drawer;
  }


}
+80 −53
Changes for tests/src/Kernel/SlotBaseTest.php: 80 added lines, 53 removed lines.
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

namespace Drupal\Tests\cache_register\Kernel;

use Drupal\cache_register\Object\SlotBaseInterface;
use Drupal\Core\Cache\CacheBackendInterface;

/**
@@ -77,7 +78,6 @@ class SlotBaseTest extends CacheRegisterKernelTestBase {
   *   Cannot rely on strtotime or associated approaches.
   * @param array $tags
   *   Array value.
   * @doesNotPerformAssertions
   */
  public function testDoSetCache($data, int $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = []) {
    $slot = $this->createSlotBase('testIsCached');
@@ -111,110 +111,137 @@ class SlotBaseTest extends CacheRegisterKernelTestBase {
   */
  public function testGetCache() {
    // Before caching.
    $slot = $this->createSlotBase('testGetCache');
    $slot_id = 'testGetCache';
    $slot = $this->createSlotBase($slot_id);
    $slot_twin = $this->createSlotBase($slot_id);
    $this->assertEquals(NULL, $slot->getCache());
    $this->assertEquals($slot->getCacheBackend()->get($slot->id()), $slot->getCache());
    $this->assertEquals($slot->getCache(), $slot_twin->getCache());

    // After caching.
    $slot->getCacheBackend()->set($slot->id(), 'cache_data');
    $this->assertNotEquals($slot->getCache(), NULL);
    $this->assertEquals($slot->getCacheBackend()->get($slot->id()), $slot->getCache());
    $this->assertEquals($slot->getCache(), $slot_twin->getCache());

    // After invalidating the cache.
    $slot->getCacheBackend()->invalidate($slot->id());
    $this->assertEquals(NULL, $slot->getCache());
    $this->assertNotEquals($slot->getCache(TRUE), NULL);
    $this->assertEquals($slot->getCacheBackend()->get($slot->id()), $slot->getCache());
    $this->assertEquals($slot->getCache(), $slot_twin->getCache());
    $this->assertEquals($slot->getCache(TRUE), $slot_twin->getCache(TRUE));

    // Repopulate and then delete the cache.
    $slot->getCacheBackend()->set($slot->id(), 'cache_data');
    $slot->getCacheBackend()->delete($slot->id());
    $this->assertEquals(NULL, $slot->getCache());
    $this->assertEquals($slot->getCacheBackend()->get($slot->id()), $slot->getCache());
    $this->assertEquals($slot->getCache(), $slot_twin->getCache());

  }

  /**
   * @covers ::doGetCacheData
   */
  public function testDoGetCacheData() {
    $slot = $this->createSlotBase('testIsCached');
    $slot_id = 'testIsCached';
    $slot = $this->createSlotBase($slot_id);
    $slot_twin = $this->createSlotBase($slot_id);
    $cid = $slot->id();
    $this->assertEquals(NULL, $slot->getCache());

    // After caching.
    $slot->getCacheBackend()->set($cid, 'cache_data');
    $this->assertNotEquals($this->callMethod($slot, 'doGetCacheData', [FALSE]), NULL);
    $this->assertEquals(
      $slot->getCache()->data,
      $this->callMethod($slot, 'doGetCacheData', [FALSE])
    );
    $cache_data = 'cache_data';
    $slot->getCacheBackend()->set($cid, $cache_data);
    $this->assertNotEquals($this->doGetCacheData($slot), NULL);
    $this->assertDoGetCacheData($slot, $slot_twin, $cache_data);

    // After invalidating the cache.
    $slot->getCacheBackend()->invalidate($slot->id());
    $this->assertDoGetCacheData($slot, $slot_twin, NULL);

    // Not sure why, but allowing invalid with this helper method throws:
    //  Fatal error: Uncaught LogicException: The database connection is not serializable.
    //   This probably means you are serializing an object that has an indirect reference
    //   to the database connection. Adjust your code so that is not necessary. Alternatively,
    //   look at DependencySerializationTrait as a temporary solution. in
    //   /var/www/html/web/core/lib/Drupal/Core/Database/Connection.php:2035
    //
    // Also tried using DependencySerializationTrait, but it didn't help.
    // As such, just need to duplicate that method here manually to test
    // allowing invalidated entries.
    // $this->assertDoGetCacheData($slot, $slot_twin, $cache_data, TRUE);

    $this->assertEquals(
      NULL,
      $this->callMethod($slot, 'doGetCacheData', [FALSE])
      $cache_data,
      $slot->getCache(TRUE)->data
    );
    $this->assertNotEquals($this->callMethod($slot, 'doGetCacheData', [TRUE]), NULL);
    $this->assertEquals(
      $slot->getCache(TRUE)->data,
      $this->callMethod($slot, 'doGetCacheData', [TRUE])
    );

    // Repopulate and then delete the cache.
    $slot->getCacheBackend()->set($cid, 'cache_data');
    $slot->getCacheBackend()->delete($cid);
    $this->assertEquals(
      NULL,
      $this->callMethod($slot, 'doGetCacheData', [FALSE])
      $this->doGetCacheData($slot, TRUE)
    );
    $this->assertEquals(
      $slot->getCache(),
      $this->callMethod($slot, 'doGetCacheData', [FALSE])
      $this->doGetCacheData($slot, TRUE),
      $this->doGetCacheData($slot_twin, TRUE),
    );

    // Repopulate the cache with a different value.
    $cache_data_2 = 'cache_data_2';
    $slot->getCacheBackend()->set($cid, $cache_data_2);
    $this->assertDoGetCacheData($slot, $slot_twin, $cache_data_2);


    // Delete the cache entry.
    $slot->getCacheBackend()->delete($cid);
    $this->assertDoGetCacheData($slot, $slot_twin, NULL);
  }

  /**
   * Provides cache tags bool value.
   * Runs assertions for testDoGetCacheData().
   *
   * @return array
   *   Returns the array values.
   * @param \Drupal\cache_register\Object\SlotBaseInterface $slot
   *   The initial slot.
   * @param \Drupal\cache_register\Object\SlotBaseInterface $slot_twin
   *   This initial slot's twin.
   * @param string|null $expected_value
   *   The value expected to be present in the cache.
   * @param bool $allow_invalid
   *   Whether to allow invalidated cache entries.
   *
   * @return void
   * @throws \ReflectionException
   */
  public function cacheTagsProvider() {
    return [
      // Valid.
      ['tag:1', TRUE],
      [['tag:1', 'tag:2'], TRUE],
      // Invalid.
      [new \stdClass(), FALSE],
      [1, FALSE],
      [['tag:1', new \stdClass()], FALSE],
      [['tag:1', 1], FALSE],
    ];
  private function assertDoGetCacheData(SlotBaseInterface $slot, SlotBaseInterface $slot_twin, ?string $expected_value, bool $allow_invalid = FALSE) {
    $real_cached_data = is_null($slot->getCache($allow_invalid)) ? NULL : $slot->getCache($allow_invalid)->data;

    $this->assertEquals($expected_value, $real_cached_data);
    $this->assertEquals($real_cached_data, $this->doGetCacheData($slot));
    $this->assertEquals($this->doGetCacheData($slot), $this->doGetCacheData($slot_twin));
  }


  /**
   * @covers ::addCacheTags
   * @dataProvider cacheTagsProvider
   */
  public function testAddCacheTags($tags, $is_valid) {
    if (!$is_valid) {
      $this->expectException('AssertionError');
    }

  public function testAddCacheTags($tags_to_add, $is_valid) {
    $slot = $this->createSlotBase('testAddCacheTags');

    $this->expectException('Drupal\cache_register\Exception\CacheNotSetError');
    $slot->addCacheTags($tags);

    $slot->setCache('some_data');
    $slot->addCacheTags($tags);

    $cache_entry = $slot->getCache();
    $tags = is_string($tags) ? [$tags] : $tags;
    foreach ($tags as $tag) {
      $this->assertContains($tag, $cache_entry->tags);
    $this->doTestAddCacheTags($slot, $tags_to_add, $is_valid);
  }

  /**
   * Gets the tags that are set on a slot's cache.
   *
   * @param \Drupal\cache_register\Object\SlotBaseInterface $slot
   *   The slot.
   *
   * @return array
   *   The tags.
   */
  private function getTagsFromSlotCacheEntry(SlotBaseInterface $slot): array {
    $this->assertNotEquals(NULL, $slot->getCache());
    return $slot->getCache()->tags;
  }

  /**
+14 −0
Changes for tests/src/Kernel/SlotTest.php: 14 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -102,4 +102,18 @@ class SlotTest extends CacheRegisterKernelTestBase {
    }
  }

  /**
   * Test addCacheTags.
   *
   * Probably overkill to test this here in addition to
   * in SlotBaseTest, but just to be safe.
   * @covers ::addCacheTags
   *
   * @dataProvider cacheTagsProvider
   */
  public function testAddCacheTags($tags_to_add, $is_valid) {
    $slot = $this->createSlot('testAddCacheTags');
    $this->doTestAddCacheTags($slot, $tags_to_add, $is_valid);
  }

}