Changeset View
Changeset View
Standalone View
Standalone View
src/unit/engine/XUnitTestEngine.php
| Show All 32 Lines | class XUnitTestEngine extends ArcanistBaseUnitTestEngine { | ||||
| * to run successfully. | * to run successfully. | ||||
| * | * | ||||
| * @return void | * @return void | ||||
| */ | */ | ||||
| protected function loadEnvironment() { | protected function loadEnvironment() { | ||||
| $this->projectRoot = $this->getWorkingCopy()->getProjectRoot(); | $this->projectRoot = $this->getWorkingCopy()->getProjectRoot(); | ||||
| // Determine build engine. | // Determine build engine. | ||||
| if (Filesystem::binaryExists("msbuild")) { | if (Filesystem::binaryExists('msbuild')) { | ||||
| $this->buildEngine = "msbuild"; | $this->buildEngine = 'msbuild'; | ||||
| } else if (Filesystem::binaryExists("xbuild")) { | } else if (Filesystem::binaryExists('xbuild')) { | ||||
| $this->buildEngine = "xbuild"; | $this->buildEngine = 'xbuild'; | ||||
| } else { | } else { | ||||
| throw new Exception("Unable to find msbuild or xbuild in PATH!"); | throw new Exception('Unable to find msbuild or xbuild in PATH!'); | ||||
| } | } | ||||
| // Determine runtime engine (.NET or Mono). | // Determine runtime engine (.NET or Mono). | ||||
| if (phutil_is_windows()) { | if (phutil_is_windows()) { | ||||
| $this->runtimeEngine = ""; | $this->runtimeEngine = ''; | ||||
| } else if (Filesystem::binaryExists("mono")) { | } else if (Filesystem::binaryExists('mono')) { | ||||
| $this->runtimeEngine = Filesystem::resolveBinary("mono"); | $this->runtimeEngine = Filesystem::resolveBinary('mono'); | ||||
| } else { | } else { | ||||
| throw new Exception("Unable to find Mono and you are not on Windows!"); | throw new Exception('Unable to find Mono and you are not on Windows!'); | ||||
| } | } | ||||
| // Read the discovery rules. | // Read the discovery rules. | ||||
| $this->discoveryRules = | $this->discoveryRules = | ||||
| $this->getConfigurationManager()->getConfigFromAnySource( | $this->getConfigurationManager()->getConfigFromAnySource( | ||||
| 'unit.csharp.discovery'); | 'unit.csharp.discovery'); | ||||
| if ($this->discoveryRules === null) { | if ($this->discoveryRules === null) { | ||||
| throw new Exception( | throw new Exception( | ||||
| "You must configure discovery rules to map C# files ". | 'You must configure discovery rules to map C# files '. | ||||
| "back to test projects (`unit.csharp.discovery` in .arcconfig)."); | 'back to test projects (`unit.csharp.discovery` in .arcconfig).'); | ||||
| } | } | ||||
| // Determine xUnit test runner path. | // Determine xUnit test runner path. | ||||
| if ($this->xunitHintPath === null) { | if ($this->xunitHintPath === null) { | ||||
| $this->xunitHintPath = | $this->xunitHintPath = | ||||
| $this->getConfigurationManager()->getConfigFromAnySource( | $this->getConfigurationManager()->getConfigFromAnySource( | ||||
| 'unit.csharp.xunit.binary'); | 'unit.csharp.xunit.binary'); | ||||
| } | } | ||||
| $xunit = $this->projectRoot.DIRECTORY_SEPARATOR.$this->xunitHintPath; | $xunit = $this->projectRoot.DIRECTORY_SEPARATOR.$this->xunitHintPath; | ||||
| if (file_exists($xunit) && $this->xunitHintPath !== null) { | if (file_exists($xunit) && $this->xunitHintPath !== null) { | ||||
| $this->testEngine = Filesystem::resolvePath($xunit); | $this->testEngine = Filesystem::resolvePath($xunit); | ||||
| } else if (Filesystem::binaryExists("xunit.console.clr4.exe")) { | } else if (Filesystem::binaryExists('xunit.console.clr4.exe')) { | ||||
| $this->testEngine = "xunit.console.clr4.exe"; | $this->testEngine = 'xunit.console.clr4.exe'; | ||||
| } else { | } else { | ||||
| throw new Exception( | throw new Exception( | ||||
| "Unable to locate xUnit console runner. Configure ". | "Unable to locate xUnit console runner. Configure ". | ||||
| "it with the `unit.csharp.xunit.binary' option in .arcconfig"); | "it with the `unit.csharp.xunit.binary' option in .arcconfig"); | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| ▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | class XUnitTestEngine extends ArcanistBaseUnitTestEngine { | ||||
| * By regenerating the projects we ensure that any missing definition entries | * By regenerating the projects we ensure that any missing definition entries | ||||
| * will cause the build to fail. | * will cause the build to fail. | ||||
| * | * | ||||
| * @return array Array of test results. | * @return array Array of test results. | ||||
| */ | */ | ||||
| private function generateProjects() { | private function generateProjects() { | ||||
| // No "Build" directory; so skip generation of projects. | // No "Build" directory; so skip generation of projects. | ||||
| if (!is_dir(Filesystem::resolvePath($this->projectRoot."/Build"))) { | if (!is_dir(Filesystem::resolvePath($this->projectRoot.'/Build'))) { | ||||
| return array(); | return array(); | ||||
| } | } | ||||
| // No "Protobuild.exe" file; so skip generation of projects. | // No "Protobuild.exe" file; so skip generation of projects. | ||||
| if (!is_file(Filesystem::resolvePath( | if (!is_file(Filesystem::resolvePath( | ||||
| $this->projectRoot."/Protobuild.exe"))) { | $this->projectRoot.'/Protobuild.exe'))) { | ||||
| return array(); | return array(); | ||||
| } | } | ||||
| // Work out what platform the user is building for already. | // Work out what platform the user is building for already. | ||||
| $platform = phutil_is_windows() ? "Windows" : "Linux"; | $platform = phutil_is_windows() ? 'Windows' : 'Linux'; | ||||
| $files = Filesystem::listDirectory($this->projectRoot); | $files = Filesystem::listDirectory($this->projectRoot); | ||||
| foreach ($files as $file) { | foreach ($files as $file) { | ||||
| if (strtolower(substr($file, -4)) == ".sln") { | if (strtolower(substr($file, -4)) == '.sln') { | ||||
| $parts = explode(".", $file); | $parts = explode('.', $file); | ||||
| $platform = $parts[count($parts) - 2]; | $platform = $parts[count($parts) - 2]; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| $regenerate_start = microtime(true); | $regenerate_start = microtime(true); | ||||
| $regenerate_future = new ExecFuture( | $regenerate_future = new ExecFuture( | ||||
| "%C Protobuild.exe --resync %s", | '%C Protobuild.exe --resync %s', | ||||
| $this->runtimeEngine, | $this->runtimeEngine, | ||||
| $platform); | $platform); | ||||
| $regenerate_future->setCWD(Filesystem::resolvePath( | $regenerate_future->setCWD(Filesystem::resolvePath( | ||||
| $this->projectRoot)); | $this->projectRoot)); | ||||
| $results = array(); | $results = array(); | ||||
| $result = new ArcanistUnitTestResult(); | $result = new ArcanistUnitTestResult(); | ||||
| $result->setName("(regenerate projects for $platform)"); | $result->setName("(regenerate projects for $platform)"); | ||||
| Show All 26 Lines | class XUnitTestEngine extends ArcanistBaseUnitTestEngine { | ||||
| */ | */ | ||||
| private function buildProjects(array $test_assemblies) { | private function buildProjects(array $test_assemblies) { | ||||
| $build_futures = array(); | $build_futures = array(); | ||||
| $build_failed = false; | $build_failed = false; | ||||
| $build_start = microtime(true); | $build_start = microtime(true); | ||||
| $results = array(); | $results = array(); | ||||
| foreach ($test_assemblies as $test_assembly) { | foreach ($test_assemblies as $test_assembly) { | ||||
| $build_future = new ExecFuture( | $build_future = new ExecFuture( | ||||
| "%C %s", | '%C %s', | ||||
| $this->buildEngine, | $this->buildEngine, | ||||
| "/p:SkipTestsOnBuild=True"); | '/p:SkipTestsOnBuild=True'); | ||||
| $build_future->setCWD(Filesystem::resolvePath( | $build_future->setCWD(Filesystem::resolvePath( | ||||
| dirname($test_assembly['project']))); | dirname($test_assembly['project']))); | ||||
| $build_futures[$test_assembly['project']] = $build_future; | $build_futures[$test_assembly['project']] = $build_future; | ||||
| } | } | ||||
| $iterator = Futures($build_futures)->limit(1); | $iterator = Futures($build_futures)->limit(1); | ||||
| foreach ($iterator as $test_assembly => $future) { | foreach ($iterator as $test_assembly => $future) { | ||||
| $result = new ArcanistUnitTestResult(); | $result = new ArcanistUnitTestResult(); | ||||
| $result->setName("(build) ".$test_assembly); | $result->setName('(build) '.$test_assembly); | ||||
| try { | try { | ||||
| $future->resolvex(); | $future->resolvex(); | ||||
| $result->setResult(ArcanistUnitTestResult::RESULT_PASS); | $result->setResult(ArcanistUnitTestResult::RESULT_PASS); | ||||
| } catch(CommandException $exc) { | } catch(CommandException $exc) { | ||||
| if ($exc->getError() > 1) { | if ($exc->getError() > 1) { | ||||
| throw $exc; | throw $exc; | ||||
| } | } | ||||
| Show All 16 Lines | class XUnitTestEngine extends ArcanistBaseUnitTestEngine { | ||||
| * @param string Name of the test assembly. | * @param string Name of the test assembly. | ||||
| * @return array The future, output filename and coverage filename | * @return array The future, output filename and coverage filename | ||||
| * stored in an array. | * stored in an array. | ||||
| */ | */ | ||||
| protected function buildTestFuture($test_assembly) { | protected function buildTestFuture($test_assembly) { | ||||
| // FIXME: Can't use TempFile here as xUnit doesn't like | // FIXME: Can't use TempFile here as xUnit doesn't like | ||||
| // UNIX-style full paths. It sees the leading / as the | // UNIX-style full paths. It sees the leading / as the | ||||
| // start of an option flag, even when quoted. | // start of an option flag, even when quoted. | ||||
| $xunit_temp = Filesystem::readRandomCharacters(10).".results.xml"; | $xunit_temp = Filesystem::readRandomCharacters(10).'.results.xml'; | ||||
| if (file_exists($xunit_temp)) { | if (file_exists($xunit_temp)) { | ||||
| unlink($xunit_temp); | unlink($xunit_temp); | ||||
| } | } | ||||
| $future = new ExecFuture( | $future = new ExecFuture( | ||||
| "%C %s /xml %s", | '%C %s /xml %s', | ||||
| trim($this->runtimeEngine." ".$this->testEngine), | trim($this->runtimeEngine.' '.$this->testEngine), | ||||
| $test_assembly, | $test_assembly, | ||||
| $xunit_temp); | $xunit_temp); | ||||
| $folder = Filesystem::resolvePath($this->projectRoot); | $folder = Filesystem::resolvePath($this->projectRoot); | ||||
| $future->setCWD($folder); | $future->setCWD($folder); | ||||
| $combined = $folder."/".$xunit_temp; | $combined = $folder.'/'.$xunit_temp; | ||||
| if (phutil_is_windows()) { | if (phutil_is_windows()) { | ||||
| $combined = $folder."\\".$xunit_temp; | $combined = $folder.'\\'.$xunit_temp; | ||||
| } | } | ||||
| return array($future, $combined, null); | return array($future, $combined, null); | ||||
| } | } | ||||
| /** | /** | ||||
| * Run the xUnit test runner on each of the assemblies and parse the | * Run the xUnit test runner on each of the assemblies and parse the | ||||
| * resulting XML. | * resulting XML. | ||||
| * | * | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | class XUnitTestEngine extends ArcanistBaseUnitTestEngine { | ||||
| * `parseCoverageResult`. | * `parseCoverageResult`. | ||||
| * @return array Test results. | * @return array Test results. | ||||
| */ | */ | ||||
| private function parseTestResult($xunit_tmp, $coverage) { | private function parseTestResult($xunit_tmp, $coverage) { | ||||
| $xunit_dom = new DOMDocument(); | $xunit_dom = new DOMDocument(); | ||||
| $xunit_dom->loadXML(Filesystem::readFile($xunit_tmp)); | $xunit_dom->loadXML(Filesystem::readFile($xunit_tmp)); | ||||
| $results = array(); | $results = array(); | ||||
| $tests = $xunit_dom->getElementsByTagName("test"); | $tests = $xunit_dom->getElementsByTagName('test'); | ||||
| foreach ($tests as $test) { | foreach ($tests as $test) { | ||||
| $name = $test->getAttribute("name"); | $name = $test->getAttribute('name'); | ||||
| $time = $test->getAttribute("time"); | $time = $test->getAttribute('time'); | ||||
| $status = ArcanistUnitTestResult::RESULT_UNSOUND; | $status = ArcanistUnitTestResult::RESULT_UNSOUND; | ||||
| switch ($test->getAttribute("result")) { | switch ($test->getAttribute('result')) { | ||||
| case "Pass": | case 'Pass': | ||||
| $status = ArcanistUnitTestResult::RESULT_PASS; | $status = ArcanistUnitTestResult::RESULT_PASS; | ||||
| break; | break; | ||||
| case "Fail": | case 'Fail': | ||||
| $status = ArcanistUnitTestResult::RESULT_FAIL; | $status = ArcanistUnitTestResult::RESULT_FAIL; | ||||
| break; | break; | ||||
| case "Skip": | case 'Skip': | ||||
| $status = ArcanistUnitTestResult::RESULT_SKIP; | $status = ArcanistUnitTestResult::RESULT_SKIP; | ||||
| break; | break; | ||||
| } | } | ||||
| $userdata = ""; | $userdata = ''; | ||||
| $reason = $test->getElementsByTagName("reason"); | $reason = $test->getElementsByTagName('reason'); | ||||
| $failure = $test->getElementsByTagName("failure"); | $failure = $test->getElementsByTagName('failure'); | ||||
| if ($reason->length > 0 || $failure->length > 0) { | if ($reason->length > 0 || $failure->length > 0) { | ||||
| $node = ($reason->length > 0) ? $reason : $failure; | $node = ($reason->length > 0) ? $reason : $failure; | ||||
| $message = $node->item(0)->getElementsByTagName("message"); | $message = $node->item(0)->getElementsByTagName('message'); | ||||
| if ($message->length > 0) { | if ($message->length > 0) { | ||||
| $userdata = $message->item(0)->nodeValue; | $userdata = $message->item(0)->nodeValue; | ||||
| } | } | ||||
| $stacktrace = $node->item(0)->getElementsByTagName("stack-trace"); | $stacktrace = $node->item(0)->getElementsByTagName('stack-trace'); | ||||
| if ($stacktrace->length > 0) { | if ($stacktrace->length > 0) { | ||||
| $userdata .= "\n".$stacktrace->item(0)->nodeValue; | $userdata .= "\n".$stacktrace->item(0)->nodeValue; | ||||
| } | } | ||||
| } | } | ||||
| $result = new ArcanistUnitTestResult(); | $result = new ArcanistUnitTestResult(); | ||||
| $result->setName($name); | $result->setName($name); | ||||
| $result->setResult($status); | $result->setResult($status); | ||||
| Show All 12 Lines | |||||