PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/LimeCli.php

http://github.com/bschussek/lime
PHP | 397 lines | 269 code | 76 blank | 52 comment | 31 complexity | 9990cab9a8648495915a14c8e1ac127c MD5 | raw file
Possible License(s): ISC
  1. <?php
  2. /*
  3. * This file is part of the Lime framework.
  4. *
  5. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  6. * (c) Bernhard Schussek <bernhard.schussek@symfony-project.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. /**
  12. * Runs the Lime CLI commands.
  13. *
  14. * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
  15. */
  16. class LimeCli
  17. {
  18. protected static $allowedOptions = array(
  19. 'help',
  20. 'init',
  21. 'processes',
  22. 'suffix',
  23. 'color',
  24. 'verbose',
  25. 'serialize',
  26. 'output',
  27. 'test',
  28. );
  29. /**
  30. * Runs a command with the given CLI arguments.
  31. *
  32. * @param array $arguments The CLI arguments
  33. * @return integer The return value of the command (0 if successful)
  34. */
  35. public function run(array $arguments)
  36. {
  37. try
  38. {
  39. list($options, $labels) = $this->parseArguments($arguments);
  40. if ($diff = array_diff(array_keys($options), self::$allowedOptions))
  41. {
  42. throw new Exception(sprintf('Unknown option(s): "%s"', implode('", "', $diff)));
  43. }
  44. if (isset($options['help']))
  45. {
  46. return $this->usage($options);
  47. }
  48. else if (isset($options['init']))
  49. {
  50. return $this->init($options);
  51. }
  52. else
  53. {
  54. return $this->test(array_slice($labels, 1), $options);
  55. }
  56. }
  57. catch (Exception $e)
  58. {
  59. echo "CLI: ".$e->getMessage()."\n";
  60. return 1;
  61. }
  62. }
  63. /**
  64. * Prints the usage information.
  65. *
  66. * @return integer The return value of the command (0 if successful)
  67. */
  68. protected function usage(array $options)
  69. {
  70. echo <<<EOF
  71. Command line utility for the Lime 2 test framework.
  72. Usage:
  73. Execute all tests set up in lime.config.php:
  74. lime
  75. Execute the test with a specific name:
  76. lime --test=<name>
  77. The name is the test file name without the suffix configured in
  78. lime.config.php.
  79. Execute all tests in <label1> AND <label2>:
  80. lime <label1> <label2>...
  81. Execute all tests in <label1> OR <label2>:
  82. lime <label1> +<label2>...
  83. Execute all tests in <label1> EXCEPT those also in <label2>:
  84. lime <label1> -<label2>...
  85. Options:
  86. --color Enforces colorization in the console output.
  87. --help This help
  88. --init Initializes the current working directory for
  89. use with Lime 2. You should adapt the generated
  90. lime.config.php to include your test files and
  91. to set up labels.
  92. --output=<output> Changes the output of the test. Can be one of
  93. "raw", "xml", "suite" and "tap".
  94. --processes=<n> Sets the number of processes to use.
  95. --serialize Enables serialization of the output. Only works
  96. with some output types (option --output).
  97. --test=<test> Executes a single test. The test name is the file name
  98. without the suffix configured in lime.config.php.
  99. --verbose Enables verbose output. Only works with some
  100. output types (option --output).
  101. Examples:
  102. Execute MyClassTest:
  103. lime --test=MyClass
  104. Execute all tests that are in label "unit" and "model" at the same
  105. time, but that are not in label "slow":
  106. lime unit model -slow
  107. Execute all tests in label "unit" and all tests in label
  108. "functional":
  109. lime unit +functional
  110. Configuration:
  111. The configuration file named lime.config.php is first searched in the
  112. current directory, then recursively in all parent directories. This
  113. means that you can launch lime also from subdirectories of your project.
  114. Included test files and test labels can be configured in the
  115. configuration file. See the user documentation for more information.
  116. EOF;
  117. return 0;
  118. }
  119. /**
  120. * Initializes a project for use with Lime.
  121. *
  122. * @return integer The return value of the command (0 if successful)
  123. */
  124. protected function init(array $options)
  125. {
  126. $absoluteLimeDir = realpath(dirname(__FILE__).'/..');
  127. $skeletonDir = $absoluteLimeDir.DIRECTORY_SEPARATOR.'data'.DIRECTORY_SEPARATOR.'skeleton';
  128. $projectDir = realpath(getcwd());
  129. if (strpos($absoluteLimeDir, $projectDir.DIRECTORY_SEPARATOR) === 0)
  130. {
  131. $relativeLimeDir = substr($absoluteLimeDir, strlen($projectDir.DIRECTORY_SEPARATOR));
  132. }
  133. echo "Creating lime.config.php...";
  134. if (!file_exists($path = $projectDir.DIRECTORY_SEPARATOR.LimeConfiguration::FILENAME))
  135. {
  136. $content = file_get_contents($skeletonDir.DIRECTORY_SEPARATOR.LimeConfiguration::FILENAME);
  137. file_put_contents($path, str_replace("\n", PHP_EOL, $content));
  138. }
  139. else
  140. {
  141. echo " exists already!";
  142. }
  143. echo "\nCreating lime executable...";
  144. if (!file_exists($path = $projectDir.DIRECTORY_SEPARATOR.'lime'))
  145. {
  146. ob_start();
  147. include $skeletonDir.DIRECTORY_SEPARATOR.'lime';
  148. $content = ob_get_clean();
  149. file_put_contents($path, str_replace(array('[?php', "\n"), array('<?php', PHP_EOL), $content));
  150. chmod($path, 0777);
  151. }
  152. else
  153. {
  154. echo " exists already!";
  155. }
  156. echo <<<EOF
  157. Initialized Lime project in $projectDir.
  158. Please add your test files to lime.config.php.
  159. You can find out more about Lime by running
  160. php lime --help
  161. EOF;
  162. return 0;
  163. }
  164. /**
  165. * Tests a given set of labels.
  166. *
  167. * Packages may given with a leading "+" or "-". The tested files are:
  168. *
  169. * * all files that are in all of the labels without leading "+" or "-"
  170. * * all files that are in any label with a leading "+"
  171. * * no files that are in any label with a leading "-"
  172. *
  173. * @param array $labels The label names
  174. * @return integer The return value of the command (0 if successful)
  175. */
  176. protected function test(array $labels, array $options)
  177. {
  178. // don't load configuration in the constructor because then --init does
  179. // not work!
  180. $configuration = LimeConfiguration::read(getcwd());
  181. if ($configuration->getLegacyMode())
  182. {
  183. LimeAutoloader::enableLegacyMode();
  184. }
  185. if (isset($options['processes']))
  186. {
  187. $configuration->setProcesses($options['processes']);
  188. }
  189. if (isset($options['suffix']))
  190. {
  191. $configuration->setSuffix($options['suffix']);
  192. }
  193. if (isset($options['output']))
  194. {
  195. $configuration->setTestOutput($options['output']);
  196. $configuration->setSuiteOutput($options['output']);
  197. }
  198. if (isset($options['color']))
  199. {
  200. $configuration->setForceColors(true);
  201. }
  202. if (isset($options['verbose']))
  203. {
  204. $configuration->setVerbose(true);
  205. }
  206. if (isset($options['serialize']))
  207. {
  208. $configuration->setSerialize(true);
  209. }
  210. if (isset($options['test']))
  211. {
  212. $fileName = $options['test'];
  213. if (!is_readable($fileName))
  214. {
  215. $loader = new LimeLoader($configuration);
  216. $files = $loader->getFilesByName($options['test']);
  217. if (count($files) == 0)
  218. {
  219. throw new Exception("No tests are registered in the test suite! Please add your tests in lime.config.php.");
  220. }
  221. else if (count($files) > 1)
  222. {
  223. $paths = array();
  224. foreach ($files as $file)
  225. {
  226. $paths[] = $file->getPath();
  227. }
  228. throw new Exception(sprintf("The name \"%s\" is ambiguous:\n - %s\nPlease launch the test with the full file path.", $labels[0], implode("\n - ", $paths)));
  229. }
  230. $fileName = $files[0]->getPath();
  231. }
  232. else
  233. {
  234. $fileName = realpath($fileName);
  235. }
  236. $configuration->getTestOutput()->focus($fileName);
  237. try
  238. {
  239. if ($configuration->getAnnotationSupport())
  240. {
  241. $support = new LimeAnnotationSupport($fileName);
  242. $result = $support->execute();
  243. }
  244. else
  245. {
  246. $result = $this->includeTest($fileName);
  247. }
  248. // xUnit compatibility
  249. $class = basename($fileName, '.php');
  250. if (class_exists($class) && is_subclass_of($class, 'LimeTestCase'))
  251. {
  252. $test = new $class($configuration);
  253. return $test->run();
  254. }
  255. else
  256. {
  257. return $result;
  258. }
  259. }
  260. catch (Exception $e)
  261. {
  262. $configuration->getTestOutput()->error(LimeError::fromException($e));
  263. return 1;
  264. }
  265. }
  266. else
  267. {
  268. $loader = new LimeLoader($configuration);
  269. $harness = new LimeHarness($configuration, $loader);
  270. $files = $loader->getFilesByLabels($labels);
  271. if (count($files) == 0)
  272. {
  273. throw new Exception("No tests are registered in the test suite! Please add your tests in lime.config.php.");
  274. }
  275. return $harness->run($files) ? 0 : 1;
  276. }
  277. }
  278. protected function includeTest($__lime_path)
  279. {
  280. LimeAnnotationSupport::setScriptPath($__lime_path);
  281. $lexer = new LimeLexerVariables(array('Test', 'Before', 'After', 'BeforeAll', 'AfterAll'), array('Before'));
  282. // make global variables _really_ global (in case someone uses "global" statements)
  283. foreach ($lexer->parse(file_get_contents($__lime_path)) as $__lime_variable)
  284. {
  285. $__lime_variable = substr($__lime_variable, 1); // strip '$'
  286. global $$__lime_variable;
  287. }
  288. return include $__lime_path;
  289. }
  290. /**
  291. * Parses the given CLI arguments and returns an array of options.
  292. *
  293. * @param array $arguments
  294. * @return array
  295. */
  296. protected function parseArguments(array $arguments)
  297. {
  298. $options = array();
  299. $parameters = array();
  300. foreach ($arguments as $argument)
  301. {
  302. if (preg_match('/^--([a-zA-Z\-]+)=(.+)$/', $argument, $matches))
  303. {
  304. if (in_array($matches[2], array('true', 'false')))
  305. {
  306. $matches[2] = eval($matches[2]);
  307. }
  308. $options[$matches[1]] = $matches[2];
  309. }
  310. else if (preg_match('/^--([a-zA-Z\-]+)$/', $argument, $matches))
  311. {
  312. $options[$matches[1]] = true;
  313. }
  314. else
  315. {
  316. $parameters[] = $argument;
  317. }
  318. }
  319. return array($options, $parameters);
  320. }
  321. }