mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:40:11 +01:00
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:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}};
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+6
-5
@@ -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
@@ -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`
|
||||
@@ -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`
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
Reference in New Issue
Block a user