annotate core/modules/migrate/tests/src/Kernel/MigrateTestBase.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\migrate\Kernel;
Chris@0 4
Chris@0 5 use Drupal\Core\Database\Database;
Chris@0 6 use Drupal\KernelTests\KernelTestBase;
Chris@0 7 use Drupal\migrate\MigrateExecutable;
Chris@0 8 use Drupal\migrate\MigrateMessageInterface;
Chris@0 9 use Drupal\migrate\Plugin\MigrateIdMapInterface;
Chris@0 10 use Drupal\migrate\Plugin\Migration;
Chris@0 11 use Drupal\migrate\Plugin\MigrationInterface;
Chris@0 12 use Drupal\migrate\Row;
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Creates abstract base class for migration tests.
Chris@0 16 */
Chris@0 17 abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageInterface {
Chris@0 18
Chris@0 19 /**
Chris@0 20 * TRUE to collect messages instead of displaying them.
Chris@0 21 *
Chris@0 22 * @var bool
Chris@0 23 */
Chris@0 24 protected $collectMessages = FALSE;
Chris@0 25
Chris@0 26 /**
Chris@0 27 * A two dimensional array of messages.
Chris@0 28 *
Chris@0 29 * The first key is the type of message, the second is just numeric. Values
Chris@0 30 * are the messages.
Chris@0 31 *
Chris@0 32 * @var array
Chris@0 33 */
Chris@0 34 protected $migrateMessages;
Chris@0 35
Chris@0 36 /**
Chris@0 37 * The primary migration being tested.
Chris@0 38 *
Chris@0 39 * @var \Drupal\migrate\Plugin\MigrationInterface
Chris@0 40 */
Chris@0 41 protected $migration;
Chris@0 42
Chris@0 43 /**
Chris@0 44 * The source database connection.
Chris@0 45 *
Chris@0 46 * @var \Drupal\Core\Database\Connection
Chris@0 47 */
Chris@0 48 protected $sourceDatabase;
Chris@0 49
Chris@0 50 public static $modules = ['migrate'];
Chris@0 51
Chris@0 52 /**
Chris@0 53 * {@inheritdoc}
Chris@0 54 */
Chris@0 55 protected function setUp() {
Chris@0 56 parent::setUp();
Chris@0 57 $this->createMigrationConnection();
Chris@0 58 $this->sourceDatabase = Database::getConnection('default', 'migrate');
Chris@0 59 }
Chris@0 60
Chris@0 61 /**
Chris@0 62 * Changes the database connection to the prefixed one.
Chris@0 63 *
Chris@0 64 * @todo Remove when we don't use global. https://www.drupal.org/node/2552791
Chris@0 65 */
Chris@0 66 private function createMigrationConnection() {
Chris@0 67 // If the backup already exists, something went terribly wrong.
Chris@0 68 // This case is possible, because database connection info is a static
Chris@0 69 // global state construct on the Database class, which at least persists
Chris@0 70 // for all test methods executed in one PHP process.
Chris@0 71 if (Database::getConnectionInfo('simpletest_original_migrate')) {
Chris@0 72 throw new \RuntimeException("Bad Database connection state: 'simpletest_original_migrate' connection key already exists. Broken test?");
Chris@0 73 }
Chris@0 74
Chris@0 75 // Clone the current connection and replace the current prefix.
Chris@0 76 $connection_info = Database::getConnectionInfo('migrate');
Chris@0 77 if ($connection_info) {
Chris@0 78 Database::renameConnection('migrate', 'simpletest_original_migrate');
Chris@0 79 }
Chris@0 80 $connection_info = Database::getConnectionInfo('default');
Chris@0 81 foreach ($connection_info as $target => $value) {
Chris@0 82 $prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix'];
Chris@0 83 // Simpletest uses 7 character prefixes at most so this can't cause
Chris@0 84 // collisions.
Chris@0 85 $connection_info[$target]['prefix']['default'] = $prefix . '0';
Chris@0 86
Chris@0 87 // Add the original simpletest prefix so SQLite can attach its database.
Chris@0 88 // @see \Drupal\Core\Database\Driver\sqlite\Connection::init()
Chris@0 89 $connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default'];
Chris@0 90 }
Chris@0 91 Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
Chris@0 92 }
Chris@0 93
Chris@0 94 /**
Chris@0 95 * {@inheritdoc}
Chris@0 96 */
Chris@0 97 protected function tearDown() {
Chris@0 98 $this->cleanupMigrateConnection();
Chris@0 99 parent::tearDown();
Chris@0 100 $this->databaseDumpFiles = [];
Chris@0 101 $this->collectMessages = FALSE;
Chris@0 102 unset($this->migration, $this->migrateMessages);
Chris@0 103 }
Chris@0 104
Chris@0 105 /**
Chris@0 106 * Cleans up the test migrate connection.
Chris@0 107 *
Chris@0 108 * @todo Remove when we don't use global. https://www.drupal.org/node/2552791
Chris@0 109 */
Chris@0 110 private function cleanupMigrateConnection() {
Chris@0 111 Database::removeConnection('migrate');
Chris@0 112 $original_connection_info = Database::getConnectionInfo('simpletest_original_migrate');
Chris@0 113 if ($original_connection_info) {
Chris@0 114 Database::renameConnection('simpletest_original_migrate', 'migrate');
Chris@0 115 }
Chris@0 116 }
Chris@0 117
Chris@0 118 /**
Chris@0 119 * Prepare any dependent migrations.
Chris@0 120 *
Chris@0 121 * @param array $id_mappings
Chris@0 122 * A list of ID mappings keyed by migration IDs. Each ID mapping is a list
Chris@0 123 * of two arrays, the first are source IDs and the second are destination
Chris@0 124 * IDs.
Chris@0 125 */
Chris@0 126 protected function prepareMigrations(array $id_mappings) {
Chris@0 127 $manager = $this->container->get('plugin.manager.migration');
Chris@0 128 foreach ($id_mappings as $migration_id => $data) {
Chris@0 129 foreach ($manager->createInstances($migration_id) as $migration) {
Chris@0 130 $id_map = $migration->getIdMap();
Chris@0 131 $id_map->setMessage($this);
Chris@0 132 $source_ids = $migration->getSourcePlugin()->getIds();
Chris@0 133 foreach ($data as $id_mapping) {
Chris@0 134 $row = new Row(array_combine(array_keys($source_ids), $id_mapping[0]), $source_ids);
Chris@0 135 $id_map->saveIdMapping($row, $id_mapping[1]);
Chris@0 136 }
Chris@0 137 }
Chris@0 138 }
Chris@0 139 }
Chris@0 140
Chris@0 141 /**
Chris@0 142 * Modify a migration's configuration before executing it.
Chris@0 143 *
Chris@0 144 * @param \Drupal\migrate\Plugin\MigrationInterface $migration
Chris@0 145 * The migration to execute.
Chris@0 146 */
Chris@0 147 protected function prepareMigration(MigrationInterface $migration) {
Chris@0 148 // Default implementation for test classes not requiring modification.
Chris@0 149 }
Chris@0 150
Chris@0 151 /**
Chris@0 152 * Executes a single migration.
Chris@0 153 *
Chris@0 154 * @param string|\Drupal\migrate\Plugin\MigrationInterface $migration
Chris@0 155 * The migration to execute, or its ID.
Chris@0 156 */
Chris@0 157 protected function executeMigration($migration) {
Chris@0 158 if (is_string($migration)) {
Chris@0 159 $this->migration = $this->getMigration($migration);
Chris@0 160 }
Chris@0 161 else {
Chris@0 162 $this->migration = $migration;
Chris@0 163 }
Chris@0 164 if ($this instanceof MigrateDumpAlterInterface) {
Chris@0 165 static::migrateDumpAlter($this);
Chris@0 166 }
Chris@0 167
Chris@0 168 $this->prepareMigration($this->migration);
Chris@0 169 (new MigrateExecutable($this->migration, $this))->import();
Chris@0 170 }
Chris@0 171
Chris@0 172 /**
Chris@0 173 * Executes a set of migrations in dependency order.
Chris@0 174 *
Chris@0 175 * @param string[] $ids
Chris@0 176 * Array of migration IDs, in any order.
Chris@0 177 */
Chris@0 178 protected function executeMigrations(array $ids) {
Chris@0 179 $manager = $this->container->get('plugin.manager.migration');
Chris@0 180 array_walk($ids, function ($id) use ($manager) {
Chris@0 181 // This is possibly a base plugin ID and we want to run all derivatives.
Chris@0 182 $instances = $manager->createInstances($id);
Chris@17 183 $this->assertNotEmpty($instances, sprintf("No migrations created for id '%s'.", $id));
Chris@0 184 array_walk($instances, [$this, 'executeMigration']);
Chris@0 185 });
Chris@0 186 }
Chris@0 187
Chris@0 188 /**
Chris@0 189 * {@inheritdoc}
Chris@0 190 */
Chris@0 191 public function display($message, $type = 'status') {
Chris@0 192 if ($this->collectMessages) {
Chris@0 193 $this->migrateMessages[$type][] = $message;
Chris@0 194 }
Chris@0 195 else {
Chris@0 196 $this->assert($type == 'status', $message, 'migrate');
Chris@0 197 }
Chris@0 198 }
Chris@0 199
Chris@0 200 /**
Chris@0 201 * Start collecting messages and erase previous messages.
Chris@0 202 */
Chris@0 203 public function startCollectingMessages() {
Chris@0 204 $this->collectMessages = TRUE;
Chris@0 205 $this->migrateMessages = [];
Chris@0 206 }
Chris@0 207
Chris@0 208 /**
Chris@0 209 * Stop collecting messages.
Chris@0 210 */
Chris@0 211 public function stopCollectingMessages() {
Chris@0 212 $this->collectMessages = FALSE;
Chris@0 213 }
Chris@0 214
Chris@0 215 /**
Chris@0 216 * Records a failure in the map table of a specific migration.
Chris@0 217 *
Chris@0 218 * This is done in order to test scenarios which require a failed row.
Chris@0 219 *
Chris@0 220 * @param string|\Drupal\migrate\Plugin\MigrationInterface $migration
Chris@0 221 * The migration entity, or its ID.
Chris@0 222 * @param array $row
Chris@0 223 * The raw source row which "failed".
Chris@0 224 * @param int $status
Chris@0 225 * (optional) The failure status. Should be one of the
Chris@0 226 * MigrateIdMapInterface::STATUS_* constants. Defaults to
Chris@0 227 * MigrateIdMapInterface::STATUS_FAILED.
Chris@0 228 */
Chris@0 229 protected function mockFailure($migration, array $row, $status = MigrateIdMapInterface::STATUS_FAILED) {
Chris@0 230 if (is_string($migration)) {
Chris@0 231 $migration = $this->getMigration($migration);
Chris@0 232 }
Chris@0 233 /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
Chris@0 234 $destination = array_map(function () {
Chris@0 235 return NULL;
Chris@0 236 }, $migration->getDestinationPlugin()->getIds());
Chris@0 237 $row = new Row($row, $migration->getSourcePlugin()->getIds());
Chris@0 238 $migration->getIdMap()->saveIdMapping($row, $destination, $status);
Chris@0 239 }
Chris@0 240
Chris@0 241 /**
Chris@0 242 * Gets the migration plugin.
Chris@0 243 *
Chris@0 244 * @param $plugin_id
Chris@0 245 * The plugin ID of the migration to get.
Chris@0 246 *
Chris@0 247 * @return \Drupal\migrate\Plugin\Migration
Chris@0 248 * The migration plugin.
Chris@0 249 */
Chris@0 250 protected function getMigration($plugin_id) {
Chris@0 251 return $this->container->get('plugin.manager.migration')->createInstance($plugin_id);
Chris@0 252 }
Chris@0 253
Chris@0 254 }