|
|
@@ -2,6 +2,23 @@
|
|
|
// Exceptions. See /LICENSE for license information.
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
|
|
+// Implementation-wise, this:
|
|
|
+//
|
|
|
+// - Uses the registered `FileTestFactory` to construct `FileTestBase`
|
|
|
+// instances.
|
|
|
+// - Constructs a `FileTestCase` that wraps each `FileTestBase` instance to
|
|
|
+// register with googletest, and to provide the actual `TestBody`.
|
|
|
+// - Using `FileTestEventListener`, runs tests in parallel prior to normal
|
|
|
+// googletest execution.
|
|
|
+// - This is required to support `--gtest_filter` and access `should_run`.
|
|
|
+// - Runs each `FileTestBase` instance to cache the `TestFile` on
|
|
|
+// `FileTestInfo`.
|
|
|
+// - Determines whether autoupdate would make changes, autoupdating if
|
|
|
+// requested.
|
|
|
+// - When googletest would normally execute the test, `FileTestCase::TestBody`
|
|
|
+// instead uses the cached state on `FileTestInfo`.
|
|
|
+// - This only occurs when neither autoupdating nor dumping output.
|
|
|
+
|
|
|
#include "testing/file_test/file_test_base.h"
|
|
|
|
|
|
#include <filesystem>
|
|
|
@@ -11,6 +28,8 @@
|
|
|
|
|
|
#include "absl/flags/flag.h"
|
|
|
#include "absl/flags/parse.h"
|
|
|
+#include "absl/strings/str_join.h"
|
|
|
+#include "absl/strings/str_split.h"
|
|
|
#include "common/check.h"
|
|
|
#include "common/error.h"
|
|
|
#include "common/exe_path.h"
|
|
|
@@ -30,7 +49,8 @@
|
|
|
|
|
|
ABSL_FLAG(std::vector<std::string>, file_tests, {},
|
|
|
"A comma-separated list of repo-relative names of test files. "
|
|
|
- "Overrides test_targets_file.");
|
|
|
+ "Similar to and overrides `--gtest_filter`, but doesn't require the "
|
|
|
+ "test class name to be known.");
|
|
|
ABSL_FLAG(std::string, test_targets_file, "",
|
|
|
"A path to a file containing repo-relative names of test files.");
|
|
|
ABSL_FLAG(bool, autoupdate, false,
|
|
|
@@ -45,6 +65,39 @@ ABSL_FLAG(bool, dump_output, false,
|
|
|
|
|
|
namespace Carbon::Testing {
|
|
|
|
|
|
+// Information for a test case.
|
|
|
+struct FileTestInfo {
|
|
|
+ // The name.
|
|
|
+ std::string test_name;
|
|
|
+
|
|
|
+ // A factory function for creating the test object.
|
|
|
+ std::function<auto()->FileTestBase*> factory_fn;
|
|
|
+
|
|
|
+ // gtest's information about the test.
|
|
|
+ ::testing::TestInfo* registered_test;
|
|
|
+
|
|
|
+ // The test result, set after running.
|
|
|
+ std::optional<ErrorOr<TestFile>> test_result;
|
|
|
+
|
|
|
+ // Whether running autoupdate would change (or when autoupdating, already
|
|
|
+ // changed) the test file. This may be true even if output passes test
|
|
|
+ // expectations.
|
|
|
+ bool autoupdate_differs = false;
|
|
|
+};
|
|
|
+
|
|
|
+// Adapts a `FileTestBase` instance to gtest for outputting results.
|
|
|
+class FileTestCase : public testing::Test {
|
|
|
+ public:
|
|
|
+ explicit FileTestCase(FileTestInfo* test_info) : test_info_(test_info) {}
|
|
|
+
|
|
|
+ // Runs a test and compares output. This keeps output split by line so that
|
|
|
+ // issues are a little easier to identify by the different line.
|
|
|
+ auto TestBody() -> void final;
|
|
|
+
|
|
|
+ private:
|
|
|
+ FileTestInfo* test_info_;
|
|
|
+};
|
|
|
+
|
|
|
// Splits outputs to string_view because gtest handles string_view by default.
|
|
|
static auto SplitOutput(llvm::StringRef output)
|
|
|
-> llvm::SmallVector<std::string_view> {
|
|
|
@@ -144,28 +197,27 @@ static auto RunAutoupdater(FileTestBase* test_base, const TestFile& test_file,
|
|
|
.Run(dry_run);
|
|
|
}
|
|
|
|
|
|
-// Runs a test and compares output. This keeps output split by line so that
|
|
|
-// issues are a little easier to identify by the different line.
|
|
|
-auto FileTestBase::TestBody() -> void {
|
|
|
- // Add a crash trace entry with the single-file test command.
|
|
|
- std::string test_command = GetBazelCommand(BazelMode::Test, test_name_);
|
|
|
- llvm::PrettyStackTraceString stack_trace_entry(test_command.c_str());
|
|
|
- llvm::errs() << "\nTo test this file alone, run:\n " << test_command
|
|
|
- << "\n\n";
|
|
|
-
|
|
|
- ErrorOr<TestFile> test_file =
|
|
|
- ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/false,
|
|
|
- absl::GetFlag(FLAGS_autoupdate));
|
|
|
- ASSERT_TRUE(test_file.ok()) << test_file.error();
|
|
|
- auto test_filename = std::filesystem::path(test_name_.str()).filename();
|
|
|
+auto FileTestCase::TestBody() -> void {
|
|
|
+ if (absl::GetFlag(FLAGS_autoupdate) || absl::GetFlag(FLAGS_dump_output)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ CARBON_CHECK(test_info_->test_result,
|
|
|
+ "Expected test to be run prior to TestBody: {0}",
|
|
|
+ test_info_->test_name);
|
|
|
+
|
|
|
+ ASSERT_TRUE(test_info_->test_result->ok())
|
|
|
+ << test_info_->test_result->error();
|
|
|
+ auto test_filename = std::filesystem::path(test_info_->test_name).filename();
|
|
|
|
|
|
// Check success/failure against `fail_` prefixes.
|
|
|
- if (test_file->run_result.per_file_success.empty()) {
|
|
|
- CompareFailPrefix(test_filename.string(), test_file->run_result.success);
|
|
|
+ TestFile& test_file = **(test_info_->test_result);
|
|
|
+ if (test_file.run_result.per_file_success.empty()) {
|
|
|
+ CompareFailPrefix(test_filename.string(), test_file.run_result.success);
|
|
|
} else {
|
|
|
bool require_overall_failure = false;
|
|
|
for (const auto& [filename, success] :
|
|
|
- test_file->run_result.per_file_success) {
|
|
|
+ test_file.run_result.per_file_success) {
|
|
|
CompareFailPrefix(filename, success);
|
|
|
if (!success) {
|
|
|
require_overall_failure = true;
|
|
|
@@ -173,76 +225,50 @@ auto FileTestBase::TestBody() -> void {
|
|
|
}
|
|
|
|
|
|
if (require_overall_failure) {
|
|
|
- EXPECT_FALSE(test_file->run_result.success)
|
|
|
+ EXPECT_FALSE(test_file.run_result.success)
|
|
|
<< "There is a per-file failure expectation, so the overall result "
|
|
|
"should have been a failure.";
|
|
|
} else {
|
|
|
// Individual files all succeeded, so the prefix is enforced on the main
|
|
|
// test file.
|
|
|
- CompareFailPrefix(test_filename.string(), test_file->run_result.success);
|
|
|
+ CompareFailPrefix(test_filename.string(), test_file.run_result.success);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Check results. Include a reminder of the autoupdate command for any
|
|
|
- // stdout/stderr differences.
|
|
|
- std::string update_message;
|
|
|
- if (test_file->autoupdate_line_number) {
|
|
|
- update_message = llvm::formatv(
|
|
|
- "If these differences are expected, try the autoupdater:\n {0}",
|
|
|
- GetBazelCommand(BazelMode::Autoupdate, test_name_));
|
|
|
- } else {
|
|
|
- update_message =
|
|
|
- "If these differences are expected, content must be updated manually.";
|
|
|
+ // Check results. Include a reminder for NOAUTOUPDATE tests.
|
|
|
+ std::unique_ptr<testing::ScopedTrace> scoped_trace;
|
|
|
+ if (!test_file.autoupdate_line_number) {
|
|
|
+ scoped_trace = std::make_unique<testing::ScopedTrace>(
|
|
|
+ __FILE__, __LINE__,
|
|
|
+ "This file is NOAUTOUPDATE, so expected differences require manual "
|
|
|
+ "updates.");
|
|
|
}
|
|
|
- SCOPED_TRACE(update_message);
|
|
|
- if (test_file->check_subset) {
|
|
|
- EXPECT_THAT(SplitOutput(test_file->actual_stdout),
|
|
|
- IsSupersetOf(test_file->expected_stdout));
|
|
|
- EXPECT_THAT(SplitOutput(test_file->actual_stderr),
|
|
|
- IsSupersetOf(test_file->expected_stderr));
|
|
|
+ if (test_file.check_subset) {
|
|
|
+ EXPECT_THAT(SplitOutput(test_file.actual_stdout),
|
|
|
+ IsSupersetOf(test_file.expected_stdout));
|
|
|
+ EXPECT_THAT(SplitOutput(test_file.actual_stderr),
|
|
|
+ IsSupersetOf(test_file.expected_stderr));
|
|
|
|
|
|
} else {
|
|
|
- EXPECT_THAT(SplitOutput(test_file->actual_stdout),
|
|
|
- ElementsAreArray(test_file->expected_stdout));
|
|
|
- EXPECT_THAT(SplitOutput(test_file->actual_stderr),
|
|
|
- ElementsAreArray(test_file->expected_stderr));
|
|
|
+ EXPECT_THAT(SplitOutput(test_file.actual_stdout),
|
|
|
+ ElementsAreArray(test_file.expected_stdout));
|
|
|
+ EXPECT_THAT(SplitOutput(test_file.actual_stderr),
|
|
|
+ ElementsAreArray(test_file.expected_stderr));
|
|
|
}
|
|
|
|
|
|
- // If there are no other test failures, check if autoupdate would make
|
|
|
- // changes. We don't do this when there _are_ failures because the
|
|
|
- // SCOPED_TRACE already contains the autoupdate reminder.
|
|
|
- if (!HasFailure() && RunAutoupdater(this, *test_file, /*dry_run=*/true)) {
|
|
|
- ADD_FAILURE() << "Autoupdate would make changes to the file content.";
|
|
|
+ if (HasFailure()) {
|
|
|
+ llvm::errs() << "\nTo test this file alone, run:\n "
|
|
|
+ << GetBazelCommand(BazelMode::Test, test_info_->test_name)
|
|
|
+ << "\n\n";
|
|
|
+ if (test_file.autoupdate_line_number) {
|
|
|
+ llvm::errs() << "\nThis test is NOAUTOUPDATE.\n\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (test_info_->autoupdate_differs) {
|
|
|
+ ADD_FAILURE() << "Autoupdate would make changes to the file content. Run:\n"
|
|
|
+ << GetBazelCommand(BazelMode::Autoupdate,
|
|
|
+ test_info_->test_name);
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-auto FileTestBase::Autoupdate() -> ErrorOr<bool> {
|
|
|
- // Add a crash trace entry mentioning which file we're updating.
|
|
|
- std::string stack_trace_string =
|
|
|
- llvm::formatv("performing autoupdate for {0}", test_name_);
|
|
|
- llvm::PrettyStackTraceString stack_trace_entry(stack_trace_string.c_str());
|
|
|
-
|
|
|
- CARBON_ASSIGN_OR_RETURN(
|
|
|
- TestFile test_file,
|
|
|
- ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/false,
|
|
|
- absl::GetFlag(FLAGS_autoupdate)));
|
|
|
- return RunAutoupdater(this, test_file, /*dry_run=*/false);
|
|
|
-}
|
|
|
-
|
|
|
-auto FileTestBase::DumpOutput() -> ErrorOr<Success> {
|
|
|
- std::string banner(79, '=');
|
|
|
- banner.append("\n");
|
|
|
- llvm::errs() << banner << "= " << test_name_ << "\n";
|
|
|
-
|
|
|
- CARBON_ASSIGN_OR_RETURN(
|
|
|
- TestFile test_file,
|
|
|
- ProcessTestFileAndRun(this, output_mutex_, /*dump_output=*/true,
|
|
|
- absl::GetFlag(FLAGS_autoupdate)));
|
|
|
- llvm::errs() << banner << test_file.actual_stdout << banner
|
|
|
- << "= Exit with success: "
|
|
|
- << (test_file.run_result.success ? "true" : "false") << "\n"
|
|
|
- << banner;
|
|
|
- return Success();
|
|
|
}
|
|
|
|
|
|
auto FileTestBase::GetLineNumberReplacements(
|
|
|
@@ -254,48 +280,91 @@ auto FileTestBase::GetLineNumberReplacements(
|
|
|
.line_formatv = R"({0})"}};
|
|
|
}
|
|
|
|
|
|
-// Returns the tests to run.
|
|
|
-static auto GetTests() -> llvm::SmallVector<std::string> {
|
|
|
- // Prefer a user-specified list if present.
|
|
|
- auto specific_tests = absl::GetFlag(FLAGS_file_tests);
|
|
|
- if (!specific_tests.empty()) {
|
|
|
- return llvm::SmallVector<std::string>(specific_tests.begin(),
|
|
|
- specific_tests.end());
|
|
|
+// If `--file_tests` is set, transform it into a `--gtest_filter`.
|
|
|
+static auto MaybeApplyFileTestsFlag(llvm::StringRef factory_name) -> void {
|
|
|
+ if (absl::GetFlag(FLAGS_file_tests).empty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ RawStringOstream filter;
|
|
|
+ llvm::ListSeparator sep(":");
|
|
|
+ for (const auto& file : absl::GetFlag(FLAGS_file_tests)) {
|
|
|
+ filter << sep << factory_name << "." << file;
|
|
|
}
|
|
|
+ absl::SetFlag(&FLAGS_gtest_filter, filter.TakeStr());
|
|
|
+}
|
|
|
|
|
|
- // Extracts tests from the target file.
|
|
|
- CARBON_CHECK(!absl::GetFlag(FLAGS_test_targets_file).empty(),
|
|
|
- "Missing --test_targets_file.");
|
|
|
- auto content = ReadFile(absl::GetFlag(FLAGS_test_targets_file));
|
|
|
- CARBON_CHECK(content.ok(), "{0}", content.error());
|
|
|
- llvm::SmallVector<std::string> all_tests;
|
|
|
- for (llvm::StringRef file_ref : llvm::split(*content, "\n")) {
|
|
|
- if (file_ref.empty()) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- all_tests.push_back(file_ref.str());
|
|
|
+// Loads tests from the manifest file, and registers them for execution. The
|
|
|
+// vector is taken as an output parameter so that the address of entries is
|
|
|
+// stable for the factory.
|
|
|
+static auto RegisterTests(FileTestFactory* test_factory,
|
|
|
+ llvm::StringRef exe_path,
|
|
|
+ llvm::SmallVectorImpl<FileTestInfo>& tests)
|
|
|
+ -> ErrorOr<Success> {
|
|
|
+ if (absl::GetFlag(FLAGS_test_targets_file).empty()) {
|
|
|
+ return Error("Missing --test_targets_file.");
|
|
|
+ }
|
|
|
+ CARBON_ASSIGN_OR_RETURN(auto test_manifest,
|
|
|
+ ReadFile(absl::GetFlag(FLAGS_test_targets_file)));
|
|
|
+
|
|
|
+ // Prepare the vector first, so that the location of entries won't change.
|
|
|
+ for (const auto& test_name :
|
|
|
+ absl::StrSplit(test_manifest, "\n", absl::SkipEmpty())) {
|
|
|
+ tests.push_back({.test_name = std::string(test_name)});
|
|
|
+ }
|
|
|
+
|
|
|
+ // Amend entries with factory functions.
|
|
|
+ for (auto& test : tests) {
|
|
|
+ llvm::StringRef test_name = test.test_name;
|
|
|
+ test.factory_fn = [test_factory, exe_path, test_name]() {
|
|
|
+ return test_factory->factory_fn(exe_path, test_name);
|
|
|
+ };
|
|
|
+ test.registered_test = testing::RegisterTest(
|
|
|
+ test_factory->name, test_name.data(), nullptr, test_name.data(),
|
|
|
+ __FILE__, __LINE__, [&test]() { return new FileTestCase(&test); });
|
|
|
}
|
|
|
- return all_tests;
|
|
|
+ return Success();
|
|
|
}
|
|
|
|
|
|
-// Runs autoupdate for the given tests. This is multi-threaded to try to get a
|
|
|
-// little extra speed.
|
|
|
-static auto RunAutoupdate(llvm::StringRef exe_path,
|
|
|
- llvm::ArrayRef<std::string> tests,
|
|
|
- FileTestFactory& test_factory) -> int {
|
|
|
+// Implements the parallel test execution through gtest's listener support.
|
|
|
+class FileTestEventListener : public testing::EmptyTestEventListener {
|
|
|
+ public:
|
|
|
+ explicit FileTestEventListener(llvm::MutableArrayRef<FileTestInfo> tests)
|
|
|
+ : tests_(tests) {}
|
|
|
+
|
|
|
+ // Runs test during start, after `should_run` is initialized. This is
|
|
|
+ // multi-threaded to get extra speed.
|
|
|
+ auto OnTestProgramStart(const testing::UnitTest& /*unit_test*/)
|
|
|
+ -> void override;
|
|
|
+
|
|
|
+ private:
|
|
|
+ llvm::MutableArrayRef<FileTestInfo> tests_;
|
|
|
+};
|
|
|
+
|
|
|
+auto FileTestEventListener::OnTestProgramStart(
|
|
|
+ const testing::UnitTest& /*unit_test*/) -> void {
|
|
|
llvm::CrashRecoveryContext::Enable();
|
|
|
llvm::DefaultThreadPool pool(
|
|
|
- {.ThreadsRequested = absl::GetFlag(FLAGS_threads)});
|
|
|
+ {.ThreadsRequested = absl::GetFlag(FLAGS_dump_output)
|
|
|
+ ? 1
|
|
|
+ : absl::GetFlag(FLAGS_threads)});
|
|
|
+ if (!absl::GetFlag(FLAGS_dump_output)) {
|
|
|
+ llvm::errs() << "Running tests with " << pool.getMaxConcurrency()
|
|
|
+ << " thread(s)\n";
|
|
|
+ }
|
|
|
|
|
|
// Guard access to both `llvm::errs` and `crashed`.
|
|
|
- std::mutex mutex;
|
|
|
bool crashed = false;
|
|
|
+ std::mutex output_mutex;
|
|
|
+
|
|
|
+ for (auto& test : tests_) {
|
|
|
+ if (!test.registered_test->should_run()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- for (const auto& test_name : tests) {
|
|
|
- pool.async([&test_factory, &mutex, &exe_path, &crashed, test_name] {
|
|
|
+ pool.async([&output_mutex, &crashed, &test] {
|
|
|
// If any thread crashed, don't try running more.
|
|
|
{
|
|
|
- std::unique_lock<std::mutex> lock(mutex);
|
|
|
+ std::unique_lock<std::mutex> lock(output_mutex);
|
|
|
if (crashed) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -307,19 +376,46 @@ static auto RunAutoupdate(llvm::StringRef exe_path,
|
|
|
llvm::CrashRecoveryContext crc;
|
|
|
crc.DumpStackAndCleanupOnFailure = true;
|
|
|
bool thread_crashed = !crc.RunSafely([&] {
|
|
|
- std::unique_ptr<FileTestBase> test(
|
|
|
- test_factory.factory_fn(exe_path, &mutex, test_name));
|
|
|
- auto result = test->Autoupdate();
|
|
|
+ std::unique_ptr<FileTestBase> test_instance(test.factory_fn());
|
|
|
+
|
|
|
+ // Add a crash trace entry with the single-file test command.
|
|
|
+ std::string test_command =
|
|
|
+ GetBazelCommand(BazelMode::Test, test.test_name);
|
|
|
+ llvm::PrettyStackTraceString stack_trace_entry(test_command.c_str());
|
|
|
+
|
|
|
+ if (absl::GetFlag(FLAGS_dump_output)) {
|
|
|
+ std::unique_lock<std::mutex> lock(output_mutex);
|
|
|
+ llvm::errs() << "\n--- Dumping: " << test.test_name << "\n\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ test.test_result = ProcessTestFileAndRun(
|
|
|
+ test_instance.get(), &output_mutex,
|
|
|
+ absl::GetFlag(FLAGS_dump_output), absl::GetFlag(FLAGS_autoupdate));
|
|
|
+
|
|
|
+ if (!test.test_result->ok()) {
|
|
|
+ std::unique_lock<std::mutex> lock(output_mutex);
|
|
|
+ llvm::errs() << "\n" << test.test_result->error().message() << "\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- std::unique_lock<std::mutex> lock(mutex);
|
|
|
- if (result.ok()) {
|
|
|
- llvm::errs() << (*result ? "!" : ".");
|
|
|
+ test.autoupdate_differs =
|
|
|
+ RunAutoupdater(test_instance.get(), **test.test_result,
|
|
|
+ /*dry_run=*/!absl::GetFlag(FLAGS_autoupdate));
|
|
|
+
|
|
|
+ std::unique_lock<std::mutex> lock(output_mutex);
|
|
|
+ if (absl::GetFlag(FLAGS_dump_output)) {
|
|
|
+ llvm::outs().flush();
|
|
|
+ const TestFile& test_file = **test.test_result;
|
|
|
+ llvm::errs() << "\n--- Exit with success: "
|
|
|
+ << (test_file.run_result.success ? "true" : "false")
|
|
|
+ << "\n--- Autoupdate differs: "
|
|
|
+ << (test.autoupdate_differs ? "true" : "false") << "\n";
|
|
|
} else {
|
|
|
- llvm::errs() << "\n" << result.error().message() << "\n";
|
|
|
+ llvm::errs() << (test.autoupdate_differs ? "!" : ".");
|
|
|
}
|
|
|
});
|
|
|
if (thread_crashed) {
|
|
|
- std::unique_lock<std::mutex> lock(mutex);
|
|
|
+ std::unique_lock<std::mutex> lock(output_mutex);
|
|
|
crashed = true;
|
|
|
}
|
|
|
});
|
|
|
@@ -332,23 +428,21 @@ static auto RunAutoupdate(llvm::StringRef exe_path,
|
|
|
std::abort();
|
|
|
}
|
|
|
llvm::errs() << "\nDone!\n";
|
|
|
- return EXIT_SUCCESS;
|
|
|
}
|
|
|
|
|
|
// Implements main() within the Carbon::Testing namespace for convenience.
|
|
|
-static auto Main(int argc, char** argv) -> int {
|
|
|
+static auto Main(int argc, char** argv) -> ErrorOr<int> {
|
|
|
Carbon::InitLLVM init_llvm(argc, argv);
|
|
|
testing::InitGoogleTest(&argc, argv);
|
|
|
auto args = absl::ParseCommandLine(argc, argv);
|
|
|
|
|
|
if (args.size() > 1) {
|
|
|
- llvm::errs() << "Unexpected arguments:";
|
|
|
+ ErrorBuilder b;
|
|
|
+ b << "Unexpected arguments:";
|
|
|
for (char* arg : llvm::ArrayRef(args).drop_front()) {
|
|
|
- llvm::errs() << " ";
|
|
|
- llvm::errs().write_escaped(arg);
|
|
|
+ b << " " << FormatEscaped(arg);
|
|
|
}
|
|
|
- llvm::errs() << "\n";
|
|
|
- return EXIT_FAILURE;
|
|
|
+ return b;
|
|
|
}
|
|
|
|
|
|
std::string exe_path = FindExecutablePath(argv[0]);
|
|
|
@@ -358,51 +452,44 @@ static auto Main(int argc, char** argv) -> int {
|
|
|
// on Windows, but POSIX requires it to be 0.
|
|
|
if (std::error_code error =
|
|
|
llvm::sys::Process::SafelyCloseFileDescriptor(0)) {
|
|
|
- llvm::errs() << "Unable to close standard input: " << error.message()
|
|
|
- << "\n";
|
|
|
- return EXIT_FAILURE;
|
|
|
+ return Error("Unable to close standard input: " + error.message());
|
|
|
}
|
|
|
if (std::error_code error =
|
|
|
llvm::sys::Process::FixupStandardFileDescriptors()) {
|
|
|
- llvm::errs() << "Unable to correct standard file descriptors: "
|
|
|
- << error.message() << "\n";
|
|
|
- return EXIT_FAILURE;
|
|
|
+ return Error("Unable to correct standard file descriptors: " +
|
|
|
+ error.message());
|
|
|
}
|
|
|
if (absl::GetFlag(FLAGS_autoupdate) && absl::GetFlag(FLAGS_dump_output)) {
|
|
|
- llvm::errs() << "--autoupdate and --dump_output are mutually exclusive.\n";
|
|
|
- return EXIT_FAILURE;
|
|
|
+ return Error("--autoupdate and --dump_output are mutually exclusive.");
|
|
|
}
|
|
|
|
|
|
- llvm::SmallVector<std::string> tests = GetTests();
|
|
|
auto test_factory = GetFileTestFactory();
|
|
|
- if (absl::GetFlag(FLAGS_autoupdate)) {
|
|
|
- return RunAutoupdate(exe_path, tests, test_factory);
|
|
|
- } else if (absl::GetFlag(FLAGS_dump_output)) {
|
|
|
- for (const auto& test_name : tests) {
|
|
|
- std::unique_ptr<FileTestBase> test(
|
|
|
- test_factory.factory_fn(exe_path, nullptr, test_name));
|
|
|
- auto result = test->DumpOutput();
|
|
|
- if (!result.ok()) {
|
|
|
- llvm::errs() << "\n" << result.error().message() << "\n";
|
|
|
- }
|
|
|
- }
|
|
|
- llvm::errs() << "\nDone!\n";
|
|
|
- return EXIT_SUCCESS;
|
|
|
- } else {
|
|
|
- for (const std::string& test_name : tests) {
|
|
|
- testing::RegisterTest(
|
|
|
- test_factory.name, test_name.c_str(), nullptr, test_name.c_str(),
|
|
|
- __FILE__, __LINE__,
|
|
|
- [&test_factory, &exe_path, test_name = test_name]() {
|
|
|
- return test_factory.factory_fn(exe_path, nullptr, test_name);
|
|
|
- });
|
|
|
- }
|
|
|
- return RUN_ALL_TESTS();
|
|
|
+
|
|
|
+ MaybeApplyFileTestsFlag(test_factory.name);
|
|
|
+
|
|
|
+ // Inline 0 entries because it will always be too large to store on the stack.
|
|
|
+ llvm::SmallVector<FileTestInfo, 0> tests;
|
|
|
+ CARBON_RETURN_IF_ERROR(RegisterTests(&test_factory, exe_path, tests));
|
|
|
+
|
|
|
+ testing::TestEventListeners& listeners =
|
|
|
+ testing::UnitTest::GetInstance()->listeners();
|
|
|
+ if (absl::GetFlag(FLAGS_autoupdate) || absl::GetFlag(FLAGS_dump_output)) {
|
|
|
+ // Suppress all of the default output.
|
|
|
+ delete listeners.Release(listeners.default_result_printer());
|
|
|
}
|
|
|
+ // Use a listener to run tests in parallel.
|
|
|
+ listeners.Append(new FileTestEventListener(tests));
|
|
|
+
|
|
|
+ return RUN_ALL_TESTS();
|
|
|
}
|
|
|
|
|
|
} // namespace Carbon::Testing
|
|
|
|
|
|
auto main(int argc, char** argv) -> int {
|
|
|
- return Carbon::Testing::Main(argc, argv);
|
|
|
+ if (auto result = Carbon::Testing::Main(argc, argv); result.ok()) {
|
|
|
+ return *result;
|
|
|
+ } else {
|
|
|
+ llvm::errs() << result.error() << "\n";
|
|
|
+ return EXIT_FAILURE;
|
|
|
+ }
|
|
|
}
|