Pull merge.
[yaffs-website] / web / core / modules / migrate / tests / src / Kernel / MigrateSourceTestBase.php
1 <?php
2
3 namespace Drupal\Tests\migrate\Kernel;
4
5 use Drupal\KernelTests\KernelTestBase;
6 use Drupal\migrate\Plugin\MigrateIdMapInterface;
7 use Drupal\migrate\Plugin\MigrationInterface;
8 use Drupal\migrate\Row;
9
10 /**
11  * Base class for tests of Migrate source plugins.
12  */
13 abstract class MigrateSourceTestBase extends KernelTestBase {
14
15   /**
16    * {@inheritdoc}
17    */
18   public static $modules = ['migrate'];
19
20   /**
21    * The mocked migration.
22    *
23    * @var \Drupal\migrate\Plugin\MigrationInterface|\Prophecy\Prophecy\ObjectProphecy
24    */
25   protected $migration;
26
27   /**
28    * The source plugin under test.
29    *
30    * @var \Drupal\migrate\Plugin\MigrateSourceInterface
31    */
32   protected $plugin;
33
34   /**
35    * The data provider.
36    *
37    * @see \Drupal\Tests\migrate\Kernel\MigrateSourceTestBase::testSource
38    *
39    * @return array
40    *   Array of data sets to test, each of which is a numerically indexed array
41    *   with the following elements:
42    *   - An array of source data, which can be optionally processed and set up
43    *     by subclasses.
44    *   - An array of expected result rows.
45    *   - (optional) The number of result rows the plugin under test is expected
46    *     to return. If this is not a numeric value, the plugin will not be
47    *     counted.
48    *   - (optional) Array of configuration options for the plugin under test.
49    */
50   abstract public function providerSource();
51
52   /**
53    * {@inheritdoc}
54    */
55   protected function setUp() {
56     parent::setUp();
57
58     // Create a mock migration. This will be injected into the source plugin
59     // under test.
60     $this->migration = $this->prophesize(MigrationInterface::class);
61
62     $this->migration->id()->willReturn(
63       $this->randomMachineName(16)
64     );
65     // Prophesize a useless ID map plugin and an empty set of destination IDs.
66     // Calling code can override these prophecies later and set up different
67     // behaviors.
68     $this->migration->getIdMap()->willReturn(
69       $this->prophesize(MigrateIdMapInterface::class)->reveal()
70     );
71     $this->migration->getDestinationIds()->willReturn([]);
72   }
73
74   /**
75    * Determines the plugin to be tested by reading the class @covers annotation.
76    *
77    * @return string
78    */
79   protected function getPluginClass() {
80     $annotations = $this->getAnnotations();
81
82     if (isset($annotations['class']['covers'])) {
83       return $annotations['class']['covers'][0];
84     }
85     else {
86       $this->fail('No plugin class was specified');
87     }
88   }
89
90   /**
91    * Instantiates the source plugin under test.
92    *
93    * @param array $configuration
94    *   The source plugin configuration.
95    *
96    * @return \Drupal\migrate\Plugin\MigrateSourceInterface|object
97    *   The fully configured source plugin.
98    */
99   protected function getPlugin(array $configuration) {
100     // Only create the plugin once per test.
101     if ($this->plugin) {
102       return $this->plugin;
103     }
104
105     $class = ltrim($this->getPluginClass(), '\\');
106
107     /** @var \Drupal\migrate\Plugin\MigratePluginManager $plugin_manager */
108     $plugin_manager = $this->container->get('plugin.manager.migrate.source');
109
110     foreach ($plugin_manager->getDefinitions() as $id => $definition) {
111       if (ltrim($definition['class'], '\\') == $class) {
112         $this->plugin = $plugin_manager
113           ->createInstance($id, $configuration, $this->migration->reveal());
114
115         $this->migration
116           ->getSourcePlugin()
117           ->willReturn($this->plugin);
118
119         return $this->plugin;
120       }
121     }
122     $this->fail('No plugin found for class ' . $class);
123   }
124
125   /**
126    * Tests the source plugin against a particular data set.
127    *
128    * @param array $source_data
129    *   The source data that the source plugin will read.
130    * @param array $expected_data
131    *   The result rows the source plugin is expected to return.
132    * @param mixed $expected_count
133    *   (optional) How many rows the source plugin is expected to return.
134    *   Defaults to count($expected_data). If set to a non-null, non-numeric
135    *   value (like FALSE or 'nope'), the source plugin will not be counted.
136    * @param array $configuration
137    *   (optional) Configuration for the source plugin.
138    * @param mixed $high_water
139    *   (optional) The value of the high water field.
140    *
141    * @dataProvider providerSource
142    */
143   public function testSource(array $source_data, array $expected_data, $expected_count = NULL, array $configuration = [], $high_water = NULL) {
144     $plugin = $this->getPlugin($configuration);
145
146     // All source plugins must define IDs.
147     $this->assertNotEmpty($plugin->getIds());
148
149     // If there is a high water mark, set it in the high water storage.
150     if (isset($high_water)) {
151       $this->container
152         ->get('keyvalue')
153         ->get('migrate:high_water')
154         ->set($this->migration->reveal()->id(), $high_water);
155     }
156
157     if (is_null($expected_count)) {
158       $expected_count = count($expected_data);
159     }
160     // If an expected count was given, assert it only if the plugin is
161     // countable.
162     if (is_numeric($expected_count)) {
163       $this->assertInstanceOf('\Countable', $plugin);
164       $this->assertCount($expected_count, $plugin);
165     }
166
167     $i = 0;
168     /** @var \Drupal\migrate\Row $row */
169     foreach ($plugin as $row) {
170       $this->assertInstanceOf(Row::class, $row);
171
172       $expected = $expected_data[$i++];
173       $actual = $row->getSource();
174
175       foreach ($expected as $key => $value) {
176         $this->assertArrayHasKey($key, $actual);
177
178         if (is_array($value)) {
179           ksort($value);
180           ksort($actual[$key]);
181           $this->assertEquals($value, $actual[$key]);
182         }
183         else {
184           $this->assertSame((string) $value, (string) $actual[$key]);
185         }
186       }
187     }
188     // False positives occur if the foreach is not entered. So, confirm the
189     // foreach loop was entered if the expected count is greater than 0.
190     if ($expected_count > 0) {
191       $this->assertGreaterThan(0, $i);
192     }
193   }
194
195 }