class AccessResultTest

Same name in other branches
  1. 9 core/tests/Drupal/Tests/Core/Access/AccessResultTest.php \Drupal\Tests\Core\Access\AccessResultTest
  2. 8.9.x core/tests/Drupal/Tests/Core/Access/AccessResultTest.php \Drupal\Tests\Core\Access\AccessResultTest
  3. 10 core/tests/Drupal/Tests/Core/Access/AccessResultTest.php \Drupal\Tests\Core\Access\AccessResultTest

@coversDefaultClass \Drupal\Core\Access\AccessResult @group Access

Hierarchy

Expanded class hierarchy of AccessResultTest

File

core/tests/Drupal/Tests/Core/Access/AccessResultTest.php, line 20

Namespace

Drupal\Tests\Core\Access
View source
class AccessResultTest extends UnitTestCase {
    
    /**
     * The cache contexts manager.
     *
     * @var \Drupal\Core\Cache\Context\CacheContextsManager|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $cacheContextsManager;
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        parent::setUp();
        $this->cacheContextsManager = $this->getMockBuilder('Drupal\\Core\\Cache\\Context\\CacheContextsManager')
            ->disableOriginalConstructor()
            ->getMock();
        $this->cacheContextsManager
            ->method('assertValidTokens')
            ->willReturn(TRUE);
        $container = new ContainerBuilder();
        $container->set('cache_contexts_manager', $this->cacheContextsManager);
        \Drupal::setContainer($container);
    }
    
    /**
     * @internal
     */
    protected function assertDefaultCacheability(AccessResult $access) : void {
        $this->assertSame([], $access->getCacheContexts());
        $this->assertSame([], $access->getCacheTags());
        $this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
    }
    
    /**
     * Tests the construction of an AccessResult object.
     *
     * @covers ::neutral
     */
    public function testConstruction() : void {
        $verify = function (AccessResult $access) {
            $this->assertFalse($access->isAllowed());
            $this->assertFalse($access->isForbidden());
            $this->assertTrue($access->isNeutral());
            $this->assertDefaultCacheability($access);
        };
        // Verify the object when using the constructor.
        $a = new AccessResultNeutral();
        $verify($a);
        // Verify the object when using the ::create() convenience method.
        $b = AccessResult::neutral();
        $verify($b);
        $this->assertEquals($a, $b);
    }
    
    /**
     * @covers ::allowed
     * @covers ::isAllowed
     * @covers ::isForbidden
     * @covers ::isNeutral
     */
    public function testAccessAllowed() : void {
        $verify = function (AccessResult $access) {
            $this->assertTrue($access->isAllowed());
            $this->assertFalse($access->isForbidden());
            $this->assertFalse($access->isNeutral());
            $this->assertDefaultCacheability($access);
        };
        // Verify the object when using the ::allowed() convenience static method.
        $b = AccessResult::allowed();
        $verify($b);
    }
    
    /**
     * @covers ::forbidden
     * @covers ::isAllowed
     * @covers ::isForbidden
     * @covers ::isNeutral
     */
    public function testAccessForbidden() : void {
        $verify = function (AccessResult $access) {
            $this->assertFalse($access->isAllowed());
            $this->assertTrue($access->isForbidden());
            $this->assertFalse($access->isNeutral());
            $this->assertDefaultCacheability($access);
        };
        // Verify the object when using the ::forbidden() convenience static method.
        $b = AccessResult::forbidden();
        $verify($b);
    }
    
    /**
     * @covers ::forbidden
     */
    public function testAccessForbiddenReason() : void {
        $verify = function (AccessResult $access, $reason) {
            $this->assertInstanceOf(AccessResultReasonInterface::class, $access);
            $this->assertSame($reason, $access->getReason());
        };
        $b = AccessResult::forbidden();
        $verify($b, '');
        $reason = $this->getRandomGenerator()
            ->string();
        $b = AccessResult::forbidden($reason);
        $verify($b, $reason);
        $b = AccessResult::forbiddenIf(TRUE, $reason);
        $verify($b, $reason);
    }
    
    /**
     * @covers ::allowedIf
     * @covers ::isAllowed
     * @covers ::isForbidden
     * @covers ::isNeutral
     */
    public function testAccessConditionallyAllowed() : void {
        $verify = function (AccessResult $access, $allowed) {
            $this->assertSame($allowed, $access->isAllowed());
            $this->assertFalse($access->isForbidden());
            $this->assertSame(!$allowed, $access->isNeutral());
            $this->assertDefaultCacheability($access);
        };
        $b1 = AccessResult::allowedIf(TRUE);
        $verify($b1, TRUE);
        $b2 = AccessResult::allowedIf(FALSE);
        $verify($b2, FALSE);
    }
    
    /**
     * @covers ::forbiddenIf
     * @covers ::isAllowed
     * @covers ::isForbidden
     * @covers ::isNeutral
     */
    public function testAccessConditionallyForbidden() : void {
        $verify = function (AccessResult $access, $forbidden) {
            $this->assertFalse($access->isAllowed());
            $this->assertSame($forbidden, $access->isForbidden());
            $this->assertSame(!$forbidden, $access->isNeutral());
            $this->assertDefaultCacheability($access);
        };
        $b1 = AccessResult::forbiddenIf(TRUE);
        $verify($b1, TRUE);
        $b2 = AccessResult::forbiddenIf(FALSE);
        $verify($b2, FALSE);
    }
    
    /**
     * @covers ::andIf
     */
    public function testAndIf() : void {
        $neutral = AccessResult::neutral('neutral message');
        $allowed = AccessResult::allowed();
        $forbidden = AccessResult::forbidden('forbidden message');
        $unused_access_result_due_to_lazy_evaluation = $this->createMock('\\Drupal\\Core\\Access\\AccessResultInterface');
        $unused_access_result_due_to_lazy_evaluation->expects($this->never())
            ->method($this->anything());
        // ALLOWED && ALLOWED === ALLOWED.
        $access = $allowed->andIf($allowed);
        $this->assertTrue($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertDefaultCacheability($access);
        // ALLOWED && NEUTRAL === NEUTRAL.
        $access = $allowed->andIf($neutral);
        $this->assertFalse($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertTrue($access->isNeutral());
        $this->assertEquals('neutral message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // ALLOWED && FORBIDDEN === FORBIDDEN.
        $access = $allowed->andIf($forbidden);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // NEUTRAL && ALLOW == NEUTRAL
        $access = $neutral->andIf($allowed);
        $this->assertFalse($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertTrue($access->isNeutral());
        $this->assertEquals('neutral message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // NEUTRAL && NEUTRAL === NEUTRAL.
        $access = $neutral->andIf($neutral);
        $this->assertFalse($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertTrue($access->isNeutral());
        $this->assertEquals('neutral message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // NEUTRAL && FORBIDDEN === FORBIDDEN.
        $access = $neutral->andIf($forbidden);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // FORBIDDEN && ALLOWED = FORBIDDEN
        $access = $forbidden->andIf($allowed);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // FORBIDDEN && NEUTRAL = FORBIDDEN
        $access = $forbidden->andIf($neutral);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // FORBIDDEN && FORBIDDEN = FORBIDDEN
        $access = $forbidden->andIf($forbidden);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // FORBIDDEN && * === FORBIDDEN: lazy evaluation verification.
        $access = $forbidden->andIf($unused_access_result_due_to_lazy_evaluation);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
    }
    
    /**
     * @covers ::orIf
     */
    public function testOrIf() : void {
        $neutral = AccessResult::neutral('neutral message');
        $neutral_other = AccessResult::neutral('other neutral message');
        $neutral_reasonless = AccessResult::neutral();
        $allowed = AccessResult::allowed();
        $forbidden = AccessResult::forbidden('forbidden message');
        $forbidden_other = AccessResult::forbidden('other forbidden message');
        $forbidden_reasonless = AccessResult::forbidden();
        $unused_access_result_due_to_lazy_evaluation = $this->createMock('\\Drupal\\Core\\Access\\AccessResultInterface');
        $unused_access_result_due_to_lazy_evaluation->expects($this->never())
            ->method($this->anything());
        // ALLOWED || ALLOWED === ALLOWED.
        $access = $allowed->orIf($allowed);
        $this->assertTrue($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertDefaultCacheability($access);
        // ALLOWED || NEUTRAL === ALLOWED.
        $access = $allowed->orIf($neutral);
        $this->assertTrue($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertDefaultCacheability($access);
        // ALLOWED || FORBIDDEN === FORBIDDEN.
        $access = $allowed->orIf($forbidden);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // NEUTRAL || NEUTRAL === NEUTRAL.
        $access = $neutral->orIf($neutral);
        $this->assertFalse($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertTrue($access->isNeutral());
        $this->assertEquals('neutral message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // Reason inheritance edge case: first reason is kept.
        $access = $neutral->orIf($neutral_other);
        $this->assertEquals('neutral message', $access->getReason());
        $access = $neutral_other->orIf($neutral);
        $this->assertEquals('other neutral message', $access->getReason());
        // Reason inheritance edge case: one of the operands is reasonless.
        $access = $neutral->orIf($neutral_reasonless);
        $this->assertEquals('neutral message', $access->getReason());
        $access = $neutral_reasonless->orIf($neutral);
        $this->assertEquals('neutral message', $access->getReason());
        $access = $neutral_reasonless->orIf($neutral_reasonless);
        $this->assertEquals('', $access->getReason());
        // NEUTRAL || ALLOWED === ALLOWED.
        $access = $neutral->orIf($allowed);
        $this->assertTrue($access->isAllowed());
        $this->assertFalse($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertDefaultCacheability($access);
        // NEUTRAL || FORBIDDEN === FORBIDDEN.
        $access = $neutral->orIf($forbidden);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // FORBIDDEN || ALLOWED === FORBIDDEN.
        $access = $forbidden->orIf($allowed);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // FORBIDDEN || NEUTRAL === FORBIDDEN.
        $access = $forbidden->orIf($neutral);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // FORBIDDEN || FORBIDDEN === FORBIDDEN.
        $access = $forbidden->orIf($forbidden);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
        // Reason inheritance edge case: first reason is kept.
        $access = $forbidden->orIf($forbidden_other);
        $this->assertEquals('forbidden message', $access->getReason());
        $access = $forbidden_other->orIf($forbidden);
        $this->assertEquals('other forbidden message', $access->getReason());
        // Reason inheritance edge case: one of the operands is reasonless.
        $access = $forbidden->orIf($forbidden_reasonless);
        $this->assertEquals('forbidden message', $access->getReason());
        $access = $forbidden_reasonless->orIf($forbidden);
        $this->assertEquals('forbidden message', $access->getReason());
        $access = $forbidden_reasonless->orIf($forbidden_reasonless);
        $this->assertEquals('', $access->getReason());
        // FORBIDDEN || * === FORBIDDEN.
        $access = $forbidden->orIf($unused_access_result_due_to_lazy_evaluation);
        $this->assertFalse($access->isAllowed());
        $this->assertTrue($access->isForbidden());
        $this->assertFalse($access->isNeutral());
        $this->assertEquals('forbidden message', $access->getReason());
        $this->assertDefaultCacheability($access);
    }
    
    /**
     * @covers ::setCacheMaxAge
     * @covers ::getCacheMaxAge
     */
    public function testCacheMaxAge() : void {
        $this->assertSame(Cache::PERMANENT, AccessResult::neutral()->getCacheMaxAge());
        $this->assertSame(1337, AccessResult::neutral()->setCacheMaxAge(1337)
            ->getCacheMaxAge());
    }
    
    /**
     * @covers ::addCacheContexts
     * @covers ::resetCacheContexts
     * @covers ::getCacheContexts
     * @covers ::cachePerPermissions
     * @covers ::cachePerUser
     * @covers ::allowedIfHasPermission
     */
    public function testCacheContexts() : void {
        $verify = function (AccessResult $access, array $contexts) {
            $this->assertFalse($access->isAllowed());
            $this->assertFalse($access->isForbidden());
            $this->assertTrue($access->isNeutral());
            $this->assertSame(Cache::PERMANENT, $access->getCacheMaxAge());
            $this->assertEqualsCanonicalizing($contexts, $access->getCacheContexts());
            $this->assertEqualsCanonicalizing([], $access->getCacheTags());
        };
        $access = AccessResult::neutral()->addCacheContexts([
            'foo',
        ]);
        $verify($access, [
            'foo',
        ]);
        // Verify resetting works.
        $access->resetCacheContexts();
        $verify($access, []);
        // Verify idempotency.
        $access->addCacheContexts([
            'foo',
        ])
            ->addCacheContexts([
            'foo',
        ]);
        $verify($access, [
            'foo',
        ]);
        // Verify same values in different call order yields the same result.
        $access->resetCacheContexts()
            ->addCacheContexts([
            'foo',
        ])
            ->addCacheContexts([
            'bar',
        ]);
        $verify($access, [
            'bar',
            'foo',
        ]);
        $access->resetCacheContexts()
            ->addCacheContexts([
            'bar',
        ])
            ->addCacheContexts([
            'foo',
        ]);
        $verify($access, [
            'bar',
            'foo',
        ]);
        // ::cachePerPermissions() convenience method.
        $contexts = [
            'user.permissions',
        ];
        $a = AccessResult::neutral()->addCacheContexts($contexts);
        $verify($a, $contexts);
        $b = AccessResult::neutral()->cachePerPermissions();
        $verify($b, $contexts);
        $this->assertEquals($a, $b);
        // ::cachePerUser() convenience method.
        $contexts = [
            'user',
        ];
        $a = AccessResult::neutral()->addCacheContexts($contexts);
        $verify($a, $contexts);
        $b = AccessResult::neutral()->cachePerUser();
        $verify($b, $contexts);
        $this->assertEquals($a, $b);
        // Both.
        $contexts = [
            'user',
            'user.permissions',
        ];
        $a = AccessResult::neutral()->addCacheContexts($contexts);
        $verify($a, $contexts);
        $b = AccessResult::neutral()->cachePerPermissions()
            ->cachePerUser();
        $verify($b, $contexts);
        $c = AccessResult::neutral()->cachePerUser()
            ->cachePerPermissions();
        $verify($c, $contexts);
        $this->assertEqualsCanonicalizing($a, $b);
        $this->assertEquals($a, $c);
        // ::allowIfHasPermission and ::allowedIfHasPermission convenience methods.
        $account = $this->createMock('\\Drupal\\Core\\Session\\AccountInterface');
        $account->expects($this->any())
            ->method('hasPermission')
            ->with('may herd llamas')
            ->willReturn(FALSE);
        $contexts = [
            'user.permissions',
        ];
        // Verify the object when using the ::allowedIfHasPermission() convenience
        // static method.
        $b = AccessResult::allowedIfHasPermission($account, 'may herd llamas');
        $verify($b, $contexts);
    }
    
    /**
     * @covers ::addCacheTags
     * @covers ::addCacheableDependency
     * @covers ::getCacheTags
     * @covers ::resetCacheTags
     */
    public function testCacheTags() : void {
        $verify = function (AccessResult $access, array $tags, array $contexts = [], $max_age = Cache::PERMANENT) {
            $this->assertFalse($access->isAllowed());
            $this->assertFalse($access->isForbidden());
            $this->assertTrue($access->isNeutral());
            $this->assertSame($max_age, $access->getCacheMaxAge());
            $this->assertEqualsCanonicalizing($contexts, $access->getCacheContexts());
            $this->assertEqualsCanonicalizing($tags, $access->getCacheTags());
        };
        $access = AccessResult::neutral()->addCacheTags([
            'foo:bar',
        ]);
        $verify($access, [
            'foo:bar',
        ]);
        // Verify resetting works.
        $access->resetCacheTags();
        $verify($access, []);
        // Verify idempotency.
        $access->addCacheTags([
            'foo:bar',
        ])
            ->addCacheTags([
            'foo:bar',
        ]);
        $verify($access, [
            'foo:bar',
        ]);
        // Verify same values in different call order yields the same result.
        $access->resetCacheTags()
            ->addCacheTags([
            'bar:baz',
        ])
            ->addCacheTags([
            'bar:qux',
        ])
            ->addCacheTags([
            'foo:bar',
        ])
            ->addCacheTags([
            'foo:baz',
        ]);
        $verify($access, [
            'bar:baz',
            'bar:qux',
            'foo:bar',
            'foo:baz',
        ]);
        $access->resetCacheTags()
            ->addCacheTags([
            'foo:bar',
        ])
            ->addCacheTags([
            'bar:qux',
        ])
            ->addCacheTags([
            'foo:baz',
        ])
            ->addCacheTags([
            'bar:baz',
        ]);
        $verify($access, [
            'bar:baz',
            'bar:qux',
            'foo:bar',
            'foo:baz',
        ]);
        // ::addCacheableDependency() convenience method.
        $node = $this->createMock('\\Drupal\\node\\NodeInterface');
        $node->expects($this->any())
            ->method('getCacheTags')
            ->willReturn([
            'node:20011988',
        ]);
        $node->expects($this->any())
            ->method('getCacheMaxAge')
            ->willReturn(600);
        $node->expects($this->any())
            ->method('getCacheContexts')
            ->willReturn([
            'user',
        ]);
        $tags = [
            'node:20011988',
        ];
        $a = AccessResult::neutral()->addCacheTags($tags);
        $verify($a, $tags);
        $b = AccessResult::neutral()->addCacheableDependency($node);
        $verify($b, $tags, [
            'user',
        ], 600);
    }
    
    /**
     * @covers ::inheritCacheability
     */
    public function testInheritCacheability() : void {
        // andIf(); 1st has defaults, 2nd has custom tags, contexts and max-age.
        $access = AccessResult::allowed();
        $other = AccessResult::allowed()->setCacheMaxAge(1500)
            ->cachePerPermissions()
            ->addCacheTags([
            'node:20011988',
        ]);
        $this->assertInstanceOf(AccessResult::class, $access->inheritCacheability($other));
        $this->assertSame([
            'user.permissions',
        ], $access->getCacheContexts());
        $this->assertSame([
            'node:20011988',
        ], $access->getCacheTags());
        $this->assertSame(1500, $access->getCacheMaxAge());
        // andIf(); 1st has custom tags, max-age, 2nd has custom contexts and
        // max-age.
        $access = AccessResult::allowed()->cachePerUser()
            ->setCacheMaxAge(43200);
        $other = AccessResult::forbidden()->addCacheTags([
            'node:14031991',
        ])
            ->setCacheMaxAge(86400);
        $this->assertInstanceOf(AccessResult::class, $access->inheritCacheability($other));
        $this->assertSame([
            'user',
        ], $access->getCacheContexts());
        $this->assertSame([
            'node:14031991',
        ], $access->getCacheTags());
        $this->assertSame(43200, $access->getCacheMaxAge());
    }
    
    /**
     * Provides a list of access result pairs and operations to test.
     *
     * This tests the propagation of cacheability metadata. Rather than testing
     * every single bit of cacheability metadata, which would lead to a mind-
     * boggling number of permutations, in this test, we only consider the
     * permutations of all pairs of the following set:
     * - Allowed, implements CDI and is cacheable.
     * - Allowed, implements CDI and is not cacheable.
     * - Allowed, does not implement CDI (hence not cacheable).
     * - Forbidden, implements CDI and is cacheable.
     * - Forbidden, implements CDI and is not cacheable.
     * - Forbidden, does not implement CDI (hence not cacheable).
     * - Neutral, implements CDI and is cacheable.
     * - Neutral, implements CDI and is not cacheable.
     * - Neutral, does not implement CDI (hence not cacheable).
     *
     * (Where "CDI" is CacheableDependencyInterface.)
     *
     * This leads to 72 permutations (9!/(9-2)! = 9*8 = 72) per operation. There
     * are two operations to test (AND and OR), so that leads to a grand total of
     * 144 permutations, all of which are tested.
     *
     * There are two "contagious" patterns:
     * - Any operation with a forbidden access result yields a forbidden result.
     *   This therefore also applies to the cacheability metadata associated with
     *   a forbidden result. This is the case for bullets 4, 5 and 6 in the set
     *   above.
     * - Any operation yields an access result object that is of the same class
     *   (implementation) as the first operand. This is because operations are
     *   invoked on the first operand. Therefore, if the first implementation
     *   does not implement CacheableDependencyInterface, then the result won't
     *   either. This is the case for bullets 3, 6 and 9 in the set above.
     */
    public static function andOrCacheabilityPropagationProvider() {
        // ct: cacheable=true, cf: cacheable=false, un: uncacheable.
        // Note: the test cases that have a "un" access result as the first operand
        // test UncacheableTestAccessResult, not AccessResult. However, we
        // definitely want to verify that AccessResult's orIf() and andIf() methods
        // work correctly when given an AccessResultInterface implementation that
        // does not implement CacheableDependencyInterface, and we want to test the
        // full gamut of permutations, so that's not a problem.
        $allowed_ct = AccessResult::allowed();
        $allowed_cf = AccessResult::allowed()->setCacheMaxAge(0);
        $allowed_un = new UncacheableTestAccessResult('ALLOWED');
        $forbidden_ct = AccessResult::forbidden();
        $forbidden_cf = AccessResult::forbidden()->setCacheMaxAge(0);
        $forbidden_un = new UncacheableTestAccessResult('FORBIDDEN');
        $neutral_ct = AccessResult::neutral();
        $neutral_cf = AccessResult::neutral()->setCacheMaxAge(0);
        $neutral_un = new UncacheableTestAccessResult('NEUTRAL');
        // Structure:
        // - First column: first access result.
        // - Second column: operator ('OR' or 'AND').
        // - Third column: second access result.
        // - Fourth column: whether result implements CacheableDependencyInterface
        // - Fifth column: whether the result is cacheable (if column 4 is TRUE)
        return [
            // Allowed (ct) OR allowed (ct,cf,un).
[
                $allowed_ct,
                'OR',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'OR',
                $allowed_cf,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'OR',
                $allowed_un,
                TRUE,
                TRUE,
            ],
            // Allowed (cf) OR allowed (ct,cf,un).
[
                $allowed_cf,
                'OR',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_cf,
                'OR',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'OR',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Allowed (un) OR allowed (ct,cf,un).
[
                $allowed_un,
                'OR',
                $allowed_ct,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'OR',
                $allowed_cf,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'OR',
                $allowed_un,
                FALSE,
                NULL,
            ],
            // Allowed (ct) OR forbidden (ct,cf,un).
[
                $allowed_ct,
                'OR',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'OR',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_ct,
                'OR',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Allowed (cf) OR forbidden (ct,cf,un).
[
                $allowed_cf,
                'OR',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_cf,
                'OR',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'OR',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Allowed (un) OR forbidden (ct,cf,un).
[
                $allowed_un,
                'OR',
                $forbidden_ct,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'OR',
                $forbidden_cf,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'OR',
                $forbidden_un,
                FALSE,
                NULL,
            ],
            // Allowed (ct) OR neutral (ct,cf,un).
[
                $allowed_ct,
                'OR',
                $neutral_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'OR',
                $neutral_cf,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'OR',
                $neutral_un,
                TRUE,
                TRUE,
            ],
            // Allowed (cf) OR neutral (ct,cf,un).
[
                $allowed_cf,
                'OR',
                $neutral_ct,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'OR',
                $neutral_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'OR',
                $neutral_un,
                TRUE,
                FALSE,
            ],
            // Allowed (un) OR neutral (ct,cf,un).
[
                $allowed_un,
                'OR',
                $neutral_ct,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'OR',
                $neutral_cf,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'OR',
                $neutral_un,
                FALSE,
                NULL,
            ],
            // Forbidden (ct) OR allowed (ct,cf,un).
[
                $forbidden_ct,
                'OR',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'OR',
                $allowed_cf,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'OR',
                $allowed_un,
                TRUE,
                TRUE,
            ],
            // Forbidden (cf) OR allowed (ct,cf,un).
[
                $forbidden_cf,
                'OR',
                $allowed_ct,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'OR',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'OR',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Forbidden (un) OR allowed (ct,cf,un).
[
                $forbidden_un,
                'OR',
                $allowed_ct,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'OR',
                $allowed_cf,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'OR',
                $allowed_un,
                FALSE,
                NULL,
            ],
            // Forbidden (ct) OR neutral (ct,cf,un).
[
                $forbidden_ct,
                'OR',
                $neutral_ct,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'OR',
                $neutral_cf,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'OR',
                $neutral_un,
                TRUE,
                TRUE,
            ],
            // Forbidden (cf) OR neutral (ct,cf,un).
[
                $forbidden_cf,
                'OR',
                $neutral_ct,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'OR',
                $neutral_cf,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'OR',
                $neutral_un,
                TRUE,
                FALSE,
            ],
            // Forbidden (un) OR neutral (ct,cf,un).
[
                $forbidden_un,
                'OR',
                $neutral_ct,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'OR',
                $neutral_cf,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'OR',
                $neutral_un,
                FALSE,
                NULL,
            ],
            // Forbidden (ct) OR forbidden (ct,cf,un).
[
                $forbidden_ct,
                'OR',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'OR',
                $forbidden_cf,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'OR',
                $forbidden_un,
                TRUE,
                TRUE,
            ],
            // Forbidden (cf) OR forbidden (ct,cf,un).
[
                $forbidden_cf,
                'OR',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_cf,
                'OR',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'OR',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Forbidden (un) OR forbidden (ct,cf,un).
[
                $forbidden_un,
                'OR',
                $forbidden_ct,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'OR',
                $forbidden_cf,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'OR',
                $forbidden_un,
                FALSE,
                NULL,
            ],
            // Neutral (ct) OR allowed (ct,cf,un).
[
                $neutral_ct,
                'OR',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'OR',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_ct,
                'OR',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Neutral (cf) OR allowed (ct,cf,un).
[
                $neutral_cf,
                'OR',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_cf,
                'OR',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'OR',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Neutral (un) OR allowed (ct,cf,un).
[
                $neutral_un,
                'OR',
                $allowed_ct,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'OR',
                $allowed_cf,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'OR',
                $allowed_un,
                FALSE,
                NULL,
            ],
            // Neutral (ct) OR neutral (ct,cf,un).
[
                $neutral_ct,
                'OR',
                $neutral_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'OR',
                $neutral_cf,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'OR',
                $neutral_un,
                TRUE,
                TRUE,
            ],
            // Neutral (cf) OR neutral (ct,cf,un).
[
                $neutral_cf,
                'OR',
                $neutral_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_cf,
                'OR',
                $neutral_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'OR',
                $neutral_un,
                TRUE,
                FALSE,
            ],
            // Neutral (un) OR neutral (ct,cf,un).
[
                $neutral_un,
                'OR',
                $neutral_ct,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'OR',
                $neutral_cf,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'OR',
                $neutral_un,
                FALSE,
                NULL,
            ],
            // Neutral (ct) OR forbidden (ct,cf,un).
[
                $neutral_ct,
                'OR',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'OR',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_ct,
                'OR',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Neutral (cf) OR forbidden (ct,cf,un).
[
                $neutral_cf,
                'OR',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_cf,
                'OR',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'OR',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Neutral (un) OR forbidden (ct,cf,un).
[
                $neutral_un,
                'OR',
                $forbidden_ct,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'OR',
                $forbidden_cf,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'OR',
                $forbidden_un,
                FALSE,
                NULL,
            ],
            // Allowed (ct) AND allowed (ct,cf,un).
[
                $allowed_ct,
                'AND',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'AND',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_ct,
                'AND',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Allowed (cf) AND allowed (ct,cf,un).
[
                $allowed_cf,
                'AND',
                $allowed_ct,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'AND',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'AND',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Allowed (un) AND allowed (ct,cf,un).
[
                $allowed_un,
                'AND',
                $allowed_ct,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'AND',
                $allowed_cf,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'AND',
                $allowed_un,
                FALSE,
                NULL,
            ],
            // Allowed (ct) AND forbidden (ct,cf,un).
[
                $allowed_ct,
                'AND',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'AND',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_ct,
                'AND',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Allowed (cf) AND forbidden (ct,cf,un).
[
                $allowed_cf,
                'AND',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_cf,
                'AND',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'AND',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Allowed (un) AND forbidden (ct,cf,un).
[
                $allowed_un,
                'AND',
                $forbidden_ct,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'AND',
                $forbidden_cf,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'AND',
                $forbidden_un,
                FALSE,
                NULL,
            ],
            // Allowed (ct) AND neutral (ct,cf,un).
[
                $allowed_ct,
                'AND',
                $neutral_ct,
                TRUE,
                TRUE,
            ],
            [
                $allowed_ct,
                'AND',
                $neutral_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_ct,
                'AND',
                $neutral_un,
                TRUE,
                FALSE,
            ],
            // Allowed (cf) AND neutral (ct,cf,un).
[
                $allowed_cf,
                'AND',
                $neutral_ct,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'AND',
                $neutral_cf,
                TRUE,
                FALSE,
            ],
            [
                $allowed_cf,
                'AND',
                $neutral_un,
                TRUE,
                FALSE,
            ],
            // Allowed (un) AND neutral (ct,cf,un).
[
                $allowed_un,
                'AND',
                $neutral_ct,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'AND',
                $neutral_cf,
                FALSE,
                NULL,
            ],
            [
                $allowed_un,
                'AND',
                $neutral_un,
                FALSE,
                NULL,
            ],
            // Forbidden (ct) AND allowed (ct,cf,un).
[
                $forbidden_ct,
                'AND',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'AND',
                $allowed_cf,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'AND',
                $allowed_un,
                TRUE,
                TRUE,
            ],
            // Forbidden (cf) AND allowed (ct,cf,un).
[
                $forbidden_cf,
                'AND',
                $allowed_ct,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'AND',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'AND',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Forbidden (un) AND allowed (ct,cf,un).
[
                $forbidden_un,
                'AND',
                $allowed_ct,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'AND',
                $allowed_cf,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'AND',
                $allowed_un,
                FALSE,
                NULL,
            ],
            // Forbidden (ct) AND neutral (ct,cf,un).
[
                $forbidden_ct,
                'AND',
                $neutral_ct,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'AND',
                $neutral_cf,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'AND',
                $neutral_un,
                TRUE,
                TRUE,
            ],
            // Forbidden (cf) AND neutral (ct,cf,un).
[
                $forbidden_cf,
                'AND',
                $neutral_ct,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'AND',
                $neutral_cf,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'AND',
                $neutral_un,
                TRUE,
                FALSE,
            ],
            // Forbidden (un) AND neutral (ct,cf,un).
[
                $forbidden_un,
                'AND',
                $neutral_ct,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'AND',
                $neutral_cf,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'AND',
                $neutral_un,
                FALSE,
                NULL,
            ],
            // Forbidden (ct) AND forbidden (ct,cf,un).
[
                $forbidden_ct,
                'AND',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'AND',
                $forbidden_cf,
                TRUE,
                TRUE,
            ],
            [
                $forbidden_ct,
                'AND',
                $forbidden_un,
                TRUE,
                TRUE,
            ],
            // Forbidden (cf) AND forbidden (ct,cf,un).
[
                $forbidden_cf,
                'AND',
                $forbidden_ct,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'AND',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $forbidden_cf,
                'AND',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Forbidden (un) AND forbidden (ct,cf,un).
[
                $forbidden_un,
                'AND',
                $forbidden_ct,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'AND',
                $forbidden_cf,
                FALSE,
                NULL,
            ],
            [
                $forbidden_un,
                'AND',
                $forbidden_un,
                FALSE,
                NULL,
            ],
            // Neutral (ct) AND allowed (ct,cf,un).
[
                $neutral_ct,
                'AND',
                $allowed_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'AND',
                $allowed_cf,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'AND',
                $allowed_un,
                TRUE,
                TRUE,
            ],
            // Neutral (cf) AND allowed (ct,cf,un).
[
                $neutral_cf,
                'AND',
                $allowed_ct,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'AND',
                $allowed_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'AND',
                $allowed_un,
                TRUE,
                FALSE,
            ],
            // Neutral (un) AND allowed (ct,cf,un).
[
                $neutral_un,
                'AND',
                $allowed_ct,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'AND',
                $allowed_cf,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'AND',
                $allowed_un,
                FALSE,
                NULL,
            ],
            // Neutral (ct) AND neutral (ct,cf,un).
[
                $neutral_ct,
                'AND',
                $neutral_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'AND',
                $neutral_cf,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'AND',
                $neutral_un,
                TRUE,
                TRUE,
            ],
            // Neutral (cf) AND neutral (ct,cf,un).
[
                $neutral_cf,
                'AND',
                $neutral_ct,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'AND',
                $neutral_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'AND',
                $neutral_un,
                TRUE,
                FALSE,
            ],
            // Neutral (un) AND neutral (ct,cf,un).
[
                $neutral_un,
                'AND',
                $neutral_ct,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'AND',
                $neutral_cf,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'AND',
                $neutral_un,
                FALSE,
                NULL,
            ],
            // Neutral (ct) AND forbidden (ct,cf,un).
[
                $neutral_ct,
                'AND',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_ct,
                'AND',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_ct,
                'AND',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Neutral (cf) AND forbidden (ct,cf,un).
[
                $neutral_cf,
                'AND',
                $forbidden_ct,
                TRUE,
                TRUE,
            ],
            [
                $neutral_cf,
                'AND',
                $forbidden_cf,
                TRUE,
                FALSE,
            ],
            [
                $neutral_cf,
                'AND',
                $forbidden_un,
                TRUE,
                FALSE,
            ],
            // Neutral (un) AND forbidden (ct,cf,un).
[
                $neutral_un,
                'AND',
                $forbidden_ct,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'AND',
                $forbidden_cf,
                FALSE,
                NULL,
            ],
            [
                $neutral_un,
                'AND',
                $forbidden_un,
                FALSE,
                NULL,
            ],
        ];
    }
    
    /**
     * @covers ::andIf
     * @covers ::orIf
     * @covers ::inheritCacheability
     *
     * @dataProvider andOrCacheabilityPropagationProvider
     */
    public function testAndOrCacheabilityPropagation(AccessResultInterface $first, $op, AccessResultInterface $second, $implements_cacheable_dependency_interface, $is_cacheable) : void {
        if ($op === 'OR') {
            $result = $first->orIf($second);
        }
        elseif ($op === 'AND') {
            $result = $first->andIf($second);
        }
        else {
            throw new \LogicException('Invalid operator specified');
        }
        if ($implements_cacheable_dependency_interface) {
            $this->assertInstanceOf(CacheableDependencyInterface::class, $result);
            if ($result instanceof CacheableDependencyInterface) {
                $this->assertSame($is_cacheable, $result->getCacheMaxAge() !== 0, 'getCacheMaxAge() matches expectations.');
            }
        }
        else {
            $this->assertNotInstanceOf(CacheableDependencyInterface::class, $result);
        }
    }
    
    /**
     * Tests orIf().
     *
     * Tests the special case of ORing non-forbidden access results that are both
     * cacheable but have different cacheability metadata.
     * This is only the case for non-forbidden access results; we still abort the
     * ORing process as soon as a forbidden access result is encountered. This is
     * tested in ::testOrIf().
     *
     * @covers ::orIf
     */
    public function testOrIfCacheabilityMerging() : void {
        $merge_both_directions = function (AccessResult $a, AccessResult $b) {
            // A globally cacheable access result.
            $a->setCacheMaxAge(3600);
            // Another access result that is cacheable per permissions.
            $b->setCacheMaxAge(86400)
                ->cachePerPermissions();
            $r1 = $a->orIf($b);
            $this->assertSame(3600, $r1->getCacheMaxAge());
            $this->assertSame([
                'user.permissions',
            ], $r1->getCacheContexts());
            $r2 = $b->orIf($a);
            $this->assertSame(3600, $r2->getCacheMaxAge());
            $this->assertSame([
                'user.permissions',
            ], $r2->getCacheContexts());
        };
        // Merge either direction, get the same result.
        $merge_both_directions(AccessResult::allowed(), AccessResult::allowed());
        $merge_both_directions(AccessResult::allowed(), AccessResult::neutral());
        $merge_both_directions(AccessResult::neutral(), AccessResult::neutral());
        $merge_both_directions(AccessResult::neutral(), AccessResult::allowed());
    }
    
    /**
     * Tests allowedIfHasPermissions().
     *
     * @param string[] $permissions
     *   The permissions to check for.
     * @param string $conjunction
     *   The conjunction to use when checking for permission. 'AND' or 'OR'.
     * @param \Drupal\Core\Access\AccessResult $expected_access
     *   The expected access check result.
     *
     * @covers ::allowedIfHasPermissions
     * @dataProvider providerTestAllowedIfHasPermissions
     */
    public function testAllowedIfHasPermissions($permissions, $conjunction, AccessResult $expected_access) : void {
        $account = $this->createMock('\\Drupal\\Core\\Session\\AccountInterface');
        $account->expects($this->any())
            ->method('hasPermission')
            ->willReturnMap([
            [
                'allowed',
                TRUE,
            ],
            [
                'denied',
                FALSE,
            ],
        ]);
        if ($permissions) {
            $expected_access->cachePerPermissions();
        }
        $access_result = AccessResult::allowedIfHasPermissions($account, $permissions, $conjunction);
        $this->assertEquals($expected_access, $access_result);
    }
    
    /**
     * Provides data for the testAllowedIfHasPermissions() method.
     *
     * @return array
     *   An array of test scenarios with permissions, logic, and expected result.
     */
    public static function providerTestAllowedIfHasPermissions() {
        $access_result = AccessResult::allowedIf(FALSE);
        $data[] = [
            [],
            'AND',
            $access_result,
        ];
        $data[] = [
            [],
            'OR',
            $access_result,
        ];
        $access_result = AccessResult::allowedIf(TRUE);
        $data[] = [
            [
                'allowed',
            ],
            'OR',
            $access_result,
        ];
        $data[] = [
            [
                'allowed',
            ],
            'AND',
            $access_result,
        ];
        $access_result = AccessResult::allowedIf(FALSE);
        $access_result->setReason("The 'denied' permission is required.");
        $data[] = [
            [
                'denied',
            ],
            'OR',
            $access_result,
        ];
        $data[] = [
            [
                'denied',
            ],
            'AND',
            $access_result,
        ];
        $access_result = AccessResult::allowedIf(TRUE);
        $data[] = [
            [
                'allowed',
                'denied',
            ],
            'OR',
            $access_result,
        ];
        $data[] = [
            [
                'denied',
                'allowed',
            ],
            'OR',
            $access_result,
        ];
        $access_result = AccessResult::allowedIf(TRUE);
        $data[] = [
            [
                'allowed',
                'denied',
                'other',
            ],
            'OR',
            $access_result,
        ];
        $access_result = AccessResult::allowedIf(FALSE);
        $access_result->setReason("The following permissions are required: 'allowed' AND 'denied'.");
        $data[] = [
            [
                'allowed',
                'denied',
            ],
            'AND',
            $access_result,
        ];
        return $data;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
AccessResultTest::$cacheContextsManager protected property The cache contexts manager.
AccessResultTest::andOrCacheabilityPropagationProvider public static function Provides a list of access result pairs and operations to test.
AccessResultTest::assertDefaultCacheability protected function @internal
AccessResultTest::providerTestAllowedIfHasPermissions public static function Provides data for the testAllowedIfHasPermissions() method.
AccessResultTest::setUp protected function Overrides UnitTestCase::setUp
AccessResultTest::testAccessAllowed public function @covers ::allowed
@covers ::isAllowed
@covers ::isForbidden
@covers ::isNeutral
AccessResultTest::testAccessConditionallyAllowed public function @covers ::allowedIf
@covers ::isAllowed
@covers ::isForbidden
@covers ::isNeutral
AccessResultTest::testAccessConditionallyForbidden public function @covers ::forbiddenIf
@covers ::isAllowed
@covers ::isForbidden
@covers ::isNeutral
AccessResultTest::testAccessForbidden public function @covers ::forbidden
@covers ::isAllowed
@covers ::isForbidden
@covers ::isNeutral
AccessResultTest::testAccessForbiddenReason public function @covers ::forbidden
AccessResultTest::testAllowedIfHasPermissions public function Tests allowedIfHasPermissions().
AccessResultTest::testAndIf public function @covers ::andIf
AccessResultTest::testAndOrCacheabilityPropagation public function @covers ::andIf
@covers ::orIf
@covers ::inheritCacheability
AccessResultTest::testCacheContexts public function @covers ::addCacheContexts
@covers ::resetCacheContexts
@covers ::getCacheContexts
@covers ::cachePerPermissions
@covers ::cachePerUser
@covers ::allowedIfHasPermission
AccessResultTest::testCacheMaxAge public function @covers ::setCacheMaxAge
@covers ::getCacheMaxAge
AccessResultTest::testCacheTags public function @covers ::addCacheTags
@covers ::addCacheableDependency
@covers ::getCacheTags
@covers ::resetCacheTags
AccessResultTest::testConstruction public function Tests the construction of an AccessResult object.
AccessResultTest::testInheritCacheability public function @covers ::inheritCacheability
AccessResultTest::testOrIf public function @covers ::orIf
AccessResultTest::testOrIfCacheabilityMerging public function Tests orIf().
ExpectDeprecationTrait::expectDeprecation public function Adds an expected deprecation.
ExpectDeprecationTrait::setUpErrorHandler public function Sets up the test error handler.
ExpectDeprecationTrait::tearDownErrorHandler public function Tears down the test error handler.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
UnitTestCase::$root protected property The app root.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::setDebugDumpHandler public static function Registers the dumper CLI handler when the DebugDump extension is enabled.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.