1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-27 16:00:59 +01:00

Render unit results as Harbormaster unit messages

Summary:
Ref T8095. Same as D13377, but for unit results.

This is a bit rough and there's some duplication between this and unit results. I'll likely merge them later, but I think some of it is superficial since these iterations are still a little crude.

Test Plan: {F523499}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T8095

Differential Revision: https://secure.phabricator.com/D13378
This commit is contained in:
epriestley 2015-06-21 10:37:55 -07:00
parent e618b672d2
commit b72deb332c
5 changed files with 191 additions and 169 deletions

View file

@ -919,6 +919,7 @@ phutil_register_library_map(array(
'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php',
'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php',
'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php',
'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php',
'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php',
'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php',
'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php',
@ -4380,6 +4381,7 @@ phutil_register_library_map(array(
'HarbormasterTargetWorker' => 'HarbormasterWorker',
'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation',
'HarbormasterUIEventListener' => 'PhabricatorEventListener',
'HarbormasterUnitPropertyView' => 'AphrontView',
'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterWorker' => 'PhabricatorWorker',

View file

@ -56,7 +56,6 @@ final class DifferentialLintField
// TODO: Look for Harbormaster messages here.
if (!$lint) {
// No Harbormaster messages, so look for legacy messages and make them
// look like modern messages.
@ -71,10 +70,14 @@ final class DifferentialLintField
$target = new HarbormasterBuildTarget();
foreach ($legacy_lint as $message) {
$modern = HarbormasterBuildLintMessage::newFromDictionary(
$target,
$this->getModernLintMessageDictionary($message));
$lint[] = $modern;
try {
$modern = HarbormasterBuildLintMessage::newFromDictionary(
$target,
$this->getModernLintMessageDictionary($message));
$lint[] = $modern;
} catch (Exception $ex) {
// Ignore any poorly formatted messages.
}
}
}
}
@ -160,6 +163,13 @@ final class DifferentialLintField
}
private function getModernLintMessageDictionary(array $map) {
// Strip out `null` values to satisfy stricter typechecks.
foreach ($map as $key => $value) {
if ($value === null) {
unset($map[$key]);
}
}
// TODO: We might need to remap some stuff here?
return $map;
}

View file

@ -48,182 +48,55 @@ final class DifferentialUnitField
$diff->attachProperty($key, idx($properties, $key));
}
$ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff);
$umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
$status = $this->renderUnitStatus($diff);
$rows = array();
$unit = array();
$rows[] = array(
'style' => 'star',
'name' => $ustar,
'value' => $umsg,
'show' => true,
);
// TODO: Look for Harbormaster results here.
$excuse = $diff->getProperty('arc:unit-excuse');
if ($excuse) {
$rows[] = array(
'style' => 'excuse',
'name' => pht('Excuse'),
'value' => phutil_escape_html_newlines($excuse),
'show' => true,
);
}
if (!$unit) {
$legacy_unit = $diff->getProperty('arc:unit');
if ($legacy_unit) {
// Show the top 100 legacy unit messages.
$legacy_unit = array_slice($legacy_unit, 0, 100);
$show_limit = 10;
$hidden = array();
$udata = $diff->getProperty('arc:unit');
if ($udata) {
$sort_map = array(
ArcanistUnitTestResult::RESULT_BROKEN => 0,
ArcanistUnitTestResult::RESULT_FAIL => 1,
ArcanistUnitTestResult::RESULT_UNSOUND => 2,
ArcanistUnitTestResult::RESULT_SKIP => 3,
ArcanistUnitTestResult::RESULT_POSTPONED => 4,
ArcanistUnitTestResult::RESULT_PASS => 5,
);
foreach ($udata as $key => $test) {
$udata[$key]['sort'] = idx($sort_map, idx($test, 'result'));
}
$udata = isort($udata, 'sort');
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($this->getViewer());
$markup_objects = array();
foreach ($udata as $key => $test) {
$userdata = idx($test, 'userdata');
if ($userdata) {
if ($userdata !== false) {
$userdata = str_replace("\000", '', $userdata);
$target = new HarbormasterBuildTarget();
foreach ($legacy_unit as $message) {
try {
$modern = HarbormasterBuildUnitMessage::newFromDictionary(
$target,
$this->getModernUnitMessageDictionary($message));
$unit[] = $modern;
} catch (Exception $ex) {
// Just ignore it if legacy messages aren't formatted like
// we expect.
}
$markup_object = id(new PhabricatorMarkupOneOff())
->setContent($userdata)
->setPreserveLinebreaks(true);
$engine->addObject($markup_object, 'default');
$markup_objects[$key] = $markup_object;
}
}
$engine->process();
foreach ($udata as $key => $test) {
$result = idx($test, 'result');
$default_hide = false;
switch ($result) {
case ArcanistUnitTestResult::RESULT_POSTPONED:
case ArcanistUnitTestResult::RESULT_PASS:
$default_hide = true;
break;
}
if ($show_limit && !$default_hide) {
--$show_limit;
$show = true;
} else {
$show = false;
if (empty($hidden[$result])) {
$hidden[$result] = 0;
}
$hidden[$result]++;
}
$value = idx($test, 'name');
$namespace = idx($test, 'namespace');
if ($namespace) {
$value = $namespace.'::'.$value;
}
if (!empty($test['link'])) {
$value = phutil_tag(
'a',
array(
'href' => $test['link'],
'target' => '_blank',
),
$value);
}
$rows[] = array(
'style' => $this->getResultStyle($result),
'name' => ucwords($result),
'value' => $value,
'show' => $show,
);
if (isset($markup_objects[$key])) {
$rows[] = array(
'style' => 'details',
'value' => $engine->getOutput($markup_objects[$key], 'default'),
'show' => false,
);
if (empty($hidden['details'])) {
$hidden['details'] = 0;
}
$hidden['details']++;
}
}
}
$show_string = $this->renderShowString($hidden);
if ($unit) {
$path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename');
foreach ($path_map as $path => $id) {
$href = '#C'.$id.'NL';
$view = new DifferentialResultsTableView();
$view->setRows($rows);
$view->setShowMoreString($show_string);
// TODO: When the diff is not the right-hand-size diff, we should
// ideally adjust this URI to be absolute.
return $view->render();
}
$path_map[$path] = $href;
}
private function getResultStyle($result) {
$map = array(
ArcanistUnitTestResult::RESULT_PASS => 'green',
ArcanistUnitTestResult::RESULT_FAIL => 'red',
ArcanistUnitTestResult::RESULT_SKIP => 'blue',
ArcanistUnitTestResult::RESULT_BROKEN => 'red',
ArcanistUnitTestResult::RESULT_UNSOUND => 'yellow',
ArcanistUnitTestResult::RESULT_POSTPONED => 'blue',
$view = id(new HarbormasterUnitPropertyView())
->setPathURIMap($path_map)
->setUnitMessages($unit);
} else {
$view = null;
}
return array(
$status,
$view,
);
return idx($map, $result);
}
private function renderShowString(array $hidden) {
if (!$hidden) {
return null;
}
// Reorder hidden things by severity.
$hidden = array_select_keys(
$hidden,
array(
ArcanistUnitTestResult::RESULT_BROKEN,
ArcanistUnitTestResult::RESULT_FAIL,
ArcanistUnitTestResult::RESULT_UNSOUND,
ArcanistUnitTestResult::RESULT_SKIP,
ArcanistUnitTestResult::RESULT_POSTPONED,
ArcanistUnitTestResult::RESULT_PASS,
'details',
)) + $hidden;
$noun = array(
ArcanistUnitTestResult::RESULT_BROKEN => pht('Broken'),
ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'),
ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'),
ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'),
ArcanistUnitTestResult::RESULT_POSTPONED => pht('Postponed'),
ArcanistUnitTestResult::RESULT_PASS => pht('Passed'),
);
$show = array();
foreach ($hidden as $key => $value) {
if ($key == 'details') {
$show[] = pht('%d Detail(s)', $value);
} else {
$show[] = $value.' '.idx($noun, $key);
}
}
return pht(
'Show Full Unit Results (%s)',
implode(', ', $show));
}
public function getWarningsForDetailView() {
@ -248,4 +121,51 @@ final class DifferentialUnitField
}
private function renderUnitStatus(DifferentialDiff $diff) {
$colors = array(
DifferentialUnitStatus::UNIT_NONE => 'grey',
DifferentialUnitStatus::UNIT_OKAY => 'green',
DifferentialUnitStatus::UNIT_WARN => 'yellow',
DifferentialUnitStatus::UNIT_FAIL => 'red',
DifferentialUnitStatus::UNIT_SKIP => 'blue',
DifferentialUnitStatus::UNIT_AUTO_SKIP => 'blue',
DifferentialUnitStatus::UNIT_POSTPONED => 'blue',
);
$icon_color = idx($colors, $diff->getUnitStatus(), 'grey');
$message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
$excuse = $diff->getProperty('arc:unit-excuse');
if (strlen($excuse)) {
$excuse = array(
phutil_tag('strong', array(), pht('Excuse:')),
' ',
phutil_escape_html_newlines($excuse),
);
}
$status = id(new PHUIStatusListView())
->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color)
->setTarget($message)
->setNote($excuse));
return $status;
}
private function getModernUnitMessageDictionary(array $map) {
// Strip out `null` values to satisfy stricter typechecks.
foreach ($map as $key => $value) {
if ($value === null) {
unset($map[$key]);
}
}
// TODO: Remap more stuff here?
return $map;
}
}

View file

@ -15,7 +15,7 @@ final class HarbormasterBuildUnitMessage
public static function initializeNewUnitMessage(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildLintMessage())
return id(new HarbormasterBuildUnitMessage())
->setBuildTargetPHID($build_target->getPHID());
}

View file

@ -0,0 +1,90 @@
<?php
final class HarbormasterUnitPropertyView extends AphrontView {
private $pathURIMap;
private $unitMessages = array();
public function setPathURIMap(array $map) {
$this->pathURIMap = $map;
return $this;
}
public function setUnitMessages(array $messages) {
assert_instances_of($messages, 'HarbormasterBuildUnitMessage');
$this->unitMessages = $messages;
return $this;
}
public function render() {
$rows = array();
$any_duration = false;
foreach ($this->unitMessages as $message) {
$result = $this->renderResult($message->getResult());
$duration = $message->getDuration();
if ($duration !== null) {
$any_duration = true;
$duration = pht('%s ms', new PhutilNumber((int)(1000 * $duration)));
}
$name = $message->getName();
$namespace = $message->getNamespace();
if (strlen($namespace)) {
$name = $namespace.'::'.$name;
}
$engine = $message->getEngine();
if (strlen($engine)) {
$name = $engine.' > '.$name;
}
$rows[] = array(
$result,
$duration,
$name,
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Result'),
pht('Time'),
pht('Test'),
))
->setColumnClasses(
array(
null,
null,
'pri wide',
))
->setColumnVisibility(
array(
true,
$any_duration,
));
return $table;
}
private function renderResult($result) {
$names = array(
ArcanistUnitTestResult::RESULT_BROKEN => pht('Broken'),
ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'),
ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'),
ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'),
ArcanistUnitTestResult::RESULT_POSTPONED => pht('Postponed'),
ArcanistUnitTestResult::RESULT_PASS => pht('Passed'),
);
$result = idx($names, $result, $result);
// TODO: Add some color.
return $result;
}
}