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 #include <cstddef>
16 #include <cstdio>
17 #include <string>
18 #include <vector>
19 #include <regex>
20 #include "lsp_api_test.h"
21 #include "lsp/include/find_rename_locations.h"
22 #include <gtest/gtest.h>
23
24 // NOLINTBEGIN
25 std::vector<std::string> fileNames = {"findRenameLocsOne.ets", "findRenameLocsTwo.ets"};
26 std::vector<std::string> fileContents = {
27 R"(
28 export function abc(x: number): void {
29 }
30
31 export function dummy(x: number): void {
32 }
33
34 export class Foo {
35 name: string = "unassigned";
36 x: number = 1;
37 y: number = 2;
38 z: number = 3;
39 constructor(name: string, x: number, y: number, z: number) {
40 this.name = name;
41 this.x = x;
42 this.y = y;
43 this.z = z;
44 }
45 };
46
47 export class Oranges {
48 name: string = "unassigned";
49 x: number = 1;
50 y: number = 2;
51 z: number = 3;
52 constructor(name: string, x: number, y: number, z: number) {
53 this.name = name;
54 this.x = x;
55 this.y = y;
56 this.z = z;
57 }
58 };
59
60 dummy(0);
61 dummy(1);
62 abc(2);
63 abc(3);
64 abc(4);
65 )",
66 R"(
67 import { dummy, abc, Foo } from "./findRenameLocsOne.ets";
68
69 dummy(4);
70 dummy(44);
71 abc(5);
72 abc(55);
73 abc(555);
74
75 let myfoo = new Foo("apples", 1, 2, 3);
76 let otherfoo = new Foo("oranges", 4, 5, 6);
77
78 console.log(myfoo)
79 console.log(otherfoo)
80 console.log(myfoo.name)
81 )"};
82
getLine(std::string source,size_t pos)83 static size_t getLine(std::string source, size_t pos)
84 {
85 size_t line = 0;
86 for (auto it = source.begin(); it < source.end() && it < source.begin() + pos; ++it) {
87 if (*it == '\n') {
88 ++line;
89 }
90 }
91 return line;
92 }
93
getLineStart(std::string source,size_t pos)94 static size_t getLineStart(std::string source, size_t pos)
95 {
96 auto it = source.begin() + pos;
97 while (it > source.begin() && *(it - 1) != '\n') {
98 --it;
99 }
100 return it - source.begin();
101 }
102
getLineEnd(std::string source,size_t pos)103 static size_t getLineEnd(std::string source, size_t pos)
104 {
105 auto it = source.begin() + pos;
106 while (it < source.end() && *it != '\n') {
107 ++it;
108 }
109 return it - source.begin();
110 }
111
112 class LspFindRenameLocationsTests : public LSPAPITests {
113 public:
genTestData(std::string word,std::string filePath,std::string source)114 std::set<ark::es2panda::lsp::RenameLocation> genTestData(std::string word, std::string filePath, std::string source)
115 {
116 std::set<ark::es2panda::lsp::RenameLocation> data;
117 std::regex regex {"\\W" + word + "\\W"};
118 auto matchBeg = std::sregex_iterator {source.begin(), source.end(), regex};
119 auto matchEnd = std::sregex_iterator();
120
121 for (auto it = matchBeg; it != matchEnd; ++it) {
122 size_t pos = it->position() + 1;
123 size_t line = getLine(source, pos);
124 size_t lineStartPos = getLineStart(source, pos);
125 size_t lineEndPos = getLineEnd(source, pos);
126 std::string prefix {source.begin() + lineStartPos, source.begin() + pos};
127 std::string suffix {source.begin() + pos + word.length(), source.begin() + lineEndPos};
128 ark::es2panda::lsp::RenameLocation loc {filePath, pos, pos + word.length(), line, prefix, suffix};
129 printf("{R\"(%s)\", %ld, %ld, %ld, R\"(%s)\", R\"(%s)\"},\n", loc.filePath.c_str(), loc.start, loc.end,
130 loc.line, loc.prefixText.c_str(), loc.suffixText.c_str());
131 data.insert(loc);
132 }
133
134 return data;
135 }
136
genTestData(std::string pattern)137 std::set<ark::es2panda::lsp::RenameLocation> genTestData(std::string pattern)
138 {
139 // Create the files
140 auto filePaths = CreateTempFile(fileNames, fileContents);
141
142 std::set<ark::es2panda::lsp::RenameLocation> data;
143 printf("std::set<ark::es2panda::lsp::RenameLocation> expected_%s = {\n", pattern.c_str());
144 for (size_t i = 0; i < filePaths.size(); ++i) {
145 auto entries = genTestData(pattern, filePaths[i], fileContents[i]);
146 for (const auto &entry : entries) {
147 data.insert(entry);
148 }
149 }
150 printf("};\n");
151 return data;
152 }
153 };
154
TEST_F(LspFindRenameLocationsTests,DISABLED_genTestData)155 TEST_F(LspFindRenameLocationsTests, DISABLED_genTestData)
156 {
157 auto data = genTestData("Foo");
158 data = genTestData("abc");
159 data = genTestData("dummy");
160 data = genTestData("name");
161 (void)data;
162 }
163
164 std::set<ark::es2panda::lsp::RenameLocation> expected_Foo = {
165 {R"(/tmp/findRenameLocsOne.ets)", 140, 143, 7, R"( export class )", R"( {)"},
166 {R"(/tmp/findRenameLocsTwo.ets)", 30, 33, 1, R"( import { dummy, abc, )",
167 R"( } from "./findRenameLocsOne.ets";)"},
168 {R"(/tmp/findRenameLocsTwo.ets)", 183, 186, 9, R"( let myfoo = new )", R"(("apples", 1, 2, 3);)"},
169 {R"(/tmp/findRenameLocsTwo.ets)", 234, 237, 10, R"( let otherfoo = new )", R"(("oranges", 4, 5, 6);)"},
170 };
171 std::set<ark::es2panda::lsp::RenameLocation> expected_abc = {
172 {R"(/tmp/findRenameLocsOne.ets)", 25, 28, 1, R"( export function )", R"((x: number): void {)"},
173 {R"(/tmp/findRenameLocsOne.ets)", 899, 902, 35, R"( )", R"((2);)"},
174 {R"(/tmp/findRenameLocsOne.ets)", 915, 918, 36, R"( )", R"((3);)"},
175 {R"(/tmp/findRenameLocsOne.ets)", 931, 934, 37, R"( )", R"((4);)"},
176 {R"(/tmp/findRenameLocsTwo.ets)", 25, 28, 1, R"( import { dummy, )",
177 R"(, Foo } from "./findRenameLocsOne.ets";)"},
178 {R"(/tmp/findRenameLocsTwo.ets)", 115, 118, 5, R"( )", R"((5);)"},
179 {R"(/tmp/findRenameLocsTwo.ets)", 131, 134, 6, R"( )", R"((55);)"},
180 {R"(/tmp/findRenameLocsTwo.ets)", 148, 151, 7, R"( )", R"((555);)"},
181 };
182 std::set<ark::es2panda::lsp::RenameLocation> expected_dummy = {
183 {R"(/tmp/findRenameLocsOne.ets)", 83, 88, 4, R"( export function )", R"((x: number): void {)"},
184 {R"(/tmp/findRenameLocsOne.ets)", 863, 868, 33, R"( )", R"((0);)"},
185 {R"(/tmp/findRenameLocsOne.ets)", 881, 886, 34, R"( )", R"((1);)"},
186 {R"(/tmp/findRenameLocsTwo.ets)", 18, 23, 1, R"( import { )",
187 R"(, abc, Foo } from "./findRenameLocsOne.ets";)"},
188 {R"(/tmp/findRenameLocsTwo.ets)", 78, 83, 3, R"( )", R"((4);)"},
189 {R"(/tmp/findRenameLocsTwo.ets)", 96, 101, 4, R"( )", R"((44);)"},
190 };
191 std::set<ark::es2panda::lsp::RenameLocation> expected_name = {
192 {R"(/tmp/findRenameLocsOne.ets)", 158, 162, 8, R"( )", R"(: string = "unassigned";)"},
193 {R"(/tmp/findRenameLocsOne.ets)", 362, 366, 13, R"( this.)", R"( = name;)"},
194 {R"(/tmp/findRenameLocsTwo.ets)", 343, 347, 14, R"( console.log(myfoo.)", R"())"},
195 };
196
TEST_F(LspFindRenameLocationsTests,FindRenameLocationsClassName)197 TEST_F(LspFindRenameLocationsTests, FindRenameLocationsClassName)
198 {
199 // Create the files
200 auto filePaths = CreateTempFile(fileNames, fileContents);
201 std::vector<ark::es2panda::SourceFile> sourceFiles;
202 for (size_t i = 0; i < filePaths.size(); ++i) {
203 sourceFiles.emplace_back(ark::es2panda::SourceFile {filePaths[i], fileContents[i]});
204 }
205 ASSERT_TRUE(sourceFiles.size() == fileNames.size());
206
207 // Search for rename locations
208 ark::es2panda::lsp::CancellationToken cancellationToken {123, nullptr};
209 auto res = ark::es2panda::lsp::FindRenameLocations(&cancellationToken, sourceFiles, sourceFiles[1], 30);
210 ASSERT_EQ(res.size(), expected_Foo.size());
211 for (auto renameLoc : res) {
212 auto found = expected_Foo.find(renameLoc);
213 ASSERT_TRUE(found != expected_Foo.end());
214 }
215 }
216
TEST_F(LspFindRenameLocationsTests,FindRenameLocationsFunctionName)217 TEST_F(LspFindRenameLocationsTests, FindRenameLocationsFunctionName)
218 {
219 // Create the files
220 auto filePaths = CreateTempFile(fileNames, fileContents);
221 std::vector<ark::es2panda::SourceFile> sourceFiles;
222 for (size_t i = 0; i < filePaths.size(); ++i) {
223 sourceFiles.emplace_back(ark::es2panda::SourceFile {filePaths[i], fileContents[i]});
224 }
225 ASSERT_TRUE(sourceFiles.size() == fileNames.size());
226
227 // Search for rename locations
228 ark::es2panda::lsp::CancellationToken cancellationToken {123, nullptr};
229 auto res = ark::es2panda::lsp::FindRenameLocations(&cancellationToken, sourceFiles, sourceFiles[0], 25);
230 ASSERT_EQ(res.size(), expected_abc.size());
231 for (auto renameLoc : res) {
232 auto found = expected_abc.find(renameLoc);
233 ASSERT_TRUE(found != expected_abc.end());
234 }
235 }
236
TEST_F(LspFindRenameLocationsTests,FindRenameLocationsFunctionName2)237 TEST_F(LspFindRenameLocationsTests, FindRenameLocationsFunctionName2)
238 {
239 // Create the files
240 auto filePaths = CreateTempFile(fileNames, fileContents);
241 std::vector<ark::es2panda::SourceFile> sourceFiles;
242 for (size_t i = 0; i < filePaths.size(); ++i) {
243 sourceFiles.emplace_back(ark::es2panda::SourceFile {filePaths[i], fileContents[i]});
244 }
245 ASSERT_TRUE(sourceFiles.size() == fileNames.size());
246
247 // Search for rename locations
248 ark::es2panda::lsp::CancellationToken cancellationToken {123, nullptr};
249 auto res = ark::es2panda::lsp::FindRenameLocations(&cancellationToken, sourceFiles, sourceFiles[0], 83);
250 ASSERT_EQ(res.size(), expected_dummy.size());
251 for (auto renameLoc : res) {
252 auto found = expected_dummy.find(renameLoc);
253 ASSERT_TRUE(found != expected_dummy.end());
254 }
255 }
256
TEST_F(LspFindRenameLocationsTests,FindRenameLocationsClassMemberName)257 TEST_F(LspFindRenameLocationsTests, FindRenameLocationsClassMemberName)
258 {
259 // Create the files
260 auto filePaths = CreateTempFile(fileNames, fileContents);
261 std::vector<ark::es2panda::SourceFile> sourceFiles;
262 for (size_t i = 0; i < filePaths.size(); ++i) {
263 sourceFiles.emplace_back(ark::es2panda::SourceFile {filePaths[i], fileContents[i]});
264 }
265 ASSERT_TRUE(sourceFiles.size() == fileNames.size());
266
267 // Search for rename locations
268 ark::es2panda::lsp::CancellationToken cancellationToken {123, nullptr};
269 auto res = ark::es2panda::lsp::FindRenameLocations(&cancellationToken, sourceFiles, sourceFiles[0], 158);
270 ASSERT_EQ(res.size(), expected_name.size());
271 for (auto renameLoc : res) {
272 auto found = expected_name.find(renameLoc);
273 ASSERT_TRUE(found != expected_name.end());
274 }
275 }
276
277 // NOLINTEND