Completed
Push — stable ( 1a9a26...6db3dc )
by Nuno
02:54 queued 01:02
created

Listener::endTestSuite()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 1
nc 1
nop 1
crap 1
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 Exception;
15
use ReflectionObject;
16
use PHPUnit\Framework\Test;
17
use PHPUnit\Framework\Warning;
18
use Whoops\Exception\Inspector;
19
use NunoMaduro\Collision\Writer;
20
use PHPUnit\Framework\TestSuite;
21
use Symfony\Component\Console\Application;
22
use PHPUnit\Framework\AssertionFailedError;
23
use Symfony\Component\Console\Input\ArgvInput;
24
use Symfony\Component\Console\Output\ConsoleOutput;
25
use NunoMaduro\Collision\Contracts\Writer as WriterContract;
26
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\Listener as ListenerContract;
27
28
/**
29
 * This is an Collision Phpunit Adapter implementation.
30
 *
31
 * @author Nuno Maduro <[email protected]>
32
 */
33
class Listener implements ListenerContract
34
{
35
    /**
36
     * Holds an instance of the writer.
37
     *
38
     * @var \NunoMaduro\Collision\Contracts\Writer
39
     */
40
    protected $writer;
41
42
    /**
43
     * Whether or not an exception was found.
44
     *
45
     * @var bool
46
     */
47
    protected static $exceptionFound = false;
48
49
    /**
50
     * Creates a new instance of the class.
51
     *
52
     * @param \NunoMaduro\Collision\Contracts\Writer|null $writer
53
     */
54 4
    public function __construct(WriterContract $writer = null)
55
    {
56 4
        $this->writer = $writer ?: $this->buildWriter();
57 4
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62 1
    public function render(\Throwable $e)
63
    {
64 1
        if (! static::$exceptionFound) {
65 1
            $inspector = new Inspector($e);
66
67 1
            $this->writer->write($inspector);
68
69 1
            static::$exceptionFound = true;
70
        }
71 1
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 1
    public function addError(Test $test, Exception $e, $time)
77
    {
78 1
        $this->render($e);
79 1
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 1
    public function addWarning(Test $test, Warning $e, $time)
85
    {
86 1
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91 1
    public function addFailure(Test $test, AssertionFailedError $e, $time)
92
    {
93 1
        $this->writer->ignoreFilesIn(['/vendor/'])
94 1
            ->showTrace(false);
95
96 1
        $this->render($e);
97 1
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 1
    public function addIncompleteTest(Test $test, Exception $e, $time)
103
    {
104 1
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109 1
    public function addRiskyTest(Test $test, Exception $e, $time)
110
    {
111 1
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 1
    public function addSkippedTest(Test $test, Exception $e, $time)
117
    {
118 1
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123 1
    public function startTestSuite(TestSuite $suite)
124
    {
125 1
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130 1
    public function endTestSuite(TestSuite $suite)
131
    {
132 1
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 1
    public function startTest(Test $test)
138
    {
139 1
        $testResultObject = $test->getTestResultObject();
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 getTestResultObject() does only exist in the following implementations of said interface: AbstractTest, AssertionExampleTest, BankAccountTest, BankAccountWithCustomExtensionTest, BeforeAndAfterTest, BeforeClassAndAfterClassTest, BeforeClassWithOnlyDataProviderTest, ChangeCurrentWorkingDirectoryTest, ClonedDependencyTest, ConcreteTest, ConcreteWithMyCustomExtensionTest, CoverageClassExtendedTest, CoverageClassTest, CoverageFunctionParenthesesTest, CoverageFunctionParenthesesWhitespaceTest, CoverageFunctionTest, CoverageMethodOneLineAnnotationTest, CoverageMethodParenthesesTest, CoverageMethodParenthesesWhitespaceTest, CoverageMethodTest, CoverageNamespacedFunctionTest, CoverageNoneTest, CoverageNotPrivateTest, CoverageNotProtectedTest, CoverageNotPublicTest, CoverageNothingTest, CoveragePrivateTest, CoverageProtectedTest, CoveragePublicTest, DataProviderDebugTest, DataProviderDependencyTest, DataProviderFilterTest, DataProviderIncompleteTest, DataProviderSkippedTest, DataProviderTest, DataProviderTestDoxTest, DependencyFailureTest, DependencySuccessTest, DummyBarTest, DummyFooTest, EmptyTestCaseTest, ExceptionInAssertPostConditionsTest, ExceptionInAssertPreConditionsTest, ExceptionInSetUpTest, ExceptionInTearDownTest, ExceptionInTest, 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\LogicalAndTest, Framework\Constraint\LogicalOrTest, Framework\Constraint\LogicalXorTest, IgnoreCodeCoverageClassTest, IncompleteTest, InheritanceA, InheritanceB, InheritedTestCase, IniTest, IsolationTest, Issue1021Test, Issue1149Test, Issue1216Test, Issue1265Test, Issue1330Test, Issue1335Test, Issue1337Test, Issue1348Test, Issue1351Test, Issue1374Test, Issue1437Test, Issue1468Test, Issue1471Test, Issue1472Test, Issue1570Test, Issue2137Test, Issue2145Test, Issue2158Test, Issue2366Test, Issue2380Test, Issue2382Test, Issue2435Test, Issue244Test, Issue2591_SeparateClassPreserveTest, Issue2591_SeparateFunctionNoPreserveTest, Issue2591_SeparateFunctionPreserveTest, Issue2725\BeforeAfterClassPidTest, Issue2731Test, Issue2811Test, Issue322Test, Issue433Test, Issue445Test, Issue498Test, Issue503Test, Issue523Test, Issue578Test, Issue581Test, Issue74Test, Issue765Test, Issue797Test, MultiDependencyTest, MultipleDataProviderTest, My\Space\ExceptionNamespaceTest, NamespaceCoverageClassExtendedTest, NamespaceCoverageClassTest, NamespaceCoverageCoversClassPublicTest, NamespaceCoverageCoversClassTest, NamespaceCoverageMethodTest, NamespaceCoverageNotPrivateTest, NamespaceCoverageNotProtectedTest, NamespaceCoverageNotPublicTest, NamespaceCoveragePrivateTest, NamespaceCoverageProtectedTest, NamespaceCoveragePublicTest, NoArgTestCaseTest, NoTestCases, NotExistingCoveredElementTest, NotPublicTestCase, NotVoidTestCase, NothingTest, OneTest, OneTestCase, OutputTestCase, OverrideTestCase, PHPUnit\Framework\AssertTest, PHPUnit\Framework\BaseTestListenerTest, PHPUnit\Framework\ConstraintTest, PHPUnit\Framework\Constraint\ArraySubsetTest, PHPUnit\Framework\Constraint\CountTest, PHPUnit\Framework\Constr...eptionMessageRegExpTest, PHPUnit\Framework\Constraint\ExceptionMessageTest, PHPUnit\Framework\Constraint\IsJsonTest, PHPUnit\Framework\Constr...rrorMessageProviderTest, PHPUnit\Framework\Constraint\JsonMatchesTest, PHPUnit\Framework\IncompleteTestCase, PHPUnit\Framework\SkippedTestCase, PHPUnit\Framework\SuiteTest, PHPUnit\Framework\TestCase, PHPUnit\Framework\TestCaseTest, PHPUnit\Framework\TestFailureTest, PHPUnit\Framework\TestImplementorTest, PHPUnit\Framework\TestListenerTest, PHPUnit\Framework\WarningTestCase, PHPUnit\Runner\PhptTestCaseTest, PHPUnit\Util\ConfigurationGeneratorTest, PHPUnit\Util\ConfigurationTest, PHPUnit\Util\GetoptTest, PHPUnit\Util\GlobalStateTest, PHPUnit\Util\JsonTest, PHPUnit\Util\PHP\AbstractPhpProcessTest, PHPUnit\Util\RegularExpressionTest, PHPUnit\Util\TestDox\NamePrettifierTest, PHPUnit\Util\TestTest, PHPUnit\Util\XmlTest, RequirementsClassBeforeClassHookTest, RequirementsTest, StackTest, StopsOnWarningTest, Success, TemplateMethodsTest, Test, TestAutoreferenced, TestDoxGroupTest, TestError, TestIncomplete, TestSkipped, TestWithTest, ThrowExceptionTestCase, ThrowNoExceptionTestCase, TwoTest, WasRun, vendor\project\StatusTest.

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...
140
141 1
        if ($testResultObject !== null) {
142 1
            $testResultObject->stopOnFailure(true);
143
        }
144 1
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149 1
    public function endTest(Test $test, $time)
150
    {
151 1
    }
152
153
    /**
154
     * Builds an Writer.
155
     *
156
     * @return \NunoMaduro\Collision\Contracts\Writer
157
     */
158 2
    protected function buildWriter(): WriterContract
159
    {
160 2
        $writer = new Writer;
161
162 2
        $application = new Application();
163 2
        $reflector = new ReflectionObject($application);
164 2
        $method = $reflector->getMethod('configureIO');
165 2
        $method->setAccessible(true);
166 2
        $method->invoke($application, new ArgvInput, $output = new ConsoleOutput);
167
168 2
        return $writer->setOutput($output);
169
    }
170
}
171