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