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 * A TestListener that generates JSON messages. 13 */ 14class PHPUnit_Util_Log_JSON extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener 15{ 16 /** 17 * @var string 18 */ 19 protected $currentTestSuiteName = ''; 20 21 /** 22 * @var string 23 */ 24 protected $currentTestName = ''; 25 26 /** 27 * @var bool 28 */ 29 protected $currentTestPass = true; 30 31 /** 32 * An error occurred. 33 * 34 * @param PHPUnit_Framework_Test $test 35 * @param Exception $e 36 * @param float $time 37 */ 38 public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) 39 { 40 $this->writeCase( 41 'error', 42 $time, 43 PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 44 PHPUnit_Framework_TestFailure::exceptionToString($e), 45 $test 46 ); 47 48 $this->currentTestPass = false; 49 } 50 51 /** 52 * A warning occurred. 53 * 54 * @param PHPUnit_Framework_Test $test 55 * @param PHPUnit_Framework_Warning $e 56 * @param float $time 57 */ 58 public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) 59 { 60 $this->writeCase( 61 'warning', 62 $time, 63 PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 64 PHPUnit_Framework_TestFailure::exceptionToString($e), 65 $test 66 ); 67 68 $this->currentTestPass = false; 69 } 70 71 /** 72 * A failure occurred. 73 * 74 * @param PHPUnit_Framework_Test $test 75 * @param PHPUnit_Framework_AssertionFailedError $e 76 * @param float $time 77 */ 78 public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) 79 { 80 $this->writeCase( 81 'fail', 82 $time, 83 PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 84 PHPUnit_Framework_TestFailure::exceptionToString($e), 85 $test 86 ); 87 88 $this->currentTestPass = false; 89 } 90 91 /** 92 * Incomplete test. 93 * 94 * @param PHPUnit_Framework_Test $test 95 * @param Exception $e 96 * @param float $time 97 */ 98 public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) 99 { 100 $this->writeCase( 101 'error', 102 $time, 103 PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 104 'Incomplete Test: ' . $e->getMessage(), 105 $test 106 ); 107 108 $this->currentTestPass = false; 109 } 110 111 /** 112 * Risky test. 113 * 114 * @param PHPUnit_Framework_Test $test 115 * @param Exception $e 116 * @param float $time 117 */ 118 public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) 119 { 120 $this->writeCase( 121 'error', 122 $time, 123 PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 124 'Risky Test: ' . $e->getMessage(), 125 $test 126 ); 127 128 $this->currentTestPass = false; 129 } 130 131 /** 132 * Skipped test. 133 * 134 * @param PHPUnit_Framework_Test $test 135 * @param Exception $e 136 * @param float $time 137 */ 138 public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) 139 { 140 $this->writeCase( 141 'error', 142 $time, 143 PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 144 'Skipped Test: ' . $e->getMessage(), 145 $test 146 ); 147 148 $this->currentTestPass = false; 149 } 150 151 /** 152 * A testsuite started. 153 * 154 * @param PHPUnit_Framework_TestSuite $suite 155 */ 156 public function startTestSuite(PHPUnit_Framework_TestSuite $suite) 157 { 158 $this->currentTestSuiteName = $suite->getName(); 159 $this->currentTestName = ''; 160 161 $this->write( 162 [ 163 'event' => 'suiteStart', 164 'suite' => $this->currentTestSuiteName, 165 'tests' => count($suite) 166 ] 167 ); 168 } 169 170 /** 171 * A testsuite ended. 172 * 173 * @param PHPUnit_Framework_TestSuite $suite 174 */ 175 public function endTestSuite(PHPUnit_Framework_TestSuite $suite) 176 { 177 $this->currentTestSuiteName = ''; 178 $this->currentTestName = ''; 179 } 180 181 /** 182 * A test started. 183 * 184 * @param PHPUnit_Framework_Test $test 185 */ 186 public function startTest(PHPUnit_Framework_Test $test) 187 { 188 $this->currentTestName = PHPUnit_Util_Test::describe($test); 189 $this->currentTestPass = true; 190 191 $this->write( 192 [ 193 'event' => 'testStart', 194 'suite' => $this->currentTestSuiteName, 195 'test' => $this->currentTestName 196 ] 197 ); 198 } 199 200 /** 201 * A test ended. 202 * 203 * @param PHPUnit_Framework_Test $test 204 * @param float $time 205 */ 206 public function endTest(PHPUnit_Framework_Test $test, $time) 207 { 208 if ($this->currentTestPass) { 209 $this->writeCase('pass', $time, [], '', $test); 210 } 211 } 212 213 /** 214 * @param string $status 215 * @param float $time 216 * @param array $trace 217 * @param string $message 218 * @param PHPUnit_Framework_TestCase|null $test 219 */ 220 protected function writeCase($status, $time, array $trace = [], $message = '', $test = null) 221 { 222 $output = ''; 223 // take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput 224 if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) { 225 $output = $test->getActualOutput(); 226 } 227 $this->write( 228 [ 229 'event' => 'test', 230 'suite' => $this->currentTestSuiteName, 231 'test' => $this->currentTestName, 232 'status' => $status, 233 'time' => $time, 234 'trace' => $trace, 235 'message' => PHPUnit_Util_String::convertToUtf8($message), 236 'output' => $output, 237 ] 238 ); 239 } 240 241 /** 242 * @param string $buffer 243 */ 244 public function write($buffer) 245 { 246 array_walk_recursive($buffer, function (&$input) { 247 if (is_string($input)) { 248 $input = PHPUnit_Util_String::convertToUtf8($input); 249 } 250 }); 251 252 parent::write(json_encode($buffer, JSON_PRETTY_PRINT)); 253 } 254} 255