PageRenderTime 237ms CodeModel.GetById 121ms app.highlight 11ms RepoModel.GetById 99ms app.codeStats 1ms

/lib/LimeCli.php

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