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 {
/**
* The source.
*
* @var \Drupal\migrate\Source
* @var \Drupal\migrate\Plugin\MigrateSourceInterface
*/
protected $source;
......@@ -219,12 +219,16 @@ public function __construct(MigrationInterface $migration, MigrateMessageInterfa
*
* Makes sure source is initialized based on migration settings.
*
* @return \Drupal\migrate\Source
* @return \Drupal\migrate\Plugin\MigrateSourceInterface
* The source.
*/
protected function getSource() {
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;
}
......@@ -256,13 +260,11 @@ public function import() {
}
catch (\Exception $e) {
$this->message->display(
$this->t('Migration failed with source plugin exception: !e',
array('!e' => $e->getMessage())), 'error');
$this->t('Migration failed with source plugin exception: !e', array('!e' => $e->getMessage())), 'error');
return MigrationInterface::RESULT_FAILED;
}
$destination = $this->migration->getDestinationPlugin();
while ($source->valid()) {
$row = $source->current();
if ($this->sourceIdValues = $row->getSourceIdValues()) {
......
......@@ -19,7 +19,7 @@
*
* @ingroup migration
*/
interface MigrateSourceInterface extends \Countable, PluginInspectionInterface {
interface MigrateSourceInterface extends \Countable, \Iterator, PluginInspectionInterface {
/**
* Returns available fields on the source.
......
......@@ -30,7 +30,7 @@ public function fields() {
/**
* {@inheritdoc}
*/
public function getIterator() {
public function initializeIterator() {
return new \ArrayIterator(array(array('id' => '')));
}
......
......@@ -112,7 +112,7 @@ protected function prepareQuery() {
* 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.
*/
protected function runQuery() {
protected function initializeIterator() {
$this->prepareQuery();
$high_water_property = $this->migration->get('highWaterProperty');
......@@ -203,18 +203,6 @@ public function count() {
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.
*
......
......@@ -176,9 +176,12 @@ public function setSourceProperty($property, $data) {
/**
* Freezes the source.
*
* @return $this
*/
public function freezeSource() {
$this->frozen = TRUE;
return $this;
}
/**
......
This diff is collapsed.
......@@ -61,15 +61,11 @@ protected function setUp() {
* Tests an import with an incomplete rewinding.
*/
public function testImportWithFailingRewind() {
$iterator = $this->getMock('\Iterator');
$exception_message = $this->getRandomGenerator()->string();
$iterator->expects($this->once())
$source = $this->getMock('Drupal\migrate\Plugin\MigrateSourceInterface');
$source->expects($this->once())
->method('rewind')
->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())
->method('getSourcePlugin')
......@@ -500,20 +496,25 @@ public function testProcessRowEmptyPipeline() {
/**
* 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() {
$iterator = $this->getMock('\Iterator');
$source = $this->getMockBuilder('Drupal\migrate\Source')
$class = 'Drupal\migrate\Plugin\migrate\source\SourcePluginBase';
$source = $this->getMockBuilder($class)
->disableOriginalConstructor()
->getMock();
->setMethods(get_class_methods($class))
->getMockForAbstractClass();
$source->expects($this->any())
->method('getIterator')
->will($this->returnValue($iterator));
$source->expects($this->once())
->method('rewind')
->will($this->returnValue(TRUE));
$source->expects($this->any())
->method('initializeIterator')
->will($this->returnValue([]));
$source->expects($this->any())
->method('valid')
->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 @@
namespace Drupal\Tests\migrate\Unit;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\migrate\Source;
/**
* Base class for Migrate module source unit tests.
*/
......@@ -87,13 +84,7 @@ protected function setUp() {
$migration->expects($this->any())
->method('getSourcePlugin')
->will($this->returnValue($plugin));
$migrateExecutable = $this->getMockBuilder('Drupal\migrate\MigrateExecutable')
->disableOriginalConstructor()
->getMock();
$this->source = new TestSource($migration, $migrateExecutable);
$cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->source->setCache($cache);
$this->source = $plugin;
}
/**
......@@ -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() {
/**
* Allows access to set protected source property.
*
* @param \Drupal\migrate\Source $source
* @param \Drupal\migrate\Plugin\MigrateSourceInterface $source
* The value to set.
*/
public function setSource($source) {
......
......@@ -39,7 +39,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
return new \ArrayIterator(array($this->values()));
}
......
......@@ -45,10 +45,10 @@ public function query() {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$this->defaultTheme = $this->variableGet('theme_default', 'Garland');
$this->adminTheme = $this->variableGet('admin_theme', null);
return parent::runQuery();
return parent::initializeIterator();
}
/**
......
......@@ -19,7 +19,7 @@ class CommentVariable extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
return new \ArrayIterator($this->getCommentVariables());
}
......
......@@ -18,7 +18,7 @@ class ContactSettings extends Variable {
/**
* {@inheritdoc}
*/
function runQuery() {
function initializeIterator() {
$default_category = $this->select('contact', 'c')
->fields('c', array('cid'))
->condition('selected', 1)
......
......@@ -21,7 +21,7 @@ class FieldInstancePerFormDisplay extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$rows = array();
$result = $this->prepareQuery()->execute();
while ($field_row = $result->fetchAssoc()) {
......
......@@ -20,7 +20,7 @@ class FieldInstancePerViewMode extends ViewModeBase {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$rows = array();
$result = $this->prepareQuery()->execute();
while ($field_row = $result->fetchAssoc()) {
......
......@@ -62,14 +62,14 @@ public function query() {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$conf_path = isset($this->configuration['conf_path']) ? $this->configuration['conf_path'] : 'sites/default';
$this->filePath = $this->variableGet('file_directory_path', $conf_path . '/files') . '/';
$this->tempFilePath = $this->variableGet('file_directory_temp', '/tmp') . '/';
// FILE_DOWNLOADS_PUBLIC == 1 and FILE_DOWNLOADS_PRIVATE == 2.
$this->isPublic = $this->variableGet('file_downloads', 1) == 1;
return parent::runQuery();
return parent::initializeIterator();
}
/**
......
......@@ -75,9 +75,9 @@ public function query() {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$this->filterDefaultFormat = $this->variableGet('filter_default_format', '1');
return parent::runQuery();
return parent::initializeIterator();
}
/**
......
......@@ -88,11 +88,11 @@ public function fields() {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$this->teaserLength = $this->variableGet('teaser_length', 600);
$this->nodePreview = $this->variableGet('node_preview', 0);
$this->themeSettings = $this->variableGet('theme_settings', array());
return parent::runQuery();
return parent::initializeIterator();
}
/**
......
......@@ -49,7 +49,7 @@ public function fields() {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$filter_roles = $this->select('filter_formats', 'f')
->fields('f', array('format', 'roles'))
->execute()
......@@ -61,7 +61,7 @@ protected function runQuery() {
$this->filterPermissions[$rid][] = $format;
}
}
return parent::runQuery();
return parent::initializeIterator();
}
/**
......
......@@ -22,7 +22,7 @@ class UploadInstance extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$prefix = 'upload';
$node_types = $this->getDatabase()->query('SELECT type FROM {node_type}')->fetchCol();
foreach ($node_types as $node_type) {
......
......@@ -46,11 +46,11 @@ public function query() {
/**
* {@inheritdoc}
*/
public function runQuery() {
public function initializeIterator() {
$conf_path = isset($this->configuration['conf_path']) ? $this->configuration['conf_path'] : 'sites/default';
$this->filePath = $this->variableGet('file_directory_path', $conf_path . '/files') . '/';
$this->tempFilePath = $this->variableGet('file_directory_temp', '/tmp') . '/';
return parent::runQuery();
return parent::initializeIterator();
}
/**
......
......@@ -23,7 +23,7 @@ class UserPictureInstance extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function runQuery() {
public function initializeIterator() {
return new \ArrayIterator(array(
array(
'id' => '',
......
......@@ -20,7 +20,7 @@ class ViewMode extends ViewModeBase {
/**
* {@inheritdoc}
*/
protected function runQuery() {
protected function initializeIterator() {
$rows = array();
$result = $this->prepareQuery()->execute();
while ($field_row = $result->fetchAssoc()) {
......
......@@ -19,7 +19,7 @@ abstract class ViewModeBase extends DrupalSqlBase {
* {@inheritdoc}
*/
public function count() {
return count($this->runQuery());
return count($this->initializeIterator());
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment