diff --git a/testing/file_test/file_test_base.cpp b/testing/file_test/file_test_base.cpp index 446135932a6c..71c2f7011f51 100644 --- a/testing/file_test/file_test_base.cpp +++ b/testing/file_test/file_test_base.cpp @@ -20,6 +20,8 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/ThreadPool.h" #include "testing/file_test/autoupdate.h" ABSL_FLAG(std::vector, file_tests, {}, @@ -30,6 +32,9 @@ ABSL_FLAG(std::string, test_targets_file, "", ABSL_FLAG(bool, autoupdate, false, "Instead of verifying files match test output, autoupdate files " "based on test output."); +ABSL_FLAG(unsigned int, threads, 0, + "Number of threads to use when autoupdating tests, or 0 to " + "automatically determine a thread count."); namespace Carbon::Testing { @@ -720,18 +725,33 @@ static auto Main(int argc, char** argv) -> int { return EXIT_FAILURE; } + // Tests might try to read from stdin. Ensure those reads fail by closing + // stdin and reopening it as /dev/null. Note that STDIN_FILENO doesn't exist + // on Windows, but POSIX requires it to be 0. + llvm::sys::Process::SafelyCloseFileDescriptor(0); + llvm::sys::Process::FixupStandardFileDescriptors(); + llvm::SmallVector tests = GetTests(); auto test_factory = GetFileTestFactory(); if (absl::GetFlag(FLAGS_autoupdate)) { + llvm::ThreadPool pool({.ThreadsRequested = absl::GetFlag(FLAGS_threads)}); + std::mutex errs_mutex; + for (const auto& test_name : tests) { - std::unique_ptr test(test_factory.factory_fn(test_name)); - auto result = test->Autoupdate(); - if (result.ok()) { - llvm::errs() << (*result ? "!" : "."); - } else { - llvm::errs() << result.error().message() << "\n"; - } + pool.async([&test_factory, &errs_mutex, test_name] { + std::unique_ptr test(test_factory.factory_fn(test_name)); + auto result = test->Autoupdate(); + + // Guard access to llvm::errs, which is not thread-safe. + std::unique_lock lock(errs_mutex); + if (result.ok()) { + llvm::errs() << (*result ? "!" : "."); + } else { + llvm::errs() << "\n" << result.error().message() << "\n"; + } + }); } + pool.wait(); llvm::errs() << "\nDone!\n"; return EXIT_SUCCESS; } else { diff --git a/toolchain/autoupdate_testdata.py b/toolchain/autoupdate_testdata.py index a472e2bd43bc..414901ed29d2 100755 --- a/toolchain/autoupdate_testdata.py +++ b/toolchain/autoupdate_testdata.py @@ -43,7 +43,7 @@ def main() -> None: argv.append("--file_tests=" + ",".join(file_tests)) # Provide an empty stdin so that the driver tests that read from stdin # don't block waiting for input. This matches the behavior of `bazel test`. - subprocess.run(argv, check=True, stdin=subprocess.DEVNULL) + subprocess.run(argv, check=True) if __name__ == "__main__":