only in patch2: unchanged: --- a/core/modules/migrate_drupal/tests/src/Traits/CreateTestContentEntitiesTrait.php +++ b/core/modules/migrate_drupal/tests/src/Traits/CreateTestContentEntitiesTrait.php @@ -57,7 +57,7 @@ protected function installEntitySchemas() { } /** - * Create several pieces of generic content. + * Adds aggregator content to the created content. */ protected function createContent() { // Create an aggregator feed. @@ -74,7 +74,13 @@ protected function createContent() { 'link' => 'http://www.example.com', ]); $item->save(); + $this->createContentUpgradeTest(); + } + /** + * Create several pieces of generic content. + */ + protected function createContentUpgradeTest() { // Create a block content. $block = BlockContent::create([ 'info' => 'block', only in patch2: unchanged: --- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php @@ -40,7 +40,6 @@ 'content_translation', 'migrate_drupal_ui', 'telephone', - 'aggregator', 'book', 'forum', 'statistics', @@ -59,7 +58,7 @@ protected function setUp() { $this->drupalLogin($this->rootUser); // Create content. - $this->createContent(); + $this->createContentUpgradeTest(); } /** @@ -118,6 +117,12 @@ protected function tearDown() { /** * Executes all steps of migrations upgrade. + * + * The upgrade is started three times. The first time is to test that + * providing incorrect database credentials fails as expected. The second + * time is to run the migration and assert the results. The third time is + * to test an incremental migration, by installing the aggregator module, + * and assert the results. */ public function testMigrateUpgrade() { $connection_options = $this->sourceDatabase->getConnectionOptions(); @@ -160,19 +165,7 @@ public function testMigrateUpgrade() { $this->assertText('Resolve the issue below to continue the upgrade.'); $this->drupalPostForm(NULL, $edits, t('Review upgrade')); - $session->pageTextContains('WARNING: Content may be overwritten on your new site.'); - $session->pageTextContains('There is conflicting content of these types:'); - $session->pageTextContains('aggregator feed entities'); - $session->pageTextContains('aggregator feed item entities'); - $session->pageTextContains('custom block entities'); - $session->pageTextContains('custom menu link entities'); - $session->pageTextContains('file entities'); - $session->pageTextContains('taxonomy term entities'); - $session->pageTextContains('user entities'); - $session->pageTextContains('comments'); - $session->pageTextContains('content item revisions'); - $session->pageTextContains('content items'); - $session->pageTextContains('There is translated content of these types:'); + $this->assertIdConflict($session); $this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.')); $this->assertResponse(200); $this->assertText('Upgrade analysis report'); @@ -187,78 +180,48 @@ public function testMigrateUpgrade() { // Restart the upgrade process. $this->drupalGet('/upgrade'); $session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.'); - $this->drupalPostForm(NULL, [], t('Continue')); $session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.'); $session->fieldExists('mysql[host]'); - $this->drupalPostForm(NULL, $edits, t('Review upgrade')); - $session->pageTextContains('WARNING: Content may be overwritten on your new site.'); + $this->assertIdConflict($session); + $this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.')); $session->statusCodeEquals(200); - $session->pageTextContains('Upgrade analysis report'); - // Ensure there are no errors about the missing modules from the test module. - $session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.')); - $session->pageTextNotContains(t('Source module not found for migration_provider_test.')); - $session->pageTextNotContains(t('Destination module not found for migration_provider_test')); - // Ensure there are no errors about any other missing migration providers. - $session->pageTextNotContains(t('module not found')); - - // Test the available migration paths. $all_available = $this->getAvailablePaths(); - foreach ($all_available as $available) { - $session->elementExists('xpath', "//span[contains(@class, 'checked') and text() = '$available']"); - $session->elementNotExists('xpath', "//span[contains(@class, 'warning') and text() = '$available']"); - } - - // Test the missing migration paths. $all_missing = $this->getMissingPaths(); - foreach ($all_missing as $missing) { - $session->elementExists('xpath', "//span[contains(@class, 'warning') and text() = '$missing']"); - $session->elementNotExists('xpath', "//span[contains(@class, 'checked') and text() = '$missing']"); - } - + $this->assertReviewPage($session, $all_available, $all_missing); $this->drupalPostForm(NULL, [], t('Perform upgrade')); $this->assertText(t('Congratulations, you upgraded Drupal!')); - // Have to reset all the statics after migration to ensure entities are - // loadable. - $this->resetAll(); - - $expected_counts = $this->getEntityCounts(); - foreach (array_keys(\Drupal::entityTypeManager() - ->getDefinitions()) as $entity_type) { - $real_count = \Drupal::entityQuery($entity_type)->count()->execute(); - $expected_count = isset($expected_counts[$entity_type]) ? $expected_counts[$entity_type] : 0; - $this->assertEqual($expected_count, $real_count, "Found $real_count $entity_type entities, expected $expected_count."); - } - - $plugin_manager = \Drupal::service('plugin.manager.migration'); - /** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */ - $all_migrations = $plugin_manager->createInstancesByTag('Drupal ' . $version); - foreach ($all_migrations as $migration) { - $id_map = $migration->getIdMap(); - foreach ($id_map as $source_id => $map) { - // Convert $source_id into a keyless array so that - // \Drupal\migrate\Plugin\migrate\id_map\Sql::getSourceHash() works as - // expected. - $source_id_values = array_values(unserialize($source_id)); - $row = $id_map->getRowBySource($source_id_values); - $destination = serialize($id_map->currentDestination()); - $message = "Migration of $source_id to $destination as part of the {$migration->id()} migration. The source row status is " . $row['source_row_status']; - // A completed migration should have maps with - // MigrateIdMapInterface::STATUS_IGNORED or - // MigrateIdMapInterface::STATUS_IMPORTED. - if ($row['source_row_status'] == MigrateIdMapInterface::STATUS_FAILED || $row['source_row_status'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE) { - $this->fail($message); - } - else { - $this->pass($message); - } - } - } \Drupal::service('module_installer')->install(['forum']); \Drupal::service('module_installer')->install(['book']); + + // Install aggregator module. + \Drupal::service('module_installer')->install(['aggregator']); + + // Test incremental migration. + // Need to update available an dmissing path lists. + $this->drupalGet('/upgrade'); + $session->responseContains('An upgrade has already been performed on this site. To perform a new migration, create a clean and empty new install of Drupal 8. Rollbacks are not yet supported through the user interface.'); + $this->drupalPostForm(NULL, [], t('Import new configuration and content from old site')); + $session->pageTextContains('WARNING: Content may be overwritten on your new site.'); + $session->pageTextContains('There is conflicting content of these types:'); + $session->pageTextContains('file entities'); + $session->pageTextContains('content item revisions'); + $session->pageTextContains('There is translated content of these types:'); + $session->pageTextContains('content items'); + + $this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.')); + $session->statusCodeEquals(200); + $all_available = $this->getAvailablePaths(); + $all_available[] = 'aggregator'; + $all_missing = $this->getMissingPaths(); + $all_missing = array_diff($all_missing, ['aggregator']); + $this->assertReviewPage($session, $all_available, $all_missing); + $this->drupalPostForm(NULL, [], t('Perform upgrade')); + $session->pageTextContains(t('Congratulations, you upgraded Drupal!')); + $this->assertMigrationResults($this->getEntityCountsIncremental(), $version); } /** @@ -315,4 +278,111 @@ protected function translatePostValues(array $values) { */ abstract protected function getMissingPaths(); + /** + * Gets the expected number of entities per entity type after incremental. + * + * @return int[] + * An array of expected counts keyed by entity type ID. + */ + abstract protected function getEntityCountsIncremental(); + + + /** + * Helper method to assert the text on the 'Upgrade analysis report' page. + * + * @param $session + * The currenct session. + * @param $all_available + * Array of modules that will be upgraded. + * @param $all_missing + * Array of modules that will not be upgraded. + */ + protected function assertReviewPage($session, $all_available, $all_missing) { + $this->assertText('Upgrade analysis report'); + + // Ensure there are no errors about the missing modules from the test module. + $session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.')); + $session->pageTextNotContains(t('Source module not found for migration_provider_test.')); + $session->pageTextNotContains(t('Destination module not found for migration_provider_test')); + // Ensure there are no errors about any other missing migration providers. + $session->pageTextNotContains(t('module not found')); + + // Test the available migration paths. + foreach ($all_available as $available) { + $session->elementExists('xpath', "//span[contains(@class, 'checked') and text() = '$available']"); + $session->elementNotExists('xpath', "//span[contains(@class, 'warning') and text() = '$available']"); + } + + // Test the missing migration paths. + foreach ($all_missing as $missing) { + $session->elementExists('xpath', "//span[contains(@class, 'warning') and text() = '$missing']"); + $session->elementNotExists('xpath', "//span[contains(@class, 'checked') and text() = '$missing']"); + } + } + + /** + * Helper method that asserts text on the ID conflict form. + * + * @param $session + * The currenct session. + */ + protected function assertIdConflict($session) { + $session->pageTextContains('WARNING: Content may be overwritten on your new site.'); + $session->pageTextContains('There is conflicting content of these types:'); + $session->pageTextContains('custom block entities'); + $session->pageTextContains('custom menu link entities'); + $session->pageTextContains('file entities'); + $session->pageTextContains('taxonomy term entities'); + $session->pageTextContains('user entities'); + $session->pageTextContains('comments'); + $session->pageTextContains('content item revisions'); + $session->pageTextContains('content items'); + $session->pageTextContains('There is translated content of these types:'); + } + + /** + * Checks that migrations have been performed successfully. + * + * @param array $expected_counts + * The expected counts of each entity type. + * @param string $version + * The drupal verison. + * + */ + protected function assertMigrationResults(array $expected_counts, $version) { + // Have to reset all the statics after migration to ensure entities are + // loadable. + $this->resetAll(); + foreach (array_keys(\Drupal::entityTypeManager()->getDefinitions()) as $entity_type) { + $real_count = (int) \Drupal::entityQuery($entity_type)->count()->execute(); + $expected_count = isset($expected_counts[$entity_type]) ? $expected_counts[$entity_type] : 0; + $this->assertSame($expected_count, $real_count, "Found $real_count $entity_type entities, expected $expected_count."); + } + $plugin_manager = \Drupal::service('plugin.manager.migration'); + /** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */ + $all_migrations = $plugin_manager->createInstancesByTag('Drupal ' . $version); + foreach ($all_migrations as $migration) { + $id_map = $migration->getIdMap(); + foreach ($id_map as $source_id => $map) { + // Convert $source_id into a keyless array so that + // \Drupal\migrate\Plugin\migrate\id_map\Sql::getSourceHash() works as + // expected. + $source_id_values = array_values(unserialize($source_id)); + $row = $id_map->getRowBySource($source_id_values); + $destination = serialize($id_map->currentDestination()); + $message = "Migration of $source_id to $destination as part of the {$migration->id()} migration. The source row status is " . $row['source_row_status']; + // A completed migration should have maps with + // MigrateIdMapInterface::STATUS_IGNORED or + // MigrateIdMapInterface::STATUS_IMPORTED. + if ($row['source_row_status'] == MigrateIdMapInterface::STATUS_FAILED || $row['source_row_status'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE) { + $this->fail($message); + } + else { + $this->pass($message); + } + } + } + + } + } only in patch2: unchanged: --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php @@ -69,22 +69,39 @@ protected function getEntityCounts() { 'user' => 7, 'user_role' => 6, 'menu_link_content' => 5, - 'view' => 16, + 'view' => 14, 'date_format' => 11, 'entity_form_display' => 29, 'entity_form_mode' => 1, - 'entity_view_display' => 53, - 'entity_view_mode' => 14, + 'entity_view_display' => 50, + 'entity_view_mode' => 12, 'base_field_override' => 38, ]; } + /** + * {@inheritdoc} + */ + protected function getEntityCountsIncremental() { + $counts = $this->getEntityCounts(); + $counts['aggregator_feed'] = 1; + $counts['aggregator_item'] = 1; + $counts['base_field_override'] = 38; + $counts['entity_form_display'] = 29; + $counts['entity_view_display'] = 53; + $counts['entity_view_mode'] = 14; + $counts['field_config'] = 84; + $counts['field_storage_config'] = 58; + $counts['node_type'] = 13; + $counts['rdf_mapping'] = 7; + $counts['view'] = 16; + return $counts; + } /** * {@inheritdoc} */ protected function getAvailablePaths() { return [ - 'aggregator', 'block', 'book', 'comment', @@ -129,6 +146,7 @@ protected function getAvailablePaths() { */ protected function getMissingPaths() { return [ + 'aggregator', 'date_api', 'date_timezone', 'event', only in patch2: unchanged: --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php @@ -39,8 +39,6 @@ protected function getSourceBasePath() { */ protected function getEntityCounts() { return [ - 'aggregator_item' => 11, - 'aggregator_feed' => 1, 'block' => 25, 'block_content' => 1, 'block_content_type' => 1, @@ -73,12 +71,12 @@ protected function getEntityCounts() { 'user' => 4, 'user_role' => 3, 'menu_link_content' => 8, - 'view' => 16, + 'view' => 14, 'date_format' => 11, 'entity_form_display' => 17, 'entity_form_mode' => 1, - 'entity_view_display' => 28, - 'entity_view_mode' => 14, + 'entity_view_display' => 25, + 'entity_view_mode' => 12, 'base_field_override' => 9, ]; } @@ -86,9 +84,27 @@ protected function getEntityCounts() { /** * {@inheritdoc} */ + protected function getEntityCountsIncremental() { + $counts = $this->getEntityCounts(); + $counts['aggregator_feed'] = 1; + $counts['aggregator_item'] = 10; + $counts['base_field_override'] = 9; + $counts['entity_form_display'] = 17; + $counts['entity_view_display'] = 28; + $counts['entity_view_mode'] = 14; + $counts['field_config'] = 61; + $counts['field_storage_config'] = 44; + $counts['rdf_mapping'] = 7; + $counts['taxonomy_term'] = 18; + $counts['view'] = 16; + return $counts; + } + + /** + * {@inheritdoc} + */ protected function getAvailablePaths() { return [ - 'aggregator', 'block', 'comment', 'contact', @@ -132,6 +148,7 @@ protected function getAvailablePaths() { */ protected function getMissingPaths() { return [ + 'aggregator', 'blog', 'book', 'color',