You are here

authcache.test in Authenticated User Page Caching (Authcache) 7.2

Tests for system.module.

File

authcache.test
View source
<?php

/**
 * @file
 * Tests for system.module.
 */

/**
 * Helper class for module test cases.
 */
class AuthcacheWebTestCase extends DrupalWebTestCase {
  protected $plainUser;
  protected $webUser;
  protected $adminUser;
  protected $superUser;
  protected $stubmod;
  protected $stubbackend;

  /**
   * {@inheritdoc}
   */
  public function setUp() {
    parent::setUp('authcache', 'authcache_test', 'authcache_backend_test');
    if ($this->profile !== 'standard') {
      $this
        ->drupalCreateContentType(array(
        'type' => 'page',
        'name' => 'Basic page',
      ));
    }

    // Plain user only has the authenticated user role.
    $this->plainUser = $this
      ->drupalCreateUser();

    // Web user has one additional role.
    $this->webUser = $this
      ->drupalCreateUser(array(
      'edit own page content',
      'create page content',
    ));

    // Admin user also has one additional role.
    $this->adminUser = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'bypass node access',
      'administer nodes',
    ));

    // Update uid 1's name and password to we know it.
    $password = user_password();
    require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
    $account = array(
      'name' => 'user1',
      'pass' => user_hash_password(trim($password)),
    );

    // We cannot use user_save() here or the password would be hashed again.
    db_update('users')
      ->fields($account)
      ->condition('uid', 1)
      ->execute();

    // Store.
    $this->superUser = user_load(1, TRUE);
    $this->superUser->pass_raw = $password;

    // HookStub.
    $this->stubmod = new ModuleStub('authcache_test');
    $this->stubbackend = new ModuleStub('authcache_backend_test');
  }

  /**
   * Reset authcache configuration and test variables.
   */
  protected function setupConfig($defaults = array()) {
    $settings = array(
      'authcache_frontcontroller_whitelist',
      'authcache_key_abbrev',
      'authcache_key_generator',
      'authcache_key_generator_keys',
      'authcacke_key_lifetime',
      'authcache_http200',
      'authcache_mimetype',
      'authcache_noajax',
      'authcache_pagecaching',
      'authcache_roles',
      'authcache_su',
    );
    foreach ($settings as $var) {
      variable_del($var);
    }
    foreach ($defaults as $var => $value) {
      variable_set($var, $value);
    }
  }

  /**
   * Reset the variables used for communicating to authcache_test module.
   */
  protected function resetTestVariables() {
    $testvars = array(
      'authcache_test_add_cookie',
      'authcache_test_authcache_account_exclude',
      'authcache_test_authcache_cancel',
      'authcache_test_authcache_canceled',
      'authcache_test_authcache_cookie',
      'authcache_test_authcache_cookie_alter',
      'authcache_test_authcache_excluded',
      'authcache_test_authcache_key_properties',
      'authcache_test_authcache_preclude',
      'authcache_test_authcache_precluded',
      'authcache_test_authcache_request_exclude',
      'authcache_test_content_type',
      'authcache_test_cookie',
      'authcache_test_cookie_alter',
      'authcache_test_early_exit',
      'authcache_test_file_transfer',
      'authcache_test_headers_sent',
      'authcache_test_key_generator_key',
      'authcache_test_pagecaching_allow',
      'authcache_test_record',
      'authcache_test_status_message',
      'authcache_test_status_message_exit',
    );
    foreach ($testvars as $var) {
      variable_del($var);
    }
    drupal_static_reset();
  }

  /**
   * Utility method, make sure the specified user is logged in.
   */
  protected function authcacheSetUser($account) {
    if ($account->uid && $account != $this->loggedInUser) {
      $this
        ->drupalLogin($account);
    }
    elseif (!$account->uid && $this->loggedInUser) {
      $this
        ->drupalLogout();
    }
  }

  /**
   * Utility method, login with specified account, send get-request, logout.
   */
  protected function authcacheGet($path, $account = NULL, $options = array(), $headers = array()) {
    if ($account) {
      $this
        ->authcacheSetUser($account);
    }
    variable_set('authcache_test_record', TRUE);
    $result = $this
      ->drupalGet($path, $options, $headers);
    variable_set('authcache_test_record', FALSE);
    return $result;
  }

  /**
   * Utility method, login with specified account, send post-request, logout.
   */
  protected function authcachePost($path, $account, $edit, $button, $options = array(), $headers = array()) {
    if ($account) {
      $this
        ->authcacheSetUser($account);
    }
    $this
      ->drupalGet($path);
    variable_set('authcache_test_record', TRUE);
    $result = $this
      ->drupalPost(NULL, $edit, $button, $options, $headers);
    variable_set('authcache_test_record', FALSE);
    return $result;
  }

  /**
   * Test whether the preceeding request did hit an exclusion rule.
   */
  protected function assertAuthcacheExcluded($with_message = NULL) {
    if ($with_message) {
      $this
        ->assertEqual($with_message, variable_get('authcache_test_authcache_excluded'));
    }
    else {
      $this
        ->assertNotEqual(NULL, variable_get('authcache_test_authcache_excluded'));
    }
  }

  /**
   * Test whether the preceeding request did not hit any exclusion rule.
   */
  protected function assertAuthcacheNotExcluded() {
    $this
      ->assertEqual(NULL, variable_get('authcache_test_authcache_excluded'));
  }

  /**
   * Test whether the preceeding request did hit an cancelation rule.
   */
  protected function assertAuthcacheCanceled($with_message = NULL) {
    if ($with_message) {
      $this
        ->assertEqual($with_message, variable_get('authcache_test_authcache_canceled'));
    }
    else {
      $this
        ->assertNotEqual(NULL, variable_get('authcache_test_authcache_canceled'));
    }
  }

  /**
   * Test whether the preceeding request did hit an cancelation rule.
   */
  protected function assertAuthcacheNotCanceled() {
    $this
      ->assertEqual(NULL, variable_get('authcache_test_authcache_canceled'));
  }

  /**
   * Test whether the preceeding request did hit an preclusion rule.
   */
  protected function assertAuthcachePrecluded($with_message = NULL) {
    if ($with_message) {
      $this
        ->assertEqual($with_message, variable_get('authcache_test_authcache_precluded'));
    }
    else {
      $this
        ->assertNotEqual(NULL, variable_get('authcache_test_authcache_precluded'));
    }
  }

  /**
   * Test whether the preceeding request did not hit any preclusion rule.
   */
  protected function assertAuthcacheNotPrecluded() {
    $this
      ->assertEqual(NULL, variable_get('authcache_test_authcache_precluded'));
  }

  /**
   * Test whether the given stub passes the invocation verifier.
   */
  protected function assertStub(HookStubProxy $stub, $verifier, $message = NULL) {
    $result = $stub
      ->verify($verifier, $error);
    if (!$message) {
      $message = t('Verify invocation of hook @hook.', array(
        '@hook' => $stub
          ->hookname(),
      ));
    }
    if (!$result && is_string($error)) {
      $message .= ' ' . $error;
    }
    $this
      ->assertTrue($result, $message);
  }

}

/**
 * Test cache policy rules, i.e. exclusion and cancelation.
 */
class AuthcachePolicyTestCase extends AuthcacheWebTestCase {
  protected $profile = 'testing';

  /**
   * {@inheritdoc}
   */
  public static function getInfo() {
    return array(
      'name' => 'Cache policy',
      'description' => 'Test cache policy rules, i.e. exclusion and cancelation.',
      'group' => 'Authcache',
    );
  }

  /**
   * Test builtin standard cache exclusion rules.
   */
  public function testDefaultExclusionRules() {

    // Basic rule 1: Never cache when no backend is available.
    $hook_boot = $this->stubbackend
      ->hook('boot', FALSE);
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('No active cache backend.'));
    $this
      ->assertStub($hook_boot, HookStub::once());
    $this->stubbackend
      ->hook('boot', NULL);

    // Basic rule 2: Never cache when request method is not GET / HEAD.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));

    // For anonymous user.
    $this
      ->resetTestVariables();
    $edit['authcache_test_form_comment'] = $this
      ->randomName(16);
    $this
      ->authcachePost('authcache-test-form', drupal_anonymous_user(), $edit, t('Save configuration'));
    $this
      ->assertAuthcacheExcluded(t('Only GET and HEAD requests allowed. Method for this request is: @method.', array(
      '@method' => 'POST',
    )));

    // For authenticated user.
    $this
      ->resetTestVariables();
    $edit['authcache_test_form_comment'] = $this
      ->randomName(16);
    $this
      ->authcachePost('authcache-test-form', $this->plainUser, $edit, t('Save configuration'));
    $this
      ->assertAuthcacheExcluded(t('Only GET and HEAD requests allowed. Method for this request is: @method.', array(
      '@method' => 'POST',
    )));

    // Basic rule 3: Never cache when Drupal core page caching is turned on.
    variable_set('cache', TRUE);
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('Drupal core page caching for anonymous users active.'));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Drupal core page caching for anonymous users active.'));
    variable_del('cache');

    // Basic rule 4: Do not cache when request came in via a whitelisted
    // frontcontroller. Test with an empty list (removing the default entry
    // pointing at index.php).
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_frontcontroller_whitelist' => array(),
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('Not invoked using an allowed front controller.'));

    // Page caching rules.
    //
    // Default configuration, no roles enabled, ensure that pages are not
    // cached for any user.
    $this
      ->setupConfig();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Enable caching for anonymous users.
    $this
      ->setupConfig(array(
      'authcache_roles' => array(
        DRUPAL_ANONYMOUS_RID => DRUPAL_ANONYMOUS_RID,
      ),
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Pages under /cart are excluded by the default rule-set, ensure that this
    // is respected.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->plainUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Enable caching for authenticated users (without additional role).
    $this
      ->setupConfig(array(
      'authcache_roles' => array(
        DRUPAL_AUTHENTICATED_RID => DRUPAL_AUTHENTICATED_RID,
      ),
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Pages under /cart are excluded by the default rule-set, ensure that this
    // is respected.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Enable caching for role of webUser.
    $roles = $this->webUser->roles;
    unset($roles[DRUPAL_AUTHENTICATED_RID]);
    $this
      ->assertEqual(1, count($roles));
    $rid = reset($roles);
    $this
      ->setupConfig(array(
      'authcache_roles' => array(
        $rid => $rid,
      ),
    ));

    // Even when the role of web-user is enabled, no caching will happen unless
    // web-users role is explicitely added to a page caching ruleset.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheExcluded(t('Account does not match any page caching rule.'));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Pages under /cart are excluded by the default rule-set, ensure that this
    // is respected.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->plainUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Enable caching for all roles.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));

    // Even when the role of web-user is enabled, no caching will happen unless
    // web-users role is explicitely added to a page caching ruleset.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Pages under /cart are excluded by the default rule-set, ensure that this
    // is respected.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();

    // Account exclusion rule 1: Do not allow superuser.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled for superuser'));

    // ... unless explicitely requested.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_su' => 1,
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());

    // Account exclusion rule 2: User has at least one non-cacheable role.
    $this
      ->setupConfig(array(
      'authcache_roles' => array(
        DRUPAL_ANONYMOUS_RID => DRUPAL_ANONYMOUS_RID,
        DRUPAL_AUTHENTICATED_RID => DRUPAL_AUTHENTICATED_RID,
      ),
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $user_roles = $this->webUser->roles;
    unset($user_roles[DRUPAL_AUTHENTICATED_RID]);
    $this
      ->assertEqual(1, count($user_roles));
    $rid = reset($user_roles);
    $roles = implode(', ', array_intersect_key(user_roles(), $user_roles));
    $this
      ->assertAuthcacheExcluded(t('Account has non-cachable role @roles', array(
      '@roles' => $roles,
    )));

    // Account exclusion rule 3: User does not match any page caching rule.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheExcluded(t('Account does not match any page caching rule.'));
  }

  /**
   * Test builtin standard cache cancelation rules.
   */
  public function testDefaultCancelationRules() {

    // CSRF-protected forms.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-form', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-form', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Form with CSRF protection on page. Enable authcache_form or implement custom code capable of handling CSRF protected forms.'));

    // Forms with value elements.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-form-value-no-cache', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-form-value-no-cache', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-form-value-cache', drupal_anonymous_user());
    $this
      ->assertAuthcacheCanceled(t('Value element %name contained in the cacheable form %form_id. Please enable a suitable Authcache integration module for that form or file a support request.', array(
      '%name' => 'authcache_test_form_value',
      '%form_id' => 'authcache_test_form_value',
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-form-value-cache', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Value element %name contained in the cacheable form %form_id. Please enable a suitable Authcache integration module for that form or file a support request.', array(
      '%name' => 'authcache_test_form_value',
      '%form_id' => 'authcache_test_form_value',
    )));

    // Pages with tabs.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-tab', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-tab', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Tabs on page. Enable authcache_menu or implement custom code capable of detecting when caching tabs is acceptable.'));

    // Pages with action links.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-action', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-action', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Action links on page. Enable authcache_menu or implement custom code capable of detecting when caching local actions is acceptable.'));

    // Pages using drupal_goto.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-redirect', drupal_anonymous_user());
    $this
      ->assertAuthcacheCanceled(t('Redirecting to @destination', array(
      '@destination' => url('user', array(
        'absolute' => TRUE,
      )),
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-redirect', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Redirecting to @destination', array(
      '@destination' => url('user', array(
        'absolute' => TRUE,
      )),
    )));

    // Status messages on page.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_status_message', TRUE);
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheCanceled(t('Status message on page'));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_status_message', TRUE);
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Status message on page'));

    // Content-types.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $allowed_mimetypes = preg_split('/(\\r\\n?|\\n)/', AUTHCACHE_MIMETYPE_DEFAULT, -1, PREG_SPLIT_NO_EMPTY);
    foreach ($allowed_mimetypes as $mime_type) {
      $this
        ->resetTestVariables();
      $save_stub = $this->stubbackend
        ->hook('authcache_backend_cache_save');
      variable_set('authcache_test_content_type', $mime_type);
      $this
        ->authcacheGet('authcache-test-content-type', drupal_anonymous_user());
      $this
        ->assertAuthcacheNotCanceled();
      $this
        ->assertAuthcacheNotExcluded();
      $this
        ->assertStub($save_stub, HookStub::any());
      $this
        ->resetTestVariables();
      variable_set('authcache_test_content_type', $mime_type);
      $save_stub = $this->stubbackend
        ->hook('authcache_backend_cache_save');
      $this
        ->authcacheGet('authcache-test-content-type', $this->plainUser);
      $this
        ->assertAuthcacheNotCanceled();
      $this
        ->assertAuthcacheNotExcluded();
      $this
        ->assertStub($save_stub, HookStub::any());
    }
    $mime_type = 'application/octet-stream';
    $this
      ->resetTestVariables();
    variable_set('authcache_test_content_type', $mime_type);
    $this
      ->authcacheGet('authcache-test-content-type', drupal_anonymous_user());
    $this
      ->assertAuthcacheCanceled(t('Only cache allowed HTTP content types (HTML, JS, etc)'));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_content_type', $mime_type);
    $this
      ->authcacheGet('authcache-test-content-type', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Only cache allowed HTTP content types (HTML, JS, etc)'));

    // Headers unexpectedly sent.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_headers_sent', array(
      'Content-Type' => 'text/html',
    ));
    $this
      ->authcacheGet('authcache-test-headers-sent', drupal_anonymous_user());
    $this
      ->assertAuthcacheCanceled(t('Private file transfers or headers were unexpectedly sent'));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_headers_sent', array(
      'Content-Type' => 'text/html',
    ));
    $this
      ->authcacheGet('authcache-test-headers-sent', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Private file transfers or headers were unexpectedly sent'));

    // Private file transfer.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $files = $this
      ->drupalGetTestFiles('html');
    $uri = reset($files)->uri;
    $headers = array(
      'Content-Type' => 'text/html',
    );
    $params = array(
      $uri,
      $headers,
    );
    $this
      ->resetTestVariables();
    variable_set('authcache_test_file_transfer', $params);
    $this
      ->authcacheGet('authcache-test-file-transfer', drupal_anonymous_user());
    $this
      ->assertAuthcacheCanceled(t('Private file transfers or headers were unexpectedly sent'));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_file_transfer', $params);
    $this
      ->authcacheGet('authcache-test-file-transfer', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Private file transfers or headers were unexpectedly sent'));

    // Redirect using location header.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-redirect-location', drupal_anonymous_user());
    $this
      ->assertAuthcacheCanceled(t('Location header detected'));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-redirect-location', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Location header detected'));

    // hook_exit called during bootstrap.
    $this
      ->resetTestVariables();
    variable_set('authcache_test_early_exit', TRUE);
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertResponse(200);

    // Changed Vary header.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-change-vary', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('The Vary header was modified during the request'));

    // FIXME: Figure out how to properly test for thrown errors from
    // simpletest.
  }

  /**
   * Test custom page caching rules (variable: authcache_pagecaching).
   */
  public function testCustomPagecaching() {

    // Page caching rules empty.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_pagecaching' => array(),
    ));

    // Never allow caching when no page caching rules are configured.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheExcluded();
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);

    // Page caching rules: Allow all roles on default page set.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_pagecaching' => array(
        array(
          'option' => 0,
          'pages' => AUTHCACHE_NOCACHE_DEFAULT,
          'noadmin' => 1,
          'roles' => array(
            'custom' => TRUE,
            'roles' => drupal_map_assoc(array_keys(user_roles())),
          ),
        ),
      ),
    ));

    // Allow page caching for all except superuser.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Pages under /cart are excluded by the default rule-set, ensure that this
    // is respected.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->webUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->adminUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Deny page caching on admin pages.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('admin', $this->adminUser);
    $this
      ->assertAuthcacheExcluded(t('Not caching admin pages (by page ruleset #@number)', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('admin', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Page caching rules: Allow all roles on default page set, including admin
    // pages.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_pagecaching' => array(
        array(
          'option' => 0,
          'pages' => AUTHCACHE_NOCACHE_DEFAULT,
          'noadmin' => 0,
          'roles' => array(
            'custom' => TRUE,
            'roles' => drupal_map_assoc(array_keys(user_roles())),
          ),
        ),
      ),
    ));

    // Allow page caching for all except superuser.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Pages under /cart are excluded by the default rule-set, ensure that this
    // is respected.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->webUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->adminUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Deny page caching on admin pages.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('admin/config', $this->adminUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('admin/config', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Page caching rules: Simple blacklist.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_pagecaching' => array(
        array(
          'option' => 0,
          'pages' => 'authcache-test-page-two',
          'noadmin' => 1,
          'roles' => array(
            'custom' => TRUE,
            'roles' => drupal_map_assoc(array_keys(user_roles())),
          ),
        ),
      ),
    ));

    // Allow page caching for all except superuser.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Allow page caching for all except superuser.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->webUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->adminUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('cart/authcache-test-default-nocache', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Deny page caching on excluded pages.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->webUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->adminUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Page caching rules: Simple whitelist.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_pagecaching' => array(
        array(
          'option' => 1,
          'pages' => 'authcache-test-page-one',
          'noadmin' => 1,
          'roles' => array(
            'custom' => TRUE,
            'roles' => drupal_map_assoc(array_keys(user_roles())),
          ),
        ),
      ),
    ));

    // Allow page caching for all except superuser.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Deny page caching for pages not in the whitelist.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', drupal_anonymous_user());
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->webUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->adminUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 1,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Combination of page caching rules: Standard rules for anonymous and
    // authenticated users, more restrictions for privileged users.
    $privileged_roles = user_roles();
    unset($privileged_roles[DRUPAL_ANONYMOUS_RID]);
    unset($privileged_roles[DRUPAL_AUTHENTICATED_RID]);
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_pagecaching' => array(
        array(
          'option' => 0,
          'pages' => AUTHCACHE_NOCACHE_DEFAULT,
          'noadmin' => 1,
          'roles' => array(
            'custom' => TRUE,
            'roles' => array(
              DRUPAL_ANONYMOUS_RID => DRUPAL_ANONYMOUS_RID,
              DRUPAL_AUTHENTICATED_RID => DRUPAL_AUTHENTICATED_RID,
            ),
          ),
        ),
        array(
          'option' => 0,
          'pages' => AUTHCACHE_NOCACHE_DEFAULT . "\nauthcache-test-page-two\n",
          'noadmin' => 1,
          'roles' => array(
            'custom' => TRUE,
            'roles' => drupal_map_assoc(array_keys($privileged_roles)),
          ),
        ),
      ),
    ));

    // Allow page caching for all except superuser.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->webUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->adminUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Allow page caching for anonymous and authenticated roles but not for
    // special roles on second page.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-two', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-two', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->webUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 2,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->adminUser);
    $this
      ->assertAuthcacheExcluded(t('Caching disabled by path list of page ruleset #@number', array(
      '@number' => 2,
    )));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-two', $this->superUser);
    $this
      ->assertAuthcacheExcluded();

    // Page caching rules: PHP.
    module_enable(array(
      'php',
    ));
    $this
      ->assertTrue(module_exists('php'));
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_pagecaching' => array(
        array(
          'option' => 2,
          'pages' => 'return variable_get(\'authcache_test_pagecaching_allow\');',
          'noadmin' => 1,
          'roles' => array(
            'custom' => TRUE,
            'roles' => drupal_map_assoc(array_keys(user_roles())),
          ),
        ),
      ),
    ));

    // Allow page caching based on custom PHP code.
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    variable_set('authcache_test_pagecaching_allow', TRUE);
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());

    // Allow page caching based on custom PHP code.
    $this
      ->resetTestVariables();
    variable_set('authcache_test_pagecaching_allow', FALSE);
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $this
      ->assertAuthcacheNotExcluded(t('Caching disabled by PHP rule of page ruleset #@number', array(
      '@number' => 1,
    )));
  }

  /**
   * Test preclusion rules.
   *
   * Try to prevent that the next request from the same client is served from
   * the cache when one of the following conditions are met:
   * - after a POST request
   * - when messages are pending after rendering the page
   * - hook_authcache_preclude implementation triggered preclusion
   */
  public function testPreclusionRules() {
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_status_message_exit', TRUE);
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcachePrecluded(t('Pending messages'));
    $this
      ->resetTestVariables();
    $reason = $this
      ->randomName(16);
    variable_set('authcache_test_authcache_preclude', $reason);
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcachePrecluded(t('Trigger preclusion: @reason', array(
      '@reason' => $reason,
    )));
  }

  /**
   * Test rest of custom options (authcache_http200, authcache_noajax).
   */
  public function testOtherOptions() {

    // Ensure that requests are not cached when status != 200 and
    // authcache_http200 is set.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-403', $this->plainUser);
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_http200' => TRUE,
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-403', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('HTTP status 404/403s/etc'));

    // Ensure that Ajax requests are not cached when authcache_noajax is set.
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));
    $this
      ->resetTestVariables();
    $save_stub = $this->stubbackend
      ->hook('authcache_backend_cache_save');
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser, array(), array(
      'X-Requested-With: XMLHttpRequest',
    ));
    $this
      ->assertAuthcacheNotCanceled();
    $this
      ->assertAuthcacheNotExcluded();
    $this
      ->assertStub($save_stub, HookStub::any());
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
      'authcache_noajax' => TRUE,
    ));
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser, array(), array(
      'X-Requested-With: XMLHttpRequest',
    ));
    $this
      ->assertAuthcacheExcluded(t('Ajax request'));
  }

  /**
   * Test API for exclusion and cancelation.
   */
  public function testExcludeCancelHooks() {
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));

    // Test hook_authcache_request_exclude.
    $this
      ->resetTestVariables();
    $reason = $this
      ->randomName(16);
    variable_set('authcache_test_authcache_request_exclude', $reason);
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Trigger request exclude: @reason', array(
      '@reason' => $reason,
    )));

    // Test hook_authcache_account_exclude.
    $this
      ->resetTestVariables();
    $reason = $this
      ->randomName(16);
    variable_set('authcache_test_authcache_account_exclude', $reason);
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheExcluded(t('Trigger account exclude for uid=@uid: @reason', array(
      '@uid' => $this->plainUser->uid,
      '@reason' => $reason,
    )));

    // Test hook_authcache_cancel.
    $this
      ->resetTestVariables();
    $reason = $this
      ->randomName(16);
    variable_set('authcache_test_authcache_cancel', $reason);
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $this
      ->assertAuthcacheCanceled(t('Trigger cancelation: @reason', array(
      '@reason' => $reason,
    )));
  }

}

/**
 * Test key computation and handling
 */
class AuthcacheAnonymousKeysTestCase extends AuthcacheWebTestCase {
  protected $profile = 'testing';

  /**
   * {@inheritdoc}
   */
  public static function getInfo() {
    return array(
      'name' => 'Anonymous User Keys',
      'description' => 'Test key computation for anonymous users',
      'group' => 'Authcache',
    );
  }

  /**
   * Test default anonymous key.
   *
   * Verify that the default key for anonymous users is never returned for
   * authenticated users.
   */
  public function testDefaultAnonymousKey() {
    global $base_root;
    $this
      ->setupConfig();
    $this
      ->resetTestVariables();
    $this
      ->drupalLogout();
    $anon_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertEqual($base_root, $anon_key);
    $this
      ->drupalLogin($this->plainUser);
    $plain_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($base_root, $plain_user_key);
    $this
      ->drupalLogout();
    $this
      ->drupalLogin($this->webUser);
    $web_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($base_root, $web_user_key);
    $this
      ->drupalLogout();
    $this
      ->drupalLogin($this->adminUser);
    $admin_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($base_root, $admin_user_key);
    $this
      ->drupalLogout();
    $this
      ->drupalLogin($this->superUser);
    $super_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($base_root, $super_user_key);
    $this
      ->drupalLogout();
  }

  /**
   * Test custom key generator.
   */
  public function testCustomKeyGenerator() {
    $mock_key = $this
      ->randomName(16);
    $this
      ->setupConfig(array(
      'authcache_key_generator' => 'authcache_test_key_generator',
    ));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_key_generator_key', $mock_key);
    $this
      ->drupalLogout();
    $anon_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertEqual($mock_key, $anon_key);
    $this
      ->drupalLogin($this->plainUser);
    $plain_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($mock_key, $plain_user_key);
    $this
      ->drupalLogout();
    $this
      ->drupalLogin($this->webUser);
    $web_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($mock_key, $web_user_key);
    $this
      ->drupalLogout();
    $this
      ->drupalLogin($this->adminUser);
    $admin_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($mock_key, $admin_user_key);
    $this
      ->drupalLogout();
    $this
      ->drupalLogin($this->superUser);
    $super_user_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->assertNotEqual($mock_key, $super_user_key);
    $this
      ->drupalLogout();
  }

  /**
   * Cover authcache_is_default_key().
   */
  public function testIsDefaultKey() {
    global $base_root;

    // For anonymous users authcache key defaults to the base root.
    $result = authcache_is_default_key($base_root);
    $this
      ->assertTrue($result);

    // Expect non-default key for authenticated users.
    $test_key = $this
      ->randomName(8);
    $this
      ->assertNotEqual($base_root, $test_key);
    $result = authcache_is_default_key($test_key);
    $this
      ->assertFalse($result);

    // If authcache_key_generator was customized even anonymous users will not
    // have a default key.
    variable_set('authcache_key_generator', 'authcache_test_key_generator');
    $result = authcache_is_default_key($base_root);
    $this
      ->assertFalse($result);
  }

}

/**
 * Test key computation and handling.
 */
class AuthcacheAuthenticatedKeysTestCase extends AuthcacheWebTestCase {
  protected $profile = 'testing';

  /**
   * {@inheritdoc}
   */
  public static function getInfo() {
    return array(
      'name' => 'Authenticated Users Keys',
      'description' => 'Test key computation for authenticated users',
      'group' => 'Authcache',
    );
  }

  /**
   * Test same keys for same roles.
   *
   * Ensure that users with the same role combination also have the same.
   * authcache key.
   */
  public function testSameKeysForSameRoles() {
    $this
      ->setupConfig();
    foreach (array(
      $this->plainUser,
      $this->webUser,
      $this->adminUser,
    ) as $account) {
      $this
        ->drupalLogin($account);
      $account_key = $this
        ->drupalGet('authcache-test-get-key');
      $this
        ->drupalLogout();
      $new_account = $this
        ->drupalCreateUser();
      user_save($new_account, array(
        'roles' => $account->roles,
      ));
      $this
        ->drupalLogin($new_account);
      $new_account_key = $this
        ->drupalGet('authcache-test-get-key');
      $this
        ->drupalLogout();
      $this
        ->assertEqual($account_key, $new_account_key);
    }
  }

  /**
   * Test different keys for different roles.
   *
   * Ensure that users with the different role combination also have the.
   * different authcache key.
   */
  public function testDifferentKeysForDifferentRoles() {
    $this
      ->setupConfig();
    $this
      ->resetTestVariables();
    $collected_keys = array();
    $this
      ->drupalLogout();
    $collected_keys[] = $this
      ->drupalGet('authcache-test-get-key');
    $accounts = array(
      $this->plainUser,
      $this->webUser,
      $this->adminUser,
      $this->superUser,
    );
    foreach ($accounts as $account) {
      $this
        ->drupalLogin($account);
      $collected_keys[] = $this
        ->drupalGet('authcache-test-get-key');
      $this
        ->drupalLogout();
    }
    sort($collected_keys);
    $this
      ->assertEqual($collected_keys, array_unique($collected_keys));
  }

  /**
   * Verify that superuser key is unique.
   */
  public function testUniqSuperuserKey() {
    $this
      ->setupConfig();
    $this
      ->resetTestVariables();
    $this
      ->drupalLogin($this->superUser);
    $superuser_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->drupalLogout();
    $new_account = $this
      ->drupalCreateUser();
    user_save($new_account, array(
      'roles' => $this->superUser->roles,
    ));
    $this
      ->drupalLogin($new_account);
    $new_account_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->drupalLogout();
    $this
      ->assertNotEqual($superuser_key, $new_account_key);
  }

  /**
   * Test custom key properties.
   *
   * Verify that custom properties provided via hook_authcache_key_properties
   * are considered.
   */
  public function testHookAuthcacheKeyProperties() {
    $this
      ->setupConfig();
    $this
      ->resetTestVariables();
    $this
      ->drupalLogin($this->plainUser);
    $original_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->drupalLogout();
    $propname = $this
      ->randomName(4);
    $propvalue = $this
      ->randomName(8);
    variable_set('authcache_test_authcache_key_properties', array(
      $propname => $propvalue,
    ));
    $this
      ->drupalLogin($this->plainUser);
    $new_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->drupalLogout();
    $this
      ->assertNotEqual($original_key, $new_key);
  }

  /**
   * Test key abbreviation (variable authcache_key_abbrev).
   */
  public function testKeyAbbreviation() {

    // No abbreviation.
    $this
      ->setupConfig(array(
      'authcache_key_abbrev' => FALSE,
    ));
    $this
      ->resetTestVariables();
    $this
      ->drupalLogin($this->plainUser);
    $noabbrev_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->drupalLogout();
    $this
      ->assertEqual(40, strlen($noabbrev_key));

    // Standard abbreviation (7 digits).
    $this
      ->setupConfig();
    $this
      ->resetTestVariables();
    $this
      ->drupalLogin($this->plainUser);
    $default_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->drupalLogout();
    $this
      ->assertEqual(7, strlen($default_key));

    // Custom abbreviation (4 digits).
    $this
      ->setupConfig(array(
      'authcache_key_abbrev' => 4,
    ));
    $this
      ->resetTestVariables();
    $this
      ->drupalLogin($this->plainUser);
    $short_key = $this
      ->drupalGet('authcache-test-get-key');
    $this
      ->drupalLogout();
    $this
      ->assertEqual(4, strlen($short_key));

    // Ensure that stdandard and custom abbreviated keys are substrings of the
    // long one.
    $this
      ->assertIdentical(strpos($noabbrev_key, $default_key), 0);
    $this
      ->assertIdentical(strpos($noabbrev_key, $short_key), 0);
  }

}

/**
 * Test API for suspecting and testing render-elements for cacheability.
 */
class AuthcacheElementCacheabilityAPITestCase extends AuthcacheWebTestCase {
  protected $profile = 'testing';

  /**
   * {@inheritdoc}
   */
  public static function getInfo() {
    return array(
      'name' => 'Element Cacheability API',
      'description' => 'Test API for suspecting and testing render-elements for cacheability.',
      'group' => 'Authcache',
    );
  }

  /**
   * Test cacheability API for render elements.
   */
  public function testElementCacheabilityAPI() {

    // Ensure that unsuspicous elements are left alone.
    $this
      ->resetTestVariables();
    $element = array(
      '#markup' => t('Does not contain personalized content'),
    );
    variable_set('authcache_test_record', TRUE);
    $output = render($element);
    $this
      ->assertEqual(t('Does not contain personalized content'), $output);
    $this
      ->assertAuthcacheNotCanceled();

    // Ensure that rendering of empty suspected elements are left alone.
    $this
      ->resetTestVariables();
    $element = array(
      '#markup' => '',
    );
    authcache_element_suspect($element);
    variable_set('authcache_test_record', TRUE);
    $output = render($element);
    $this
      ->assertFalse($output);
    $this
      ->assertAuthcacheNotCanceled();

    // Ensure that rendering of suspected elements trigger cancelation with
    // generic message.
    $this
      ->resetTestVariables();
    $element = array(
      '#markup' => t('Some personalized content'),
    );
    authcache_element_suspect($element);
    variable_set('authcache_test_record', TRUE);
    $output = render($element);
    $this
      ->assertEqual(t('Some personalized content'), $output);
    $this
      ->assertAuthcacheCanceled(t('Non cacheable render element on page'));

    // Ensure that cancelation message can be customized.
    $this
      ->resetTestVariables();
    $element = array(
      '#markup' => t('Some personalized content'),
    );
    authcache_element_suspect($element, t('Customized cancelation message'));
    variable_set('authcache_test_record', TRUE);
    $output = render($element);
    $this
      ->assertEqual(t('Some personalized content'), $output);
    $this
      ->assertAuthcacheCanceled(t('Customized cancelation message'));

    // Ensure that rendering of empty suspected elements trigger cancelation if
    // strict mode is enabled.
    $this
      ->resetTestVariables();
    $element = array(
      '#markup' => '',
    );
    authcache_element_suspect($element, t('Customized cancelation message'), TRUE);
    variable_set('authcache_test_record', TRUE);
    $output = render($element);
    $this
      ->assertFalse($output);
    $this
      ->assertAuthcacheCanceled(t('Customized cancelation message'));

    // Ensure that value elements trigger cancelation.
    $this
      ->resetTestVariables();
    $element = array(
      '#type' => 'value',
      '#value' => 42,
    );
    authcache_element_suspect($element);
    variable_set('authcache_test_record', TRUE);
    $output = render($element);
    $this
      ->assertFalse($output);
    $this
      ->assertAuthcacheCanceled(t('Non cacheable render element on page'));

    // Ensure that suspicous elements can be marked as being cacheable.
    $this
      ->resetTestVariables();
    $element = array(
      '#markup' => t('Some personalized content'),
    );
    authcache_element_suspect($element);
    authcache_element_set_cacheable($element);
    variable_set('authcache_test_record', TRUE);
    $output = render($element);
    $this
      ->assertEqual(t('Some personalized content'), $output);
    $this
      ->assertAuthcacheNotCanceled();
  }

}

/**
 * Test cookie management.
 */
class AuthcacheCookiesTestCase extends AuthcacheWebTestCase {
  protected $profile = 'testing';

  /**
   * {@inheritdoc}
   */
  public static function getInfo() {
    return array(
      'name' => 'Cookie API',
      'description' => 'Test cookie management',
      'group' => 'Authcache',
    );
  }

  /**
   * Extract cookie records from a list of HTTP response headers.
   */
  public function extractSetCookies($headers) {
    $cookies = array();
    $attribute_names = array(
      'expires' => 'Expires',
      'max-age' => 'Max-Age',
      'domain' => 'Domain',
      'path' => 'Path',
      'secure' => 'Secure',
      'httponly' => 'HttpOnly',
      'samesite' => 'SameSite',
    );
    foreach ($headers as $header) {
      if (preg_match('/^Set-Cookie:\\s+(.*)\\s*$/', trim($header), $matches)) {
        $fields = preg_split('/\\s*;\\s/', $matches[1]);
        debug($header);
        $namepair = array_shift($fields);
        list($name, $value) = explode('=', $namepair, 2);
        $cookies[$name]['value'] = $value;
        foreach ($fields as $parampair) {
          $kv = explode('=', $parampair, 2);
          $attr_key = strtolower($kv[0]);
          $this
            ->assertTrue(isset($attribute_names[$attr_key]), t('Valid cookie attribute name @attr', array(
            '@attr' => $kv[0],
          )));
          $attr_name = $attribute_names[$attr_key];
          $attr_value = count($kv) === 2 ? $kv[1] : TRUE;
          $cookies[$name][$attr_name] = $attr_value;
        }
        debug($cookies);
      }
    }
    return $cookies;
  }

  /**
   * Verify that hook_authcache_cookie is called on every request.
   */
  public function testCookieHooks() {
    $this
      ->setupConfig(array(
      'authcache_roles' => drupal_map_assoc(array_keys(user_roles())),
    ));

    // Test for cacheable anonymous user on cacheable page.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    $cookie_uid = variable_get('authcache_test_authcache_cookie');
    $this
      ->assertEqual(0, $cookie_uid, t('hook_authcache_cookie called for anonymous user'));
    list($alter_cookies, $alter_cookie_uid) = variable_get('authcache_test_authcache_cookie_alter');
    $this
      ->assertEqual(0, $alter_cookie_uid, t('hook_authcache_cookie_alter called for anonymous user'));

    // Test for cacheable authenticated user on cacheable page.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->plainUser);
    $cookie_uid = variable_get('authcache_test_authcache_cookie');
    $this
      ->assertEqual($this->plainUser->uid, $cookie_uid, t('hook_authcache_cookie called for anonymous user'));
    list($alter_cookies, $alter_cookie_uid) = variable_get('authcache_test_authcache_cookie_alter');
    $this
      ->assertEqual($this->plainUser->uid, $alter_cookie_uid, t('hook_authcache_cookie_alter called for plain user'));

    // Ensure that this even works for superuser.
    $this
      ->resetTestVariables();
    $this
      ->authcacheGet('authcache-test-page-one', $this->superUser);
    $cookie_uid = variable_get('authcache_test_authcache_cookie');
    $this
      ->assertEqual($this->superUser->uid, $cookie_uid, t('hook_authcache_cookie called for anonymous user'));
    list($alter_cookies, $alter_cookie_uid) = variable_get('authcache_test_authcache_cookie_alter');
    $this
      ->assertEqual($this->superUser->uid, $alter_cookie_uid, t('hook_authcache_cookie_alter called for super user'));

    // Ensure that cookies set via hook_authcache_cookie show up in
    // hook_authcache_cookie_alter.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 3600,
        'path' => $cookie_path,
      ),
    ));
    $this
      ->authcacheGet('authcache-test-page-one', drupal_anonymous_user());
    list($alter_cookies, $alter_cookie_uid) = variable_get('authcache_test_authcache_cookie_alter');
    $this
      ->assertEqual($cookie_value, $alter_cookies[$cookie_name]['value']);
    $this
      ->assertEqual($cookie_path, $alter_cookies[$cookie_name]['path']);

    // Test whether cookies set using hook_authcache_cookie are present on the
    // response.
    //
    // With lifetime.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 3600,
        'path' => $cookie_path,
      ),
    ));
    $now = time();
    $this
      ->drupalGet('authcache-test-page-one');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], $cookie_value);
    $timestamp = strtotime($response_cookies[$cookie_name]['Expires']);
    $this
      ->assertTrue($timestamp >= $now - 3600 * 0.1);
    $this
      ->assertTrue($timestamp <= $now + 3600 * 1.1);
    $this
      ->assertEqual($response_cookies[$cookie_name]['Path'], $cookie_path);

    // No lifetime (delete when browser window closes).
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 0,
        'path' => $cookie_path,
      ),
    ));
    $this
      ->drupalGet('authcache-test-page-one');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], $cookie_value);
    $this
      ->assertTrue(!isset($response_cookies[$cookie_name]['Expires']));
    $this
      ->assertEqual($response_cookies[$cookie_name]['Path'], $cookie_path);

    // HTTP only flag.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
        'httponly' => TRUE,
      ),
    ));
    $this
      ->drupalGet('authcache-test-page-one');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($cookie_value, $response_cookies[$cookie_name]['value']);
    $this
      ->assertTrue($response_cookies[$cookie_name]['HttpOnly']);

    // Secure flag.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
        'secure' => TRUE,
      ),
    ));
    $this
      ->drupalGet('authcache-test-page-one');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($cookie_value, $response_cookies[$cookie_name]['value']);
    $this
      ->assertTrue($response_cookies[$cookie_name]['Secure']);

    // SameSite attribute.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
        'samesite' => 'Strict',
      ),
    ));
    $this
      ->drupalGet('authcache-test-page-one');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($cookie_value, $response_cookies[$cookie_name]['value']);
    $this
      ->assertEqual('Strict', $response_cookies[$cookie_name]['SameSite']);

    // Ensure that cookies can be injected using the alter-hook.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie_alter', array(
      $cookie_name,
      array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
      ),
    ));
    $this
      ->drupalGet('authcache-test-page-one');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], $cookie_value);
    $this
      ->assertEqual($response_cookies[$cookie_name]['Path'], $cookie_path);

    // Ensure that cookies can be deleted from the alter-hook.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
      ),
    ));
    variable_set('authcache_test_cookie_alter', array(
      $cookie_name,
      array(
        'present' => FALSE,
        'value' => $cookie_value,
        'path' => $cookie_path,
      ),
    ));
    $this
      ->drupalGet('authcache-test-page-one');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertFalse(isset($response_cookies[$cookie_name]));
  }

  /**
   * Tests for authcache_add_cookie function.
   */
  public function testCookieFunction() {

    // With lifetime.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 3600,
        'path' => $cookie_path,
      ),
    ));
    $now = time();
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], $cookie_value);
    $timestamp = strtotime($response_cookies[$cookie_name]['Expires']);
    $this
      ->assertTrue($timestamp >= $now - 3600 * 0.1);
    $this
      ->assertTrue($timestamp <= $now + 3600 * 1.1);
    $this
      ->assertEqual($response_cookies[$cookie_name]['Path'], $cookie_path);

    // No lifetime (delete when browser window closes).
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 0,
        'path' => $cookie_path,
      ),
    ));
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], $cookie_value);
    $this
      ->assertTrue(!isset($response_cookies[$cookie_name]['Expires']));
    $this
      ->assertEqual($response_cookies[$cookie_name]['Path'], $cookie_path);

    // HTTP only flag.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
        'httponly' => TRUE,
      ),
    ));
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($cookie_value, $response_cookies[$cookie_name]['value']);
    $this
      ->assertTrue($response_cookies[$cookie_name]['HttpOnly']);

    // Secure flag.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
        'secure' => TRUE,
      ),
    ));
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($cookie_value, $response_cookies[$cookie_name]['value']);
    $this
      ->assertTrue($response_cookies[$cookie_name]['Secure']);

    // Ensure that cookies can be deleted from the alter-hook.
    $this
      ->resetTestVariables();
    $cookie_name = $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    $cookie_path = $this
      ->randomName(8);
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'path' => $cookie_path,
      ),
    ));
    variable_set('authcache_test_cookie_alter', array(
      $cookie_name,
      array(
        'present' => FALSE,
        'value' => $cookie_value,
        'path' => $cookie_path,
      ),
    ));
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertFalse(isset($response_cookies[$cookie_name]));
  }

  /**
   * Ensure that cookies are set only if necessary on repeating requests.
   */
  public function testCookieOnSubsequentRequests() {

    // With lifetime.
    $cookie_name = 'Drupal.authcache_test.' . $this
      ->randomName(8);
    $cookie_value = $this
      ->randomName(16);
    url('', array(
      'prefix' => &$prefix,
    ));
    $cookie_path = (empty($prefix) ? '' : $prefix) . '/';
    $this
      ->resetTestVariables();
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 3600,
        'path' => $cookie_path,
      ),
    ));

    // On first request, we expect that the cookie is set.
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], $cookie_value);
    $this
      ->assertEqual($response_cookies[$cookie_name]['Path'], $cookie_path);
    $this
      ->resetTestVariables();
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 3600,
        'path' => $cookie_path,
      ),
    ));

    // On subsequent request, cookie should not be set again on response.
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertFalse(isset($response_cookies[$cookie_name]));
    $cookie_value = $this
      ->randomName(16);
    $this
      ->resetTestVariables();
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 3600,
        'path' => $cookie_path,
      ),
    ));

    // When the value changes, however cookie should be present again on
    // subsequent request.
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], $cookie_value);
    $this
      ->assertEqual($response_cookies[$cookie_name]['Path'], $cookie_path);
    $this
      ->resetTestVariables();
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => TRUE,
        'value' => $cookie_value,
        'lifetime' => 3600,
        'path' => $cookie_path,
      ),
    ));

    // On subsequent request, cookie should not be set again on response.
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertFalse(isset($response_cookies[$cookie_name]));
    $this
      ->resetTestVariables();
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => FALSE,
      ),
    ));

    // Cookies can be removed by setting the 'present' key to false on the
    // cookie record.
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertEqual($response_cookies[$cookie_name]['value'], 'deleted');
    $this
      ->resetTestVariables();
    variable_set('authcache_test_add_cookie', array(
      $cookie_name => array(
        'present' => FALSE,
      ),
    ));

    // On subsequent request, delete cookie header should not be set again.
    $this
      ->drupalGet('authcache-test-add-cookie');
    $response_cookies = $this
      ->extractSetCookies($this->headers);
    $this
      ->assertFalse(isset($response_cookies[$cookie_name]));
  }

}

Classes

Namesort descending Description
AuthcacheAnonymousKeysTestCase Test key computation and handling
AuthcacheAuthenticatedKeysTestCase Test key computation and handling.
AuthcacheCookiesTestCase Test cookie management.
AuthcacheElementCacheabilityAPITestCase Test API for suspecting and testing render-elements for cacheability.
AuthcachePolicyTestCase Test cache policy rules, i.e. exclusion and cancelation.
AuthcacheWebTestCase Helper class for module test cases.