AbstractIntegrationCaseFactory.php 7.4 KB

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