Skip to content

Commit b811ad6

Browse files
authored
[mlir-lsp] Un-revert unit test additions (#90232)
This reverts the revert commit 6844c2f, which was comprised of the following commits: 1. f3f6f22 - [mlir-lsp] Initialize `Reply::method` member (#89857) 2. 37e13d4 - [mlir-lsp] Log invalid notification params (#89856) 3. ba1b52e - [mlir-lsp] Add `outgoingNotification` unit test 4. 84bc21f - [mlir-lsp] Add transport unit tests (#89855) Of these, (4) specifically caused issues in Windows pre-merge buildbots, in the `TransportTest.MethodNotFound` unit test that it added. The failure was caused by a statement that asserted that opening a file stream on a newly created temporary file did not result in an error, but this assert failed on Windows. This patch adds additional error logging for failures, to make it clearer what went wrong when failures occur. This patch also addresses the Windows failure by ensuring temporary files are created in the system temporary directory.
1 parent f6187c7 commit b811ad6

File tree

6 files changed

+154
-6
lines changed

6 files changed

+154
-6
lines changed

mlir/include/mlir/Tools/lsp-server-support/Transport.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,15 @@ class MessageHandler {
147147
void (ThisT::*handler)(const Param &)) {
148148
notificationHandlers[method] = [method, handler,
149149
thisPtr](llvm::json::Value rawParams) {
150-
llvm::Expected<Param> param = parse<Param>(rawParams, method, "request");
151-
if (!param)
152-
return llvm::consumeError(param.takeError());
150+
llvm::Expected<Param> param =
151+
parse<Param>(rawParams, method, "notification");
152+
if (!param) {
153+
return llvm::consumeError(
154+
llvm::handleErrors(param.takeError(), [](const LSPError &lspError) {
155+
Logger::error("JSON parsing error: {0}",
156+
lspError.message.c_str());
157+
}));
158+
}
153159
(thisPtr->*handler)(*param);
154160
};
155161
}

mlir/lib/Tools/lsp-server-support/Transport.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ class Reply {
5151

5252
Reply::Reply(const llvm::json::Value &id, llvm::StringRef method,
5353
JSONTransport &transport, std::mutex &transportOutputMutex)
54-
: id(id), transport(&transport),
54+
: method(method), id(id), transport(&transport),
5555
transportOutputMutex(transportOutputMutex) {}
5656

5757
Reply::Reply(Reply &&other)
58-
: replied(other.replied.load()), id(std::move(other.id)),
59-
transport(other.transport),
58+
: method(other.method), replied(other.replied.load()),
59+
id(std::move(other.id)), transport(other.transport),
6060
transportOutputMutex(other.transportOutputMutex) {
6161
other.transport = nullptr;
6262
}

mlir/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_subdirectory(Support)
2020
add_subdirectory(Rewrite)
2121
add_subdirectory(TableGen)
2222
add_subdirectory(Target)
23+
add_subdirectory(Tools)
2324
add_subdirectory(Transforms)
2425

2526
if(MLIR_ENABLE_EXECUTION_ENGINE)

mlir/unittests/Tools/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_subdirectory(lsp-server-support)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
add_mlir_unittest(MLIRLspServerSupportTests
2+
Transport.cpp
3+
)
4+
target_link_libraries(MLIRLspServerSupportTests
5+
PRIVATE
6+
MLIRLspServerSupportLib)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//===- Transport.cpp - LSP JSON transport unit tests ----------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "mlir/Tools/lsp-server-support/Transport.h"
10+
#include "mlir/Tools/lsp-server-support/Logging.h"
11+
#include "mlir/Tools/lsp-server-support/Protocol.h"
12+
#include "llvm/Support/FileSystem.h"
13+
#include "gmock/gmock.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace mlir;
17+
using namespace mlir::lsp;
18+
using namespace testing;
19+
20+
namespace {
21+
22+
TEST(TransportTest, SendReply) {
23+
std::string out;
24+
llvm::raw_string_ostream os(out);
25+
JSONTransport transport(nullptr, os);
26+
MessageHandler handler(transport);
27+
28+
transport.reply(1989, nullptr);
29+
EXPECT_THAT(out, HasSubstr("\"id\":1989"));
30+
EXPECT_THAT(out, HasSubstr("\"result\":null"));
31+
}
32+
33+
class TransportInputTest : public Test {
34+
llvm::SmallVector<char> inputPath;
35+
std::FILE *in = nullptr;
36+
std::string output = "";
37+
llvm::raw_string_ostream os;
38+
std::optional<JSONTransport> transport = std::nullopt;
39+
std::optional<MessageHandler> messageHandler = std::nullopt;
40+
41+
protected:
42+
TransportInputTest() : os(output) {}
43+
44+
void SetUp() override {
45+
std::error_code ec =
46+
llvm::sys::fs::createTemporaryFile("lsp-unittest", "json", inputPath);
47+
ASSERT_FALSE(ec) << "Could not create temporary file: " << ec.message();
48+
49+
in = std::fopen(inputPath.data(), "r");
50+
ASSERT_TRUE(in) << "Could not open temporary file: "
51+
<< std::strerror(errno);
52+
transport.emplace(in, os, JSONStreamStyle::Delimited);
53+
messageHandler.emplace(*transport);
54+
}
55+
56+
void TearDown() override {
57+
EXPECT_EQ(std::fclose(in), 0)
58+
<< "Could not close temporary file FD: " << std::strerror(errno);
59+
std::error_code ec =
60+
llvm::sys::fs::remove(inputPath, /*IgnoreNonExisting=*/false);
61+
EXPECT_FALSE(ec) << "Could not remove temporary file '" << inputPath.data()
62+
<< "': " << ec.message();
63+
}
64+
65+
void writeInput(StringRef buffer) {
66+
std::error_code ec;
67+
llvm::raw_fd_ostream os(inputPath.data(), ec);
68+
ASSERT_FALSE(ec) << "Could not write to '" << inputPath.data()
69+
<< "': " << ec.message();
70+
os << buffer;
71+
os.close();
72+
}
73+
74+
StringRef getOutput() const { return output; }
75+
MessageHandler &getMessageHandler() { return *messageHandler; }
76+
77+
void runTransport() {
78+
bool gotEOF = false;
79+
llvm::Error err = llvm::handleErrors(
80+
transport->run(*messageHandler), [&](const llvm::ECError &ecErr) {
81+
gotEOF = ecErr.convertToErrorCode() == std::errc::io_error;
82+
});
83+
llvm::consumeError(std::move(err));
84+
EXPECT_TRUE(gotEOF);
85+
}
86+
};
87+
88+
TEST_F(TransportInputTest, RequestWithInvalidParams) {
89+
struct Handler {
90+
void onMethod(const TextDocumentItem &params,
91+
mlir::lsp::Callback<TextDocumentIdentifier> callback) {}
92+
} handler;
93+
getMessageHandler().method("invalid-params-request", &handler,
94+
&Handler::onMethod);
95+
96+
writeInput("{\"jsonrpc\":\"2.0\",\"id\":92,"
97+
"\"method\":\"invalid-params-request\",\"params\":{}}\n");
98+
runTransport();
99+
EXPECT_THAT(getOutput(), HasSubstr("error"));
100+
EXPECT_THAT(getOutput(), HasSubstr("missing value at (root).uri"));
101+
}
102+
103+
TEST_F(TransportInputTest, NotificationWithInvalidParams) {
104+
// JSON parsing errors are only reported via error logging. As a result, this
105+
// test can't make any expectations -- but it prints the output anyway, by way
106+
// of demonstration.
107+
Logger::setLogLevel(Logger::Level::Error);
108+
109+
struct Handler {
110+
void onNotification(const TextDocumentItem &params) {}
111+
} handler;
112+
getMessageHandler().notification("invalid-params-notification", &handler,
113+
&Handler::onNotification);
114+
115+
writeInput("{\"jsonrpc\":\"2.0\",\"method\":\"invalid-params-notification\","
116+
"\"params\":{}}\n");
117+
runTransport();
118+
}
119+
120+
TEST_F(TransportInputTest, MethodNotFound) {
121+
writeInput("{\"jsonrpc\":\"2.0\",\"id\":29,\"method\":\"ack\"}\n");
122+
runTransport();
123+
EXPECT_THAT(getOutput(), HasSubstr("\"id\":29"));
124+
EXPECT_THAT(getOutput(), HasSubstr("\"error\""));
125+
EXPECT_THAT(getOutput(), HasSubstr("\"message\":\"method not found: ack\""));
126+
}
127+
128+
TEST_F(TransportInputTest, OutgoingNotification) {
129+
auto notifyFn = getMessageHandler().outgoingNotification<CompletionList>(
130+
"outgoing-notification");
131+
notifyFn(CompletionList{});
132+
EXPECT_THAT(getOutput(), HasSubstr("\"method\":\"outgoing-notification\""));
133+
}
134+
} // namespace

0 commit comments

Comments
 (0)