Version 1
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Config / ConfigDependencyTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Config;
4
5 use Drupal\entity_test\Entity\EntityTest;
6 use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
7
8 /**
9  * Tests for configuration dependencies.
10  *
11  * @coversDefaultClass \Drupal\Core\Config\ConfigManager
12  *
13  * @group config
14  */
15 class ConfigDependencyTest extends EntityKernelTestBase {
16
17   /**
18    * Modules to enable.
19    *
20    * The entity_test module is enabled to provide content entity types.
21    *
22    * @var array
23    */
24   public static $modules = ['config_test', 'entity_test', 'user'];
25
26   /**
27    * Tests that calculating dependencies for system module.
28    */
29   public function testNonEntity() {
30     $this->installConfig(['system']);
31     $config_manager = \Drupal::service('config.manager');
32     $dependents = $config_manager->findConfigEntityDependents('module', ['system']);
33     $this->assertTrue(isset($dependents['system.site']), 'Simple configuration system.site has a UUID key even though it is not a configuration entity and therefore is found when looking for dependencies of the System module.');
34     // Ensure that calling
35     // \Drupal\Core\Config\ConfigManager::findConfigEntityDependentsAsEntities()
36     // does not try to load system.site as an entity.
37     $config_manager->findConfigEntityDependentsAsEntities('module', ['system']);
38   }
39
40   /**
41    * Tests creating dependencies on configuration entities.
42    */
43   public function testDependencyManagement() {
44     /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
45     $config_manager = \Drupal::service('config.manager');
46     $storage = $this->container->get('entity.manager')->getStorage('config_test');
47     // Test dependencies between modules.
48     $entity1 = $storage->create(
49       [
50         'id' => 'entity1',
51         'dependencies' => [
52           'enforced' => [
53             'module' => ['node']
54           ]
55         ]
56       ]
57     );
58     $entity1->save();
59
60     $dependents = $config_manager->findConfigEntityDependents('module', ['node']);
61     $this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.');
62     $dependents = $config_manager->findConfigEntityDependents('module', ['config_test']);
63     $this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the config_test module.');
64     $dependents = $config_manager->findConfigEntityDependents('module', ['views']);
65     $this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Views module.');
66     // Ensure that the provider of the config entity is not actually written to
67     // the dependencies array.
68     $raw_config = $this->config('config_test.dynamic.entity1');
69     $root_module_dependencies = $raw_config->get('dependencies.module');
70     $this->assertTrue(empty($root_module_dependencies), 'Node module is not written to the root dependencies array as it is enforced.');
71
72     // Create additional entities to test dependencies on config entities.
73     $entity2 = $storage->create(['id' => 'entity2', 'dependencies' => ['enforced' => ['config' => [$entity1->getConfigDependencyName()]]]]);
74     $entity2->save();
75     $entity3 = $storage->create(['id' => 'entity3', 'dependencies' => ['enforced' => ['config' => [$entity2->getConfigDependencyName()]]]]);
76     $entity3->save();
77     $entity4 = $storage->create(['id' => 'entity4', 'dependencies' => ['enforced' => ['config' => [$entity3->getConfigDependencyName()]]]]);
78     $entity4->save();
79
80     // Test getting $entity1's dependencies as configuration dependency objects.
81     $dependents = $config_manager->findConfigEntityDependents('config', [$entity1->getConfigDependencyName()]);
82     $this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on itself.');
83     $this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity1.');
84     $this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.');
85     $this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.');
86
87     // Test getting $entity2's dependencies as entities.
88     $dependents = $config_manager->findConfigEntityDependentsAsEntities('config', [$entity2->getConfigDependencyName()]);
89     $dependent_ids = $this->getDependentIds($dependents);
90     $this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on config_test.dynamic.entity1.');
91     $this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on itself.');
92     $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity2.');
93     $this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity2.');
94
95     // Test getting node module's dependencies as configuration dependency
96     // objects.
97     $dependents = $config_manager->findConfigEntityDependents('module', ['node']);
98     $this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.');
99     $this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the Node module.');
100     $this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
101     $this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
102
103     // Test getting node module's dependencies as configuration dependency
104     // objects after making $entity3 also dependent on node module but $entity1
105     // no longer depend on node module.
106     $entity1->setEnforcedDependencies([])->save();
107     $entity3->setEnforcedDependencies(['module' => ['node'], 'config' => [$entity2->getConfigDependencyName()]])->save();
108     $dependents = $config_manager->findConfigEntityDependents('module', ['node']);
109     $this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Node module.');
110     $this->assertFalse(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 does not have a dependency on the Node module.');
111     $this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
112     $this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
113
114     // Test dependency on a content entity.
115     $entity_test = EntityTest::create([
116       'name' => $this->randomString(),
117       'type' => 'entity_test',
118     ]);
119     $entity_test->save();
120     $entity2->setEnforcedDependencies(['config' => [$entity1->getConfigDependencyName()], 'content' => [$entity_test->getConfigDependencyName()]])->save();;
121     $dependents = $config_manager->findConfigEntityDependents('content', [$entity_test->getConfigDependencyName()]);
122     $this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the content entity.');
123     $this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the content entity.');
124     $this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the content entity (via entity2).');
125     $this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the content entity (via entity3).');
126
127     // Create a configuration entity of a different type with the same ID as one
128     // of the entities already created.
129     $alt_storage = $this->container->get('entity.manager')->getStorage('config_query_test');
130     $alt_storage->create(['id' => 'entity1', 'dependencies' => ['enforced' => ['config' => [$entity1->getConfigDependencyName()]]]])->save();
131     $alt_storage->create(['id' => 'entity2', 'dependencies' => ['enforced' => ['module' => ['views']]]])->save();
132
133     $dependents = $config_manager->findConfigEntityDependentsAsEntities('config', [$entity1->getConfigDependencyName()]);
134     $dependent_ids = $this->getDependentIds($dependents);
135     $this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on itself.');
136     $this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity1.');
137     $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.');
138     $this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.');
139     $this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_query_test.dynamic.entity1 has a dependency on config_test.dynamic.entity1.');
140     $this->assertFalse(in_array('config_query_test:entity2', $dependent_ids), 'config_query_test.dynamic.entity2 does not have a dependency on config_test.dynamic.entity1.');
141
142     $dependents = $config_manager->findConfigEntityDependentsAsEntities('module', ['node', 'views']);
143     $dependent_ids = $this->getDependentIds($dependents);
144     $this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on Views or Node.');
145     $this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on Views or Node.');
146     $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on Views or Node.');
147     $this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on Views or Node.');
148     $this->assertFalse(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 does not have a dependency on Views or Node.');
149     $this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on Views or Node.');
150
151     $dependents = $config_manager->findConfigEntityDependentsAsEntities('module', ['config_test']);
152     $dependent_ids = $this->getDependentIds($dependents);
153     $this->assertTrue(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 has a dependency on config_test module.');
154     $this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test module.');
155     $this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test module.');
156     $this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test module.');
157     $this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 has a dependency on config_test module.');
158     $this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on config_test module.');
159
160     // Test the ability to find missing content dependencies.
161     $missing_dependencies = $config_manager->findMissingContentDependencies();
162     $this->assertEqual([], $missing_dependencies);
163
164     $expected = [$entity_test->uuid() => [
165       'entity_type' => 'entity_test',
166       'bundle' => $entity_test->bundle(),
167       'uuid' => $entity_test->uuid(),
168     ]];
169     // Delete the content entity so that is it now missing.
170     $entity_test->delete();
171     $missing_dependencies = $config_manager->findMissingContentDependencies();
172     $this->assertEqual($expected, $missing_dependencies);
173
174     // Add a fake missing dependency to ensure multiple missing dependencies
175     // work.
176     $entity1->setEnforcedDependencies(['content' => [$entity_test->getConfigDependencyName(), 'entity_test:bundle:uuid']])->save();;
177     $expected['uuid'] = [
178       'entity_type' => 'entity_test',
179       'bundle' => 'bundle',
180       'uuid' => 'uuid',
181     ];
182     $missing_dependencies = $config_manager->findMissingContentDependencies();
183     $this->assertEqual($expected, $missing_dependencies);
184   }
185
186   /**
187    * Tests ConfigManager::uninstall() and config entity dependency management.
188    */
189   public function testConfigEntityUninstall() {
190     /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
191     $config_manager = \Drupal::service('config.manager');
192     /** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
193     $storage = $this->container->get('entity.manager')
194       ->getStorage('config_test');
195     // Test dependencies between modules.
196     $entity1 = $storage->create(
197       [
198         'id' => 'entity1',
199         'dependencies' => [
200           'enforced' => [
201             'module' => ['node', 'config_test']
202           ],
203         ],
204       ]
205     );
206     $entity1->save();
207     $entity2 = $storage->create(
208       [
209         'id' => 'entity2',
210         'dependencies' => [
211           'enforced' => [
212             'config' => [$entity1->getConfigDependencyName()],
213           ],
214         ],
215       ]
216     );
217     $entity2->save();
218     // Perform a module rebuild so we can know where the node module is located
219     // and uninstall it.
220     // @todo Remove as part of https://www.drupal.org/node/2186491
221     system_rebuild_module_data();
222     // Test that doing a config uninstall of the node module deletes entity2
223     // since it is dependent on entity1 which is dependent on the node module.
224     $config_manager->uninstall('module', 'node');
225     $this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
226     $this->assertFalse($storage->load('entity2'), 'Entity 2 deleted');
227   }
228
229   /**
230    * Data provider for self::testConfigEntityUninstallComplex().
231    */
232   public function providerConfigEntityUninstallComplex() {
233     // Ensure that alphabetical order has no influence on dependency fixing and
234     // removal.
235     return [
236       [['a', 'b', 'c', 'd']],
237       [['d', 'c', 'b', 'a']],
238       [['c', 'd', 'a', 'b']],
239     ];
240   }
241
242   /**
243    * Tests complex configuration entity dependency handling during uninstall.
244    *
245    * Configuration entities can be deleted or updated during module uninstall
246    * because they have dependencies on the module.
247    *
248    * @param array $entity_id_suffixes
249    *   The suffixes to add to the 4 entities created by the test.
250    *
251    * @dataProvider providerConfigEntityUninstallComplex
252    */
253   public function testConfigEntityUninstallComplex(array $entity_id_suffixes) {
254     /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
255     $config_manager = \Drupal::service('config.manager');
256     /** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
257     $storage = $this->container->get('entity.manager')
258       ->getStorage('config_test');
259     // Entity 1 will be deleted because it depends on node.
260     $entity_1 = $storage->create(
261       [
262         'id' => 'entity_' . $entity_id_suffixes[0],
263         'dependencies' => [
264           'enforced' => [
265             'module' => ['node', 'config_test']
266           ],
267         ],
268       ]
269     );
270     $entity_1->save();
271
272     // Entity 2 has a dependency on entity 1 but it can be fixed because
273     // \Drupal\config_test\Entity::onDependencyRemoval() will remove the
274     // dependency before config entities are deleted.
275     $entity_2 = $storage->create(
276       [
277         'id' => 'entity_' . $entity_id_suffixes[1],
278         'dependencies' => [
279           'enforced' => [
280             'config' => [$entity_1->getConfigDependencyName()],
281           ],
282         ],
283       ]
284     );
285     $entity_2->save();
286
287     // Entity 3 will be unchanged because it is dependent on entity 2 which can
288     // be fixed. The ConfigEntityInterface::onDependencyRemoval() method will
289     // not be called for this entity.
290     $entity_3 = $storage->create(
291       [
292         'id' => 'entity_' . $entity_id_suffixes[2],
293         'dependencies' => [
294           'enforced' => [
295             'config' => [$entity_2->getConfigDependencyName()],
296           ],
297         ],
298       ]
299     );
300     $entity_3->save();
301
302     // Entity 4's config dependency will be fixed but it will still be deleted
303     // because it also depends on the node module.
304     $entity_4 = $storage->create(
305       [
306         'id' => 'entity_' . $entity_id_suffixes[3],
307         'dependencies' => [
308           'enforced' => [
309             'config' => [$entity_1->getConfigDependencyName()],
310             'module' => ['node', 'config_test']
311           ],
312         ],
313       ]
314     );
315     $entity_4->save();
316
317     // Set a more complicated test where dependencies will be fixed.
318     \Drupal::state()->set('config_test.fix_dependencies', [$entity_1->getConfigDependencyName()]);
319     \Drupal::state()->set('config_test.on_dependency_removal_called', []);
320
321     // Do a dry run using
322     // \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
323     $config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('module', ['node']);
324     $this->assertEqual($entity_1->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 1 will be deleted.');
325     $this->assertEqual($entity_2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
326     $this->assertEqual($entity_3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
327     $this->assertEqual($entity_4->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 4 will be deleted.');
328
329     $called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
330     $this->assertFalse(in_array($entity_3->id(), $called), 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
331     $this->assertIdentical([$entity_1->id(), $entity_4->id(), $entity_2->id()], $called, 'The most dependent entites have ConfigEntityInterface::onDependencyRemoval() called first.');
332
333     // Perform a module rebuild so we can know where the node module is located
334     // and uninstall it.
335     // @todo Remove as part of https://www.drupal.org/node/2186491
336     system_rebuild_module_data();
337     // Perform the uninstall.
338     $config_manager->uninstall('module', 'node');
339
340     // Test that expected actions have been performed.
341     $this->assertFalse($storage->load($entity_1->id()), 'Entity 1 deleted');
342     $entity_2 = $storage->load($entity_2->id());
343     $this->assertTrue($entity_2, 'Entity 2 not deleted');
344     $this->assertEqual($entity_2->calculateDependencies()->getDependencies()['config'], [], 'Entity 2 dependencies updated to remove dependency on entity 1.');
345     $entity_3 = $storage->load($entity_3->id());
346     $this->assertTrue($entity_3, 'Entity 3 not deleted');
347     $this->assertEqual($entity_3->calculateDependencies()->getDependencies()['config'], [$entity_2->getConfigDependencyName()], 'Entity 3 still depends on entity 2.');
348     $this->assertFalse($storage->load($entity_4->id()), 'Entity 4 deleted');
349   }
350
351   /**
352    * @covers ::uninstall
353    * @covers ::getConfigEntitiesToChangeOnDependencyRemoval
354    */
355   public function testConfigEntityUninstallThirdParty() {
356     /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
357     $config_manager = \Drupal::service('config.manager');
358     /** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
359     $storage = $this->container->get('entity_type.manager')
360       ->getStorage('config_test');
361     // Entity 1 will be fixed because it only has a dependency via third-party
362     // settings, which are fixable.
363     $entity_1 = $storage->create([
364       'id' => 'entity_1',
365       'dependencies' => [
366         'enforced' => [
367           'module' => ['config_test'],
368         ],
369       ],
370       'third_party_settings' => [
371         'node' => [
372           'foo' => 'bar',
373         ],
374       ],
375     ]);
376     $entity_1->save();
377
378     // Entity 2 has a dependency on entity 1.
379     $entity_2 = $storage->create([
380       'id' => 'entity_2',
381       'dependencies' => [
382         'enforced' => [
383           'config' => [$entity_1->getConfigDependencyName()],
384         ],
385       ],
386       'third_party_settings' => [
387         'node' => [
388           'foo' => 'bar',
389         ],
390       ],
391     ]);
392     $entity_2->save();
393
394     // Entity 3 will be unchanged because it is dependent on entity 2 which can
395     // be fixed. The ConfigEntityInterface::onDependencyRemoval() method will
396     // not be called for this entity.
397     $entity_3 = $storage->create([
398       'id' => 'entity_3',
399       'dependencies' => [
400         'enforced' => [
401           'config' => [$entity_2->getConfigDependencyName()],
402         ],
403       ],
404     ]);
405     $entity_3->save();
406
407     // Entity 4's config dependency will be fixed but it will still be deleted
408     // because it also depends on the node module.
409     $entity_4 = $storage->create([
410       'id' => 'entity_4',
411       'dependencies' => [
412         'enforced' => [
413           'config' => [$entity_1->getConfigDependencyName()],
414           'module' => ['node', 'config_test'],
415         ],
416       ],
417     ]);
418     $entity_4->save();
419
420     \Drupal::state()->set('config_test.fix_dependencies', []);
421     \Drupal::state()->set('config_test.on_dependency_removal_called', []);
422
423     // Do a dry run using
424     // \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
425     $config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('module', ['node']);
426     $config_entity_ids = [
427       'update' => [],
428       'delete' => [],
429       'unchanged' => [],
430     ];
431     foreach ($config_entities as $type => $config_entities_by_type) {
432       foreach ($config_entities_by_type as $config_entity) {
433         $config_entity_ids[$type][] = $config_entity->id();
434       }
435     }
436     $expected = [
437       'update' => [$entity_1->id(), $entity_2->id()],
438       'delete' => [$entity_4->id()],
439       'unchanged' => [$entity_3->id()],
440     ];
441     $this->assertSame($expected, $config_entity_ids);
442
443     $called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
444     $this->assertFalse(in_array($entity_3->id(), $called), 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
445     $this->assertSame([$entity_1->id(), $entity_4->id(), $entity_2->id()], $called, 'The most dependent entities have ConfigEntityInterface::onDependencyRemoval() called first.');
446
447     // Perform a module rebuild so we can know where the node module is located
448     // and uninstall it.
449     // @todo Remove as part of https://www.drupal.org/node/2186491
450     system_rebuild_module_data();
451     // Perform the uninstall.
452     $config_manager->uninstall('module', 'node');
453
454     // Test that expected actions have been performed.
455     $entity_1 = $storage->load($entity_1->id());
456     $this->assertTrue($entity_1, 'Entity 1 not deleted');
457     $this->assertSame($entity_1->getThirdPartySettings('node'), [], 'Entity 1 third party settings updated.');
458     $entity_2 = $storage->load($entity_2->id());
459     $this->assertTrue($entity_2, 'Entity 2 not deleted');
460     $this->assertSame($entity_2->getThirdPartySettings('node'), [], 'Entity 2 third party settings updated.');
461     $this->assertSame($entity_2->calculateDependencies()->getDependencies()['config'], [$entity_1->getConfigDependencyName()], 'Entity 2 still depends on entity 1.');
462     $entity_3 = $storage->load($entity_3->id());
463     $this->assertTrue($entity_3, 'Entity 3 not deleted');
464     $this->assertSame($entity_3->calculateDependencies()->getDependencies()['config'], [$entity_2->getConfigDependencyName()], 'Entity 3 still depends on entity 2.');
465     $this->assertFalse($storage->load($entity_4->id()), 'Entity 4 deleted');
466   }
467
468   /**
469    * Tests deleting a configuration entity and dependency management.
470    */
471   public function testConfigEntityDelete() {
472     /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
473     $config_manager = \Drupal::service('config.manager');
474     /** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
475     $storage = $this->container->get('entity.manager')->getStorage('config_test');
476     // Test dependencies between configuration entities.
477     $entity1 = $storage->create(
478       [
479         'id' => 'entity1'
480       ]
481     );
482     $entity1->save();
483     $entity2 = $storage->create(
484       [
485         'id' => 'entity2',
486         'dependencies' => [
487           'enforced' => [
488             'config' => [$entity1->getConfigDependencyName()],
489           ],
490         ],
491       ]
492     );
493     $entity2->save();
494
495     // Do a dry run using
496     // \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
497     $config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity1->getConfigDependencyName()]);
498     $this->assertEqual($entity2->uuid(), reset($config_entities['delete'])->uuid(), 'Entity 2 will be deleted.');
499     $this->assertTrue(empty($config_entities['update']), 'No dependent configuration entities will be updated.');
500     $this->assertTrue(empty($config_entities['unchanged']), 'No dependent configuration entities will be unchanged.');
501
502     // Test that doing a delete of entity1 deletes entity2 since it is dependent
503     // on entity1.
504     $entity1->delete();
505     $this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
506     $this->assertFalse($storage->load('entity2'), 'Entity 2 deleted');
507
508     // Set a more complicated test where dependencies will be fixed.
509     \Drupal::state()->set('config_test.fix_dependencies', [$entity1->getConfigDependencyName()]);
510
511     // Entity1 will be deleted by the test.
512     $entity1 = $storage->create(
513       [
514         'id' => 'entity1',
515       ]
516     );
517     $entity1->save();
518
519     // Entity2 has a dependency on Entity1 but it can be fixed because
520     // \Drupal\config_test\Entity::onDependencyRemoval() will remove the
521     // dependency before config entities are deleted.
522     $entity2 = $storage->create(
523       [
524         'id' => 'entity2',
525         'dependencies' => [
526           'enforced' => [
527             'config' => [$entity1->getConfigDependencyName()],
528           ],
529         ],
530       ]
531     );
532     $entity2->save();
533
534     // Entity3 will be unchanged because it is dependent on Entity2 which can
535     // be fixed.
536     $entity3 = $storage->create(
537       [
538         'id' => 'entity3',
539         'dependencies' => [
540           'enforced' => [
541             'config' => [$entity2->getConfigDependencyName()],
542           ],
543         ],
544       ]
545     );
546     $entity3->save();
547
548     // Do a dry run using
549     // \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
550     $config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity1->getConfigDependencyName()]);
551     $this->assertTrue(empty($config_entities['delete']), 'No dependent configuration entities will be deleted.');
552     $this->assertEqual($entity2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
553     $this->assertEqual($entity3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
554
555     // Perform the uninstall.
556     $entity1->delete();
557
558     // Test that expected actions have been performed.
559     $this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
560     $entity2 = $storage->load('entity2');
561     $this->assertTrue($entity2, 'Entity 2 not deleted');
562     $this->assertEqual($entity2->calculateDependencies()->getDependencies()['config'], [], 'Entity 2 dependencies updated to remove dependency on Entity1.');
563     $entity3 = $storage->load('entity3');
564     $this->assertTrue($entity3, 'Entity 3 not deleted');
565     $this->assertEqual($entity3->calculateDependencies()->getDependencies()['config'], [$entity2->getConfigDependencyName()], 'Entity 3 still depends on Entity 2.');
566   }
567
568   /**
569    * Tests getConfigEntitiesToChangeOnDependencyRemoval() with content entities.
570    *
571    * At the moment there is no runtime code that calculates configuration
572    * dependencies on content entity delete because this calculation is expensive
573    * and all content dependencies are soft. This test ensures that the code
574    * works for content entities.
575    *
576    * @see \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval()
577    */
578   public function testContentEntityDelete() {
579     $this->installEntitySchema('entity_test');
580     /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
581     $config_manager = \Drupal::service('config.manager');
582
583     $content_entity = EntityTest::create();
584     $content_entity->save();
585     /** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
586     $storage = $this->container->get('entity.manager')->getStorage('config_test');
587     $entity1 = $storage->create(
588       [
589         'id' => 'entity1',
590         'dependencies' => [
591           'enforced' => [
592             'content' => [$content_entity->getConfigDependencyName()]
593           ],
594         ],
595       ]
596     );
597     $entity1->save();
598     $entity2 = $storage->create(
599       [
600         'id' => 'entity2',
601         'dependencies' => [
602           'enforced' => [
603             'config' => [$entity1->getConfigDependencyName()]
604           ],
605         ],
606       ]
607     );
608     $entity2->save();
609
610     // Create a configuration entity that is not in the dependency chain.
611     $entity3 = $storage->create(['id' => 'entity3']);
612     $entity3->save();
613
614     $config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('content', [$content_entity->getConfigDependencyName()]);
615     $this->assertEqual($entity1->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 1 will be deleted.');
616     $this->assertEqual($entity2->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 2 will be deleted.');
617     $this->assertTrue(empty($config_entities['update']), 'No dependencies of the content entity will be updated.');
618     $this->assertTrue(empty($config_entities['unchanged']), 'No dependencies of the content entity will be unchanged.');
619   }
620
621   /**
622    * Gets a list of identifiers from an array of configuration entities.
623    *
624    * @param \Drupal\Core\Config\Entity\ConfigEntityInterface[] $dependents
625    *   An array of configuration entities.
626    *
627    * @return array
628    *   An array with values of entity_type_id:ID
629    */
630   protected function getDependentIds(array $dependents) {
631     $dependent_ids = [];
632     foreach ($dependents as $dependent) {
633       $dependent_ids[] = $dependent->getEntityTypeId() . ':' . $dependent->id();
634     }
635     return $dependent_ids;
636   }
637
638 }