Merge toolchain file_test children in order to improve linking. (#3206)

Specifically this should improve linking by producing one large binary
instead of one-per-directory. The inclusion of the driver hits the size
issue. Separating out things which have more llvm deps has been
discussed, but I'm not doing that here because I think the semantics
layer will need to depend on clang for interop, and we'd lose a lot of
the benefits that way. Also, having just one place to look seems
simpler.

Includes supporting changes to file_test infrastructure, the most
significant of which is probably passing tests via file instead of a
really large args thing, using a custom rule to do that. That's because
dealing with the layered filegroups that allow the toolchain setup is
more complicated, and this approach scales well.

Combined test time is ~9s, so not sharding right now.

I wasn't sure if people would prefer having the autoupdate script under
testing, so I left it alone for now.
This commit is contained in:
Jon Ross-Perkins
2023-09-11 17:54:41 +00:00
committed by GitHub
parent 5fc013b38f
commit 8aa3d960f5
21 changed files with 284 additions and 408 deletions
+56 -51
View File
@@ -6,6 +6,7 @@
#include <filesystem>
#include <fstream>
#include <string>
#include <utility>
#include "absl/flags/flag.h"
@@ -18,7 +19,10 @@
#include "llvm/Support/MemoryBuffer.h"
ABSL_FLAG(std::vector<std::string>, file_tests, {},
"A comma-separated list of tests for file_test infrastructure.");
"A comma-separated list of repo-relative names of test files. "
"Overrides test_targets_file.");
ABSL_FLAG(std::string, test_targets_file, "",
"A path to a file containing repo-relative names of test files.");
ABSL_FLAG(bool, autoupdate, false,
"Instead of verifying files match test output, autoupdate files "
"based on test output.");
@@ -31,7 +35,7 @@ using ::testing::MatchesRegex;
using ::testing::StrEq;
// Reads a file to string.
static auto ReadFile(std::filesystem::path path) -> std::string {
static auto ReadFile(std::string_view path) -> std::string {
std::ifstream proto_file(path);
std::stringstream buffer;
buffer << proto_file.rdbuf();
@@ -57,13 +61,14 @@ auto FileTestBase::TestBody() -> void {
CARBON_CHECK(target);
// This advice overrides the --file_tests flag provided by the file_test rule.
llvm::errs() << "\nTo test this file alone, run:\n bazel test " << target
<< " --test_arg=--file_tests=" << GetTestFilename() << "\n\n";
<< " --test_arg=--file_tests=" << test_name_ << "\n\n";
TestContext context;
auto run_result = ProcessTestFileAndRun(context);
ASSERT_TRUE(run_result.ok()) << run_result.error();
ValidateRun();
EXPECT_THAT(!llvm::StringRef(path().filename()).starts_with("fail_"),
auto test_filename = std::filesystem::path(test_name_.str()).filename();
EXPECT_THAT(!llvm::StringRef(test_filename).starts_with("fail_"),
Eq(context.exit_with_success))
<< "Tests should be prefixed with `fail_` if and only if running them "
"is expected to fail.";
@@ -87,7 +92,7 @@ auto FileTestBase::Autoupdate() -> ErrorOr<bool> {
TestContext context;
auto run_result = ProcessTestFileAndRun(context);
if (!run_result.ok()) {
return ErrorBuilder() << "Error updating " << GetTestFilename() << ": "
return ErrorBuilder() << "Error updating " << test_name_ << ": "
<< run_result.error();
}
if (!context.autoupdate_line_number) {
@@ -110,8 +115,9 @@ auto FileTestBase::Autoupdate() -> ErrorOr<bool> {
}
return AutoupdateFileTest(
path(), context.input_content, filenames, *context.autoupdate_line_number,
context.non_check_lines, context.stdout, context.stderr,
std::filesystem::absolute(test_name_.str()), context.input_content,
filenames, *context.autoupdate_line_number, context.non_check_lines,
context.stdout, context.stderr,
GetLineNumberReplacement(filenames_for_line_number),
[&](std::string& line) { DoExtraCheckReplacements(line); });
}
@@ -127,7 +133,7 @@ auto FileTestBase::GetLineNumberReplacement(
auto FileTestBase::ProcessTestFileAndRun(TestContext& context)
-> ErrorOr<Success> {
// Store the file so that test_files can use references to content.
context.input_content = ReadFile(path());
context.input_content = ReadFile(test_name_);
// Load expected output.
CARBON_RETURN_IF_ERROR(ProcessTestFile(context));
@@ -330,8 +336,9 @@ auto FileTestBase::ProcessTestFile(TestContext& context) -> ErrorOr<Success> {
file_content.end() - current_file_start)));
} else {
// If no file splitting happened, use the main file as the test file.
context.test_files.push_back(
TestFile(path().filename().string(), file_content));
// There will always be a `/` unless tests are in the repo root.
context.test_files.push_back(TestFile(
test_name_.drop_front(test_name_.rfind("/") + 1).str(), file_content));
}
// Assume there is always a suffix `\n` in output.
@@ -431,16 +438,31 @@ auto FileTestBase::TransformExpectation(int line_index, llvm::StringRef in)
return Matcher<std::string>{MatchesRegex(str)};
}
auto FileTestBase::GetTestFilename() -> std::string {
const char* src_dir = getenv("TEST_SRCDIR");
CARBON_CHECK(src_dir);
return path().lexically_relative(
std::filesystem::path(src_dir).append("carbon"));
// 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());
}
// 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));
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());
}
return all_tests;
}
} // namespace Carbon::Testing
auto main(int argc, char** argv) -> int {
// Implements main() within the Carbon::Testing namespace for convenience.
static auto Main(int argc, char** argv) -> int {
absl::ParseCommandLine(argc, argv);
testing::InitGoogleTest(&argc, argv);
llvm::setBugReportMsg(
@@ -454,46 +476,29 @@ auto main(int argc, char** argv) -> int {
return EXIT_FAILURE;
}
// Configure the base directory for test names.
const char* target = getenv("TEST_TARGET");
CARBON_CHECK(target);
llvm::StringRef target_dir = target;
std::error_code ec;
std::filesystem::path working_dir = std::filesystem::current_path(ec);
CARBON_CHECK(!ec) << ec.message();
// Leaves one slash.
CARBON_CHECK(target_dir.consume_front("/"));
target_dir = target_dir.substr(0, target_dir.rfind(":"));
std::string base_dir = working_dir.string() + target_dir.str() + "/";
auto test_factory = Carbon::Testing::GetFileTestFactory();
for (const auto& file_test : absl::GetFlag(FLAGS_file_tests)) {
// Pass the absolute path to the factory function.
auto path = std::filesystem::absolute(file_test, ec);
CARBON_CHECK(!ec) << file_test << ": " << ec.message();
CARBON_CHECK(llvm::StringRef(path.string()).starts_with(base_dir))
<< "\n " << path << "\n should start with\n " << base_dir;
if (absl::GetFlag(FLAGS_autoupdate)) {
std::unique_ptr<Carbon::Testing::FileTestBase> test(
test_factory.factory_fn(path));
llvm::SmallVector<std::string> tests = GetTests();
auto test_factory = GetFileTestFactory();
if (absl::GetFlag(FLAGS_autoupdate)) {
for (const auto& test_name : tests) {
std::unique_ptr<FileTestBase> test(test_factory.factory_fn(test_name));
auto result = test->Autoupdate();
llvm::errs() << (result.ok() ? (*result ? "!" : ".")
: result.error().message());
} else {
std::string test_name = path.string().substr(base_dir.size());
testing::RegisterTest(test_factory.name, test_name.c_str(), nullptr,
test_name.c_str(), __FILE__, __LINE__,
[=]() { return test_factory.factory_fn(path); });
}
}
if (absl::GetFlag(FLAGS_autoupdate)) {
llvm::errs() << "\nDone!\n";
}
if (absl::GetFlag(FLAGS_autoupdate)) {
return EXIT_SUCCESS;
} else {
for (llvm::StringRef test_name : tests) {
testing::RegisterTest(test_factory.name, test_name.data(), nullptr,
test_name.data(), __FILE__, __LINE__,
[&test_factory, test_name = test_name]() {
return test_factory.factory_fn(test_name);
});
}
return RUN_ALL_TESTS();
}
}
} // namespace Carbon::Testing
auto main(int argc, char** argv) -> int { Carbon::Testing::Main(argc, argv); }