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
+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);
}