Reworked TapParser to be compliant and more robust.
Added another test case from #571. Updated the output of TapParser::processTestLine. Broke TapParser::parse down in simpler methods. TapParser: ignore leading garbage and properly complain on missing TAP log. TapParser: detect and report duplicated TAP log. TapParser: got rid of the "test" and "suite" values. They are only available with PHPUnit. TapParser: append the message from yaml diagnostic to existing message. Reworked the dispaly of test results. PHPUnit plugin: pretty print test data.
This commit is contained in:
parent
2c43cd1cac
commit
9d4116e3c9
|
@ -194,14 +194,20 @@ Services</a> sektionen under dit Bitbucket-repository.',
|
|||
'end' => 'Slut',
|
||||
'from' => 'Fra',
|
||||
'to' => 'Til',
|
||||
'suite' => 'Suite',
|
||||
'test' => 'Test',
|
||||
'result' => 'Resultat',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Tog %d sekunder',
|
||||
'build_created' => 'Build Oprettet',
|
||||
'build_started' => 'Build Startet',
|
||||
'build_finished' => 'Build Afsluttet',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Navn',
|
||||
|
|
|
@ -192,14 +192,20 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab
|
|||
'end' => 'Ende',
|
||||
'from' => 'Von',
|
||||
'to' => 'Bis',
|
||||
'suite' => 'Suite',
|
||||
'test' => 'Test',
|
||||
'result' => 'Resultat',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Benötigte %d Sekunden',
|
||||
'build_created' => 'Build erstellt',
|
||||
'build_started' => 'Build gestartet',
|
||||
'build_finished' => 'Build abgeschlossen',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Name',
|
||||
|
|
|
@ -194,14 +194,20 @@ Services</a> του Bitbucket αποθετηρίου σας.',
|
|||
'end' => 'Τέλος',
|
||||
'from' => 'Από',
|
||||
'to' => 'Προς',
|
||||
'suite' => 'Σουίτα',
|
||||
'test' => 'Τέστ',
|
||||
'result' => 'Αποτέλεσμα',
|
||||
'ok' => 'ΟΚ',
|
||||
'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα',
|
||||
'build_created' => 'Η κατασκευή δημιουργήθηκε',
|
||||
'build_started' => 'Η κατασκευή άρχισε',
|
||||
'build_finished' => 'Η κατασκευή ολοκληρώθηκε',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Όνομα',
|
||||
|
|
|
@ -198,14 +198,20 @@ PHPCI',
|
|||
'end' => 'End',
|
||||
'from' => 'From',
|
||||
'to' => 'To',
|
||||
'suite' => 'Suite',
|
||||
'test' => 'Test',
|
||||
'result' => 'Result',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Took %d seconds',
|
||||
'build_created' => 'Build Created',
|
||||
'build_started' => 'Build Started',
|
||||
'build_finished' => 'Build Finished',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Name',
|
||||
|
|
|
@ -195,14 +195,20 @@ PHPCI',
|
|||
'end' => 'Fin',
|
||||
'from' => 'À partir de',
|
||||
'to' => 'jusque',
|
||||
'suite' => 'Suite',
|
||||
'test' => 'Test',
|
||||
'result' => 'Resultat',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Exécuté en %d secondes',
|
||||
'build_created' => 'Build créé',
|
||||
'build_started' => 'Build démarré',
|
||||
'build_finished' => 'Build terminé',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'Pas de message',
|
||||
'test_success' => 'Réussi(s): %d',
|
||||
'test_fail' => 'Echec(s): %d',
|
||||
'test_skipped' => 'Passé(s): %d',
|
||||
'test_error' => 'Erreurs: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Nom',
|
||||
|
|
|
@ -197,14 +197,20 @@ PHPCI',
|
|||
'end' => 'Finisci',
|
||||
'from' => 'Da',
|
||||
'to' => 'A',
|
||||
'suite' => 'Suite',
|
||||
'test' => 'Test',
|
||||
'result' => 'Risultati',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Sono stati impiegati %d seconds',
|
||||
'build_created' => 'Build Creata',
|
||||
'build_started' => 'Build Avviata',
|
||||
'build_finished' => 'Build Terminata',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Nome',
|
||||
|
|
|
@ -195,14 +195,20 @@ Services</a> sectie van je Bitbucket repository toegevoegd worden.',
|
|||
'end' => 'Einde',
|
||||
'from' => 'Van',
|
||||
'to' => 'Tot',
|
||||
'suite' => 'Suite',
|
||||
'test' => 'Test',
|
||||
'result' => 'Resultaat',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Duurde %d seconden',
|
||||
'build_created' => 'Build aangemaakt',
|
||||
'build_started' => 'Build gestart',
|
||||
'build_finished' => 'Build beëindigd',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Naam',
|
||||
|
|
|
@ -198,14 +198,20 @@ Services</a> repozytoria Bitbucket.',
|
|||
'end' => 'Koniec',
|
||||
'from' => 'Od',
|
||||
'to' => 'Do',
|
||||
'suite' => 'Zestaw ',
|
||||
'test' => 'Test',
|
||||
'result' => 'Wynik',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Zajęło %d sekund',
|
||||
'build_created' => 'Budowanie Stworzone',
|
||||
'build_started' => 'Budowanie Rozpoczęte',
|
||||
'build_finished' => 'Budowanie Zakończone',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Nazwa',
|
||||
|
|
|
@ -193,14 +193,20 @@ PHPCI',
|
|||
'end' => 'Конец',
|
||||
'from' => 'Из',
|
||||
'to' => 'В',
|
||||
'suite' => 'Комплект',
|
||||
'test' => 'Тест',
|
||||
'result' => 'Результат',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Заняло секунд: %d',
|
||||
'build_created' => 'Сборка создана',
|
||||
'build_started' => 'Сборка запущена',
|
||||
'build_finished' => 'Сборка окончена',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Имя',
|
||||
|
|
|
@ -195,14 +195,20 @@ PHPCI',
|
|||
'end' => 'Кінець',
|
||||
'from' => 'Від',
|
||||
'to' => 'До',
|
||||
'suite' => 'Комплект',
|
||||
'test' => 'Тест',
|
||||
'result' => 'Результат',
|
||||
'ok' => 'OK',
|
||||
'took_n_seconds' => 'Зайняло %d секунд',
|
||||
'build_created' => 'Збірка створена',
|
||||
'build_started' => 'Збірка розпочата',
|
||||
'build_finished' => 'Збірка завершена',
|
||||
'test_message' => 'Message',
|
||||
'test_no_message' => 'No message',
|
||||
'test_success' => 'Succesfull: %d',
|
||||
'test_fail' => 'Failures: %d',
|
||||
'test_skipped' => 'Skipped: %d',
|
||||
'test_error' => 'Errors: %d',
|
||||
'test_todo' => 'Todos: %d',
|
||||
'test_total' => '%d test(s)',
|
||||
|
||||
// Users
|
||||
'name' => 'Ім’я',
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
namespace PHPCI\Plugin\Util;
|
||||
|
||||
use Exception;
|
||||
use PHPCI\Helper\Lang;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Processes TAP format strings into usable test result data.
|
||||
|
@ -10,18 +12,41 @@ use PHPCI\Helper\Lang;
|
|||
*/
|
||||
class TapParser
|
||||
{
|
||||
const TEST_COUNTS_PATTERN = '/([0-9]+)\.\.([0-9]+)/';
|
||||
const TEST_LINE_PATTERN = '/(ok|not ok)\s+[0-9]+\s+\-\s+([^\n]+)::([^\n]+)/';
|
||||
const TEST_MESSAGE_PATTERN = '/message\:\s+\'([^\']+)\'/';
|
||||
const TEST_COVERAGE_PATTERN = '/Generating code coverage report/';
|
||||
const TEST_SKIP_PATTERN = '/ok\s+[0-9]+\s+\-\s+#\s+SKIP/';
|
||||
const TEST_COUNTS_PATTERN = '/^\d+\.\.(\d+)/';
|
||||
const TEST_LINE_PATTERN = '/^(ok|not ok)(?:\s+\d+)?(?:\s+\-)?\s*(.*?)(?:\s*#\s*(skip|todo)\s*(.*))?\s*$/i';
|
||||
const TEST_YAML_START = '/^(\s*)---/';
|
||||
const TEST_DIAGNOSTIC = '/^#/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tapString;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $failures = 0;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $lines;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $lineNumber;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
protected $testCount;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $results;
|
||||
|
||||
/**
|
||||
* Create a new TAP parser for a given string.
|
||||
* @param string $tapString The TAP format string to be parsed.
|
||||
|
@ -38,81 +63,175 @@ class TapParser
|
|||
{
|
||||
// Split up the TAP string into an array of lines, then
|
||||
// trim all of the lines so there's no leading or trailing whitespace.
|
||||
$lines = explode("\n", $this->tapString);
|
||||
$lines = array_map(function ($line) {
|
||||
return trim($line);
|
||||
}, $lines);
|
||||
$this->lines = array_map('rtrim', explode("\n", $this->tapString));
|
||||
$this->lineNumber = 0;
|
||||
|
||||
// Check TAP version:
|
||||
$versionLine = array_shift($lines);
|
||||
$this->testCount = false;
|
||||
$this->results = array();
|
||||
|
||||
if ($versionLine != 'TAP version 13') {
|
||||
throw new \Exception(Lang::get('tap_version'));
|
||||
$header = $this->findTapLog();
|
||||
|
||||
$line = $this->nextLine();
|
||||
if ($line === $header) {
|
||||
throw new Exception("Duplicated TAP log, please check the configration.");
|
||||
}
|
||||
|
||||
if (isset($lines[count($lines) - 1]) && preg_match(self::TEST_COVERAGE_PATTERN, $lines[count($lines) - 1])) {
|
||||
array_pop($lines);
|
||||
if ($lines[count($lines) - 1] == "") {
|
||||
array_pop($lines);
|
||||
while ($line !== false && ($this->testCount === false || count($this->results) < $this->testCount)) {
|
||||
$this->parseLine($line);
|
||||
$line = $this->nextLine();
|
||||
}
|
||||
|
||||
if (count($this->results) !== $this->testCount) {
|
||||
throw new Exception(Lang::get('tap_error'));
|
||||
}
|
||||
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
/** Looks for the start of the TAP log in the string.
|
||||
*
|
||||
* @return string The TAP header line.
|
||||
*
|
||||
* @throws Exception if no TAP log is found or versions mismatch.
|
||||
*/
|
||||
protected function findTapLog()
|
||||
{
|
||||
// Look for the beggning of the TAP output
|
||||
do {
|
||||
$header = $this->nextLine();
|
||||
} while ($header !== false && substr($header, 0, 12) !== 'TAP version ');
|
||||
|
||||
//
|
||||
if ($header === false) {
|
||||
throw new Exception('No TAP log found, please check the configuration.');
|
||||
} elseif ($header !== 'TAP version 13') {
|
||||
throw new Exception(Lang::get('tap_version'));
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/** Fetch the next line.
|
||||
*
|
||||
* @return string|false The next line or false if the end has been reached.
|
||||
*/
|
||||
protected function nextLine()
|
||||
{
|
||||
if ($this->lineNumber < count($this->lines)) {
|
||||
return $this->lines[$this->lineNumber++];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Parse a single line.
|
||||
*
|
||||
* @param string $line
|
||||
*/
|
||||
protected function parseLine($line)
|
||||
{
|
||||
if (preg_match(self::TEST_COUNTS_PATTERN, $line, $matches)) {
|
||||
$this->testCount = intval($matches[1]);
|
||||
|
||||
} elseif (preg_match(self::TEST_DIAGNOSTIC, $line)) {
|
||||
return;
|
||||
|
||||
} elseif (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) {
|
||||
$this->results[] = $this->processTestLine(
|
||||
$matches[1],
|
||||
isset($matches[2]) ? $matches[2] : '',
|
||||
isset($matches[3]) ? $matches[3] : null,
|
||||
isset($matches[4]) ? $matches[4] : null
|
||||
);
|
||||
|
||||
} elseif (preg_match(self::TEST_YAML_START, $line, $matches)) {
|
||||
$diagnostic = $this->processYamlBlock($matches[1]);
|
||||
$test = array_pop($this->results);
|
||||
if (isset($test['message'], $diagnostic['message'])) {
|
||||
$test['message'] .= PHP_EOL . $diagnostic['message'];
|
||||
unset($diagnostic['message']);
|
||||
}
|
||||
$this->results[] = array_replace($test, $diagnostic);
|
||||
|
||||
} else {
|
||||
throw new Exception(sprintf('Incorrect TAP data, line %d: %s', $this->lineNumber, $line));
|
||||
}
|
||||
|
||||
$matches = array();
|
||||
$totalTests = 0;
|
||||
if (preg_match(self::TEST_COUNTS_PATTERN, $lines[0], $matches)) {
|
||||
array_shift($lines);
|
||||
$totalTests = (int) $matches[2];
|
||||
}
|
||||
|
||||
if (isset($lines[count($lines) - 1]) &&
|
||||
preg_match(self::TEST_COUNTS_PATTERN, $lines[count($lines) - 1], $matches)) {
|
||||
array_pop($lines);
|
||||
$totalTests = (int) $matches[2];
|
||||
}
|
||||
|
||||
$rtn = $this->processTestLines($lines);
|
||||
|
||||
if ($totalTests != count($rtn)) {
|
||||
throw new \Exception(Lang::get('tap_error'));
|
||||
}
|
||||
|
||||
return $rtn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the individual test lines within a TAP string.
|
||||
* @param $lines
|
||||
* Process an individual test line.
|
||||
*
|
||||
* @param string $result
|
||||
* @param string $message
|
||||
* @param string $directive
|
||||
* @param string $reason
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function processTestLines($lines)
|
||||
protected function processTestLine($result, $message, $directive, $reason)
|
||||
{
|
||||
$rtn = array();
|
||||
$test = array(
|
||||
'pass' => true,
|
||||
'message' => $message,
|
||||
'severity' => 'success',
|
||||
);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$matches = array();
|
||||
|
||||
if (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) {
|
||||
$ok = ($matches[1] == 'ok' ? true : false);
|
||||
|
||||
if (!$ok) {
|
||||
$this->failures++;
|
||||
}
|
||||
|
||||
$item = array(
|
||||
'pass' => $ok,
|
||||
'suite' => $matches[2],
|
||||
'test' => $matches[3],
|
||||
);
|
||||
|
||||
$rtn[] = $item;
|
||||
} elseif (preg_match(self::TEST_SKIP_PATTERN, $line, $matches)) {
|
||||
$rtn[] = array('message' => 'SKIP');
|
||||
} elseif (preg_match(self::TEST_MESSAGE_PATTERN, $line, $matches)) {
|
||||
$rtn[count($rtn) - 1]['message'] = $matches[1];
|
||||
}
|
||||
if ($result !== 'ok') {
|
||||
$test['pass'] = false;
|
||||
$test['severity'] = substr($message, 0, 6) === 'Error:' ? 'error' : 'fail';
|
||||
$this->failures++;
|
||||
}
|
||||
|
||||
return $rtn;
|
||||
if ($directive) {
|
||||
$test = $this->processDirective($test, $directive, $reason);
|
||||
}
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/** Process an indented Yaml block.
|
||||
*
|
||||
* @param string $indent The block indentation to ignore.
|
||||
*
|
||||
* @return array The processed Yaml content.
|
||||
*/
|
||||
protected function processYamlBlock($indent)
|
||||
{
|
||||
$startLine = $this->lineNumber+1;
|
||||
$endLine = $indent.'...';
|
||||
$yamlLines = array();
|
||||
do {
|
||||
$line = $this->nextLine();
|
||||
if ($line === false) {
|
||||
throw new Exception(Lang::get('tap_error_endless_yaml', $startLine));
|
||||
} elseif ($line === $endLine) {
|
||||
break;
|
||||
}
|
||||
$yamlLines[] = substr($line, strlen($indent));
|
||||
|
||||
} while (true);
|
||||
|
||||
return Yaml::parse(join("\n", $yamlLines));
|
||||
}
|
||||
|
||||
/** Process a TAP directive
|
||||
*
|
||||
* @param array $test
|
||||
* @param string $directive
|
||||
* @param string $reason
|
||||
* @return array
|
||||
*/
|
||||
protected function processDirective($test, $directive, $reason)
|
||||
{
|
||||
$test['severity'] = strtolower($directive) === 'skip' ? 'skipped' : 'todo';
|
||||
|
||||
if (!empty($reason)) {
|
||||
if (!empty($test['message'])) {
|
||||
$test['message'] .= ', '.$test['severity'].': ';
|
||||
}
|
||||
$test['message'] .= $reason;
|
||||
}
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,22 +5,254 @@ use PHPCI\Plugin\Util\TapParser;
|
|||
|
||||
class TapParserTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSkipped()
|
||||
public function testSimple()
|
||||
{
|
||||
$content = <<<TAP
|
||||
Leading garbage !
|
||||
TAP version 13
|
||||
ok 1 - SomeTest::testAnother
|
||||
not ok
|
||||
1..2
|
||||
Trailing garbage !
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$result = $parser->parse();
|
||||
|
||||
$this->assertEquals(array(
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'SomeTest::testAnother'),
|
||||
array('pass' => false, 'severity' => 'fail', 'message' => ''),
|
||||
), $result);
|
||||
|
||||
$this->assertEquals(1, $parser->getTotalFailures());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessageRegExp /No TAP/
|
||||
*/
|
||||
public function testNoTapData()
|
||||
{
|
||||
$content = <<<TAP
|
||||
Only garbage !
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$parser->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessageRegExp /Duplicated TAP/
|
||||
*/
|
||||
public function testDuplicateOutput()
|
||||
{
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
TAP version 13
|
||||
ok 1 - SomeTest::testAnother
|
||||
ok 1 - SomeTest::testAnother
|
||||
not ok - Failure: SomeTest::testAnother
|
||||
not ok - Failure: SomeTest::testAnother
|
||||
not ok 3 - Error: SomeTest::testAnother
|
||||
not ok 3 - Error: SomeTest::testAnother
|
||||
1..3
|
||||
1..3
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$parser->parse();
|
||||
}
|
||||
|
||||
public function testSuiteAndTest()
|
||||
{
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
ok 1 - SomeTest::testAnother
|
||||
ok 2 - # SKIP
|
||||
not ok - Failure: SomeTest::testAnother
|
||||
not ok 3 - Error: SomeTest::testAnother
|
||||
1..3
|
||||
Trailing garbage !
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$result = $parser->parse();
|
||||
|
||||
$this->assertEquals(array(
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'SomeTest::testAnother',),
|
||||
array('pass' => false, 'severity' => 'fail', 'message' => 'Failure: SomeTest::testAnother'),
|
||||
array('pass' => false, 'severity' => 'error', 'message' => 'Error: SomeTest::testAnother'),
|
||||
), $result);
|
||||
|
||||
$this->assertEquals(2, $parser->getTotalFailures());
|
||||
}
|
||||
|
||||
|
||||
public function testSkipped()
|
||||
{
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
ok 1 - # SKIP
|
||||
ok 2 - # SKIP foobar
|
||||
ok 3 - foo # SKIP bar
|
||||
1..3
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$result = $parser->parse();
|
||||
|
||||
$this->assertEquals(array(
|
||||
array('pass' => true, 'severity' => 'skipped', 'message' => ''),
|
||||
array('pass' => true, 'severity' => 'skipped', 'message' => 'foobar'),
|
||||
array('pass' => true, 'severity' => 'skipped', 'message' => 'foo, skipped: bar'),
|
||||
), $result);
|
||||
|
||||
$this->assertEquals(0, $parser->getTotalFailures());
|
||||
}
|
||||
|
||||
public function testTodo()
|
||||
{
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
ok 1 - SomeTest::testAnother # TODO really implement this test
|
||||
ok 2 - # TODO really implement this test
|
||||
ok 3 - this is a message # TODO really implement this test
|
||||
ok 4 - # TODO
|
||||
1..4
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$result = $parser->parse();
|
||||
|
||||
$this->assertEquals(array(
|
||||
array('pass' => true, 'severity' => 'todo', 'message' => 'SomeTest::testAnother, todo: really implement this test'),
|
||||
array('pass' => true, 'severity' => 'todo', 'message' => 'really implement this test'),
|
||||
array('pass' => true, 'severity' => 'todo', 'message' => 'this is a message, todo: really implement this test'),
|
||||
array('pass' => true, 'severity' => 'todo', 'message' => ''),
|
||||
), $result);
|
||||
|
||||
$this->assertEquals(0, $parser->getTotalFailures());
|
||||
}
|
||||
|
||||
public function testYamlDiagnostic()
|
||||
{
|
||||
// From https://phpunit.de/manual/current/en/logging.html#logging.tap
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
not ok 1 - FOO
|
||||
---
|
||||
message: BAR
|
||||
...
|
||||
1..1
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$result = $parser->parse();
|
||||
|
||||
$this->assertEquals(array(
|
||||
array(
|
||||
'pass' => false,
|
||||
'severity' => 'fail',
|
||||
'message' => 'FOO' . PHP_EOL . 'BAR',
|
||||
),
|
||||
), $result);
|
||||
|
||||
$this->assertEquals(1, $parser->getTotalFailures());
|
||||
}
|
||||
|
||||
public function testFailureAndError()
|
||||
{
|
||||
// From https://phpunit.de/manual/current/en/logging.html#logging.tap
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
not ok 1 - Failure: testFailure::FailureErrorTest
|
||||
not ok 2 - Error: testError::FailureErrorTest
|
||||
1..2
|
||||
TAP;
|
||||
$parser = new TapParser($content);
|
||||
$result = $parser->parse();
|
||||
|
||||
$this->assertEquals(array(
|
||||
array('pass' => true, 'suite' => 'SomeTest', 'test' => 'testAnother'),
|
||||
array('message' => 'SKIP'),
|
||||
array(
|
||||
'pass' => false,
|
||||
'severity' => 'fail',
|
||||
'message' => 'Failure: testFailure::FailureErrorTest',
|
||||
),
|
||||
array(
|
||||
'pass' => false,
|
||||
'severity' => 'error',
|
||||
'message' => 'Error: testError::FailureErrorTest',
|
||||
)
|
||||
), $result);
|
||||
|
||||
$this->assertEquals(2, $parser->getTotalFailures());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
*/
|
||||
public function testGarbage()
|
||||
{
|
||||
$content = "Garbage !";
|
||||
|
||||
$parser = new TapParser($content);
|
||||
$parser->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
*/
|
||||
public function testInvalidTestCount()
|
||||
{
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
ok 1 - SomeTest::testAnother
|
||||
not ok
|
||||
1..5
|
||||
TAP;
|
||||
|
||||
$parser = new TapParser($content);
|
||||
$parser->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
*/
|
||||
public function testEndlessYaml()
|
||||
{
|
||||
$content = <<<TAP
|
||||
TAP version 13
|
||||
ok 1 - SomeTest::testAnother
|
||||
---
|
||||
1..1
|
||||
TAP;
|
||||
|
||||
$parser = new TapParser($content);
|
||||
$parser->parse();
|
||||
}
|
||||
|
||||
public function testCodeception()
|
||||
{
|
||||
$content = <<< TAP
|
||||
TAP version 13
|
||||
ok 1 - try to access the dashboard as a guest (Auth/GuestAccessDashboardAndRedirectCept)
|
||||
ok 2 - see the login page (Auth/SeeLoginCept)
|
||||
ok 3 - click forgot password and see the email form (Auth/SeeLoginForgotPasswordCept)
|
||||
ok 4 - see powered by runmybusiness branding (Auth/ShouldSeePoweredByBrandingCept)
|
||||
ok 5 - submit invalid credentials (Auth/SubmitLoginAndFailCept)
|
||||
ok 6 - submit valid credentials and see the dashboard (Auth/SubmitLoginAndSucceedCept)
|
||||
1..6
|
||||
TAP;
|
||||
|
||||
$parser = new TapParser($content);
|
||||
$result = $parser->parse();
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'try to access the dashboard as a guest (Auth/GuestAccessDashboardAndRedirectCept)'),
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'see the login page (Auth/SeeLoginCept)'),
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'click forgot password and see the email form (Auth/SeeLoginForgotPasswordCept)'),
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'see powered by runmybusiness branding (Auth/ShouldSeePoweredByBrandingCept)'),
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'submit invalid credentials (Auth/SubmitLoginAndFailCept)'),
|
||||
array('pass' => true, 'severity' => 'success', 'message' => 'submit valid credentials and see the dashboard (Auth/SubmitLoginAndSucceedCept)'),
|
||||
),
|
||||
$result
|
||||
);
|
||||
|
||||
$this->assertEquals(0, $parser->getTotalFailures());
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,4 +84,11 @@
|
|||
padding: 0;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
#phpunit-data th div { margin: 0 0.5em; }
|
||||
#phpunit-data .success td { background: none; color: #00a65a; }
|
||||
#phpunit-data .fail td { background: none; color: #f56954; }
|
||||
#phpunit-data .error td { background: none; color: #f56954; }
|
||||
#phpunit-data .skipped td { background: none; color: #e08e0b; }
|
||||
#phpunit-data .todo td { background: none; color: #00c0ef; }
|
||||
|
|
|
@ -6,6 +6,13 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({
|
|||
displayOnUpdate: false,
|
||||
box: true,
|
||||
rendered: false,
|
||||
statusMap: {
|
||||
success : 'ok',
|
||||
fail: 'remove',
|
||||
error: 'warning-sign',
|
||||
todo: 'info-sign',
|
||||
skipped: 'exclamation-sign'
|
||||
},
|
||||
|
||||
register: function() {
|
||||
var self = this;
|
||||
|
@ -21,6 +28,11 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({
|
|||
query();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '#phpunit-data .test-toggle', function(ev) {
|
||||
var input = $(ev.target);
|
||||
$('#phpunit-data tbody ' + input.data('target')).toggle(input.prop('checked'));
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -28,7 +40,7 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({
|
|||
return $('<div class="table-responsive"><table class="table" id="phpunit-data">' +
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
' <th>'+Lang.get('test')+'</th>' +
|
||||
' <th>'+Lang.get('test_message')+'</th>' +
|
||||
'</tr>' +
|
||||
'</thead><tbody></tbody></table></div>');
|
||||
},
|
||||
|
@ -43,7 +55,9 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({
|
|||
this.lastData = e.queryData;
|
||||
|
||||
var tests = this.lastData[0].meta_value;
|
||||
var thead = $('#phpunit-data thead tr');
|
||||
var tbody = $('#phpunit-data tbody');
|
||||
thead.empty().append('<th>'+Lang.get('test_message')+'</th>');
|
||||
tbody.empty();
|
||||
|
||||
if (tests.length == 0) {
|
||||
|
@ -51,24 +65,74 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({
|
|||
return;
|
||||
}
|
||||
|
||||
var counts = { success: 0, fail: 0, error: 0, skipped: 0, todo: 0 }, total = 0;
|
||||
|
||||
for (var i in tests) {
|
||||
|
||||
var row = $('<tr>' +
|
||||
'<td><strong>'+tests[i].suite+'' +
|
||||
'::'+tests[i].test+'</strong><br>' +
|
||||
''+(tests[i].message || '')+'</td>' +
|
||||
'</tr>');
|
||||
|
||||
if (!tests[i].pass) {
|
||||
row.addClass('danger');
|
||||
} else {
|
||||
row.addClass('success');
|
||||
}
|
||||
|
||||
tbody.append(row);
|
||||
var severity = tests[i].severity || 'success',
|
||||
message = tests[i].message || ('<i>' + Lang.get('test_no_message') + '</i>');
|
||||
counts[severity]++;
|
||||
total++;
|
||||
tbody.append(
|
||||
'<tr class="'+ severity + '">' +
|
||||
'<td colspan="3">' +
|
||||
'<div>' + message + '</div>' +
|
||||
(tests[i].data ? '<div>' + this.repr(tests[i].data) + '</div>' : '') +
|
||||
'</td>' +
|
||||
'</tr>'
|
||||
);
|
||||
}
|
||||
|
||||
var checkboxes = $('<th/>');
|
||||
thead.append(checkboxes).append('<th>' + Lang.get('test_total', total) + '</th>');
|
||||
|
||||
for (var key in counts) {
|
||||
var count = counts[key];
|
||||
if(count > 0) {
|
||||
checkboxes.append(
|
||||
'<div style="float:left" class="' + key + '"><input type="checkbox" class="test-toggle" data-target=".' + key + '" ' +
|
||||
(key !== 'success' ? ' checked' : '') + '/> ' +
|
||||
Lang.get('test_'+key, count)+ '</div> '
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
tbody.find('.success').hide();
|
||||
|
||||
$('#build-phpunit-errors').show();
|
||||
},
|
||||
|
||||
repr: function(data)
|
||||
{
|
||||
switch(typeof(data)) {
|
||||
case 'boolean':
|
||||
return '<span class="boolean">' + (data ? 'true' : 'false') + '</span>';
|
||||
case 'string':
|
||||
return '<span class="string">"' + data + '"</span>';
|
||||
case 'undefined': case null:
|
||||
return '<span class="null">null</span>';
|
||||
case 'object':
|
||||
var rows = [];
|
||||
if(data instanceof Array) {
|
||||
for(var i in data) {
|
||||
rows.push('<tr><td colspan="3">' + this.repr(data[i]) + ',</td></tr>');
|
||||
}
|
||||
} else {
|
||||
for(var key in data) {
|
||||
rows.push(
|
||||
'<tr>' +
|
||||
'<td>' + this.repr(key) + '</td>' +
|
||||
'<td>=></td>' +
|
||||
'<td>' + this.repr(data[key]) + ',</td>' +
|
||||
'</tr>');
|
||||
}
|
||||
}
|
||||
return '<table>' +
|
||||
'<tr><th colspan="3">array(</th></tr>' +
|
||||
rows.join('') +
|
||||
'<tr><th colspan="3">)</th></tr>' +
|
||||
'</table>';
|
||||
}
|
||||
return '???';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue