Newer
Older

Alex Pott
committed
simpletest_script_print_error('Test group not found: ' . $first_group);
simpletest_script_print_alternatives($first_group, $all_groups);
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
}
// Merge the tests from the groups together.

Dries Buytaert
committed
foreach ($args['test_names'] as $group_name) {
$test_list = array_merge($test_list, array_keys($groups[$group_name]));

Dries Buytaert
committed
}
// Ensure our list of tests contains only one entry for each test.
$test_list = array_unique($test_list);

Dries Buytaert
committed
}

Dries Buytaert
committed
// If the test list creation does not automatically limit by test type then
// we need to do so here.
if (!$types_processed) {
$test_list = array_filter($test_list, function ($test_class) use ($args) {
$test_info = TestDiscovery::getTestInfo($test_class);
return in_array($test_info['type'], $args['types'], TRUE);
});
}

Dries Buytaert
committed
if (empty($test_list)) {
simpletest_script_print_error('No valid tests were specified.');
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);

Dries Buytaert
committed
}
return $test_list;

Dries Buytaert
committed
/**
* Initialize the reporter.
*/
function simpletest_script_reporter_init() {

Dries Buytaert
committed
global $args, $test_list, $results_map;

Dries Buytaert
committed

Lee Rowlands
committed
$results_map = [

Dries Buytaert
committed
'pass' => 'Pass',
'fail' => 'Fail',

Alex Pott
committed
'exception' => 'Exception',

Lee Rowlands
committed
];

Dries Buytaert
committed
echo "\n";
echo "Drupal test run\n";
echo "---------------\n";
echo "\n";

Dries Buytaert
committed

Dries Buytaert
committed
// Tell the user about what tests are to be run.
if ($args['all']) {
echo "All tests will run.\n\n";
}
else {
echo "Tests to be run:\n";

Dries Buytaert
committed
foreach ($test_list as $class_name) {

Angie Byron
committed
echo " - $class_name\n";

Dries Buytaert
committed
}
echo "\n";

Dries Buytaert
committed

Dries Buytaert
committed
echo "Test run started:\n";

Angie Byron
committed
echo " " . date('l, F j, Y - H:i', $_SERVER['REQUEST_TIME']) . "\n";
Timer::start('run-tests');

Dries Buytaert
committed
echo "\n";

Dries Buytaert
committed
echo "Test summary\n";
echo "------------\n";

Dries Buytaert
committed
echo "\n";

Angie Byron
committed
/**
* Displays the assertion result summary for a single test class.
*
* @param string $class
* The test class name that was run.
* @param array $results
* The assertion results using #pass, #fail, #exception, #debug array keys.
*/
function simpletest_script_reporter_display_summary($class, $results) {
// Output all test results vertically aligned.
// Cut off the class name after 60 chars, and pad each group with 3 digits
// by default (more than 999 assertions are rare).

Lee Rowlands
committed
$output = vsprintf('%-60.60s %10s %9s %14s %12s', [

Angie Byron
committed
$class,

Alex Pott
committed
$results['#pass'] . ' passes',
!$results['#fail'] ? '' : $results['#fail'] . ' fails',

Angie Byron
committed
!$results['#exception'] ? '' : $results['#exception'] . ' exceptions',

Alex Pott
committed
!$results['#debug'] ? '' : $results['#debug'] . ' messages',

Lee Rowlands
committed
]);

Angie Byron
committed
$status = ($results['#fail'] || $results['#exception'] ? 'fail' : 'pass');
simpletest_script_print($output . "\n", simpletest_script_color_code($status));
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Display jUnit XML test results.

Dries Buytaert
committed
*/
function simpletest_script_reporter_write_xml_results(TestRunResultsStorageInterface $test_run_results_storage) {

Dries Buytaert
committed
global $args, $test_ids, $results_map;

Dries Buytaert
committed
try {
$results = simpletest_script_load_messages_by_test_id($test_run_results_storage, $test_ids);
}
catch (Exception $e) {
echo (string) $e;
exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
}

Dries Buytaert
committed
$test_class = '';

Lee Rowlands
committed
$xml_files = [];

Dries Buytaert
committed
foreach ($results as $result) {
if (isset($results_map[$result->status])) {
if ($result->test_class != $test_class) {

Alex Pott
committed
// We've moved onto a new class, so write the last classes results to a
// file:

Dries Buytaert
committed
if (isset($xml_files[$test_class])) {
file_put_contents($args['xml'] . '/' . str_replace('\\', '_', $test_class) . '.xml', $xml_files[$test_class]['doc']->saveXML());

Dries Buytaert
committed
unset($xml_files[$test_class]);
}
$test_class = $result->test_class;
if (!isset($xml_files[$test_class])) {
$doc = new DomDocument('1.0');
$root = $doc->createElement('testsuite');
$root = $doc->appendChild($root);

Lee Rowlands
committed
$xml_files[$test_class] = ['doc' => $doc, 'suite' => $root];

Dries Buytaert
committed
}
}
// For convenience:
$dom_document = &$xml_files[$test_class]['doc'];
// Create the XML element for this test case:
$case = $dom_document->createElement('testcase');
$case->setAttribute('classname', $test_class);
if (str_contains($result->function, '->')) {

Lee Rowlands
committed
[$class, $name] = explode('->', $result->function, 2);

Alex Pott
committed
}
else {
$name = $result->function;
}

Dries Buytaert
committed
$case->setAttribute('name', $name);

Alex Pott
committed
// Passes get no further attention, but failures and exceptions get to add
// more detail:

Dries Buytaert
committed
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
if ($result->status == 'fail') {
$fail = $dom_document->createElement('failure');
$fail->setAttribute('type', 'failure');
$fail->setAttribute('message', $result->message_group);
$text = $dom_document->createTextNode($result->message);
$fail->appendChild($text);
$case->appendChild($fail);
}
elseif ($result->status == 'exception') {
// In the case of an exception the $result->function may not be a class
// method so we record the full function name:
$case->setAttribute('name', $result->function);
$fail = $dom_document->createElement('error');
$fail->setAttribute('type', 'exception');
$fail->setAttribute('message', $result->message_group);
$full_message = $result->message . "\n\nline: " . $result->line . "\nfile: " . $result->file;
$text = $dom_document->createTextNode($full_message);
$fail->appendChild($text);
$case->appendChild($fail);
}
// Append the test case XML to the test suite:
$xml_files[$test_class]['suite']->appendChild($case);
}
}
// The last test case hasn't been saved to a file yet, so do that now:
if (isset($xml_files[$test_class])) {
file_put_contents($args['xml'] . '/' . str_replace('\\', '_', $test_class) . '.xml', $xml_files[$test_class]['doc']->saveXML());

Dries Buytaert
committed
unset($xml_files[$test_class]);
}
}
/**
* Stop the test timer.
*/
function simpletest_script_reporter_timer_stop() {

Dries Buytaert
committed
echo "\n";
$end = Timer::stop('run-tests');
echo "Test run duration: " . \Drupal::service('date.formatter')->formatInterval((int) ($end['time'] / 1000));

Dries Buytaert
committed
echo "\n\n";

Dries Buytaert
committed
}
/**
* Display test results.
*/
function simpletest_script_reporter_display_results(TestRunResultsStorageInterface $test_run_results_storage) {

Dries Buytaert
committed
global $args, $test_ids, $results_map;

Dries Buytaert
committed

Dries Buytaert
committed
if ($args['verbose']) {
// Report results.

Dries Buytaert
committed
echo "Detailed test results\n";
echo "---------------------\n";

Dries Buytaert
committed
try {
$results = simpletest_script_load_messages_by_test_id($test_run_results_storage, $test_ids);
}
catch (Exception $e) {
echo (string) $e;
exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
}

Dries Buytaert
committed
$test_class = '';

Dries Buytaert
committed
foreach ($results as $result) {

Dries Buytaert
committed
if (isset($results_map[$result->status])) {
if ($result->test_class != $test_class) {
// Display test class every time results are for new test class.
echo "\n\n---- $result->test_class ----\n\n\n";
$test_class = $result->test_class;

Dries Buytaert
committed

catch
committed
// Print table header.
echo "Status Group Filename Line Function \n";
echo "--------------------------------------------------------------------------------\n";

Dries Buytaert
committed
}
simpletest_script_format_result($result);
}
}

Dries Buytaert
committed
/**

Alex Pott
committed
* Format the result so that it fits within 80 characters.

Dries Buytaert
committed
*

Alex Pott
committed
* @param object $result
* The result object to format.

Dries Buytaert
committed
*/
function simpletest_script_format_result($result) {
global $args, $results_map, $color;

Dries Buytaert
committed

Dries Buytaert
committed
$summary = sprintf("%-9.9s %-10.10s %-17.17s %4.4s %-35.35s\n",
$results_map[$result->status], $result->message_group, basename($result->file), $result->line, $result->function);

Dries Buytaert
committed
simpletest_script_print($summary, simpletest_script_color_code($result->status));

Dries Buytaert
committed
$message = trim(strip_tags($result->message));
if ($args['non-html']) {
$message = Html::decodeEntities($message, ENT_QUOTES, 'UTF-8');
}
$lines = explode("\n", wordwrap($message), 76);

Dries Buytaert
committed
foreach ($lines as $line) {
echo " $line\n";
}
}

Dries Buytaert
committed

Dries Buytaert
committed
/**

Alex Pott
committed
* Print error messages so the user will notice them.

Dries Buytaert
committed
*

Alex Pott
committed
* Print error message prefixed with " ERROR: " and displayed in fail color if
* color output is enabled.
*
* @param string $message
* The message to print.

Dries Buytaert
committed
*/

Dries Buytaert
committed
function simpletest_script_print_error($message) {
simpletest_script_print(" ERROR: $message\n", SIMPLETEST_SCRIPT_COLOR_FAIL);

Dries Buytaert
committed
}

Dries Buytaert
committed

Dries Buytaert
committed
/**

Alex Pott
committed
* Print a message to the console, using a color.

Dries Buytaert
committed
*

Alex Pott
committed
* @param string $message
* The message to print.
* @param int $color_code
* The color code to use for coloring.

Dries Buytaert
committed
*/
function simpletest_script_print($message, $color_code) {
global $args;
if ($args['color']) {
echo "\033[" . $color_code . "m" . $message . "\033[0m";
}
else {
echo $message;
}
}
/**
* Get the color code associated with the specified status.
*

Alex Pott
committed
* @param string $status
* The status string to get code for. Special cases are: 'pass', 'fail', or
* 'exception'.
*
* @return int
* Color code. Returns 0 for default case.

Dries Buytaert
committed
*/
function simpletest_script_color_code($status) {
switch ($status) {
case 'pass':
return SIMPLETEST_SCRIPT_COLOR_PASS;

Alex Pott
committed

Dries Buytaert
committed
case 'fail':
return SIMPLETEST_SCRIPT_COLOR_FAIL;

Alex Pott
committed

Dries Buytaert
committed
case 'exception':
return SIMPLETEST_SCRIPT_COLOR_EXCEPTION;
}

Alex Pott
committed
// Default formatting.
return 0;

Dries Buytaert
committed
}

Angie Byron
committed
/**
* Prints alternative test names.
*
* Searches the provided array of string values for close matches based on the
* Levenshtein algorithm.
*
* @param string $string
* A string to test.
* @param array $array
* A list of strings to search.
* @param int $degree
* The matching strictness. Higher values return fewer matches. A value of
* 4 means that the function will return strings from $array if the candidate
* string in $array would be identical to $string by changing 1/4 or fewer of
* its characters.

Alex Pott
committed
*
* @see http://php.net/manual/function.levenshtein.php

Angie Byron
committed
*/
function simpletest_script_print_alternatives($string, $array, $degree = 4) {

Lee Rowlands
committed
$alternatives = [];

Angie Byron
committed
foreach ($array as $item) {
$lev = levenshtein($string, $item);
if ($lev <= strlen($item) / $degree || str_contains($string, $item)) {

Angie Byron
committed
$alternatives[] = $item;
}
}
if (!empty($alternatives)) {
simpletest_script_print(" Did you mean?\n", SIMPLETEST_SCRIPT_COLOR_FAIL);
foreach ($alternatives as $alternative) {
simpletest_script_print(" - $alternative\n", SIMPLETEST_SCRIPT_COLOR_FAIL);
}
}
}

Angie Byron
committed
/**

catch
committed
* Loads test result messages from the database.

Angie Byron
committed
*
* Messages are ordered by test class and message id.
*
* @param array $test_ids
* Array of test IDs of the messages to be loaded.
*
* @return array

catch
committed
* Array of test result messages from the database.

Angie Byron
committed
*/
function simpletest_script_load_messages_by_test_id(TestRunResultsStorageInterface $test_run_results_storage, $test_ids) {

Angie Byron
committed
global $args;

Lee Rowlands
committed
$results = [];

Angie Byron
committed
// Sqlite has a maximum number of variables per query. If required, the
// database query is split into chunks.
if (count($test_ids) > SIMPLETEST_SCRIPT_SQLITE_VARIABLE_LIMIT && !empty($args['sqlite'])) {
$test_id_chunks = array_chunk($test_ids, SIMPLETEST_SCRIPT_SQLITE_VARIABLE_LIMIT);
}
else {

Lee Rowlands
committed
$test_id_chunks = [$test_ids];

Angie Byron
committed
}
foreach ($test_id_chunks as $test_id_chunk) {
try {
$result_chunk = [];
foreach ($test_id_chunk as $test_id) {
$test_run = TestRun::get($test_run_results_storage, $test_id);
$result_chunk = array_merge($result_chunk, $test_run->getLogEntriesByTestClass());
}
}
catch (Exception $e) {
echo (string) $e;
exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION);
}

Angie Byron
committed
if ($result_chunk) {
$results = array_merge($results, $result_chunk);
}
}
return $results;
}