diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -211,6 +211,11 @@ 'ArcanistXMLLinterTestCase' => 'lint/linter/__tests__/ArcanistXMLLinterTestCase.php', 'ArcanistXUnitTestResultParser' => 'unit/parser/ArcanistXUnitTestResultParser.php', 'CSharpToolsTestEngine' => 'unit/engine/CSharpToolsTestEngine.php', + 'GoBaseTestEngine' => 'unit/engine/GoBaseTestEngine.php', + 'GoTestEngine' => 'unit/engine/GoTestEngine.php', + 'GoTestRaceEngine' => 'unit/engine/GoTestRaceEngine.php', + 'GodepGoTestEngine' => 'unit/engine/GodepGoTestEngine.php', + 'GodepGoTestRaceEngine' => 'unit/engine/GodepGoTestRaceEngine.php', 'NoseTestEngine' => 'unit/engine/NoseTestEngine.php', 'PhpunitTestEngine' => 'unit/engine/PhpunitTestEngine.php', 'PhpunitTestEngineTestCase' => 'unit/engine/__tests__/PhpunitTestEngineTestCase.php', @@ -389,6 +394,11 @@ 'ArcanistXMLLinter' => 'ArcanistLinter', 'ArcanistXMLLinterTestCase' => 'ArcanistLinterTestCase', 'CSharpToolsTestEngine' => 'XUnitTestEngine', + 'GoBaseTestEngine' => 'ArcanistUnitTestEngine', + 'GoTestEngine' => 'GoBaseTestEngine', + 'GoTestRaceEngine' => 'GoBaseTestEngine', + 'GodepGoTestEngine' => 'GoBaseTestEngine', + 'GodepGoTestRaceEngine' => 'GoBaseTestEngine', 'NoseTestEngine' => 'ArcanistUnitTestEngine', 'PhpunitTestEngine' => 'ArcanistUnitTestEngine', 'PhpunitTestEngineTestCase' => 'ArcanistTestCase', diff --git a/src/unit/engine/GoBaseTestEngine.php b/src/unit/engine/GoBaseTestEngine.php new file mode 100644 --- /dev/null +++ b/src/unit/engine/GoBaseTestEngine.php @@ -0,0 +1,54 @@ +affectedPackages = array(); + foreach ($this->getPaths() as $path) { + // Must always test a package. + if (!is_dir($path)) { + // If it's a file but not a go file. Skip this test + if (substr($path, -3) != '.go') { + continue; + } + + $path = dirname($path); + } + + if (!array_key_exists($path, $this->affectedPackages)) { + $this->affectedPackages[] = $path; + } + } + + if (empty($this->affectedPackages)) { + throw new ArcanistNoEffectException('No tests to run.'); + } + + $parser = new ArcanistGoTestResultParser(); + $results = array(); + + foreach ($this->affectedPackages as $package) { + if ($package == '.') { + $package = ''; + } + list($err, $stdout, $stderr) = $this->testPackage($package); + $r = $parser->parseTestResults(null, $stderr.$stdout); + $results = array_merge($results, $r); + } + + return $results; + } +} diff --git a/src/unit/engine/GoTestEngine.php b/src/unit/engine/GoTestEngine.php new file mode 100644 --- /dev/null +++ b/src/unit/engine/GoTestEngine.php @@ -0,0 +1,10 @@ + $line) { + for ($i = 0; $i < count($test_results); $i++) { + $line = $test_results[$i]; + + if (strlen($line) >= 18 + && strncmp($line, '==================', 18) === 0 + && strncmp($test_results[$i + 1], 'WARNING: DATA RACE', 18) === 0) { + // We have a race condition + $i++; // Advance to WARNING: DATA RACE + $reason = ''; + $test_name = ''; + + // loop to collect all data and move to the === line + while (strncmp($test_results[$i], '==================', 18) !== 0) { + if (strncmp($test_results[$i], 'Goroutine', 9) === 0) { + $meta = array(); + preg_match( + '/^.*\.(?P[^\.]+)$/', + $test_results[$i + 1], + $meta); + $test_name = $meta['test_name'].' Race Detected'; + } + $reason .= $test_results[$i++]."\n"; + } + + $result = new ArcanistUnitTestResult(); + $result->setName($test_name); + $result->setResult(ArcanistUnitTestResult::RESULT_FAIL); + $result->setUserData($reason); + + $test_case_results[] = $result; + + continue; + } if (strncmp($line, '--- PASS', 8) === 0) { // We have a passing test diff --git a/src/unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php b/src/unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php --- a/src/unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php +++ b/src/unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php @@ -39,6 +39,47 @@ $parsed_results[1]->getResult()); } + public function testRaceWarningTestCaseSuccess() { + $stubbed_results = Filesystem::readFile( + dirname(__FILE__).'/testresults/go.race-warning-test-case-success-go1.4'); + + $parsed_results = id(new ArcanistGoTestResultParser()) + ->parseTestResults('racepackage_test.go', $stubbed_results); + + $this->assertEqual(3, count($parsed_results)); + $this->assertEqual( + ArcanistUnitTestResult::RESULT_FAIL, + $parsed_results[0]->getResult()); + $this->assertEqual( + 'WARNING: DATA RACE', + idx(explode("\n", $parsed_results[0]->getUserData()), 0)); + $this->assertEqual( + ArcanistUnitTestResult::RESULT_PASS, + $parsed_results[1]->getResult()); + } + + public function testRaceWarningTestCaseFailure() { + $stubbed_results = Filesystem::readFile( + dirname(__FILE__).'/testresults/go.race-warning-test-case-failure-go1.4'); + + $parsed_results = id(new ArcanistGoTestResultParser()) + ->parseTestResults('racepackage_test.go', $stubbed_results); + + $this->assertEqual(3, count($parsed_results)); + $this->assertEqual( + ArcanistUnitTestResult::RESULT_FAIL, + $parsed_results[0]->getResult()); + $this->assertEqual( + 'WARNING: DATA RACE', + idx(explode("\n", $parsed_results[0]->getUserData()), 0)); + $this->assertEqual( + ArcanistUnitTestResult::RESULT_PASS, + $parsed_results[1]->getResult()); + $this->assertEqual( + "racepackage_test.go:30: got: 2, want: 1\n", + $parsed_results[2]->getUserData()); + } + public function testMultipleTestCasesSuccessful() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.multiple-test-cases-successful'); diff --git a/src/unit/parser/__tests__/testresults/go.race-warning-test-case-failure-go1.4 b/src/unit/parser/__tests__/testresults/go.race-warning-test-case-failure-go1.4 new file mode 100644 --- /dev/null +++ b/src/unit/parser/__tests__/testresults/go.race-warning-test-case-failure-go1.4 @@ -0,0 +1,29 @@ +================== +WARNING: DATA RACE +Write by goroutine 8: + racepackage.func~002() + /tmp/racepackage_test.go:21 +0x5d + +Previous write by goroutine 7: + racepackage.func~001() + /tmp/racepackage_test.go:17 +0x5d + +Goroutine 8 (running) created at: + racepackage.TestBar() + /tmp/racepackage_test.go:22 +0x1e8 + testing.tRunner() + /usr/local/Cellar/go/1.4.2/libexec/src/testing/testing.go:447 +0x133 + +Goroutine 7 (finished) created at: + racepackage.TestBar() + /tmp/racepackage_test.go:18 +0x13e + testing.tRunner() + /usr/local/Cellar/go/1.4.2/libexec/src/testing/testing.go:447 +0x133 +================== +=== RUN TestFoo +--- PASS: TestFoo (0.03s) +=== RUN TestBar +--- FAIL: TestBar (0.02s) + racepackage_test.go:30: got: 2, want: 1 +PASS +ok package/racepackage 0.042s diff --git a/src/unit/parser/__tests__/testresults/go.race-warning-test-case-success-go1.4 b/src/unit/parser/__tests__/testresults/go.race-warning-test-case-success-go1.4 new file mode 100644 --- /dev/null +++ b/src/unit/parser/__tests__/testresults/go.race-warning-test-case-success-go1.4 @@ -0,0 +1,28 @@ +================== +WARNING: DATA RACE +Write by goroutine 8: + racepackage.func~002() + /tmp/racepackage_test.go:21 +0x5d + +Previous write by goroutine 7: + racepackage.func~001() + /tmp/racepackage_test.go:17 +0x5d + +Goroutine 8 (running) created at: + racepackage.TestBar() + /tmp/racepackage_test.go:22 +0x1e8 + testing.tRunner() + /usr/local/Cellar/go/1.4.2/libexec/src/testing/testing.go:447 +0x133 + +Goroutine 7 (finished) created at: + racepackage.TestBar() + /tmp/racepackage_test.go:18 +0x13e + testing.tRunner() + /usr/local/Cellar/go/1.4.2/libexec/src/testing/testing.go:447 +0x133 +================== +=== RUN TestFoo +--- PASS: TestFoo (0.03s) +=== RUN TestBar +--- PASS: TestBar (0.01s) +PASS +ok package/racepackage 0.042s