ConfigUpdateUnitTestBase.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. <?php
  2. namespace Drupal\Tests\config_update\Unit;
  3. use Drupal\Core\StringTranslation\TranslatableMarkup;
  4. use Drupal\Tests\UnitTestCase;
  5. use Symfony\Component\EventDispatcher\Event;
  6. /**
  7. * Base class for unit testing in Config Update Manager.
  8. *
  9. * This class provides some mock classes for unit testing.
  10. */
  11. abstract class ConfigUpdateUnitTestBase extends UnitTestCase {
  12. /**
  13. * The mocked entity definition information.
  14. *
  15. * They are not sorted, to test that the methods sort them. Also there are a
  16. * couple with prefixes that are subsets of each other.
  17. *
  18. * @var string[]
  19. *
  20. * @see ConfigUpdateUnitTestBase::getEntityManagerMock().
  21. */
  22. protected $entityDefinitionInformation = [
  23. ['prefix' => 'foo.bar', 'type' => 'foo'],
  24. ['prefix' => 'foo.barbaz', 'type' => 'bar'],
  25. ['prefix' => 'baz.foo', 'type' => 'baz'],
  26. ];
  27. /**
  28. * Creates a mock entity manager for the test.
  29. *
  30. * @see ConfigUpdateUnitTestBase::entityDefinitionInformation
  31. */
  32. protected function getEntityManagerMock() {
  33. $definitions = [];
  34. $map = [];
  35. foreach ($this->entityDefinitionInformation as $info) {
  36. $def = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityTypeInterface')->getMock();
  37. $def
  38. ->expects($this->any())
  39. ->method('getConfigPrefix')
  40. ->willReturn($info['prefix']);
  41. $def
  42. ->expects($this->any())
  43. ->method('entityClassImplements')
  44. ->willReturn(TRUE);
  45. $def
  46. ->method('getKey')
  47. ->willReturn('id');
  48. $def->getConfigPrefix();
  49. $definitions[$info['type']] = $def;
  50. $map[] = [$info['type'], FALSE, $def];
  51. $map[] = [$info['type'], TRUE, $def];
  52. }
  53. // Add in a content entity definition, which shouldn't be recognized by the
  54. // config lister class.
  55. $def = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityTypeInterface')->getMock();
  56. $def
  57. ->expects($this->any())
  58. ->method('entityClassImplements')
  59. ->willReturn(FALSE);
  60. $definitions['content_entity'] = $def;
  61. $manager = $this->getMockBuilder('Drupal\Core\Entity\EntityTypeManagerInterface')->getMock();
  62. $manager
  63. ->method('getDefinitions')
  64. ->willReturn($definitions);
  65. $manager
  66. ->method('getDefinition')
  67. ->will($this->returnValueMap($map));
  68. $manager
  69. ->method('getStorage')
  70. ->will($this->returnCallback([$this, 'mockGetStorage']));
  71. return $manager;
  72. }
  73. /**
  74. * Mocks the getStorage() method for the entity manager.
  75. */
  76. public function mockGetStorage($entity_type) {
  77. // Figure out the config prefix for this entity type.
  78. $prefix = '';
  79. foreach ($this->entityDefinitionInformation as $info) {
  80. if ($info['type'] == $entity_type) {
  81. $prefix = $info['prefix'];
  82. }
  83. }
  84. // This is used in ConfigReverter::import(). Although it is supposed to
  85. // be entity storage, we'll use our mock config object instead.
  86. return new MockConfig('', $prefix, $this);
  87. }
  88. /**
  89. * Array of active configuration information for mocking.
  90. *
  91. * Array structure: Each element is an array whose first element is a
  92. * provider name, and second is an array of config items it provides.
  93. *
  94. * @var array
  95. *
  96. * @see ConfigUpdateUnitTestBase::getConfigStorageMock()
  97. */
  98. protected $configStorageActiveInfo = [
  99. ['foo.bar', ['foo.bar.one', 'foo.bar.two', 'foo.bar.three']],
  100. ['foo.barbaz', ['foo.barbaz.four', 'foo.barbaz.five', 'foo.barbaz.six']],
  101. ['baz.foo', []],
  102. ['',
  103. [
  104. 'foo.bar.one',
  105. 'foo.bar.two',
  106. 'foo.bar.three',
  107. 'foo.barbaz.four',
  108. 'foo.barbaz.five',
  109. 'foo.barbaz.six',
  110. 'something.else',
  111. 'another.one',
  112. ],
  113. ],
  114. ];
  115. /**
  116. * Array of extension configuration information for mocking.
  117. *
  118. * Array structure: Each element is an array whose first element is a
  119. * provider name, and second is an array of config items it provides.
  120. *
  121. * @var array
  122. *
  123. * @see ConfigUpdateUnitTestBase::getConfigStorageMock()
  124. */
  125. protected $configStorageExtensionInfo = [
  126. ['foo.bar', ['foo.bar.one', 'foo.bar.two', 'foo.bar.seven']],
  127. ['baz.foo', []],
  128. // This next item is assumed to be element 2 of the array. If not, you
  129. // will need to change ConfigUpdateUnitTestBase::getConfigStorageMock().
  130. ['',
  131. [
  132. 'foo.bar.one',
  133. 'foo.bar.two',
  134. 'foo.bar.seven',
  135. 'foo.barbaz.four',
  136. 'foo.barnot.three',
  137. 'something.else',
  138. ],
  139. ],
  140. ];
  141. /**
  142. * Array of optional configuration information for mocking.
  143. *
  144. * Array structure: Each element is an array whose first element is a
  145. * provider name, and second is an array of config items it provides.
  146. *
  147. * @var array
  148. *
  149. * @see ConfigUpdateUnitTestBase::getConfigStorageMock()
  150. */
  151. protected $configStorageOptionalInfo = [
  152. ['foo.bar', []],
  153. ['foo.barbaz', ['foo.barbaz.four']],
  154. // This next item is assumed to be element 2 of the array. If not, you
  155. // will need to change ConfigUpdateUnitTestBase::getConfigStorageMock().
  156. ['', ['foo.barbaz.four']],
  157. ];
  158. /**
  159. * Creates a mock config storage object for the test.
  160. *
  161. * @param string $type
  162. * Type of storage object to return: 'active', 'extension', or 'optional'.
  163. * In active storage, the read() method is mocked to assume you are reading
  164. * core.extension to get the profile name, so it returns that information.
  165. * For extension and optional storage, the getComponentNames() method is
  166. * mocked, and for all storages, the listAll() method is mocked.
  167. *
  168. * @see ConfigUpdateUnitTestBase::configStorageActiveInfo
  169. * @see ConfigUpdateUnitTestBase::configStorageExtensionInfo
  170. * @see ConfigUpdateUnitTestBase::configStorageOptionalInfo
  171. */
  172. protected function getConfigStorageMock($type) {
  173. if ($type == 'active') {
  174. $storage = $this->getMockBuilder('Drupal\Core\Config\StorageInterface')->getMock();
  175. // Various tests assume various values of configuration that need to be
  176. // read from active storage.
  177. $map = [
  178. ['core.extension', ['profile' => 'standard']],
  179. ['foo.bar.one', ['foo.bar.one' => 'active', 'id' => 'one']],
  180. ['missing', FALSE],
  181. ['in.extension',
  182. ['in.extension' => 'active', '_core' => 'core_for_in.extension'],
  183. ],
  184. ['in.both', ['in.both' => 'active']],
  185. ['in.optional', ['in.optional' => 'active']],
  186. ];
  187. $storage
  188. ->method('read')
  189. ->will($this->returnValueMap($map));
  190. $storage
  191. ->method('listAll')
  192. ->will($this->returnValueMap($this->configStorageActiveInfo));
  193. }
  194. elseif ($type == 'extension') {
  195. $storage = $this->getMockBuilder('Drupal\Core\Config\ExtensionInstallStorage')->disableOriginalConstructor()->getMock();
  196. $value = [];
  197. foreach ($this->configStorageExtensionInfo[2][1] as $item) {
  198. $value[$item] = 'ignored';
  199. }
  200. $storage
  201. ->method('getComponentNames')
  202. ->willReturn($value);
  203. $storage
  204. ->method('listAll')
  205. ->will($this->returnValueMap($this->configStorageExtensionInfo));
  206. $map = [
  207. ['in.extension', ['in.extension' => 'extension']],
  208. ['in.both', ['in.both' => 'extension']],
  209. ['in.optional', FALSE],
  210. ['foo.bar.one', ['foo.bar.one' => 'extension', 'id' => 'one']],
  211. ['another', ['another' => 'extension', 'id' => 'one']],
  212. ['missing2', FALSE],
  213. ];
  214. $storage
  215. ->method('read')
  216. ->will($this->returnValueMap($map));
  217. }
  218. else {
  219. $storage = $this->getMockBuilder('Drupal\Core\Config\ExtensionInstallStorage')->disableOriginalConstructor()->getMock();
  220. $value = [];
  221. foreach ($this->configStorageOptionalInfo[2][1] as $item) {
  222. $value[$item] = 'ignored';
  223. }
  224. $storage
  225. ->method('getComponentNames')
  226. ->willReturn($value);
  227. $storage
  228. ->method('listAll')
  229. ->will($this->returnValueMap($this->configStorageOptionalInfo));
  230. $map = [
  231. ['in.optional', ['in.optional' => 'optional']],
  232. ['in.both', ['in.both' => 'optional']],
  233. ['missing2', FALSE],
  234. ];
  235. $storage
  236. ->method('read')
  237. ->will($this->returnValueMap($map));
  238. }
  239. return $storage;
  240. }
  241. /**
  242. * Creates a mock module handler for the test.
  243. */
  244. protected function getModuleHandlerMock() {
  245. $manager = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandlerInterface')->getMock();
  246. $manager->method('getModuleList')
  247. ->willReturn(['foo_module' => '', 'standard' => '']);
  248. return $manager;
  249. }
  250. /**
  251. * Creates a mock theme handler for the test.
  252. */
  253. protected function getThemeHandlerMock() {
  254. $manager = $this->getMockBuilder('Drupal\Core\Extension\ThemeHandlerInterface')->getMock();
  255. $manager->method('listInfo')
  256. ->willReturn(['foo_theme' => '']);
  257. return $manager;
  258. }
  259. /**
  260. * Creates a mock string translation class for the test.
  261. */
  262. protected function getTranslationMock() {
  263. $translation = $this->getMockBuilder('Drupal\Core\StringTranslation\TranslationInterface')->getMock();
  264. $translation
  265. ->method('translateString')
  266. ->will($this->returnCallback([$this, 'mockTranslate']));
  267. return $translation;
  268. }
  269. /**
  270. * Mocks the translateString() method for the string translation mock object.
  271. *
  272. * @param \Drupal\Core\StringTranslation\TranslatableMarkup $input
  273. * Object to translate.
  274. *
  275. * @return string
  276. * The untranslated string from $input.
  277. */
  278. public function mockTranslate(TranslatableMarkup $input) {
  279. return $input->getUntranslatedString();
  280. }
  281. /**
  282. * List of mock-dispatched events.
  283. *
  284. * Each element of the array is the call parameters to dispatchEvent() in
  285. * the mocked dispatch class: name and event instance.
  286. *
  287. * @var array
  288. *
  289. * @see ConfigUpdateUnitTestBase::getEventDispatcherMock()
  290. */
  291. protected $dispatchedEvents = [];
  292. /**
  293. * Mocks the event dispatcher service.
  294. *
  295. * Stores dispatched events in ConfigUpdateUnitTestBase::dispatchedEvents.
  296. */
  297. protected function getEventDispatcherMock() {
  298. $event = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
  299. $event
  300. ->method('dispatch')
  301. ->will($this->returnCallback([$this, 'mockDispatch']));
  302. return $event;
  303. }
  304. /**
  305. * Mocks event dispatch.
  306. *
  307. * For \Symfony\Component\EventDispatcher\EventDispatchInterface::dispatch().
  308. */
  309. public function mockDispatch($name, Event $event = NULL) {
  310. $this->dispatchedEvents[] = [$name, $event];
  311. }
  312. /**
  313. * Mock config storage for the mock config factory.
  314. *
  315. * This is actually managed by the MockConfig class in this file.
  316. *
  317. * @var array
  318. */
  319. protected $configStorage = [];
  320. /**
  321. * Gets the value of the mocked config storage.
  322. */
  323. public function getConfigStorage() {
  324. return $this->configStorage;
  325. }
  326. /**
  327. * Sets the value of the mocked config storage.
  328. */
  329. public function setConfigStorage($values) {
  330. $this->configStorage = $values;
  331. }
  332. /**
  333. * Creates a mock config factory class for the test.
  334. */
  335. protected function getConfigFactoryMock() {
  336. $config = $this->getMockBuilder('Drupal\Core\Config\ConfigFactoryInterface')->getMock();
  337. $config
  338. ->method('getEditable')
  339. ->will($this->returnCallback([$this, 'mockGetEditable']));
  340. return $config;
  341. }
  342. /**
  343. * Mocks the getEditable() method for the mock config factory.
  344. *
  345. * @param string $name
  346. * Name of the config object to get an editable object for.
  347. *
  348. * @return MockConfig
  349. * Editable mock config object.
  350. */
  351. public function mockGetEditable($name) {
  352. return new MockConfig($name, '', $this);
  353. }
  354. }
  355. /**
  356. * Mock class for mutable configuration, config entity, and entity storage.
  357. */
  358. class MockConfig {
  359. /**
  360. * Name of the config.
  361. *
  362. * @var string
  363. */
  364. protected $name = '';
  365. /**
  366. * Prefix for the entity type being mocked, for entity storage mocking.
  367. *
  368. * @var string
  369. */
  370. protected $entityPrefix = '';
  371. /**
  372. * Test class this comes from.
  373. *
  374. * @var \Drupal\Tests\config_update\Unit\ConfigUpdateUnitTestBase
  375. */
  376. protected $test;
  377. /**
  378. * Current value of the configuration.
  379. *
  380. * @var array
  381. */
  382. protected $value = '';
  383. /**
  384. * Constructs a mock config object.
  385. *
  386. * @param string $name
  387. * Name of the config that is being mocked. Can be blank.
  388. * @param string $entity_prefix
  389. * Prefix for the entity type that is being mocked. Often blank.
  390. * @param \Drupal\Tests\config_update\Unit\ConfigUpdateUnitTestBase $test
  391. * Test class this comes from.
  392. */
  393. public function __construct($name, $entity_prefix, ConfigUpdateUnitTestBase $test) {
  394. $this->name = $name;
  395. $this->entityPrefix = $entity_prefix;
  396. $this->test = $test;
  397. $storage = $test->getConfigStorage();
  398. if ($name && isset($storage[$name])) {
  399. $value = $storage[$name];
  400. $value['is_new'] = FALSE;
  401. }
  402. else {
  403. $value['is_new'] = TRUE;
  404. }
  405. $value['_core'] = 'core_for_' . $name;
  406. $this->value = $value;
  407. }
  408. /**
  409. * Gets a component of the configuration value.
  410. */
  411. public function get($key) {
  412. return isset($this->value[$key]) ? $this->value[$key] : NULL;
  413. }
  414. /**
  415. * Sets a component of the configuration value.
  416. */
  417. public function set($key, $value) {
  418. $this->value[$key] = $value;
  419. return $this;
  420. }
  421. /**
  422. * Sets the entire configuration value.
  423. */
  424. public function setData($value) {
  425. // Retain the _core key.
  426. $core = isset($this->value['_core']) ? $this->value['_core'] : '';
  427. $this->value = $value;
  428. if ($core) {
  429. $this->value['_core'] = $core;
  430. }
  431. return $this;
  432. }
  433. /**
  434. * Saves the configuration.
  435. */
  436. public function save() {
  437. $config = $this->test->getConfigStorage();
  438. $config[$this->name] = $this->value;
  439. $this->test->setConfigStorage($config);
  440. return $this;
  441. }
  442. /**
  443. * Deletes the configuration.
  444. */
  445. public function delete() {
  446. $config = $this->test->getConfigStorage();
  447. unset($config[$this->name]);
  448. $this->test->setConfigStorage($config);
  449. return $this;
  450. }
  451. /**
  452. * Mocks the createFromStorageRecord() method from entity storage.
  453. */
  454. public function createFromStorageRecord($values) {
  455. if (!$this->entityPrefix) {
  456. return NULL;
  457. }
  458. // This is supposed to return an entity, but the only method we need is
  459. // save(), so instead set up and return this object.
  460. $this->name = $this->entityPrefix . '.' . $values['id'];
  461. $this->value = $values;
  462. $this->value['_core'] = 'core_for_' . $this->name;
  463. return $this;
  464. }
  465. /**
  466. * Mocks the updateFromStorageRecord() method from entity storage.
  467. */
  468. public function updateFromStorageRecord($object, $values) {
  469. return $object->createFromStorageRecord($values);
  470. }
  471. /**
  472. * Mocks the load() method for entity storage.
  473. */
  474. public function load($id) {
  475. $full_name = $this->entityPrefix . '.' . $id;
  476. $configs = $this->test->getConfigStorage();
  477. if (isset($configs[$full_name])) {
  478. $this->value = $configs[$full_name];
  479. $this->name = $full_name;
  480. $this->value['_core'] = 'core_for_' . $full_name;
  481. return $this;
  482. }
  483. return NULL;
  484. }
  485. }