|
@@ -22,6 +22,7 @@
|
|
|
#include "testing/file_test/file_test_base.h"
|
|
#include "testing/file_test/file_test_base.h"
|
|
|
|
|
|
|
|
#include <atomic>
|
|
#include <atomic>
|
|
|
|
|
+#include <chrono>
|
|
|
#include <cstdlib>
|
|
#include <cstdlib>
|
|
|
#include <filesystem>
|
|
#include <filesystem>
|
|
|
#include <functional>
|
|
#include <functional>
|
|
@@ -65,6 +66,9 @@ ABSL_FLAG(unsigned int, threads, 0,
|
|
|
ABSL_FLAG(bool, dump_output, false,
|
|
ABSL_FLAG(bool, dump_output, false,
|
|
|
"Instead of verifying files match test output, directly dump output "
|
|
"Instead of verifying files match test output, directly dump output "
|
|
|
"to stderr.");
|
|
"to stderr.");
|
|
|
|
|
+ABSL_FLAG(int, print_slowest_tests, 5,
|
|
|
|
|
+ "The number of tests to print when showing slowest tests. Set to 0 "
|
|
|
|
|
+ "to disabling printing. Set to -1 to print all tests.");
|
|
|
|
|
|
|
|
namespace Carbon::Testing {
|
|
namespace Carbon::Testing {
|
|
|
|
|
|
|
@@ -86,6 +90,9 @@ struct FileTestInfo {
|
|
|
// changed) the test file. This may be true even if output passes test
|
|
// changed) the test file. This may be true even if output passes test
|
|
|
// expectations.
|
|
// expectations.
|
|
|
bool autoupdate_differs = false;
|
|
bool autoupdate_differs = false;
|
|
|
|
|
+
|
|
|
|
|
+ // Time spent in the test total, including processing and autoupdate.
|
|
|
|
|
+ std::chrono::milliseconds elapsed_ms = std::chrono::milliseconds(0);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// Adapts a `FileTestBase` instance to gtest for outputting results.
|
|
// Adapts a `FileTestBase` instance to gtest for outputting results.
|
|
@@ -363,6 +370,7 @@ static auto SingleThreaded(llvm::ArrayRef<FileTestInfo> tests) -> bool {
|
|
|
// CrashRecoveryContext.
|
|
// CrashRecoveryContext.
|
|
|
static auto RunSingleTestHelper(FileTestInfo& test, FileTestBase& test_instance)
|
|
static auto RunSingleTestHelper(FileTestInfo& test, FileTestBase& test_instance)
|
|
|
-> void {
|
|
-> void {
|
|
|
|
|
+ Timer timer;
|
|
|
// Add a crash trace entry with the single-file test command.
|
|
// Add a crash trace entry with the single-file test command.
|
|
|
std::string test_command = GetBazelCommand(BazelMode::Test, test.test_name);
|
|
std::string test_command = GetBazelCommand(BazelMode::Test, test.test_name);
|
|
|
llvm::PrettyStackTraceString stack_trace_entry(test_command.c_str());
|
|
llvm::PrettyStackTraceString stack_trace_entry(test_command.c_str());
|
|
@@ -372,10 +380,12 @@ static auto RunSingleTestHelper(FileTestInfo& test, FileTestBase& test_instance)
|
|
|
!err.ok()) {
|
|
!err.ok()) {
|
|
|
test.test_result = std::move(err).error();
|
|
test.test_result = std::move(err).error();
|
|
|
}
|
|
}
|
|
|
|
|
+ test.elapsed_ms += timer.elapsed_ms();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Runs a single test. Uses a CrashRecoveryContext, and returns false on a
|
|
// Runs a single test. Uses a CrashRecoveryContext, and returns false on a
|
|
|
-// crash.
|
|
|
|
|
|
|
+// crash. For test_elapsed_ms, try to exclude time spent waiting on
|
|
|
|
|
+// output_mutex.
|
|
|
static auto RunSingleTest(FileTestInfo& test, bool single_threaded,
|
|
static auto RunSingleTest(FileTestInfo& test, bool single_threaded,
|
|
|
std::mutex& output_mutex) -> bool {
|
|
std::mutex& output_mutex) -> bool {
|
|
|
std::unique_ptr<FileTestBase> test_instance(test.factory_fn());
|
|
std::unique_ptr<FileTestBase> test_instance(test.factory_fn());
|
|
@@ -386,8 +396,11 @@ static auto RunSingleTest(FileTestInfo& test, bool single_threaded,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Load expected output.
|
|
// Load expected output.
|
|
|
|
|
+ Timer process_timer;
|
|
|
test.test_result = ProcessTestFile(test_instance->test_name(),
|
|
test.test_result = ProcessTestFile(test_instance->test_name(),
|
|
|
absl::GetFlag(FLAGS_autoupdate));
|
|
absl::GetFlag(FLAGS_autoupdate));
|
|
|
|
|
+ test.elapsed_ms = process_timer.elapsed_ms();
|
|
|
|
|
+
|
|
|
if (test.test_result->ok()) {
|
|
if (test.test_result->ok()) {
|
|
|
// Execution must be serialized for either serial tests or console
|
|
// Execution must be serialized for either serial tests or console
|
|
|
// output.
|
|
// output.
|
|
@@ -418,9 +431,11 @@ static auto RunSingleTest(FileTestInfo& test, bool single_threaded,
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ Timer autoupdate_timer;
|
|
|
test.autoupdate_differs =
|
|
test.autoupdate_differs =
|
|
|
RunAutoupdater(test_instance.get(), **test.test_result,
|
|
RunAutoupdater(test_instance.get(), **test.test_result,
|
|
|
/*dry_run=*/!absl::GetFlag(FLAGS_autoupdate));
|
|
/*dry_run=*/!absl::GetFlag(FLAGS_autoupdate));
|
|
|
|
|
+ test.elapsed_ms += autoupdate_timer.elapsed_ms();
|
|
|
|
|
|
|
|
std::unique_lock<std::mutex> lock(output_mutex);
|
|
std::unique_lock<std::mutex> lock(output_mutex);
|
|
|
if (absl::GetFlag(FLAGS_dump_output)) {
|
|
if (absl::GetFlag(FLAGS_dump_output)) {
|
|
@@ -447,8 +462,11 @@ auto FileTestEventListener::OnTestProgramStart(
|
|
|
} else {
|
|
} else {
|
|
|
// Enable the CRC for use in `RunSingleTest`.
|
|
// Enable the CRC for use in `RunSingleTest`.
|
|
|
llvm::CrashRecoveryContext::Enable();
|
|
llvm::CrashRecoveryContext::Enable();
|
|
|
- pool = std::make_unique<llvm::DefaultThreadPool>(llvm::ThreadPoolStrategy{
|
|
|
|
|
- .ThreadsRequested = absl::GetFlag(FLAGS_threads)});
|
|
|
|
|
|
|
+ llvm::ThreadPoolStrategy thread_strategy = {
|
|
|
|
|
+ .ThreadsRequested = absl::GetFlag(FLAGS_threads),
|
|
|
|
|
+ // Disable hyper threads to reduce contention.
|
|
|
|
|
+ .UseHyperThreads = false};
|
|
|
|
|
+ pool = std::make_unique<llvm::DefaultThreadPool>(thread_strategy);
|
|
|
}
|
|
}
|
|
|
if (!absl::GetFlag(FLAGS_dump_output)) {
|
|
if (!absl::GetFlag(FLAGS_dump_output)) {
|
|
|
llvm::errs() << "Running tests with " << pool->getMaxConcurrency()
|
|
llvm::errs() << "Running tests with " << pool->getMaxConcurrency()
|
|
@@ -457,12 +475,15 @@ auto FileTestEventListener::OnTestProgramStart(
|
|
|
|
|
|
|
|
// Guard access to output (stdout and stderr).
|
|
// Guard access to output (stdout and stderr).
|
|
|
std::mutex output_mutex;
|
|
std::mutex output_mutex;
|
|
|
- std::atomic<bool> crashed = false;
|
|
|
|
|
|
|
|
|
|
|
|
+ std::atomic<bool> crashed = false;
|
|
|
|
|
+ Timer all_timer;
|
|
|
|
|
+ int run_count = 0;
|
|
|
for (auto& test : tests_) {
|
|
for (auto& test : tests_) {
|
|
|
if (!test.registered_test->should_run()) {
|
|
if (!test.registered_test->should_run()) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
+ ++run_count;
|
|
|
|
|
|
|
|
pool->async([&] {
|
|
pool->async([&] {
|
|
|
// If any thread crashed, don't try running more.
|
|
// If any thread crashed, don't try running more.
|
|
@@ -482,7 +503,37 @@ auto FileTestEventListener::OnTestProgramStart(
|
|
|
// We expect to have leaked memory if one or more of our tests crashed.
|
|
// We expect to have leaked memory if one or more of our tests crashed.
|
|
|
std::abort();
|
|
std::abort();
|
|
|
}
|
|
}
|
|
|
- llvm::errs() << "\nDone!\n";
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate the total test time.
|
|
|
|
|
+ auto all_elapsed_ms = all_timer.elapsed_ms();
|
|
|
|
|
+ auto total_elapsed_ms = std::chrono::milliseconds(0);
|
|
|
|
|
+ for (auto& test : tests_) {
|
|
|
|
|
+ total_elapsed_ms += test.elapsed_ms;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ llvm::errs() << "\nRan " << run_count << " tests in "
|
|
|
|
|
+ << all_elapsed_ms.count() << " ms wall time, "
|
|
|
|
|
+ << total_elapsed_ms.count() << " ms across threads\n";
|
|
|
|
|
+
|
|
|
|
|
+ // When there are multiple tests, give additional timing details, particularly
|
|
|
|
|
+ // slowest tests.
|
|
|
|
|
+ auto print_slowest_tests = absl::GetFlag(FLAGS_print_slowest_tests);
|
|
|
|
|
+ if (run_count > 1 && print_slowest_tests != 0) {
|
|
|
|
|
+ llvm::errs() << " Slowest tests:\n";
|
|
|
|
|
+ llvm::sort(tests_, [](const FileTestInfo& lhs, const FileTestInfo& rhs) {
|
|
|
|
|
+ return lhs.elapsed_ms > rhs.elapsed_ms;
|
|
|
|
|
+ });
|
|
|
|
|
+ int count = print_slowest_tests > 0 ? print_slowest_tests : run_count;
|
|
|
|
|
+ for (const auto& test : tests_.take_front(count)) {
|
|
|
|
|
+ std::chrono::milliseconds run_ms(0);
|
|
|
|
|
+ if (test.test_result && test.test_result->ok()) {
|
|
|
|
|
+ run_ms = test.test_result.value()->run_elapsed_ms;
|
|
|
|
|
+ }
|
|
|
|
|
+ llvm::errs() << " - " << test.test_name << ": "
|
|
|
|
|
+ << test.elapsed_ms.count() << " ms, " << run_ms.count()
|
|
|
|
|
+ << " ms in Run\n";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Implements main() within the Carbon::Testing namespace for convenience.
|
|
// Implements main() within the Carbon::Testing namespace for convenience.
|