Changeset View
Changeset View
Standalone View
Standalone View
src/unit/engine/MidjeUnitTestEngine.php
- This file was added.
| <?php | |||||
| /** | |||||
| * Very basic unit test engine which runs libphutil tests. | |||||
| */ | |||||
| final class MidjeUnitTestEngine extends ArcanistUnitTestEngine { | |||||
| public function getEngineConfigurationName() { | |||||
| return 'midje'; | |||||
| } | |||||
| protected function supportsRunAllTests() { | |||||
| return true; | |||||
| } | |||||
| public function shouldEchoTestResults() { | |||||
| return false; | |||||
| } | |||||
| public function run() { | |||||
| if ($this->getRunAllTests()) { | |||||
| $run_tests = $this->getAllTests(); | |||||
| } else { | |||||
| $run_tests = $this->getTestsForPaths(); | |||||
| } | |||||
| if (!$run_tests) { | |||||
| throw new ArcanistNoEffectException(pht('No tests to run.')); | |||||
| } | |||||
| $midje_config = new TempFile(); | |||||
| Filesystem::writeFile($midje_config, | |||||
| <<<CFG | |||||
| (change-defaults | |||||
| :emitter 'midje.emission.plugins.junit | |||||
| :colorize false) | |||||
| CFG | |||||
| ); | |||||
| try { | |||||
| list($stdout, $stderr) = execx('lein midje %C :config %s', | |||||
| implode(' ', $run_tests), | |||||
| $midje_config); | |||||
| } catch (CommandException $e) { | |||||
| // Handle only special error codes (see e.g. `man 1 bash` about | |||||
| // "EXIT STATUS") | |||||
| if ($e->getError() > 125) { | |||||
| throw $e; | |||||
| } | |||||
| } | |||||
| $results = array(); | |||||
| foreach ($run_tests as $test) { | |||||
| $xunit_report = 'target/surefire-reports/TEST-'.$test.'.xml'; | |||||
| $cover_report = ''; | |||||
| $results[] = $this->parseTestResults( | |||||
| $test, | |||||
| $xunit_report, | |||||
| $cover_report); | |||||
| } | |||||
| return array_mergev($results); | |||||
| } | |||||
| private function parseTestResults($test, $xunit_report, $cover_report) { | |||||
| $xunit_results = Filesystem::readFile($xunit_report); | |||||
| return id(new ArcanistXUnitTestResultParser()) | |||||
| ->parseTestResults($xunit_results); | |||||
| } | |||||
| private $clojureExtensions = array('clj', 'cljs', 'cljc'); | |||||
| private function isSupportedFileType($path) { | |||||
| $extension = idx(pathinfo($path), 'extension'); | |||||
| foreach ($this->clojureExtensions as $clojure_extension) { | |||||
| if ($extension == $clojure_extension) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * For a given test file, figure out the namespace it contains. | |||||
| * | |||||
| * @param $path string Filename with extension | |||||
| * @return string Namespace | |||||
| */ | |||||
| private function testPathToNamespace($path) { | |||||
| $path_stub = $this->stripTestPathPrefix( | |||||
| $this->stripFileExtension($path)); | |||||
| $namespace = str_replace('_', '-', $path_stub); | |||||
| $namespace = str_replace('/', '.', $namespace); | |||||
| return $namespace; | |||||
| } | |||||
| // FIXME This list should actually be retrieved from `:test-paths` in | |||||
| // "project.clj" | |||||
| private $projectTestPaths = array('test'); | |||||
| private $testPrefix = 'test_'; | |||||
| private $testSuffix = ''; | |||||
| /** | |||||
| * Remove test path prefix from a file name. | |||||
| * | |||||
| * Tries to remove all known `:test-paths` until one matches. | |||||
| */ | |||||
| private function stripTestPathPrefix($path) { | |||||
| foreach ($this->projectTestPaths as $project_test_path) { | |||||
| $prefix = $project_test_path.'/'; | |||||
| $prefix_len = strlen($prefix); | |||||
| if (substr($path, 0, $prefix_len) == $prefix) { | |||||
| return substr($path, $prefix_len); | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Small convenience wrapper around `pathinfo()`. | |||||
| */ | |||||
| private function stripFileExtension($path) { | |||||
| $path_info = pathinfo($path); | |||||
| return $path_info['dirname'].'/'.$path_info['filename']; | |||||
| } | |||||
| /** | |||||
| * For a given path, figure out the corresponding test case's namespace. | |||||
| * | |||||
| * @param $path string Path of the file to be tested. | |||||
| * @return string or null Namespace of the test case, if any. | |||||
| */ | |||||
| private function pathToTest($path) { | |||||
| $path_info = pathinfo($path); | |||||
| $base_test_path = $path_info['dirname'].'/'. | |||||
| $this->testPrefix.$path_info['filename'].$this->testSuffix. | |||||
| '.'.$path_info['extension']; | |||||
| while ($base_test_path) { | |||||
| foreach ($this->projectTestPaths as $project_test_path) { | |||||
| $test_path = $project_test_path.'/'.$base_test_path; | |||||
| if (is_readable($test_path)) { | |||||
| return $this->testPathToNamespace($test_path); | |||||
| } | |||||
| } | |||||
| // FIXME This could be simplified and sped up, if we had `:source-paths` | |||||
| // from "project.clj". | |||||
| $next_pos = strpos($base_test_path, DIRECTORY_SEPARATOR); | |||||
| if (!$next_pos) { | |||||
| return; | |||||
| } | |||||
| $base_test_path = substr($base_test_path, $next_pos + 1); | |||||
| } | |||||
| return; | |||||
| } | |||||
| /** | |||||
| * Retrieve all test cases. | |||||
| * | |||||
| * @return list<string> The names of the test case namespaces to be executed. | |||||
| */ | |||||
| private function getAllTests() { | |||||
| $test_paths = array(); | |||||
| foreach ($this->projectTestPaths as $project_test_path) { | |||||
| foreach ($this->clojureExtensions as $extension) { | |||||
| $test_paths = array_merge($test_paths, | |||||
| glob($project_test_path.'/**/*.'.$extension)); | |||||
| } | |||||
| } | |||||
| $run_tests = array(); | |||||
| foreach ($test_paths as $test_path) { | |||||
| $test = $this->testPathToNamespace($test_path); | |||||
| if ($test) { | |||||
| $run_tests[] = $test; | |||||
| } | |||||
| } | |||||
| return $run_tests; | |||||
| } | |||||
| /** | |||||
| * Retrieve all relevant test cases. | |||||
| * | |||||
| * For every affected file, it looks for a corresponding test case namespace | |||||
| * with a `-test` suffix. | |||||
| * | |||||
| * @return list<string> The names of the test case namespaces to be executed. | |||||
| */ | |||||
| private function getTestsForPaths() { | |||||
| $run_tests = array(); | |||||
| foreach ($this->getPaths() as $path) { | |||||
| if (!$this->isSupportedFileType($path)) { | |||||
| continue; | |||||
| } | |||||
| $test = $this->pathToTest($path); | |||||
| if ($test) { | |||||
| $run_tests[] = $test; | |||||
| } | |||||
| } | |||||
| return $run_tests; | |||||
| } | |||||
| } | |||||