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:
Jon Ross-Perkins
2025-01-21 22:04:13 +00:00
committed by GitHub
parent 667a010ae6
commit e393c769af
9 changed files with 225 additions and 136 deletions
+6 -4
View File
@@ -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
View File
@@ -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>
+3 -3
View File
@@ -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; })),
+41 -22
View File
@@ -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();
+12 -7
View File
@@ -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};
};
+92 -68
View File
@@ -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);
}
+2 -2
View File
@@ -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
View File
@@ -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`
+7 -6
View File
@@ -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{