From 8aa3d960f56560d238ba0d258bbb88cc6c49733f Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Mon, 11 Sep 2023 10:54:41 -0700 Subject: [PATCH] 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. --- explorer/file_test.cpp | 6 +- testing/file_test/file_test_base.cpp | 107 +++++++++++----------- testing/file_test/file_test_base.h | 21 ++--- testing/file_test/file_test_base_test.cpp | 2 +- testing/file_test/rules.bzl | 49 ++++++++-- toolchain/autoupdate_testdata.py | 67 ++++++-------- toolchain/check/BUILD | 16 +--- toolchain/check/check_file_test.cpp | 26 ------ toolchain/codegen/BUILD | 16 +--- toolchain/codegen/codegen_file_test.cpp | 26 ------ toolchain/driver/BUILD | 27 +----- toolchain/driver/driver_file_test.cpp | 33 ------- toolchain/driver/driver_file_test_base.h | 34 ------- toolchain/lex/BUILD | 17 +--- toolchain/lex/lex_file_test.cpp | 46 ---------- toolchain/lower/BUILD | 16 +--- toolchain/lower/lower_file_test.cpp | 26 ------ toolchain/parse/BUILD | 16 +--- toolchain/parse/parse_file_test.cpp | 26 ------ toolchain/testing/BUILD | 20 ++++ toolchain/testing/file_test.cpp | 95 +++++++++++++++++++ 21 files changed, 284 insertions(+), 408 deletions(-) delete mode 100644 toolchain/check/check_file_test.cpp delete mode 100644 toolchain/codegen/codegen_file_test.cpp delete mode 100644 toolchain/driver/driver_file_test.cpp delete mode 100644 toolchain/driver/driver_file_test_base.h delete mode 100644 toolchain/lex/lex_file_test.cpp delete mode 100644 toolchain/lower/lower_file_test.cpp delete mode 100644 toolchain/parse/parse_file_test.cpp create mode 100644 toolchain/testing/file_test.cpp diff --git a/explorer/file_test.cpp b/explorer/file_test.cpp index 8d76d7a91b81..9107de492748 100644 --- a/explorer/file_test.cpp +++ b/explorer/file_test.cpp @@ -18,8 +18,8 @@ namespace { class ExplorerFileTest : public FileTestBase { public: - explicit ExplorerFileTest(std::filesystem::path path) - : FileTestBase(std::move(path)), + explicit ExplorerFileTest(llvm::StringRef test_name) + : FileTestBase(test_name), prelude_line_re_(R"(prelude.carbon:(\d+))"), timing_re_(R"((Time elapsed in \w+: )\d+(ms))") { CARBON_CHECK(prelude_line_re_.ok()) << prelude_line_re_.error(); @@ -100,7 +100,7 @@ class ExplorerFileTest : public FileTestBase { private: // Trace output is directly checked for a few tests. auto check_trace_output() -> bool { - return path().string().find("/trace/") != std::string::npos; + return test_name().find("/trace/") != std::string::npos; } TestRawOstream trace_stream_; diff --git a/testing/file_test/file_test_base.cpp b/testing/file_test/file_test_base.cpp index 9d0456a88806..087042864f9e 100644 --- a/testing/file_test/file_test_base.cpp +++ b/testing/file_test/file_test_base.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "absl/flags/flag.h" @@ -18,7 +19,10 @@ #include "llvm/Support/MemoryBuffer.h" ABSL_FLAG(std::vector, 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 { 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 { } 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 { // 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 { 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{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 { + // Prefer a user-specified list if present. + auto specific_tests = absl::GetFlag(FLAGS_file_tests); + if (!specific_tests.empty()) { + return llvm::SmallVector(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 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 test( - test_factory.factory_fn(path)); + llvm::SmallVector tests = GetTests(); + auto test_factory = GetFileTestFactory(); + if (absl::GetFlag(FLAGS_autoupdate)) { + for (const auto& test_name : tests) { + std::unique_ptr 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); } diff --git a/testing/file_test/file_test_base.h b/testing/file_test/file_test_base.h index ef4d25905997..a4c3f39d4381 100644 --- a/testing/file_test/file_test_base.h +++ b/testing/file_test/file_test_base.h @@ -8,7 +8,6 @@ #include #include -#include #include #include "common/error.h" @@ -43,7 +42,7 @@ class FileTestBase : public testing::Test { // Provided for child class convenience. using LineNumberReplacement = FileTestLineNumberReplacement; - explicit FileTestBase(std::filesystem::path path) : path_(std::move(path)) {} + explicit FileTestBase(llvm::StringRef test_name) : test_name_(test_name) {} // Implemented by children to run the test. For example, TestBody validates // stdout and stderr. Children should use fs for file content, and may add @@ -82,8 +81,8 @@ class FileTestBase : public testing::Test { // Runs the test and autoupdates checks. Returns true if updated. auto Autoupdate() -> ErrorOr; - // Returns the full path of the file being tested. - auto path() -> const std::filesystem::path& { return path_; }; + // Returns the name of the test (relative to the repo root). + auto test_name() const -> llvm::StringRef { return test_name_; } private: // Encapsulates test context generated by processing and running. @@ -135,15 +134,12 @@ class FileTestBase : public testing::Test { // Processes the test input, producing test files and expected output. auto ProcessTestFile(TestContext& context) -> ErrorOr; - // Gets the test filename, relative to the target directory. - auto GetTestFilename() -> std::string; - // Transforms an expectation on a given line from `FileCheck` syntax into a // standard regex matcher. static auto TransformExpectation(int line_index, llvm::StringRef in) -> ErrorOr>; - const std::filesystem::path path_; + llvm::StringRef test_name_; }; // Aggregate a name and factory function for tests using this framework. @@ -152,7 +148,7 @@ struct FileTestFactory { const char* name; // A factory function for tests. - std::function factory_fn; + std::function factory_fn; }; // Must be implemented by the individual file_test to initialize tests. @@ -166,10 +162,9 @@ struct FileTestFactory { extern auto GetFileTestFactory() -> FileTestFactory; // Provides a standard GetFileTestFactory implementation. -#define CARBON_FILE_TEST_FACTORY(Name) \ - auto GetFileTestFactory()->FileTestFactory { \ - return {(#Name), \ - [](const std::filesystem::path& path) { return new Name(path); }}; \ +#define CARBON_FILE_TEST_FACTORY(Name) \ + auto GetFileTestFactory()->FileTestFactory { \ + return {#Name, [](llvm::StringRef path) { return new Name(path); }}; \ } } // namespace Carbon::Testing diff --git a/testing/file_test/file_test_base_test.cpp b/testing/file_test/file_test_base_test.cpp index e0916261fd9b..7231bc3506f9 100644 --- a/testing/file_test/file_test_base_test.cpp +++ b/testing/file_test/file_test_base_test.cpp @@ -31,7 +31,7 @@ class FileTestBaseTest : public FileTestBase { } stdout << "\n"; - auto filename = path().filename(); + auto filename = std::filesystem::path(test_name().str()).filename(); if (filename == "args.carbon") { // 'args.carbon' has custom arguments, so don't do regular argument // validation for it. diff --git a/testing/file_test/rules.bzl b/testing/file_test/rules.bzl index 008ff9d63516..9818130b6907 100644 --- a/testing/file_test/rules.bzl +++ b/testing/file_test/rules.bzl @@ -2,9 +2,41 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""Rules for building file tests. + +file_test uses the tests_as_input_file rule to transform test dependencies into +a file which can be accessed as a list. This avoids long argument parsing. +""" + load("@rules_cc//cc:defs.bzl", "cc_test") -"""Rules for building fuzz tests.""" +DataFilesInfo = provider( + "Data files for this target.", + fields = { + "data_files": "Data files for this target", + }, +) + +def _tests_as_input_file_rule_impl(ctx): + data_files = [] + for tests in ctx.attr.data: + data_files.extend( + [f.path for f in tests[DefaultInfo].data_runfiles.files.to_list()], + ) + data_files.extend( + [f.path for f in tests[DefaultInfo].files.to_list()], + ) + ctx.actions.write(ctx.outputs.data_files, "\n".join(data_files) + "\n") + +_tests_as_input_file_rule = rule( + attrs = { + "data": attr.label_list(allow_files = True), + }, + outputs = { + "data_files": "%{name}.txt", + }, + implementation = _tests_as_input_file_rule_impl, +) def file_test(name, tests, data = [], args = [], **kwargs): """Generates tests using the file_test base. @@ -20,12 +52,17 @@ def file_test(name, tests, data = [], args = [], **kwargs): args: Passed to cc_test. **kwargs: Passed to cc_test. """ + + # Ensure tests are always a filegroup for tests_as_input_file_rule. + tests_file = "{0}.tests".format(name) + _tests_as_input_file_rule( + name = tests_file, + data = tests, + testonly = 1, + ) cc_test( name = name, - data = tests + data, - args = ["--file_tests=" + ",".join([ - "$(location {0})".format(x) - for x in tests - ])] + args, + data = [tests_file] + tests + data, + args = ["--test_targets_file=$(rootpath :{0})".format(tests_file)] + args, **kwargs ) diff --git a/toolchain/autoupdate_testdata.py b/toolchain/autoupdate_testdata.py index 07a75b37ec22..93c135e2f6d8 100755 --- a/toolchain/autoupdate_testdata.py +++ b/toolchain/autoupdate_testdata.py @@ -8,49 +8,38 @@ Exceptions. See /LICENSE for license information. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ -import argparse import subprocess - -TARGETS = { - "check": "//toolchain/check:check_file_test", - "codegen": "//toolchain/codegen:codegen_file_test", - "driver": "//toolchain/driver:driver_file_test", - "lex": "//toolchain/lex:lex_file_test", - "lower": "//toolchain/lower:lower_file_test", - "parse": "//toolchain/parse:parse_file_test", -} +import sys +from pathlib import Path def main() -> None: - parser = argparse.ArgumentParser() - parser.add_argument( - "dirs", - # We don't use `choices` because it seems to conflict with "*". - nargs="*", - default=TARGETS.keys(), - help="Optionally restrict directories to update. Defaults to all.", - ) - parsed_args = parser.parse_args() - - # Deduplicate and validate arguments. - dirs = set(parsed_args.dirs) - invalid_dirs = dirs.difference(TARGETS.keys()) - if invalid_dirs: - exit( - f"Invalid dirs: {', '.join(invalid_dirs)}; " - f"allowed dirs are {', '.join(TARGETS.keys())}." - ) - - # Build the targets together if there's more than one. Otherwise, we may as - # well build and run together. - if len(dirs) > 1: - subprocess.check_call( - ["bazel", "build", "-c", "opt"] + [TARGETS[d] for d in dirs] - ) - for d in dirs: - subprocess.check_call( - ["bazel", "run", "-c", "opt", TARGETS[d], "--", "--autoupdate"] - ) + argv = [ + "bazel", + "run", + "-c", + "opt", + "//toolchain/testing:file_test", + "--", + "--autoupdate", + ] + # Support specifying tests to update, such as: + # ./autoupdate_testdata.py lex/**/* + if len(sys.argv) > 1: + repo_root = Path(__file__).parent.parent + file_tests = [] + # Filter down to just test files. + for f in sys.argv[1:]: + if f.endswith(".carbon"): + path = str(Path(f).absolute().relative_to(repo_root)) + if path.find("/testdata/"): + file_tests.append(path) + if not file_tests: + sys.exit( + f"Args do not seem to be test files; for example, {sys.argv[1]}" + ) + argv.append("--file_tests=" + ",".join(file_tests)) + subprocess.run(argv, check=True) if __name__ == "__main__": diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 36d38537e731..540362da25a0 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -4,11 +4,15 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("//bazel/sh_run:rules.bzl", "glob_sh_run") -load("//testing/file_test:rules.bzl", "file_test") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") package(default_visibility = ["//visibility:public"]) +filegroup( + name = "testdata", + data = glob(["testdata/**/*.carbon"]), +) + cc_library( name = "node_stack", srcs = ["node_stack.cpp"], @@ -62,16 +66,6 @@ cc_library( ], ) -file_test( - name = "check_file_test", - srcs = ["check_file_test.cpp"], - tests = glob(["testdata/**/*.carbon"]), - deps = [ - "//toolchain/driver:driver_file_test_base", - "@llvm-project//llvm:Support", - ], -) - cc_fuzz_test( name = "check_fuzzer", size = "small", diff --git a/toolchain/check/check_file_test.cpp b/toolchain/check/check_file_test.cpp deleted file mode 100644 index 22ea68831e8b..000000000000 --- a/toolchain/check/check_file_test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include "llvm/ADT/SmallVector.h" -#include "toolchain/driver/driver_file_test_base.h" - -namespace Carbon::Testing { -namespace { - -class CheckFileTest : public DriverFileTestBase { - public: - using DriverFileTestBase::DriverFileTestBase; - - auto GetDefaultArgs() -> llvm::SmallVector override { - return {"compile", "--phase=check", "--dump-semantics-ir", "%s"}; - } -}; - -} // namespace - -CARBON_FILE_TEST_FACTORY(CheckFileTest); - -} // namespace Carbon::Testing diff --git a/toolchain/codegen/BUILD b/toolchain/codegen/BUILD index 7a681318ae4d..41772690704e 100644 --- a/toolchain/codegen/BUILD +++ b/toolchain/codegen/BUILD @@ -3,10 +3,14 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_cc//cc:defs.bzl", "cc_library") -load("//testing/file_test:rules.bzl", "file_test") package(default_visibility = ["//visibility:public"]) +filegroup( + name = "testdata", + data = glob(["testdata/**/*.carbon"]), +) + cc_library( name = "codegen", srcs = ["codegen.cpp"], @@ -21,13 +25,3 @@ cc_library( "@llvm-project//llvm:TargetParser", ], ) - -file_test( - name = "codegen_file_test", - srcs = ["codegen_file_test.cpp"], - tests = glob(["testdata/**/*.carbon"]), - deps = [ - "//toolchain/driver:driver_file_test_base", - "@llvm-project//llvm:Support", - ], -) diff --git a/toolchain/codegen/codegen_file_test.cpp b/toolchain/codegen/codegen_file_test.cpp deleted file mode 100644 index 2810d20d9f4a..000000000000 --- a/toolchain/codegen/codegen_file_test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include "llvm/ADT/SmallVector.h" -#include "toolchain/driver/driver_file_test_base.h" - -namespace Carbon::Testing { -namespace { - -class CodeGenFileTest : public DriverFileTestBase { - public: - using DriverFileTestBase::DriverFileTestBase; - - auto GetDefaultArgs() -> llvm::SmallVector override { - CARBON_FATAL() << "ARGS is always set in these tests"; - } -}; - -} // namespace - -CARBON_FILE_TEST_FACTORY(CodeGenFileTest); - -} // namespace Carbon::Testing diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index e718526a4ae3..a5e87e5880ae 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -4,11 +4,15 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load("//bazel/cc_toolchains:defs.bzl", "cc_env") -load("//testing/file_test:rules.bzl", "file_test") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") package(default_visibility = ["//visibility:public"]) +filegroup( + name = "testdata", + data = glob(["testdata/**/*.carbon"]), +) + cc_library( name = "driver", srcs = ["driver.cpp"], @@ -33,16 +37,6 @@ cc_library( ], ) -file_test( - name = "driver_file_test", - srcs = ["driver_file_test.cpp"], - tests = glob(["testdata/**/*.carbon"]), - deps = [ - ":driver_file_test_base", - "@llvm-project//llvm:Support", - ], -) - cc_test( name = "driver_test", size = "small", @@ -72,17 +66,6 @@ cc_fuzz_test( ], ) -cc_library( - name = "driver_file_test_base", - testonly = 1, - hdrs = ["driver_file_test_base.h"], - deps = [ - ":driver", - "//testing/file_test:file_test_base", - "@llvm-project//llvm:Support", - ], -) - cc_binary( name = "carbon", srcs = ["driver_main.cpp"], diff --git a/toolchain/driver/driver_file_test.cpp b/toolchain/driver/driver_file_test.cpp deleted file mode 100644 index b388c524984f..000000000000 --- a/toolchain/driver/driver_file_test.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include "llvm/ADT/SmallVector.h" -#include "toolchain/driver/driver_file_test_base.h" - -namespace Carbon::Testing { -namespace { - -class DriverFileTest : public DriverFileTestBase { - public: - using DriverFileTestBase::DriverFileTestBase; - - auto GetDefaultArgs() -> llvm::SmallVector override { - CARBON_FATAL() << "ARGS is always set in these tests"; - } - - auto DoExtraCheckReplacements(std::string& check_line) -> void override { - // TODO: Disable token output, it's not interesting for these tests. - if (llvm::StringRef(check_line).starts_with("// CHECK:STDOUT: {")) { - check_line = "// CHECK:STDOUT: {{.*}}"; - } - } -}; - -} // namespace - -CARBON_FILE_TEST_FACTORY(DriverFileTest); - -} // namespace Carbon::Testing diff --git a/toolchain/driver/driver_file_test_base.h b/toolchain/driver/driver_file_test_base.h deleted file mode 100644 index f28dde9c0406..000000000000 --- a/toolchain/driver/driver_file_test_base.h +++ /dev/null @@ -1,34 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#ifndef CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_ -#define CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_ - -#include - -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/VirtualFileSystem.h" -#include "testing/file_test/file_test_base.h" -#include "toolchain/driver/driver.h" - -namespace Carbon::Testing { - -// Provides common test support for the driver. This is used by file tests in -// phase subdirectories. -class DriverFileTestBase : public FileTestBase { - public: - using FileTestBase::FileTestBase; - - auto Run(const llvm::SmallVector& test_args, - llvm::vfs::InMemoryFileSystem& fs, llvm::raw_pwrite_stream& stdout, - llvm::raw_pwrite_stream& stderr) -> ErrorOr override { - Driver driver(fs, stdout, stderr); - return driver.RunCommand(test_args); - } -}; - -} // namespace Carbon::Testing - -#endif // CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_ diff --git a/toolchain/lex/BUILD b/toolchain/lex/BUILD index d8db20ec2c61..5125802e30f3 100644 --- a/toolchain/lex/BUILD +++ b/toolchain/lex/BUILD @@ -4,11 +4,15 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load("//bazel/sh_run:rules.bzl", "glob_sh_run") -load("//testing/file_test:rules.bzl", "file_test") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") package(default_visibility = ["//visibility:public"]) +filegroup( + name = "testdata", + data = glob(["testdata/**/*.carbon"]), +) + cc_library( name = "token_kind", srcs = ["token_kind.cpp"], @@ -249,17 +253,6 @@ cc_binary( ], ) -file_test( - name = "lex_file_test", - srcs = ["lex_file_test.cpp"], - tests = glob(["testdata/**/*.carbon"]), - deps = [ - "//toolchain/driver:driver_file_test_base", - "@com_googlesource_code_re2//:re2", - "@llvm-project//llvm:Support", - ], -) - glob_sh_run( args = [ "$(location //toolchain/driver:carbon)", diff --git a/toolchain/lex/lex_file_test.cpp b/toolchain/lex/lex_file_test.cpp deleted file mode 100644 index 4b8e4a300c05..000000000000 --- a/toolchain/lex/lex_file_test.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include "llvm/ADT/SmallVector.h" -#include "re2/re2.h" -#include "toolchain/driver/driver_file_test_base.h" - -namespace Carbon::Testing { -namespace { - -class LexerFileTest : public DriverFileTestBase { - public: - explicit LexerFileTest(std::filesystem::path path) - : DriverFileTestBase(std::move(path)), - end_of_file_re_((R"((EndOfFile.*column: )( *\d+))")) {} - - auto GetDefaultArgs() -> llvm::SmallVector override { - return {"compile", "--phase=lex", "--dump-tokens", "%s"}; - } - - auto GetLineNumberReplacement(llvm::ArrayRef /*filenames*/) - -> LineNumberReplacement override { - return {.has_file = false, - .pattern = R"(line: (\s*\d+))", - // The `{{{{` becomes `{{`. - .line_formatv = "{{{{ *}}{0}"}; - } - - auto DoExtraCheckReplacements(std::string& check_line) -> void override { - // Ignore the resulting column of EndOfFile because it's often the end of - // the CHECK comment. - RE2::Replace(&check_line, end_of_file_re_, R"(\1{{ *\\d+}})"); - } - - private: - RE2 end_of_file_re_; -}; - -} // namespace - -CARBON_FILE_TEST_FACTORY(LexerFileTest); - -} // namespace Carbon::Testing diff --git a/toolchain/lower/BUILD b/toolchain/lower/BUILD index 0b0143104743..eceeb5fa6990 100644 --- a/toolchain/lower/BUILD +++ b/toolchain/lower/BUILD @@ -4,10 +4,14 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("//bazel/sh_run:rules.bzl", "glob_sh_run") -load("//testing/file_test:rules.bzl", "file_test") package(default_visibility = ["//visibility:public"]) +filegroup( + name = "testdata", + data = glob(["testdata/**/*.carbon"]), +) + cc_library( name = "lower", srcs = ["lower.cpp"], @@ -45,16 +49,6 @@ cc_library( ], ) -file_test( - name = "lower_file_test", - srcs = ["lower_file_test.cpp"], - tests = glob(["testdata/**/*.carbon"]), - deps = [ - "//toolchain/driver:driver_file_test_base", - "@llvm-project//llvm:Support", - ], -) - glob_sh_run( args = [ "$(location //toolchain/driver:carbon)", diff --git a/toolchain/lower/lower_file_test.cpp b/toolchain/lower/lower_file_test.cpp deleted file mode 100644 index 85af6e5ff2cc..000000000000 --- a/toolchain/lower/lower_file_test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include "llvm/ADT/SmallVector.h" -#include "toolchain/driver/driver_file_test_base.h" - -namespace Carbon::Testing { -namespace { - -class LowerFileTest : public DriverFileTestBase { - public: - using DriverFileTestBase::DriverFileTestBase; - - auto GetDefaultArgs() -> llvm::SmallVector override { - return {"compile", "--phase=lower", "--dump-llvm-ir", "%s"}; - } -}; - -} // namespace - -CARBON_FILE_TEST_FACTORY(LowerFileTest); - -} // namespace Carbon::Testing diff --git a/toolchain/parse/BUILD b/toolchain/parse/BUILD index cfd2a616d593..056b2443decf 100644 --- a/toolchain/parse/BUILD +++ b/toolchain/parse/BUILD @@ -4,11 +4,15 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("//bazel/sh_run:rules.bzl", "glob_sh_run") -load("//testing/file_test:rules.bzl", "file_test") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") package(default_visibility = ["//visibility:public"]) +filegroup( + name = "testdata", + data = glob(["testdata/**/*.carbon"]), +) + cc_library( name = "node_kind", srcs = ["node_kind.cpp"], @@ -119,16 +123,6 @@ cc_test( ], ) -file_test( - name = "parse_file_test", - srcs = ["parse_file_test.cpp"], - tests = glob(["testdata/**/*.carbon"]), - deps = [ - "//toolchain/driver:driver_file_test_base", - "@llvm-project//llvm:Support", - ], -) - glob_sh_run( args = [ "$(location //toolchain/driver:carbon)", diff --git a/toolchain/parse/parse_file_test.cpp b/toolchain/parse/parse_file_test.cpp deleted file mode 100644 index 3ee5862907ae..000000000000 --- a/toolchain/parse/parse_file_test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Part of the Carbon Language project, under the Apache License v2.0 with LLVM -// Exceptions. See /LICENSE for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include "llvm/ADT/SmallVector.h" -#include "toolchain/driver/driver_file_test_base.h" - -namespace Carbon::Testing { -namespace { - -class ParseFileTest : public DriverFileTestBase { - public: - using DriverFileTestBase::DriverFileTestBase; - - auto GetDefaultArgs() -> llvm::SmallVector override { - return {"compile", "--phase=parse", "--dump-parse-tree", "%s"}; - } -}; - -} // namespace - -CARBON_FILE_TEST_FACTORY(ParseFileTest); - -} // namespace Carbon::Testing diff --git a/toolchain/testing/BUILD b/toolchain/testing/BUILD index a3c08fae30b9..091357d52463 100644 --- a/toolchain/testing/BUILD +++ b/toolchain/testing/BUILD @@ -2,4 +2,24 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +load("//testing/file_test:rules.bzl", "file_test") + package(default_visibility = ["//visibility:public"]) + +file_test( + name = "file_test", + srcs = ["file_test.cpp"], + tests = [ + "//toolchain/check:testdata", + "//toolchain/codegen:testdata", + "//toolchain/driver:testdata", + "//toolchain/lex:testdata", + "//toolchain/lower:testdata", + "//toolchain/parse:testdata", + ], + deps = [ + "//testing/file_test:file_test_base", + "//toolchain/driver", + "@llvm-project//llvm:Support", + ], +) diff --git a/toolchain/testing/file_test.cpp b/toolchain/testing/file_test.cpp new file mode 100644 index 000000000000..e3f65d64e7fd --- /dev/null +++ b/toolchain/testing/file_test.cpp @@ -0,0 +1,95 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_ +#define CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_ + +#include + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "testing/file_test/file_test_base.h" +#include "toolchain/driver/driver.h" + +namespace Carbon::Testing { +namespace { + +// Provides common test support for the driver. This is used by file tests in +// phase subdirectories. +class ToolchainFileTest : public FileTestBase { + public: + explicit ToolchainFileTest(llvm::StringRef test_name) + : FileTestBase(test_name), component_(GetComponent(test_name)) {} + + auto Run(const llvm::SmallVector& test_args, + llvm::vfs::InMemoryFileSystem& fs, llvm::raw_pwrite_stream& stdout, + llvm::raw_pwrite_stream& stderr) -> ErrorOr override { + Driver driver(fs, stdout, stderr); + return driver.RunCommand(test_args); + } + + auto GetDefaultArgs() -> llvm::SmallVector override { + if (component_ == "check") { + return {"compile", "--phase=check", "--dump-semantics-ir", "%s"}; + } else if (component_ == "lex") { + return {"compile", "--phase=lex", "--dump-tokens", "%s"}; + } else if (component_ == "lower") { + return {"compile", "--phase=lower", "--dump-llvm-ir", "%s"}; + } else if (component_ == "parse") { + return {"compile", "--phase=parse", "--dump-parse-tree", "%s"}; + } else if (component_ == "codegen" || component_ == "driver") { + CARBON_FATAL() << "ARGS is always set in these tests"; + } else { + CARBON_FATAL() << "Unexpected test component " << component_ << ": " + << test_name(); + } + } + + auto GetLineNumberReplacement(llvm::ArrayRef filenames) + -> LineNumberReplacement override { + if (component_ == "lex") { + return {.has_file = false, + .pattern = R"(line: (\s*\d+))", + // The `{{{{` becomes `{{`. + .line_formatv = "{{{{ *}}{0}"}; + } else { + return FileTestBase::GetLineNumberReplacement(filenames); + } + } + + auto DoExtraCheckReplacements(std::string& check_line) -> void override { + if (component_ == "driver") { + // TODO: Disable token output, it's not interesting for these tests. + if (llvm::StringRef(check_line).starts_with("// CHECK:STDOUT: {")) { + check_line = "// CHECK:STDOUT: {{.*}}"; + } + } else if (component_ == "lex") { + // Ignore the resulting column of EndOfFile because it's often the end of + // the CHECK comment. + static RE2 end_of_file_re(R"((EndOfFile.*column: )( *\d+))"); + RE2::Replace(&check_line, end_of_file_re, R"(\1{{ *\\d+}})"); + } else { + return FileTestBase::DoExtraCheckReplacements(check_line); + } + } + + private: + // Returns the toolchain subdirectory being tested. + static auto GetComponent(llvm::StringRef test_name) -> llvm::StringRef { + CARBON_CHECK(test_name.consume_front("toolchain/")); + test_name = test_name.take_front(test_name.find("/")); + return test_name; + } + + const llvm::StringRef component_; +}; + +} // namespace + +CARBON_FILE_TEST_FACTORY(ToolchainFileTest); + +} // namespace Carbon::Testing + +#endif // CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_