1 // Copyright 2021 The Tint Authors.
2 //
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 <string>
16
17 #include "gtest/gtest.h"
18
19 #include "fuzzers/tint_regex_fuzzer/wgsl_mutator.h"
20
21 namespace tint {
22 namespace fuzzers {
23 namespace regex_fuzzer {
24 namespace {
25
26 // Swaps two non-consecutive regions in the edge
TEST(SwapRegionsTest,SwapIntervalsEdgeNonConsecutive)27 TEST(SwapRegionsTest, SwapIntervalsEdgeNonConsecutive) {
28 std::string R1 = ";region1;", R2 = ";regionregion2;",
29 R3 = ";regionregionregion3;";
30 std::string all_regions = R1 + R2 + R3;
31
32 // this call should swap R1 with R3.
33 SwapIntervals(0, R1.length(), R1.length() + R2.length(), R3.length(),
34 all_regions);
35
36 ASSERT_EQ(R3 + R2 + R1, all_regions);
37 }
38
39 // Swaps two non-consecutive regions not in the edge
TEST(SwapRegionsTest,SwapIntervalsNonConsecutiveNonEdge)40 TEST(SwapRegionsTest, SwapIntervalsNonConsecutiveNonEdge) {
41 std::string R1 = ";region1;", R2 = ";regionregion2;",
42 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
43 R5 = ";regionregionregionregionregion5;";
44 std::string all_regions = R1 + R2 + R3 + R4 + R5;
45
46 // this call should swap R2 with R4.
47 SwapIntervals(R1.length(), R2.length(),
48 R1.length() + R2.length() + R3.length(), R4.length(),
49 all_regions);
50
51 ASSERT_EQ(R1 + R4 + R3 + R2 + R5, all_regions);
52 }
53
54 // Swaps two consecutive regions not in the edge (sorrounded by other
55 // regions)
TEST(SwapRegionsTest,SwapIntervalsConsecutiveEdge)56 TEST(SwapRegionsTest, SwapIntervalsConsecutiveEdge) {
57 std::string R1 = ";region1;", R2 = ";regionregion2;",
58 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
59 R5 = ";regionregionregionregionregion5;";
60 std::string all_regions = R1 + R2 + R3 + R4;
61
62 // this call should swap R2 with R3.
63 SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length(),
64 R3.length(), all_regions);
65
66 ASSERT_EQ(R1 + R3 + R2 + R4, all_regions);
67 }
68
69 // Swaps two consecutive regions not in the edge (not sorrounded by other
70 // regions)
TEST(SwapRegionsTest,SwapIntervalsConsecutiveNonEdge)71 TEST(SwapRegionsTest, SwapIntervalsConsecutiveNonEdge) {
72 std::string R1 = ";region1;", R2 = ";regionregion2;",
73 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
74 R5 = ";regionregionregionregionregion5;";
75 std::string all_regions = R1 + R2 + R3 + R4 + R5;
76
77 // this call should swap R4 with R5.
78 SwapIntervals(R1.length() + R2.length() + R3.length(), R4.length(),
79 R1.length() + R2.length() + R3.length() + R4.length(),
80 R5.length(), all_regions);
81
82 ASSERT_EQ(R1 + R2 + R3 + R5 + R4, all_regions);
83 }
84
85 // Deletes the first region.
TEST(DeleteRegionTest,DeleteFirstRegion)86 TEST(DeleteRegionTest, DeleteFirstRegion) {
87 std::string R1 = ";region1;", R2 = ";regionregion2;",
88 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
89 R5 = ";regionregionregionregionregion5;";
90 std::string all_regions = R1 + R2 + R3 + R4 + R5;
91
92 // This call should delete R1.
93 DeleteInterval(0, R1.length(), all_regions);
94
95 ASSERT_EQ(";" + R2 + R3 + R4 + R5, all_regions);
96 }
97
98 // Deletes the last region.
TEST(DeleteRegionTest,DeleteLastRegion)99 TEST(DeleteRegionTest, DeleteLastRegion) {
100 std::string R1 = ";region1;", R2 = ";regionregion2;",
101 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
102 R5 = ";regionregionregionregionregion5;";
103 std::string all_regions = R1 + R2 + R3 + R4 + R5;
104
105 // This call should delete R5.
106 DeleteInterval(R1.length() + R2.length() + R3.length() + R4.length(),
107 R5.length(), all_regions);
108
109 ASSERT_EQ(R1 + R2 + R3 + R4 + ";", all_regions);
110 }
111
112 // Deletes the middle region.
TEST(DeleteRegionTest,DeleteMiddleRegion)113 TEST(DeleteRegionTest, DeleteMiddleRegion) {
114 std::string R1 = ";region1;", R2 = ";regionregion2;",
115 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
116 R5 = ";regionregionregionregionregion5;";
117 std::string all_regions = R1 + R2 + R3 + R4 + R5;
118
119 // This call should delete R3.
120 DeleteInterval(R1.length() + R2.length(), R3.length(), all_regions);
121
122 ASSERT_EQ(R1 + R2 + ";" + R4 + R5, all_regions);
123 }
124
TEST(InsertRegionTest,InsertRegionTest1)125 TEST(InsertRegionTest, InsertRegionTest1) {
126 std::string R1 = ";region1;", R2 = ";regionregion2;",
127 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
128 R5 = ";regionregionregionregionregion5;";
129 std::string all_regions = R1 + R2 + R3 + R4 + R5;
130
131 // This call should insert R2 after R4.
132 DuplicateInterval(R1.length(), R2.length(),
133 R1.length() + R2.length() + R3.length() + R4.length() - 1,
134 all_regions);
135
136 ASSERT_EQ(R1 + R2 + R3 + R4 + R2.substr(1, R2.size() - 1) + R5, all_regions);
137 }
138
TEST(InsertRegionTest,InsertRegionTest2)139 TEST(InsertRegionTest, InsertRegionTest2) {
140 std::string R1 = ";region1;", R2 = ";regionregion2;",
141 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
142 R5 = ";regionregionregionregionregion5;";
143
144 std::string all_regions = R1 + R2 + R3 + R4 + R5;
145
146 // This call should insert R3 after R1.
147 DuplicateInterval(R1.length() + R2.length(), R3.length(), R1.length() - 1,
148 all_regions);
149
150 ASSERT_EQ(R1 + R3.substr(1, R3.length() - 1) + R2 + R3 + R4 + R5,
151 all_regions);
152 }
153
TEST(InsertRegionTest,InsertRegionTest3)154 TEST(InsertRegionTest, InsertRegionTest3) {
155 std::string R1 = ";region1;", R2 = ";regionregion2;",
156 R3 = ";regionregionregion3;", R4 = ";regionregionregionregion4;",
157 R5 = ";regionregionregionregionregion5;";
158
159 std::string all_regions = R1 + R2 + R3 + R4 + R5;
160
161 // This call should insert R2 after R5.
162 DuplicateInterval(R1.length(), R2.length(), all_regions.length() - 1,
163 all_regions);
164
165 ASSERT_EQ(R1 + R2 + R3 + R4 + R5 + R2.substr(1, R2.length() - 1),
166 all_regions);
167 }
168
TEST(ReplaceIdentifierTest,ReplaceIdentifierTest1)169 TEST(ReplaceIdentifierTest, ReplaceIdentifierTest1) {
170 std::string R1 = "|region1|", R2 = "; region2;",
171 R3 = "---------region3---------", R4 = "++region4++",
172 R5 = "***region5***";
173 std::string all_regions = R1 + R2 + R3 + R4 + R5;
174
175 // Replaces R3 with R1.
176 ReplaceRegion(0, R1.length(), R1.length() + R2.length(), R3.length(),
177 all_regions);
178
179 ASSERT_EQ(R1 + R2 + R1 + R4 + R5, all_regions);
180 }
181
TEST(ReplaceIdentifierTest,ReplaceIdentifierTest2)182 TEST(ReplaceIdentifierTest, ReplaceIdentifierTest2) {
183 std::string R1 = "|region1|", R2 = "; region2;",
184 R3 = "---------region3---------", R4 = "++region4++",
185 R5 = "***region5***";
186 std::string all_regions = R1 + R2 + R3 + R4 + R5;
187
188 // Replaces R5 with R3.
189 ReplaceRegion(R1.length() + R2.length(), R3.length(),
190 R1.length() + R2.length() + R3.length() + R4.length(),
191 R5.length(), all_regions);
192
193 ASSERT_EQ(R1 + R2 + R3 + R4 + R3, all_regions);
194 }
195
TEST(GetIdentifierTest,GetIdentifierTest1)196 TEST(GetIdentifierTest, GetIdentifierTest1) {
197 std::string wgsl_code =
198 R"(fn clamp_0acf8f() {
199 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
200 }
201 [[stage(vertex)]]
202 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
203 clamp_0acf8f();"
204 return vec4<f32>();
205 }
206 [[stage(fragment)]]
207 fn fragment_main() {
208 clamp_0acf8f();
209 }
210 [[stage(compute), workgroup_size(1)]]
211 fn compute_main() {"
212 var<private> foo: f32 = 0.0;
213 clamp_0acf8f();
214 })";
215
216 std::vector<std::pair<size_t, size_t>> identifiers_pos =
217 GetIdentifiers(wgsl_code);
218
219 std::vector<std::pair<size_t, size_t>> ground_truth = {
220 std::make_pair(3, 12), std::make_pair(28, 3), std::make_pair(37, 4),
221 std::make_pair(49, 5), std::make_pair(60, 3), std::make_pair(68, 4),
222 std::make_pair(81, 4), std::make_pair(111, 5), std::make_pair(133, 2),
223 std::make_pair(143, 4), std::make_pair(155, 7), std::make_pair(175, 4),
224 std::make_pair(196, 12), std::make_pair(222, 6), std::make_pair(234, 3),
225 std::make_pair(258, 5), std::make_pair(282, 2), std::make_pair(294, 4),
226 std::make_pair(311, 12), std::make_pair(343, 5), std::make_pair(359, 14),
227 std::make_pair(385, 2), std::make_pair(396, 4), std::make_pair(414, 3),
228 std::make_pair(427, 3), std::make_pair(432, 3), std::make_pair(451, 12)};
229
230 ASSERT_EQ(ground_truth, identifiers_pos);
231 }
232
TEST(TestGetLiteralsValues,TestGetLiteralsValues1)233 TEST(TestGetLiteralsValues, TestGetLiteralsValues1) {
234 std::string wgsl_code =
235 R"(fn clamp_0acf8f() {
236 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
237 }
238 [[stage(vertex)]]
239 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
240 clamp_0acf8f();
241 var foo_1: i32 = 3;
242 return vec4<f32>();
243 }
244 [[stage(fragment)]]
245 fn fragment_main() {
246 clamp_0acf8f();
247 }
248 [[stage(compute), workgroup_size(1)]]
249 fn compute_main() {
250 var<private> foo: f32 = 0.0;
251 var foo_2: i32 = 10;
252 clamp_0acf8f();
253 }
254 foo_1 = 5 + 7;
255 var foo_3 : i32 = -20;)";
256
257 std::vector<std::pair<size_t, size_t>> literals_pos =
258 GetIntLiterals(wgsl_code);
259
260 std::vector<std::string> ground_truth = {"3", "10", "5", "7", "-20"};
261
262 std::vector<std::string> result;
263
264 for (auto pos : literals_pos) {
265 result.push_back(wgsl_code.substr(pos.first, pos.second));
266 }
267
268 ASSERT_EQ(ground_truth, result);
269 }
270
TEST(InsertReturnTest,FindClosingBrace)271 TEST(InsertReturnTest, FindClosingBrace) {
272 std::string wgsl_code =
273 R"(fn clamp_0acf8f() {
274 if(false){
275
276 } else{
277 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
278 }
279 }
280 [[stage(vertex)]]
281 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
282 clamp_0acf8f();
283 var foo_1: i32 = 3;
284 return vec4<f32>();
285 }
286 [[stage(fragment)]]
287 fn fragment_main() {
288 clamp_0acf8f();
289 }
290 [[stage(compute), workgroup_size(1)]]
291 fn compute_main() {
292 var<private> foo: f32 = 0.0;
293 var foo_2: i32 = 10;
294 clamp_0acf8f();
295 }
296 foo_1 = 5 + 7;
297 var foo_3 : i32 = -20;
298 )";
299 size_t opening_bracket_pos = 18;
300 size_t closing_bracket_pos = FindClosingBrace(opening_bracket_pos, wgsl_code);
301
302 // The -1 is needed since the function body starts after the left bracket.
303 std::string function_body = wgsl_code.substr(
304 opening_bracket_pos + 1, closing_bracket_pos - opening_bracket_pos - 1);
305 std::string expected =
306 R"(
307 if(false){
308
309 } else{
310 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
311 }
312 )";
313 ASSERT_EQ(expected, function_body);
314 }
315
TEST(InsertReturnTest,FindClosingBraceFailing)316 TEST(InsertReturnTest, FindClosingBraceFailing) {
317 std::string wgsl_code =
318 R"(fn clamp_0acf8f() {
319 // This comment } causes the test to fail.
320 "if(false){
321
322 } else{
323 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
324 }
325 }
326 [[stage(vertex)]]
327 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
328 clamp_0acf8f();
329 var foo_1: i32 = 3;
330 return vec4<f32>();
331 }
332 [[stage(fragment)]]
333 fn fragment_main() {
334 clamp_0acf8f();
335 }
336 [[stage(compute), workgroup_size(1)]]
337 fn compute_main() {
338 var<private> foo: f32 = 0.0;
339 var foo_2: i32 = 10;
340 clamp_0acf8f();
341 }
342 foo_1 = 5 + 7;
343 var foo_3 : i32 = -20;)";
344 size_t opening_bracket_pos = 18;
345 size_t closing_bracket_pos = FindClosingBrace(opening_bracket_pos, wgsl_code);
346
347 // The -1 is needed since the function body starts after the left bracket.
348 std::string function_body = wgsl_code.substr(
349 opening_bracket_pos + 1, closing_bracket_pos - opening_bracket_pos - 1);
350 std::string expected =
351 R"(// This comment } causes the test to fail.
352 "if(false){
353
354 } else{
355 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
356 })";
357 ASSERT_NE(expected, function_body);
358 }
359
TEST(TestInsertReturn,TestInsertReturn1)360 TEST(TestInsertReturn, TestInsertReturn1) {
361 std::string wgsl_code =
362 R"(fn clamp_0acf8f() {
363 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
364 }
365 [[stage(vertex)]]
366 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
367 clamp_0acf8f();
368 var foo_1: i32 = 3;
369 return vec4<f32>();
370 }
371 [[stage(fragment)]]
372 fn fragment_main() {
373 clamp_0acf8f();
374 }
375 [[stage(compute), workgroup_size(1)]]
376 fn compute_main() {
377 var<private> foo: f32 = 0.0;
378 var foo_2: i32 = 10;
379 clamp_0acf8f();
380 }
381 foo_1 = 5 + 7;
382 var foo_3 : i32 = -20;)";
383
384 std::vector<size_t> semicolon_pos;
385 for (size_t pos = wgsl_code.find(";", 0); pos != std::string::npos;
386 pos = wgsl_code.find(";", pos + 1)) {
387 semicolon_pos.push_back(pos);
388 }
389
390 // should insert a return true statement after the first semicolon of the
391 // first function the the WGSL-like string above.
392 wgsl_code.insert(semicolon_pos[0] + 1, "return true;");
393
394 std::string expected_wgsl_code =
395 R"(fn clamp_0acf8f() {
396 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());return true;
397 }
398 [[stage(vertex)]]
399 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
400 clamp_0acf8f();
401 var foo_1: i32 = 3;
402 return vec4<f32>();
403 }
404 [[stage(fragment)]]
405 fn fragment_main() {
406 clamp_0acf8f();
407 }
408 [[stage(compute), workgroup_size(1)]]
409 fn compute_main() {
410 var<private> foo: f32 = 0.0;
411 var foo_2: i32 = 10;
412 clamp_0acf8f();
413 }
414 foo_1 = 5 + 7;
415 var foo_3 : i32 = -20;)";
416
417 ASSERT_EQ(expected_wgsl_code, wgsl_code);
418 }
419
TEST(TestInsertReturn,TestFunctionPositions)420 TEST(TestInsertReturn, TestFunctionPositions) {
421 std::string wgsl_code =
422 R"(fn clamp_0acf8f() {
423 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
424 }
425 [[stage(vertex)]]
426 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
427 clamp_0acf8f();
428 var foo_1: i32 = 3;
429 return vec4<f32>();
430 }
431 [[stage(fragment)]]
432 fn fragment_main() {
433 clamp_0acf8f();
434 }
435 [[stage(compute), workgroup_size(1)]]
436 fn compute_main() {
437 var<private> foo: f32 = 0.0;
438 var foo_2: i32 = 10;
439 clamp_0acf8f();
440 }
441 fn vert_main() -> [[builtin(position)]] vec4<f32> {
442 clamp_0acf8f();
443 var foo_1: i32 = 3;
444 return vec4<f32>();
445 }
446 foo_1 = 5 + 7;
447 var foo_3 : i32 = -20;)";
448
449 std::vector<size_t> function_positions = GetFunctionBodyPositions(wgsl_code);
450 std::vector<size_t> expected_positions = {193, 622};
451 ASSERT_EQ(expected_positions, function_positions);
452 }
453
TEST(TestInsertReturn,TestMissingSemicolon)454 TEST(TestInsertReturn, TestMissingSemicolon) {
455 std::string wgsl_code =
456 R"(fn clamp_0acf8f() {
457 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>())
458 }
459 [[stage(vertex)]]
460 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
461 clamp_0acf8f()
462 var foo_1: i32 = 3
463 return vec4<f32>()
464 }
465 [[stage(fragment)]]
466 fn fragment_main() {
467 clamp_0acf8f();
468 }
469 [[stage(compute), workgroup_size(1)]]
470 fn compute_main() {
471 var<private> foo: f32 = 0.0;
472 var foo_2: i32 = 10;
473 clamp_0acf8f();
474 }
475 fn vert_main() -> [[builtin(position)]] vec4<f32> {
476 clamp_0acf8f()
477 var foo_1: i32 = 3
478 return vec4<f32>()
479 }
480 foo_1 = 5 + 7;
481 var foo_3 : i32 = -20;)";
482
483 RandomGenerator generator(0);
484 InsertReturnStatement(wgsl_code, generator);
485
486 // No semicolons found in the function's body, so wgsl_code
487 // should remain unchanged.
488 std::string expected_wgsl_code =
489 R"(fn clamp_0acf8f() {
490 var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>())
491 }
492 [[stage(vertex)]]
493 fn vertex_main() -> [[builtin(position)]] vec4<f32> {
494 clamp_0acf8f()
495 var foo_1: i32 = 3
496 return vec4<f32>()
497 }
498 [[stage(fragment)]]
499 fn fragment_main() {
500 clamp_0acf8f();
501 }
502 [[stage(compute), workgroup_size(1)]]
503 fn compute_main() {
504 var<private> foo: f32 = 0.0;
505 var foo_2: i32 = 10;
506 clamp_0acf8f();
507 }
508 fn vert_main() -> [[builtin(position)]] vec4<f32> {
509 clamp_0acf8f()
510 var foo_1: i32 = 3
511 return vec4<f32>()
512 }
513 foo_1 = 5 + 7;
514 var foo_3 : i32 = -20;)";
515 ASSERT_EQ(expected_wgsl_code, wgsl_code);
516 }
517
518 } // namespace
519 } // namespace regex_fuzzer
520 } // namespace fuzzers
521 } // namespace tint
522