Chris@0: createMigrationConnection(); Chris@0: $this->sourceDatabase = Database::getConnection('default', 'migrate'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Changes the database connection to the prefixed one. Chris@0: * Chris@0: * @todo Remove when we don't use global. https://www.drupal.org/node/2552791 Chris@0: */ Chris@0: private function createMigrationConnection() { Chris@0: // If the backup already exists, something went terribly wrong. Chris@0: // This case is possible, because database connection info is a static Chris@0: // global state construct on the Database class, which at least persists Chris@0: // for all test methods executed in one PHP process. Chris@0: if (Database::getConnectionInfo('simpletest_original_migrate')) { Chris@0: throw new \RuntimeException("Bad Database connection state: 'simpletest_original_migrate' connection key already exists. Broken test?"); Chris@0: } Chris@0: Chris@0: // Clone the current connection and replace the current prefix. Chris@0: $connection_info = Database::getConnectionInfo('migrate'); Chris@0: if ($connection_info) { Chris@0: Database::renameConnection('migrate', 'simpletest_original_migrate'); Chris@0: } Chris@0: $connection_info = Database::getConnectionInfo('default'); Chris@0: foreach ($connection_info as $target => $value) { Chris@0: $prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix']; Chris@0: // Simpletest uses 7 character prefixes at most so this can't cause Chris@0: // collisions. Chris@0: $connection_info[$target]['prefix']['default'] = $prefix . '0'; Chris@0: Chris@0: // Add the original simpletest prefix so SQLite can attach its database. Chris@0: // @see \Drupal\Core\Database\Driver\sqlite\Connection::init() Chris@0: $connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default']; Chris@0: } Chris@0: Database::addConnectionInfo('migrate', 'default', $connection_info['default']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function tearDown() { Chris@0: $this->cleanupMigrateConnection(); Chris@0: parent::tearDown(); Chris@0: $this->databaseDumpFiles = []; Chris@0: $this->collectMessages = FALSE; Chris@0: unset($this->migration, $this->migrateMessages); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Cleans up the test migrate connection. Chris@0: * Chris@0: * @todo Remove when we don't use global. https://www.drupal.org/node/2552791 Chris@0: */ Chris@0: private function cleanupMigrateConnection() { Chris@0: Database::removeConnection('migrate'); Chris@0: $original_connection_info = Database::getConnectionInfo('simpletest_original_migrate'); Chris@0: if ($original_connection_info) { Chris@0: Database::renameConnection('simpletest_original_migrate', 'migrate'); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Prepare any dependent migrations. Chris@0: * Chris@0: * @param array $id_mappings Chris@0: * A list of ID mappings keyed by migration IDs. Each ID mapping is a list Chris@0: * of two arrays, the first are source IDs and the second are destination Chris@0: * IDs. Chris@0: */ Chris@0: protected function prepareMigrations(array $id_mappings) { Chris@0: $manager = $this->container->get('plugin.manager.migration'); Chris@0: foreach ($id_mappings as $migration_id => $data) { Chris@0: foreach ($manager->createInstances($migration_id) as $migration) { Chris@0: $id_map = $migration->getIdMap(); Chris@0: $id_map->setMessage($this); Chris@0: $source_ids = $migration->getSourcePlugin()->getIds(); Chris@0: foreach ($data as $id_mapping) { Chris@0: $row = new Row(array_combine(array_keys($source_ids), $id_mapping[0]), $source_ids); Chris@0: $id_map->saveIdMapping($row, $id_mapping[1]); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Modify a migration's configuration before executing it. Chris@0: * Chris@0: * @param \Drupal\migrate\Plugin\MigrationInterface $migration Chris@0: * The migration to execute. Chris@0: */ Chris@0: protected function prepareMigration(MigrationInterface $migration) { Chris@0: // Default implementation for test classes not requiring modification. Chris@0: } Chris@0: Chris@0: /** Chris@0: * Executes a single migration. Chris@0: * Chris@0: * @param string|\Drupal\migrate\Plugin\MigrationInterface $migration Chris@0: * The migration to execute, or its ID. Chris@0: */ Chris@0: protected function executeMigration($migration) { Chris@0: if (is_string($migration)) { Chris@0: $this->migration = $this->getMigration($migration); Chris@0: } Chris@0: else { Chris@0: $this->migration = $migration; Chris@0: } Chris@0: if ($this instanceof MigrateDumpAlterInterface) { Chris@0: static::migrateDumpAlter($this); Chris@0: } Chris@0: Chris@0: $this->prepareMigration($this->migration); Chris@0: (new MigrateExecutable($this->migration, $this))->import(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Executes a set of migrations in dependency order. Chris@0: * Chris@0: * @param string[] $ids Chris@0: * Array of migration IDs, in any order. Chris@0: */ Chris@0: protected function executeMigrations(array $ids) { Chris@0: $manager = $this->container->get('plugin.manager.migration'); Chris@0: array_walk($ids, function ($id) use ($manager) { Chris@0: // This is possibly a base plugin ID and we want to run all derivatives. Chris@0: $instances = $manager->createInstances($id); Chris@17: $this->assertNotEmpty($instances, sprintf("No migrations created for id '%s'.", $id)); Chris@0: array_walk($instances, [$this, 'executeMigration']); Chris@0: }); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function display($message, $type = 'status') { Chris@0: if ($this->collectMessages) { Chris@0: $this->migrateMessages[$type][] = $message; Chris@0: } Chris@0: else { Chris@0: $this->assert($type == 'status', $message, 'migrate'); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Start collecting messages and erase previous messages. Chris@0: */ Chris@0: public function startCollectingMessages() { Chris@0: $this->collectMessages = TRUE; Chris@0: $this->migrateMessages = []; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Stop collecting messages. Chris@0: */ Chris@0: public function stopCollectingMessages() { Chris@0: $this->collectMessages = FALSE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Records a failure in the map table of a specific migration. Chris@0: * Chris@0: * This is done in order to test scenarios which require a failed row. Chris@0: * Chris@0: * @param string|\Drupal\migrate\Plugin\MigrationInterface $migration Chris@0: * The migration entity, or its ID. Chris@0: * @param array $row Chris@0: * The raw source row which "failed". Chris@0: * @param int $status Chris@0: * (optional) The failure status. Should be one of the Chris@0: * MigrateIdMapInterface::STATUS_* constants. Defaults to Chris@0: * MigrateIdMapInterface::STATUS_FAILED. Chris@0: */ Chris@0: protected function mockFailure($migration, array $row, $status = MigrateIdMapInterface::STATUS_FAILED) { Chris@0: if (is_string($migration)) { Chris@0: $migration = $this->getMigration($migration); Chris@0: } Chris@0: /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ Chris@0: $destination = array_map(function () { Chris@0: return NULL; Chris@0: }, $migration->getDestinationPlugin()->getIds()); Chris@0: $row = new Row($row, $migration->getSourcePlugin()->getIds()); Chris@0: $migration->getIdMap()->saveIdMapping($row, $destination, $status); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the migration plugin. Chris@0: * Chris@0: * @param $plugin_id Chris@0: * The plugin ID of the migration to get. Chris@0: * Chris@0: * @return \Drupal\migrate\Plugin\Migration Chris@0: * The migration plugin. Chris@0: */ Chris@0: protected function getMigration($plugin_id) { Chris@0: return $this->container->get('plugin.manager.migration')->createInstance($plugin_id); Chris@0: } Chris@0: Chris@0: }