3 namespace Drupal\migrate_drupal_ui\Tests;
5 use Drupal\Core\Database\Database;
6 use Drupal\migrate\Plugin\MigrateIdMapInterface;
7 use Drupal\migrate_drupal\MigrationConfigurationTrait;
8 use Drupal\simpletest\WebTestBase;
11 * Provides a base class for testing migration upgrades in the UI.
13 abstract class MigrateUpgradeTestBase extends WebTestBase {
14 use MigrationConfigurationTrait;
17 * Use the Standard profile to test help implementations of many core modules.
19 protected $profile = 'standard';
22 * The source database connection.
24 * @var \Drupal\Core\Database\Connection
26 protected $sourceDatabase;
33 public static $modules = [
35 'content_translation',
47 protected function setUp() {
49 $this->createMigrationConnection();
50 $this->sourceDatabase = Database::getConnection('default', 'migrate_drupal_ui');
52 // Log in as user 1. Migrations in the UI can only be performed as user 1.
53 $this->drupalLogin($this->rootUser);
57 * Loads a database fixture into the source database connection.
60 * Path to the dump file.
62 protected function loadFixture($path) {
63 $default_db = Database::getConnection()->getKey();
64 Database::setActiveConnection($this->sourceDatabase->getKey());
66 if (substr($path, -3) == '.gz') {
67 $path = 'compress.zlib://' . $path;
71 Database::setActiveConnection($default_db);
75 * Changes the database connection to the prefixed one.
77 * @todo Remove when we don't use global. https://www.drupal.org/node/2552791
79 protected function createMigrationConnection() {
80 $connection_info = Database::getConnectionInfo('default')['default'];
81 if ($connection_info['driver'] === 'sqlite') {
82 // Create database file in the test site's public file directory so that
83 // \Drupal\simpletest\TestBase::restoreEnvironment() will delete this once
84 // the test is complete.
85 $file = $this->publicFilesDirectory . '/' . $this->testId . '-migrate.db.sqlite';
87 $connection_info['database'] = $file;
88 $connection_info['prefix'] = '';
91 $prefix = is_array($connection_info['prefix']) ? $connection_info['prefix']['default'] : $connection_info['prefix'];
92 // Simpletest uses fixed length prefixes. Create a new prefix for the
93 // source database. Adding to the end of the prefix ensures that
94 // \Drupal\simpletest\TestBase::restoreEnvironment() will remove the
96 $connection_info['prefix'] = $prefix . '0';
99 Database::addConnectionInfo('migrate_drupal_ui', 'default', $connection_info);
105 protected function tearDown() {
106 Database::removeConnection('migrate_drupal_ui');
111 * Executes all steps of migrations upgrade.
113 public function testMigrateUpgrade() {
114 $connection_options = $this->sourceDatabase->getConnectionOptions();
115 $this->drupalGet('/upgrade');
116 $this->assertText('Upgrade a site by importing it into a clean and empty new install of Drupal 8. You will lose any existing configuration once you import your site into it. See the online documentation for Drupal site upgrades for more detailed information.');
118 $this->drupalPostForm(NULL, [], t('Continue'));
119 $this->assertText('Provide credentials for the database of the Drupal site you want to upgrade.');
120 $this->assertFieldByName('mysql[host]');
122 $driver = $connection_options['driver'];
123 $connection_options['prefix'] = $connection_options['prefix']['default'];
125 // Use the driver connection form to get the correct options out of the
126 // database settings. This supports all of the databases we test against.
127 $drivers = drupal_get_database_types();
128 $form = $drivers[$driver]->getFormOptions($connection_options);
129 $connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
131 $driver => $connection_options,
132 'source_base_path' => $this->getSourceBasePath(),
134 if (count($drivers) !== 1) {
135 $edit['driver'] = $driver;
137 $edits = $this->translatePostValues($edit);
139 // Ensure submitting the form with invalid database credentials gives us a
141 $this->drupalPostForm(NULL, [$driver . '[database]' => 'wrong'] + $edits, t('Review upgrade'));
142 $this->assertText('Resolve the issue below to continue the upgrade.');
144 $this->drupalPostForm(NULL, $edits, t('Review upgrade'));
145 $this->assertResponse(200);
146 $this->assertText('Are you sure?');
147 $this->drupalPostForm(NULL, [], t('Perform upgrade'));
148 $this->assertText(t('Congratulations, you upgraded Drupal!'));
150 // Have to reset all the statics after migration to ensure entities are
154 $expected_counts = $this->getEntityCounts();
155 foreach (array_keys(\Drupal::entityTypeManager()
156 ->getDefinitions()) as $entity_type) {
157 $real_count = \Drupal::entityQuery($entity_type)->count()->execute();
158 $expected_count = isset($expected_counts[$entity_type]) ? $expected_counts[$entity_type] : 0;
159 $this->assertEqual($expected_count, $real_count, "Found $real_count $entity_type entities, expected $expected_count.");
162 $version_tag = 'Drupal ' . $this->getLegacyDrupalVersion($this->sourceDatabase);
163 $plugin_manager = \Drupal::service('plugin.manager.migration');
164 /** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */
165 $all_migrations = $plugin_manager->createInstancesByTag($version_tag);
166 foreach ($all_migrations as $migration) {
167 $id_map = $migration->getIdMap();
168 foreach ($id_map as $source_id => $map) {
169 // Convert $source_id into a keyless array so that
170 // \Drupal\migrate\Plugin\migrate\id_map\Sql::getSourceHash() works as
172 $source_id_values = array_values(unserialize($source_id));
173 $row = $id_map->getRowBySource($source_id_values);
174 $destination = serialize($id_map->currentDestination());
175 $message = "Migration of $source_id to $destination as part of the {$migration->id()} migration. The source row status is " . $row['source_row_status'];
176 // A completed migration should have maps with
177 // MigrateIdMapInterface::STATUS_IGNORED or
178 // MigrateIdMapInterface::STATUS_IMPORTED.
179 if ($row['source_row_status'] == MigrateIdMapInterface::STATUS_FAILED || $row['source_row_status'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE) {
180 $this->fail($message);
183 $this->pass($message);
187 \Drupal::service('module_installer')->install(['forum']);
188 \Drupal::service('module_installer')->install(['book']);
192 * Gets the source base path for the concrete test.
195 * The source base path.
197 abstract protected function getSourceBasePath();
200 * Gets the expected number of entities per entity type after migration.
203 * An array of expected counts keyed by entity type ID.
205 abstract protected function getEntityCounts();