Page MenuHomeSealhub

No OneTemporary

diff --git a/src/unit/engine/CSharpToolsTestEngine.php b/src/unit/engine/CSharpToolsTestEngine.php
index 03369798..34f8e511 100644
--- a/src/unit/engine/CSharpToolsTestEngine.php
+++ b/src/unit/engine/CSharpToolsTestEngine.php
@@ -1,287 +1,290 @@
<?php
/**
* Uses cscover (http://github.com/hach-que/cstools) to report code coverage.
*
* This engine inherits from `XUnitTestEngine`, where xUnit is used to actually
* run the unit tests and this class provides a thin layer on top to collect
* code coverage data with a third-party tool.
*
* @group unitrun
*/
final class CSharpToolsTestEngine extends XUnitTestEngine {
private $cscoverHintPath;
private $coverEngine;
private $cachedResults;
private $matchRegex;
private $excludedFiles;
/**
* Overridden version of `loadEnvironment` to support a different set
* of configuration values and to pull in the cstools config for
* code coverage.
*/
protected function loadEnvironment(
$config_item = 'unit.csharp.xunit.binary') {
$config = $this->getConfigurationManager();
$this->xunitHintPath =
$config->getConfigFromAnySource('unit.csharp.xunit.binary');
$this->cscoverHintPath =
$config->getConfigFromAnySource('unit.csharp.cscover.binary');
$this->matchRegex =
$config->getConfigFromAnySource('unit.csharp.coverage.match');
$this->excludedFiles =
$config->getConfigFromAnySource('unit.csharp.coverage.excluded');
parent::loadEnvironment($config_item);
if ($this->getEnableCoverage() === false) {
return;
}
// Determine coverage path.
if ($this->cscoverHintPath === null) {
throw new Exception(
"Unable to locate cscover. Configure it with ".
"the `unit.csharp.coverage.binary' option in .arcconfig");
}
$cscover = $this->projectRoot."/".$this->cscoverHintPath;
if (file_exists($cscover)) {
$this->coverEngine = Filesystem::resolvePath($cscover);
} else {
throw new Exception(
"Unable to locate cscover coverage runner ".
"(have you built yet?)");
}
}
/**
* Returns whether the specified assembly should be instrumented for
* code coverage reporting. Checks the excluded file list and the
* matching regex if they are configured.
*
* @return boolean Whether the assembly should be instrumented.
*/
private function assemblyShouldBeInstrumented($file) {
if ($this->excludedFiles !== null) {
if (array_key_exists((string)$file, $this->excludedFiles)) {
return false;
}
}
if ($this->matchRegex !== null) {
if (preg_match($this->matchRegex, $file) === 1) {
return true;
} else {
return false;
}
}
return true;
}
/**
* Overridden version of `buildTestFuture` so that the unit test can be run
* via `cscover`, which instruments assemblies and reports on code coverage.
*
* @param string Name of the test assembly.
* @return array The future, output filename and coverage filename
* stored in an array.
*/
protected function buildTestFuture($test_assembly) {
if ($this->getEnableCoverage() === false) {
return parent::buildTestFuture($test_assembly);
}
// FIXME: Can't use TempFile here as xUnit doesn't like
// UNIX-style full paths. It sees the leading / as the
// start of an option flag, even when quoted.
$xunit_temp = $test_assembly.".results.xml";
if (file_exists($xunit_temp)) {
unlink($xunit_temp);
}
$cover_temp = new TempFile();
$cover_temp->setPreserveFile(true);
$xunit_cmd = $this->runtimeEngine;
$xunit_args = null;
if ($xunit_cmd === "") {
$xunit_cmd = $this->testEngine;
$xunit_args = csprintf(
"%s /xml %s /silent",
$test_assembly.".dll",
$xunit_temp);
} else {
$xunit_args = csprintf(
"%s %s /xml %s /silent",
$this->testEngine,
$test_assembly.".dll",
$xunit_temp);
}
$assembly_dir = $test_assembly."/bin/Debug/";
$assemblies_to_instrument = array();
foreach (Filesystem::listDirectory($assembly_dir) as $file) {
if (substr($file, -4) == ".dll" || substr($file, -4) == ".exe") {
if ($this->assemblyShouldBeInstrumented($file)) {
$assemblies_to_instrument[] = $assembly_dir.$file;
}
}
}
+ if (count($assemblies_to_instrument) === 0) {
+ return parent::buildTestFuture($test_assembly);
+ }
$future = new ExecFuture(
"%C -o %s -c %s -a %s -w %s %Ls",
trim($this->runtimeEngine." ".$this->coverEngine),
$cover_temp,
$xunit_cmd,
$xunit_args,
$assembly_dir,
$assemblies_to_instrument);
$future->setCWD(Filesystem::resolvePath($this->projectRoot));
return array(
$future,
$this->projectRoot."/".$assembly_dir.$xunit_temp,
$cover_temp);
}
/**
* Returns coverage results for the unit tests.
*
* @param string The name of the coverage file if one was provided by
* `buildTestFuture`.
* @return array Code coverage results, or null.
*/
protected function parseCoverageResult($cover_file) {
if ($this->getEnableCoverage() === false) {
return parent::parseCoverageResult($cover_file);
}
return $this->readCoverage($cover_file);
}
/**
* Retrieves the cached results for a coverage result file. The coverage
* result file is XML and can be large depending on what has been instrumented
* so we cache it in case it's requested again.
*
* @param string The name of the coverage file.
* @return array Code coverage results, or null if not cached.
*/
private function getCachedResultsIfPossible($cover_file) {
if ($this->cachedResults == null) {
$this->cachedResults = array();
}
if (array_key_exists((string)$cover_file, $this->cachedResults)) {
return $this->cachedResults[(string)$cover_file];
}
return null;
}
/**
* Stores the code coverage results in the cache.
*
* @param string The name of the coverage file.
* @param array The results to cache.
*/
private function addCachedResults($cover_file, array $results) {
if ($this->cachedResults == null) {
$this->cachedResults = array();
}
$this->cachedResults[(string)$cover_file] = $results;
}
/**
* Processes a set of XML tags as code coverage results. We parse
* the `instrumented` and `executed` tags with this method so that
* we can access the data multiple times without a performance hit.
*
* @param array The array of XML tags to parse.
* @return array A PHP array containing the data.
*/
private function processTags($tags) {
$results = array();
foreach ($tags as $tag) {
$results[] = array(
"file" => $tag->getAttribute("file"),
"start" => $tag->getAttribute("start"),
"end" => $tag->getAttribute("end"));
}
return $results;
}
/**
* Reads the code coverage results from the cscover results file.
*
* @param string The path to the code coverage file.
* @return array The code coverage results.
*/
public function readCoverage($cover_file) {
$cached = $this->getCachedResultsIfPossible($cover_file);
if ($cached !== null) {
return $cached;
}
$coverage_dom = new DOMDocument();
$coverage_dom->loadXML(Filesystem::readFile($cover_file));
$modified = $this->getPaths();
$files = array();
$reports = array();
$instrumented = array();
$executed = array();
$instrumented = $this->processTags(
$coverage_dom->getElementsByTagName("instrumented"));
$executed = $this->processTags(
$coverage_dom->getElementsByTagName("executed"));
foreach ($instrumented as $instrument) {
$absolute_file = $instrument["file"];
$relative_file = substr($absolute_file, strlen($this->projectRoot) + 1);
if (!in_array($relative_file, $files)) {
$files[] = $relative_file;
}
}
foreach ($files as $file) {
$absolute_file = $this->projectRoot."/".$file;
// get total line count in file
$line_count = count(file($absolute_file));
$coverage = array();
for ($i = 0; $i < $line_count; $i++) {
$coverage[$i] = 'N';
}
foreach ($instrumented as $instrument) {
if ($instrument["file"] !== $absolute_file) {
continue;
}
for (
$i = $instrument["start"];
$i <= $instrument["end"];
$i++) {
$coverage[$i - 1] = 'U';
}
}
foreach ($executed as $execute) {
if ($execute["file"] !== $absolute_file) {
continue;
}
for (
$i = $execute["start"];
$i <= $execute["end"];
$i++) {
$coverage[$i - 1] = 'C';
}
}
$reports[$file] = implode($coverage);
}
$this->addCachedResults($cover_file, $reports);
return $reports;
}
}
diff --git a/src/unit/engine/XUnitTestEngine.php b/src/unit/engine/XUnitTestEngine.php
index e2cd105a..b3920fad 100644
--- a/src/unit/engine/XUnitTestEngine.php
+++ b/src/unit/engine/XUnitTestEngine.php
@@ -1,452 +1,457 @@
<?php
/**
* Uses xUnit (http://xunit.codeplex.com/) to test C# code.
*
* Assumes that when modifying a file with a path like `SomeAssembly/MyFile.cs`,
* that the test assembly that verifies the functionality of `SomeAssembly` is
* located at `SomeAssembly.Tests`.
*
* @group unitrun
* @concrete-extensible
*/
class XUnitTestEngine extends ArcanistBaseUnitTestEngine {
protected $runtimeEngine;
protected $buildEngine;
protected $testEngine;
protected $projectRoot;
protected $xunitHintPath;
+ protected $discoveryRules;
/**
* This test engine supports running all tests.
*/
protected function supportsRunAllTests() {
return true;
}
/**
* Determines what executables and test paths to use. Between platforms
* this also changes whether the test engine is run under .NET or Mono. It
* also ensures that all of the required binaries are available for the tests
* to run successfully.
*
* @return void
*/
protected function loadEnvironment($config_item = 'unit.xunit.binary') {
$this->projectRoot = $this->getWorkingCopy()->getProjectRoot();
// Determine build engine.
if (Filesystem::binaryExists("msbuild")) {
$this->buildEngine = "msbuild";
} else if (Filesystem::binaryExists("xbuild")) {
$this->buildEngine = "xbuild";
} else {
throw new Exception("Unable to find msbuild or xbuild in PATH!");
}
// Determine runtime engine (.NET or Mono).
if (phutil_is_windows()) {
$this->runtimeEngine = "";
} else if (Filesystem::binaryExists("mono")) {
$this->runtimeEngine = Filesystem::resolveBinary("mono");
} else {
throw new Exception("Unable to find Mono and you are not on Windows!");
}
+ // Read the discovery rules.
+ $this->discoveryRules =
+ $this->getConfigurationManager()->getConfigFromAnySource(
+ 'unit.csharp.discovery');
+ if ($this->discoveryRules === null) {
+ throw new Exception(
+ "You must configure discovery rules to map C# files ".
+ "back to test projects (`unit.csharp.discovery` in .arcconfig).");
+ }
+
// Determine xUnit test runner path.
if ($this->xunitHintPath === null) {
$this->xunitHintPath =
$this->getConfigurationManager()->getConfigFromAnySource(
- 'unit.xunit.binary');
- }
- if ($this->xunitHintPath === null) {
+ 'unit.csharp.xunit.binary');
}
- $xunit = $this->projectRoot."/".$this->xunitHintPath;
- if (file_exists($xunit)) {
+ $xunit = $this->projectRoot.DIRECTORY_SEPARATOR.$this->xunitHintPath;
+ if (file_exists($xunit) && $this->xunitHintPath !== null) {
$this->testEngine = Filesystem::resolvePath($xunit);
} else if (Filesystem::binaryExists("xunit.console.clr4.exe")) {
$this->testEngine = "xunit.console.clr4.exe";
} else {
throw new Exception(
"Unable to locate xUnit console runner. Configure ".
"it with the `$config_item' option in .arcconfig");
}
}
- /**
- * Returns all available tests and related projects. Recurses into
- * Protobuild submodules if they are present.
- *
- * @return array Mappings of test project to project being tested.
- */
- public function getAllAvailableTestsAndRelatedProjects($path = null) {
- if ($path == null) {
- $path = $this->projectRoot;
- }
- $entries = Filesystem::listDirectory($path);
- $mappings = array();
- foreach ($entries as $entry) {
- if (substr($entry, -6) === ".Tests") {
- if (is_dir($path."/".$entry)) {
- $mappings[$path."/".$entry] = $path."/".
- substr($entry, 0, strlen($entry) - 6);
- }
- } elseif (is_dir($path."/".$entry."/Build")) {
- if (file_exists($path."/".$entry."/Build/Module.xml")) {
- // The entry is a Protobuild submodule, which we should
- // also recurse into.
- $submappings =
- $this->getAllAvailableTestsAndRelatedProjects($path."/".$entry);
- foreach ($submappings as $key => $value) {
- $mappings[$key] = $value;
- }
- }
- }
- }
- return $mappings;
- }
-
/**
* Main entry point for the test engine. Determines what assemblies to
* build and test based on the files that have changed.
*
* @return array Array of test results.
*/
public function run() {
$this->loadEnvironment();
- $affected_tests = array();
if ($this->getRunAllTests()) {
- echo "Loading tests..."."\n";
- $entries = $this->getAllAvailableTestsAndRelatedProjects();
- foreach ($entries as $key => $value) {
- echo "Test: ".substr($key, strlen($this->projectRoot) + 1)."\n";
- $affected_tests[] = substr($key, strlen($this->projectRoot) + 1);
- }
+ $paths = id(new FileFinder($this->projectRoot))->find();
} else {
$paths = $this->getPaths();
+ }
+
+ return $this->runAllTests($this->mapPathsToResults($paths));
+ }
+ /**
+ * Applies the discovery rules to the set of paths specified.
+ *
+ * @param array Array of paths.
+ * @return array Array of paths to test projects and assemblies.
+ */
+ public function mapPathsToResults(array $paths) {
+ $results = array();
+ foreach ($this->discoveryRules as $regex => $targets) {
+ $regex = str_replace('/', '\\/', $regex);
foreach ($paths as $path) {
- if (substr($path, -4) == ".dll" ||
- substr($path, -4) == ".mdb") {
- continue;
- }
- if (substr_count($path, "/") > 0) {
- $components = explode("/", $path);
- $affected_assembly = $components[0];
-
- // If the change is made inside an assembly that has a `.Tests`
- // extension, then the developer has changed the actual tests.
- if (substr($affected_assembly, -6) === ".Tests") {
- $affected_assembly_path = Filesystem::resolvePath(
- $affected_assembly);
- $test_assembly = $affected_assembly;
- } else {
- $affected_assembly_path = Filesystem::resolvePath(
- $affected_assembly.".Tests");
- $test_assembly = $affected_assembly.".Tests";
- }
- if (is_dir($affected_assembly_path) &&
- !in_array($test_assembly, $affected_tests)) {
- $affected_tests[] = $test_assembly;
+ if (preg_match('/'.$regex.'/', $path) === 1) {
+ foreach ($targets as $target) {
+ // Index 0 is the test project (.csproj file)
+ // Index 1 is the output assembly (.dll file)
+ $project = preg_replace('/'.$regex.'/', $target[0], $path);
+ $project = $this->projectRoot.DIRECTORY_SEPARATOR.$project;
+ $assembly = preg_replace('/'.$regex.'/', $target[1], $path);
+ $assembly = $this->projectRoot.DIRECTORY_SEPARATOR.$assembly;
+ if (file_exists($project)) {
+ $project = Filesystem::resolvePath($project);
+ $assembly = Filesystem::resolvePath($assembly);
+
+ // Check to ensure uniqueness.
+ $exists = false;
+ foreach ($results as $existing) {
+ if ($existing['assembly'] === $assembly) {
+ $exists = true;
+ break;
+ }
+ }
+
+ if (!$exists) {
+ print "Discovered test at ".$assembly."\n";
+ $results[] = array(
+ 'project' => $project,
+ 'assembly' => $assembly);
+ }
+ }
}
}
}
}
-
- return $this->runAllTests($affected_tests);
+ return $results;
}
/**
* Builds and runs the specified test assemblies.
*
- * @param array Array of test assemblies.
+ * @param array Array of paths to test project files.
* @return array Array of test results.
*/
- public function runAllTests(array $test_assemblies) {
- if (empty($test_assemblies)) {
+ public function runAllTests(array $test_projects) {
+ if (empty($test_projects)) {
return array();
}
$results = array();
$results[] = $this->generateProjects();
if ($this->resultsContainFailures($results)) {
return array_mergev($results);
}
- $results[] = $this->buildProjects($test_assemblies);
+ $results[] = $this->buildProjects($test_projects);
if ($this->resultsContainFailures($results)) {
return array_mergev($results);
}
- $results[] = $this->testAssemblies($test_assemblies);
+ $results[] = $this->testAssemblies($test_projects);
return array_mergev($results);
}
/**
* Determine whether or not a current set of results contains any failures.
* This is needed since we build the assemblies as part of the unit tests, but
* we can't run any of the unit tests if the build fails.
*
* @param array Array of results to check.
* @return bool If there are any failures in the results.
*/
private function resultsContainFailures(array $results) {
$results = array_mergev($results);
foreach ($results as $result) {
if ($result->getResult() != ArcanistUnitTestResult::RESULT_PASS) {
return true;
}
}
return false;
}
/**
* If the `Build` directory exists, we assume that this is a multi-platform
* project that requires generation of C# project files. Because we want to
* test that the generation and subsequent build is whole, we need to
* regenerate any projects in case the developer has added files through an
* IDE and then forgotten to add them to the respective `.definitions` file.
* By regenerating the projects we ensure that any missing definition entries
* will cause the build to fail.
*
* @return array Array of test results.
*/
private function generateProjects() {
// No "Build" directory; so skip generation of projects.
if (!is_dir(Filesystem::resolvePath($this->projectRoot."/Build"))) {
return array();
}
+ // No "Protobuild.exe" file; so skip generation of projects.
+ if (!is_file(Filesystem::resolvePath(
+ $this->projectRoot."/Protobuild.exe"))) {
+
+ return array();
+ }
+
// Work out what platform the user is building for already.
$platform = phutil_is_windows() ? "Windows" : "Linux";
$files = Filesystem::listDirectory($this->projectRoot);
foreach ($files as $file) {
if (strtolower(substr($file, -4)) == ".sln") {
$parts = explode(".", $file);
$platform = $parts[count($parts) - 2];
break;
}
}
$regenerate_start = microtime(true);
$regenerate_future = new ExecFuture(
"%C Protobuild.exe --resync %s",
$this->runtimeEngine,
$platform);
$regenerate_future->setCWD(Filesystem::resolvePath(
$this->projectRoot));
$results = array();
$result = new ArcanistUnitTestResult();
$result->setName("(regenerate projects for $platform)");
try {
$regenerate_future->resolvex();
$result->setResult(ArcanistUnitTestResult::RESULT_PASS);
} catch(CommandException $exc) {
if ($exc->getError() > 1) {
throw $exc;
}
$result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
$result->setUserdata($exc->getStdout());
}
$result->setDuration(microtime(true) - $regenerate_start);
$results[] = $result;
return $results;
}
/**
* Build the projects relevant for the specified test assemblies and return
* the results of the builds as test results. This build also passes the
* "SkipTestsOnBuild" parameter when building the projects, so that MSBuild
* conditionals can be used to prevent any tests running as part of the
* build itself (since the unit tester is about to run each of the tests
* individually).
*
* @param array Array of test assemblies.
* @return array Array of test results.
*/
private function buildProjects(array $test_assemblies) {
$build_futures = array();
$build_failed = false;
$build_start = microtime(true);
$results = array();
foreach ($test_assemblies as $test_assembly) {
$build_future = new ExecFuture(
"%C %s",
$this->buildEngine,
"/p:SkipTestsOnBuild=True");
$build_future->setCWD(Filesystem::resolvePath(
- $this->projectRoot."/".$test_assembly));
- $build_futures[$test_assembly] = $build_future;
+ dirname($test_assembly['project'])));
+ $build_futures[$test_assembly['project']] = $build_future;
}
$iterator = Futures($build_futures)->limit(1);
foreach ($iterator as $test_assembly => $future) {
$result = new ArcanistUnitTestResult();
$result->setName("(build) ".$test_assembly);
try {
$future->resolvex();
$result->setResult(ArcanistUnitTestResult::RESULT_PASS);
} catch(CommandException $exc) {
if ($exc->getError() > 1) {
throw $exc;
}
$result->setResult(ArcanistUnitTestResult::RESULT_FAIL);
$result->setUserdata($exc->getStdout());
$build_failed = true;
}
$result->setDuration(microtime(true) - $build_start);
$results[] = $result;
}
return $results;
}
/**
* Build the future for running a unit test. This can be
* overridden to enable support for code coverage via
* another tool
*
* @param string Name of the test assembly.
* @return array The future, output filename and coverage filename
* stored in an array.
*/
protected function buildTestFuture($test_assembly) {
// FIXME: Can't use TempFile here as xUnit doesn't like
// UNIX-style full paths. It sees the leading / as the
// start of an option flag, even when quoted.
- $xunit_temp = $test_assembly.".results.xml";
+ $xunit_temp = Filesystem::readRandomCharacters(10).".results.xml";
if (file_exists($xunit_temp)) {
unlink($xunit_temp);
}
$future = new ExecFuture(
- "%C %s /xml %s /silent",
+ "%C %s /xml %s",
trim($this->runtimeEngine." ".$this->testEngine),
- $test_assembly."/bin/Debug/".$test_assembly.".dll",
+ $test_assembly,
$xunit_temp);
- $future->setCWD(Filesystem::resolvePath($this->projectRoot));
- return array($future, $xunit_temp, null);
+ $folder = Filesystem::resolvePath($this->projectRoot);
+ $future->setCWD($folder);
+ $combined = $folder."/".$xunit_temp;
+ if (phutil_is_windows()) {
+ $combined = $folder."\\".$xunit_temp;
+ }
+ return array($future, $combined, null);
}
/**
* Run the xUnit test runner on each of the assemblies and parse the
* resulting XML.
*
* @param array Array of test assemblies.
* @return array Array of test results.
*/
private function testAssemblies(array $test_assemblies) {
$results = array();
// Build the futures for running the tests.
$futures = array();
$outputs = array();
$coverages = array();
foreach ($test_assemblies as $test_assembly) {
- list($future, $xunit_temp, $coverage) =
- $this->buildTestFuture($test_assembly);
- $futures[$test_assembly] = $future;
- $outputs[$test_assembly] = $xunit_temp;
- $coverages[$test_assembly] = $coverage;
+ list($future_r, $xunit_temp, $coverage) =
+ $this->buildTestFuture($test_assembly['assembly']);
+ $futures[$test_assembly['assembly']] = $future_r;
+ $outputs[$test_assembly['assembly']] = $xunit_temp;
+ $coverages[$test_assembly['assembly']] = $coverage;
}
// Run all of the tests.
- foreach (Futures($futures) as $test_assembly => $future) {
- $future->resolve();
+ foreach (Futures($futures)->limit(8) as $test_assembly => $future) {
+ list($err, $stdout, $stderr) = $future->resolve();
if (file_exists($outputs[$test_assembly])) {
$result = $this->parseTestResult(
$outputs[$test_assembly],
$coverages[$test_assembly]);
$results[] = $result;
unlink($outputs[$test_assembly]);
} else {
- $result = new ArcanistUnitTestResult();
- $result->setName("(execute) ".$test_assembly);
- $result->setResult(ArcanistUnitTestResult::RESULT_BROKEN);
- $result->setUserData($outputs[$test_assembly]." not found on disk.");
- $results[] = array($result);
+ // FIXME: There's a bug in Mono which causes a segmentation fault
+ // when xUnit.NET runs; this causes the XML file to not appear
+ // (depending on when the segmentation fault occurs). See
+ // https://bugzilla.xamarin.com/show_bug.cgi?id=16379
+ // for more information.
+
+ // Since it's not possible for the user to correct this error, we
+ // ignore the fact the tests didn't run here.
+ //
}
}
return array_mergev($results);
}
/**
* Returns null for this implementation as xUnit does not support code
* coverage directly. Override this method in another class to provide
* code coverage information (also see `CSharpToolsUnitEngine`).
*
* @param string The name of the coverage file if one was provided by
* `buildTestFuture`.
* @return array Code coverage results, or null.
*/
protected function parseCoverageResult($coverage) {
return null;
}
/**
* Parses the test results from xUnit.
*
* @param string The name of the xUnit results file.
* @param string The name of the coverage file if one was provided by
* `buildTestFuture`. This is passed through to
* `parseCoverageResult`.
* @return array Test results.
*/
private function parseTestResult($xunit_tmp, $coverage) {
$xunit_dom = new DOMDocument();
$xunit_dom->loadXML(Filesystem::readFile($xunit_tmp));
$results = array();
$tests = $xunit_dom->getElementsByTagName("test");
foreach ($tests as $test) {
$name = $test->getAttribute("name");
$time = $test->getAttribute("time");
$status = ArcanistUnitTestResult::RESULT_UNSOUND;
switch ($test->getAttribute("result")) {
case "Pass":
$status = ArcanistUnitTestResult::RESULT_PASS;
break;
case "Fail":
$status = ArcanistUnitTestResult::RESULT_FAIL;
break;
case "Skip":
$status = ArcanistUnitTestResult::RESULT_SKIP;
break;
}
$userdata = "";
$reason = $test->getElementsByTagName("reason");
$failure = $test->getElementsByTagName("failure");
if ($reason->length > 0 || $failure->length > 0) {
$node = ($reason->length > 0) ? $reason : $failure;
$message = $node->item(0)->getElementsByTagName("message");
if ($message->length > 0) {
$userdata = $message->item(0)->nodeValue;
}
$stacktrace = $node->item(0)->getElementsByTagName("stack-trace");
if ($stacktrace->length > 0) {
$userdata .= "\n".$stacktrace->item(0)->nodeValue;
}
}
$result = new ArcanistUnitTestResult();
$result->setName($name);
$result->setResult($status);
$result->setDuration($time);
$result->setUserData($userdata);
if ($coverage != null) {
$result->setCoverage($this->parseCoverageResult($coverage));
}
$results[] = $result;
}
return $results;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 07:27 (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
556854
Default Alt Text
(28 KB)

Event Timeline