Test Failed
Push — master ( b1e763...e9b74b )
by Hannes
02:19
created

TestCommand   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 119
rs 10
c 0
b 0
f 0
wmc 14

3 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 51 1
B bootstrap() 0 15 6
C execute() 0 40 7
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace hanneskod\readmetester\Console;
6
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputOption;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use hanneskod\readmetester\EngineFactory;
13
use hanneskod\readmetester\SourceFileIterator;
14
use hanneskod\readmetester\Example\RegexpFilter;
15
use hanneskod\readmetester\Example\UnnamedFilter;
16
use hanneskod\readmetester\Example\NullFilter;
17
use hanneskod\readmetester\Runner\IsolationRunner;
18
19
/**
20
 * CLI command to run test
21
 */
22
class TestCommand extends Command
23
{
24
    /**
25
     * Path to default bootstrap file
26
     */
27
    const DEFAULT_BOOTSTRAP = 'vendor/autoload.php';
28
29
    protected function configure()
30
    {
31
        $this->setName('test')
32
            ->setDescription('Test examples in readme file')
33
            ->addArgument(
34
                'source',
35
                InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
36
                'One or more files or directories to test',
37
                ['README.md']
38
            )
39
            ->addOption(
40
                'filter',
41
                null,
42
                InputOption::VALUE_REQUIRED,
43
                'Filter which examples to test using a regular expression'
44
            )
45
            ->addOption(
46
                'format',
47
                null,
48
                InputOption::VALUE_REQUIRED,
49
                "One of 'std' or 'json'"
50
            )
51
            ->addOption(
52
                'named-only',
53
                null,
54
                InputOption::VALUE_NONE,
55
                'Test only named examples'
56
            )
57
            ->addOption(
58
                'ignore-unknown-annotations',
59
                null,
60
                InputOption::VALUE_NONE,
61
                'Ignore example annotations that are not known to readme-tester'
62
            )
63
            ->addOption(
64
                'isolation',
65
                null,
66
                InputOption::VALUE_NONE,
67
                'Run tests in isolation'
68
            )
69
            ->addOption(
70
                'bootstrap',
71
                null,
72
                InputOption::VALUE_REQUIRED,
73
                'A "bootstrap" PHP file that is run before testing'
74
            )
75
            ->addOption(
76
                'no-auto-bootstrap',
77
                null,
78
                InputOption::VALUE_NONE,
79
                "Don't try to load a local composer autoloader when boostrap is not definied"
80
            )
81
        ;
82
    }
83
84
    protected function execute(InputInterface $input, OutputInterface $output): int
85
    {
86
        $filter = $input->getOption('filter')
87
            ? new RegexpFilter($input->getOption('filter'))
88
            : ($input->getOption('named-only') ? new UnnamedFilter : new NullFilter);
89
90
        $engineFactory = new EngineFactory;
91
92
        if ($input->getOption('isolation')) {
93
            $engineFactory->setRunner(new IsolationRunner);
94
        }
95
96
        $engine = $engineFactory->createEngine(
97
            $filter,
98
            $input->getOption('ignore-unknown-annotations')
99
        );
100
101
        $exitStatus = new ExitStatusListener;
102
        $engine->registerListener($exitStatus);
103
104
        $formatter = $input->getOption('format') == 'json'
105
            ? new JsonFormatter($output)
106
            : new DefaultFormatter($output);
107
108
        $engine->registerListener($formatter);
109
110
        $formatter->onInvokationStart();
111
112
        $this->bootstrap($input, $formatter);
113
114
        foreach ($input->getArgument('source') as $source) {
115
            foreach (new SourceFileIterator($source) as $filename => $contents) {
116
                $formatter->onFile($filename);
117
                $engine->testFile($contents);
118
            }
119
        }
120
121
        $formatter->onInvokationEnd();
122
123
        return $exitStatus->getStatusCode();
124
    }
125
126
    private function bootstrap(InputInterface $input, $formatter)
127
    {
128
        if ($filename = $input->getOption('bootstrap')) {
129
            if (!file_exists($filename) || !is_readable($filename)) {
130
                throw new \RuntimeException("Unable to bootstrap $filename");
131
            }
132
133
            require_once $filename;
134
            $formatter->onBootstrap($filename);
135
            return;
136
        }
137
138
        if (!$input->getOption('no-auto-bootstrap') && is_readable(self::DEFAULT_BOOTSTRAP)) {
139
            require_once self::DEFAULT_BOOTSTRAP;
140
            $formatter->onBootstrap(self::DEFAULT_BOOTSTRAP);
141
        }
142
    }
143
}
144