1<?php 2 3/** 4 * @file 5 * Provides testing functionality. 6 */ 7 8use Drupal\Component\Uuid\Php; 9use Drupal\Core\Asset\AttachedAssets; 10use Drupal\Core\Asset\AttachedAssetsInterface; 11use Drupal\Core\Database\Database; 12use Drupal\Core\File\Exception\FileException; 13use Drupal\Core\File\FileSystemInterface; 14use Drupal\Core\Render\Element; 15use Drupal\Core\Routing\RouteMatchInterface; 16use Drupal\Core\StreamWrapper\PublicStream; 17use Drupal\Core\Test\JUnitConverter; 18use Drupal\Core\Test\PhpUnitTestRunner; 19use Drupal\Core\Test\TestDatabase; 20use Drupal\Core\Url; 21use Drupal\simpletest\Form\SimpletestResultsForm; 22use Drupal\simpletest\TestDiscovery; 23use PHPUnit\Framework\TestCase; 24 25/** 26 * Implements hook_help(). 27 */ 28function simpletest_help($route_name, RouteMatchInterface $route_match) { 29 switch ($route_name) { 30 case 'help.page.simpletest': 31 $output = ''; 32 $output .= '<h3>' . t('About') . '</h3>'; 33 $output .= '<p>' . t('The Testing module provides a framework for running automated tests. It can be used to verify a working state of Drupal before and after any code changes, or as a means for developers to write and execute tests for their modules. For more information, see the <a href=":simpletest">online documentation for the Testing module</a>.', [':simpletest' => 'https://www.drupal.org/documentation/modules/simpletest']) . '</p>'; 34 $output .= '<h3>' . t('Uses') . '</h3>'; 35 $output .= '<dl>'; 36 $output .= '<dt>' . t('Running tests') . '</dt>'; 37 $output .= '<dd><p>' . t('Visit the <a href=":admin-simpletest">Testing page</a> to display a list of available tests. For comprehensive testing, select <em>all</em> tests, or individually select tests for more targeted testing. Note that it might take several minutes for all tests to complete.', [':admin-simpletest' => Url::fromRoute('simpletest.test_form')->toString()]) . '</p>'; 38 $output .= '<p>' . t('After the tests run, a message will be displayed next to each test group indicating whether tests within it passed, failed, or had exceptions. A pass means that the test returned the expected results, while fail means that it did not. An exception normally indicates an error outside of the test, such as a PHP warning or notice. If there were failures or exceptions, the results will be expanded to show details, and the tests that had failures or exceptions will be indicated in red or pink rows. You can then use these results to refine your code and tests, until all tests pass.') . '</p></dd>'; 39 $output .= '</dl>'; 40 return $output; 41 42 case 'simpletest.test_form': 43 $output = t('Select the test(s) or test group(s) you would like to run, and click <em>Run tests</em>.'); 44 return $output; 45 } 46} 47 48/** 49 * Implements hook_theme(). 50 */ 51function simpletest_theme() { 52 return [ 53 'simpletest_result_summary' => [ 54 'variables' => ['label' => NULL, 'items' => [], 'pass' => 0, 'fail' => 0, 'exception' => 0, 'debug' => 0], 55 ], 56 ]; 57} 58 59/** 60 * Implements hook_js_alter(). 61 */ 62function simpletest_js_alter(&$javascript, AttachedAssetsInterface $assets) { 63 // Since SimpleTest is a special use case for the table select, stick the 64 // SimpleTest JavaScript above the table select. 65 $simpletest = drupal_get_path('module', 'simpletest') . '/simpletest.js'; 66 if (array_key_exists($simpletest, $javascript) && array_key_exists('core/misc/tableselect.js', $javascript)) { 67 $javascript[$simpletest]['weight'] = $javascript['core/misc/tableselect.js']['weight'] - 1; 68 } 69} 70 71/** 72 * Prepares variables for simpletest result summary templates. 73 * 74 * Default template: simpletest-result-summary.html.twig. 75 * 76 * @param array $variables 77 * An associative array containing: 78 * - label: An optional label to be rendered before the results. 79 * - ok: The overall group result pass or fail. 80 * - pass: The number of passes. 81 * - fail: The number of fails. 82 * - exception: The number of exceptions. 83 * - debug: The number of debug messages. 84 */ 85function template_preprocess_simpletest_result_summary(&$variables) { 86 $variables['items'] = _simpletest_build_summary_line($variables); 87} 88 89/** 90 * Formats each test result type pluralized summary. 91 * 92 * @param array $summary 93 * A summary of the test results. 94 * 95 * @return array 96 * The pluralized test summary items. 97 */ 98function _simpletest_build_summary_line($summary) { 99 $translation = \Drupal::translation(); 100 $items['pass'] = $translation->formatPlural($summary['pass'], '1 pass', '@count passes'); 101 $items['fail'] = $translation->formatPlural($summary['fail'], '1 fail', '@count fails'); 102 $items['exception'] = $translation->formatPlural($summary['exception'], '1 exception', '@count exceptions'); 103 if ($summary['debug']) { 104 $items['debug'] = $translation->formatPlural($summary['debug'], '1 debug message', '@count debug messages'); 105 } 106 return $items; 107} 108 109/** 110 * Formats test result summaries into a comma separated string for run-tests.sh. 111 * 112 * @param array $summary 113 * A summary of the test results. 114 * 115 * @return string 116 * A concatenated string of the formatted test results. 117 */ 118function _simpletest_format_summary_line($summary) { 119 $parts = _simpletest_build_summary_line($summary); 120 return implode(', ', $parts); 121} 122 123/** 124 * Runs tests. 125 * 126 * @param array[] $test_list 127 * List of tests to run. The top level is keyed by type of test, either 128 * 'simpletest' or 'phpunit'. Under that is an array of class names to run. 129 * 130 * @return string 131 * The test ID. 132 */ 133function simpletest_run_tests($test_list) { 134 // We used to separate PHPUnit and Simpletest tests for a performance 135 // optimization. In order to support backwards compatibility check if these 136 // keys are set and create a single test list. 137 // @todo https://www.drupal.org/node/2748967 Remove BC support in Drupal 9. 138 if (isset($test_list['simpletest'])) { 139 $test_list = array_merge($test_list, $test_list['simpletest']); 140 unset($test_list['simpletest']); 141 } 142 if (isset($test_list['phpunit'])) { 143 $test_list = array_merge($test_list, $test_list['phpunit']); 144 unset($test_list['phpunit']); 145 } 146 147 $test_id = \Drupal::database()->insert('simpletest_test_id') 148 ->useDefaults(['test_id']) 149 ->execute(); 150 151 // Clear out the previous verbose files. 152 try { 153 \Drupal::service('file_system')->deleteRecursive('public://simpletest/verbose'); 154 } 155 catch (FileException $e) { 156 // Ignore failed deletes. 157 } 158 159 // Get the info for the first test being run. 160 $first_test = reset($test_list); 161 $info = TestDiscovery::getTestInfo($first_test); 162 163 $batch = [ 164 'title' => t('Running tests'), 165 'operations' => [ 166 ['_simpletest_batch_operation', [$test_list, $test_id]], 167 ], 168 'finished' => '_simpletest_batch_finished', 169 'progress_message' => '', 170 'library' => ['simpletest/drupal.simpletest'], 171 'init_message' => t('Processing test @num of @max - %test.', ['%test' => $info['name'], '@num' => '1', '@max' => count($test_list)]), 172 ]; 173 batch_set($batch); 174 175 \Drupal::moduleHandler()->invokeAllDeprecated('Convert your test to a PHPUnit-based one and implement test listeners. See https://www.drupal.org/node/2934242', 'test_group_started'); 176 177 return $test_id; 178} 179 180/** 181 * Executes PHPUnit tests and returns the results of the run. 182 * 183 * @param $test_id 184 * The current test ID. 185 * @param $unescaped_test_classnames 186 * An array of test class names, including full namespaces, to be passed as 187 * a regular expression to PHPUnit's --filter option. 188 * @param int $status 189 * (optional) The exit status code of the PHPUnit process will be assigned to 190 * this variable. 191 * 192 * @return array 193 * The parsed results of PHPUnit's JUnit XML output, in the format of 194 * {simpletest}'s schema. 195 * 196 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 197 * \Drupal\Core\Test\PhpUnitTestRunner::runTests() instead. 198 * 199 * @see https://www.drupal.org/node/2948547 200 */ 201function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames, &$status = NULL) { 202 $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); 203 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::runTests() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 204 return $runner->runTests($test_id, $unescaped_test_classnames, $status); 205} 206 207/** 208 * Inserts the parsed PHPUnit results into {simpletest}. 209 * 210 * @param array[] $phpunit_results 211 * An array of test results returned from simpletest_phpunit_xml_to_rows(). 212 * 213 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 214 * \Drupal\Core\Test\TestDatabase::processPhpUnitResults() instead. 215 * 216 * @see https://www.drupal.org/node/3075252 217 */ 218function simpletest_process_phpunit_results($phpunit_results) { 219 @trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\TestDatabase::processPhpUnitResults() instead. See https://www.drupal.org/node/3075252', E_USER_DEPRECATED); 220 TestDatabase::processPhpUnitResults($phpunit_results); 221} 222 223/** 224 * Maps phpunit results to a data structure for batch messages and run-tests.sh. 225 * 226 * @param array $results 227 * The output from simpletest_run_phpunit_tests(). 228 * 229 * @return array 230 * The test result summary. A row per test class. 231 * 232 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 233 * \Drupal\Core\Test\PhpUnitTestRunner::summarizeResults() instead. 234 * 235 * @see https://www.drupal.org/node/2948547 236 */ 237function simpletest_summarize_phpunit_result($results) { 238 $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); 239 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::summarizeResults() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 240 return $runner->summarizeResults($results); 241} 242 243/** 244 * Returns the path to use for PHPUnit's --log-junit option. 245 * 246 * @param $test_id 247 * The current test ID. 248 * 249 * @return string 250 * Path to the PHPUnit XML file to use for the current $test_id. 251 * 252 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 253 * \Drupal\Core\Test\PhpUnitTestRunner::xmlLogFilepath() instead. 254 * 255 * @see https://www.drupal.org/node/2948547 256 */ 257function simpletest_phpunit_xml_filepath($test_id) { 258 $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); 259 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::xmlLogFilepath() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 260 return $runner->xmlLogFilePath($test_id); 261} 262 263/** 264 * Returns the path to core's phpunit.xml.dist configuration file. 265 * 266 * @return string 267 * The path to core's phpunit.xml.dist configuration file. 268 * 269 * @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0. PHPUnit test 270 * runners should change directory into core/ and then run the phpunit tool. 271 * See simpletest_phpunit_run_command() for an example. 272 * 273 * @see simpletest_phpunit_run_command() 274 */ 275function simpletest_phpunit_configuration_filepath() { 276 @trigger_error('The ' . __FUNCTION__ . ' function is deprecated since version 8.4.x and will be removed in 9.0.0.', E_USER_DEPRECATED); 277 return \Drupal::root() . '/core/phpunit.xml.dist'; 278} 279 280/** 281 * Executes the PHPUnit command. 282 * 283 * @param array $unescaped_test_classnames 284 * An array of test class names, including full namespaces, to be passed as 285 * a regular expression to PHPUnit's --filter option. 286 * @param string $phpunit_file 287 * A filepath to use for PHPUnit's --log-junit option. 288 * @param int $status 289 * (optional) The exit status code of the PHPUnit process will be assigned to 290 * this variable. 291 * @param string $output 292 * (optional) The output by running the phpunit command. 293 * 294 * @return string 295 * The results as returned by exec(). 296 * 297 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 298 * \Drupal\Core\Test\PhpUnitTestRunner::runCommand() instead. 299 * 300 * @see https://www.drupal.org/node/2948547 301 */ 302function simpletest_phpunit_run_command(array $unescaped_test_classnames, $phpunit_file, &$status = NULL, &$output = NULL) { 303 $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); 304 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::runCommand() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 305 return $runner->runCommand($unescaped_test_classnames, $phpunit_file, $status, $output); 306} 307 308/** 309 * Returns the command to run PHPUnit. 310 * 311 * @return string 312 * The command that can be run through exec(). 313 * 314 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 315 * \Drupal\Core\Test\PhpUnitTestRunner::phpUnitCommand() instead. 316 * 317 * @see https://www.drupal.org/node/2948547 318 */ 319function simpletest_phpunit_command() { 320 $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); 321 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\PhpUnitTestRunner::phpUnitCommand() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 322 return $runner->phpUnitCommand(); 323} 324 325/** 326 * Implements callback_batch_operation(). 327 */ 328function _simpletest_batch_operation($test_list_init, $test_id, &$context) { 329 \Drupal::service('test_discovery')->registerTestNamespaces(); 330 // Get working values. 331 if (!isset($context['sandbox']['max'])) { 332 // First iteration: initialize working values. 333 $test_list = $test_list_init; 334 $context['sandbox']['max'] = count($test_list); 335 $test_results = ['#pass' => 0, '#fail' => 0, '#exception' => 0, '#debug' => 0]; 336 } 337 else { 338 // Nth iteration: get the current values where we last stored them. 339 $test_list = $context['sandbox']['tests']; 340 $test_results = $context['sandbox']['test_results']; 341 } 342 $max = $context['sandbox']['max']; 343 344 // Perform the next test. 345 $test_class = array_shift($test_list); 346 if (is_subclass_of($test_class, TestCase::class)) { 347 $runner = PhpUnitTestRunner::create(\Drupal::getContainer()); 348 $phpunit_results = $runner->runTests($test_id, [$test_class]); 349 TestDatabase::processPhpUnitResults($phpunit_results); 350 $test_results[$test_class] = simpletest_summarize_phpunit_result($phpunit_results)[$test_class]; 351 } 352 else { 353 $test = new $test_class($test_id); 354 $test->run(); 355 \Drupal::moduleHandler()->invokeAllDeprecated('Convert your test to a PHPUnit-based one and implement test listeners. See https://www.drupal.org/node/2934242', 'test_finished', [$test->results]); 356 $test_results[$test_class] = $test->results; 357 } 358 $size = count($test_list); 359 $info = TestDiscovery::getTestInfo($test_class); 360 361 // Gather results and compose the report. 362 foreach ($test_results[$test_class] as $key => $value) { 363 $test_results[$key] += $value; 364 } 365 $test_results[$test_class]['#name'] = $info['name']; 366 $items = []; 367 foreach (Element::children($test_results) as $class) { 368 $class_test_result = $test_results[$class] + [ 369 '#theme' => 'simpletest_result_summary', 370 '#label' => t($test_results[$class]['#name'] . ':'), 371 ]; 372 array_unshift($items, \Drupal::service('renderer')->render($class_test_result)); 373 } 374 $context['message'] = t('Processed test @num of @max - %test.', ['%test' => $info['name'], '@num' => $max - $size, '@max' => $max]); 375 $overall_results = $test_results + [ 376 '#theme' => 'simpletest_result_summary', 377 '#label' => t('Overall results:'), 378 ]; 379 $context['message'] .= \Drupal::service('renderer')->render($overall_results); 380 381 $item_list = [ 382 '#theme' => 'item_list', 383 '#items' => $items, 384 ]; 385 $context['message'] .= \Drupal::service('renderer')->render($item_list); 386 387 // Save working values for the next iteration. 388 $context['sandbox']['tests'] = $test_list; 389 $context['sandbox']['test_results'] = $test_results; 390 // The test_id is the only thing we need to save for the report page. 391 $context['results']['test_id'] = $test_id; 392 393 // Multistep processing: report progress. 394 $context['finished'] = 1 - $size / $max; 395} 396 397/** 398 * Implements callback_batch_finished(). 399 */ 400function _simpletest_batch_finished($success, $results, $operations, $elapsed) { 401 if ($success) { 402 \Drupal::messenger()->addStatus(t('The test run finished in @elapsed.', ['@elapsed' => $elapsed])); 403 } 404 else { 405 // Use the test_id passed as a parameter to _simpletest_batch_operation(). 406 $test_id = $operations[0][1][1]; 407 408 // Retrieve the last database prefix used for testing and the last test 409 // class that was run from. Use the information to read the lgo file 410 // in case any fatal errors caused the test to crash. 411 $last_test = TestDatabase::lastTestGet($test_id); 412 (new TestDatabase($last_test['last_prefix']))->logRead($test_id, $last_test['test_class']); 413 414 \Drupal::messenger()->addError(t('The test run did not successfully finish.')); 415 \Drupal::messenger()->addWarning(t('Use the <em>Clean environment</em> button to clean-up temporary files and tables.')); 416 } 417 \Drupal::moduleHandler()->invokeAllDeprecated('Convert your test to a PHPUnit-based one and implement test listeners. See https://www.drupal.org/node/2934242', 'test_group_finished'); 418} 419 420/** 421 * Get information about the last test that ran given a test ID. 422 * 423 * @param $test_id 424 * The test ID to get the last test from. 425 * 426 * @return array 427 * Array containing the last database prefix used and the last test class 428 * that ran. 429 * 430 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 431 * \Drupal\Core\Test\TestDatabase::lastTestGet() instead. 432 * 433 * @see https://www.drupal.org/node/3075252 434 */ 435function simpletest_last_test_get($test_id) { 436 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\TestDatabase::lastTestGet() instead. See https://www.drupal.org/node/3075252', E_USER_DEPRECATED); 437 return array_values(TestDatabase::lastTestGet($test_id)); 438} 439 440/** 441 * Reads the error log and reports any errors as assertion failures. 442 * 443 * The errors in the log should only be fatal errors since any other errors 444 * will have been recorded by the error handler. 445 * 446 * @param $test_id 447 * The test ID to which the log relates. 448 * @param $database_prefix 449 * The database prefix to which the log relates. 450 * @param $test_class 451 * The test class to which the log relates. 452 * 453 * @return bool 454 * Whether any fatal errors were found. 455 * 456 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 457 * \Drupal\Core\Test\TestDatabase::logRead() instead. 458 * 459 * @see https://www.drupal.org/node/3075252 460 */ 461function simpletest_log_read($test_id, $database_prefix, $test_class) { 462 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\TestDatabase::logRead() instead. See https://www.drupal.org/node/3075252', E_USER_DEPRECATED); 463 $test_db = new TestDatabase($database_prefix); 464 return $test_db->logRead($test_id, $test_class); 465} 466 467/** 468 * Store an assertion from outside the testing context. 469 * 470 * This is useful for inserting assertions that can only be recorded after 471 * the test case has been destroyed, such as PHP fatal errors. The caller 472 * information is not automatically gathered since the caller is most likely 473 * inserting the assertion on behalf of other code. In all other respects 474 * the method behaves just like \Drupal\simpletest\TestBase::assert() in terms 475 * of storing the assertion. 476 * 477 * @param string $test_id 478 * The test ID to which the assertion relates. 479 * @param string $test_class 480 * The test class to store an assertion for. 481 * @param bool|string $status 482 * A boolean or a string of 'pass' or 'fail'. TRUE means 'pass'. 483 * @param string $message 484 * The assertion message. 485 * @param string $group 486 * The assertion message group. 487 * @param array $caller 488 * The an array containing the keys 'file' and 'line' that represent the file 489 * and line number of that file that is responsible for the assertion. 490 * 491 * @return 492 * Message ID of the stored assertion. 493 * 494 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 495 * \Drupal\Core\Test\TestDatabase::insertAssert() instead. 496 * 497 * @see https://www.drupal.org/node/3075252 498 */ 499function simpletest_insert_assert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = []) { 500 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\TestDatabase::insertAssert() instead. See https://www.drupal.org/node/3075252', E_USER_DEPRECATED); 501 TestDatabase::insertAssert($test_id, $test_class, $status, $message, $group, $caller); 502} 503 504/** 505 * Gets a list of all of the tests provided by the system. 506 * 507 * The list of test classes is loaded by searching the designated directory for 508 * each module for files matching the PSR-4 standard. Once loaded the test list 509 * is cached and stored in a static variable. 510 * 511 * @param string $extension 512 * (optional) The name of an extension to limit discovery to; e.g., 'node'. 513 * @param string[] $types 514 * An array of included test types. 515 * 516 * @return array[] 517 * An array of tests keyed with the groups, and then keyed by test classes. 518 * For example: 519 * @code 520 * $groups['Block'] => array( 521 * 'BlockTestCase' => array( 522 * 'name' => 'Block functionality', 523 * 'description' => 'Add, edit and delete custom block.', 524 * 'group' => 'Block', 525 * ), 526 * ); 527 * @endcode 528 * 529 * @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. Use 530 * \Drupal::service('test_discovery')->getTestClasses($extension, $types) 531 * instead. 532 */ 533function simpletest_test_get_all($extension = NULL, array $types = []) { 534 @trigger_error('The ' . __FUNCTION__ . ' function is deprecated in version 8.3.x and will be removed in 9.0.0. Use \Drupal::service(\'test_discovery\')->getTestClasses($extension, $types) instead.', E_USER_DEPRECATED); 535 return \Drupal::service('test_discovery')->getTestClasses($extension, $types); 536} 537 538/** 539 * Registers test namespaces of all extensions and core test classes. 540 * 541 * @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. Use 542 * \Drupal::service('test_discovery')->registerTestNamespaces() instead. 543 */ 544function simpletest_classloader_register() { 545 @trigger_error('The ' . __FUNCTION__ . ' function is deprecated in version 8.3.x and will be removed in 9.0.0. Use \Drupal::service(\'test_discovery\')->registerTestNamespaces() instead.', E_USER_DEPRECATED); 546 \Drupal::service('test_discovery')->registerTestNamespaces(); 547} 548 549/** 550 * Generates a test file. 551 * 552 * @param string $filename 553 * The name of the file, including the path. The suffix '.txt' is appended to 554 * the supplied file name and the file is put into the public:// files 555 * directory. 556 * @param int $width 557 * The number of characters on one line. 558 * @param int $lines 559 * The number of lines in the file. 560 * @param string $type 561 * (optional) The type, one of: 562 * - text: The generated file contains random ASCII characters. 563 * - binary: The generated file contains random characters whose codes are in 564 * the range of 0 to 31. 565 * - binary-text: The generated file contains random sequence of '0' and '1' 566 * values. 567 * 568 * @return string 569 * The name of the file, including the path. 570 * 571 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 572 * \Drupal\Tests\TestFileCreationTrait::generateFile() instead. 573 * 574 * @see https://www.drupal.org/node/3077768 575 */ 576function simpletest_generate_file($filename, $width, $lines, $type = 'binary-text') { 577 @trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Tests\TestFileCreationTrait::generateFile() instead. See https://www.drupal.org/node/3077768', E_USER_DEPRECATED); 578 $text = ''; 579 for ($i = 0; $i < $lines; $i++) { 580 // Generate $width - 1 characters to leave space for the "\n" character. 581 for ($j = 0; $j < $width - 1; $j++) { 582 switch ($type) { 583 case 'text': 584 $text .= chr(rand(32, 126)); 585 break; 586 case 'binary': 587 $text .= chr(rand(0, 31)); 588 break; 589 case 'binary-text': 590 default: 591 $text .= rand(0, 1); 592 break; 593 } 594 } 595 $text .= "\n"; 596 } 597 598 // Create filename. 599 file_put_contents('public://' . $filename . '.txt', $text); 600 return $filename; 601} 602 603/** 604 * Removes all temporary database tables and directories. 605 * 606 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the 607 * environment_cleaner service and call its cleanEnvironment() method, or use 608 * \Drupal\Core\Test\EnvironmentCleaner::cleanEnvironment() instead. 609 * 610 * @see https://www.drupal.org/node/3076634 611 */ 612function simpletest_clean_environment() { 613 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the environment_cleaner service and call its cleanEnvironment() method, or use \Drupal\Core\Test\EnvironmentCleaner::cleanEnvironment() instead.. See https://www.drupal.org/node/3076634', E_USER_DEPRECATED); 614 /* @var $cleaner \Drupal\simpletest\EnvironmentCleanerService */ 615 $cleaner = \Drupal::service('environment_cleaner'); 616 $cleaner->cleanEnvironment(); 617} 618 619/** 620 * Removes prefixed tables from the database from crashed tests. 621 * 622 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the 623 * environment_cleaner service and call its cleanDatabase() method, or use 624 * \Drupal\Core\Test\EnvironmentCleaner::cleanDatabase() instead. 625 * 626 * @see https://www.drupal.org/node/3076634 627 */ 628function simpletest_clean_database() { 629 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the environment_cleaner service and call its cleanDatabase() method, or use \Drupal\Core\Test\EnvironmentCleaner::cleanDatabase() instead. See https://www.drupal.org/node/3076634', E_USER_DEPRECATED); 630 /* @var $cleaner \Drupal\simpletest\EnvironmentCleanerService */ 631 $cleaner = \Drupal::service('environment_cleaner'); 632 $cleaner->cleanDatabase(); 633} 634 635/** 636 * Finds all leftover temporary directories and removes them. 637 * 638 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the 639 * environment_cleaner service and call its cleanTemporaryDirectories() 640 * method, or use 641 * \Drupal\Core\Test\EnvironmentCleaner::cleanTemporaryDirectories() instead. 642 * 643 * @see https://www.drupal.org/node/3076634 644 */ 645function simpletest_clean_temporary_directories() { 646 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the environment_cleaner service and call its cleanTemporaryDirectories() method, or use \Drupal\Core\Test\EnvironmentCleaner::cleanTemporaryDirectories() instead. See https://www.drupal.org/node/3076634', E_USER_DEPRECATED); 647 /* @var $cleaner \Drupal\simpletest\EnvironmentCleanerService */ 648 $cleaner = \Drupal::service('environment_cleaner'); 649 $cleaner->cleanTemporaryDirectories(); 650} 651 652/** 653 * Clears the test result tables. 654 * 655 * @param $test_id 656 * Test ID to remove results for, or NULL to remove all results. 657 * 658 * @return int 659 * The number of results that were removed. 660 * 661 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the 662 * environment_cleaner service and call its cleanResultsTable() method, or use 663 * \Drupal\Core\Test\EnvironmentCleaner::cleanResultsTable() instead. 664 * 665 * @see https://www.drupal.org/node/3076634 666 */ 667function simpletest_clean_results_table($test_id = NULL) { 668 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Access the environment_cleaner service and call its cleanResultsTable() method, or use \Drupal\Core\Test\EnvironmentCleaner::cleanResultsTable() instead. See https://www.drupal.org/node/3076634', E_USER_DEPRECATED); 669 $count = 0; 670 if (\Drupal::config('simpletest.settings')->get('clear_results')) { 671 /* @var $cleaner \Drupal\simpletest\EnvironmentCleanerService */ 672 $cleaner = \Drupal::service('environment_cleaner'); 673 $count = $cleaner->cleanResultsTable($test_id); 674 } 675 return $count; 676} 677 678/** 679 * Converts PHPUnit's JUnit XML output to an array. 680 * 681 * @param $test_id 682 * The current test ID. 683 * @param $phpunit_xml_file 684 * Path to the PHPUnit XML file. 685 * 686 * @return array[]|null 687 * The results as array of rows in a format that can be inserted into 688 * {simpletest}. If the phpunit_xml_file does not have any contents then the 689 * function will return NULL. 690 * 691 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 692 * \Drupal\Core\Test\JUnitConverter::xmlToRows() instead. 693 * 694 * @see https://www.drupal.org/node/2948547 695 */ 696function simpletest_phpunit_xml_to_rows($test_id, $phpunit_xml_file) { 697 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::xmlToRows() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 698 return JUnitConverter::xmlToRows($test_id, $phpunit_xml_file) ?: NULL; 699} 700 701/** 702 * Finds all test cases recursively from a test suite list. 703 * 704 * @param \SimpleXMLElement $element 705 * The PHPUnit xml to search for test cases. 706 * @param \SimpleXMLElement $parent 707 * (Optional) The parent of the current element. Defaults to NULL. 708 * 709 * @return array 710 * A list of all test cases. 711 * 712 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 713 * \Drupal\Core\Test\JUnitConverter::findTestCases() instead. 714 * 715 * @see https://www.drupal.org/node/2948547 716 */ 717function simpletest_phpunit_find_testcases(\SimpleXMLElement $element, \SimpleXMLElement $parent = NULL) { 718 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::findTestCases() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 719 return JUnitConverter::findTestCases($element, $parent); 720} 721 722/** 723 * Converts a PHPUnit test case result to a {simpletest} result row. 724 * 725 * @param int $test_id 726 * The current test ID. 727 * @param \SimpleXMLElement $test_case 728 * The PHPUnit test case represented as XML element. 729 * 730 * @return array 731 * An array containing the {simpletest} result row. 732 * 733 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use 734 * \Drupal\Core\Test\JUnitConverter::convertTestCaseToSimpletestRow() instead. 735 * 736 * @see https://www.drupal.org/node/2948547 737 */ 738function simpletest_phpunit_testcase_to_row($test_id, \SimpleXMLElement $test_case) { 739 @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Test\JUnitConverter::convertTestCaseToSimpletestRow() instead. See https://www.drupal.org/node/2948547', E_USER_DEPRECATED); 740 return JUnitConverter::convertTestCaseToSimpletestRow($test_id, $test_case); 741} 742 743/** 744 * Display test results from run-tests.sh in a browser. 745 * 746 * @internal 747 * This function is only used by run-tests.sh 748 * 749 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. This function 750 * supports the --browser option in this script. Use the --verbose option 751 * instead. 752 * 753 * @see https://www.drupal.org/node/3083549 754 */ 755function _simpletest_run_tests_script_open_browser() { 756 global $test_ids; 757 758 try { 759 $connection = Database::getConnection('default', 'test-runner'); 760 $results = $connection->select('simpletest') 761 ->fields('simpletest') 762 ->condition('test_id', $test_ids, 'IN') 763 ->orderBy('test_class') 764 ->orderBy('message_id') 765 ->execute() 766 ->fetchAll(); 767 } 768 catch (Exception $e) { 769 echo (string) $e; 770 exit(SIMPLETEST_SCRIPT_EXIT_EXCEPTION); 771 } 772 773 // Get the results form. 774 $form = []; 775 SimpletestResultsForm::addResultForm($form, $results); 776 777 // Get the assets to make the details element collapsible and theme the result 778 // form. 779 $assets = new AttachedAssets(); 780 $assets->setLibraries([ 781 'core/drupal.collapse', 782 'system/admin', 783 'simpletest/drupal.simpletest', 784 ]); 785 $resolver = \Drupal::service('asset.resolver'); 786 list($js_assets_header, $js_assets_footer) = $resolver->getJsAssets($assets, FALSE); 787 $js_collection_renderer = \Drupal::service('asset.js.collection_renderer'); 788 $js_assets_header = $js_collection_renderer->render($js_assets_header); 789 $js_assets_footer = $js_collection_renderer->render($js_assets_footer); 790 $css_assets = \Drupal::service('asset.css.collection_renderer')->render($resolver->getCssAssets($assets, FALSE)); 791 792 // Make the html page to write to disk. 793 $render_service = \Drupal::service('renderer'); 794 $html = '<head>' . $render_service->renderPlain($js_assets_header) . $render_service->renderPlain($css_assets) . '</head><body>' . $render_service->renderPlain($form) . $render_service->renderPlain($js_assets_footer) . '</body>'; 795 796 // Ensure we have assets verbose directory - tests with no verbose output will 797 // not have created one. 798 $directory = PublicStream::basePath() . '/simpletest/verbose'; 799 \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); 800 $php = new Php(); 801 $uuid = $php->generate(); 802 $filename = $directory . '/results-' . $uuid . '.html'; 803 $base_url = getenv('SIMPLETEST_BASE_URL'); 804 if (empty($base_url)) { 805 simpletest_script_print_error("--browser needs argument --url."); 806 } 807 $url = $base_url . '/' . PublicStream::basePath() . '/simpletest/verbose/results-' . $uuid . '.html'; 808 file_put_contents($filename, $html); 809 810 // See if we can find an OS helper to open URLs in default browser. 811 $browser = FALSE; 812 if (shell_exec('which xdg-open')) { 813 $browser = 'xdg-open'; 814 } 815 elseif (shell_exec('which open')) { 816 $browser = 'open'; 817 } 818 elseif (substr(PHP_OS, 0, 3) == 'WIN') { 819 $browser = 'start'; 820 } 821 822 if ($browser) { 823 shell_exec($browser . ' ' . escapeshellarg($url)); 824 } 825 else { 826 // Can't find assets valid browser. 827 print 'Open file://' . realpath($filename) . ' in your browser to see the verbose output.'; 828 } 829} 830