You are here

EntityViewsDataTest.php in Zircon Profile 8

Same filename and directory in other branches
  1. 8.0 core/modules/views/tests/src/Unit/EntityViewsDataTest.php

File

core/modules/views/tests/src/Unit/EntityViewsDataTest.php
View source
<?php

/**
 * @file
 * Contains \Drupal\Tests\views\Unit\EntityViewsDataTest.
 */
namespace Drupal\Tests\views\Unit {
  use Drupal\Core\Config\Entity\ConfigEntityType;
  use Drupal\Core\Entity\ContentEntityType;
  use Drupal\Core\Entity\EntityType;
  use Drupal\Core\Entity\EntityTypeInterface;
  use Drupal\Core\Field\BaseFieldDefinition;
  use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
  use Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem;
  use Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem;
  use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
  use Drupal\Core\Field\Plugin\Field\FieldType\UriItem;
  use Drupal\Core\Field\Plugin\Field\FieldType\UuidItem;
  use Drupal\text\Plugin\Field\FieldType\TextLongItem;
  use Drupal\entity_test\Entity\EntityTest;
  use Drupal\entity_test\Entity\EntityTestMul;
  use Drupal\entity_test\Entity\EntityTestMulRev;
  use Drupal\Tests\UnitTestCase;
  use Drupal\views\EntityViewsData;
  use Symfony\Component\DependencyInjection\ContainerBuilder;

  /**
   * @coversDefaultClass \Drupal\views\EntityViewsData
   * @group Views
   */
  class EntityViewsDataTest extends UnitTestCase {

    /**
     * Entity info to use in this test.
     *
     * @var \Drupal\Core\Entity\EntityTypeInterface|\Drupal\Tests\views\Unit\TestEntityType
     */
    protected $baseEntityType;

    /**
     * The mocked entity storage.
     *
     * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage|\PHPUnit_Framework_MockObject_MockObject
     */
    protected $entityStorage;

    /**
     * The mocked entity manager.
     *
     * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
     */
    protected $entityManager;

    /**
     * The mocked module handler.
     *
     * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
     */
    protected $moduleHandler;

    /**
     * The mocked translation manager.
     *
     * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
     */
    protected $translationManager;

    /**
     * The tested entity views controller.
     *
     * @var \Drupal\Tests\views\Unit\TestEntityViewsData
     */
    protected $viewsData;

    /**
     * {@inheritdoc}
     */
    protected function setUp() {
      $this->entityStorage = $this
        ->getMockBuilder('Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage')
        ->disableOriginalConstructor()
        ->getMock();
      $this->entityManager = $this
        ->getMock('Drupal\\Core\\Entity\\EntityManagerInterface');
      $typed_data_manager = $this
        ->getMockBuilder('Drupal\\Core\\TypedData\\TypedDataManager')
        ->disableOriginalConstructor()
        ->getMock();
      $typed_data_manager
        ->expects($this
        ->any())
        ->method('createDataDefinition')
        ->willReturn($this
        ->getMock('Drupal\\Core\\TypedData\\DataDefinitionInterface'));
      $this->baseEntityType = new TestEntityType([
        'base_table' => 'entity_test',
        'id' => 'entity_test',
        'label' => 'Entity test',
        'entity_keys' => [
          'id' => 'id',
          'langcode' => 'langcode',
          'bundle' => 'type',
          'revision' => 'revision_id',
        ],
        'provider' => 'entity_test',
        'list_cache_contexts' => [
          'entity_test_list_cache_context',
        ],
      ]);
      $this->translationManager = $this
        ->getStringTranslationStub();
      $this->moduleHandler = $this
        ->getMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
      $this->viewsData = new TestEntityViewsData($this->baseEntityType, $this->entityStorage, $this->entityManager, $this->moduleHandler, $this->translationManager);
      $field_type_manager = $this
        ->getMockBuilder('Drupal\\Core\\Field\\FieldTypePluginManager')
        ->disableOriginalConstructor()
        ->getMock();
      $field_type_manager
        ->expects($this
        ->any())
        ->method('getDefaultStorageSettings')
        ->willReturn([]);
      $field_type_manager
        ->expects($this
        ->any())
        ->method('getDefaultFieldSettings')
        ->willReturn([]);
      $container = new ContainerBuilder();
      $container
        ->set('plugin.manager.field.field_type', $field_type_manager);
      $container
        ->set('entity.manager', $this->entityManager);
      $container
        ->set('typed_data_manager', $typed_data_manager);
      \Drupal::setContainer($container);
    }

    /**
     * Helper method to setup base fields.
     *
     * @param \Drupal\Core\Field\BaseFieldDefinition[] $base_fields
     *   The base fields which are adapted.
     *
     * @return \Drupal\Core\Field\BaseFieldDefinition[]
     *   The setup base fields.
     */
    protected function setupBaseFields(array $base_fields) {

      // Add a description field to the fields supplied by the EntityTest
      // classes. This example comes from the taxonomy Term entity.
      $base_fields['description'] = BaseFieldDefinition::create('text_long')
        ->setLabel(t('Description'))
        ->setDescription(t('A description of the term.'))
        ->setTranslatable(TRUE)
        ->setDisplayOptions('view', array(
        'label' => 'hidden',
        'type' => 'text_default',
        'weight' => 0,
      ))
        ->setDisplayConfigurable('view', TRUE)
        ->setDisplayOptions('form', array(
        'type' => 'text_textfield',
        'weight' => 0,
      ))
        ->setDisplayConfigurable('form', TRUE);

      // Add a URL field; this example is from the Comment entity.
      $base_fields['homepage'] = BaseFieldDefinition::create('uri')
        ->setLabel(t('Homepage'))
        ->setDescription(t("The comment author's home page address."))
        ->setTranslatable(TRUE)
        ->setSetting('max_length', 255);
      foreach ($base_fields as $name => $base_field) {
        $base_field
          ->setName($name);
      }
      return $base_fields;
    }

    /**
     * Tests base tables.
     */
    public function testBaseTables() {
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertEquals('entity_test', $data['entity_test']['table']['entity type']);
      $this
        ->assertEquals(FALSE, $data['entity_test']['table']['entity revision']);
      $this
        ->assertEquals('Entity test', $data['entity_test']['table']['group']);
      $this
        ->assertEquals('entity_test', $data['entity_test']['table']['provider']);
      $this
        ->assertEquals('id', $data['entity_test']['table']['base']['field']);
      $this
        ->assertEquals([
        'entity_test_list_cache_context',
      ], $data['entity_test']['table']['base']['cache_contexts']);
      $this
        ->assertEquals('Entity test', $data['entity_test']['table']['base']['title']);
      $this
        ->assertFalse(isset($data['entity_test']['table']['defaults']));
      $this
        ->assertFalse(isset($data['entity_test_mul_property_data']));
      $this
        ->assertFalse(isset($data['revision_table']));
      $this
        ->assertFalse(isset($data['revision_data_table']));
    }

    /**
     * Tests data_table support.
     */
    public function testDataTable() {
      $entity_type = $this->baseEntityType
        ->set('data_table', 'entity_test_mul_property_data')
        ->set('id', 'entity_test_mul')
        ->set('translatable', TRUE)
        ->setKey('label', 'label');
      $this->viewsData
        ->setEntityType($entity_type);

      // Tests the join definition between the base and the data table.
      $data = $this->viewsData
        ->getViewsData();
      $base_views_data = $data['entity_test'];

      // Ensure that the base table is set to the data table.
      $this
        ->assertEquals('id', $data['entity_test_mul_property_data']['table']['base']['field']);
      $this
        ->assertEquals('Entity test', $data['entity_test_mul_property_data']['table']['base']['title']);
      $this
        ->assertFalse(isset($data['entity_test']['table']['base']));
      $this
        ->assertEquals('entity_test_mul', $data['entity_test_mul_property_data']['table']['entity type']);
      $this
        ->assertEquals(FALSE, $data['entity_test_mul_property_data']['table']['entity revision']);
      $this
        ->assertEquals('Entity test', $data['entity_test_mul_property_data']['table']['group']);
      $this
        ->assertEquals('entity_test', $data['entity_test']['table']['provider']);
      $this
        ->assertEquals([
        'field' => 'label',
        'table' => 'entity_test_mul_property_data',
      ], $data['entity_test_mul_property_data']['table']['base']['defaults']);

      // Ensure the join information is set up properly.
      $this
        ->assertCount(1, $base_views_data['table']['join']);
      $this
        ->assertEquals([
        'entity_test_mul_property_data' => [
          'left_field' => 'id',
          'field' => 'id',
          'type' => 'INNER',
        ],
      ], $base_views_data['table']['join']);
      $this
        ->assertFalse(isset($data['revision_table']));
      $this
        ->assertFalse(isset($data['revision_data_table']));
    }

    /**
     * Tests revision table without data table support.
     */
    public function testRevisionTableWithoutDataTable() {
      $entity_type = $this->baseEntityType
        ->set('revision_table', 'entity_test_mulrev_revision')
        ->set('revision_data_table', NULL)
        ->set('id', 'entity_test_mulrev')
        ->setKey('revision', 'revision_id');
      $this->viewsData
        ->setEntityType($entity_type);
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertEquals('Entity test revisions', $data['entity_test_mulrev_revision']['table']['base']['title']);
      $this
        ->assertEquals('revision_id', $data['entity_test_mulrev_revision']['table']['base']['field']);
      $this
        ->assertEquals(FALSE, $data['entity_test']['table']['entity revision']);
      $this
        ->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_revision']['table']['entity type']);
      $this
        ->assertEquals(TRUE, $data['entity_test_mulrev_revision']['table']['entity revision']);
      $this
        ->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_revision']['table']['entity type']);
      $this
        ->assertEquals(TRUE, $data['entity_test_mulrev_revision']['table']['entity revision']);
      $this
        ->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']);
      $this
        ->assertEquals('entity_test', $data['entity_test']['table']['provider']);

      // Ensure the join information is set up properly.
      // Tests the join definition between the base and the revision table.
      $revision_data = $data['entity_test_mulrev_revision'];
      $this
        ->assertCount(1, $revision_data['table']['join']);
      $this
        ->assertEquals([
        'entity_test' => [
          'left_field' => 'revision_id',
          'field' => 'revision_id',
          'type' => 'INNER',
        ],
      ], $revision_data['table']['join']);
      $this
        ->assertFalse(isset($data['data_table']));
    }

    /**
     * Tests revision table with data table support.
     */
    public function testRevisionTableWithRevisionDataTableAndDataTable() {
      $entity_type = $this->baseEntityType
        ->set('data_table', 'entity_test_mul_property_data')
        ->set('revision_table', 'entity_test_mulrev_revision')
        ->set('revision_data_table', 'entity_test_mulrev_property_revision')
        ->set('id', 'entity_test_mulrev')
        ->set('translatable', TRUE)
        ->setKey('revision', 'revision_id');
      $this->viewsData
        ->setEntityType($entity_type);
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertEquals('Entity test revisions', $data['entity_test_mulrev_property_revision']['table']['base']['title']);
      $this
        ->assertEquals('revision_id', $data['entity_test_mulrev_property_revision']['table']['base']['field']);
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['table']['base']));
      $this
        ->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_property_revision']['table']['entity type']);
      $this
        ->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']);
      $this
        ->assertEquals('entity_test', $data['entity_test']['table']['provider']);

      // Ensure the join information is set up properly.
      // Tests the join definition between the base and the revision table.
      $revision_data = $data['entity_test_mulrev_property_revision'];
      $this
        ->assertCount(2, $revision_data['table']['join']);
      $this
        ->assertEquals([
        'entity_test_mul_property_data' => [
          'left_field' => 'revision_id',
          'field' => 'revision_id',
          'type' => 'INNER',
        ],
        'entity_test_mulrev_revision' => [
          'left_field' => 'revision_id',
          'field' => 'revision_id',
          'type' => 'INNER',
        ],
      ], $revision_data['table']['join']);
      $this
        ->assertFalse(isset($data['data_table']));
    }

    /**
     * Tests revision table with data table support.
     */
    public function testRevisionTableWithRevisionDataTable() {
      $entity_type = $this->baseEntityType
        ->set('revision_table', 'entity_test_mulrev_revision')
        ->set('revision_data_table', 'entity_test_mulrev_property_revision')
        ->set('id', 'entity_test_mulrev')
        ->set('translatable', TRUE)
        ->setKey('revision', 'revision_id');
      $this->viewsData
        ->setEntityType($entity_type);
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertEquals('Entity test revisions', $data['entity_test_mulrev_property_revision']['table']['base']['title']);
      $this
        ->assertEquals('revision_id', $data['entity_test_mulrev_property_revision']['table']['base']['field']);
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['table']['base']));
      $this
        ->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_property_revision']['table']['entity type']);
      $this
        ->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']);
      $this
        ->assertEquals('entity_test', $data['entity_test']['table']['provider']);

      // Ensure the join information is set up properly.
      // Tests the join definition between the base and the revision table.
      $revision_data = $data['entity_test_mulrev_property_revision'];
      $this
        ->assertCount(2, $revision_data['table']['join']);
      $this
        ->assertEquals([
        'entity_test_mulrev_field_data' => [
          'left_field' => 'revision_id',
          'field' => 'revision_id',
          'type' => 'INNER',
        ],
        'entity_test_mulrev_revision' => [
          'left_field' => 'revision_id',
          'field' => 'revision_id',
          'type' => 'INNER',
        ],
      ], $revision_data['table']['join']);
      $this
        ->assertFalse(isset($data['data_table']));
    }

    /**
     * Helper method to mock all store definitions.
     */
    protected function setupFieldStorageDefinition() {
      $id_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $id_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(IntegerItem::schema($id_field_storage_definition));
      $uuid_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $uuid_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(UuidItem::schema($uuid_field_storage_definition));
      $type_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $type_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(StringItem::schema($type_field_storage_definition));
      $langcode_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $langcode_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(LanguageItem::schema($langcode_field_storage_definition));
      $name_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $name_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(StringItem::schema($name_field_storage_definition));
      $description_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $description_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(TextLongItem::schema($description_field_storage_definition));
      $homepage_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $homepage_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(UriItem::schema($homepage_field_storage_definition));

      // Setup the user_id entity reference field.
      $this->entityManager
        ->expects($this
        ->any())
        ->method('getDefinition')
        ->willReturnMap([
        [
          'user',
          TRUE,
          static::userEntityInfo(),
        ],
      ]);
      $user_id_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $user_id_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSetting')
        ->with('target_type')
        ->willReturn('user');
      $user_id_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSettings')
        ->willReturn([
        'target_type' => 'user',
      ]);
      $user_id_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(EntityReferenceItem::schema($user_id_field_storage_definition));
      $revision_id_field_storage_definition = $this
        ->getMock('Drupal\\Core\\Field\\FieldStorageDefinitionInterface');
      $revision_id_field_storage_definition
        ->expects($this
        ->any())
        ->method('getSchema')
        ->willReturn(IntegerItem::schema($revision_id_field_storage_definition));
      $this->entityManager
        ->expects($this
        ->any())
        ->method('getFieldStorageDefinitions')
        ->willReturn([
        'id' => $id_field_storage_definition,
        'uuid' => $uuid_field_storage_definition,
        'type' => $type_field_storage_definition,
        'langcode' => $langcode_field_storage_definition,
        'name' => $name_field_storage_definition,
        'description' => $description_field_storage_definition,
        'homepage' => $homepage_field_storage_definition,
        'user_id' => $user_id_field_storage_definition,
        'revision_id' => $revision_id_field_storage_definition,
      ]);
    }

    /**
     * Tests fields on the base table.
     */
    public function testBaseTableFields() {
      $base_field_definitions = $this
        ->setupBaseFields(EntityTest::baseFieldDefinitions($this->baseEntityType));
      $user_base_field_definitions = [
        'uid' => BaseFieldDefinition::create('integer')
          ->setLabel(t('ID'))
          ->setDescription(t('The ID of the user entity.'))
          ->setReadOnly(TRUE)
          ->setSetting('unsigned', TRUE),
      ];
      $this->entityManager
        ->expects($this
        ->any())
        ->method('getBaseFieldDefinitions')
        ->will($this
        ->returnValueMap([
        [
          'user',
          $user_base_field_definitions,
        ],
        [
          'entity_test',
          $base_field_definitions,
        ],
      ]));

      // Setup the table mapping.
      $table_mapping = $this
        ->getMock('Drupal\\Core\\Entity\\Sql\\TableMappingInterface');
      $table_mapping
        ->expects($this
        ->any())
        ->method('getTableNames')
        ->willReturn([
        'entity_test',
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getColumnNames')
        ->willReturnMap([
        [
          'id',
          [
            'value' => 'id',
          ],
        ],
        [
          'uuid',
          [
            'value' => 'uuid',
          ],
        ],
        [
          'type',
          [
            'value' => 'type',
          ],
        ],
        [
          'langcode',
          [
            'value' => 'langcode',
          ],
        ],
        [
          'name',
          [
            'value' => 'name',
          ],
        ],
        [
          'description',
          [
            'value' => 'description__value',
            'format' => 'description__format',
          ],
        ],
        [
          'homepage',
          [
            'value' => 'homepage',
          ],
        ],
        [
          'user_id',
          [
            'target_id' => 'user_id',
          ],
        ],
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getFieldNames')
        ->willReturnMap([
        [
          'entity_test',
          [
            'id',
            'uuid',
            'type',
            'langcode',
            'name',
            'description',
            'homepage',
            'user_id',
          ],
        ],
      ]);
      $this->entityStorage
        ->expects($this
        ->once())
        ->method('getTableMapping')
        ->willReturn($table_mapping);
      $this
        ->setupFieldStorageDefinition();
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertNumericField($data['entity_test']['id']);
      $this
        ->assertField($data['entity_test']['id'], 'id');
      $this
        ->assertUuidField($data['entity_test']['uuid']);
      $this
        ->assertField($data['entity_test']['uuid'], 'uuid');
      $this
        ->assertStringField($data['entity_test']['type']);
      $this
        ->assertEquals('type', $data['entity_test']['type']['entity field']);
      $this
        ->assertLanguageField($data['entity_test']['langcode']);
      $this
        ->assertField($data['entity_test']['langcode'], 'langcode');
      $this
        ->assertEquals('Original language', $data['entity_test']['langcode']['title']);
      $this
        ->assertStringField($data['entity_test']['name']);
      $this
        ->assertField($data['entity_test']['name'], 'name');
      $this
        ->assertLongTextField($data['entity_test'], 'description');
      $this
        ->assertField($data['entity_test']['description__value'], 'description');
      $this
        ->assertField($data['entity_test']['description__format'], 'description');
      $this
        ->assertUriField($data['entity_test']['homepage']);
      $this
        ->assertField($data['entity_test']['homepage'], 'homepage');
      $this
        ->assertEntityReferenceField($data['entity_test']['user_id']);
      $this
        ->assertField($data['entity_test']['user_id'], 'user_id');
      $relationship = $data['entity_test']['user_id']['relationship'];
      $this
        ->assertEquals('users_field_data', $relationship['base']);
      $this
        ->assertEquals('uid', $relationship['base field']);
    }

    /**
     * Tests fields on the data table.
     */
    public function testDataTableFields() {
      $entity_type = $this->baseEntityType
        ->set('data_table', 'entity_test_mul_property_data')
        ->set('base_table', 'entity_test_mul')
        ->set('id', 'entity_test_mul')
        ->setKey('bundle', 'type');
      $base_field_definitions = $this
        ->setupBaseFields(EntityTestMul::baseFieldDefinitions($this->baseEntityType));
      $base_field_definitions['type'] = BaseFieldDefinition::create('entity_reference')
        ->setLabel('entity test type')
        ->setSetting('target_type', 'entity_test_bundle')
        ->setTranslatable(TRUE);
      $base_field_definitions = $this
        ->setupBaseFields($base_field_definitions);
      $user_base_field_definitions = [
        'uid' => BaseFieldDefinition::create('integer')
          ->setLabel(t('ID'))
          ->setDescription(t('The ID of the user entity.'))
          ->setReadOnly(TRUE)
          ->setSetting('unsigned', TRUE),
      ];
      $entity_test_type = new ConfigEntityType([
        'id' => 'entity_test_bundle',
      ]);
      $this->entityManager
        ->expects($this
        ->any())
        ->method('getBaseFieldDefinitions')
        ->will($this
        ->returnValueMap([
        [
          'user',
          $user_base_field_definitions,
        ],
        [
          'entity_test_mul',
          $base_field_definitions,
        ],
      ]));
      $this->viewsData
        ->setEntityType($entity_type);

      // Setup the table mapping.
      $table_mapping = $this
        ->getMock('Drupal\\Core\\Entity\\Sql\\TableMappingInterface');
      $table_mapping
        ->expects($this
        ->any())
        ->method('getTableNames')
        ->willReturn([
        'entity_test_mul',
        'entity_test_mul_property_data',
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getColumnNames')
        ->willReturnMap([
        [
          'id',
          [
            'value' => 'id',
          ],
        ],
        [
          'uuid',
          [
            'value' => 'uuid',
          ],
        ],
        [
          'type',
          [
            'value' => 'type',
          ],
        ],
        [
          'langcode',
          [
            'value' => 'langcode',
          ],
        ],
        [
          'name',
          [
            'value' => 'name',
          ],
        ],
        [
          'description',
          [
            'value' => 'description__value',
            'format' => 'description__format',
          ],
        ],
        [
          'homepage',
          [
            'value' => 'homepage',
          ],
        ],
        [
          'user_id',
          [
            'target_id' => 'user_id',
          ],
        ],
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getFieldNames')
        ->willReturnMap([
        [
          'entity_test_mul',
          [
            'uuid',
          ],
        ],
        [
          'entity_test_mul_property_data',
          [
            'id',
            'type',
            'langcode',
            'name',
            'description',
            'homepage',
            'user_id',
          ],
        ],
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getFieldTableName')
        ->willReturnCallback(function ($field) {
        if ($field == 'uuid') {
          return 'entity_test_mul';
        }
        return 'entity_test_mul_property_data';
      });
      $this->entityStorage
        ->expects($this
        ->once())
        ->method('getTableMapping')
        ->willReturn($table_mapping);
      $this
        ->setupFieldStorageDefinition();
      $user_entity_type = static::userEntityInfo();
      $this->entityManager
        ->expects($this
        ->any())
        ->method('getDefinition')
        ->will($this
        ->returnValueMap([
        [
          'user',
          TRUE,
          $user_entity_type,
        ],
        [
          'entity_test_bundle',
          TRUE,
          $entity_test_type,
        ],
      ]));
      $data = $this->viewsData
        ->getViewsData();

      // Check the base fields.
      $this
        ->assertFalse(isset($data['entity_test_mul']['id']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['type']));
      $this
        ->assertUuidField($data['entity_test_mul']['uuid']);
      $this
        ->assertField($data['entity_test_mul']['uuid'], 'uuid');
      $this
        ->assertFalse(isset($data['entity_test_mul']['type']['relationship']));

      // Also ensure that field_data only fields don't appear on the base table.
      $this
        ->assertFalse(isset($data['entity_test_mul']['name']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['description']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['description__value']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['description__format']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['user_id']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['homepage']));

      // Check the data fields.
      $this
        ->assertNumericField($data['entity_test_mul_property_data']['id']);
      $this
        ->assertField($data['entity_test_mul_property_data']['id'], 'id');
      $this
        ->assertBundleField($data['entity_test_mul_property_data']['type']);
      $this
        ->assertField($data['entity_test_mul_property_data']['type'], 'type');
      $this
        ->assertLanguageField($data['entity_test_mul_property_data']['langcode']);
      $this
        ->assertField($data['entity_test_mul_property_data']['langcode'], 'langcode');
      $this
        ->assertEquals('Translation language', $data['entity_test_mul_property_data']['langcode']['title']);
      $this
        ->assertStringField($data['entity_test_mul_property_data']['name']);
      $this
        ->assertField($data['entity_test_mul_property_data']['name'], 'name');
      $this
        ->assertLongTextField($data['entity_test_mul_property_data'], 'description');
      $this
        ->assertField($data['entity_test_mul_property_data']['description__value'], 'description');
      $this
        ->assertField($data['entity_test_mul_property_data']['description__format'], 'description');
      $this
        ->assertUriField($data['entity_test_mul_property_data']['homepage']);
      $this
        ->assertField($data['entity_test_mul_property_data']['homepage'], 'homepage');
      $this
        ->assertEntityReferenceField($data['entity_test_mul_property_data']['user_id']);
      $this
        ->assertField($data['entity_test_mul_property_data']['user_id'], 'user_id');
      $relationship = $data['entity_test_mul_property_data']['user_id']['relationship'];
      $this
        ->assertEquals('users_field_data', $relationship['base']);
      $this
        ->assertEquals('uid', $relationship['base field']);
    }

    /**
     * Tests fields on the revision table.
     */
    public function testRevisionTableFields() {
      $entity_type = $this->baseEntityType
        ->set('base_table', 'entity_test_mulrev')
        ->set('revision_table', 'entity_test_mulrev_revision')
        ->set('data_table', 'entity_test_mulrev_property_data')
        ->set('revision_data_table', 'entity_test_mulrev_property_revision')
        ->set('id', 'entity_test_mulrev')
        ->set('translatable', TRUE);
      $base_field_definitions = $this
        ->setupBaseFields(EntityTestMulRev::baseFieldDefinitions($this->baseEntityType));
      $user_base_field_definitions = [
        'uid' => BaseFieldDefinition::create('integer')
          ->setLabel(t('ID'))
          ->setDescription(t('The ID of the user entity.'))
          ->setReadOnly(TRUE)
          ->setSetting('unsigned', TRUE),
      ];
      $this->entityManager
        ->expects($this
        ->any())
        ->method('getBaseFieldDefinitions')
        ->will($this
        ->returnValueMap([
        [
          'user',
          $user_base_field_definitions,
        ],
        [
          'entity_test_mulrev',
          $base_field_definitions,
        ],
      ]));
      $this->viewsData
        ->setEntityType($entity_type);

      // Setup the table mapping.
      $table_mapping = $this
        ->getMock('Drupal\\Core\\Entity\\Sql\\TableMappingInterface');
      $table_mapping
        ->expects($this
        ->any())
        ->method('getTableNames')
        ->willReturn([
        'entity_test_mulrev',
        'entity_test_mulrev_revision',
        'entity_test_mulrev_property_data',
        'entity_test_mulrev_property_revision',
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getColumnNames')
        ->willReturnMap([
        [
          'id',
          [
            'value' => 'id',
          ],
        ],
        [
          'uuid',
          [
            'value' => 'uuid',
          ],
        ],
        [
          'type',
          [
            'value' => 'type',
          ],
        ],
        [
          'langcode',
          [
            'value' => 'langcode',
          ],
        ],
        [
          'name',
          [
            'value' => 'name',
          ],
        ],
        [
          'description',
          [
            'value' => 'description__value',
            'format' => 'description__format',
          ],
        ],
        [
          'homepage',
          [
            'value' => 'homepage',
          ],
        ],
        [
          'user_id',
          [
            'target_id' => 'user_id',
          ],
        ],
        [
          'revision_id',
          [
            'value' => 'id',
          ],
        ],
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getFieldNames')
        ->willReturnMap([
        [
          'entity_test_mulrev',
          [
            'id',
            'revision_id',
            'uuid',
            'type',
          ],
        ],
        [
          'entity_test_mulrev_revision',
          [
            'id',
            'revision_id',
            'langcode',
          ],
        ],
        [
          'entity_test_mulrev_property_data',
          [
            'id',
            'revision_id',
            'langcode',
            'name',
            'description',
            'homepage',
            'user_id',
          ],
        ],
        [
          'entity_test_mulrev_property_revision',
          [
            'id',
            'revision_id',
            'langcode',
            'name',
            'description',
            'homepage',
            'user_id',
          ],
        ],
      ]);
      $table_mapping
        ->expects($this
        ->any())
        ->method('getFieldTableName')
        ->willReturnCallback(function ($field) {
        if ($field == 'uuid') {
          return 'entity_test_mulrev';
        }
        return 'entity_test_mulrev_property_data';
      });
      $this->entityStorage
        ->expects($this
        ->once())
        ->method('getTableMapping')
        ->willReturn($table_mapping);
      $this
        ->setupFieldStorageDefinition();
      $data = $this->viewsData
        ->getViewsData();

      // Check the base fields.
      $this
        ->assertFalse(isset($data['entity_test_mulrev']['id']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev']['type']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev']['revision_id']));
      $this
        ->assertUuidField($data['entity_test_mulrev']['uuid']);
      $this
        ->assertField($data['entity_test_mulrev']['uuid'], 'uuid');

      // Also ensure that field_data only fields don't appear on the base table.
      $this
        ->assertFalse(isset($data['entity_test_mulrev']['name']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['description']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['description__value']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['description__format']));
      $this
        ->assertFalse(isset($data['entity_test_mul']['homepage']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev']['langcode']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev']['user_id']));

      // Check the revision fields. The revision ID should only appear in the data
      // table.
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['revision_id']));

      // Also ensure that field_data only fields don't appear on the revision table.
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['id']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['name']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['description']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['description__value']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['description__format']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['homepage']));
      $this
        ->assertFalse(isset($data['entity_test_mulrev_revision']['user_id']));

      // Check the data fields.
      $this
        ->assertNumericField($data['entity_test_mulrev_property_data']['id']);
      $this
        ->assertField($data['entity_test_mulrev_property_data']['id'], 'id');
      $this
        ->assertNumericField($data['entity_test_mulrev_property_data']['revision_id']);
      $this
        ->assertField($data['entity_test_mulrev_property_data']['revision_id'], 'revision_id');
      $this
        ->assertLanguageField($data['entity_test_mulrev_property_data']['langcode']);
      $this
        ->assertField($data['entity_test_mulrev_property_data']['langcode'], 'langcode');
      $this
        ->assertStringField($data['entity_test_mulrev_property_data']['name']);
      $this
        ->assertField($data['entity_test_mulrev_property_data']['name'], 'name');
      $this
        ->assertLongTextField($data['entity_test_mulrev_property_data'], 'description');
      $this
        ->assertField($data['entity_test_mulrev_property_data']['description__value'], 'description');
      $this
        ->assertField($data['entity_test_mulrev_property_data']['description__format'], 'description');
      $this
        ->assertUriField($data['entity_test_mulrev_property_data']['homepage']);
      $this
        ->assertField($data['entity_test_mulrev_property_data']['homepage'], 'homepage');
      $this
        ->assertEntityReferenceField($data['entity_test_mulrev_property_data']['user_id']);
      $this
        ->assertField($data['entity_test_mulrev_property_data']['user_id'], 'user_id');
      $relationship = $data['entity_test_mulrev_property_data']['user_id']['relationship'];
      $this
        ->assertEquals('users_field_data', $relationship['base']);
      $this
        ->assertEquals('uid', $relationship['base field']);

      // Check the property data fields.
      $this
        ->assertNumericField($data['entity_test_mulrev_property_revision']['id']);
      $this
        ->assertField($data['entity_test_mulrev_property_revision']['id'], 'id');
      $this
        ->assertLanguageField($data['entity_test_mulrev_property_revision']['langcode']);
      $this
        ->assertField($data['entity_test_mulrev_property_revision']['langcode'], 'langcode');
      $this
        ->assertEquals('Translation language', $data['entity_test_mulrev_property_revision']['langcode']['title']);
      $this
        ->assertStringField($data['entity_test_mulrev_property_revision']['name']);
      $this
        ->assertField($data['entity_test_mulrev_property_revision']['name'], 'name');
      $this
        ->assertLongTextField($data['entity_test_mulrev_property_revision'], 'description');
      $this
        ->assertField($data['entity_test_mulrev_property_revision']['description__value'], 'description');
      $this
        ->assertField($data['entity_test_mulrev_property_revision']['description__format'], 'description');
      $this
        ->assertUriField($data['entity_test_mulrev_property_revision']['homepage']);
      $this
        ->assertField($data['entity_test_mulrev_property_revision']['homepage'], 'homepage');
      $this
        ->assertEntityReferenceField($data['entity_test_mulrev_property_revision']['user_id']);
      $this
        ->assertField($data['entity_test_mulrev_property_revision']['user_id'], 'user_id');
      $relationship = $data['entity_test_mulrev_property_revision']['user_id']['relationship'];
      $this
        ->assertEquals('users_field_data', $relationship['base']);
      $this
        ->assertEquals('uid', $relationship['base field']);
    }

    /**
     * Tests generic stuff per field.
     *
     * @param array $data
     *   The views data to check.
     * @param string $field_name
     *   The entity field name.
     */
    protected function assertField($data, $field_name) {
      $this
        ->assertEquals($field_name, $data['entity field']);
    }

    /**
     * Tests add link types.
     */
    public function testEntityLinks() {
      $this->baseEntityType
        ->setLinkTemplate('canonical', '/entity_test/{entity_test}');
      $this->baseEntityType
        ->setLinkTemplate('edit-form', '/entity_test/{entity_test}/edit');
      $this->baseEntityType
        ->setLinkTemplate('delete-form', '/entity_test/{entity_test}/delete');
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertEquals('entity_link', $data['entity_test']['view_entity_test']['field']['id']);
      $this
        ->assertEquals('entity_link_edit', $data['entity_test']['edit_entity_test']['field']['id']);
      $this
        ->assertEquals('entity_link_delete', $data['entity_test']['delete_entity_test']['field']['id']);
    }

    /**
     * Tests additional edit links.
     */
    public function testEntityLinksJustEditForm() {
      $this->baseEntityType
        ->setLinkTemplate('edit-form', '/entity_test/{entity_test}/edit');
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertFalse(isset($data['entity_test']['view_entity_test']));
      $this
        ->assertFalse(isset($data['entity_test']['delete_entity_test']));
      $this
        ->assertEquals('entity_link_edit', $data['entity_test']['edit_entity_test']['field']['id']);
    }

    /**
     * @covers ::getViewsData
     */
    public function testGetViewsDataWithoutEntityOperations() {

      // Make sure there is no list builder. The API does not document is
      // supports resetting entity handlers, so this might break in the future.
      $this->baseEntityType
        ->setListBuilderClass(NULL);
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertArrayNotHasKey('operations', $data[$this->baseEntityType
        ->getBaseTable()]);
    }

    /**
     * @covers ::getViewsData
     */
    public function testGetViewsDataWithEntityOperations() {
      $this->baseEntityType
        ->setListBuilderClass('\\Drupal\\Core\\Entity\\EntityListBuilder');
      $data = $this->viewsData
        ->getViewsData();
      $this
        ->assertSame('entity_operations', $data[$this->baseEntityType
        ->getBaseTable()]['operations']['field']['id']);
    }

    /**
     * Tests views data for a string field.
     *
     * @param $data
     *   The views data to check.
     */
    protected function assertStringField($data) {
      $this
        ->assertEquals('field', $data['field']['id']);
      $this
        ->assertEquals('string', $data['filter']['id']);
      $this
        ->assertEquals('string', $data['argument']['id']);
      $this
        ->assertEquals('standard', $data['sort']['id']);
    }

    /**
     * Tests views data for a URI field.
     *
     * @param $data
     *   The views data to check.
     */
    protected function assertUriField($data) {
      $this
        ->assertEquals('field', $data['field']['id']);
      $this
        ->assertEquals('string', $data['field']['default_formatter']);
      $this
        ->assertEquals('string', $data['filter']['id']);
      $this
        ->assertEquals('string', $data['argument']['id']);
      $this
        ->assertEquals('standard', $data['sort']['id']);
    }

    /**
     * Tests views data for a long text field.
     *
     * @param $data
     *   The views data for the table this field is in.
     * @param $field_name
     *   The name of the field being checked.
     */
    protected function assertLongTextField($data, $field_name) {
      $value_field = $data[$field_name . '__value'];
      $this
        ->assertEquals('field', $value_field['field']['id']);
      $this
        ->assertEquals($field_name . '__format', $value_field['field']['format']);
      $this
        ->assertEquals('string', $value_field['filter']['id']);
      $this
        ->assertEquals('string', $value_field['argument']['id']);
      $this
        ->assertEquals('standard', $value_field['sort']['id']);
      $this
        ->assertStringField($data[$field_name . '__format']);
    }

    /**
     * Tests views data for a UUID field.
     *
     * @param array $data
     *   The views data to check.
     */
    protected function assertUuidField($data) {

      // @todo Can we provide additional support for UUIDs in views?
      $this
        ->assertEquals('field', $data['field']['id']);
      $this
        ->assertFalse($data['field']['click sortable']);
      $this
        ->assertEquals('string', $data['filter']['id']);
      $this
        ->assertEquals('string', $data['argument']['id']);
      $this
        ->assertEquals('standard', $data['sort']['id']);
    }

    /**
     * Tests views data for a numeric field.
     *
     * @param array $data
     *   The views data to check.
     */
    protected function assertNumericField($data) {
      $this
        ->assertEquals('field', $data['field']['id']);
      $this
        ->assertEquals('numeric', $data['filter']['id']);
      $this
        ->assertEquals('numeric', $data['argument']['id']);
      $this
        ->assertEquals('standard', $data['sort']['id']);
    }

    /**
     * Tests views data for a language field.
     *
     * @param array $data
     *   The views data to check.
     */
    protected function assertLanguageField($data) {
      $this
        ->assertEquals('field', $data['field']['id']);
      $this
        ->assertEquals('language', $data['filter']['id']);
      $this
        ->assertEquals('language', $data['argument']['id']);
      $this
        ->assertEquals('standard', $data['sort']['id']);
    }

    /**
     * Tests views data for a entity reference field.
     */
    protected function assertEntityReferenceField($data) {
      $this
        ->assertEquals('field', $data['field']['id']);
      $this
        ->assertEquals('numeric', $data['filter']['id']);
      $this
        ->assertEquals('numeric', $data['argument']['id']);
      $this
        ->assertEquals('standard', $data['sort']['id']);
    }

    /**
     * Tests views data for a bundle field.
     */
    protected function assertBundleField($data) {
      $this
        ->assertEquals('field', $data['field']['id']);
      $this
        ->assertEquals('bundle', $data['filter']['id']);
      $this
        ->assertEquals('string', $data['argument']['id']);
      $this
        ->assertEquals('standard', $data['sort']['id']);
    }

    /**
     * Returns entity info for the user entity.
     *
     * @return array
     */
    protected static function userEntityInfo() {
      return new ContentEntityType([
        'id' => 'user',
        'class' => 'Drupal\\user\\Entity\\User',
        'label' => 'User',
        'base_table' => 'users',
        'data_table' => 'users_field_data',
        'entity_keys' => [
          'id' => 'uid',
          'uuid' => 'uuid',
        ],
      ]);
    }

  }
  class TestEntityViewsData extends EntityViewsData {
    public function setEntityType(EntityTypeInterface $entity_type) {
      $this->entityType = $entity_type;
    }

  }
  class TestEntityType extends EntityType {

    /**
     * Sets a specific entity key.
     *
     * @param string $key
     *   The name of the entity key.
     * @param string $value
     *   The new value of the key.
     *
     * @return $this
     */
    public function setKey($key, $value) {
      $this->entity_keys[$key] = $value;
      return $this;
    }

  }
}
namespace {
  if (!function_exists('t')) {
    function t($string, array $args = []) {
      return strtr($string, $args);
    }
  }
}

Classes