mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 18:50:09 +01:00
Add support for testing with stdin (#4819)
In order to write language-server tests, we need some way to pass stdin input. This adds support for a split "// --- STDIN" which will be provided as a temp file for testing. Note this does more stdin -> input_stream style renaming, this is just bugging me more since I know shadowing works but it can be subtle to read, particularly since I'm now making direct use of stdin in a handful of spots.
This commit is contained in:
@@ -29,7 +29,8 @@ class ExplorerFileTest : public FileTestBase {
|
||||
|
||||
auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
|
||||
llvm::raw_pwrite_stream& stdout, llvm::raw_pwrite_stream& stderr)
|
||||
FILE* /*input_stream*/, llvm::raw_pwrite_stream& output_stream,
|
||||
llvm::raw_pwrite_stream& error_stream)
|
||||
-> ErrorOr<RunResult> override {
|
||||
// Add the prelude.
|
||||
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> prelude =
|
||||
@@ -52,9 +53,10 @@ class ExplorerFileTest : public FileTestBase {
|
||||
args.push_back(arg.data());
|
||||
}
|
||||
|
||||
int exit_code = ExplorerMain(
|
||||
args.size(), args.data(), /*install_path=*/"", PreludePath, stdout,
|
||||
stderr, check_trace_output() ? stdout : trace_stream_, *fs);
|
||||
int exit_code =
|
||||
ExplorerMain(args.size(), args.data(), /*install_path=*/"", PreludePath,
|
||||
output_stream, error_stream,
|
||||
check_trace_output() ? output_stream : trace_stream_, *fs);
|
||||
|
||||
return {{.success = exit_code == EXIT_SUCCESS}};
|
||||
}
|
||||
|
||||
+32
-24
@@ -50,9 +50,11 @@ class MyFileTest : public FileTestBase {
|
||||
// Called as part of individual test executions.
|
||||
auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
|
||||
const llvm::SmallVector<TestFile>& test_files,
|
||||
llvm::raw_pwrite_stream& stdout, llvm::raw_pwrite_stream& stderr)
|
||||
FILE* input_stream, llvm::raw_pwrite_stream& output_stream,
|
||||
llvm::raw_pwrite_stream& error_stream)
|
||||
-> ErrorOr<RunResult> override {
|
||||
return MyFunctionality(test_args, stdout, stderr);
|
||||
return MyFunctionality(test_args, input_stream, output_stream,
|
||||
error_stream);
|
||||
}
|
||||
|
||||
// Provides arguments which are used in tests that don't provide ARGS.
|
||||
@@ -108,21 +110,22 @@ Supported comment markers are:
|
||||
// NOAUTOUPDATE
|
||||
```
|
||||
|
||||
Controls whether the checks in the file will be autoupdated if --autoupdate
|
||||
is passed. Exactly one of these markers must be present. If the file uses
|
||||
splits, the marker must currently be before any splits.
|
||||
Controls whether the checks in the file will be autoupdated if
|
||||
`--autoupdate` is passed. Exactly one of these markers must be present. If
|
||||
the file uses splits, the marker must currently be before any splits.
|
||||
|
||||
When autoupdating, CHECKs will be inserted starting below AUTOUPDATE. When a
|
||||
CHECK has line information, autoupdate will try to insert the CHECK
|
||||
immediately next to the line it's associated with, with stderr CHECKs
|
||||
preceding the line and stdout CHECKs following the line. When that happens,
|
||||
any subsequent CHECK lines without line information, or that refer to lines
|
||||
appearing earlier, will immediately follow. As an exception, if no STDOUT
|
||||
check line refers to any line in the test, all STDOUT check lines are placed
|
||||
at the end of the file instead of immediately after AUTOUPDATE.
|
||||
When autoupdating, `CHECK`s will be inserted starting below `AUTOUPDATE`.
|
||||
When a `CHECK` has line information, autoupdate will try to insert the
|
||||
`CHECK` immediately next to the line it's associated with, with stderr
|
||||
`CHECK`s preceding the line and stdout `CHECK`s following the line. When
|
||||
that happens, any subsequent `CHECK` lines without line information, or that
|
||||
refer to lines appearing earlier, will immediately follow. As an exception,
|
||||
if no `STDOUT` check line refers to any line in the test, all `STDOUT` check
|
||||
lines are placed at the end of the file instead of immediately after
|
||||
`AUTOUPDATE`.
|
||||
|
||||
When using split files, if the last split file is named
|
||||
`// --- AUTOUPDATE-SPLIT`, all CHECKs will be added there; no line
|
||||
`// --- AUTOUPDATE-SPLIT`, all `CHECK`s will be added there; no line
|
||||
associations occur.
|
||||
|
||||
- ```
|
||||
@@ -149,8 +152,8 @@ Supported comment markers are:
|
||||
Replaces some implementation-specific identifier with a value. (Mappings
|
||||
provided by way of an optional `MyFileTest::GetArgReplacements`)
|
||||
|
||||
ARGS can be specified at most once. If not provided, the FileTestBase child
|
||||
is responsible for providing default arguments.
|
||||
`ARGS` can be specified at most once. If not provided, the `FileTestBase`
|
||||
child is responsible for providing default arguments.
|
||||
|
||||
- ```
|
||||
// EXTRA-ARGS: <arguments>
|
||||
@@ -173,28 +176,33 @@ Supported comment markers are:
|
||||
This should be avoided because we are partly ensuring that streams are an
|
||||
API, but is helpful when wrapping Clang, where stderr is used directly.
|
||||
|
||||
SET-CAPTURE-CONSOLE-OUTPUT can be specified at most once.
|
||||
`SET-CAPTURE-CONSOLE-OUTPUT` can be specified at most once.
|
||||
|
||||
- ```
|
||||
// SET-CHECK-SUBSET
|
||||
```
|
||||
|
||||
By default, all lines of output must have a CHECK match. Adding this as a
|
||||
By default, all lines of output must have a `CHECK` match. Adding this as a
|
||||
option sets it so that non-matching lines are ignored. All provided
|
||||
CHECK:STDOUT: and CHECK:STDERR: lines must still have a match in output.
|
||||
`CHECK:STDOUT:` and `CHECK:STDERR:` lines must still have a match in output.
|
||||
|
||||
SET-CHECK-SUBSET can be specified at most once.
|
||||
`SET-CHECK-SUBSET`can be specified at most once.
|
||||
|
||||
- ```
|
||||
// --- <filename>
|
||||
```
|
||||
|
||||
By default, all file content is provided to the test as a single file in
|
||||
test_files. Using this marker allows the file to be split into multiple
|
||||
files which will all be passed to test_files.
|
||||
`test_files`. Using this marker allows the file to be split into multiple
|
||||
files which will all be passed to `test_files`.
|
||||
|
||||
Files are not created on disk; it's expected the child will create an
|
||||
InMemoryFilesystem if needed.
|
||||
Files are not created on disk; instead, content is passed in through the
|
||||
`fs` passed to `Run`.
|
||||
|
||||
If the filename is `STDIN`, it will be provided as `input_stream` instead of
|
||||
in `test_files`. Currently, autoupdate can place `CHECK` lines in the
|
||||
`STDIN` split; use `AUTOUPDATE-SPLIT` to avoid that (see `AUTOUPDATE` for
|
||||
information).
|
||||
|
||||
- ```
|
||||
// CHECK:STDOUT: <output line>
|
||||
|
||||
@@ -39,7 +39,7 @@ class FileTestAutoupdater {
|
||||
const llvm::SmallVector<llvm::StringRef>& filenames,
|
||||
int autoupdate_line_number, bool autoupdate_split,
|
||||
const llvm::SmallVector<FileTestLine>& non_check_lines,
|
||||
llvm::StringRef stdout, llvm::StringRef stderr,
|
||||
llvm::StringRef actual_stdout, llvm::StringRef actual_stderr,
|
||||
const std::optional<RE2>& default_file_re,
|
||||
const llvm::SmallVector<LineNumberReplacement>& line_number_replacements,
|
||||
std::function<void(std::string&)> do_extra_check_replacements)
|
||||
@@ -56,8 +56,8 @@ class FileTestAutoupdater {
|
||||
do_extra_check_replacements_(std::move(do_extra_check_replacements)),
|
||||
// BuildCheckLines should only be called after other member
|
||||
// initialization.
|
||||
stdout_(BuildCheckLines(stdout, "STDOUT")),
|
||||
stderr_(BuildCheckLines(stderr, "STDERR")),
|
||||
stdout_(BuildCheckLines(actual_stdout, "STDOUT")),
|
||||
stderr_(BuildCheckLines(actual_stderr, "STDERR")),
|
||||
any_attached_stdout_lines_(llvm::any_of(
|
||||
stdout_.lines,
|
||||
[&](const CheckLine& line) { return line.line_number() != -1; })),
|
||||
|
||||
@@ -58,6 +58,8 @@ using ::testing::Matcher;
|
||||
using ::testing::MatchesRegex;
|
||||
using ::testing::StrEq;
|
||||
|
||||
static constexpr llvm::StringLiteral StdinFilename = "STDIN";
|
||||
|
||||
// Reads a file to string.
|
||||
static auto ReadFile(std::string_view path) -> ErrorOr<std::string> {
|
||||
std::ifstream proto_file{std::string(path)};
|
||||
@@ -186,15 +188,15 @@ auto FileTestBase::TestBody() -> void {
|
||||
}
|
||||
SCOPED_TRACE(update_message);
|
||||
if (context.check_subset) {
|
||||
EXPECT_THAT(SplitOutput(context.stdout),
|
||||
EXPECT_THAT(SplitOutput(context.actual_stdout),
|
||||
IsSupersetOf(context.expected_stdout));
|
||||
EXPECT_THAT(SplitOutput(context.stderr),
|
||||
EXPECT_THAT(SplitOutput(context.actual_stderr),
|
||||
IsSupersetOf(context.expected_stderr));
|
||||
|
||||
} else {
|
||||
EXPECT_THAT(SplitOutput(context.stdout),
|
||||
EXPECT_THAT(SplitOutput(context.actual_stdout),
|
||||
ElementsAreArray(context.expected_stdout));
|
||||
EXPECT_THAT(SplitOutput(context.stderr),
|
||||
EXPECT_THAT(SplitOutput(context.actual_stderr),
|
||||
ElementsAreArray(context.expected_stderr));
|
||||
}
|
||||
|
||||
@@ -232,8 +234,9 @@ auto FileTestBase::RunAutoupdater(const TestContext& context, bool dry_run)
|
||||
GetBazelCommand(BazelMode::Test, test_name_),
|
||||
GetBazelCommand(BazelMode::Dump, test_name_),
|
||||
context.input_content, filenames, *context.autoupdate_line_number,
|
||||
context.autoupdate_split, context.non_check_lines, context.stdout,
|
||||
context.stderr, GetDefaultFileRE(expected_filenames),
|
||||
context.autoupdate_split, context.non_check_lines,
|
||||
context.actual_stdout, context.actual_stderr,
|
||||
GetDefaultFileRE(expected_filenames),
|
||||
GetLineNumberReplacements(expected_filenames),
|
||||
[&](std::string& line) { DoExtraCheckReplacements(line); })
|
||||
.Run(dry_run);
|
||||
@@ -266,7 +269,8 @@ auto FileTestBase::DumpOutput() -> ErrorOr<Success> {
|
||||
return ErrorBuilder() << "Error updating " << test_name_ << ": "
|
||||
<< run_result.error();
|
||||
}
|
||||
llvm::errs() << banner << context.stdout << banner << "= Exit with success: "
|
||||
llvm::errs() << banner << context.actual_stdout << banner
|
||||
<< "= Exit with success: "
|
||||
<< (context.run_result.success ? "true" : "false") << "\n"
|
||||
<< banner;
|
||||
return Success();
|
||||
@@ -297,18 +301,32 @@ auto FileTestBase::ProcessTestFileAndRun(TestContext& context)
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
DoArgReplacements(context.test_args, context.test_files));
|
||||
|
||||
// stdin needs to exist on-disk for compatibility. We'll use a pointer for it.
|
||||
FILE* input_stream = nullptr;
|
||||
auto erase_input_on_exit = llvm::make_scope_exit([&input_stream]() {
|
||||
if (input_stream) {
|
||||
// fclose should delete the tmpfile.
|
||||
fclose(input_stream);
|
||||
input_stream = nullptr;
|
||||
}
|
||||
});
|
||||
|
||||
// Create the files in-memory.
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs =
|
||||
new llvm::vfs::InMemoryFileSystem;
|
||||
for (const auto& test_file : context.test_files) {
|
||||
if (!fs->addFile(test_file.filename, /*ModificationTime=*/0,
|
||||
llvm::MemoryBuffer::getMemBuffer(
|
||||
test_file.content, test_file.filename,
|
||||
/*RequiresNullTerminator=*/false))) {
|
||||
if (test_file.filename == StdinFilename) {
|
||||
input_stream = tmpfile();
|
||||
fwrite(test_file.content.c_str(), sizeof(char), test_file.content.size(),
|
||||
input_stream);
|
||||
rewind(input_stream);
|
||||
} else if (!fs->addFile(test_file.filename, /*ModificationTime=*/0,
|
||||
llvm::MemoryBuffer::getMemBuffer(
|
||||
test_file.content, test_file.filename,
|
||||
/*RequiresNullTerminator=*/false))) {
|
||||
return ErrorBuilder() << "File is repeated: " << test_file.filename;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the arguments to StringRef and const char* to match the
|
||||
// expectations of PrettyStackTraceProgram and Run.
|
||||
llvm::SmallVector<llvm::StringRef> test_args_ref;
|
||||
@@ -340,20 +358,21 @@ auto FileTestBase::ProcessTestFileAndRun(TestContext& context)
|
||||
if (context.capture_console_output) {
|
||||
// No need to flush stderr.
|
||||
llvm::outs().flush();
|
||||
context.stdout += GetCapturedStdout();
|
||||
context.stderr += GetCapturedStderr();
|
||||
context.actual_stdout += GetCapturedStdout();
|
||||
context.actual_stderr += GetCapturedStderr();
|
||||
}
|
||||
});
|
||||
|
||||
// Prepare string streams to capture output. In order to address casting
|
||||
// constraints, we split calls to Run as a ternary based on whether we want to
|
||||
// capture output.
|
||||
llvm::raw_svector_ostream stdout(context.stdout);
|
||||
llvm::raw_svector_ostream stderr(context.stderr);
|
||||
llvm::raw_svector_ostream output_stream(context.actual_stdout);
|
||||
llvm::raw_svector_ostream error_stream(context.actual_stderr);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
context.run_result,
|
||||
context.dump_output ? Run(test_args_ref, fs, llvm::outs(), llvm::errs())
|
||||
: Run(test_args_ref, fs, stdout, stderr));
|
||||
context.dump_output
|
||||
? Run(test_args_ref, fs, input_stream, llvm::outs(), llvm::errs())
|
||||
: Run(test_args_ref, fs, input_stream, output_stream, error_stream));
|
||||
|
||||
return Success();
|
||||
}
|
||||
@@ -380,10 +399,11 @@ auto FileTestBase::DoArgReplacements(
|
||||
it = test_args.erase(it);
|
||||
for (const auto& file : test_files) {
|
||||
const std::string& filename = file.filename;
|
||||
if (!filename.ends_with(".h")) {
|
||||
it = test_args.insert(it, filename);
|
||||
++it;
|
||||
if (filename == StdinFilename || filename.ends_with(".h")) {
|
||||
continue;
|
||||
}
|
||||
it = test_args.insert(it, filename);
|
||||
++it;
|
||||
}
|
||||
// Back up once because the for loop will advance.
|
||||
--it;
|
||||
@@ -546,7 +566,6 @@ static auto AddTestFile(llvm::StringRef filename, std::string* content,
|
||||
llvm::SmallVector<FileTestBase::TestFile>* test_files)
|
||||
-> ErrorOr<Success> {
|
||||
CARBON_RETURN_IF_ERROR(ReplaceContentKeywords(filename, content));
|
||||
|
||||
test_files->push_back(
|
||||
{.filename = filename.str(), .content = std::move(*content)});
|
||||
content->clear();
|
||||
|
||||
@@ -68,9 +68,13 @@ class FileTestBase : public testing::Test {
|
||||
explicit FileTestBase(std::mutex* output_mutex, llvm::StringRef test_name)
|
||||
: output_mutex_(output_mutex), 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
|
||||
// more files.
|
||||
// Implemented by children to run the test. The framework will validate the
|
||||
// content written to `output_stream` and `error_stream`. Children should use
|
||||
// `fs` for file content, and may add more files.
|
||||
//
|
||||
// If there is a split test file named "STDIN", then its contents will be
|
||||
// provided at `input_stream` instead of `fs`. Otherwise, `input_stream` will
|
||||
// be null.
|
||||
//
|
||||
// Any test expectations should be called from ValidateRun, not Run.
|
||||
//
|
||||
@@ -78,8 +82,9 @@ class FileTestBase : public testing::Test {
|
||||
// RunResult otherwise.
|
||||
virtual auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
|
||||
llvm::raw_pwrite_stream& stdout,
|
||||
llvm::raw_pwrite_stream& stderr) -> ErrorOr<RunResult> = 0;
|
||||
FILE* input_stream, llvm::raw_pwrite_stream& output_stream,
|
||||
llvm::raw_pwrite_stream& error_stream)
|
||||
-> ErrorOr<RunResult> = 0;
|
||||
|
||||
// Implemented by children to do post-Run test expectations. Only called when
|
||||
// testing. Does not need to be provided if only CHECK test expectations are
|
||||
@@ -174,8 +179,8 @@ class FileTestBase : public testing::Test {
|
||||
llvm::SmallVector<testing::Matcher<std::string>> expected_stderr;
|
||||
|
||||
// stdout and stderr from Run. 16 is arbitrary but a required value.
|
||||
llvm::SmallString<16> stdout;
|
||||
llvm::SmallString<16> stderr;
|
||||
llvm::SmallString<16> actual_stdout;
|
||||
llvm::SmallString<16> actual_stderr;
|
||||
|
||||
RunResult run_result = {.success = false};
|
||||
};
|
||||
|
||||
@@ -23,7 +23,8 @@ class FileTestBaseTest : public FileTestBase {
|
||||
|
||||
auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
|
||||
llvm::raw_pwrite_stream& stdout, llvm::raw_pwrite_stream& stderr)
|
||||
FILE* input_stream, llvm::raw_pwrite_stream& output_stream,
|
||||
llvm::raw_pwrite_stream& error_stream)
|
||||
-> ErrorOr<RunResult> override;
|
||||
|
||||
auto GetArgReplacements() -> llvm::StringMap<std::string> override {
|
||||
@@ -55,13 +56,13 @@ class FileTestBaseTest : public FileTestBase {
|
||||
|
||||
// Prints arguments so that they can be validated in tests.
|
||||
static auto PrintArgs(llvm::ArrayRef<llvm::StringRef> args,
|
||||
llvm::raw_pwrite_stream& stdout) -> void {
|
||||
llvm::raw_pwrite_stream& output_stream) -> void {
|
||||
llvm::ListSeparator sep;
|
||||
stdout << args.size() << " args: ";
|
||||
output_stream << args.size() << " args: ";
|
||||
for (auto arg : args) {
|
||||
stdout << sep << "`" << arg << "`";
|
||||
output_stream << sep << "`" << arg << "`";
|
||||
}
|
||||
stdout << "\n";
|
||||
output_stream << "\n";
|
||||
}
|
||||
|
||||
// Verifies arguments are well-structured, and returns the files in them.
|
||||
@@ -85,113 +86,131 @@ static auto GetFilesFromArgs(llvm::ArrayRef<llvm::StringRef> args,
|
||||
struct TestParams {
|
||||
// These are the arguments to `Run()`.
|
||||
llvm::vfs::InMemoryFileSystem& fs;
|
||||
llvm::raw_pwrite_stream& stdout;
|
||||
llvm::raw_pwrite_stream& stderr;
|
||||
FILE* input_stream;
|
||||
llvm::raw_pwrite_stream& output_stream;
|
||||
llvm::raw_pwrite_stream& error_stream;
|
||||
|
||||
// This is assigned after construction.
|
||||
llvm::ArrayRef<llvm::StringRef> files;
|
||||
};
|
||||
|
||||
// Does printing and returns expected results for alternating_files.carbon.
|
||||
// Prints and returns expected results for alternating_files.carbon.
|
||||
static auto TestAlternatingFiles(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
params.stdout << "unattached message 1\n"
|
||||
<< "a.carbon:2: message 2\n"
|
||||
<< "b.carbon:5: message 3\n"
|
||||
<< "a.carbon:2: message 4\n"
|
||||
<< "b.carbon:5: message 5\n"
|
||||
<< "unattached message 6\n";
|
||||
params.stderr << "unattached message 1\n"
|
||||
<< "a.carbon:2: message 2\n"
|
||||
<< "b.carbon:5: message 3\n"
|
||||
<< "a.carbon:2: message 4\n"
|
||||
<< "b.carbon:5: message 5\n"
|
||||
<< "unattached message 6\n";
|
||||
params.output_stream << "unattached message 1\n"
|
||||
<< "a.carbon:2: message 2\n"
|
||||
<< "b.carbon:5: message 3\n"
|
||||
<< "a.carbon:2: message 4\n"
|
||||
<< "b.carbon:5: message 5\n"
|
||||
<< "unattached message 6\n";
|
||||
params.error_stream << "unattached message 1\n"
|
||||
<< "a.carbon:2: message 2\n"
|
||||
<< "b.carbon:5: message 3\n"
|
||||
<< "a.carbon:2: message 4\n"
|
||||
<< "b.carbon:5: message 5\n"
|
||||
<< "unattached message 6\n";
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for capture_console_output.carbon.
|
||||
// Prints and returns expected results for capture_console_output.carbon.
|
||||
static auto TestCaptureConsoleOutput(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
llvm::errs() << "llvm::errs\n";
|
||||
params.stderr << "params.stderr\n";
|
||||
params.error_stream << "params.error_stream\n";
|
||||
llvm::outs() << "llvm::outs\n";
|
||||
params.stdout << "params.stdout\n";
|
||||
params.output_stream << "params.output_stream\n";
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for example.carbon.
|
||||
// Prints and returns expected results for example.carbon.
|
||||
static auto TestExample(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
int delta_line = 10;
|
||||
params.stdout << "something\n"
|
||||
<< "\n"
|
||||
<< "example.carbon:" << delta_line + 1 << ": Line delta\n"
|
||||
<< "example.carbon:" << delta_line << ": Negative line delta\n"
|
||||
<< "+*[]{}\n"
|
||||
<< "Foo baz\n";
|
||||
params.output_stream << "something\n"
|
||||
<< "\n"
|
||||
<< "example.carbon:" << delta_line + 1
|
||||
<< ": Line delta\n"
|
||||
<< "example.carbon:" << delta_line
|
||||
<< ": Negative line delta\n"
|
||||
<< "+*[]{}\n"
|
||||
<< "Foo baz\n";
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for fail_example.carbon.
|
||||
// Prints and returns expected results for fail_example.carbon.
|
||||
static auto TestFailExample(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
params.stderr << "Oops\n";
|
||||
params.error_stream << "Oops\n";
|
||||
return {{.success = false}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for
|
||||
// Prints and returns expected results for
|
||||
// file_only_re_multi_file.carbon.
|
||||
static auto TestFileOnlyREMultiFile(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
int msg_count = 0;
|
||||
params.stdout << "unattached message " << ++msg_count << "\n"
|
||||
<< "file: a.carbon\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "line: 3: attached message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "line: 8: late message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "file: b.carbon\n"
|
||||
<< "line: 2: attached message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "line: 7: late message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n";
|
||||
params.output_stream << "unattached message " << ++msg_count << "\n"
|
||||
<< "file: a.carbon\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "line: 3: attached message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "line: 8: late message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "file: b.carbon\n"
|
||||
<< "line: 2: attached message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n"
|
||||
<< "line: 7: late message " << ++msg_count << "\n"
|
||||
<< "unattached message " << ++msg_count << "\n";
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for file_only_re_one_file.carbon.
|
||||
// Prints and returns expected results for file_only_re_one_file.carbon.
|
||||
static auto TestFileOnlyREOneFile(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
params.stdout << "unattached message 1\n"
|
||||
<< "file: file_only_re_one_file.carbon\n"
|
||||
<< "line: 1\n"
|
||||
<< "unattached message 2\n";
|
||||
params.output_stream << "unattached message 1\n"
|
||||
<< "file: file_only_re_one_file.carbon\n"
|
||||
<< "line: 1\n"
|
||||
<< "unattached message 2\n";
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for no_line_number.carbon.
|
||||
// Prints and returns expected results for no_line_number.carbon.
|
||||
static auto TestNoLineNumber(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
params.stdout << "a.carbon: msg1\n"
|
||||
"msg2\n"
|
||||
"b.carbon: msg3\n"
|
||||
"msg4\n"
|
||||
"a.carbon: msg5\n";
|
||||
params.output_stream << "a.carbon: msg1\n"
|
||||
"msg2\n"
|
||||
"b.carbon: msg3\n"
|
||||
"msg4\n"
|
||||
"a.carbon: msg5\n";
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for unattached_multi_file.carbon.
|
||||
// Prints and returns expected results for stdin.carbon.
|
||||
static auto TestStdin(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
CARBON_CHECK(params.input_stream);
|
||||
constexpr int ReadSize = 256;
|
||||
char buf[ReadSize];
|
||||
while (feof(params.input_stream) == 0) {
|
||||
auto read = fread(&buf, sizeof(char), ReadSize, params.input_stream);
|
||||
if (read > 0) {
|
||||
params.error_stream.write(buf, read);
|
||||
}
|
||||
}
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Prints and returns expected results for unattached_multi_file.carbon.
|
||||
static auto TestUnattachedMultiFile(TestParams& params)
|
||||
-> ErrorOr<FileTestBaseTest::RunResult> {
|
||||
params.stdout << "unattached message 1\n"
|
||||
<< "unattached message 2\n";
|
||||
params.stderr << "unattached message 3\n"
|
||||
<< "unattached message 4\n";
|
||||
params.output_stream << "unattached message 1\n"
|
||||
<< "unattached message 2\n";
|
||||
params.error_stream << "unattached message 3\n"
|
||||
<< "unattached message 4\n";
|
||||
return {{.success = true}};
|
||||
}
|
||||
|
||||
// Does printing and returns expected results for:
|
||||
// Prints and returns expected results for:
|
||||
// - fail_multi_success_overall_fail.carbon
|
||||
// - multi_success.carbon
|
||||
// - multi_success_and_fail.carbon
|
||||
@@ -220,8 +239,8 @@ static auto EchoFileContent(TestParams& params)
|
||||
for (int line_number = 1; !buffer.empty(); ++line_number) {
|
||||
auto [line, remainder] = buffer.split('\n');
|
||||
if (!line.empty() && !line.starts_with("//")) {
|
||||
params.stdout << test_file << ":" << line_number << ": " << line
|
||||
<< "\n";
|
||||
params.output_stream << test_file << ":" << line_number << ": " << line
|
||||
<< "\n";
|
||||
}
|
||||
buffer = remainder;
|
||||
}
|
||||
@@ -232,9 +251,9 @@ static auto EchoFileContent(TestParams& params)
|
||||
auto FileTestBaseTest::Run(
|
||||
const llvm::SmallVector<llvm::StringRef>& test_args,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
|
||||
llvm::raw_pwrite_stream& stdout, llvm::raw_pwrite_stream& stderr)
|
||||
-> ErrorOr<RunResult> {
|
||||
PrintArgs(test_args, stdout);
|
||||
FILE* input_stream, llvm::raw_pwrite_stream& output_stream,
|
||||
llvm::raw_pwrite_stream& error_stream) -> ErrorOr<RunResult> {
|
||||
PrintArgs(test_args, output_stream);
|
||||
|
||||
auto filename = std::filesystem::path(test_name().str()).filename();
|
||||
if (filename == "args.carbon") {
|
||||
@@ -254,6 +273,8 @@ auto FileTestBaseTest::Run(
|
||||
.Case("file_only_re_one_file.carbon", &TestFileOnlyREOneFile)
|
||||
.Case("file_only_re_multi_file.carbon", &TestFileOnlyREMultiFile)
|
||||
.Case("no_line_number.carbon", &TestNoLineNumber)
|
||||
.Case("stdin.carbon", &TestStdin)
|
||||
.Case("stdin_and_autoupdate_split.carbon", &TestStdin)
|
||||
.Case("unattached_multi_file.carbon", &TestUnattachedMultiFile)
|
||||
.Case("fail_multi_success_overall_fail.carbon",
|
||||
[&](TestParams&) {
|
||||
@@ -273,7 +294,10 @@ auto FileTestBaseTest::Run(
|
||||
.Default(&EchoFileContent);
|
||||
|
||||
// Call the appropriate test function for the file.
|
||||
TestParams params = {.fs = *fs, .stdout = stdout, .stderr = stderr};
|
||||
TestParams params = {.fs = *fs,
|
||||
.input_stream = input_stream,
|
||||
.output_stream = output_stream,
|
||||
.error_stream = error_stream};
|
||||
CARBON_ASSIGN_OR_RETURN(params.files, GetFilesFromArgs(test_args, *fs));
|
||||
return test_fn(params);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/capture_console_output.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/capture_console_output.carbon
|
||||
// CHECK:STDERR: params.stderr
|
||||
// CHECK:STDERR: params.error_stream
|
||||
// CHECK:STDERR: llvm::errs
|
||||
|
||||
// CHECK:STDOUT: 2 args: `default_args`, `capture_console_output.carbon`
|
||||
// CHECK:STDOUT: params.stdout
|
||||
// CHECK:STDOUT: params.output_stream
|
||||
// CHECK:STDOUT: llvm::outs
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// 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
|
||||
|
||||
// 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/stdin.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/stdin.carbon
|
||||
|
||||
// --- a.carbon
|
||||
|
||||
// --- STDIN
|
||||
|
||||
S
|
||||
t
|
||||
d
|
||||
i
|
||||
n
|
||||
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: S
|
||||
// CHECK:STDERR: t
|
||||
// CHECK:STDERR: d
|
||||
// CHECK:STDERR: i
|
||||
// CHECK:STDERR: n
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: 2 args: `default_args`, `a.carbon`
|
||||
@@ -35,7 +35,8 @@ class ToolchainFileTest : public FileTestBase {
|
||||
|
||||
auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
|
||||
llvm::raw_pwrite_stream& stdout, llvm::raw_pwrite_stream& stderr)
|
||||
FILE* input_stream, llvm::raw_pwrite_stream& output_stream,
|
||||
llvm::raw_pwrite_stream& error_stream)
|
||||
-> ErrorOr<RunResult> override {
|
||||
CARBON_ASSIGN_OR_RETURN(auto prelude, installation_.ReadPreludeManifest());
|
||||
if (!is_no_prelude()) {
|
||||
@@ -46,16 +47,16 @@ class ToolchainFileTest : public FileTestBase {
|
||||
|
||||
Driver driver({.fs = fs,
|
||||
.installation = &installation_,
|
||||
.input_stream = nullptr,
|
||||
.output_stream = &stdout,
|
||||
.error_stream = &stderr});
|
||||
.input_stream = input_stream,
|
||||
.output_stream = &output_stream,
|
||||
.error_stream = &error_stream});
|
||||
auto driver_result = driver.RunCommand(test_args);
|
||||
// If any diagnostics have been produced, add a trailing newline to make the
|
||||
// last diagnostic match intermediate diagnostics (that have a newline
|
||||
// separator between them). This reduces churn when adding new diagnostics
|
||||
// to test cases.
|
||||
if (stderr.tell() > 0) {
|
||||
stderr << '\n';
|
||||
if (error_stream.tell() > 0) {
|
||||
error_stream << '\n';
|
||||
}
|
||||
|
||||
RunResult result{
|
||||
|
||||
Reference in New Issue
Block a user