Update to Drupal 8.2.0. For more information, see https://www.drupal.org/project/drupal/releases/8.2.0

This commit is contained in:
Pantheon Automation 2016-10-06 15:16:20 -07:00 committed by Greg Anderson
parent 2f563ab520
commit f1c8716f57
1732 changed files with 52334 additions and 11780 deletions

View file

@ -0,0 +1,9 @@
langcode: en
status: true
name: High Water import node
type: high_water_import_node
description: ''
help: ''
new_revision: false
preview_mode: 1
display_submitted: true

View file

@ -0,0 +1,7 @@
type: module
name: Migrate SQL Source test
description: 'Provides a database table and records for SQL import testing.'
package: Testing
core: 8.x
dependencies:
- migrate

View file

@ -0,0 +1,16 @@
id: high_water_test
label: High water test.
source:
plugin: high_water_test
high_water_property:
name: changed
destination:
plugin: entity:node
migration_tags:
test: test
process:
changed: changed
title: title
type:
plugin: default_value
default_value: high_water_import_node

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\migrate_sql_test\Plugin\migrate\source;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
/**
* Source plugin for migration high water tests.
*
* @MigrateSource(
* id = "high_water_test"
* )
*/
class HighWaterTest extends SqlBase {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this
->select('high_water_node', 'm')
->fields('m', array_keys($this->fields()));
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
$fields = [
'id' => $this->t('Id'),
'title' => $this->t('Title'),
'changed' => $this->t('Changed'),
];
return $fields;
}
/**
* {@inheritdoc}
*/
public function getIds() {
return [
'id' => [
'type' => 'integer',
],
];
}
}

View file

@ -0,0 +1,177 @@
<?php
namespace Drupal\Tests\migrate\Kernel;
/**
* Tests migration high water property.
*
* @group migrate
*/
class HighWaterTest extends MigrateTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'system',
'user',
'node',
'migrate',
'migrate_sql_test',
'field',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create source test table.
$this->sourceDatabase->schema()->createTable('high_water_node', [
'fields' => [
'id' => [
'description' => 'Serial',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
],
'changed' => [
'description' => 'Highwater',
'type' => 'int',
'unsigned' => TRUE,
],
'title' => [
'description' => 'Title',
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => '',
],
],
'primary key' => [
'id',
],
'description' => 'Contains nodes to import',
]);
// Add 3 items to source table.
$this->sourceDatabase->insert('high_water_node')
->fields([
'title',
'changed',
])
->values([
'title' => 'Item 1',
'changed' => 1,
])
->values([
'title' => 'Item 2',
'changed' => 2,
])
->values([
'title' => 'Item 3',
'changed' => 3,
])
->execute();
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installSchema('node', 'node_access');
$this->executeMigration('high_water_test');
}
/**
* Tests high water property of SqlBase.
*/
public function testHighWater() {
// Assert all of the nodes have been imported.
$this->assertNodeExists('Item 1');
$this->assertNodeExists('Item 2');
$this->assertNodeExists('Item 3');
// Update Item 1 setting its high_water_property to value that is below
// current high water mark.
$this->sourceDatabase->update('high_water_node')
->fields([
'title' => 'Item 1 updated',
'changed' => 2,
])
->condition('title', 'Item 1')
->execute();
// Update Item 2 setting its high_water_property to value equal to
// current high water mark.
$this->sourceDatabase->update('high_water_node')
->fields([
'title' => 'Item 2 updated',
'changed' => 3,
])
->condition('title', 'Item 2')
->execute();
// Update Item 3 setting its high_water_property to value that is above
// current high water mark.
$this->sourceDatabase->update('high_water_node')
->fields([
'title' => 'Item 3 updated',
'changed' => 4,
])
->condition('title', 'Item 3')
->execute();
// Execute migration again.
$this->executeMigration('high_water_test');
// Item with lower highwater should not be updated.
$this->assertNodeExists('Item 1');
$this->assertNodeDoesNotExist('Item 1 updated');
// Item with equal highwater should not be updated.
$this->assertNodeExists('Item 2');
$this->assertNodeDoesNotExist('Item 2 updated');
// Item with greater highwater should be updated.
$this->assertNodeExists('Item 3 updated');
$this->assertNodeDoesNotExist('Item 3');
}
/**
* Assert that node with given title exists.
*
* @param string $title
* Title of the node.
*/
protected function assertNodeExists($title) {
self::assertTrue($this->nodeExists($title));
}
/**
* Assert that node with given title does not exist.
*
* @param string $title
* Title of the node.
*/
protected function assertNodeDoesNotExist($title) {
self::assertFalse($this->nodeExists($title));
}
/**
* Checks if node with given title exists.
*
* @param string $title
* Title of the node.
*
* @return bool
*/
protected function nodeExists($title) {
$query = \Drupal::entityQuery('node');
$result = $query
->condition('title', $title)
->range(0, 1)
->execute();
return !empty($result);
}
}

View file

@ -0,0 +1,180 @@
<?php
namespace Drupal\Tests\migrate\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
/**
* Base class for tests of Migrate source plugins.
*/
abstract class MigrateSourceTestBase extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['migrate'];
/**
* The mocked migration.
*
* @var MigrationInterface|\Prophecy\Prophecy\ObjectProphecy
*/
protected $migration;
/**
* The source plugin under test.
*
* @var \Drupal\migrate\Plugin\MigrateSourceInterface
*/
protected $plugin;
/**
* The data provider.
*
* @see \Drupal\Tests\migrate\Kernel\MigrateSourceTestBase::testSource
*
* @return array
* Array of data sets to test, each of which is a numerically indexed array
* with the following elements:
* - An array of source data, which can be optionally processed and set up
* by subclasses.
* - An array of expected result rows.
* - (optional) The number of result rows the plugin under test is expected
* to return. If this is not a numeric value, the plugin will not be
* counted.
* - (optional) Array of configuration options for the plugin under test.
*/
abstract public function providerSource();
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create a mock migration. This will be injected into the source plugin
// under test.
$this->migration = $this->prophesize(MigrationInterface::class);
$this->migration->id()->willReturn(
$this->randomMachineName(16)
);
// Prophesize a useless ID map plugin and an empty set of destination IDs.
// Calling code can override these prophecies later and set up different
// behaviors.
$this->migration->getIdMap()->willReturn(
$this->prophesize(MigrateIdMapInterface::class)->reveal()
);
$this->migration->getDestinationIds()->willReturn([]);
}
/**
* Determines the plugin to be tested by reading the class @covers annotation.
*
* @return string
*/
protected function getPluginClass() {
$annotations = $this->getAnnotations();
if (isset($annotations['class']['covers'])) {
return $annotations['class']['covers'][0];
}
else {
$this->fail('No plugin class was specified');
}
}
/**
* Instantiates the source plugin under test.
*
* @param array $configuration
* The source plugin configuration.
*
* @return \Drupal\migrate\Plugin\MigrateSourceInterface|object
* The fully configured source plugin.
*/
protected function getPlugin(array $configuration) {
// Only create the plugin once per test.
if ($this->plugin) {
return $this->plugin;
}
$class = ltrim($this->getPluginClass(), '\\');
/** @var \Drupal\migrate\Plugin\MigratePluginManager $plugin_manager */
$plugin_manager = $this->container->get('plugin.manager.migrate.source');
foreach ($plugin_manager->getDefinitions() as $id => $definition) {
if (ltrim($definition['class'], '\\') == $class) {
$this->plugin = $plugin_manager
->createInstance($id, $configuration, $this->migration->reveal());
$this->migration
->getSourcePlugin()
->willReturn($this->plugin);
return $this->plugin;
}
}
$this->fail('No plugin found for class ' . $class);
}
/**
* Tests the source plugin against a particular data set.
*
* @param array $source_data
* The source data that the source plugin will read.
* @param array $expected_data
* The result rows the source plugin is expected to return.
* @param mixed $expected_count
* (optional) How many rows the source plugin is expected to return.
* Defaults to count($expected_data). If set to a non-null, non-numeric
* value (like FALSE or 'nope'), the source plugin will not be counted.
* @param array $configuration
* (optional) Configuration for the source plugin.
*
* @dataProvider providerSource
*/
public function testSource(array $source_data, array $expected_data, $expected_count = NULL, array $configuration = []) {
$plugin = $this->getPlugin($configuration);
// All source plugins must define IDs.
$this->assertNotEmpty($plugin->getIds());
if (is_null($expected_count)) {
$expected_count = count($expected_data);
}
// If an expected count was given, assert it only if the plugin is
// countable.
if (is_numeric($expected_count)) {
$this->assertInstanceOf('\Countable', $plugin);
$this->assertCount($expected_count, $plugin);
}
$i = 0;
/** @var \Drupal\migrate\Row $row */
foreach ($plugin as $row) {
$this->assertInstanceOf(Row::class, $row);
$expected = $expected_data[$i++];
$actual = $row->getSource();
foreach ($expected as $key => $value) {
$this->assertArrayHasKey($key, $actual);
if (is_array($value)) {
ksort($value);
ksort($actual[$key]);
$this->assertEquals($value, $actual[$key]);
}
else {
$this->assertSame((string) $value, (string) $actual[$key]);
}
}
}
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace Drupal\Tests\migrate\Kernel;
use Drupal\Core\Database\Driver\sqlite\Connection;
/**
* Base class for tests of Migrate source plugins that use a database.
*/
abstract class MigrateSqlSourceTestBase extends MigrateSourceTestBase {
/**
* Builds an in-memory SQLite database from a set of source data.
*
* @param array $source_data
* The source data, keyed by table name. Each table is an array containing
* the rows in that table.
*
* @return \Drupal\Core\Database\Driver\sqlite\Connection
* The SQLite database connection.
*/
protected function getDatabase(array $source_data) {
// Create an in-memory SQLite database. Plugins can interact with it like
// any other database, and it will cease to exist when the connection is
// closed.
$connection_options = ['database' => ':memory:'];
$pdo = Connection::open($connection_options);
$connection = new Connection($pdo, $connection_options);
// Create the tables and fill them with data.
foreach ($source_data as $table => $rows) {
// Use the biggest row to build the table schema.
$counts = array_map('count', $rows);
asort($counts);
end($counts);
$pilot = $rows[key($counts)];
$connection->schema()
->createTable($table, [
// SQLite uses loose affinity typing, so it's OK for every field to
// be a text field.
'fields' => array_map(function() { return ['type' => 'text']; }, $pilot),
]);
$fields = array_keys($pilot);
$insert = $connection->insert($table)->fields($fields);
array_walk($rows, [$insert, 'values']);
$insert->execute();
}
return $connection;
}
/**
* Tests the source plugin against a particular data set.
*
* @param array $source_data
* The source data that the plugin will read. See getDatabase() for the
* expected format.
* @param array $expected_data
* The result rows the plugin is expected to return.
* @param int $expected_count
* (optional) How many rows the source plugin is expected to return.
* @param array $configuration
* (optional) Configuration for the source plugin.
*
* @dataProvider providerSource
*
* @requires extension pdo_sqlite
*/
public function testSource(array $source_data, array $expected_data, $expected_count = NULL, array $configuration = []) {
$plugin = $this->getPlugin($configuration);
// Since we don't yet inject the database connection, we need to use a
// reflection hack to set it in the plugin instance.
$reflector = new \ReflectionObject($plugin);
$property = $reflector->getProperty('database');
$property->setAccessible(TRUE);
$property->setValue($plugin, $this->getDatabase($source_data));
parent::testSource($source_data, $expected_data, $expected_count, $configuration);
}
}

View file

@ -7,6 +7,8 @@ use Drupal\KernelTests\KernelTestBase;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\MigrateMessageInterface;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Plugin\Migration;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
/**
@ -136,6 +138,16 @@ abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageI
}
}
/**
* Modify a migration's configuration before executing it.
*
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The migration to execute.
*/
protected function prepareMigration(MigrationInterface $migration) {
// Default implementation for test classes not requiring modification.
}
/**
* Executes a single migration.
*
@ -152,6 +164,8 @@ abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageI
if ($this instanceof MigrateDumpAlterInterface) {
static::migrateDumpAlter($this);
}
$this->prepareMigration($this->migration);
(new MigrateExecutable($this->migration, $this))->import();
}

View file

@ -34,8 +34,8 @@ class MigrationTest extends KernelTestBase {
$this->assertEqual('entity:entity_view_mode', $migration->getDestinationPlugin()->getPluginId());
// Test the source plugin is invalidated.
$migration->set('source', ['plugin' => 'd6_field']);
$this->assertEqual('d6_field', $migration->getSourcePlugin()->getPluginId());
$migration->set('source', ['plugin' => 'embedded_data', 'data_rows' => [], 'ids' => []]);
$this->assertEqual('embedded_data', $migration->getSourcePlugin()->getPluginId());
// Test the destination plugin is invalidated.
$migration->set('destination', ['plugin' => 'null']);

View file

@ -0,0 +1,76 @@
<?php
namespace Drupal\Tests\migrate\Kernel\Plugin;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the migration plugin manager.
*
* @coversDefaultClass \Drupal\migrate\Plugin\MigratePluginManager
* @group migrate
*/
class MigrationPluginConfigurationTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'migrate',
'migrate_drupal',
// Test with a simple migration.
'ban',
];
/**
* Test merging configuration into a plugin through the plugin manager.
*
* @dataProvider mergeProvider
*/
public function testConfigurationMerge($configuration, $expected) {
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->container->get('plugin.manager.migration')->createInstance('d7_blocked_ips', $configuration);
$source_configuration = $migration->getSourceConfiguration();
$this->assertEquals($expected, $source_configuration);
}
/**
* Provide configuration data for testing.
*/
public function mergeProvider() {
return [
// Tests adding new configuration to a migration.
[
// New configuration.
[
'source' => [
'constants' => [
'added_setting' => 'Ban them all!',
],
],
],
// Expected final source configuration.
[
'plugin' => 'd7_blocked_ips',
'constants' => [
'added_setting' => 'Ban them all!',
],
],
],
// Tests overriding pre-existing configuration in a migration.
[
// New configuration.
[
'source' => [
'plugin' => 'a_different_plugin',
],
],
// Expected final source configuration.
[
'plugin' => 'a_different_plugin',
],
],
];
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace Drupal\Tests\migrate\Kernel\Plugin;
use Drupal\Core\Database\Database;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the migration plugin manager.
*
* @coversDefaultClass \Drupal\migrate\Plugin\MigratePluginManager
* @group migrate
*/
class MigrationPluginListTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'migrate',
// Test with all modules containing Drupal migrations.
'action',
'aggregator',
'ban',
'block',
'block_content',
'book',
'comment',
'contact',
'dblog',
'field',
'file',
'filter',
'forum',
'image',
'language',
'locale',
'menu_link_content',
'menu_ui',
'node',
'path',
'search',
'shortcut',
'simpletest',
'statistics',
'syslog',
'system',
'taxonomy',
'text',
'tracker',
'update',
'user',
];
/**
* @covers ::getDefinitions
*/
public function testGetDefinitions() {
// Make sure retrieving all the core migration plugins does not throw any
// errors.
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
// All the plugins provided by core depend on migrate_drupal.
$this->assertEmpty($migration_plugins);
// Enable a module that provides migrations that do not depend on
// migrate_drupal.
$this->enableModules(['migrate_external_translated_test']);
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
// All the plugins provided by migrate_external_translated_test do not
// depend on migrate_drupal.
$this::assertArrayHasKey('external_translated_test_node', $migration_plugins);
$this::assertArrayHasKey('external_translated_test_node_translation', $migration_plugins);
// Disable the test module and the list should be empty again.
$this->disableModules(['migrate_external_translated_test']);
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
// All the plugins provided by core depend on migrate_drupal.
$this->assertEmpty($migration_plugins);
// Enable migrate_drupal to test that the plugins can now be discovered.
$this->enableModules(['migrate_drupal']);
// Set up a migrate database connection so that plugin discovery works.
// Clone the current connection and replace the current prefix.
$connection_info = Database::getConnectionInfo('migrate');
if ($connection_info) {
Database::renameConnection('migrate', 'simpletest_original_migrate');
}
$connection_info = Database::getConnectionInfo('default');
foreach ($connection_info as $target => $value) {
$prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix'];
// Simpletest uses 7 character prefixes at most so this can't cause
// collisions.
$connection_info[$target]['prefix']['default'] = $prefix . '0';
// Add the original simpletest prefix so SQLite can attach its database.
// @see \Drupal\Core\Database\Driver\sqlite\Connection::init()
$connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default'];
}
Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
// All the plugins provided by core depend on migrate_drupal.
$this->assertNotEmpty($migration_plugins);
}
}

View file

@ -33,10 +33,27 @@ class MigrationTest extends KernelTestBase {
* @covers ::getMigrationDependencies
*/
public function testGetMigrationDependencies() {
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([
'migration_dependencies' => NULL
]);
$this->assertNotEmpty($migration->getMigrationDependencies(), 'Migration dependencies is not empty');
$plugin_manager = \Drupal::service('plugin.manager.migration');
$plugin_definition = [
'process' => [
'f1' => 'bar',
'f2' => [
'plugin' => 'migration',
'migration' => 'm1'
],
'f3' => [
'plugin' => 'iterator',
'process' => [
'target_id' => [
'plugin' => 'migration',
'migration' => 'm2',
],
],
],
],
];
$migration = $plugin_manager->createStubMigration($plugin_definition);
$this->assertSame(['required' => [], 'optional' => ['m1', 'm2']], $migration->getMigrationDependencies());
}
/**

View file

@ -0,0 +1,183 @@
<?php
namespace Drupal\Tests\migrate\Kernel\process;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\KernelTests\Core\File\FileTestBase;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Plugin\migrate\process\FileCopy;
use Drupal\migrate\Row;
/**
* Tests the copy_file process plugin.
*
* @group migrate
*/
class CopyFileTest extends FileTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system'];
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->fileSystem = $this->container->get('file_system');
$this->container->get('stream_wrapper_manager')->registerWrapper('temporary', 'Drupal\Core\StreamWrapper\TemporaryStream', StreamWrapperInterface::LOCAL_NORMAL);
}
/**
* Test successful imports/copies.
*/
public function testSuccessfulCopies() {
$file = $this->createUri(NULL, NULL, 'temporary');
$file_absolute = $this->fileSystem->realpath($file);
$data_sets = [
// Test a local to local copy.
[
$this->root . '/core/modules/simpletest/files/image-test.jpg',
'public://file1.jpg'
],
// Test a temporary file using an absolute path.
[
$file_absolute,
'temporary://test.jpg'
],
// Test a temporary file using a relative path.
[
$file_absolute,
'temporary://core/modules/simpletest/files/test.jpg'
],
];
foreach ($data_sets as $data) {
list($source_path, $destination_path) = $data;
$actual_destination = $this->doImport($source_path, $destination_path);
$message = sprintf('File %s exists', $destination_path);
$this->assertFileExists($destination_path, $message);
// Make sure we didn't accidentally do a move.
$this->assertFileExists($source_path, $message);
$this->assertSame($actual_destination, $destination_path, 'The import returned the copied filename.');
}
}
/**
* Test successful file reuse.
*/
public function testSuccessfulReuse() {
$source_path = $this->root . '/core/modules/simpletest/files/image-test.jpg';
$destination_path = 'public://file1.jpg';
$file_reuse = file_unmanaged_copy($source_path, $destination_path);
$timestamp = (new \SplFileInfo($file_reuse))->getMTime();
$this->assertInternalType('int', $timestamp);
// We need to make sure the modified timestamp on the file is sooner than
// the attempted migration.
sleep(1);
$configuration = ['reuse' => TRUE];
$this->doImport($source_path, $destination_path, $configuration);
clearstatcache(TRUE, $destination_path);
$modified_timestamp = (new \SplFileInfo($destination_path))->getMTime();
$this->assertEquals($timestamp, $modified_timestamp);
$configuration = ['reuse' => FALSE];
$this->doImport($source_path, $destination_path, $configuration);
clearstatcache(TRUE, $destination_path);
$modified_timestamp = (new \SplFileInfo($destination_path))->getMTime();
$this->assertGreaterThan($timestamp, $modified_timestamp);
}
/**
* Test successful moves.
*/
public function testSuccessfulMoves() {
$file_1 = $this->createUri(NULL, NULL, 'temporary');
$file_1_absolute = $this->fileSystem->realpath($file_1);
$file_2 = $this->createUri(NULL, NULL, 'temporary');
$file_2_absolute = $this->fileSystem->realpath($file_2);
$local_file = $this->createUri(NULL, NULL, 'public');
$data_sets = [
// Test a local to local copy.
[
$local_file,
'public://file1.jpg'
],
// Test a temporary file using an absolute path.
[
$file_1_absolute,
'temporary://test.jpg'
],
// Test a temporary file using a relative path.
[
$file_2_absolute,
'temporary://core/modules/simpletest/files/test.jpg'
],
];
foreach ($data_sets as $data) {
list($source_path, $destination_path) = $data;
$actual_destination = $this->doImport($source_path, $destination_path, ['move' => TRUE]);
$message = sprintf('File %s exists', $destination_path);
$this->assertFileExists($destination_path, $message);
$message = sprintf('File %s does not exist', $source_path);
$this->assertFileNotExists($source_path, $message);
$this->assertSame($actual_destination, $destination_path, 'The importer returned the moved filename.');
}
}
/**
* Test that non-existent files throw an exception.
*
* @expectedException \Drupal\migrate\MigrateException
*
* @expectedExceptionMessage File '/non/existent/file' does not exist
*/
public function testNonExistentSourceFile() {
$source = '/non/existent/file';
$this->doImport($source, 'public://wontmatter.jpg');
}
/**
* Test the 'rename' overwrite mode.
*/
public function testRenameFile() {
$source = $this->createUri(NULL, NULL, 'temporary');
$destination = $this->createUri('foo.txt', NULL, 'public');
$expected_destination = 'public://foo_0.txt';
$actual_destination = $this->doImport($source, $destination, ['rename' => TRUE]);
$this->assertFileExists($expected_destination, 'File was renamed on import');
$this->assertSame($actual_destination, $expected_destination, 'The importer returned the renamed filename.');
}
/**
* Do an import using the destination.
*
* @param string $source_path
* Source path to copy from.
* @param string $destination_path
* The destination path to copy to.
* @param array $configuration
* Process plugin configuration settings.
*
* @throws \Drupal\migrate\MigrateException
*/
protected function doImport($source_path, $destination_path, $configuration = []) {
$plugin = FileCopy::create($this->container, $configuration, 'file_copy', []);
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
$row = new Row([], []);
$result = $plugin->transform([$source_path, $destination_path], $executable, $row, 'foobaz');
// Return the imported file Uri.
return $result;
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace Drupal\Tests\migrate\Unit\Event;
use Drupal\migrate\Event\EventBase;
/**
* @coversDefaultClass \Drupal\migrate\Event\EventBase
* @group migrate
*/
class EventBaseTest extends \PHPUnit_Framework_TestCase {
/**
* Test getMigration method.
*
* @covers ::__construct
* @covers ::getMigration
*/
public function testGetMigration() {
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
$event = new EventBase($migration, $message_service, $row, [1, 2, 3]);
$this->assertSame($migration, $event->getMigration());
}
/**
* Test logging a message.
*
* @covers ::__construct
* @covers ::logMessage
*/
public function testLogMessage() {
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface');
$event = new EventBase($migration, $message_service->reveal());
// Assert that the intended calls to the services happen.
$message_service->display('status message', 'status')->shouldBeCalledTimes(1);
$event->logMessage('status message');
$message_service->display('warning message', 'warning')->shouldBeCalledTimes(1);
$event->logMessage('warning message', 'warning');
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Drupal\Tests\migrate\Unit\Event;
use Drupal\migrate\Event\MigrateImportEvent;
/**
* @coversDefaultClass \Drupal\migrate\Event\MigrateImportEvent
* @group migrate
*/
class MigrateImportEventTest extends \PHPUnit_Framework_TestCase {
/**
* Test getMigration method.
*
* @covers ::__construct
* @covers ::getMigration
*/
public function testGetMigration() {
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
$event = new MigrateImportEvent($migration, $message_service);
$this->assertSame($migration, $event->getMigration());
}
/**
* Test logging a message.
*
* @covers ::__construct
* @covers ::logMessage
*/
public function testLogMessage() {
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface');
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface');
$event = new MigrateImportEvent($migration->reveal(), $message_service->reveal());
// Assert that the intended calls to the services happen.
$message_service->display('status message', 'status')->shouldBeCalledTimes(1);
$event->logMessage('status message');
$message_service->display('warning message', 'warning')->shouldBeCalledTimes(1);
$event->logMessage('warning message', 'warning');
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Drupal\Tests\migrate\Unit\Event;
use Drupal\migrate\Event\MigratePostRowSaveEvent;
/**
* @coversDefaultClass \Drupal\migrate\Event\MigratePostRowSaveEvent
* @group migrate
*/
class MigratePostRowSaveEventTest extends EventBaseTest {
/**
* Test getDestinationIdValues method.
*
* @covers ::__construct
* @covers ::getDestinationIdValues
*/
public function testGetDestinationIdValues() {
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
$event = new MigratePostRowSaveEvent($migration, $message_service, $row, [1, 2, 3]);
$this->assertSame([1, 2, 3], $event->getDestinationIdValues());
}
/**
* Test getRow method.
*
* @covers ::__construct
* @covers ::getRow
*/
public function testGetRow() {
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface');
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
$event = new MigratePostRowSaveEvent($migration, $message_service->reveal(), $row, [1, 2, 3]);
$this->assertSame($row, $event->getRow());
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Drupal\Tests\migrate\Unit\Event;
use Drupal\migrate\Event\MigratePreRowSaveEvent;
/**
* @coversDefaultClass \Drupal\migrate\Event\MigratePreRowSaveEvent
* @group migrate
*/
class MigratePreRowSaveEventTest extends EventBaseTest {
/**
* Test getRow method.
*
* @covers ::__construct
* @covers ::getRow
*/
public function testGetRow() {
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
$event = new MigratePreRowSaveEvent($migration, $message_service, $row);
$this->assertSame($row, $event->getRow());
}
}

View file

@ -114,9 +114,9 @@ class MigrateExecutableTest extends MigrateTestCase {
->with($row, array('test'))
->will($this->returnValue(array('id' => 'test')));
$this->migration->expects($this->once())
$this->migration
->method('getDestinationPlugin')
->will($this->returnValue($destination));
->willReturn($destination);
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
}
@ -156,9 +156,9 @@ class MigrateExecutableTest extends MigrateTestCase {
->with($row, array('test'))
->will($this->returnValue(TRUE));
$this->migration->expects($this->once())
$this->migration
->method('getDestinationPlugin')
->will($this->returnValue($destination));
->willReturn($destination);
$this->idMap->expects($this->never())
->method('saveIdMapping');
@ -196,9 +196,9 @@ class MigrateExecutableTest extends MigrateTestCase {
->with($row, array('test'))
->will($this->returnValue(array()));
$this->migration->expects($this->once())
$this->migration
->method('getDestinationPlugin')
->will($this->returnValue($destination));
->willReturn($destination);
$this->idMap->expects($this->once())
->method('saveIdMapping')
@ -256,9 +256,9 @@ class MigrateExecutableTest extends MigrateTestCase {
->with($row, array('test'))
->will($this->throwException(new MigrateException($exception_message)));
$this->migration->expects($this->once())
$this->migration
->method('getDestinationPlugin')
->will($this->returnValue($destination));
->willReturn($destination);
$this->idMap->expects($this->once())
->method('saveIdMapping')
@ -306,7 +306,7 @@ class MigrateExecutableTest extends MigrateTestCase {
$destination->expects($this->never())
->method('import');
$this->migration->expects($this->once())
$this->migration
->method('getDestinationPlugin')
->willReturn($destination);
@ -354,9 +354,9 @@ class MigrateExecutableTest extends MigrateTestCase {
->with($row, array('test'))
->will($this->throwException(new \Exception($exception_message)));
$this->migration->expects($this->once())
$this->migration
->method('getDestinationPlugin')
->will($this->returnValue($destination));
->willReturn($destination);
$this->idMap->expects($this->once())
->method('saveIdMapping')

View file

@ -10,6 +10,8 @@ namespace Drupal\Tests\migrate\Unit;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
@ -75,7 +77,21 @@ class MigrateSourceTest extends MigrateTestCase {
* @return \Drupal\migrate\Plugin\MigrateSourceInterface
* A mocked source plugin.
*/
protected function getSource($configuration = [], $migrate_config = [], $status = MigrateIdMapInterface::STATUS_NEEDS_UPDATE) {
protected function getSource($configuration = [], $migrate_config = [], $status = MigrateIdMapInterface::STATUS_NEEDS_UPDATE, $high_water_value = NULL) {
$container = new ContainerBuilder();
\Drupal::setContainer($container);
$key_value = $this->getMock(KeyValueStoreInterface::class);
$key_value_factory = $this->getMock(KeyValueFactoryInterface::class);
$key_value_factory
->method('get')
->with('migrate:high_water')
->willReturn($key_value);
$container->set('keyvalue', $key_value_factory);
$container->set('cache.migrate', $this->getMock(CacheBackendInterface::class));
$this->migrationConfiguration = $this->defaultMigrationConfiguration + $migrate_config;
$this->migration = parent::getMigration();
$this->executable = $this->getMigrateExecutable($this->migration);
@ -90,47 +106,45 @@ class MigrateSourceTest extends MigrateTestCase {
->willReturn($id_map_array);
$constructor_args = [$configuration, 'd6_action', [], $this->migration];
$methods = ['getModuleHandler', 'fields', 'getIds', '__toString', 'getIterator', 'prepareRow', 'initializeIterator', 'calculateDependencies'];
$source_plugin = $this->getMock('\Drupal\migrate\Plugin\migrate\source\SourcePluginBase', $methods, $constructor_args);
$methods = ['getModuleHandler', 'fields', 'getIds', '__toString', 'prepareRow', 'initializeIterator'];
$source_plugin = $this->getMock(SourcePluginBase::class, $methods, $constructor_args);
$source_plugin
->expects($this->any())
->method('fields')
->willReturn([]);
$source_plugin
->expects($this->any())
->method('getIds')
->willReturn([]);
$source_plugin
->expects($this->any())
->method('__toString')
->willReturn('');
$source_plugin
->expects($this->any())
->method('prepareRow')
->willReturn(empty($migrate_config['prepare_row_false']));
$rows = [$this->row];
if (isset($configuration['high_water_property']) && isset($high_water_value)) {
$property = $configuration['high_water_property']['name'];
$rows = array_filter($rows, function (array $row) use ($property, $high_water_value) {
return $row[$property] >= $high_water_value;
});
}
$iterator = new \ArrayIterator($rows);
$source_plugin
->expects($this->any())
->method('initializeIterator')
->willReturn([]);
$iterator = new \ArrayIterator([$this->row]);
$source_plugin
->expects($this->any())
->method('getIterator')
->willReturn($iterator);
$module_handler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
$module_handler = $this->getMock(ModuleHandlerInterface::class);
$source_plugin
->expects($this->any())
->method('getModuleHandler')
->willReturn($module_handler);
$this->migration
->expects($this->any())
->method('getSourcePlugin')
->willReturn($source_plugin);
return $this->migration->getSourcePlugin();
return $source_plugin;
}
/**
@ -138,9 +152,8 @@ class MigrateSourceTest extends MigrateTestCase {
* @expectedException \Drupal\migrate\MigrateException
*/
public function testHighwaterTrackChangesIncompatible() {
$source_config = ['track_changes' => TRUE];
$migration_config = ['highWaterProperty' => ['name' => 'something']];
$this->getSource($source_config, $migration_config);
$source_config = ['track_changes' => TRUE, 'high_water_property' => ['name' => 'something']];
$this->getSource($source_config);
}
/**
@ -168,9 +181,20 @@ class MigrateSourceTest extends MigrateTestCase {
// Test the skip argument.
$source = $this->getSource(['skip_count' => TRUE]);
$this->assertEquals(-1, $source->count());
$this->migrationConfiguration['id'] = 'test_migration';
$migration = $this->getMigration();
$source = new StubSourceGeneratorPlugin([], '', [], $migration);
// Test the skipCount property's default value.
$this->assertEquals(-1, $source->count());
// Test the count value using a generator.
$source = new StubSourceGeneratorPlugin(['skip_count' => FALSE], '', [], $migration);
$this->assertEquals(3, $source->count());
}
/**
/**
* Test that the key can be set for the count cache.
*
* @covers ::count
@ -219,14 +243,12 @@ class MigrateSourceTest extends MigrateTestCase {
* Test that an outdated highwater mark does not cause a row to be imported.
*/
public function testOutdatedHighwater() {
$source = $this->getSource([], [], MigrateIdMapInterface::STATUS_IMPORTED);
// Set the originalHighwater to something higher than our timestamp.
$this->migration
->expects($this->any())
->method('getHighwater')
->willReturn($this->row['timestamp'] + 1);
$configuration = [
'high_water_property' => [
'name' => 'timestamp',
],
];
$source = $this->getSource($configuration, [], MigrateIdMapInterface::STATUS_IMPORTED, $this->row['timestamp'] + 1);
// The current highwater mark is now higher than the row timestamp so no row
// is expected.
@ -240,13 +262,17 @@ class MigrateSourceTest extends MigrateTestCase {
* @throws \Exception
*/
public function testNewHighwater() {
$configuration = [
'high_water_property' => [
'name' => 'timestamp',
],
];
// Set a highwater property field for source. Now we should have a row
// because the row timestamp is greater than the current highwater mark.
$source = $this->getSource([], ['highWaterProperty' => ['name' => 'timestamp']], MigrateIdMapInterface::STATUS_IMPORTED);
$source = $this->getSource($configuration, [], MigrateIdMapInterface::STATUS_IMPORTED, $this->row['timestamp'] - 1);
$source->rewind();
$this->assertTrue(is_a($source->current(), 'Drupal\migrate\Row'), 'Incoming row timestamp is greater than current highwater mark so we have a row.');
$this->assertInstanceOf(Row::class, $source->current(), 'Incoming row timestamp is greater than current highwater mark so we have a row.');
}
/**
@ -387,6 +413,21 @@ class MigrateSourceTest extends MigrateTestCase {
$this->assertFalse($source->prepareRow($row));
}
/**
* Test that cacheCounts, skipCount, trackChanges preserve their default
* values.
*/
public function testDefaultPropertiesValues() {
$this->migrationConfiguration['id'] = 'test_migration';
$migration = $this->getMigration();
$source = new StubSourceGeneratorPlugin([], '', [], $migration);
// Test the default value of the skipCount Value;
$this->assertTrue($source->getSkipCount());
$this->assertTrue($source->getCacheCounts());
$this->assertTrue($source->getTrackChanges());
}
/**
* Gets a mock executable for the test.
*
@ -450,3 +491,61 @@ class StubSourcePlugin extends SourcePluginBase {
}
}
/**
* Stubbed source plugin with a generator as iterator. Also it overwrites the
* $skipCount, $cacheCounts and $trackChanges properties.
*/
class StubSourceGeneratorPlugin extends StubSourcePlugin {
/**
* {@inheritdoc}
*/
protected $skipCount = TRUE;
/**
* {@inheritdoc}
*/
protected $cacheCounts = TRUE;
/**
* {@inheritdoc}
*/
protected $trackChanges = TRUE;
/**
* Return the skipCount value.
*/
public function getSkipCount() {
return $this->skipCount;
}
/**
* Return the cacheCounts value.
*/
public function getCacheCounts() {
return $this->cacheCounts;
}
/**
* Return the trackChanges value.
*/
public function getTrackChanges() {
return $this->trackChanges;
}
/**
* {@inheritdoc}
*/
protected function initializeIterator() {
$data = [
['title' => 'foo'],
['title' => 'bar'],
['title' => 'iggy'],
];
foreach ($data as $row) {
yield $row;
}
}
}

View file

@ -3,9 +3,16 @@
namespace Drupal\Tests\migrate\Unit;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
/**
* Base class for Migrate module source unit tests.
*
* @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
* \Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase instead.
*/
abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
@ -44,9 +51,9 @@ abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
* Once the migration is run, we save a mark of the migrated sources, so the
* migration can run again and update only new sources or changed sources.
*
* @var string
* @var mixed
*/
const ORIGINAL_HIGH_WATER = '';
const ORIGINAL_HIGH_WATER = NULL;
/**
* Expected results after the source parsing.
@ -77,6 +84,27 @@ abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
$state = $this->getMock('Drupal\Core\State\StateInterface');
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
// Mock a key-value store to return high-water values.
$key_value = $this->getMock(KeyValueStoreInterface::class);
// SourcePluginBase does not yet support full dependency injection so we
// need to make sure that \Drupal::keyValue() works as expected by mocking
// the keyvalue service.
$key_value_factory = $this->getMock(KeyValueFactoryInterface::class);
$key_value_factory
->method('get')
->with('migrate:high_water')
->willReturn($key_value);
try {
$container = \Drupal::getContainer();
}
catch (ContainerNotInitializedException $e) {
$container = new ContainerBuilder();
}
$container->set('keyvalue', $key_value_factory);
\Drupal::setContainer($container);
$migration = $this->getMigration();
$migration->expects($this->any())
->method('getHighWater')

View file

@ -80,7 +80,7 @@ abstract class MigrateTestCase extends UnitTestCase {
$migration->method('getHighWaterProperty')
->willReturnCallback(function () use ($configuration) {
return isset($configuration['highWaterProperty']) ? $configuration['highWaterProperty'] : '';
return isset($configuration['high_water_property']) ? $configuration['high_water_property'] : '';
});
$migration->method('set')

View file

@ -1,118 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\Tests\migrate\Unit\MigrationStorageTest.
*/
namespace Drupal\Tests\migrate\Unit;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryFactoryInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\migrate\MigrationStorage;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\migrate\MigrationStorage
* @group migrate
*/
class MigrationStorageTest extends UnitTestCase {
/**
* The migration storage.
*
* @var \Drupal\Tests\migrate\Unit\TestMigrationStorage
*/
protected $storage;
/**
* The storage query.
*
* @var \Drupal\Core\Entity\Query\QueryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $query;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->query = $this->getMock(QueryInterface::class);
$this->query->method('condition')
->willReturnSelf();
$query_factory = $this->getMock(QueryFactoryInterface::class);
$query_factory->method('get')
->willReturn($this->query);
$this->storage = new TestMigrationStorage(
$this->getMock(EntityTypeInterface::class),
$this->getMock(ConfigFactoryInterface::class),
$this->getMock(UuidInterface::class),
$this->getMock(LanguageManagerInterface::class),
$query_factory
);
}
/**
* Tests getVariantIds() when variants exist.
*
* @covers ::getVariantIds
*/
public function testGetVariantIdsWithVariants() {
$this->query->method('execute')
->willReturn(['d6_node__page', 'd6_node__article']);
$ids = $this->storage->getVariantIds(['d6_node:*', 'd6_user']);
$this->assertSame(['d6_node__page', 'd6_node__article', 'd6_user'], $ids);
}
/**
* Tests getVariantIds() when no variants exist.
*
* @covers ::getVariantIds
*/
public function testGetVariantIdsNoVariants() {
$this->query->method('execute')
->willReturn([]);
$ids = $this->storage->getVariantIds(['d6_node:*', 'd6_user']);
$this->assertSame(['d6_user'], $ids);
}
/**
* Tests getVariantIds().
*
* This tests getVariantIds() when no variants exist and there are no static
* (non-variant) dependencies.
*
* @covers ::getVariantIds
*/
public function testGetVariantIdsNoVariantsOrStaticDependencies() {
$this->query->method('execute')
->willReturn([]);
$ids = $this->storage->getVariantIds(['d6_node:*', 'd6_node_revision:*']);
$this->assertSame([], $ids);
}
}
/**
* Test version of \Drupal\migrate\MigrationStorage.
*
* This class exposes protected methods for testing.
*/
class TestMigrationStorage extends MigrationStorage {
/**
* {@inheritdoc}
*/
public function getVariantIds(array $ids) {
return parent::getVariantIds($ids);
}
}

View file

@ -1,4 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\Tests\migrate\Unit\process\GetTest.

View file

@ -0,0 +1,66 @@
<?php
namespace Drupal\Tests\migrate\Unit\process;
use Drupal\migrate\Plugin\migrate\process\UrlEncode;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\MigrateMessage;
use Drupal\migrate\Row;
use Drupal\Tests\migrate\Unit\MigrateTestCase;
/**
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\process\UrlEncode
* @group file
*/
class UrlEncodeTest extends MigrateTestCase {
/**
* @inheritdoc
*/
protected $migrationConfiguration = [
'id' => 'test',
];
/**
* The data provider for testing URL encoding scenarios.
*
* @return array
* An array of URLs to test.
*/
public function urlDataProvider() {
return array(
'A URL with no characters requiring encoding' => array('http://example.com/normal_url.html', 'http://example.com/normal_url.html'),
'The definitive use case - encoding spaces in URLs' => array('http://example.com/url with spaces.html', 'http://example.com/url%20with%20spaces.html'),
'Definitive use case 2 - spaces in directories' => array('http://example.com/dir with spaces/foo.html', 'http://example.com/dir%20with%20spaces/foo.html'),
'Local filespecs without spaces should not be transformed' => array('/tmp/normal.txt', '/tmp/normal.txt'),
'Local filespecs with spaces should not be transformed' => array('/tmp/with spaces.txt', '/tmp/with spaces.txt'),
'Make sure URL characters (:, ?, &) are not encoded but others are.' => array('https://example.com/?a=b@c&d=e+f%', 'https://example.com/?a%3Db%40c&d%3De%2Bf%25'),
);
}
/**
* Cover various encoding scenarios.
* @dataProvider urlDataProvider
*/
public function testUrls($input, $output) {
$this->assertEquals($output, $this->doTransform($input));
}
/**
* Perform the urlencode process plugin over the given value.
*
* @param string $value
* URL to be encoded.
*
* @return string
* Encoded URL.
*/
protected function doTransform($value) {
$executable = new MigrateExecutable($this->getMigration(), new MigrateMessage());
$row = new Row([], []);
return (new UrlEncode([], 'urlencode', []))
->transform($value, $executable, $row, 'foobaz');
}
}