AbstractIntegrationCaseFactory.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <?php
  2. /*
  3. * This file is part of PHP CS Fixer.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  7. *
  8. * This source file is subject to the MIT license that is bundled
  9. * with this source code in the file LICENSE.
  10. */
  11. namespace PhpCsFixer\Tests\Test;
  12. use PhpCsFixer\RuleSet;
  13. use Symfony\Component\Finder\SplFileInfo;
  14. /**
  15. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  16. *
  17. * @internal
  18. */
  19. abstract class AbstractIntegrationCaseFactory implements IntegrationCaseFactoryInterface
  20. {
  21. /**
  22. * @param SplFileInfo $file
  23. *
  24. * @return IntegrationCase
  25. */
  26. public function create(SplFileInfo $file)
  27. {
  28. try {
  29. if (!preg_match(
  30. '/^
  31. --TEST-- \r?\n(?<title> .*?)
  32. \s --RULESET-- \r?\n(?<ruleset> .*?)
  33. (?:\s --CONFIG-- \r?\n(?<config> .*?))?
  34. (?:\s --SETTINGS-- \r?\n(?<settings> .*?))?
  35. (?:\s --REQUIREMENTS-- \r?\n(?<requirements> .*?))?
  36. (?:\s --EXPECT-- \r?\n(?<expect> .*?\r?\n*))?
  37. (?:\s --INPUT-- \r?\n(?<input> .*))?
  38. $/sx',
  39. $file->getContents(),
  40. $match
  41. )) {
  42. throw new \InvalidArgumentException('File format is invalid.');
  43. }
  44. $match = array_merge(
  45. [
  46. 'config' => null,
  47. 'settings' => null,
  48. 'requirements' => null,
  49. 'expect' => null,
  50. 'input' => null,
  51. ],
  52. $match
  53. );
  54. return new IntegrationCase(
  55. $file->getRelativePathname(),
  56. $this->determineTitle($file, $match['title']),
  57. $this->determineSettings($file, $match['settings']),
  58. $this->determineRequirements($file, $match['requirements']),
  59. $this->determineConfig($file, $match['config']),
  60. $this->determineRuleset($file, $match['ruleset']),
  61. $this->determineExpectedCode($file, $match['expect']),
  62. $this->determineInputCode($file, $match['input'])
  63. );
  64. } catch (\InvalidArgumentException $e) {
  65. throw new \InvalidArgumentException(
  66. sprintf('%s Test file: "%s".', $e->getMessage(), $file->getRelativePathname()),
  67. $e->getCode(),
  68. $e
  69. );
  70. }
  71. }
  72. /**
  73. * Parses the '--CONFIG--' block of a '.test' file.
  74. *
  75. * @param SplFileInfo $file
  76. * @param string $config
  77. *
  78. * @return array
  79. */
  80. protected function determineConfig(SplFileInfo $file, $config)
  81. {
  82. $parsed = $this->parseJson($config, [
  83. 'indent' => ' ',
  84. 'lineEnding' => "\n",
  85. ]);
  86. if (!\is_string($parsed['indent'])) {
  87. throw new \InvalidArgumentException(sprintf(
  88. 'Expected string value for "indent", got "%s".',
  89. \is_object($parsed['indent']) ? \get_class($parsed['indent']) : \gettype($parsed['indent']).'#'.$parsed['indent']
  90. ));
  91. }
  92. if (!\is_string($parsed['lineEnding'])) {
  93. throw new \InvalidArgumentException(sprintf(
  94. 'Expected string value for "lineEnding", got "%s".',
  95. \is_object($parsed['lineEnding']) ? \get_class($parsed['lineEnding']) : \gettype($parsed['lineEnding']).'#'.$parsed['lineEnding']
  96. ));
  97. }
  98. return $parsed;
  99. }
  100. /**
  101. * Parses the '--REQUIREMENTS--' block of a '.test' file and determines requirements.
  102. *
  103. * @param SplFileInfo $file
  104. * @param string $config
  105. *
  106. * @return array
  107. */
  108. protected function determineRequirements(SplFileInfo $file, $config)
  109. {
  110. $parsed = $this->parseJson($config, [
  111. 'php' => \PHP_VERSION_ID,
  112. ]);
  113. if (!\is_int($parsed['php'])) {
  114. throw new \InvalidArgumentException(sprintf(
  115. 'Expected int value like 50509 for "php", got "%s".',
  116. \is_object($parsed['php']) ? \get_class($parsed['php']) : \gettype($parsed['php']).'#'.$parsed['php']
  117. ));
  118. }
  119. return $parsed;
  120. }
  121. /**
  122. * Parses the '--RULESET--' block of a '.test' file and determines what fixers should be used.
  123. *
  124. * @param SplFileInfo $file
  125. * @param string $config
  126. *
  127. * @return RuleSet
  128. */
  129. protected function determineRuleset(SplFileInfo $file, $config)
  130. {
  131. return new RuleSet($this->parseJson($config));
  132. }
  133. /**
  134. * Parses the '--TEST--' block of a '.test' file and determines title.
  135. *
  136. * @param SplFileInfo $file
  137. * @param string $config
  138. *
  139. * @return string
  140. */
  141. protected function determineTitle(SplFileInfo $file, $config)
  142. {
  143. return $config;
  144. }
  145. /**
  146. * Parses the '--SETTINGS--' block of a '.test' file and determines settings.
  147. *
  148. * @param SplFileInfo $file
  149. * @param string $config
  150. *
  151. * @return array
  152. */
  153. protected function determineSettings(SplFileInfo $file, $config)
  154. {
  155. $parsed = $this->parseJson($config, [
  156. 'checkPriority' => true,
  157. ]);
  158. if (!\is_bool($parsed['checkPriority'])) {
  159. throw new \InvalidArgumentException(sprintf(
  160. 'Expected bool value for "checkPriority", got "%s".',
  161. \is_object($parsed['checkPriority']) ? \get_class($parsed['checkPriority']) : \gettype($parsed['checkPriority']).'#'.$parsed['checkPriority']
  162. ));
  163. }
  164. return $parsed;
  165. }
  166. /**
  167. * @param SplFileInfo $file
  168. * @param null|string $code
  169. *
  170. * @return string
  171. */
  172. protected function determineExpectedCode(SplFileInfo $file, $code)
  173. {
  174. $code = $this->determineCode($file, $code, '-out.php');
  175. if (null === $code) {
  176. throw new \InvalidArgumentException('Missing expected code.');
  177. }
  178. return $code;
  179. }
  180. /**
  181. * @param SplFileInfo $file
  182. * @param null|string $code
  183. *
  184. * @return null|string
  185. */
  186. protected function determineInputCode(SplFileInfo $file, $code)
  187. {
  188. return $this->determineCode($file, $code, '-in.php');
  189. }
  190. /**
  191. * @param SplFileInfo $file
  192. * @param null|string $code
  193. * @param string $suffix
  194. *
  195. * @return null|string
  196. */
  197. private function determineCode(SplFileInfo $file, $code, $suffix)
  198. {
  199. if (null !== $code) {
  200. return $code;
  201. }
  202. $candidateFile = new SplFileInfo($file->getPathname().$suffix, '', '');
  203. if ($candidateFile->isFile()) {
  204. return $candidateFile->getContents();
  205. }
  206. }
  207. /**
  208. * @param null|string $encoded
  209. * @param null|array $template
  210. *
  211. * @return array
  212. */
  213. private function parseJson($encoded, array $template = null)
  214. {
  215. // content is optional if template is provided
  216. if (!$encoded && null !== $template) {
  217. $decoded = [];
  218. } else {
  219. $decoded = json_decode($encoded, true);
  220. if (JSON_ERROR_NONE !== json_last_error()) {
  221. throw new \InvalidArgumentException(sprintf('Malformed JSON: "%s", error: "%s".', $encoded, json_last_error_msg()));
  222. }
  223. }
  224. if (null !== $template) {
  225. $decoded = array_merge(
  226. $template,
  227. array_intersect_key(
  228. $decoded,
  229. array_flip(array_keys($template))
  230. )
  231. );
  232. }
  233. return $decoded;
  234. }
  235. }