1 /**
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <string>
17 #include <vector>
18 #include "lsp_api_test.h"
19 #include "lsp/include/internal_api.h"
20 #include "util/arktsconfig.h"
21 #include "public/public.h"
22
23 using ark::es2panda::lsp::Initializer;
24
25 class LSPGetCompilerOptionsDiagnosticsTests : public LSPAPITests {
26 public:
27 LSPGetCompilerOptionsDiagnosticsTests() = default;
28 ~LSPGetCompilerOptionsDiagnosticsTests() override = default;
29
30 NO_COPY_SEMANTIC(LSPGetCompilerOptionsDiagnosticsTests);
31 NO_MOVE_SEMANTIC(LSPGetCompilerOptionsDiagnosticsTests);
32
MockGetGlobalDiagnostics(std::vector<std::string> & files,std::vector<std::string> & texts)33 DiagnosticReferences MockGetGlobalDiagnostics(std::vector<std::string> &files, std::vector<std::string> &texts)
34 {
35 auto filePaths = CreateTempFile(files, texts);
36 Initializer initializer;
37 DiagnosticReferences diagnostics;
38
39 for (auto const &file : filePaths) {
40 auto ctx = initializer.CreateContext(file.c_str(), ES2PANDA_STATE_CHECKED);
41 ark::es2panda::lsp::GetGlobalDiagnostics(ctx, diagnostics);
42 initializer.DestroyContext(ctx);
43 }
44
45 return diagnostics;
46 }
47
MockGetOptionDiagnostics(std::vector<std::string> & files,std::vector<std::string> & texts)48 DiagnosticReferences MockGetOptionDiagnostics(std::vector<std::string> &files, std::vector<std::string> &texts)
49 {
50 auto filePaths = CreateTempFile(files, texts);
51
52 DiagnosticReferences diagnostics;
53 int const expectedFileCount = 1;
54
55 if (filePaths.size() < expectedFileCount) {
56 return diagnostics;
57 }
58
59 Initializer initializer;
60 auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED);
61 ark::es2panda::util::DiagnosticEngine *diagnosticEngine =
62 reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx)->diagnosticEngine;
63 auto config = ark::es2panda::ArkTsConfig {filePaths[1], *diagnosticEngine};
64 std::unordered_set<std::string> parsedConfigPath;
65 config.Parse(parsedConfigPath);
66 ark::es2panda::lsp::GetOptionDiagnostics(ctx, diagnostics);
67 initializer.DestroyContext(ctx);
68
69 return diagnostics;
70 }
71 };
72
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetGlobalDiagnostics1)73 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetGlobalDiagnostics1)
74 {
75 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets"};
76 std::vector<std::string> texts = {
77 R"(
78 import { test } from "";
79 )"};
80
81 DiagnosticReferences diagnostics = MockGetGlobalDiagnostics(files, texts);
82
83 int const defaultDiagnosticsSize = 0;
84 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
85
86 const auto &diag = diagnostics.diagnostic[0];
87 ASSERT_EQ(diag.severity_, DiagnosticSeverity::Error);
88 ASSERT_NE(diag.message_.find("Import path cannot be empty"), std::string::npos);
89 }
90
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetGlobalDiagnostics2)91 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetGlobalDiagnostics2)
92 {
93 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets"};
94 std::vector<std::string> texts = {
95 R"(
96 import { test } from "@unknown/package";
97 )"};
98
99 DiagnosticReferences diagnostics = MockGetGlobalDiagnostics(files, texts);
100
101 int const defaultDiagnosticsSize = 0;
102 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
103
104 const auto &diag = diagnostics.diagnostic[0];
105 ASSERT_EQ(diag.severity_, DiagnosticSeverity::Error);
106 ASSERT_NE(diag.message_.find("Can't find prefix for"), std::string::npos);
107 }
108
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetGlobalDiagnostics3)109 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetGlobalDiagnostics3)
110 {
111 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets"};
112 std::vector<std::string> texts = {
113 R"(
114 import { test2 } from "./non_existent.ets";
115 )"};
116
117 DiagnosticReferences diagnostics = MockGetGlobalDiagnostics(files, texts);
118
119 int const defaultDiagnosticsSize = 0;
120 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
121
122 const auto &diag = diagnostics.diagnostic[0];
123 ASSERT_EQ(diag.severity_, DiagnosticSeverity::Error);
124
125 bool isPathError = (diag.message_.find("Not supported path:") != std::string::npos) ||
126 (diag.message_.find("Not an available source path:") != std::string::npos);
127 ASSERT_TRUE(isPathError);
128 }
129
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetGlobalDiagnostics4)130 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetGlobalDiagnostics4)
131 {
132 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets",
133 "get_compiler_options_diagnostics_test_helper.ets",
134 "get_compiler_options_diagnostics_test_utils.ets"};
135 std::vector<std::string> texts = {
136 R"(
137 import { helper } from "./get_compiler_options_diagnostics_test_helper.ets";
138 import { utils } from "./get_compiler_options_diagnostics_test_utils.ets";
139 import { missing } from "./missing.ets";
140
141 function main() {
142 helper(utils.getValue());
143 }
144 )",
145 R"(
146 import { test1 } from "";
147 export function helper(value: number) {
148 return value * 2;
149 }
150 )",
151 R"(
152 import { test2 } from "./non_existent.ets";
153 export const utils = {
154 getValue: () => helper(5)
155 })"};
156
157 DiagnosticReferences diagnostics = MockGetGlobalDiagnostics(files, texts);
158
159 int const defaultDiagnosticsSize = 0;
160 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
161
162 bool foundEmptyPath = false;
163 bool foundPathError = false;
164
165 for (const auto &diag : diagnostics.diagnostic) {
166 ASSERT_EQ(diag.severity_, DiagnosticSeverity::Error);
167 if (diag.message_.find("Import path cannot be empty") != std::string::npos) {
168 foundEmptyPath = true;
169 }
170 if (diag.message_.find("Not supported path:") != std::string::npos) {
171 foundPathError = true;
172 }
173 }
174
175 ASSERT_TRUE(foundEmptyPath);
176 ASSERT_TRUE(foundPathError);
177 }
178
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetOptionDiagnostics1)179 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetOptionDiagnostics1)
180 {
181 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets", "arktsconfig.json"};
182 std::vector<std::string> texts = {
183 R"(
184 function A(a:number, b:number) {
185 return a + b;
186 }
187 )",
188 R"({
189 "invalid json"
190 })"};
191
192 DiagnosticReferences diagnostics = MockGetOptionDiagnostics(files, texts);
193
194 int const defaultDiagnosticsSize = 0;
195 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
196 bool found = false;
197 for (const auto &diag : diagnostics.diagnostic) {
198 if (diag.message_.find("ArkTsConfig is not valid json") != std::string::npos) {
199 found = true;
200 }
201 }
202
203 ASSERT_TRUE(found);
204 }
205
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetOptionDiagnostics2)206 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetOptionDiagnostics2)
207 {
208 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets", "arktsconfig.json"};
209 std::vector<std::string> texts = {
210 R"(
211 function A(a:number, b:number) {
212 return a + b;
213 }
214 )",
215 R"({
216 "extends": "./arktsconfig.json",
217 "compilerOptions": {
218 "baseUrl": "./temp"
219 }
220 })"};
221
222 DiagnosticReferences diagnostics = MockGetOptionDiagnostics(files, texts);
223
224 int const defaultDiagnosticsSize = 0;
225 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
226 bool found = false;
227 for (const auto &diag : diagnostics.diagnostic) {
228 if (diag.message_.find("Encountered cyclic import in") != std::string::npos) {
229 found = true;
230 }
231 }
232
233 ASSERT_TRUE(found);
234 }
235
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetOptionDiagnostics3)236 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetOptionDiagnostics3)
237 {
238 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets", "arktsconfig.json"};
239 std::vector<std::string> texts = {
240 R"(
241 function A(a:number, b:number) {
242 return a + b;
243 }
244 )",
245 R"({
246 "compilerOptions": {
247 "paths": {
248 "std": []
249 }
250 }
251 })"};
252
253 DiagnosticReferences diagnostics = MockGetOptionDiagnostics(files, texts);
254
255 int const defaultDiagnosticsSize = 0;
256 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
257 bool found = false;
258 for (const auto &diag : diagnostics.diagnostic) {
259 if (diag.message_.find("Substitutions for pattern") != std::string::npos) {
260 found = true;
261 }
262 }
263
264 ASSERT_TRUE(found);
265 }
266
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetOptionDiagnostics4)267 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetOptionDiagnostics4)
268 {
269 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets", "arktsconfig.json"};
270 std::vector<std::string> texts = {
271 R"(
272 function A(a:number, b:number) {
273 return a + b;
274 }
275 )",
276 R"({
277 "compilerOptions": {
278 "baseUrl": "/path",
279 "paths": {
280 "std": ["./path1"]
281 },
282 "dynamicPaths": {
283 "dynamic_import_tests": {
284 "language": "invalid_lang",
285 "hasDecl": true
286 }
287 }
288 }
289 })"};
290
291 DiagnosticReferences diagnostics = MockGetOptionDiagnostics(files, texts);
292
293 int const defaultDiagnosticsSize = 0;
294 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
295 bool found = false;
296 for (const auto &diag : diagnostics.diagnostic) {
297 if (diag.message_.find("value for dynamic path with key") != std::string::npos) {
298 found = true;
299 }
300 }
301
302 ASSERT_TRUE(found);
303 }
304
TEST_F(LSPGetCompilerOptionsDiagnosticsTests,GetOptionDiagnostics5)305 TEST_F(LSPGetCompilerOptionsDiagnosticsTests, GetOptionDiagnostics5)
306 {
307 std::vector<std::string> files = {"get_compiler_options_diagnostics_test_main.ets", "arktsconfig.json"};
308 std::vector<std::string> texts = {
309 R"(
310 function A(a:number, b:number) {
311 return a + b;
312 }
313 )",
314 R"({
315 "compilerOptions": {
316 "baseUrl": "./temp",
317 "paths": {
318 "std": ["./path1"]
319 },
320 "dynamicPaths": {
321 "dynamic_import_tests": {
322 "language": "ts",
323 "hasDecl": true
324 }
325 }
326 }
327 })"};
328
329 DiagnosticReferences diagnostics = MockGetOptionDiagnostics(files, texts);
330
331 int const defaultDiagnosticsSize = 0;
332 ASSERT_GT(diagnostics.diagnostic.size(), defaultDiagnosticsSize);
333 bool found = false;
334 for (const auto &diag : diagnostics.diagnostic) {
335 if (diag.message_.find("Interoperability with language") != std::string::npos) {
336 found = true;
337 }
338 }
339
340 ASSERT_TRUE(found);
341 }