Commit 260f9367 authored by alexpott's avatar alexpott

Issue #2427335 by benjy, chx, dawehner: Combine legacy Source class into SourcePluginBase

parent 08ce5a70
...@@ -162,7 +162,7 @@ class MigrateExecutable implements MigrateExecutableInterface { ...@@ -162,7 +162,7 @@ class MigrateExecutable implements MigrateExecutableInterface {
/** /**
* The source. * The source.
* *
* @var \Drupal\migrate\Source * @var \Drupal\migrate\Plugin\MigrateSourceInterface
*/ */
protected $source; protected $source;
...@@ -219,12 +219,16 @@ public function __construct(MigrationInterface $migration, MigrateMessageInterfa ...@@ -219,12 +219,16 @@ public function __construct(MigrationInterface $migration, MigrateMessageInterfa
* *
* Makes sure source is initialized based on migration settings. * Makes sure source is initialized based on migration settings.
* *
* @return \Drupal\migrate\Source * @return \Drupal\migrate\Plugin\MigrateSourceInterface
* The source. * The source.
*/ */
protected function getSource() { protected function getSource() {
if (!isset($this->source)) { if (!isset($this->source)) {
$this->source = new Source($this->migration, $this); $this->source = $this->migration->getSourcePlugin();
// @TODO, find out how to remove this.
// @see https://drupal.org/node/2443617
$this->source->migrateExecutable = $this;
} }
return $this->source; return $this->source;
} }
...@@ -256,13 +260,11 @@ public function import() { ...@@ -256,13 +260,11 @@ public function import() {
} }
catch (\Exception $e) { catch (\Exception $e) {
$this->message->display( $this->message->display(
$this->t('Migration failed with source plugin exception: !e', $this->t('Migration failed with source plugin exception: !e', array('!e' => $e->getMessage())), 'error');
array('!e' => $e->getMessage())), 'error');
return MigrationInterface::RESULT_FAILED; return MigrationInterface::RESULT_FAILED;
} }
$destination = $this->migration->getDestinationPlugin(); $destination = $this->migration->getDestinationPlugin();
while ($source->valid()) { while ($source->valid()) {
$row = $source->current(); $row = $source->current();
if ($this->sourceIdValues = $row->getSourceIdValues()) { if ($this->sourceIdValues = $row->getSourceIdValues()) {
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
* *
* @ingroup migration * @ingroup migration
*/ */
interface MigrateSourceInterface extends \Countable, PluginInspectionInterface { interface MigrateSourceInterface extends \Countable, \Iterator, PluginInspectionInterface {
/** /**
* Returns available fields on the source. * Returns available fields on the source.
......
...@@ -30,7 +30,7 @@ public function fields() { ...@@ -30,7 +30,7 @@ public function fields() {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getIterator() { public function initializeIterator() {
return new \ArrayIterator(array(array('id' => ''))); return new \ArrayIterator(array(array('id' => '')));
} }
......
...@@ -112,7 +112,7 @@ protected function prepareQuery() { ...@@ -112,7 +112,7 @@ protected function prepareQuery() {
* We could simply execute the query and be functionally correct, but * We could simply execute the query and be functionally correct, but
* we will take advantage of the PDO-based API to optimize the query up-front. * we will take advantage of the PDO-based API to optimize the query up-front.
*/ */
protected function runQuery() { protected function initializeIterator() {
$this->prepareQuery(); $this->prepareQuery();
$high_water_property = $this->migration->get('highWaterProperty'); $high_water_property = $this->migration->get('highWaterProperty');
...@@ -203,18 +203,6 @@ public function count() { ...@@ -203,18 +203,6 @@ public function count() {
return $this->query()->countQuery()->execute()->fetchField(); return $this->query()->countQuery()->execute()->fetchField();
} }
/**
* Returns the iterator that will yield the row arrays to be processed.
*
* @return \Iterator
*/
public function getIterator() {
if (!isset($this->iterator)) {
$this->iterator = $this->runQuery();
}
return $this->iterator;
}
/** /**
* Check if we can join against the map table. * Check if we can join against the map table.
* *
......
...@@ -176,9 +176,12 @@ public function setSourceProperty($property, $data) { ...@@ -176,9 +176,12 @@ public function setSourceProperty($property, $data) {
/** /**
* Freezes the source. * Freezes the source.
*
* @return $this
*/ */
public function freezeSource() { public function freezeSource() {
$this->frozen = TRUE; $this->frozen = TRUE;
return $this;
} }
/** /**
......
This diff is collapsed.
...@@ -61,15 +61,11 @@ protected function setUp() { ...@@ -61,15 +61,11 @@ protected function setUp() {
* Tests an import with an incomplete rewinding. * Tests an import with an incomplete rewinding.
*/ */
public function testImportWithFailingRewind() { public function testImportWithFailingRewind() {
$iterator = $this->getMock('\Iterator');
$exception_message = $this->getRandomGenerator()->string(); $exception_message = $this->getRandomGenerator()->string();
$iterator->expects($this->once()) $source = $this->getMock('Drupal\migrate\Plugin\MigrateSourceInterface');
$source->expects($this->once())
->method('rewind') ->method('rewind')
->will($this->throwException(new \Exception($exception_message))); ->will($this->throwException(new \Exception($exception_message)));
$source = $this->getMock('Drupal\migrate\Plugin\MigrateSourceInterface');
$source->expects($this->any())
->method('getIterator')
->will($this->returnValue($iterator));
$this->migration->expects($this->any()) $this->migration->expects($this->any())
->method('getSourcePlugin') ->method('getSourcePlugin')
...@@ -500,20 +496,25 @@ public function testProcessRowEmptyPipeline() { ...@@ -500,20 +496,25 @@ public function testProcessRowEmptyPipeline() {
/** /**
* Returns a mock migration source instance. * Returns a mock migration source instance.
* *
* @return \Drupal\migrate\Source|\PHPUnit_Framework_MockObject_MockObject * @return \Drupal\migrate\Plugin\MigrateSourceInterface|\PHPUnit_Framework_MockObject_MockObject
*/ */
protected function getMockSource() { protected function getMockSource() {
$iterator = $this->getMock('\Iterator'); $iterator = $this->getMock('\Iterator');
$source = $this->getMockBuilder('Drupal\migrate\Source') $class = 'Drupal\migrate\Plugin\migrate\source\SourcePluginBase';
$source = $this->getMockBuilder($class)
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->setMethods(get_class_methods($class))
->getMockForAbstractClass();
$source->expects($this->any()) $source->expects($this->any())
->method('getIterator') ->method('getIterator')
->will($this->returnValue($iterator)); ->will($this->returnValue($iterator));
$source->expects($this->once()) $source->expects($this->once())
->method('rewind') ->method('rewind')
->will($this->returnValue(TRUE)); ->will($this->returnValue(TRUE));
$source->expects($this->any())
->method('initializeIterator')
->will($this->returnValue([]));
$source->expects($this->any()) $source->expects($this->any())
->method('valid') ->method('valid')
->will($this->onConsecutiveCalls(TRUE, FALSE)); ->will($this->onConsecutiveCalls(TRUE, FALSE));
......
<?php
/**
* @file
* Contains \Drupal\migrate\Unit\MigrateSourceTest
*/
namespace Drupal\Tests\migrate\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
/**
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
* @group migrate
*/
class MigrateSourceTest extends MigrateTestCase {
/**
* Override the migration config.
*
* @var array
*/
protected $defaultMigrationConfiguration = [
'id' => 'test_migration',
'source' => [],
];
/**
* Test row data.
*
* @var array
*/
protected $row = ['test_sourceid1' => '1', 'timestamp' => 500];
/**
* Test source ids.
*
* @var array
*/
protected $sourceIds = ['test_sourceid1' => 'test_sourceid1'];
/**
* The migration entity.
*
* @var \Drupal\migrate\Entity\Migration
*/
protected $migration;
/**
* The migrate executable.
*
* @var \Drupal\migrate\MigrateExecutable
*/
protected $executable;
/**
* Get the source plugin to test.
*
* @param array $configuration
* The source configuration.
* @param array $migrate_config
* The migration configuration to be used in parent::getMigration().
* @param int $status
* The default status for the new rows to be imported.
*
* @return \Drupal\migrate\Plugin\MigrateSourceInterface
* A mocked source plugin.
*/
protected function getSource($configuration = [], $migrate_config = [], $status = MigrateIdMapInterface::STATUS_NEEDS_UPDATE) {
$this->migrationConfiguration = $this->defaultMigrationConfiguration + $migrate_config;
$this->migration = parent::getMigration();
$this->executable = $this->getMigrateExecutable($this->migration);
// Update the idMap for Source so the default is that the row has already
// been imported. This allows us to use the highwater mark to decide on the
// outcome of whether we choose to import the row.
$id_map_array = ['original_hash' => '', 'hash' => '', 'source_row_status' => $status];
$this->idMap
->expects($this->any())
->method('getRowBySource')
->willReturn($id_map_array);
$constructor_args = [$configuration, 'd6_action', [], $this->migration];
$methods = ['getModuleHandler', 'fields', 'getIds', '__toString', 'getIterator', 'prepareRow', 'initializeIterator'];
$source_plugin = $this->getMock('\Drupal\migrate\Plugin\migrate\source\SourcePluginBase', $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']));
$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');
$source_plugin
->expects($this->any())
->method('getModuleHandler')
->willReturn($module_handler);
$this->migration
->expects($this->any())
->method('getSourcePlugin')
->willReturn($source_plugin);
return $this->migration->getSourcePlugin();
}
/**
* @expectedException \Drupal\migrate\MigrateException
*/
public function testHighwaterTrackChangesIncompatible() {
$source_config = ['track_changes' => TRUE];
$migration_config = ['highWaterProperty' => ['name' => 'something']];
$this->getSource($source_config, $migration_config);
}
/**
* Test that the source count is correct.
*/
public function testCount() {
$container = new ContainerBuilder();
$container->register('cache.migrate', 'Drupal\Core\Cache\NullBackend')
->setArguments(['migrate']);
\Drupal::setContainer($container);
// Test that the basic count works.
$source = $this->getSource();
$this->assertEquals(1, $source->count());
// Test caching the count works.
$source = $this->getSource(['cache_counts' => TRUE]);
$this->assertEquals(1, $source->count());
// Test the skip argument.
$source = $this->getSource(['skip_count' => TRUE]);
$this->assertEquals(-1, $source->count());
}
/**
* Test that we don't get a row if prepareRow() is false.
*/
public function testPrepareRowFalse() {
$source = $this->getSource([], ['prepare_row_false' => TRUE]);
$source->rewind();
$this->assertNull($source->current(), 'No row is available when prepareRow() is false.');
}
/**
* Test that the when a source id is in the idList, we don't get a row.
*/
public function testIdInList() {
$source = $this->getSource([], ['idlist' => ['test_sourceid1']]);
$source->rewind();
$this->assertNull($source->current(), 'No row is available because id was in idList.');
}
/**
* Test that $row->needsUpdate() works as expected.
*/
public function testNextNeedsUpdate() {
$source = $this->getSource();
// $row->needsUpdate() === TRUE so we get a row.
$source->rewind();
$this->assertTrue(is_a($source->current(), 'Drupal\migrate\Row'), '$row->needsUpdate() is TRUE so we got a row.');
// Test that we don't get a row when the incoming row is marked as imported.
$source = $this->getSource([], [], MigrateIdMapInterface::STATUS_IMPORTED);
$source->rewind();
$this->assertNull($source->current(), 'Row was already imported, should be NULL');
}
/**
* 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);
// The current highwater mark is now higher than the row timestamp so no row
// is expected.
$source->rewind();
$this->assertNull($source->current(), 'Original highwater mark is higher than incoming row timestamp.');
}
/**
* Test that a highwater mark newer than our saved one imports a row.
*
* @throws \Exception
*/
public function testNewHighwater() {
// 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->rewind();
$this->assertTrue(is_a($source->current(), 'Drupal\migrate\Row'), 'Incoming row timestamp is greater than current highwater mark so we have a row.');
}
/**
* Get a mock executable for the test.
*
* @param \Drupal\migrate\Entity\MigrationInterface $migration
* The migration entity.
*
* @return \Drupal\migrate\MigrateExecutable
* The migrate executable.
*/
protected function getMigrateExecutable($migration) {
$message = $this->getMock('Drupal\migrate\MigrateMessageInterface');
return new MigrateExecutable($migration, $message);
}
}
...@@ -7,9 +7,6 @@ ...@@ -7,9 +7,6 @@
namespace Drupal\Tests\migrate\Unit; namespace Drupal\Tests\migrate\Unit;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\migrate\Source;
/** /**
* Base class for Migrate module source unit tests. * Base class for Migrate module source unit tests.
*/ */
...@@ -87,13 +84,7 @@ protected function setUp() { ...@@ -87,13 +84,7 @@ protected function setUp() {
$migration->expects($this->any()) $migration->expects($this->any())
->method('getSourcePlugin') ->method('getSourcePlugin')
->will($this->returnValue($plugin)); ->will($this->returnValue($plugin));
$migrateExecutable = $this->getMockBuilder('Drupal\migrate\MigrateExecutable') $this->source = $plugin;
->disableOriginalConstructor()
->getMock();
$this->source = new TestSource($migration, $migrateExecutable);
$cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->source->setCache($cache);
} }
/** /**
...@@ -113,9 +104,3 @@ protected function getValue($row, $key) { ...@@ -113,9 +104,3 @@ protected function getValue($row, $key) {
} }
} }
class TestSource extends Source {
public function setCache(CacheBackendInterface $cache) {
$this->cache = $cache;
}
}
...@@ -129,7 +129,7 @@ public function maxExecTimeExceeded() { ...@@ -129,7 +129,7 @@ public function maxExecTimeExceeded() {
/** /**
* Allows access to set protected source property. * Allows access to set protected source property.
* *
* @param \Drupal\migrate\Source $source * @param \Drupal\migrate\Plugin\MigrateSourceInterface $source
* The value to set. * The value to set.
*/ */
public function setSource($source) { public function setSource($source) {
......
...@@ -39,7 +39,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition ...@@ -39,7 +39,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function runQuery() { protected function initializeIterator() {
return new \ArrayIterator(array($this->values())); return new \ArrayIterator(array($this->values()));
} }
......
...@@ -45,10 +45,10 @@ public function query() { ...@@ -45,10 +45,10 @@ public function query() {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function runQuery() { protected function initializeIterator() {
$this->defaultTheme = $this->variableGet('theme_default', 'Garland'); $this->defaultTheme = $this->variableGet('theme_default', 'Garland');
$this->adminTheme = $this->variableGet('admin_theme', null); $this->adminTheme = $this->variableGet('admin_theme', null);
return parent::runQuery(); return parent::initializeIterator();
} }
/** /**
......
...@@ -19,7 +19,7 @@ class CommentVariable extends DrupalSqlBase { ...@@ -19,7 +19,7 @@ class CommentVariable extends DrupalSqlBase {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function runQuery() { protected function initializeIterator() {
return new \ArrayIterator($this->getCommentVariables()); return new \ArrayIterator($this->getCommentVariables());
} }
......
...@@ -18,7 +18,7 @@ class ContactSettings extends Variable { ...@@ -18,7 +18,7 @@ class ContactSettings extends Variable {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
function runQuery() { function initializeIterator() {
$default_category = $this->select('contact', 'c') $default_category = $this->select('contact', 'c')
->fields('c', array('cid')) ->fields('c', array('cid'))
->condition('selected', 1) ->condition('selected', 1)
......
...@@ -21,7 +21,7 @@ class FieldInstancePerFormDisplay extends DrupalSqlBase { ...@@ -21,7 +21,7 @@ class FieldInstancePerFormDisplay extends DrupalSqlBase {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function runQuery() { protected function initializeIterator() {
$rows = array(); $rows = array();
$result = $this->prepareQuery()->execute(); $result = $this->prepareQuery()->execute();
while ($field_row = $result->fetchAssoc()) { while ($field_row = $result->fetchAssoc()) {
......
...@@ -20,7 +20,7 @@ class FieldInstancePerViewMode extends ViewModeBase { ...@@ -20,7 +20,7 @@ class FieldInstancePerViewMode extends ViewModeBase {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function runQuery() { protected function initializeIterator() {
$rows = array(); $rows = array();
$result = $this->prepareQuery()->execute(); $result = $this->prepareQuery()->execute();