Completed
Pull Request — master (#46)
by Jitendra
01:52
created

TestCommand::shouldGenerateTest()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 4
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the PHINT package.
5
 *
6
 * (c) Jitendra Adhikari <[email protected]>
7
 *     <https://github.com/adhocore>
8
 *
9
 * Licensed under MIT license.
10
 */
11
12
namespace Ahc\Phint\Console;
13
14
use Ahc\Cli\IO\Interactor;
15
use Ahc\Phint\Generator\TwigGenerator;
16
use Ahc\Phint\Util\Composer;
17
18
class TestCommand extends BaseCommand
19
{
20
    /** @var string Command name */
21
    protected $_name = 'test';
22
23
    /** @var string Command description */
24
    protected $_desc = 'Generate test stubs';
25
26
    /**
27
     * Configure the command options/arguments.
28
     *
29
     * @return void
30
     */
31
    protected function onConstruct()
32
    {
33
        $this
34
            ->option('-t --no-teardown', 'Dont add teardown method')
35
            ->option('-s --no-setup', 'Dont add setup method')
36
            ->option('-n --naming', "Test method naming format\n(t: testMethod | m: test_method | i: it_tests_)")
37
            ->option('-a --with-abstract', 'Create stub for abstract/interface class')
38
            ->option('-p --phpunit [classFqcn]', 'Base PHPUnit class to extend from')
39
            ->option('-x --template', "User supplied template path\nIt has higher precedence than inbuilt templates")
40
            ->usage(
41
                '<bold>  phint test</end> <comment>-n i</end>        With `it_` naming<eol/>' .
42
                '<bold>  phint t</end> <comment>--no-teardown</end>  Without `tearDown()`<eol/>' .
43
                '<bold>  phint test</end> <comment>-a</end>          With stubs for abstract/interface<eol/>'
44
            );
45
    }
46
47
    public function interact(Interactor $io)
48
    {
49
        $promptConfig = [
50
            'naming' => [
51
                'choices' => ['t' => 'testMethod', 'i' => 'it_tests_', 'm' => 'test_method'],
52
                'default' => 't',
53
            ],
54
            'phpunit' => [
55
                'default' => 'PHPUnit\\Framework\\TestCase',
56
            ],
57
            'template' => false,
58
        ];
59
60
        $this->logging('start');
61
        $this->promptAll($io, $promptConfig);
62
    }
63
64
    /**
65
     * Generate test stubs.
66
     *
67
     * @return void
68
     */
69
    public function execute()
70
    {
71
        $io = $this->app()->io();
72
73
        $io->comment('Preparing metadata ...', true);
74
        $metadata = $this->prepare();
75
76
        if (empty($metadata)) {
77
            $io->bgGreen('Looks like nothing to do here', true);
78
79
            return;
80
        }
81
82
        $io->comment('Generating tests ...', true);
83
        $generated = $this->generate($metadata, $this->values());
84
85
        if ($generated) {
86
            $io->cyan("$generated test(s) generated", true);
87
        }
88
89
        $io->ok('Done', true);
90
        $this->logging('end');
91
    }
92
93
    protected function prepare(): array
94
    {
95
        // Sorry psr-0!
96
        $namespaces  = $this->_composer->config('autoload.psr-4');
97
        $namespaces += $this->_composer->config('autoload-dev.psr-4');
98
99
        $srcNs = $testNs = [];
100
101
        foreach ($namespaces as $ns => $path) {
102
            $ns = \rtrim($ns, '\\') . '\\';
103
            if (\preg_match('!^(source|src|lib|class)/?!', $path)) {
104
                $path    = \rtrim($path, '/\\') . '/';
105
                $srcNs[] = ['ns' => $ns, 'nsPath' => "{$this->_workDir}/$path"];
106
            } elseif (\strpos($path, 'test') === 0) {
107
                $path   = \rtrim($path, '/\\') . '/';
108
                $testNs = ['ns' => $ns, 'nsPath' => "{$this->_workDir}/$path"];
109
            }
110
        }
111
112
        if (empty($srcNs) || empty($testNs)) {
113
            throw new \RuntimeException(
114
                'The composer.json#(autoload.psr-4, autoload-dev.psr-4) contains no `src` or `test` paths'
115
            );
116
        }
117
118
        return $this->getTestMetadata($this->getSourceClasses(), $srcNs, $testNs);
119
    }
120
121
    protected function getTestMetadata(array $classes, array $srcNs, array $testNs): array
122
    {
123
        $metadata = [];
124
125
        foreach ($classes as $classFqcn) {
126
            if ([] === $meta = $this->getClassMetaData($classFqcn)) {
127
                continue;
128
            }
129
130
            $metadata[] = $meta + $this->convertToTest($meta, $srcNs, $testNs);
131
        }
132
133
        return $metadata;
134
    }
135
136
    private function convertToTest(array $metadata, array $srcNs, array $testNs): array
137
    {
138
        $testClass = $metadata['className'] . 'Test';
139
        $testPath  = \str_replace(\array_column($srcNs, 'nsPath'), $testNs['nsPath'], $metadata['classPath']);
140
        $testPath  = \preg_replace('!\.php$!i', 'Test.php', $testPath);
141
        $testFqcn  = \str_replace(\array_column($srcNs, 'ns'), $testNs['ns'], $metadata['classFqcn']) . 'Test';
142
143
        $testNamespace = \str_replace(\array_column($srcNs, 'ns'), $testNs['ns'], $metadata['namespace']);
144
145
        return compact('testClass', 'testNamespace', 'testFqcn', 'testPath');
146
    }
147
148
    protected function generate(array $testMetadata, array $parameters): int
149
    {
150
        $generator = new TwigGenerator($this->getTemplatePaths($parameters), $this->getCachePath());
151
152
        return $generator->generateTests($testMetadata, $parameters);
153
    }
154
}
155