Newer
Older
Alex Pott
committed
<?php
/**
* @file
* Contains \Drupal\simpletest\Form\SimpletestResultsForm.
*/
namespace Drupal\simpletest\Form;
use Drupal\Component\Utility\SafeMarkup;
Alex Pott
committed
use Drupal\Core\Database\Connection;
use Drupal\Core\Form\FormBase;
Dries Buytaert
committed
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\simpletest\TestDiscovery;
Alex Pott
committed
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
Alex Pott
committed
/**
* Test results form for $test_id.
*
* Note that the UI strings are not translated because this form is also used
* from run-tests.sh.
*
* @see simpletest_script_open_browser()
* @see run-tests.sh
Alex Pott
committed
*/
class SimpletestResultsForm extends FormBase {
Alex Pott
committed
/**
* Associative array of themed result images keyed by status.
*
* @var array
*/
protected $statusImageMap;
/**
* The database connection service.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
Alex Pott
committed
return new static(
Angie Byron
committed
$container->get('database')
Alex Pott
committed
);
Alex Pott
committed
}
/**
* Constructs a \Drupal\simpletest\Form\SimpletestResultsForm object.
*
* @param \Drupal\Core\Database\Connection $database
* The database connection service.
*/
Angie Byron
committed
public function __construct(Connection $database) {
Alex Pott
committed
$this->database = $database;
Dries Buytaert
committed
}
/**
* Builds the status image map.
*/
protected static function buildStatusImageMap() {
$image_pass = array(
'#theme' => 'image',
'#uri' => 'core/misc/icons/73b355/check.svg',
'#width' => 18,
'#height' => 18,
'#alt' => 'Pass',
);
$image_fail = array(
'#theme' => 'image',
'#uri' => 'core/misc/icons/ea2800/error.svg',
'#width' => 18,
'#height' => 18,
'#alt' => 'Fail',
);
$image_exception = array(
'#theme' => 'image',
'#uri' => 'core/misc/icons/e29700/warning.svg',
'#width' => 18,
'#height' => 18,
'#alt' => 'Exception',
);
$image_debug = array(
'#theme' => 'image',
'#uri' => 'core/misc/icons/e29700/warning.svg',
'#width' => 18,
'#height' => 18,
'#alt' => 'Debug',
);
return array(
'pass' => $image_pass,
'fail' => $image_fail,
'exception' => $image_exception,
'debug' => $image_debug,
Alex Pott
committed
);
}
/**
* {@inheritdoc}
*/
Alex Pott
committed
public function getFormId() {
Alex Pott
committed
return 'simpletest_results_form';
}
/**
* {@inheritdoc}
*/
Dries Buytaert
committed
public function buildForm(array $form, FormStateInterface $form_state, $test_id = NULL) {
Jennifer Hodgdon
committed
// Make sure there are test results to display and a re-run is not being
// performed.
Alex Pott
committed
$results = array();
if (is_numeric($test_id) && !$results = $this->getResults($test_id)) {
drupal_set_message($this->t('No test results to display.'), 'error');
Angie Byron
committed
return new RedirectResponse($this->url('simpletest.test_form', array(), array('absolute' => TRUE)));
Alex Pott
committed
}
// Load all classes and include CSS.
$form['#attached']['library'][] = 'simpletest/drupal.simpletest';
// Add the results form.
$filter = static::addResultForm($form, $results, $this->getStringTranslation());
Alex Pott
committed
// Actions.
$form['#action'] = $this->url('simpletest.result_form', array('test_id' => 're-run'));
Alex Pott
committed
$form['action'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Actions'),
Alex Pott
committed
'#attributes' => array('class' => array('container-inline')),
'#weight' => -11,
);
$form['action']['filter'] = array(
'#type' => 'select',
'#title' => 'Filter',
'#options' => array(
'all' => $this->t('All (@count)', array('@count' => count($filter['pass']) + count($filter['fail']))),
'pass' => $this->t('Pass (@count)', array('@count' => count($filter['pass']))),
'fail' => $this->t('Fail (@count)', array('@count' => count($filter['fail']))),
Alex Pott
committed
),
);
$form['action']['filter']['#default_value'] = ($filter['fail'] ? 'fail' : 'all');
// Categorized test classes for to be used with selected filter value.
$form['action']['filter_pass'] = array(
'#type' => 'hidden',
'#default_value' => implode(',', $filter['pass']),
);
$form['action']['filter_fail'] = array(
'#type' => 'hidden',
'#default_value' => implode(',', $filter['fail']),
);
$form['action']['op'] = array(
'#type' => 'submit',
'#value' => $this->t('Run tests'),
Alex Pott
committed
);
$form['action']['return'] = array(
'#type' => 'link',
'#title' => $this->t('Return to list'),
'#url' => Url::fromRoute('simpletest.test_form'),
Alex Pott
committed
);
if (is_numeric($test_id)) {
simpletest_clean_results_table($test_id);
}
return $form;
}
/**
* {@inheritdoc}
*/
Dries Buytaert
committed
public function submitForm(array &$form, FormStateInterface $form_state) {
Alex Pott
committed
$pass = $form_state->getValue('filter_pass') ? explode(',', $form_state->getValue('filter_pass')) : array();
$fail = $form_state->getValue('filter_fail') ? explode(',', $form_state->getValue('filter_fail')) : array();
Alex Pott
committed
Alex Pott
committed
if ($form_state->getValue('filter') == 'all') {
Alex Pott
committed
$classes = array_merge($pass, $fail);
}
Alex Pott
committed
elseif ($form_state->getValue('filter') == 'pass') {
Alex Pott
committed
$classes = $pass;
}
else {
$classes = $fail;
}
if (!$classes) {
Alex Pott
committed
$form_state->setRedirect('simpletest.test_form');
Alex Pott
committed
return;
}
Angie Byron
committed
$form_execute = array();
$form_state_execute = new FormState();
Alex Pott
committed
foreach ($classes as $class) {
Angie Byron
committed
$form_state_execute->setValue(['tests', $class], $class);
Alex Pott
committed
}
Alex Pott
committed
// Submit the simpletest test form to rerun the tests.
Angie Byron
committed
// Under normal circumstances, a form object's submitForm() should never be
// called directly, FormBuilder::submitForm() should be called instead.
Angie Byron
committed
// However, it calls $form_state->setProgrammed(), which disables the Batch API.
$simpletest_test_form = SimpletestTestForm::create(\Drupal::getContainer());
Angie Byron
committed
$simpletest_test_form->buildForm($form_execute, $form_state_execute);
$simpletest_test_form->submitForm($form_execute, $form_state_execute);
Alex Pott
committed
if ($redirect = $form_state_execute->getRedirect()) {
$form_state->setRedirectUrl($redirect);
}
Alex Pott
committed
}
/**
* Get test results for $test_id.
*
* @param int $test_id
* The test_id to retrieve results of.
*
* @return array
* Array of results grouped by test_class.
*/
protected function getResults($test_id) {
return $this->database->select('simpletest')
Alex Pott
committed
->fields('simpletest')
->condition('test_id', $test_id)
->orderBy('test_class')
->orderBy('message_id')
->execute()
->fetchAll();
}
Alex Pott
committed
/**
* Adds the result form to a $form.
*
* This is a static method so that run-tests.sh can use it to generate a
* results page completely external to Drupal. This is why the UI strings are
* not wrapped in t().
*
* @param array $form
* The form to attach the results to.
* @param array $test_results
* The simpletest results.
*
* @return array
* A list of tests the passed and failed. The array has two keys, 'pass' and
* 'fail'. Each contains a list of test classes.
*
* @see simpletest_script_open_browser()
* @see run-tests.sh
*/
public static function addResultForm(array &$form, array $results) {
// Transform the test results to be grouped by test class.
Alex Pott
committed
$test_results = array();
foreach ($results as $result) {
if (!isset($test_results[$result->test_class])) {
$test_results[$result->test_class] = array();
}
$test_results[$result->test_class][] = $result;
}
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
$image_status_map = static::buildStatusImageMap();
// Keep track of which test cases passed or failed.
$filter = array(
'pass' => array(),
'fail' => array(),
);
// Summary result widget.
$form['result'] = array(
'#type' => 'fieldset',
'#title' => 'Results',
// Because this is used in a theme-less situation need to provide a
// default.
'#attributes' => array(),
);
$form['result']['summary'] = $summary = array(
'#theme' => 'simpletest_result_summary',
'#pass' => 0,
'#fail' => 0,
'#exception' => 0,
'#debug' => 0,
);
\Drupal::service('test_discovery')->registerTestNamespaces();
// Cycle through each test group.
$header = array(
'Message',
'Group',
'Filename',
'Line',
'Function',
array('colspan' => 2, 'data' => 'Status')
);
$form['result']['results'] = array();
foreach ($test_results as $group => $assertions) {
// Create group details with summary information.
$info = TestDiscovery::getTestInfo($group);
$form['result']['results'][$group] = array(
'#type' => 'details',
'#title' => $info['name'],
'#open' => TRUE,
'#description' => $info['description'],
);
$form['result']['results'][$group]['summary'] = $summary;
$group_summary =& $form['result']['results'][$group]['summary'];
// Create table of assertions for the group.
$rows = array();
foreach ($assertions as $assertion) {
$row = array();
$row[] = ['data' => ['#markup' => $assertion->message]];
$row[] = $assertion->message_group;
$row[] = \Drupal::service('file_system')->basename(($assertion->file));
$row[] = $assertion->line;
$row[] = $assertion->function;
$row[] = ['data' => $image_status_map[$assertion->status]];
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
$class = 'simpletest-' . $assertion->status;
if ($assertion->message_group == 'Debug') {
$class = 'simpletest-debug';
}
$rows[] = array('data' => $row, 'class' => array($class));
$group_summary['#' . $assertion->status]++;
$form['result']['summary']['#' . $assertion->status]++;
}
$form['result']['results'][$group]['table'] = array(
'#type' => 'table',
'#header' => $header,
'#rows' => $rows,
);
// Set summary information.
$group_summary['#ok'] = $group_summary['#fail'] + $group_summary['#exception'] == 0;
$form['result']['results'][$group]['#open'] = !$group_summary['#ok'];
// Store test group (class) as for use in filter.
$filter[$group_summary['#ok'] ? 'pass' : 'fail'][] = $group;
}
// Overall summary status.
$form['result']['summary']['#ok'] = $form['result']['summary']['#fail'] + $form['result']['summary']['#exception'] == 0;
return $filter;