mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 17:31:07 +01:00
Add a framework for LSP testing. (#4841)
Also adds tests for exit and initialize, just basic things.
This commit is contained in:
@@ -344,13 +344,16 @@ auto FileTestBase::ProcessTestFileAndRun(TestContext& context)
|
||||
llvm::PrettyStackTraceProgram stack_trace_entry(
|
||||
test_argv_for_stack_trace.size() - 1, test_argv_for_stack_trace.data());
|
||||
|
||||
// Execution must be serialized for either serial tests or console output.
|
||||
std::unique_lock<std::mutex> output_lock;
|
||||
if (output_mutex_ &&
|
||||
(context.capture_console_output || !AllowParallelRun())) {
|
||||
output_lock = std::unique_lock<std::mutex>(*output_mutex_);
|
||||
}
|
||||
|
||||
// Conditionally capture console output. We use a scope exit to ensure the
|
||||
// captures terminate even on run failures.
|
||||
std::unique_lock<std::mutex> output_lock;
|
||||
if (context.capture_console_output) {
|
||||
if (output_mutex_) {
|
||||
output_lock = std::unique_lock<std::mutex>(*output_mutex_);
|
||||
}
|
||||
CaptureStderr();
|
||||
CaptureStdout();
|
||||
}
|
||||
@@ -514,6 +517,84 @@ struct SplitState {
|
||||
int file_index = 0;
|
||||
};
|
||||
|
||||
// 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,
|
||||
llvm::StringRef test_name, int* lsp_id)
|
||||
-> ErrorOr<size_t> {
|
||||
auto keyword = llvm::StringRef(*content).substr(keyword_pos);
|
||||
|
||||
// Line replacements aren't handled here.
|
||||
static constexpr llvm::StringLiteral Line = "[[@LINE";
|
||||
if (keyword.starts_with(Line)) {
|
||||
// Just move past the prefix to find the next one.
|
||||
return keyword_pos + Line.size();
|
||||
}
|
||||
|
||||
// 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);
|
||||
return keyword_pos + test_name.size();
|
||||
}
|
||||
|
||||
// Reformatted as an LSP call with headers.
|
||||
static constexpr llvm::StringLiteral Lsp = "[[@LSP:";
|
||||
if (keyword.starts_with(Lsp)) {
|
||||
auto method_start = keyword_pos + Lsp.size();
|
||||
|
||||
static constexpr llvm::StringLiteral LspEnd = "]]";
|
||||
auto keyword_end = content->find("]]", method_start);
|
||||
if (keyword_end == std::string::npos) {
|
||||
return ErrorBuilder()
|
||||
<< "Missing `" << LspEnd << "` after `" << Lsp << "`";
|
||||
}
|
||||
|
||||
auto method_end = content->find(":", method_start);
|
||||
auto extra_content_start = method_end + 1;
|
||||
if (method_end == std::string::npos || method_end > keyword_end) {
|
||||
method_end = keyword_end;
|
||||
extra_content_start = keyword_end;
|
||||
}
|
||||
auto method = content->substr(method_start, method_end - method_start);
|
||||
|
||||
auto extra_content =
|
||||
content->substr(extra_content_start, keyword_end - extra_content_start);
|
||||
std::string extra_content_sep;
|
||||
if (!extra_content.empty()) {
|
||||
extra_content_sep = ",";
|
||||
if (!extra_content.starts_with("\n")) {
|
||||
extra_content_sep += " ";
|
||||
}
|
||||
}
|
||||
|
||||
// Form the JSON.
|
||||
std::string json;
|
||||
if (method == "exit") {
|
||||
if (!extra_content.empty()) {
|
||||
return Error("`[[@LSP:exit:` cannot include extra content");
|
||||
}
|
||||
json = R"({"jsonrpc": "2.0", "method": "exit"})";
|
||||
} else {
|
||||
json = llvm::formatv(
|
||||
R"({{"jsonrpc": "2.0", "id": "{0}", "method": "{1}"{2}{3}})",
|
||||
++(*lsp_id), method, extra_content_sep, extra_content)
|
||||
.str();
|
||||
}
|
||||
// Add the Content-Length header. The `2` accounts for extra newlines.
|
||||
auto json_with_header =
|
||||
llvm::formatv("Content-Length: {0}\n\n{1}\n", json.size() + 2, json)
|
||||
.str();
|
||||
// Insert the content.
|
||||
content->replace(keyword_pos, keyword_end + 2 - keyword_pos,
|
||||
json_with_header);
|
||||
return keyword_pos + json_with_header.size();
|
||||
}
|
||||
|
||||
return ErrorBuilder() << "Unexpected use of `[[@` at `"
|
||||
<< keyword.substr(0, 5) << "`";
|
||||
}
|
||||
|
||||
// Replaces the content keywords.
|
||||
//
|
||||
// TEST_NAME is the only content keyword at present, but we do validate that
|
||||
@@ -543,20 +624,13 @@ static auto ReplaceContentKeywords(llvm::StringRef filename,
|
||||
test_name.consume_front("fail_");
|
||||
test_name.consume_front("todo_");
|
||||
|
||||
// A counter for LSP calls.
|
||||
int lsp_id = 0;
|
||||
while (keyword_pos != std::string::npos) {
|
||||
static constexpr llvm::StringLiteral TestName = "[[@TEST_NAME]]";
|
||||
auto keyword = llvm::StringRef(*content).substr(keyword_pos);
|
||||
if (keyword.starts_with(TestName)) {
|
||||
content->replace(keyword_pos, TestName.size(), test_name);
|
||||
keyword_pos += test_name.size();
|
||||
} else if (keyword.starts_with("[[@LINE")) {
|
||||
// Just move past the prefix to find the next one.
|
||||
keyword_pos += Prefix.size();
|
||||
} else {
|
||||
return ErrorBuilder()
|
||||
<< "Unexpected use of `[[@` at `" << keyword.substr(0, 5) << "`";
|
||||
}
|
||||
keyword_pos = content->find(Prefix, keyword_pos);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
auto keyword_end,
|
||||
ReplaceContentKeywordAt(content, keyword_pos, test_name, &lsp_id));
|
||||
keyword_pos = content->find(Prefix, keyword_end);
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user