Completed
Push — stable ( cbd099...ce6aca )
by Nuno
14:51 queued 11s
created

Listener::render()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8.6881

Importance

Changes 0
Metric Value
dl 0
loc 35
ccs 11
cts 19
cp 0.5789
rs 8.7377
c 0
b 0
f 0
cc 6
nc 8
nop 2
crap 8.6881
1
<?php
2
3
/**
4
 * This file is part of Collision.
5
 *
6
 * (c) Nuno Maduro <[email protected]>
7
 *
8
 *  For the full copyright and license information, please view the LICENSE
9
 *  file that was distributed with this source code.
10
 */
11
12
namespace NunoMaduro\Collision\Adapters\Phpunit;
13
14
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\Listener as ListenerContract;
15
use NunoMaduro\Collision\Contracts\Writer as WriterContract;
16
use NunoMaduro\Collision\Writer;
17
use PHPUnit\Framework\AssertionFailedError;
18
use PHPUnit\Framework\ExceptionWrapper;
19
use PHPUnit\Framework\ExpectationFailedException;
20
use PHPUnit\Framework\Test;
21
use PHPUnit\Framework\TestSuite;
22
use PHPUnit\Framework\Warning;
23
use ReflectionObject;
24
use Symfony\Component\Console\Application;
25
use Symfony\Component\Console\Input\ArgvInput;
26
use Symfony\Component\Console\Output\ConsoleOutput;
27
use Whoops\Exception\Inspector;
28
29
if (class_exists(\PHPUnit\Runner\Version::class) && intval(substr(\PHPUnit\Runner\Version::id(), 0, 1)) >= 7) {
30
31
    /**
32
     * This is an Collision Phpunit Adapter implementation.
33
     *
34
     * @author Nuno Maduro <[email protected]>
35
     */
36
    class Listener implements ListenerContract
37
    {
38
        /**
39
         * Holds an instance of the writer.
40
         *
41
         * @var \NunoMaduro\Collision\Contracts\Writer
42
         */
43
        protected $writer;
44
45
        /**
46
         * Creates a new instance of the class.
47
         *
48
         * @param  \NunoMaduro\Collision\Contracts\Writer|null  $writer
49
         */
50 2
        public function __construct(WriterContract $writer = null)
51
        {
52 2
            $this->writer = $writer ?: $this->buildWriter();
53 2
        }
54
55
        /**
56
         * {@inheritdoc}
57
         */
58 1
        public function render(Test $test, \Throwable $throwable)
59
        {
60 1
            $this->writer->ignoreFilesIn([
61 1
                '/vendor\/phpunit\/phpunit\/src/',
62
                '/vendor\/mockery\/mockery/',
63
                '/vendor\/laravel\/framework\/src\/Illuminate\/Testing/',
64
                '/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Testing/',
65
            ]);
66
67 1
            $output = $this->writer->getOutput();
68
69 1
            if (method_exists($test, 'getName')) {
70
                $output->writeln("\n");
71
                $name = "\e[2m" . get_class($test) . "\e[22m";
72
                $output->writeln(sprintf(
73
                    '  <bg=red;options=bold> FAIL </> <fg=red;options=bold></><fg=default>%s <fg=red;options=bold>➜</> %s</>',
74
                    $name,
75
                    $test->getName(false)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PHPUnit\Framework\Test as the method getName() does only exist in the following implementations of said interface: AbstractIssue3881Test, AbstractTest, AssertionExampleTest, BankAccountTest, BankAccountWithCustomExtensionTest, BeforeAndAfterTest, BeforeClassAndAfterClassTest, BeforeClassWithOnlyDataProviderTest, ChangeCurrentWorkingDirectoryTest, ClonedDependencyTest, ConcreteTest, ConcreteWithMyCustomExtensionTest, ConsecutiveParametersTest, CoverageClassExtendedTest, CoverageClassNothingTest, CoverageClassTest, CoverageClassWithoutAnnotationsTest, CoverageCoversOverridesCoversNothingTest, CoverageFunctionParenthesesTest, CoverageFunctionParenthesesWhitespaceTest, CoverageFunctionTest, CoverageMethodNothingCoversMethod, CoverageMethodNothingTest, CoverageMethodOneLineAnnotationTest, CoverageMethodParenthesesTest, CoverageMethodParenthesesWhitespaceTest, CoverageMethodTest, CoverageNamespacedFunctionTest, CoverageNoneTest, CoverageNotPrivateTest, CoverageNotProtectedTest, CoverageNotPublicTest, CoveragePrivateTest, CoverageProtectedTest, CoveragePublicTest, DataProviderDebugTest, DataProviderDependencyTest, DataProviderFilterTest, DataProviderIncompleteTest, DataProviderSkippedTest, DataProviderTest, DataproviderExecutionOrderTest, DependencyFailureTest, DependencySuccessTest, DoNoAssertionTestCase, DoesNotPerformAssertions...erformingAssertionsTest, DummyBarTest, DummyFooTest, DuplicateKeyDataProviderTest, EmptyDataProviderTest, EmptyTestCaseTest, ExceptionInAssertPostConditionsTest, ExceptionInAssertPreConditionsTest, ExceptionInSetUpTest, ExceptionInTearDownAfterClassTest, ExceptionInTearDownTest, ExceptionInTest, ExceptionInTestDetectedInTeardown, ExceptionStackTest, ExceptionTest, Failure, FailureTest, FatalTest, Foo\DataProviderIssue2833\FirstTest, Foo\DataProviderIssue2833\SecondTest, Foo\DataProviderIssue2859\TestWithDataProviderTest, Foo\DataProviderIssue2922\FirstTest, Foo\DataProviderIssue2922\SecondHelloWorldTest, Foo_Bar_Issue684Test, Framework\Constraint\LogicalXorTest, GeneratorTest, IgnoreCodeCoverageClassTest, Illuminate\Foundation\Testing\TestCase, IncompleteTest, InheritanceA, InheritanceB, InheritedTestCase, IniTest, InvocationMockerTest, IsolationTest, Issue1021Test, Issue1149Test, Issue1216Test, Issue1265Test, Issue1330Test, Issue1335Test, Issue1337Test, Issue1348Test, Issue1351Test, Issue1374Test, Issue1437Test, Issue1468Test, Issue1471Test, Issue1472Test, Issue1570Test, Issue2085Test, Issue2137Test, Issue2145Test, Issue2158Test, Issue2366Test, Issue2380Test, Issue2382Test, Issue2435Test, Issue2725\BeforeAfterClassPidTest, Issue2731Test, Issue2811Test, Issue2830Test, Issue2972\Issue2972Test, Issue3093Test, Issue322Test, Issue3739\Issue3739Test, Issue3881Test, Issue3889Test, Issue3904Test, Issue3983Test, Issue433Test, Issue445Test, Issue498Test, Issue503Test, Issue578Test, Issue581Test, Issue74Test, Issue765Test, Issue797Test, MockBuilderTest, MockObjectTest, ModifiedConstructorTestCase, MultiDependencyTest, MultipleDataProviderTest, My\Space\ExceptionNamespaceTest, NamespaceCoverageClassExtendedTest, NamespaceCoverageClassTest, NamespaceCoverageCoversClassPublicTest, NamespaceCoverageCoversClassTest, NamespaceCoverageMethodTest, NamespaceCoverageNotPrivateTest, NamespaceCoverageNotProtectedTest, NamespaceCoverageNotPublicTest, NamespaceCoveragePrivateTest, NamespaceCoverageProtectedTest, NamespaceCoveragePublicTest, NoArgTestCaseTest, NoTestCases, NotExistingCoveredElementTest, NotPublicTestCase, NotVoidTestCase, NothingTest, NullTestResultCacheTest, NumericGroupAnnotationTest, OneTest, OneTestCase, Orchestra\Testbench\TestCase, OutputTestCase, OverrideTestCase, PHPUnit\Framework\AssertTest, PHPUnit\Framework\ConstraintTest, PHPUnit\Framework\Constraint\ArrayHasKeyTest, PHPUnit\Framework\Constraint\ArraySubsetTest, PHPUnit\Framework\Constraint\CallbackTest, PHPUnit\Framework\Constraint\ClassHasAttributeTest, PHPUnit\Framework\Constr...sHasStaticAttributeTest, PHPUnit\Framework\Constraint\ConstraintTestCase, PHPUnit\Framework\Constraint\CountTest, PHPUnit\Framework\Constraint\DirectoryExistsTest, PHPUnit\Framework\Constraint\ExceptionCodeTest, PHPUnit\Framework\Constr...eptionMessageRegExpTest, PHPUnit\Framework\Constraint\ExceptionMessageTest, PHPUnit\Framework\Constraint\ExceptionTest, PHPUnit\Framework\Constraint\FileExistsTest, PHPUnit\Framework\Constraint\GreaterThanTest, PHPUnit\Framework\Constraint\IsEmptyTest, PHPUnit\Framework\Constraint\IsEqualTest, PHPUnit\Framework\Constraint\IsIdenticalTest, PHPUnit\Framework\Constraint\IsInstanceOfTest, PHPUnit\Framework\Constraint\IsJsonTest, PHPUnit\Framework\Constraint\IsNullTest, PHPUnit\Framework\Constraint\IsReadableTest, PHPUnit\Framework\Constraint\IsTypeTest, PHPUnit\Framework\Constraint\IsWritableTest, PHPUnit\Framework\Constr...rrorMessageProviderTest, PHPUnit\Framework\Constraint\JsonMatchesTest, PHPUnit\Framework\Constraint\LessThanTest, PHPUnit\Framework\Constraint\LogicalAndTest, PHPUnit\Framework\Constraint\LogicalNotTest, PHPUnit\Framework\Constraint\LogicalOrTest, PHPUnit\Framework\Constr...\ObjectHasAttributeTest, PHPUnit\Framework\Constraint\RegularExpressionTest, PHPUnit\Framework\Constraint\SameSizeTest, PHPUnit\Framework\Constraint\StringContainsTest, PHPUnit\Framework\Constraint\StringEndsWithTest, PHPUnit\Framework\Constr...esFormatDescriptionTest, PHPUnit\Framework\Constraint\StringStartsWithTest, PHPUnit\Framework\Constr...TraversableContainsTest, PHPUnit\Framework\DataProviderTestSuite, PHPUnit\Framework\ExceptionTest, PHPUnit\Framework\ExceptionWrapperTest, PHPUnit\Framework\FunctionsTest, PHPUnit\Framework\IncompleteTestCase, PHPUnit\Framework\IncompleteTestCaseTest, PHPUnit\Framework\InvalidArgumentExceptionTest, PHPUnit\Framework\MockOb...\ConfigurableMethodTest, PHPUnit\Framework\MockOb...ConfigurableMethodsTest, PHPUnit\Framework\MockObject\InvocationHandlerTest, PHPUnit\Framework\MockObject\MatcherTest, PHPUnit\Framework\MockObject\MockClassTest, PHPUnit\Framework\MockObject\MockMethodTest, PHPUnit\Framework\MockObject\MockTraitTest, PHPUnit\Framework\SkippedTestCase, PHPUnit\Framework\SkippedTestCaseTest, PHPUnit\Framework\TestBuilderTest, PHPUnit\Framework\TestCase, PHPUnit\Framework\TestCaseTest, PHPUnit\Framework\TestFailureTest, PHPUnit\Framework\TestImplementorTest, PHPUnit\Framework\TestListenerTest, PHPUnit\Framework\TestSuite, PHPUnit\Framework\TestSuiteIteratorTest, PHPUnit\Framework\TestSuiteTest, PHPUnit\Framework\WarningTestCase, PHPUnit\Runner\DefaultTestResultCacheTest, PHPUnit\Runner\Filter\NameFilterIteratorTest, PHPUnit\Runner\PhptTestCase, PHPUnit\Runner\PhptTestCaseTest, PHPUnit\Runner\ResultCacheExtensionTest, PHPUnit\Runner\TestSuiteSorterTest, PHPUnit\SelfTest\Basic\SetUpBeforeClassTest, PHPUnit\SelfTest\Basic\SetUpTest, PHPUnit\SelfTest\Basic\StatusTest, PHPUnit\SelfTest\Basic\TearDownAfterClassTest, PHPUnit\StaticAnalysis\TestUsingMocks, PHPUnit\TestFixture\ActualOutputTest, PHPUnit\Test\HookTest, PHPUnit\Util\Annotation\RegistryTest, PHPUnit\Util\ColorTest, PHPUnit\Util\ConfigurationGeneratorTest, PHPUnit\Util\ConfigurationTest, PHPUnit\Util\GetoptTest, PHPUnit\Util\GlobalStateTest, PHPUnit\Util\JsonTest, PHPUnit\Util\PHP\AbstractPhpProcessTest, PHPUnit\Util\RegularExpressionTest, PHPUnit\Util\TestClassTest, PHPUnit\Util\TestDox\CliTestDoxPrinterColorTest, PHPUnit\Util\TestDox\CliTestDoxPrinterTest, PHPUnit\Util\TestDox\NamePrettifierTest, PHPUnit\Util\TestDox\ano...amePrettifierTest.php$0, PHPUnit\Util\TestDox\ano...amePrettifierTest.php$1, PHPUnit\Util\TestDox\ano...amePrettifierTest.php$2, PHPUnit\Util\TestDox\ano...amePrettifierTest.php$3, PHPUnit\Util\XDebugFilterScriptGeneratorTest, PHPUnit\Util\XmlTest, ProxyObjectTest, RequirementsClassBeforeClassHookTest, RequirementsTest, RouterTest, SeparateClassRunMethodInNewProcessTest, SeparateProcessesTest, StackTest, StopOnErrorTestSuite, StopsOnWarningTest, Success, TemplateMethodsTest, Test, Test3194, TestAutoreferenced, TestCaseWithExceptionInHook, TestDoxGroupTest, TestError, TestIncomplete, TestResultCacheTest, TestRisky, TestSkipped, TestWarning, TestWithAnnotations, TestWithDifferentDurations, TestWithDifferentNames, TestWithDifferentOutput, TestWithDifferentSizes, TestWithDifferentStatuses, TestWithTest, Test\Issue3156Test, Test\Issue3379Test, ThrowExceptionTestCase, ThrowNoExceptionTestCase, TwoTest, WasRun.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
76
                ));
77
            }
78
79 1
            if ($throwable instanceof ExceptionWrapper && $throwable->getOriginalException() !== null) {
80
                $throwable = $throwable->getOriginalException();
81
            }
82
83 1
            $inspector = new Inspector($throwable);
84
85 1
            $this->writer->write($inspector);
86
87 1
            if ($throwable instanceof ExpectationFailedException && $comparisionFailure = $throwable->getComparisonFailure()) {
88
                $output->write($comparisionFailure->getDiff());
89
            }
90
91 1
            $this->terminate();
92 1
        }
93
94
        /**
95
         * {@inheritdoc}
96
         */
97
        public function addError(Test $test, \Throwable $throwable, float $time): void
98
        {
99
            $this->render($test, $throwable);
100
        }
101
102
        /**
103
         * {@inheritdoc}
104
         */
105 1
        public function addWarning(Test $test, Warning $t, float $time): void
106
        {
107 1
        }
108
109
        /**
110
         * {@inheritdoc}
111
         */
112 1
        public function addFailure(Test $test, AssertionFailedError $throwable, float $time): void
113
        {
114 1
            $reflector = new ReflectionObject($throwable);
115
116 1
            if ($reflector->hasProperty('message')) {
117 1
                $message = trim((string) preg_replace("/\r|\n/", ' ', $throwable->getMessage()));
118 1
                $property = $reflector->getProperty('message');
119 1
                $property->setAccessible(true);
120 1
                $property->setValue($throwable, $message);
121
            }
122
123 1
            $this->render($test, $throwable);
124 1
        }
125
126
        /**
127
         * {@inheritdoc}
128
         */
129 1
        public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
130
        {
131 1
        }
132
133
        /**
134
         * {@inheritdoc}
135
         */
136 1
        public function addRiskyTest(Test $test, \Throwable $t, float $time): void
137
        {
138 1
        }
139
140
        /**
141
         * {@inheritdoc}
142
         */
143 1
        public function addSkippedTest(Test $test, \Throwable $t, float $time): void
144
        {
145 1
        }
146
147
        /**
148
         * {@inheritdoc}
149
         */
150 1
        public function startTestSuite(TestSuite $suite): void
151
        {
152 1
        }
153
154
        /**
155
         * {@inheritdoc}
156
         */
157 1
        public function endTestSuite(TestSuite $suite): void
158
        {
159 1
        }
160
161
        /**
162
         * {@inheritdoc}
163
         */
164 1
        public function startTest(Test $test): void
165
        {
166 1
        }
167
168
        /**
169
         * {@inheritdoc}
170
         */
171 1
        public function endTest(Test $test, float $time): void
172
        {
173 1
        }
174
175
        /**
176
         * Builds an Writer.
177
         *
178
         * @return \NunoMaduro\Collision\Contracts\Writer
179
         */
180 1
        protected function buildWriter(): WriterContract
181
        {
182 1
            $writer = new Writer();
183
184 1
            $application = new Application();
185 1
            $reflector = new ReflectionObject($application);
186 1
            $method = $reflector->getMethod('configureIO');
187 1
            $method->setAccessible(true);
188 1
            $method->invoke($application, new ArgvInput, $output = new ConsoleOutput);
189
190 1
            return $writer->setOutput($output);
191
        }
192
193
        /**
194
         * Terminates the test.
195
         */
196
        public function terminate(): void
197
        {
198
            exit(1);
199
        }
200
    }
201
}
202