IntegrationCaseFactory.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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. final class IntegrationCaseFactory
  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. array(
  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. $match['title'],
  57. $this->determineSettings($match['settings']),
  58. $this->determineRequirements($match['requirements']),
  59. $this->determineConfig($match['config']),
  60. $this->determineRuleset($match['ruleset']),
  61. $this->determineExpectedCode($match['expect'], $file),
  62. $this->determineInputCode($match['input'], $file)
  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 string $config
  76. *
  77. * @return array
  78. */
  79. private function determineConfig($config)
  80. {
  81. $parsed = $this->parseJson($config, array(
  82. 'indent' => ' ',
  83. 'lineEnding' => "\n",
  84. ));
  85. if (!is_string($parsed['indent'])) {
  86. throw new \InvalidArgumentException(sprintf(
  87. 'Expected string value for "indent", got "%s".',
  88. is_object($parsed['indent']) ? get_class($parsed['indent']) : gettype($parsed['indent']).'#'.$parsed['indent']
  89. ));
  90. }
  91. if (!is_string($parsed['lineEnding'])) {
  92. throw new \InvalidArgumentException(sprintf(
  93. 'Expected string value for "lineEnding", got "%s".',
  94. is_object($parsed['lineEnding']) ? get_class($parsed['lineEnding']) : gettype($parsed['lineEnding']).'#'.$parsed['lineEnding']
  95. ));
  96. }
  97. return $parsed;
  98. }
  99. /**
  100. * Parses the '--REQUIREMENTS--' block of a '.test' file and determines requirements.
  101. *
  102. * @param string $config
  103. *
  104. * @return array
  105. */
  106. private function determineRequirements($config)
  107. {
  108. $parsed = $this->parseJson($config, array(
  109. 'hhvm' => true,
  110. 'php' => PHP_VERSION_ID,
  111. ));
  112. if (!is_int($parsed['php']) || $parsed['php'] < 50306) {
  113. throw new \InvalidArgumentException(sprintf(
  114. 'Expected int >= 50306 value for "php", got "%s".',
  115. is_object($parsed['php']) ? get_class($parsed['php']) : gettype($parsed['php']).'#'.$parsed['php']
  116. ));
  117. }
  118. if (!is_bool($parsed['hhvm'])) {
  119. throw new \InvalidArgumentException(sprintf(
  120. 'Expected bool value for "hhvm", got "%s".',
  121. is_object($parsed['hhvm']) ? get_class($parsed['hhvm']) : gettype($parsed['hhvm']).'#'.$parsed['hhvm']
  122. ));
  123. }
  124. return $parsed;
  125. }
  126. /**
  127. * Parses the '--RULESET--' block of a '.test' file and determines what fixers should be used.
  128. *
  129. * @param string $config
  130. *
  131. * @return RuleSet
  132. */
  133. private function determineRuleset($config)
  134. {
  135. return new RuleSet($this->parseJson($config));
  136. }
  137. /**
  138. * Parses the '--SETTINGS--' block of a '.test' file and determines settings.
  139. *
  140. * @param string $config
  141. *
  142. * @return array
  143. */
  144. private function determineSettings($config)
  145. {
  146. $parsed = $this->parseJson($config, array(
  147. 'checkPriority' => true,
  148. ));
  149. if (!is_bool($parsed['checkPriority'])) {
  150. throw new \InvalidArgumentException(sprintf(
  151. 'Expected bool value for "checkPriority", got "%s".',
  152. is_object($parsed['checkPriority']) ? get_class($parsed['checkPriority']) : gettype($parsed['checkPriority']).'#'.$parsed['checkPriority']
  153. ));
  154. }
  155. return $parsed;
  156. }
  157. /**
  158. * @param null|string $code
  159. * @param SplFileInfo $file
  160. *
  161. * @return string
  162. */
  163. private function determineExpectedCode($code, SplFileInfo $file)
  164. {
  165. $code = $this->determineCode($code, $file, '-out.php');
  166. if (null === $code) {
  167. throw new \InvalidArgumentException('Missing expected code.');
  168. }
  169. return $code;
  170. }
  171. /**
  172. * @param null|string $code
  173. * @param SplFileInfo $file
  174. *
  175. * @return null|string
  176. */
  177. private function determineInputCode($code, SplFileInfo $file)
  178. {
  179. return $this->determineCode($code, $file, '-in.php');
  180. }
  181. /**
  182. * @param null|string $code
  183. * @param SplFileInfo $file
  184. * @param string $suffix
  185. *
  186. * @return null|string
  187. */
  188. private function determineCode($code, SplFileInfo $file, $suffix)
  189. {
  190. if (null !== $code) {
  191. return $code;
  192. }
  193. $candidateFile = new SplFileInfo($file->getPathname().$suffix, '', '');
  194. if ($candidateFile->isFile()) {
  195. return $candidateFile->getContents();
  196. }
  197. }
  198. /**
  199. * @param null|string $encoded
  200. * @param null|array $template
  201. *
  202. * @return array
  203. */
  204. private function parseJson($encoded, array $template = null)
  205. {
  206. // content is optional if template is provided
  207. if (!$encoded && null !== $template) {
  208. $decoded = array();
  209. } else {
  210. $decoded = json_decode($encoded, true);
  211. if (JSON_ERROR_NONE !== json_last_error()) {
  212. throw new \InvalidArgumentException(sprintf('Malformed JSON: "%s", error: "%s".', $encoded, json_last_error_msg()));
  213. }
  214. }
  215. if (null !== $template) {
  216. $decoded = array_merge(
  217. $template,
  218. array_intersect_key(
  219. $decoded,
  220. array_flip(array_keys($template))
  221. )
  222. );
  223. }
  224. return $decoded;
  225. }
  226. }