mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
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:
@@ -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); }
|
||||
|
||||
Reference in New Issue
Block a user