• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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