AgaviTesting::processCommandlineOptions()   D
last analyzed

Complexity

Conditions 25
Paths 52

Size

Total Lines 112
Code Lines 82

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 82
nc 52
nop 0
dl 0
loc 112
rs 4.5682
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Agavi\Testing;
3
4
// +---------------------------------------------------------------------------+
5
// | This file is part of the Agavi package.                                   |
6
// | Copyright (c) 2005-2011 the Agavi Project.                                |
7
// |                                                                           |
8
// | For the full copyright and license information, please view the LICENSE   |
9
// | file that was distributed with this source code. You can also view the    |
10
// | LICENSE file online at http://www.agavi.org/LICENSE.txt                   |
11
// |   vi: set noexpandtab:                                                    |
12
// |   Local Variables:                                                        |
13
// |   indent-tabs-mode: t                                                     |
14
// |   End:                                                                    |
15
// +---------------------------------------------------------------------------+
16
17
use Agavi\Config\Config;
18
use Agavi\Config\ConfigCache;
19
use Agavi\Exception\AgaviException;
20
use Agavi\Util\RecursiveDirectoryFilterIterator;
21
use Agavi\Util\Toolkit;
22
use PHP_CodeCoverage_Filter;
23
24
/**
25
 * Main framework class used for autoloading and initial bootstrapping of the
26
 * Agavi testing environment
27
 *
28
 * @package    agavi
29
 * @subpackage testing
30
 *
31
 * @author     Felix Gilcher <[email protected]>
32
 * @copyright  The Agavi Project
33
 *
34
 * @since      1.0.0
35
 * @deprecated 1.1.0 Use PhpUnitCli
36
 *
37
 * @version    $Id$
38
 */
39
class AgaviTesting
40
{
41
    /**
42
     * @var       PHP_CodeCoverage_Filter The code coverage filter for our tests.
43
     */
44
    public static $codeCoverageFilter = null;
45
    
46
    /**
47
     * Get the code coverage filter instance we will use for tests.
48
     * When running PHPUnit 3.5, this will return the singleton instance.
49
     * When running PHPUnit 3.6, this will return the instance we hold internally;
50
     * this same instance will be passed to PHPUnit in AgaviTesting::dispatch().
51
     *
52
     * @return     PHP_CodeCoverage_Filter The code coverage filter for our tests.
53
     *
54
     * @author     David Zülke <[email protected]>
55
     * @since      1.0.7
56
     * @deprecated 1.1.0 Use PhpUnitCli
57
     */
58
    public static function getCodeCoverageFilter()
59
    {
60
        if (self::$codeCoverageFilter === null) {
61
            // PHP_CodeCoverage doesn't expose any version info, we'll have to check if there is a static getInstance method
62
            self::$codeCoverageFilter = method_exists('PHP_CodeCoverage_Filter', 'getInstance') ? PHP_CodeCoverage_Filter::getInstance() : new PHP_CodeCoverage_Filter();
63
        }
64
        
65
        return self::$codeCoverageFilter;
66
    }
67
68
    /**
69
     * Startup the Agavi core
70
     *
71
     * @param      string $environment environment the environment to use for this session.
72
     *
73
     * @author     Felix Gilcher <[email protected]>
74
     * @since      1.0.0
75
     * @deprecated 1.1.0 Use PhpUnitCli
76
     */
77
    public static function bootstrap($environment = null)
78
    {
79
        PhpUnitCli::bootstrap($environment);
80
    }
81
82
    /**
83
     * Dispatch the test run.
84
     *
85
     * @param      array $arguments An array of arguments configuring PHPUnit behavior.
86
     * @param      bool  $exit Whether exit() should be called with an appropriate shell
87
     *                   exit status to indicate success or failures/errors.
88
     *
89
     * @return     \PHPUnit_Framework_TestResult The PHPUnit result object.
90
     *
91
     * @author     Felix Gilcher <[email protected]>
92
     * @author     David Zülke <[email protected]>
93
     * @since      1.0.0
94
     * @deprecated 1.1.0 Use PhpUnitCli
95
     */
96
    public static function dispatch($arguments = array(), $exit = true)
97
    {
98
        
99
        $suites = include ConfigCache::checkConfig(Config::get('core.testing_dir').'/config/suites.xml');
100
        $master_suite = new TestSuite('Master');
101
        
102
        if (!empty($arguments['include-suite'])) {
103
            $names = explode(',', $arguments['include-suite']);
104
            unset($arguments['include-suite']);
105
            
106 View Code Duplication
            foreach ($names as $name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
                if (empty($suites[$name])) {
108
                    throw new \InvalidArgumentException(sprintf('Invalid suite name %1$s.', $name));
109
                }
110
                
111
                $master_suite->addTest(self::createSuite($name, $suites[$name]));
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesting::createSuite() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
112
            }
113
        } else {
114
            $excludes = array();
115
            if (!empty($arguments['exclude-suite'])) {
116
                $excludes = explode(',', $arguments['exclude-suite']);
117
                unset($arguments['exclude-suite']);
118
            }
119
            foreach ($suites as $name => $suite) {
120
                if (!in_array($name, $excludes)) {
121
                    $master_suite->addTest(self::createSuite($name, $suite));
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesting::createSuite() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
122
                }
123
            }
124
        }
125
        
126
        if (version_compare(\PHPUnit_Runner_Version::id(), '3.6', '<')) {
127
            // PHP_CodeCoverage_Filter is a singleton
128
            $runner = new \PHPUnit_TextUI_TestRunner();
129
        } else {
130
            // PHP_CodeCoverage_Filter instance must be passed to the test runner
131
            $runner = new \PHPUnit_TextUI_TestRunner(null, self::$codeCoverageFilter);
132
        }
133
        $result = $runner->doRun($master_suite, $arguments);
134
        if ($exit) {
135
            // bai
136
            exit(self::getExitStatus($result));
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesting::getExitStatus() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Coding Style Compatibility introduced by
The method dispatch() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
137
        } else {
138
            // return result so calling code can use it
139
            return $result;
140
        }
141
    }
142
    
143
    /**
144
     * Compute a shell exit status for the given result.
145
     * Behaves like PHPUnit_TextUI_Command.
146
     *
147
     * @param      PHPUnit_Framework_TestResult The test result object.
148
     *
149
     * @return     int The shell exit code.
150
     * @deprecated 1.1.0 Use PhpUnitCli
151
     */
152
    public static function getExitStatus(\PHPUnit_Framework_TestResult $result)
153
    {
154
        if ($result->wasSuccessful()) {
155
            return \PHPUnit_TextUI_TestRunner::SUCCESS_EXIT;
156
        } elseif ($result->errorCount()) {
157
            return \PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT;
158
        } else {
159
            return \PHPUnit_TextUI_TestRunner::FAILURE_EXIT;
160
        }
161
    }
162
    
163
    /**
164
     * Initialize a suite from the given instructions and add registered tests.
165
     *
166
     * @param      string Name of the suite
167
     * @param      array  An array containing information about the suite
168
     *
169
     * @return     TestSuite The initialized test suite object.
170
     *
171
     * @author     Felix Gilcher <[email protected]>
172
     * @since      1.0.0
173
     * @deprecated 1.1.0 Use PhpUnitCli
174
     */
175
    protected static function createSuite($name, array $suite)
176
    {
177
        $base = (null == $suite['base']) ? 'tests' : $suite['base'];
178
        if (!Toolkit::isPathAbsolute($base)) {
179
            $base = Config::get('core.testing_dir').'/'.$base;
180
        }
181
        $s = new $suite['class']($name);
182
        if (!empty($suite['includes'])) {
183
            foreach (new \RecursiveIteratorIterator(
184
                    new RecursiveDirectoryFilterIterator(
185
                        new \RecursiveDirectoryIterator($base),
186
                        $suite['includes'],
187
                        $suite['excludes']
188
                    ),
189
                    \RecursiveIteratorIterator::CHILD_FIRST
190
                ) as $finfo) {
191
                if ($finfo->isFile()) {
192
                    $s->addTestFile($finfo->getPathName());
193
                }
194
            }
195
        }
196 View Code Duplication
        foreach ($suite['testfiles'] as $file) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
            if (!Toolkit::isPathAbsolute($file)) {
198
                $file = $base.'/'.$file;
199
            }
200
            $s->addTestFile($file);
201
        }
202
        return $s;
203
    }
204
    
205
    /**
206
     * Handles the commandline arguments passed.
207
     *
208
     * @return     array the commandline arguments
209
     *
210
     * @author     Felix Gilcher <[email protected]>
211
     * @since      1.0.0
212
     * @deprecated 1.1.0 Use PhpUnitCli
213
     */
214
    public static function processCommandlineOptions()
0 ignored issues
show
Coding Style introduced by
processCommandlineOptions uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
215
    {
216
        $longOptions = array(
217
            'configuration=',
218
            'coverage-html=',
219
            'coverage-clover=',
220
            'coverage-source=',
221
            'coverage-xml=',
222
            'report=',
223
            'environment=',
224
            'help',
225
            'log-graphviz=',
226
            'log-json=',
227
            'log-metrics=',
228
            'log-pmd=',
229
            'log-tap=',
230
            'log-xml=',
231
            'include-suite=',
232
            'exclude-suite=',
233
        );
234
        
235
        try {
236
            $options = \PHPUnit_Util_Getopt::getopt(
237
                $_SERVER['argv'],
238
                'd:',
239
                $longOptions
240
            );
241
        } catch (\RuntimeException $e) {
242
            \PHPUnit_TextUI_TestRunner::showError($e->getMessage());
243
        }
244
        
245
        $arguments = array();
246
        
247
        foreach ($options[0] as $option) {
248
            switch ($option[0]) {
249
                case '--configuration':
250
                    $arguments['configuration'] = $option[1];
251
                    break;
252
                
253
                case '--coverage-clover':
254
                case '--coverage-xml':
255
                    if (self::checkCodeCoverageDeps()) {
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesti...checkCodeCoverageDeps() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
256
                        $arguments['coverageClover'] = $option[1];
257
                    }
258
                    break;
259
                
260
                case '--coverage-source':
261
                    if (self::checkCodeCoverageDeps()) {
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesti...checkCodeCoverageDeps() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
262
                        $arguments['coverageSource'] = $option[1];
263
                    }
264
                    break;
265
                
266
                case '--coverage-html':
267
                case '--report':
268
                    if (self::checkCodeCoverageDeps()) {
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesti...checkCodeCoverageDeps() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
269
                        $arguments['reportDirectory'] = $option[1];
270
                    }
271
                    break;
272
                
273
                case '--environment':
274
                    $arguments['environment'] = $option[1];
275
                    break;
276
                
277
                case '--help':
278
                    self::showHelp();
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesting::showHelp() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
279
                    exit(\PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Coding Style Compatibility introduced by
The method processCommandlineOptions() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
280
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
281
                
282
                case '--log-json':
283
                    $arguments['jsonLogfile'] = $option[1];
284
                    break;
285
                
286
                case '--log-graphviz':
287
                    if (\PHPUnit_Util_Filesystem::fileExistsInIncludePath('Image/GraphViz.php')) {
288
                        $arguments['graphvizLogfile'] = $option[1];
289
                    } else {
290
                        throw new \Exception('The Image_GraphViz package is not installed.');
291
                    }
292
                    break;
293
                
294
                case '--log-tap':
295
                    $arguments['tapLogfile'] = $option[1];
296
                    break;
297
                
298
                case '--log-xml':
299
                    $arguments['xmlLogfile'] = $option[1];
300
                    break;
301
                
302
                case '--log-pmd':
303
                    if (self::checkCodeCoverageDeps()) {
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesti...checkCodeCoverageDeps() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
304
                        $arguments['pmdXML'] = $option[1];
305
                    }
306
                    break;
307
                
308
                case '--log-metrics':
309
                    if (self::checkCodeCoverageDeps()) {
0 ignored issues
show
Deprecated Code introduced by
The method Agavi\Testing\AgaviTesti...checkCodeCoverageDeps() has been deprecated with message: 1.1.0 Use PhpUnitCli

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
310
                        $arguments['metricsXML'] = $option[1];
311
                    }
312
                    break;
313
                
314
                case '--include-suite':
315
                    $arguments['include-suite'] = $option[1];
316
                    break;
317
                
318
                case '--exclude-suite':
319
                    $arguments['exclude-suite'] = $option[1];
320
                    break;
321
            }
322
        }
323
        
324
        return $arguments;
325
    }
326
    
327
    /**
328
     * Checks whether all dependencies for writing code coverage information
329
     * are met.
330
     *
331
     * @return     true if all deps are met
332
     * @throws     AgaviException if a dependency is missing
333
     *
334
     * @author     Felix Gilcher <[email protected]>
335
     * @since      1.0.0
336
     * @deprecated 1.1.0 Use PhpUnitCli
337
     */
338
    protected static function checkCodeCoverageDeps()
339
    {
340
        if (extension_loaded('tokenizer') && extension_loaded('xdebug')) {
341
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return true; (boolean) is incompatible with the return type documented by Agavi\Testing\AgaviTesting::checkCodeCoverageDeps of type Agavi\Testing\true.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
342
        } else {
343
            if (!extension_loaded('tokenizer')) {
344
                throw new AgaviException('The tokenizer extension is not loaded.');
345
            } else {
346
                throw new AgaviException('The Xdebug extension is not loaded.');
347
            }
348
        }
349
        
350
        return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
351
    }
352
    
353
    /**
354
     * shows the help for the commandline call
355
     *
356
     * @author     Felix Gilcher <[email protected]>
357
     * @since      1.0.0
358
     * @deprecated 1.1.0 Use PhpUnitCli
359
     */
360
    protected static function showHelp()
361
    {
362
        \PHPUnit_TextUI_TestRunner::printVersionString();
363
364
        print <<<EOT
365
Usage: run-tests.php [switches]
366
367
  --environment <envname>  use environment named <envname> to run the tests. Defaults to "testing".
368
369
  --log-graphviz <file>    Log test execution in GraphViz markup.
370
  --log-json <file>        Log test execution in JSON format.
371
  --log-tap <file>         Log test execution in TAP format to file.
372
  --log-xml <file>         Log test execution in XML format to file.
373
  --log-metrics <file>     Write metrics report in XML format.
374
  --log-pmd <file>         Write violations report in PMD XML format.
375
376
  --configuration <file>   PHPUnit XML configuration file to use.
377
378
  --coverage-html <dir>    Generate code coverage report in HTML format.
379
  --coverage-clover <file> Write code coverage data in Clover XML format.
380
  --coverage-source <dir>  Write code coverage / source data in XML format.
381
382
  --include-suite <suites> run only suites named <suite>, accepts a list of suites, comma separated.
383
  --exclude-suite <suites> run all but suites named <suite>, accepts a list of suites, comma separated.
384
385
  --help                   Prints this usage information.
386
387
388
EOT;
389
    }
390
}
391