PageRenderTime 26ms CodeModel.GetById 47ms RepoModel.GetById 32ms app.codeStats 0ms

/dev/tests/static/testsuite/Magento/Test/Integrity/Di/CompilerTest.php

https://gitlab.com/crazybutterfly815/magento2
PHP | 405 lines | 337 code | 18 blank | 50 comment | 5 complexity | a47f179ff53129d3da9ec77be978b567 MD5 | raw file
  1. <?php
  2. /**
  3. * Compiler test. Check compilation of DI definitions and code generation
  4. *
  5. * Copyright © 2016 Magento. All rights reserved.
  6. * See COPYING.txt for license details.
  7. */
  8. namespace Magento\Test\Integrity\Di;
  9. use Magento\Framework\Api\Code\Generator\Mapper;
  10. use Magento\Framework\Api\Code\Generator\SearchResults;
  11. use Magento\Framework\App\Filesystem\DirectoryList;
  12. use Magento\Framework\Component\ComponentRegistrar;
  13. use Magento\Framework\Interception\Code\InterfaceValidator;
  14. use Magento\Framework\ObjectManager\Code\Generator\Converter;
  15. use Magento\Framework\ObjectManager\Code\Generator\Factory;
  16. use Magento\Framework\ObjectManager\Code\Generator\Repository;
  17. use Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator;
  18. use Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator;
  19. use Magento\Framework\App\Utility\Files;
  20. use Magento\TestFramework\Integrity\PluginValidator;
  21. /**
  22. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  23. */
  24. class CompilerTest extends \PHPUnit_Framework_TestCase
  25. {
  26. /**
  27. * @var string
  28. */
  29. protected $_command;
  30. /**
  31. * @var \Magento\Framework\Shell
  32. */
  33. protected $_shell;
  34. /**
  35. * @var string
  36. */
  37. protected $_generationDir;
  38. /**
  39. * @var string
  40. */
  41. protected $_compilationDir;
  42. /**
  43. * @var \Magento\Framework\ObjectManager\Config\Mapper\Dom()
  44. */
  45. protected $_mapper;
  46. /**
  47. * @var \Magento\Framework\Code\Validator
  48. */
  49. protected $_validator;
  50. /**
  51. * Class arguments reader
  52. *
  53. * @var PluginValidator
  54. */
  55. protected $pluginValidator;
  56. protected function setUp()
  57. {
  58. $this->_shell = new \Magento\Framework\Shell(new \Magento\Framework\Shell\CommandRenderer());
  59. $basePath = BP;
  60. $basePath = str_replace('\\', '/', $basePath);
  61. $directoryList = new DirectoryList($basePath);
  62. $this->_generationDir = $directoryList->getPath(DirectoryList::GENERATION);
  63. $this->_compilationDir = $directoryList->getPath(DirectoryList::DI);
  64. $this->_command = 'php ' . $basePath . '/bin/magento setup:di:compile';
  65. $booleanUtils = new \Magento\Framework\Stdlib\BooleanUtils();
  66. $constInterpreter = new \Magento\Framework\Data\Argument\Interpreter\Constant();
  67. $argumentInterpreter = new \Magento\Framework\Data\Argument\Interpreter\Composite(
  68. [
  69. 'boolean' => new \Magento\Framework\Data\Argument\Interpreter\Boolean($booleanUtils),
  70. 'string' => new \Magento\Framework\Data\Argument\Interpreter\StringUtils($booleanUtils),
  71. 'number' => new \Magento\Framework\Data\Argument\Interpreter\Number(),
  72. 'null' => new \Magento\Framework\Data\Argument\Interpreter\NullType(),
  73. 'object' => new \Magento\Framework\Data\Argument\Interpreter\DataObject($booleanUtils),
  74. 'const' => $constInterpreter,
  75. 'init_parameter' => new \Magento\Framework\App\Arguments\ArgumentInterpreter($constInterpreter),
  76. ],
  77. \Magento\Framework\ObjectManager\Config\Reader\Dom::TYPE_ATTRIBUTE
  78. );
  79. // Add interpreters that reference the composite
  80. $argumentInterpreter->addInterpreter(
  81. 'array',
  82. new \Magento\Framework\Data\Argument\Interpreter\ArrayType($argumentInterpreter)
  83. );
  84. $this->_mapper = new \Magento\Framework\ObjectManager\Config\Mapper\Dom(
  85. $argumentInterpreter,
  86. $booleanUtils,
  87. new \Magento\Framework\ObjectManager\Config\Mapper\ArgumentParser()
  88. );
  89. $this->_validator = new \Magento\Framework\Code\Validator();
  90. $this->_validator->add(new \Magento\Framework\Code\Validator\ConstructorIntegrity());
  91. $this->_validator->add(new \Magento\Framework\Code\Validator\ContextAggregation());
  92. $this->_validator->add(new \Magento\Framework\Code\Validator\TypeDuplication());
  93. $this->_validator->add(new \Magento\Framework\Code\Validator\ArgumentSequence());
  94. $this->_validator->add(new \Magento\Framework\Code\Validator\ConstructorArgumentTypes());
  95. $this->pluginValidator = new PluginValidator(new InterfaceValidator());
  96. }
  97. /**
  98. * Validate DI config file
  99. *
  100. * @param string $file
  101. */
  102. protected function _validateFile($file)
  103. {
  104. $dom = new \DOMDocument();
  105. $dom->load($file);
  106. $data = $this->_mapper->convert($dom);
  107. foreach ($data as $instanceName => $parameters) {
  108. if (!isset($parameters['parameters']) || empty($parameters['parameters'])) {
  109. continue;
  110. }
  111. if (\Magento\Framework\App\Utility\Classes::isVirtual($instanceName)) {
  112. $instanceName = \Magento\Framework\App\Utility\Classes::resolveVirtualType($instanceName);
  113. }
  114. if (!$this->_classExistsAsReal($instanceName)) {
  115. continue;
  116. }
  117. $reflectionClass = new \ReflectionClass($instanceName);
  118. $constructor = $reflectionClass->getConstructor();
  119. if (!$constructor) {
  120. $this->fail('Class ' . $instanceName . ' does not have __constructor');
  121. }
  122. $parameters = $parameters['parameters'];
  123. $classParameters = $constructor->getParameters();
  124. foreach ($classParameters as $classParameter) {
  125. $parameterName = $classParameter->getName();
  126. if (array_key_exists($parameterName, $parameters)) {
  127. unset($parameters[$parameterName]);
  128. }
  129. }
  130. $message = 'Configuration of ' . $instanceName . ' contains data for non-existed parameters: ' . implode(
  131. ', ',
  132. array_keys($parameters)
  133. );
  134. $this->assertEmpty($parameters, $message);
  135. }
  136. }
  137. /**
  138. * Checks if class is a real one or generated Factory
  139. * @param string $instanceName class name
  140. * @throws \PHPUnit_Framework_AssertionFailedError
  141. * @return bool
  142. */
  143. protected function _classExistsAsReal($instanceName)
  144. {
  145. if (class_exists($instanceName)) {
  146. return true;
  147. }
  148. // check for generated factory
  149. if (substr($instanceName, -7) == 'Factory' && class_exists(substr($instanceName, 0, -7))) {
  150. return false;
  151. }
  152. $this->fail('Detected configuration of non existed class: ' . $instanceName);
  153. }
  154. /**
  155. * Get php classes list
  156. *
  157. * @return array
  158. */
  159. protected function _phpClassesDataProvider()
  160. {
  161. $generationPath = str_replace('/', '\\', $this->_generationDir);
  162. $files = Files::init()->getPhpFiles(Files::INCLUDE_APP_CODE | Files::INCLUDE_LIBS);
  163. $patterns = ['/' . preg_quote($generationPath) . '/',];
  164. $replacements = [''];
  165. $componentRegistrar = new ComponentRegistrar();
  166. foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $modulePath) {
  167. $patterns[] = '/' . preg_quote(str_replace('/', '\\', $modulePath)) . '/';
  168. $replacements[] = '\\' . str_replace('_', '\\', $moduleName);
  169. }
  170. foreach ($componentRegistrar->getPaths(ComponentRegistrar::LIBRARY) as $libPath) {
  171. $patterns[] = '/' . preg_quote(str_replace('/', '\\', $libPath)) . '/';
  172. $replacements[] = '\\Magento\\Framework';
  173. }
  174. /** Convert file names into class name format */
  175. $classes = [];
  176. foreach ($files as $file) {
  177. $file = str_replace('/', '\\', $file);
  178. $filePath = preg_replace($patterns, $replacements, $file);
  179. $className = substr($filePath, 0, -4);
  180. if (class_exists($className, false)) {
  181. $file = str_replace('\\', DIRECTORY_SEPARATOR, $file);
  182. $classes[$file] = $className;
  183. }
  184. }
  185. /** Build class inheritance hierarchy */
  186. $output = [];
  187. $allowedFiles = array_keys($classes);
  188. foreach ($classes as $class) {
  189. if (!in_array($class, $output)) {
  190. $output = array_merge($output, $this->_buildInheritanceHierarchyTree($class, $allowedFiles));
  191. $output = array_unique($output);
  192. }
  193. }
  194. /** Convert data into data provider format */
  195. $outputClasses = [];
  196. foreach ($output as $className) {
  197. $outputClasses[] = [$className];
  198. }
  199. return $outputClasses;
  200. }
  201. /**
  202. * Build inheritance hierarchy tree
  203. *
  204. * @param string $className
  205. * @param array $allowedFiles
  206. * @return array
  207. */
  208. protected function _buildInheritanceHierarchyTree($className, array $allowedFiles)
  209. {
  210. $output = [];
  211. if (0 !== strpos($className, '\\')) {
  212. $className = '\\' . $className;
  213. }
  214. $class = new \ReflectionClass($className);
  215. $parent = $class->getParentClass();
  216. $file = false;
  217. if ($parent) {
  218. $file = str_replace('\\', DIRECTORY_SEPARATOR, $parent->getFileName());
  219. }
  220. /** Prevent analysis of non Magento classes */
  221. if ($parent && in_array($file, $allowedFiles)) {
  222. $output = array_merge(
  223. $this->_buildInheritanceHierarchyTree($parent->getName(), $allowedFiles),
  224. [$className],
  225. $output
  226. );
  227. } else {
  228. $output[] = $className;
  229. }
  230. return array_unique($output);
  231. }
  232. /**
  233. * Validate class
  234. *
  235. * @param string $className
  236. */
  237. protected function _validateClass($className)
  238. {
  239. try {
  240. $this->_validator->validate($className);
  241. } catch (\Magento\Framework\Exception\ValidatorException $exceptions) {
  242. $this->fail($exceptions->getMessage());
  243. } catch (\ReflectionException $exceptions) {
  244. $this->fail($exceptions->getMessage());
  245. }
  246. }
  247. /**
  248. * Validate DI configuration
  249. */
  250. public function testConfigurationOfInstanceParameters()
  251. {
  252. $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
  253. $invoker(
  254. function ($file) {
  255. $this->_validateFile($file);
  256. },
  257. Files::init()->getDiConfigs(true)
  258. );
  259. }
  260. /**
  261. * Validate constructor integrity
  262. */
  263. public function testConstructorIntegrity()
  264. {
  265. $generatorIo = new \Magento\Framework\Code\Generator\Io(
  266. new \Magento\Framework\Filesystem\Driver\File(),
  267. $this->_generationDir
  268. );
  269. $generator = new \Magento\Framework\Code\Generator(
  270. $generatorIo,
  271. [
  272. Factory::ENTITY_TYPE => \Magento\Framework\ObjectManager\Code\Generator\Factory::class,
  273. Repository::ENTITY_TYPE => \Magento\Framework\ObjectManager\Code\Generator\Repository::class,
  274. Converter::ENTITY_TYPE => \Magento\Framework\ObjectManager\Code\Generator\Converter::class,
  275. Mapper::ENTITY_TYPE => \Magento\Framework\Api\Code\Generator\Mapper::class,
  276. SearchResults::ENTITY_TYPE => \Magento\Framework\Api\Code\Generator\SearchResults::class,
  277. ExtensionAttributesInterfaceGenerator::ENTITY_TYPE =>
  278. \Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator::class,
  279. ExtensionAttributesGenerator::ENTITY_TYPE =>
  280. \Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator::class
  281. ]
  282. );
  283. $generationAutoloader = new \Magento\Framework\Code\Generator\Autoloader($generator);
  284. spl_autoload_register([$generationAutoloader, 'load']);
  285. $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
  286. $invoker(
  287. function ($className) {
  288. $this->_validateClass($className);
  289. },
  290. $this->_phpClassesDataProvider()
  291. );
  292. spl_autoload_unregister([$generationAutoloader, 'load']);
  293. }
  294. /**
  295. * Test consistency of plugin interfaces
  296. */
  297. public function testPluginInterfaces()
  298. {
  299. $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
  300. $invoker(
  301. function ($plugin, $type) {
  302. $this->validatePlugins($plugin, $type);
  303. },
  304. $this->pluginDataProvider()
  305. );
  306. }
  307. /**
  308. * Validate plugin interface
  309. *
  310. * @param string $plugin
  311. * @param string $type
  312. */
  313. protected function validatePlugins($plugin, $type)
  314. {
  315. try {
  316. $module = \Magento\Framework\App\Utility\Classes::getClassModuleName($type);
  317. if (Files::init()->isModuleExists($module)) {
  318. $this->pluginValidator->validate($plugin, $type);
  319. }
  320. } catch (\Magento\Framework\Exception\ValidatorException $exception) {
  321. $this->fail($exception->getMessage());
  322. }
  323. }
  324. /**
  325. * Get application plugins
  326. *
  327. * @return array
  328. */
  329. protected function pluginDataProvider()
  330. {
  331. $files = Files::init()->getDiConfigs();
  332. $plugins = [];
  333. foreach ($files as $file) {
  334. $dom = new \DOMDocument();
  335. $dom->load($file);
  336. $xpath = new \DOMXPath($dom);
  337. $pluginList = $xpath->query('//config/type/plugin');
  338. foreach ($pluginList as $node) {
  339. /** @var $node \DOMNode */
  340. $type = $node->parentNode->attributes->getNamedItem('name')->nodeValue;
  341. $type = \Magento\Framework\App\Utility\Classes::resolveVirtualType($type);
  342. if ($node->attributes->getNamedItem('type')) {
  343. $plugin = $node->attributes->getNamedItem('type')->nodeValue;
  344. $plugin = \Magento\Framework\App\Utility\Classes::resolveVirtualType($plugin);
  345. $plugins[] = ['plugin' => $plugin, 'intercepted type' => $type];
  346. }
  347. }
  348. }
  349. return $plugins;
  350. }
  351. /**
  352. * Test DI compiler
  353. *
  354. * @depends testConfigurationOfInstanceParameters
  355. * @depends testConstructorIntegrity
  356. * @depends testPluginInterfaces
  357. */
  358. public function testCompiler()
  359. {
  360. $this->markTestSkipped('MAGETWO-52570');
  361. try {
  362. $this->_shell->execute($this->_command);
  363. } catch (\Magento\Framework\Exception\LocalizedException $exception) {
  364. $this->fail($exception->getPrevious()->getMessage());
  365. }
  366. }
  367. }