1<?php
2/*
3 * This file is part of PHPUnit.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11/**
12 * Base class for test listeners that interact with an issue tracker.
13 */
14abstract class PHPUnit_Extensions_TicketListener implements PHPUnit_Framework_TestListener
15{
16    /**
17     * @var array
18     */
19    protected $ticketCounts = [];
20
21    /**
22     * @var bool
23     */
24    protected $ran = false;
25
26    /**
27     * An error occurred.
28     *
29     * @param PHPUnit_Framework_Test $test
30     * @param Exception              $e
31     * @param float                  $time
32     */
33    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
34    {
35    }
36
37    /**
38     * A failure occurred.
39     *
40     * @param PHPUnit_Framework_Test                 $test
41     * @param PHPUnit_Framework_AssertionFailedError $e
42     * @param float                                  $time
43     */
44    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
45    {
46    }
47
48    /**
49     * Incomplete test.
50     *
51     * @param PHPUnit_Framework_Test $test
52     * @param Exception              $e
53     * @param float                  $time
54     */
55    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
56    {
57    }
58
59    /**
60     * Risky test.
61     *
62     * @param PHPUnit_Framework_Test $test
63     * @param Exception              $e
64     * @param float                  $time
65     */
66    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
67    {
68    }
69
70    /**
71     * Skipped test.
72     *
73     * @param PHPUnit_Framework_Test $test
74     * @param Exception              $e
75     * @param float                  $time
76     */
77    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
78    {
79    }
80
81    /**
82     * A test suite started.
83     *
84     * @param PHPUnit_Framework_TestSuite $suite
85     */
86    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
87    {
88    }
89
90    /**
91     * A test suite ended.
92     *
93     * @param PHPUnit_Framework_TestSuite $suite
94     */
95    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
96    {
97    }
98
99    /**
100     * A test started.
101     *
102     * @param PHPUnit_Framework_Test $test
103     */
104    public function startTest(PHPUnit_Framework_Test $test)
105    {
106        if (!$test instanceof PHPUnit_Framework_WarningTestCase) {
107            if ($this->ran) {
108                return;
109            }
110
111            $name    = $test->getName(false);
112            $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name);
113
114            foreach ($tickets as $ticket) {
115                $this->ticketCounts[$ticket][$name] = 1;
116            }
117
118            $this->ran = true;
119        }
120    }
121
122    /**
123     * A test ended.
124     *
125     * @param PHPUnit_Framework_Test $test
126     * @param float                  $time
127     */
128    public function endTest(PHPUnit_Framework_Test $test, $time)
129    {
130        if (!$test instanceof PHPUnit_Framework_WarningTestCase) {
131            if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
132                $ifStatus   = ['assigned', 'new', 'reopened'];
133                $newStatus  = 'closed';
134                $message    = 'Automatically closed by PHPUnit (test passed).';
135                $resolution = 'fixed';
136                $cumulative = true;
137            } elseif ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
138                $ifStatus   = ['closed'];
139                $newStatus  = 'reopened';
140                $message    = 'Automatically reopened by PHPUnit (test failed).';
141                $resolution = '';
142                $cumulative = false;
143            } else {
144                return;
145            }
146
147            $name    = $test->getName(false);
148            $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name);
149
150            foreach ($tickets as $ticket) {
151                // Remove this test from the totals (if it passed).
152                if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) {
153                    unset($this->ticketCounts[$ticket][$name]);
154                }
155
156                // Only close tickets if ALL referenced cases pass
157                // but reopen tickets if a single test fails.
158                if ($cumulative) {
159                    // Determine number of to-pass tests:
160                    if (count($this->ticketCounts[$ticket]) > 0) {
161                        // There exist remaining test cases with this reference.
162                        $adjustTicket = false;
163                    } else {
164                        // No remaining tickets, go ahead and adjust.
165                        $adjustTicket = true;
166                    }
167                } else {
168                    $adjustTicket = true;
169                }
170
171                $ticketInfo = $this->getTicketInfo($ticket);
172
173                if ($adjustTicket && in_array($ticketInfo['status'], $ifStatus)) {
174                    $this->updateTicket($ticket, $newStatus, $message, $resolution);
175                }
176            }
177        }
178    }
179
180    /**
181     * @param mixed $ticketId
182     *
183     * @return mixed
184     */
185    abstract protected function getTicketInfo($ticketId = null);
186
187    /**
188     * @param string $ticketId
189     * @param string $newStatus
190     * @param string $message
191     * @param string $resolution
192     */
193    abstract protected function updateTicket($ticketId, $newStatus, $message, $resolution);
194}
195