Allow args, includes, and splits in file_test includes (#5610)

This builds a mechanism for the toolchain to construct more complex
min_prelude files, which in turn should allow the toolchain to stop
special-casing the min_prelude directory. For example, instead of:

```
      args.insert(args.end(), {"--custom-core",
                               "--exclude-dump-file-prefix=include_files/"});
```

This should allow (in a `min_prelude` file):

```
// EXTRA-ARGS: --custom-core --exclude-dump-file-prefix=include_files/
```

Then when that min_prelude is included, it'd be used.

But also, this should allow sharing between min_prelude files with use
of `INCLUDE-FILE`, which as we make progressively more complex
min_preludes might become useful.
This commit is contained in:
Jon Ross-Perkins
2025-06-05 16:23:15 +00:00
committed by GitHub
parent a508b00883
commit 51b4abec20
18 changed files with 396 additions and 152 deletions
+5 -1
View File
@@ -62,7 +62,11 @@ file_test(
name = "file_test_base_test",
size = "small",
srcs = ["file_test_base_test.cpp"],
tests = glob(["testdata/**"]),
data = glob(["testdata/include_files/**"]),
tests = glob(
["testdata/**"],
exclude = ["testdata/include_files/**"],
),
deps = [
":file_test_base",
"//common:ostream",
+13 -5
View File
@@ -202,18 +202,26 @@ Supported comment markers are:
// EXTRA-ARGS: <arguments>
```
Same as `ARGS`, including substitution behavior, but appends to the default
argument list instead of replacing it.
Same as `ARGS`, including substitution behavior, but appends to the argument
list instead of replacing it.
`EXTRA-ARGS` can be specified at most once, and a test cannot specify both
`ARGS` and `EXTRA-ARGS`.
`EXTRA-ARGS` can be repeated, and provided with `ARGS`.
- ```
// INCLUDE-FILE: <path/from/repository/root>
```
Includes the specified file in the test's virtual file system and adds the
path to the file's splits.
included file's content to the current file's splits. Included files can
use:
- `// ARGS`
- `// EXTRA-ARGS`
- `// INCLUDE-FILE`
- `// --- <filename>`
Included files will use `include_files/<filename>` for their name when a
split isn't provided.
This can be used to provide a minimal `Core` package (and `prelude` library)
in toolchain tests. See the
+18 -16
View File
@@ -126,6 +126,19 @@ static auto TestCaptureConsoleOutput(TestParams& params)
return {{.success = true}};
}
// Prints and returns expected results for escaping.carbon.
static auto TestEscaping(TestParams& params)
-> ErrorOr<FileTestBaseTest::RunResult> {
params.error_stream << "carriage return\r\n"
"{one brace}\n"
"{{two braces}}\n"
"[one bracket]\n"
"[[two brackets]]\n"
"end of line whitespace \n"
"\ttabs\t\n";
return {{.success = true}};
}
// Prints and returns expected results for example.carbon.
static auto TestExample(TestParams& params)
-> ErrorOr<FileTestBaseTest::RunResult> {
@@ -189,19 +202,6 @@ static auto TestNoLineNumber(TestParams& params)
return {{.success = true}};
}
// Prints and returns expected results for escaping.carbon.
static auto TestEscaping(TestParams& params)
-> ErrorOr<FileTestBaseTest::RunResult> {
params.error_stream << "carriage return\r\n"
"{one brace}\n"
"{{two braces}}\n"
"[one bracket]\n"
"[[two brackets]]\n"
"end of line whitespace \n"
"\ttabs\t\n";
return {{.success = true}};
}
// Prints and returns expected results for unattached_multi_file.carbon.
static auto TestUnattachedMultiFile(TestParams& params)
-> ErrorOr<FileTestBaseTest::RunResult> {
@@ -269,9 +269,11 @@ auto FileTestBaseTest::Run(
PrintArgs(test_args, output_stream);
auto filename = std::filesystem::path(test_name().str()).filename();
if (filename == "args.carbon" || filename == "include_file.carbon") {
// 'args.carbon' and 'include_file.carbon' have custom arguments, so don't
// do regular argument validation for them.
if (filename == "args.carbon" || filename == "include_args.carbon" ||
filename == "include_extra_args.carbon" ||
filename == "include_args_and_extra_args.carbon") {
// These files are testing argument behavior, which doesn't work with the
// default test logic.
return {{.success = true}};
}
+1 -1
View File
@@ -102,8 +102,8 @@ auto RunTestFile(const FileTestBase& test_base, bool dump_output,
// Process arguments.
if (test_file.test_args.empty()) {
test_file.test_args = test_base.GetDefaultArgs();
test_file.test_args.append(test_file.extra_args);
}
test_file.test_args.append(test_file.extra_args);
CARBON_RETURN_IF_ERROR(DoArgReplacements(
test_file.test_args, test_base.GetArgReplacements(), all_splits));
+222 -124
View File
@@ -16,6 +16,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/JSON.h"
#include "testing/base/file_helpers.h"
#include "testing/file_test/line.h"
namespace Carbon::Testing {
@@ -28,7 +29,7 @@ using ::testing::StrEq;
static auto TryConsumeConflictMarker(bool running_autoupdate,
llvm::StringRef line,
llvm::StringRef line_trimmed,
bool* inside_conflict_marker)
bool& inside_conflict_marker)
-> ErrorOr<bool> {
bool is_start = line.starts_with("<<<<<<<");
bool is_middle = line.starts_with("=======") || line.starts_with("|||||||");
@@ -41,7 +42,7 @@ static auto TryConsumeConflictMarker(bool running_autoupdate,
// Autoupdate tracks conflict markers for context, and will discard
// conflicting lines when it can autoupdate them.
if (*inside_conflict_marker) {
if (inside_conflict_marker) {
if (is_start) {
return ErrorBuilder() << "Unexpected conflict marker inside conflict:\n"
<< line;
@@ -50,7 +51,7 @@ static auto TryConsumeConflictMarker(bool running_autoupdate,
return true;
}
if (is_end) {
*inside_conflict_marker = false;
inside_conflict_marker = false;
return true;
}
@@ -66,7 +67,7 @@ static auto TryConsumeConflictMarker(bool running_autoupdate,
<< line;
} else {
if (is_start) {
*inside_conflict_marker = true;
inside_conflict_marker = true;
return true;
}
if (is_middle || is_end) {
@@ -114,10 +115,10 @@ static auto ExtractFilePathFromUri(llvm::StringRef uri)
// When `FROM_FILE_SPLIT` is used in path `textDocument.text`, populate the
// value from the split matching the `uri`. Only used for
// `textDocument/didOpen`.
static auto AutoFillDidOpenParams(llvm::json::Object* params,
static auto AutoFillDidOpenParams(llvm::json::Object& params,
llvm::ArrayRef<TestFile::Split> splits)
-> ErrorOr<Success> {
auto* text_document = params->getObject("textDocument");
auto* text_document = params.getObject("textDocument");
if (text_document == nullptr) {
return Success();
}
@@ -144,12 +145,12 @@ static auto AutoFillDidOpenParams(llvm::json::Object* params,
}
// Reformats `[[@LSP:` and similar keyword as an LSP call with headers.
static auto ReplaceLspKeywordAt(std::string* content, size_t keyword_pos,
static auto ReplaceLspKeywordAt(std::string& content, size_t keyword_pos,
int& lsp_call_id,
llvm::ArrayRef<TestFile::Split> splits)
-> ErrorOr<size_t> {
llvm::StringRef content_at_keyword =
llvm::StringRef(*content).substr(keyword_pos);
llvm::StringRef(content).substr(keyword_pos);
auto [keyword, body_start] = content_at_keyword.split(":");
if (body_start.empty()) {
@@ -198,7 +199,7 @@ static auto ReplaceLspKeywordAt(std::string* content, size_t keyword_pos,
if (extra_content_label == "params" &&
method_or_id == "textDocument/didOpen") {
CARBON_RETURN_IF_ERROR(
AutoFillDidOpenParams(parsed_extra_content.getAsObject(), splits));
AutoFillDidOpenParams(*parsed_extra_content.getAsObject(), splits));
}
}
@@ -231,17 +232,17 @@ static auto ReplaceLspKeywordAt(std::string* content, size_t keyword_pos,
.str();
int keyword_len =
(body_start.data() + body_end + LspEnd.size()) - keyword.data();
content->replace(keyword_pos, keyword_len, json_with_header);
content.replace(keyword_pos, keyword_len, json_with_header);
return keyword_pos + json_with_header.size();
}
// Replaces the keyword at the given position. Returns the position to start a
// find for the next keyword.
static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
static auto ReplaceContentKeywordAt(std::string& content, size_t keyword_pos,
llvm::StringRef test_name, int& lsp_call_id,
llvm::ArrayRef<TestFile::Split> splits)
-> ErrorOr<size_t> {
auto keyword = llvm::StringRef(*content).substr(keyword_pos);
auto keyword = llvm::StringRef(content).substr(keyword_pos);
// Line replacements aren't handled here.
static constexpr llvm::StringLiteral Line = "[[@LINE";
@@ -253,7 +254,7 @@ static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
// Replaced with the actual test name.
static constexpr llvm::StringLiteral TestName = "[[@TEST_NAME]]";
if (keyword.starts_with(TestName)) {
content->replace(keyword_pos, TestName.size(), test_name);
content.replace(keyword_pos, TestName.size(), test_name);
return keyword_pos + test_name.size();
}
@@ -270,12 +271,12 @@ static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
// TEST_NAME is the only content keyword at present, but we do validate that
// other names are reserved.
static auto ReplaceContentKeywords(llvm::StringRef filename,
std::string* content,
std::string& content,
llvm::ArrayRef<TestFile::Split> splits)
-> ErrorOr<Success> {
static constexpr llvm::StringLiteral Prefix = "[[@";
auto keyword_pos = content->find(Prefix);
auto keyword_pos = content.find(Prefix);
// Return early if not finding anything.
if (keyword_pos == std::string::npos) {
return Success();
@@ -303,44 +304,45 @@ static auto ReplaceContentKeywords(llvm::StringRef filename,
auto keyword_end,
ReplaceContentKeywordAt(content, keyword_pos, test_name, lsp_call_id,
splits));
keyword_pos = content->find(Prefix, keyword_end);
keyword_pos = content.find(Prefix, keyword_end);
}
return Success();
}
// Adds a file. Used for both split and unsplit test files.
static auto AddSplit(llvm::StringRef filename, std::string* content,
llvm::SmallVector<TestFile::Split>* file_splits)
static auto AddSplit(llvm::StringRef filename, std::string& content,
llvm::SmallVector<TestFile::Split>& file_splits)
-> ErrorOr<Success> {
CARBON_RETURN_IF_ERROR(
ReplaceContentKeywords(filename, content, *file_splits));
file_splits->push_back(
{.filename = filename.str(), .content = std::move(*content)});
content->clear();
ReplaceContentKeywords(filename, content, file_splits));
file_splits.push_back(
{.filename = filename.str(), .content = std::move(content)});
content.clear();
return Success();
}
// Process file split ("---") lines when found. Returns true if the line is
// consumed.
// consumed. `non_check_lines` is only provided for the main file, and will be
// null for includes.
static auto TryConsumeSplit(llvm::StringRef line, llvm::StringRef line_trimmed,
bool found_autoupdate, int* line_index,
SplitState* split,
llvm::SmallVector<TestFile::Split>* file_splits,
bool missing_autoupdate, int& line_index,
SplitState& split,
llvm::SmallVector<TestFile::Split>& file_splits,
llvm::SmallVector<FileTestLine>* non_check_lines)
-> ErrorOr<bool> {
if (!line_trimmed.consume_front("// ---")) {
if (!split->has_splits() && !line_trimmed.starts_with("//") &&
if (!split.has_splits() && !line_trimmed.starts_with("//") &&
!line_trimmed.empty()) {
split->found_code_pre_split = true;
split.found_code_pre_split = true;
}
// Add the line to the current file's content (which may not be a split
// file).
split->add_content(line);
split.add_content(line);
return false;
}
if (!found_autoupdate) {
if (missing_autoupdate) {
// If there's a split, all output is appended at the end of each file
// before AUTOUPDATE. We may want to change that, but it's not
// necessary to handle right now.
@@ -350,12 +352,12 @@ static auto TryConsumeSplit(llvm::StringRef line, llvm::StringRef line_trimmed,
}
// On a file split, add the previous file, then start a new one.
if (split->has_splits()) {
if (split.has_splits()) {
CARBON_RETURN_IF_ERROR(
AddSplit(split->filename, &split->content, file_splits));
AddSplit(split.filename, split.content, file_splits));
} else {
split->content.clear();
if (split->found_code_pre_split) {
split.content.clear();
if (split.found_code_pre_split) {
// For the first split, we make sure there was no content prior.
return Error(
"When using split files, there must be no content before the first "
@@ -363,16 +365,18 @@ static auto TryConsumeSplit(llvm::StringRef line, llvm::StringRef line_trimmed,
}
}
++split->file_index;
split->filename = line_trimmed.trim();
if (split->filename.empty()) {
++split.file_index;
split.filename = line_trimmed.trim();
if (split.filename.empty()) {
return Error("Missing filename for split.");
}
// The split line is added to non_check_lines for retention in autoupdate, but
// is not added to the test file content.
*line_index = 0;
non_check_lines->push_back(
FileTestLine(split->file_index, *line_index, line));
line_index = 0;
if (non_check_lines) {
non_check_lines->push_back(
FileTestLine(split.file_index, line_index, line));
}
return true;
}
@@ -497,20 +501,26 @@ static auto TransformExpectation(int line_index, llvm::StringRef in)
}
// Once all content is processed, do any remaining split processing.
static auto FinishSplit(llvm::StringRef test_name, SplitState* split,
llvm::SmallVector<TestFile::Split>* file_splits)
static auto FinishSplit(llvm::StringRef filename, bool is_include_file,
SplitState& split,
llvm::SmallVector<TestFile::Split>& file_splits)
-> ErrorOr<Success> {
if (split->has_splits()) {
return AddSplit(split->filename, &split->content, file_splits);
if (split.has_splits()) {
return AddSplit(split.filename, split.content, file_splits);
} else {
// If no file splitting happened, use the main file as the test file.
// There will always be a `/` unless tests are in the repo root.
return AddSplit(test_name.drop_front(test_name.rfind("/") + 1),
&split->content, file_splits);
std::string split_name = std::filesystem::path(filename.str()).filename();
if (is_include_file) {
split_name.insert(0, "include_files/");
}
return AddSplit(split_name, split.content, file_splits);
}
}
// Process CHECK lines when found. Returns true if the line is consumed.
// `expected_stdout` and `expected_stderr` are null in included files, where
// it's an error to use `CHECK`.
static auto TryConsumeCheck(
bool running_autoupdate, int line_index, llvm::StringRef line,
llvm::StringRef line_trimmed,
@@ -521,6 +531,11 @@ static auto TryConsumeCheck(
return false;
}
if (!expected_stdout) {
return ErrorBuilder() << "Included files can't add CHECKs: "
<< line_trimmed;
}
// Don't build expectations when doing an autoupdate. We don't want to
// break the autoupdate on an invalid CHECK line.
if (!running_autoupdate) {
@@ -542,50 +557,58 @@ static auto TryConsumeCheck(
// Processes ARGS and EXTRA-ARGS lines when found. Returns true if the line is
// consumed.
static auto TryConsumeArgs(llvm::StringRef line, llvm::StringRef line_trimmed,
llvm::SmallVector<std::string>* args,
llvm::SmallVector<std::string>* extra_args)
llvm::SmallVector<std::string>& args)
-> ErrorOr<bool> {
llvm::SmallVector<std::string>* arg_list = nullptr;
if (line_trimmed.consume_front("// ARGS: ")) {
arg_list = args;
} else if (line_trimmed.consume_front("// EXTRA-ARGS: ")) {
arg_list = extra_args;
} else {
if (!line_trimmed.consume_front("// ARGS: ")) {
return false;
}
if (!args->empty() || !extra_args->empty()) {
return ErrorBuilder() << "ARGS / EXTRA-ARGS specified multiple times: "
<< line.str();
if (!args.empty()) {
return ErrorBuilder() << "ARGS specified multiple times: " << line.str();
}
// Split the line into arguments.
std::pair<llvm::StringRef, llvm::StringRef> cursor =
llvm::getToken(line_trimmed);
while (!cursor.first.empty()) {
arg_list->push_back(std::string(cursor.first));
args.push_back(std::string(cursor.first));
cursor = llvm::getToken(cursor.second);
}
return true;
}
static auto TryConsumeExtraArgs(llvm::StringRef line_trimmed,
llvm::SmallVector<std::string>& extra_args)
-> ErrorOr<bool> {
if (!line_trimmed.consume_front("// EXTRA-ARGS: ")) {
return false;
}
// Split the line into arguments.
std::pair<llvm::StringRef, llvm::StringRef> cursor =
llvm::getToken(line_trimmed);
while (!cursor.first.empty()) {
extra_args.push_back(std::string(cursor.first));
cursor = llvm::getToken(cursor.second);
}
return true;
}
static auto TryConsumeIncludeFile(
llvm::StringRef line_trimmed,
llvm::SmallVector<TestFile::Split>* include_files) -> ErrorOr<bool> {
static auto TryConsumeIncludeFile(llvm::StringRef line_trimmed,
llvm::SmallVector<std::string>& include_files)
-> ErrorOr<bool> {
if (!line_trimmed.consume_front("// INCLUDE-FILE: ")) {
return false;
}
std::filesystem::path path = std::string(line_trimmed);
CARBON_ASSIGN_OR_RETURN(std::string content, ReadFile(path));
include_files->push_back(
{.filename = std::filesystem::path("include_files") / path.filename(),
.content = content});
include_files.push_back(line_trimmed.str());
return true;
}
// Processes AUTOUPDATE lines when found. Returns true if the line is consumed.
// `found_autoupdate` and `autoupdate_line_number` are only provided for the
// main file; it's an error to have autoupdate in included files.
static auto TryConsumeAutoupdate(int line_index, llvm::StringRef line_trimmed,
bool* found_autoupdate,
std::optional<int>* autoupdate_line_number)
@@ -595,6 +618,10 @@ static auto TryConsumeAutoupdate(int line_index, llvm::StringRef line_trimmed,
if (line_trimmed != Autoupdate && line_trimmed != NoAutoupdate) {
return false;
}
if (!found_autoupdate) {
return ErrorBuilder() << "Included files can't control autoupdate: "
<< line_trimmed;
}
if (*found_autoupdate) {
return Error("Multiple AUTOUPDATE/NOAUTOUPDATE settings found");
}
@@ -606,12 +633,16 @@ static auto TryConsumeAutoupdate(int line_index, llvm::StringRef line_trimmed,
}
// Processes SET-* lines when found. Returns true if the line is consumed.
// If `flag` is null, we're in an included file where the flag can't be set.
static auto TryConsumeSetFlag(llvm::StringRef line_trimmed,
llvm::StringLiteral flag_name, bool* flag)
-> ErrorOr<bool> {
if (!line_trimmed.consume_front("// ") || line_trimmed != flag_name) {
return false;
}
if (!flag) {
return ErrorBuilder() << "Included files can't set flag: " << line_trimmed;
}
if (*flag) {
return ErrorBuilder() << flag_name << " was specified multiple times";
}
@@ -619,49 +650,62 @@ static auto TryConsumeSetFlag(llvm::StringRef line_trimmed,
return true;
}
auto ProcessTestFile(llvm::StringRef test_name, bool running_autoupdate)
-> ErrorOr<TestFile> {
TestFile test_file;
// Store the file so that file_splits can use references to content.
CARBON_ASSIGN_OR_RETURN(test_file.input_content, ReadFile(test_name.str()));
// Original file content, and a cursor for walking through it.
llvm::StringRef file_content = test_file.input_content;
llvm::StringRef cursor = file_content;
// Whether either AUTOUDPATE or NOAUTOUPDATE was found.
bool found_autoupdate = false;
// Process content for either the main file (with `test_file` and
// `found_autoupdate` provided) or an included file (with those arguments null).
//
// - `found_autoupdate` is set to true when either `AUTOUPDATE` or
// `NOAUTOUPDATE` are found.
// - `args` is set from `ARGS`.
// - `extra_args` accumulates `EXTRA-ARGS`.
// - `splits` accumulates split form for the test (`// --- <filename>`, or the
// full file named as `filename` when there are no splits in the file).
// - `include_files` accumulates `INCLUDE-FILE`.
static auto ProcessFileContent(llvm::StringRef filename,
llvm::StringRef content_cursor,
bool running_autoupdate, TestFile* test_file,
bool* found_autoupdate,
llvm::SmallVector<std::string>& args,
llvm::SmallVector<std::string>& extra_args,
llvm::SmallVector<TestFile::Split>& splits,
llvm::SmallVector<std::string>& include_files)
-> ErrorOr<Success> {
// The index in the current test file. Will be reset on splits.
int line_index = 0;
SplitState split;
// When autoupdating, we track whether we're inside conflict markers.
// Otherwise conflict markers are errors.
bool inside_conflict_marker = false;
while (!cursor.empty()) {
auto [line, next_cursor] = cursor.split("\n");
cursor = next_cursor;
SplitState split_state;
while (!content_cursor.empty()) {
auto [line, next_cursor] = content_cursor.split("\n");
content_cursor = next_cursor;
auto line_trimmed = line.ltrim();
bool is_consumed = false;
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeConflictMarker(running_autoupdate, line, line_trimmed,
&inside_conflict_marker));
inside_conflict_marker));
if (is_consumed) {
continue;
}
// At this point, remaining lines are part of the test input.
// We need to consume a split, but the main file has a little more handling.
bool missing_autoupdate = false;
llvm::SmallVector<FileTestLine>* non_check_lines = nullptr;
if (test_file) {
missing_autoupdate = !*found_autoupdate;
non_check_lines = &test_file->non_check_lines;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeSplit(line, line_trimmed, found_autoupdate, &line_index,
&split, &test_file.file_splits,
&test_file.non_check_lines));
TryConsumeSplit(line, line_trimmed, missing_autoupdate, line_index,
split_state, splits, non_check_lines));
if (is_consumed) {
continue;
}
@@ -674,63 +718,99 @@ auto ProcessTestFile(llvm::StringRef test_name, bool running_autoupdate)
}
CARBON_ASSIGN_OR_RETURN(
is_consumed, TryConsumeCheck(running_autoupdate, line_index, line,
line_trimmed, &test_file.expected_stdout,
&test_file.expected_stderr));
is_consumed,
TryConsumeCheck(running_autoupdate, line_index, line, line_trimmed,
test_file ? &test_file->expected_stdout : nullptr,
test_file ? &test_file->expected_stderr : nullptr));
if (is_consumed) {
continue;
}
// At this point, lines are retained as non-CHECK lines.
test_file.non_check_lines.push_back(
FileTestLine(split.file_index, line_index, line));
if (test_file) {
// At this point, lines are retained as non-CHECK lines.
test_file->non_check_lines.push_back(
FileTestLine(split_state.file_index, line_index, line));
}
CARBON_ASSIGN_OR_RETURN(
is_consumed, TryConsumeArgs(line, line_trimmed, &test_file.test_args,
&test_file.extra_args));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeIncludeFile(line_trimmed, &test_file.include_file_splits));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeAutoupdate(line_index, line_trimmed, &found_autoupdate,
&test_file.autoupdate_line_number));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeSetFlag(line_trimmed, "SET-CAPTURE-CONSOLE-OUTPUT",
&test_file.capture_console_output));
CARBON_ASSIGN_OR_RETURN(is_consumed,
TryConsumeArgs(line, line_trimmed, args));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(is_consumed,
TryConsumeSetFlag(line_trimmed, "SET-CHECK-SUBSET",
&test_file.check_subset));
TryConsumeExtraArgs(line_trimmed, extra_args));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(is_consumed,
TryConsumeIncludeFile(line_trimmed, include_files));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeAutoupdate(
line_index, line_trimmed, found_autoupdate,
test_file ? &test_file->autoupdate_line_number : nullptr));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeSetFlag(
line_trimmed, "SET-CAPTURE-CONSOLE-OUTPUT",
test_file ? &test_file->capture_console_output : nullptr));
if (is_consumed) {
continue;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeSetFlag(line_trimmed, "SET-CHECK-SUBSET",
test_file ? &test_file->check_subset : nullptr));
if (is_consumed) {
continue;
}
}
CARBON_RETURN_IF_ERROR(FinishSplit(filename, /*is_include_file=*/!test_file,
split_state, splits));
if (test_file) {
test_file->has_splits = split_state.has_splits();
}
return Success();
}
auto ProcessTestFile(llvm::StringRef test_name, bool running_autoupdate)
-> ErrorOr<TestFile> {
TestFile test_file;
// Store the original content, to avoid a read when autoupdating.
CARBON_ASSIGN_OR_RETURN(test_file.input_content, ReadFile(test_name.str()));
// Whether either AUTOUDPATE or NOAUTOUPDATE was found.
bool found_autoupdate = false;
// INCLUDE-FILE uses, accumulated across both the main file and any includes
// (recursively).
llvm::SmallVector<std::string> include_files;
// Process the main file.
CARBON_RETURN_IF_ERROR(ProcessFileContent(
test_name, test_file.input_content, running_autoupdate, &test_file,
&found_autoupdate, test_file.test_args, test_file.extra_args,
test_file.file_splits, include_files));
if (!found_autoupdate) {
return ErrorBuilder() << "Missing AUTOUPDATE/NOAUTOUPDATE setting: "
<< test_name;
}
test_file.has_splits = split.has_splits();
CARBON_RETURN_IF_ERROR(
FinishSplit(test_name, &split, &test_file.file_splits));
constexpr llvm::StringLiteral AutoupdateSplit = "AUTOUPDATE-SPLIT";
// Validate AUTOUPDATE-SPLIT use, and remove it from test files if present.
if (test_file.has_splits) {
constexpr llvm::StringLiteral AutoupdateSplit = "AUTOUPDATE-SPLIT";
for (const auto& test_file :
llvm::ArrayRef(test_file.file_splits).drop_back()) {
if (test_file.filename == AutoupdateSplit) {
@@ -754,6 +834,24 @@ auto ProcessTestFile(llvm::StringRef test_name, bool running_autoupdate)
test_file.expected_stderr.push_back(StrEq(""));
}
// Process includes. This can add entries to `include_files`.
for (size_t i = 0; i < include_files.size(); ++i) {
const auto& filename = include_files[i];
CARBON_ASSIGN_OR_RETURN(std::string content, ReadFile(filename));
// Note autoupdate never touches included files.
CARBON_RETURN_IF_ERROR(ProcessFileContent(
filename, content, /*running_autoupdate=*/false,
/*test_file=*/nullptr,
/*found_autoupdate=*/nullptr, test_file.test_args, test_file.extra_args,
test_file.include_file_splits, include_files));
}
for (const auto& split : test_file.include_file_splits) {
if (split.filename == AutoupdateSplit) {
return Error("AUTOUPDATE-SPLIT is disallowed in included files");
}
}
return std::move(test_file);
}
@@ -1,12 +1,13 @@
// 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-FILE: testing/file_test/testdata/no_line_number.carbon
//
// INCLUDE-FILE: testing/file_test/testdata/include_files/args.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_file.carbon
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_args.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_file.carbon
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_args.carbon
// CHECK:STDOUT: 3 args: `default_args`, `include_file.carbon`, `include_files/no_line_number.carbon`
// CHECK:STDOUT: 3 args: `foo`, `bar`, `baz`
@@ -0,0 +1,14 @@
// 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-FILE: testing/file_test/testdata/include_files/extra_args.carbon
// ARGS: foo
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_args_and_extra_args.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_args_and_extra_args.carbon
// CHECK:STDOUT: 3 args: `foo`, `bar`, `baz`
+13
View File
@@ -0,0 +1,13 @@
// 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-FILE: testing/file_test/testdata/include_files/empty.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_empty.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_empty.carbon
// CHECK:STDOUT: 3 args: `default_args`, `include_empty.carbon`, `empty.carbon`
+14
View File
@@ -0,0 +1,14 @@
// 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
//
// EXTRA-ARGS: foo
// INCLUDE-FILE: testing/file_test/testdata/include_files/extra_args.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_extra_args.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_extra_args.carbon
// CHECK:STDOUT: 6 args: `default_args`, `include_extra_args.carbon`, `include_files/extra_args.carbon`, `foo`, `bar`, `baz`
+5
View File
@@ -0,0 +1,5 @@
// 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
//
// ARGS: foo bar baz
+5
View File
@@ -0,0 +1,5 @@
// 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
// --- empty.carbon
@@ -0,0 +1,5 @@
// 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
//
// EXTRA-ARGS: bar baz
@@ -0,0 +1,5 @@
// 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
no split
@@ -0,0 +1,13 @@
// 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-FILE: testing/file_test/testdata/include_files/split.carbon
// --- c.carbon
c
// --- d.carbon
d
+11
View File
@@ -0,0 +1,11 @@
// 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
// --- a.carbon
a
// --- b.carbon
b
+14
View File
@@ -0,0 +1,14 @@
// 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-FILE: testing/file_test/testdata/include_files/no_split.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_no_split.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_no_split.carbon
// CHECK:STDOUT: 3 args: `default_args`, `include_no_split.carbon`, `include_files/no_split.carbon`
// CHECK:STDOUT: include_files/no_split.carbon:5: no split
+17
View File
@@ -0,0 +1,17 @@
// 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-FILE: testing/file_test/testdata/include_files/recursive.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_recursive.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_recursive.carbon
// CHECK:STDOUT: 6 args: `default_args`, `include_recursive.carbon`, `c.carbon`, `d.carbon`, `a.carbon`, `b.carbon`
// CHECK:STDOUT: c.carbon:2: c
// CHECK:STDOUT: d.carbon:2: d
// CHECK:STDOUT: a.carbon:2: a
// CHECK:STDOUT: b.carbon:2: b
+15
View File
@@ -0,0 +1,15 @@
// 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-FILE: testing/file_test/testdata/include_files/split.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/include_split.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_split.carbon
// CHECK:STDOUT: 4 args: `default_args`, `include_split.carbon`, `a.carbon`, `b.carbon`
// CHECK:STDOUT: a.carbon:2: a
// CHECK:STDOUT: b.carbon:2: b