From 8616edd24f8ded10f25a259e8d01244d9fb44f29 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Mon, 18 Oct 2021 09:37:25 +0100
Subject: [PATCH] Issue #2976098 by quietone, alexpott, Sivaji_Ganesh_Jojodae,
 joachim, benjifisher, Wim Leers, mikelutz, catch, dinarcon, scotwith1t,
 heddn: MigrateExecutable should add details for the migration & destination
 property to exceptions that cause a row failure

---
 .../Migrate/d7/MigrateFieldInstanceTest.php   |   8 +-
 .../Kernel/Migrate/d7/MigrateFieldTest.php    |   8 +-
 .../modules/migrate/src/MigrateExecutable.php | 253 ++++++++++--------
 .../src/Plugin/migrate/process/FormatDate.php |   4 +-
 .../process/DownloadFunctionalTest.php        |   4 +-
 .../src/Kernel/MigrateExecutableTest.php      |  27 ++
 .../tests/src/Kernel/MigrateMessageTest.php   |   6 +-
 .../tests/src/Unit/MigrateExecutableTest.php  | 118 --------
 .../tests/src/Unit/process/FormatDateTest.php |   4 +-
 .../migrate/process/ImageStyleMappings.php    |   2 +-
 10 files changed, 194 insertions(+), 240 deletions(-)

diff --git a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php
index 7e0372ceef0a..a8cd47d682c0 100644
--- a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php
+++ b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldInstanceTest.php
@@ -234,16 +234,16 @@ public function testFieldInstances() {
     }, iterator_to_array($migration->getIdMap()->getMessages()));
     $this->assertCount(8, $errors);
     sort($errors);
-    $message = 'Can\'t migrate source field field_text_long_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
+    $message = 'd7_field_instance:type: Can\'t migrate source field field_text_long_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
     $this->assertEquals($errors[0], $message);
     $this->assertEquals($errors[1], $message);
-    $message = 'Can\'t migrate source field field_text_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
+    $message = 'd7_field_instance:type: Can\'t migrate source field field_text_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
     $this->assertEquals($errors[2], $message);
     $this->assertEquals($errors[3], $message);
-    $message = 'Can\'t migrate source field field_text_sum_plain of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
+    $message = 'd7_field_instance:type: Can\'t migrate source field field_text_sum_plain of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
     $this->assertEquals($errors[4], $message);
     $this->assertEquals($errors[5], $message);
-    $message = 'Can\'t migrate source field field_text_sum_plain_filtered of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
+    $message = 'd7_field_instance:type: Can\'t migrate source field field_text_sum_plain_filtered of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text';
     $this->assertEquals($errors[6], $message);
     $this->assertEquals($errors[7], $message);
   }
diff --git a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php
index 1ad4c722cff3..ef6ff99cc278 100644
--- a/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php
+++ b/core/modules/field/tests/src/Kernel/Migrate/d7/MigrateFieldTest.php
@@ -182,10 +182,10 @@ public function testFields() {
     }, iterator_to_array($migration->getIdMap()->getMessages()));
     sort($errors);
     $this->assertCount(4, $errors);
-    $this->assertEquals('Can\'t migrate source field field_text_long_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[0]);
-    $this->assertEquals('Can\'t migrate source field field_text_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[1]);
-    $this->assertEquals('Can\'t migrate source field field_text_sum_plain of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[2]);
-    $this->assertEquals('Can\'t migrate source field field_text_sum_plain_filtered of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[3]);
+    $this->assertEquals('d7_field:type: Can\'t migrate source field field_text_long_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[0]);
+    $this->assertEquals('d7_field:type: Can\'t migrate source field field_text_plain_filtered configured with both plain text and filtered text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[1]);
+    $this->assertEquals('d7_field:type: Can\'t migrate source field field_text_sum_plain of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[2]);
+    $this->assertEquals('d7_field:type: Can\'t migrate source field field_text_sum_plain_filtered of type text_with_summary configured with plain text processing. See https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#plain-text', $errors[3]);
   }
 
 }
diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php
index 2bf383ecd83f..24fe07ae4b5b 100644
--- a/core/modules/migrate/src/MigrateExecutable.php
+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -178,9 +178,7 @@ public function import() {
     }
 
     $this->migration->setStatus(MigrationInterface::STATUS_IMPORTING);
-    $return = MigrationInterface::RESULT_COMPLETED;
     $source = $this->getSource();
-    $id_map = $this->getIdMap();
 
     try {
       $source->rewind();
@@ -196,88 +194,113 @@ public function import() {
       return MigrationInterface::RESULT_FAILED;
     }
 
-    $destination = $this->migration->getDestinationPlugin();
-    while ($source->valid()) {
-      $row = $source->current();
-      $this->sourceIdValues = $row->getSourceIdValues();
-
+    // Get the process pipeline.
+    $pipeline = FALSE;
+    if ($source->valid()) {
       try {
-        $this->processRow($row);
-        $save = TRUE;
+        $pipeline = $this->migration->getProcessPlugins();
       }
       catch (MigrateException $e) {
+        $row = $source->current();
+        $this->sourceIdValues = $row->getSourceIdValues();
         $this->getIdMap()->saveIdMapping($row, [], $e->getStatus());
         $this->saveMessage($e->getMessage(), $e->getLevel());
-        $save = FALSE;
-      }
-      catch (MigrateSkipRowException $e) {
-        if ($e->getSaveToMap()) {
-          $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_IGNORED);
-        }
-        if ($message = trim($e->getMessage())) {
-          $this->saveMessage($message, MigrationInterface::MESSAGE_INFORMATIONAL);
-        }
-        $save = FALSE;
       }
+    }
+
+    $return = MigrationInterface::RESULT_COMPLETED;
+    if ($pipeline) {
+      $id_map = $this->getIdMap();
+      $destination = $this->migration->getDestinationPlugin();
+      while ($source->valid()) {
+        $row = $source->current();
+        $this->sourceIdValues = $row->getSourceIdValues();
 
-      if ($save) {
         try {
-          $this->getEventDispatcher()->dispatch(new MigratePreRowSaveEvent($this->migration, $this->message, $row), MigrateEvents::PRE_ROW_SAVE);
-          $destination_ids = $id_map->lookupDestinationIds($this->sourceIdValues);
-          $destination_id_values = $destination_ids ? reset($destination_ids) : [];
-          $destination_id_values = $destination->import($row, $destination_id_values);
-          $this->getEventDispatcher()->dispatch(new MigratePostRowSaveEvent($this->migration, $this->message, $row, $destination_id_values), MigrateEvents::POST_ROW_SAVE);
-          if ($destination_id_values) {
-            // We do not save an idMap entry for config.
-            if ($destination_id_values !== TRUE) {
-              $id_map->saveIdMapping($row, $destination_id_values, $this->sourceRowStatus, $destination->rollbackAction());
-            }
-          }
-          else {
-            $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_FAILED);
-            if (!$id_map->messageCount()) {
-              $message = $this->t('New object was not saved, no error provided');
-              $this->saveMessage($message);
-              $this->message->display($message);
-            }
+          foreach ($pipeline as $destination_property_name => $plugins) {
+            $this->processPipeline($row, $destination_property_name, $plugins, NULL);
           }
+          $save = TRUE;
         }
         catch (MigrateException $e) {
           $this->getIdMap()->saveIdMapping($row, [], $e->getStatus());
-          $this->saveMessage($e->getMessage(), $e->getLevel());
+          $msg = sprintf("%s:%s: %s", $this->migration->getPluginId(), $destination_property_name, $e->getMessage());
+          $this->saveMessage($msg, $e->getLevel());
+          $save = FALSE;
         }
-        catch (\Exception $e) {
-          $this->getIdMap()->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_FAILED);
-          $this->handleException($e);
+        catch (MigrateSkipRowException $e) {
+          if ($e->getSaveToMap()) {
+            $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_IGNORED);
+          }
+          if ($message = trim($e->getMessage())) {
+            $msg = sprintf("%s:%s: %s", $this->migration->getPluginId(), $destination_property_name, $message);
+            $this->saveMessage($msg, MigrationInterface::MESSAGE_INFORMATIONAL);
+          }
+          $save = FALSE;
         }
-      }
 
-      $this->sourceRowStatus = MigrateIdMapInterface::STATUS_IMPORTED;
+        if ($save) {
+          try {
+            $this->getEventDispatcher()
+              ->dispatch(new MigratePreRowSaveEvent($this->migration, $this->message, $row), MigrateEvents::PRE_ROW_SAVE);
+            $destination_ids = $id_map->lookupDestinationIds($this->sourceIdValues);
+            $destination_id_values = $destination_ids ? reset($destination_ids) : [];
+            $destination_id_values = $destination->import($row, $destination_id_values);
+            $this->getEventDispatcher()
+              ->dispatch(new MigratePostRowSaveEvent($this->migration, $this->message, $row, $destination_id_values), MigrateEvents::POST_ROW_SAVE);
+            if ($destination_id_values) {
+              // We do not save an idMap entry for config.
+              if ($destination_id_values !== TRUE) {
+                $id_map->saveIdMapping($row, $destination_id_values, $this->sourceRowStatus, $destination->rollbackAction());
+              }
+            }
+            else {
+              $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_FAILED);
+              if (!$id_map->messageCount()) {
+                $message = $this->t('New object was not saved, no error provided');
+                $this->saveMessage($message);
+                $this->message->display($message);
+              }
+            }
+          }
+          catch (MigrateException $e) {
+            $this->getIdMap()->saveIdMapping($row, [], $e->getStatus());
+            $this->saveMessage($e->getMessage(), $e->getLevel());
+          }
+          catch (\Exception $e) {
+            $this->getIdMap()
+              ->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_FAILED);
+            $this->handleException($e);
+          }
+        }
 
-      // Check for memory exhaustion.
-      if (($return = $this->checkStatus()) != MigrationInterface::RESULT_COMPLETED) {
-        break;
-      }
+        $this->sourceRowStatus = MigrateIdMapInterface::STATUS_IMPORTED;
 
-      // If anyone has requested we stop, return the requested result.
-      if ($this->migration->getStatus() == MigrationInterface::STATUS_STOPPING) {
-        $return = $this->migration->getInterruptionResult();
-        $this->migration->clearInterruptionResult();
-        break;
-      }
+        // Check for memory exhaustion.
+        if (($return = $this->checkStatus()) != MigrationInterface::RESULT_COMPLETED) {
+          break;
+        }
 
-      try {
-        $source->next();
-      }
-      catch (\Exception $e) {
-        $this->message->display(
-          $this->t('Migration failed with source plugin exception: @e in @file line @line', [
-            '@e' => $e->getMessage(),
-            '@file' => $e->getFile(),
-            '@line' => $e->getLine(),
-          ]), 'error');
-        $this->migration->setStatus(MigrationInterface::STATUS_IDLE);
-        return MigrationInterface::RESULT_FAILED;
+        // If anyone has requested we stop, return the requested result.
+        if ($this->migration->getStatus() == MigrationInterface::STATUS_STOPPING) {
+          $return = $this->migration->getInterruptionResult();
+          $this->migration->clearInterruptionResult();
+          break;
+        }
+
+        try {
+          $source->next();
+        }
+        catch (\Exception $e) {
+          $this->message->display(
+            $this->t('Migration failed with source plugin exception: @e in @file line @line', [
+              '@e' => $e->getMessage(),
+              '@file' => $e->getFile(),
+              '@line' => $e->getLine(),
+            ]), 'error');
+          $this->migration->setStatus(MigrationInterface::STATUS_IDLE);
+          return MigrationInterface::RESULT_FAILED;
+        }
       }
     }
 
@@ -366,56 +389,74 @@ protected function getIdMap() {
    */
   public function processRow(Row $row, array $process = NULL, $value = NULL) {
     foreach ($this->migration->getProcessPlugins($process) as $destination => $plugins) {
-      $multiple = FALSE;
-      /** @var \Drupal\migrate\Plugin\MigrateProcessInterface $plugin */
-      foreach ($plugins as $plugin) {
-        $definition = $plugin->getPluginDefinition();
-        // Many plugins expect a scalar value but the current value of the
-        // pipeline might be multiple scalars (this is set by the previous
-        // plugin) and in this case the current value needs to be iterated
-        // and each scalar separately transformed.
-        if ($multiple && !$definition['handle_multiples']) {
-          $new_value = [];
-          if (!is_array($value)) {
-            throw new MigrateException(sprintf('Pipeline failed at %s plugin for destination %s: %s received instead of an array,', $plugin->getPluginId(), $destination, $value));
-          }
-          $break = FALSE;
-          foreach ($value as $scalar_value) {
-            try {
-              $new_value[] = $plugin->transform($scalar_value, $this, $row, $destination);
-            }
-            catch (MigrateSkipProcessException $e) {
-              $new_value[] = NULL;
-              $break = TRUE;
-            }
-          }
-          $value = $new_value;
-          if ($break) {
-            break;
-          }
+      $this->processPipeline($row, $destination, $plugins, $value);
+    }
+  }
+
+  /**
+   * Runs a process pipeline.
+   *
+   * @param \Drupal\migrate\Row $row
+   *   The $row to be processed.
+   * @param string $destination
+   *   The destination property name.
+   * @param array $plugins
+   *   The process pipeline plugins.
+   * @param mixed $value
+   *   (optional) Initial value of the pipeline for the destination.
+   *
+   * @see \Drupal\migrate\MigrateExecutableInterface::processRow
+   *
+   * @throws \Drupal\migrate\MigrateException
+   */
+  protected function processPipeline(Row $row, string $destination, array $plugins, $value) {
+    $multiple = FALSE;
+    /** @var \Drupal\migrate\Plugin\MigrateProcessInterface $plugin */
+    foreach ($plugins as $plugin) {
+      $definition = $plugin->getPluginDefinition();
+      // Many plugins expect a scalar value but the current value of the
+      // pipeline might be multiple scalars (this is set by the previous plugin)
+      // and in this case the current value needs to be iterated and each scalar
+      // separately transformed.
+      if ($multiple && !$definition['handle_multiples']) {
+        $new_value = [];
+        if (!is_array($value)) {
+          throw new MigrateException(sprintf('Pipeline failed at %s plugin for destination %s: %s received instead of an array,', $plugin->getPluginId(), $destination, $value));
         }
-        else {
+        $break = FALSE;
+        foreach ($value as $scalar_value) {
           try {
-            $value = $plugin->transform($value, $this, $row, $destination);
+            $new_value[] = $plugin->transform($scalar_value, $this, $row, $destination);
           }
           catch (MigrateSkipProcessException $e) {
-            $value = NULL;
-            break;
+            $new_value[] = NULL;
+            $break = TRUE;
           }
-          $multiple = $plugin->multiple();
+        }
+        $value = $new_value;
+        if ($break) {
+          break;
         }
       }
-      // Ensure all values, including nulls, are migrated.
-      if ($plugins) {
-        if (isset($value)) {
-          $row->setDestinationProperty($destination, $value);
+      else {
+        try {
+          $value = $plugin->transform($value, $this, $row, $destination);
         }
-        else {
-          $row->setEmptyDestinationProperty($destination);
+        catch (MigrateSkipProcessException $e) {
+          $value = NULL;
+          break;
         }
+        $multiple = $plugin->multiple();
+      }
+    }
+    // Ensure all values, including nulls, are migrated.
+    if ($plugins) {
+      if (isset($value)) {
+        $row->setDestinationProperty($destination, $value);
+      }
+      else {
+        $row->setEmptyDestinationProperty($destination);
       }
-      // Reset the value.
-      $value = NULL;
     }
   }
 
diff --git a/core/modules/migrate/src/Plugin/migrate/process/FormatDate.php b/core/modules/migrate/src/Plugin/migrate/process/FormatDate.php
index 63e15bd108bd..e5d14efe7a64 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/FormatDate.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/FormatDate.php
@@ -130,10 +130,10 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
       $transformed = DateTimePlus::createFromFormat($fromFormat, $value, $from_timezone, $settings)->format($toFormat, ['timezone' => $to_timezone]);
     }
     catch (\InvalidArgumentException $e) {
-      throw new MigrateException(sprintf("Format date plugin could not transform '%s' using the format '%s' for destination '%s'. Error: %s", $value, $fromFormat, $destination_property, $e->getMessage()), $e->getCode(), $e);
+      throw new MigrateException(sprintf("Format date plugin could not transform '%s' using the format '%s'. Error: %s", $value, $fromFormat, $e->getMessage()), $e->getCode(), $e);
     }
     catch (\UnexpectedValueException $e) {
-      throw new MigrateException(sprintf("Format date plugin could not transform '%s' using the format '%s' for destination '%s'. Error: %s", $value, $fromFormat, $destination_property, $e->getMessage()), $e->getCode(), $e);
+      throw new MigrateException(sprintf("Format date plugin could not transform '%s' using the format '%s'. Error: %s", $value, $fromFormat, $e->getMessage()), $e->getCode(), $e);
     }
 
     return $transformed;
diff --git a/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php b/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php
index 8cac2948887b..6048f740eeee 100644
--- a/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php
+++ b/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php
@@ -75,7 +75,9 @@ public function testExceptionThrow() {
     $messages = $id_map_plugin->getMessages(['url' => $invalid_url])->fetchAll();
     $this->assertCount(1, $messages);
     $message = reset($messages);
-    $this->assertEquals("Client error: `GET $invalid_url` resulted in a `404 Not Found` response ($invalid_url)", $message->message);
+
+    $id = $migration->getPluginId();
+    $this->assertEquals("$id:uri: Client error: `GET $invalid_url` resulted in a `404 Not Found` response ($invalid_url)", $message->message);
     $this->assertEquals(MigrationInterface::MESSAGE_ERROR, $message->level);
 
     // Check that the second row was migrated successfully.
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateExecutableTest.php b/core/modules/migrate/tests/src/Kernel/MigrateExecutableTest.php
index a1dc4bb67340..65e3f13143c9 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateExecutableTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateExecutableTest.php
@@ -45,6 +45,33 @@ public function testMigrateExecutable() {
     $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
     $executable = new TestMigrateExecutable($migration);
     $this->assertEquals(MigrationInterface::RESULT_COMPLETED, $executable->import());
+
+    // Test the exception message when a process plugin throws a
+    // MigrateSkipRowException. Change the definition to have one data row and a
+    // process that will throw a MigrateSkipRowException on every row.
+    $definition['source']['data_rows'] = [
+      [
+        'key' => '1',
+        'field1' => 'f1value1',
+      ],
+    ];
+    $definition['process'] = [
+      'foo' => [
+        'plugin' => 'skip_row_if_not_set',
+        'index' => 'foo',
+        'source' => 'field1',
+        'message' => 'test message',
+      ],
+    ];
+
+    $migration = \Drupal::service('plugin.manager.migration')
+      ->createStubMigration($definition);
+    $executable = new TestMigrateExecutable($migration);
+    $executable->import();
+    $messages = iterator_to_array($migration->getIdMap()->getMessages());
+    $this->assertCount(1, $messages);
+    $expected = $migration->getPluginId() . ':foo: test message';
+    $this->assertEquals($expected, $messages[0]->message);
   }
 
 }
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php b/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php
index 5a3d6e37b22d..9f8d12edd49c 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php
@@ -94,7 +94,8 @@ public function testMessagesTeed() {
     $executable = new MigrateExecutable($this->migration, $this);
     $executable->import();
     $this->assertCount(1, $this->messages);
-    $this->assertSame("source_message: 'a message' is not an array", reset($this->messages));
+    $id = $this->migration->getPluginId();
+    $this->assertSame("source_message: $id:message: 'a message' is not an array", reset($this->messages));
   }
 
   /**
@@ -104,13 +105,14 @@ public function testMessagesTeed() {
    * objects have the expected keys.
    */
   public function testGetMessages() {
+    $id = $this->migration->getPluginId();
     $expected_message = (object) [
       'src_name' => 'source_message',
       'dest_config_name' => NULL,
       'msgid' => '1',
       Sql::SOURCE_IDS_HASH => '170cde81762e22552d1b1578cf3804c89afefe9efbc7cc835185d7141060b032',
       'level' => '1',
-      'message' => "'a message' is not an array",
+      'message' => "$id:message: 'a message' is not an array",
     ];
     $executable = new MigrateExecutable($this->migration, $this);
     $executable->import();
diff --git a/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php b/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php
index 0829688cd73a..5999e7af8cbf 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php
@@ -111,19 +111,6 @@ public function testImportWithValidRow() {
       ->disableOriginalConstructor()
       ->getMock();
 
-    $row->expects($this->once())
-      ->method('getSourceIdValues')
-      ->will($this->returnValue(['id' => 'test']));
-
-    $this->idMap->expects($this->once())
-      ->method('lookupDestinationIds')
-      ->with(['id' => 'test'])
-      ->will($this->returnValue([['test']]));
-
-    $source->expects($this->once())
-      ->method('current')
-      ->will($this->returnValue($row));
-
     $this->executable->setSource($source);
 
     $this->migration->expects($this->once())
@@ -131,10 +118,6 @@ public function testImportWithValidRow() {
       ->will($this->returnValue([]));
 
     $destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
-    $destination->expects($this->once())
-      ->method('import')
-      ->with($row, ['test'])
-      ->will($this->returnValue(['id' => 'test']));
 
     $this->migration
       ->method('getDestinationPlugin')
@@ -153,19 +136,6 @@ public function testImportWithValidRowWithoutDestinationId() {
       ->disableOriginalConstructor()
       ->getMock();
 
-    $row->expects($this->once())
-      ->method('getSourceIdValues')
-      ->will($this->returnValue(['id' => 'test']));
-
-    $this->idMap->expects($this->once())
-      ->method('lookupDestinationIds')
-      ->with(['id' => 'test'])
-      ->will($this->returnValue([['test']]));
-
-    $source->expects($this->once())
-      ->method('current')
-      ->will($this->returnValue($row));
-
     $this->executable->setSource($source);
 
     $this->migration->expects($this->once())
@@ -173,10 +143,6 @@ public function testImportWithValidRowWithoutDestinationId() {
       ->will($this->returnValue([]));
 
     $destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
-    $destination->expects($this->once())
-      ->method('import')
-      ->with($row, ['test'])
-      ->will($this->returnValue(TRUE));
 
     $this->migration
       ->method('getDestinationPlugin')
@@ -198,14 +164,6 @@ public function testImportWithValidRowNoDestinationValues() {
       ->disableOriginalConstructor()
       ->getMock();
 
-    $row->expects($this->once())
-      ->method('getSourceIdValues')
-      ->will($this->returnValue(['id' => 'test']));
-
-    $source->expects($this->once())
-      ->method('current')
-      ->will($this->returnValue($row));
-
     $this->executable->setSource($source);
 
     $this->migration->expects($this->once())
@@ -213,35 +171,11 @@ public function testImportWithValidRowNoDestinationValues() {
       ->will($this->returnValue([]));
 
     $destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
-    $destination->expects($this->once())
-      ->method('import')
-      ->with($row, ['test'])
-      ->will($this->returnValue([]));
 
     $this->migration
       ->method('getDestinationPlugin')
       ->willReturn($destination);
 
-    $this->idMap->expects($this->once())
-      ->method('saveIdMapping')
-      ->with($row, [], MigrateIdMapInterface::STATUS_FAILED, NULL);
-
-    $this->idMap->expects($this->once())
-      ->method('messageCount')
-      ->will($this->returnValue(0));
-
-    $this->idMap->expects($this->once())
-      ->method('saveMessage');
-
-    $this->idMap->expects($this->once())
-      ->method('lookupDestinationIds')
-      ->with(['id' => 'test'])
-      ->will($this->returnValue([['test']]));
-
-    $this->message->expects($this->once())
-      ->method('display')
-      ->with('New object was not saved, no error provided');
-
     $this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
   }
 
@@ -258,14 +192,6 @@ public function testImportWithValidRowWithDestinationMigrateException() {
       ->disableOriginalConstructor()
       ->getMock();
 
-    $row->expects($this->once())
-      ->method('getSourceIdValues')
-      ->will($this->returnValue(['id' => 'test']));
-
-    $source->expects($this->once())
-      ->method('current')
-      ->will($this->returnValue($row));
-
     $this->executable->setSource($source);
 
     $this->migration->expects($this->once())
@@ -273,27 +199,11 @@ public function testImportWithValidRowWithDestinationMigrateException() {
       ->will($this->returnValue([]));
 
     $destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
-    $destination->expects($this->once())
-      ->method('import')
-      ->with($row, ['test'])
-      ->will($this->throwException(new MigrateException($exception_message)));
 
     $this->migration
       ->method('getDestinationPlugin')
       ->willReturn($destination);
 
-    $this->idMap->expects($this->once())
-      ->method('saveIdMapping')
-      ->with($row, [], MigrateIdMapInterface::STATUS_FAILED, NULL);
-
-    $this->idMap->expects($this->once())
-      ->method('saveMessage');
-
-    $this->idMap->expects($this->once())
-      ->method('lookupDestinationIds')
-      ->with(['id' => 'test'])
-      ->will($this->returnValue([['test']]));
-
     $this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
   }
 
@@ -356,14 +266,6 @@ public function testImportWithValidRowWithException() {
       ->disableOriginalConstructor()
       ->getMock();
 
-    $row->expects($this->once())
-      ->method('getSourceIdValues')
-      ->will($this->returnValue(['id' => 'test']));
-
-    $source->expects($this->once())
-      ->method('current')
-      ->will($this->returnValue($row));
-
     $this->executable->setSource($source);
 
     $this->migration->expects($this->once())
@@ -371,31 +273,11 @@ public function testImportWithValidRowWithException() {
       ->will($this->returnValue([]));
 
     $destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
-    $destination->expects($this->once())
-      ->method('import')
-      ->with($row, ['test'])
-      ->will($this->throwException(new \Exception($exception_message)));
 
     $this->migration
       ->method('getDestinationPlugin')
       ->willReturn($destination);
 
-    $this->idMap->expects($this->once())
-      ->method('saveIdMapping')
-      ->with($row, [], MigrateIdMapInterface::STATUS_FAILED, NULL);
-
-    $this->idMap->expects($this->once())
-      ->method('saveMessage');
-
-    $this->idMap->expects($this->once())
-      ->method('lookupDestinationIds')
-      ->with(['id' => 'test'])
-      ->will($this->returnValue([['test']]));
-
-    $this->message->expects($this->once())
-      ->method('display')
-      ->with($exception_message);
-
     $this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
   }
 
diff --git a/core/modules/migrate/tests/src/Unit/process/FormatDateTest.php b/core/modules/migrate/tests/src/Unit/process/FormatDateTest.php
index 75de7741720b..58d61538b641 100644
--- a/core/modules/migrate/tests/src/Unit/process/FormatDateTest.php
+++ b/core/modules/migrate/tests/src/Unit/process/FormatDateTest.php
@@ -54,7 +54,7 @@ public function testMigrateExceptionBadFormat() {
     ];
 
     $this->expectException(MigrateException::class);
-    $this->expectExceptionMessage("Format date plugin could not transform 'January 5, 1955' using the format 'm/d/Y' for destination 'field_date'. Error: The date cannot be created from a format.");
+    $this->expectExceptionMessage("Format date plugin could not transform 'January 5, 1955' using the format 'm/d/Y'. Error: The date cannot be created from a format.");
     $this->plugin = new FormatDate($configuration, 'test_format_date', []);
     $this->plugin->transform('January 5, 1955', $this->migrateExecutable, $this->row, 'field_date');
   }
@@ -69,7 +69,7 @@ public function testMigrateExceptionUnexpectedValue() {
     ];
 
     $this->expectException(MigrateException::class);
-    $this->expectExceptionMessage("Format date plugin could not transform '01/05/55' using the format 'm/d/Y' for destination 'field_date'. Error: The created date does not match the input value.");
+    $this->expectExceptionMessage("Format date plugin could not transform '01/05/55' using the format 'm/d/Y'. Error: The created date does not match the input value.");
     $this->plugin = new FormatDate($configuration, 'test_format_date', []);
     $this->plugin->transform('01/05/55', $this->migrateExecutable, $this->row, 'field_date');
   }
diff --git a/core/modules/responsive_image/src/Plugin/migrate/process/ImageStyleMappings.php b/core/modules/responsive_image/src/Plugin/migrate/process/ImageStyleMappings.php
index 3a216bbd0046..5e6848380dda 100644
--- a/core/modules/responsive_image/src/Plugin/migrate/process/ImageStyleMappings.php
+++ b/core/modules/responsive_image/src/Plugin/migrate/process/ImageStyleMappings.php
@@ -21,7 +21,7 @@ class ImageStyleMappings extends ProcessPluginBase {
    */
   public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
     if (!is_array($value)) {
-      throw new MigrateException(sprintf('Input should be an array for destination %s', $destination_property));
+      throw new MigrateException('Input should be an array');
     }
 
     list($mappings, $breakpoint_group) = $value;
-- 
GitLab