ls-message-unittest.cc 8.53 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/torque/ls/json.h"
#include "src/torque/ls/message-handler.h"
#include "src/torque/ls/message.h"
#include "src/torque/server-data.h"
#include "src/torque/source-positions.h"
10
#include "test/unittests/test-utils.h"
11 12 13 14 15 16

namespace v8 {
namespace internal {
namespace torque {
namespace ls {

17
TEST(LanguageServerMessage, InitializeRequest) {
18 19 20 21 22
  InitializeRequest request;
  request.set_id(5);
  request.set_method("initialize");
  request.params();

23
  bool writer_called = false;
24 25
  HandleMessage(std::move(request.GetJsonValue()), [&](JsonValue raw_response) {
    InitializeResponse response(std::move(raw_response));
26 27 28

    // Check that the response id matches up with the request id, and that
    // the language server signals its support for definitions.
29
    EXPECT_EQ(response.id(), 5);
30 31
    EXPECT_TRUE(response.result().capabilities().definitionProvider());
    EXPECT_TRUE(response.result().capabilities().documentSymbolProvider());
32 33

    writer_called = true;
34
  });
35
  EXPECT_TRUE(writer_called);
36 37
}

38 39
TEST(LanguageServerMessage,
     RegisterDynamicCapabilitiesAfterInitializedNotification) {
40 41 42
  Request<bool> notification;
  notification.set_method("initialized");

43
  bool writer_called = false;
44 45 46
  HandleMessage(std::move(notification.GetJsonValue()), [&](JsonValue
                                                                raw_request) {
    RegistrationRequest request(std::move(raw_request));
47

48 49
    ASSERT_EQ(request.method(), "client/registerCapability");
    ASSERT_EQ(request.params().registrations_size(), (size_t)1);
50 51

    Registration registration = request.params().registrations(0);
52
    ASSERT_EQ(registration.method(), "workspace/didChangeWatchedFiles");
53 54 55 56

    auto options =
        registration
            .registerOptions<DidChangeWatchedFilesRegistrationOptions>();
57
    ASSERT_EQ(options.watchers_size(), (size_t)1);
58 59

    writer_called = true;
60
  });
61
  EXPECT_TRUE(writer_called);
62 63
}

64
TEST(LanguageServerMessage, GotoDefinitionUnkownFile) {
65
  SourceFileMap::Scope source_file_map_scope("");
66 67 68 69 70 71

  GotoDefinitionRequest request;
  request.set_id(42);
  request.set_method("textDocument/definition");
  request.params().textDocument().set_uri("file:///unknown.tq");

72
  bool writer_called = false;
73 74
  HandleMessage(std::move(request.GetJsonValue()), [&](JsonValue raw_response) {
    GotoDefinitionResponse response(std::move(raw_response));
75 76
    EXPECT_EQ(response.id(), 42);
    EXPECT_TRUE(response.IsNull("result"));
77 78

    writer_called = true;
79
  });
80
  EXPECT_TRUE(writer_called);
81 82
}

83
TEST(LanguageServerMessage, GotoDefinition) {
84
  SourceFileMap::Scope source_file_map_scope("");
85 86
  SourceId test_id = SourceFileMap::AddSource("file://test.tq");
  SourceId definition_id = SourceFileMap::AddSource("file://base.tq");
87 88

  LanguageServerData::Scope server_data_scope;
89 90 91 92 93
  LanguageServerData::AddDefinition(
      {test_id, LineAndColumn::WithUnknownOffset(1, 0),
       LineAndColumn::WithUnknownOffset(1, 10)},
      {definition_id, LineAndColumn::WithUnknownOffset(4, 1),
       LineAndColumn::WithUnknownOffset(4, 5)});
94 95 96 97 98 99 100 101 102

  // First, check a unknown definition. The result must be null.
  GotoDefinitionRequest request;
  request.set_id(42);
  request.set_method("textDocument/definition");
  request.params().textDocument().set_uri("file://test.tq");
  request.params().position().set_line(2);
  request.params().position().set_character(0);

103
  bool writer_called = false;
104 105
  HandleMessage(std::move(request.GetJsonValue()), [&](JsonValue raw_response) {
    GotoDefinitionResponse response(std::move(raw_response));
106 107
    EXPECT_EQ(response.id(), 42);
    EXPECT_TRUE(response.IsNull("result"));
108 109

    writer_called = true;
110
  });
111
  EXPECT_TRUE(writer_called);
112 113 114 115 116 117 118 119 120

  // Second, check a known defintion.
  request = GotoDefinitionRequest();
  request.set_id(43);
  request.set_method("textDocument/definition");
  request.params().textDocument().set_uri("file://test.tq");
  request.params().position().set_line(1);
  request.params().position().set_character(5);

121
  writer_called = false;
122 123
  HandleMessage(std::move(request.GetJsonValue()), [&](JsonValue raw_response) {
    GotoDefinitionResponse response(std::move(raw_response));
124 125
    EXPECT_EQ(response.id(), 43);
    ASSERT_FALSE(response.IsNull("result"));
126 127

    Location location = response.result();
128 129 130 131 132
    EXPECT_EQ(location.uri(), "file://base.tq");
    EXPECT_EQ(location.range().start().line(), 4);
    EXPECT_EQ(location.range().start().character(), 1);
    EXPECT_EQ(location.range().end().line(), 4);
    EXPECT_EQ(location.range().end().character(), 5);
133 134

    writer_called = true;
135
  });
136
  EXPECT_TRUE(writer_called);
137 138
}

139
TEST(LanguageServerMessage, CompilationErrorSendsDiagnostics) {
140
  DiagnosticsFiles::Scope diagnostic_files_scope;
141
  LanguageServerData::Scope server_data_scope;
142
  TorqueMessages::Scope messages_scope;
143
  SourceFileMap::Scope source_file_map_scope("");
144 145

  TorqueCompilerResult result;
146 147
  { Error("compilation failed somehow"); }
  result.messages = std::move(TorqueMessages::Get());
148 149
  result.source_file_map = SourceFileMap::Get();

150
  bool writer_called = false;
151 152
  CompilationFinished(std::move(result), [&](JsonValue raw_response) {
    PublishDiagnosticsNotification notification(std::move(raw_response));
153 154 155 156 157 158 159 160 161

    EXPECT_EQ(notification.method(), "textDocument/publishDiagnostics");
    ASSERT_FALSE(notification.IsNull("params"));
    EXPECT_EQ(notification.params().uri(), "<unknown>");

    ASSERT_GT(notification.params().diagnostics_size(), static_cast<size_t>(0));
    Diagnostic diagnostic = notification.params().diagnostics(0);
    EXPECT_EQ(diagnostic.severity(), Diagnostic::kError);
    EXPECT_EQ(diagnostic.message(), "compilation failed somehow");
162 163

    writer_called = true;
164
  });
165
  EXPECT_TRUE(writer_called);
166 167
}

168 169
TEST(LanguageServerMessage, LintErrorSendsDiagnostics) {
  DiagnosticsFiles::Scope diagnostic_files_scope;
170
  TorqueMessages::Scope messages_scope;
171
  LanguageServerData::Scope server_data_scope;
172 173
  SourceFileMap::Scope sourc_file_map_scope("");
  SourceId test_id = SourceFileMap::AddSource("file://test.tq");
174 175

  // No compilation errors but two lint warnings.
176
  {
177 178 179 180
    SourcePosition pos1{test_id, LineAndColumn::WithUnknownOffset(0, 0),
                        LineAndColumn::WithUnknownOffset(0, 1)};
    SourcePosition pos2{test_id, LineAndColumn::WithUnknownOffset(1, 0),
                        LineAndColumn::WithUnknownOffset(1, 1)};
181 182 183 184
    Lint("lint error 1").Position(pos1);
    Lint("lint error 2").Position(pos2);
  }

185
  TorqueCompilerResult result;
186
  result.messages = std::move(TorqueMessages::Get());
187 188
  result.source_file_map = SourceFileMap::Get();

189
  bool writer_called = false;
190 191
  CompilationFinished(std::move(result), [&](JsonValue raw_response) {
    PublishDiagnosticsNotification notification(std::move(raw_response));
192 193 194

    EXPECT_EQ(notification.method(), "textDocument/publishDiagnostics");
    ASSERT_FALSE(notification.IsNull("params"));
195
    EXPECT_EQ(notification.params().uri(), "file://test.tq");
196 197 198 199 200 201 202 203 204

    ASSERT_EQ(notification.params().diagnostics_size(), static_cast<size_t>(2));
    Diagnostic diagnostic1 = notification.params().diagnostics(0);
    EXPECT_EQ(diagnostic1.severity(), Diagnostic::kWarning);
    EXPECT_EQ(diagnostic1.message(), "lint error 1");

    Diagnostic diagnostic2 = notification.params().diagnostics(1);
    EXPECT_EQ(diagnostic2.severity(), Diagnostic::kWarning);
    EXPECT_EQ(diagnostic2.message(), "lint error 2");
205 206

    writer_called = true;
207
  });
208
  EXPECT_TRUE(writer_called);
209 210 211 212
}

TEST(LanguageServerMessage, CleanCompileSendsNoDiagnostics) {
  LanguageServerData::Scope server_data_scope;
213
  SourceFileMap::Scope sourc_file_map_scope("");
214 215 216 217

  TorqueCompilerResult result;
  result.source_file_map = SourceFileMap::Get();

218
  CompilationFinished(std::move(result), [](JsonValue raw_response) {
219 220 221 222
    FAIL() << "Sending unexpected response!";
  });
}

223 224
TEST(LanguageServerMessage, NoSymbolsSendsEmptyResponse) {
  LanguageServerData::Scope server_data_scope;
225
  SourceFileMap::Scope sourc_file_map_scope("");
226 227 228 229

  DocumentSymbolRequest request;
  request.set_id(42);
  request.set_method("textDocument/documentSymbol");
230
  request.params().textDocument().set_uri("file://test.tq");
231

232
  bool writer_called = false;
233 234
  HandleMessage(std::move(request.GetJsonValue()), [&](JsonValue raw_response) {
    DocumentSymbolResponse response(std::move(raw_response));
235 236
    EXPECT_EQ(response.id(), 42);
    EXPECT_EQ(response.result_size(), static_cast<size_t>(0));
237 238

    writer_called = true;
239
  });
240
  EXPECT_TRUE(writer_called);
241 242
}

243 244 245 246
}  // namespace ls
}  // namespace torque
}  // namespace internal
}  // namespace v8