1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/source_report.h" |
6 | #include "vm/dart_api_impl.h" |
7 | #include "vm/unit_test.h" |
8 | |
9 | namespace dart { |
10 | |
11 | #ifndef PRODUCT |
12 | |
13 | static ObjectPtr ExecuteScript(const char* script, bool allow_errors = false) { |
14 | Dart_Handle lib; |
15 | { |
16 | TransitionVMToNative transition(Thread::Current()); |
17 | if (allow_errors) { |
18 | lib = TestCase::LoadTestScriptWithErrors(script, NULL); |
19 | } else { |
20 | lib = TestCase::LoadTestScript(script, NULL); |
21 | } |
22 | EXPECT_VALID(lib); |
23 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
24 | EXPECT_VALID(result); |
25 | } |
26 | return Api::UnwrapHandle(lib); |
27 | } |
28 | |
29 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_NoCalls) { |
30 | char buffer[1024]; |
31 | const char* kScript = |
32 | "main() {\n" |
33 | "}" ; |
34 | |
35 | Library& lib = Library::Handle(); |
36 | lib ^= ExecuteScript(kScript); |
37 | ASSERT(!lib.IsNull()); |
38 | const Script& script = |
39 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
40 | SourceReport report(SourceReport::kCoverage); |
41 | JSONStream js; |
42 | report.PrintJSON(&js, script); |
43 | ElideJSONSubstring("libraries" , js.ToCString(), buffer); |
44 | EXPECT_STREQ( |
45 | "{\"type\":\"SourceReport\",\"ranges\":" |
46 | |
47 | // One compiled range, one hit at function declaration. |
48 | "[{\"scriptIndex\":0,\"startPos\":0,\"endPos\":9,\"compiled\":true," |
49 | "\"coverage\":{\"hits\":[0],\"misses\":[]}}]," |
50 | |
51 | // One script in the script table. |
52 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
53 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
54 | buffer); |
55 | } |
56 | |
57 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_SimpleCall) { |
58 | char buffer[1024]; |
59 | const char* kScript = |
60 | "helper0() {}\n" |
61 | "helper1() {}\n" |
62 | "main() {\n" |
63 | " if (true) {\n" |
64 | " helper0();\n" |
65 | " } else {\n" |
66 | " helper1();\n" |
67 | " }\n" |
68 | "}" ; |
69 | |
70 | Library& lib = Library::Handle(); |
71 | lib ^= ExecuteScript(kScript); |
72 | ASSERT(!lib.IsNull()); |
73 | const Script& script = |
74 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
75 | |
76 | SourceReport report(SourceReport::kCoverage); |
77 | JSONStream js; |
78 | report.PrintJSON(&js, script); |
79 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
80 | ElideJSONSubstring("libraries" , buffer, buffer); |
81 | EXPECT_STREQ( |
82 | "{\"type\":\"SourceReport\",\"ranges\":[" |
83 | |
84 | // One range compiled with one hit at function declaration (helper0). |
85 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
86 | "\"coverage\":{\"hits\":[0],\"misses\":[]}}," |
87 | |
88 | // One range not compiled (helper1). |
89 | "{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false}," |
90 | |
91 | // One range with two hits and a miss (main). |
92 | "{\"scriptIndex\":0,\"startPos\":26,\"endPos\":94,\"compiled\":true," |
93 | "\"coverage\":{\"hits\":[26,53],\"misses\":[79]}}]," |
94 | |
95 | // Only one script in the script table. |
96 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
97 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
98 | buffer); |
99 | } |
100 | |
101 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_ForceCompile) { |
102 | char buffer[1024]; |
103 | const char* kScript = |
104 | "helper0() {}\n" |
105 | "helper1() {}\n" |
106 | "main() {\n" |
107 | " if (true) {\n" |
108 | " helper0();\n" |
109 | " } else {\n" |
110 | " helper1();\n" |
111 | " }\n" |
112 | "}" ; |
113 | |
114 | Library& lib = Library::Handle(); |
115 | lib ^= ExecuteScript(kScript); |
116 | ASSERT(!lib.IsNull()); |
117 | const Script& script = |
118 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
119 | |
120 | SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile); |
121 | JSONStream js; |
122 | report.PrintJSON(&js, script); |
123 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
124 | ElideJSONSubstring("libraries" , buffer, buffer); |
125 | |
126 | EXPECT_STREQ( |
127 | "{\"type\":\"SourceReport\",\"ranges\":[" |
128 | |
129 | // One range compiled with one hit at function declaration (helper0). |
130 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
131 | "\"coverage\":{\"hits\":[0],\"misses\":[]}}," |
132 | |
133 | // This range is compiled even though it wasn't called (helper1). |
134 | "{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":true," |
135 | "\"coverage\":{\"hits\":[],\"misses\":[13]}}," |
136 | |
137 | // One range with two hits and a miss (main). |
138 | "{\"scriptIndex\":0,\"startPos\":26,\"endPos\":94,\"compiled\":true," |
139 | "\"coverage\":{\"hits\":[26,53],\"misses\":[79]}}]," |
140 | |
141 | // Only one script in the script table. |
142 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
143 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
144 | buffer); |
145 | } |
146 | |
147 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_NoForceCompile) { |
148 | char buffer[1024]; |
149 | const char* kScript = |
150 | "helper0() {}\n" |
151 | "class Unused {\n" |
152 | " helper1() { helper0(); }\n" |
153 | "}\n" |
154 | "main() {\n" |
155 | " helper0();\n" |
156 | "}" ; |
157 | |
158 | Library& lib = Library::Handle(); |
159 | lib ^= ExecuteScript(kScript); |
160 | ASSERT(!lib.IsNull()); |
161 | const Script& script = |
162 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
163 | |
164 | SourceReport report(SourceReport::kCoverage); |
165 | JSONStream js; |
166 | report.PrintJSON(&js, script); |
167 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
168 | ElideJSONSubstring("libraries" , buffer, buffer); |
169 | EXPECT_STREQ( |
170 | "{\"type\":\"SourceReport\",\"ranges\":[" |
171 | |
172 | // UnusedClass is not compiled. |
173 | "{\"scriptIndex\":0,\"startPos\":13,\"endPos\":55,\"compiled\":false}," |
174 | |
175 | // helper0 is compiled. |
176 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
177 | "\"coverage\":{\"hits\":[0],\"misses\":[]}}," |
178 | |
179 | // One range with two hits (main). |
180 | "{\"scriptIndex\":0,\"startPos\":57,\"endPos\":79,\"compiled\":true," |
181 | "\"coverage\":{\"hits\":[57,68],\"misses\":[]}}]," |
182 | |
183 | // Only one script in the script table. |
184 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
185 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
186 | buffer); |
187 | } |
188 | |
189 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_ForceCompile) { |
190 | char buffer[1024]; |
191 | const char* kScript = |
192 | "helper0() {}\n" |
193 | "class Unused {\n" |
194 | " helper1() { helper0(); }\n" |
195 | "}\n" |
196 | "main() {\n" |
197 | " helper0();\n" |
198 | "}" ; |
199 | |
200 | Library& lib = Library::Handle(); |
201 | lib ^= ExecuteScript(kScript); |
202 | ASSERT(!lib.IsNull()); |
203 | const Script& script = |
204 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
205 | |
206 | SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile); |
207 | JSONStream js; |
208 | report.PrintJSON(&js, script); |
209 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
210 | ElideJSONSubstring("libraries" , buffer, buffer); |
211 | EXPECT_STREQ( |
212 | "{\"type\":\"SourceReport\",\"ranges\":[" |
213 | |
214 | // UnusedClass.helper1 is compiled. |
215 | "{\"scriptIndex\":0,\"startPos\":30,\"endPos\":53,\"compiled\":true," |
216 | "\"coverage\":{\"hits\":[],\"misses\":[30,42]}}," |
217 | |
218 | // helper0 is compiled. |
219 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
220 | "\"coverage\":{\"hits\":[0],\"misses\":[]}}," |
221 | |
222 | // One range with two hits (main). |
223 | "{\"scriptIndex\":0,\"startPos\":57,\"endPos\":79,\"compiled\":true," |
224 | "\"coverage\":{\"hits\":[57,68],\"misses\":[]}}]," |
225 | |
226 | // Only one script in the script table. |
227 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
228 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
229 | buffer); |
230 | } |
231 | |
232 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_ForceCompileError) { |
233 | char buffer[1024]; |
234 | const char* kScript = |
235 | "helper0() {}\n" |
236 | "class Unused {\n" |
237 | " helper1() { helper0()+ }\n" // syntax error |
238 | "}\n" |
239 | "main() {\n" |
240 | " helper0();\n" |
241 | "}" ; |
242 | |
243 | Library& lib = Library::Handle(); |
244 | lib ^= ExecuteScript(kScript, true); |
245 | ASSERT(!lib.IsNull()); |
246 | const Script& script = |
247 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
248 | |
249 | SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile); |
250 | JSONStream js; |
251 | report.PrintJSON(&js, script); |
252 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
253 | ElideJSONSubstring("libraries" , buffer, buffer); |
254 | EXPECT_STREQ( |
255 | "{\"type\":\"SourceReport\",\"ranges\":[" |
256 | |
257 | // UnusedClass has a syntax error. |
258 | "{\"scriptIndex\":0,\"startPos\":30,\"endPos\":53,\"compiled\":false," |
259 | "\"error\":{\"type\":\"@Error\",\"_vmType\":\"LanguageError\"," |
260 | "\"kind\":\"LanguageError\",\"id\":\"objects\\/0\"," |
261 | "\"message\":\"'file:\\/\\/\\/test-lib': error: " |
262 | "\\/test-lib:3:26: " |
263 | "Error: This couldn't be parsed.\\n" |
264 | " helper1() { helper0()+ }\\n ^\"}}," |
265 | |
266 | // helper0 is compiled. |
267 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
268 | "\"coverage\":{\"hits\":[0],\"misses\":[]}}," |
269 | |
270 | // One range with two hits (main). |
271 | "{\"scriptIndex\":0,\"startPos\":57,\"endPos\":79,\"compiled\":true," |
272 | "\"coverage\":{\"hits\":[57,68],\"misses\":[]}}]," |
273 | |
274 | // Only one script in the script table. |
275 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
276 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
277 | buffer); |
278 | } |
279 | |
280 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_NestedFunctions) { |
281 | char buffer[1024]; |
282 | const char* kScript = |
283 | "helper0() {\n" |
284 | " nestedHelper0() {}\n" |
285 | " nestedHelper1() {}\n" |
286 | " nestedHelper0();\n" |
287 | "}\n" |
288 | "helper1() {}\n" |
289 | "main() {\n" |
290 | " if (true) {\n" |
291 | " helper0();\n" |
292 | " } else {\n" |
293 | " helper1();\n" |
294 | " }\n" |
295 | "}" ; |
296 | |
297 | Library& lib = Library::Handle(); |
298 | lib ^= ExecuteScript(kScript); |
299 | ASSERT(!lib.IsNull()); |
300 | const Script& script = |
301 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
302 | |
303 | SourceReport report(SourceReport::kCoverage); |
304 | JSONStream js; |
305 | report.PrintJSON(&js, script); |
306 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
307 | ElideJSONSubstring("libraries" , buffer, buffer); |
308 | |
309 | EXPECT_STREQ( |
310 | "{\"type\":\"SourceReport\",\"ranges\":[" |
311 | |
312 | // One range compiled with one hit (helper0). |
313 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":73,\"compiled\":true," |
314 | "\"coverage\":{\"hits\":[0,69],\"misses\":[]}}," |
315 | |
316 | // One range not compiled (helper1). |
317 | "{\"scriptIndex\":0,\"startPos\":75,\"endPos\":86,\"compiled\":false}," |
318 | |
319 | // One range with two hits and a miss (main). |
320 | "{\"scriptIndex\":0,\"startPos\":88,\"endPos\":156,\"compiled\":true," |
321 | "\"coverage\":{\"hits\":[88,115],\"misses\":[141]}}," |
322 | |
323 | // Nested range compiled (nestedHelper0). |
324 | "{\"scriptIndex\":0,\"startPos\":14,\"endPos\":31,\"compiled\":true," |
325 | "\"coverage\":{\"hits\":[14],\"misses\":[]}}," |
326 | |
327 | // Nested range not compiled (nestedHelper1). |
328 | "{\"scriptIndex\":0,\"startPos\":35,\"endPos\":52,\"compiled\":false}]," |
329 | |
330 | // Only one script in the script table. |
331 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
332 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
333 | buffer); |
334 | } |
335 | |
336 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_RestrictedRange) { |
337 | char buffer[1024]; |
338 | const char* kScript = |
339 | "helper0() {\n" |
340 | " nestedHelper0() {}\n" |
341 | " nestedHelper1() {}\n" |
342 | " nestedHelper0();\n" |
343 | "}\n" |
344 | "helper1() {}\n" |
345 | "main() {\n" |
346 | " if (true) {\n" |
347 | " helper0();\n" |
348 | " } else {\n" |
349 | " helper1();\n" |
350 | " }\n" |
351 | "}" ; |
352 | |
353 | Library& lib = Library::Handle(); |
354 | lib ^= ExecuteScript(kScript); |
355 | ASSERT(!lib.IsNull()); |
356 | const Script& script = |
357 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
358 | const Function& helper = Function::Handle( |
359 | lib.LookupLocalFunction(String::Handle(String::New("helper0" )))); |
360 | |
361 | SourceReport report(SourceReport::kCoverage); |
362 | JSONStream js; |
363 | // Restrict the report to only helper0 and it's nested functions. |
364 | report.PrintJSON(&js, script, helper.token_pos(), helper.end_token_pos()); |
365 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
366 | ElideJSONSubstring("libraries" , buffer, buffer); |
367 | |
368 | EXPECT_STREQ( |
369 | "{\"type\":\"SourceReport\",\"ranges\":[" |
370 | |
371 | // One range compiled with one hit (helper0). |
372 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":73,\"compiled\":true," |
373 | "\"coverage\":{\"hits\":[0,69],\"misses\":[]}}," |
374 | |
375 | // Nested range compiled (nestedHelper0). |
376 | "{\"scriptIndex\":0,\"startPos\":14,\"endPos\":31,\"compiled\":true," |
377 | "\"coverage\":{\"hits\":[14],\"misses\":[]}}," |
378 | |
379 | // Nested range not compiled (nestedHelper1). |
380 | "{\"scriptIndex\":0,\"startPos\":35,\"endPos\":52,\"compiled\":false}]," |
381 | |
382 | // Only one script in the script table. |
383 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
384 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
385 | buffer); |
386 | } |
387 | |
388 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_AllFunctions) { |
389 | const char* kScript = |
390 | "helper0() {}\n" |
391 | "helper1() {}\n" |
392 | "main() {\n" |
393 | " if (true) {\n" |
394 | " helper0();\n" |
395 | " } else {\n" |
396 | " helper1();\n" |
397 | " }\n" |
398 | "}" ; |
399 | |
400 | Library& lib = Library::Handle(); |
401 | lib ^= ExecuteScript(kScript); |
402 | ASSERT(!lib.IsNull()); |
403 | |
404 | SourceReport report(SourceReport::kCoverage); |
405 | JSONStream js; |
406 | |
407 | // We generate a report with all functions in the VM. |
408 | Script& null_script = Script::Handle(); |
409 | report.PrintJSON(&js, null_script); |
410 | const char* result = js.ToCString(); |
411 | |
412 | // Sanity check the header. |
413 | EXPECT_SUBSTRING("{\"type\":\"SourceReport\",\"ranges\":[" , result); |
414 | |
415 | // Make sure that the main function was found. |
416 | EXPECT_SUBSTRING( |
417 | "\"startPos\":26,\"endPos\":94,\"compiled\":true," |
418 | "\"coverage\":{\"hits\":[26,53],\"misses\":[79]}" , |
419 | result); |
420 | |
421 | // More than one script is referenced in the report. |
422 | EXPECT_SUBSTRING("\"scriptIndex\":0" , result); |
423 | EXPECT_SUBSTRING("\"scriptIndex\":1" , result); |
424 | EXPECT_SUBSTRING("\"scriptIndex\":2" , result); |
425 | } |
426 | |
427 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_AllFunctions_ForceCompile) { |
428 | const char* kScript = |
429 | "helper0() {}\n" |
430 | "helper1() {}\n" |
431 | "main() {\n" |
432 | " if (true) {\n" |
433 | " helper0();\n" |
434 | " } else {\n" |
435 | " helper1();\n" |
436 | " }\n" |
437 | "}" ; |
438 | |
439 | Library& lib = Library::Handle(); |
440 | lib ^= ExecuteScript(kScript); |
441 | ASSERT(!lib.IsNull()); |
442 | |
443 | SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile); |
444 | JSONStream js; |
445 | |
446 | // We generate a report with all functions in the VM. |
447 | Script& null_script = Script::Handle(); |
448 | report.PrintJSON(&js, null_script); |
449 | const char* result = js.ToCString(); |
450 | |
451 | // Sanity check the header. |
452 | EXPECT_SUBSTRING("{\"type\":\"SourceReport\",\"ranges\":[" , result); |
453 | |
454 | // Make sure that the main function was found. |
455 | EXPECT_SUBSTRING( |
456 | "\"startPos\":26,\"endPos\":94,\"compiled\":true," |
457 | "\"coverage\":{\"hits\":[26,53],\"misses\":[79]}" , |
458 | result); |
459 | |
460 | // More than one script is referenced in the report. |
461 | EXPECT_SUBSTRING("\"scriptIndex\":0" , result); |
462 | EXPECT_SUBSTRING("\"scriptIndex\":1" , result); |
463 | EXPECT_SUBSTRING("\"scriptIndex\":2" , result); |
464 | } |
465 | |
466 | ISOLATE_UNIT_TEST_CASE(SourceReport_CallSites_SimpleCall) { |
467 | char buffer[1024]; |
468 | const char* kScript = |
469 | "helper0() {}\n" |
470 | "helper1() {}\n" |
471 | "main() {\n" |
472 | " helper0();\n" |
473 | "}" ; |
474 | |
475 | Library& lib = Library::Handle(); |
476 | lib ^= ExecuteScript(kScript); |
477 | ASSERT(!lib.IsNull()); |
478 | const Script& script = |
479 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
480 | |
481 | SourceReport report(SourceReport::kCallSites); |
482 | JSONStream js; |
483 | report.PrintJSON(&js, script); |
484 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
485 | ElideJSONSubstring("libraries" , buffer, buffer); |
486 | EXPECT_STREQ( |
487 | "{\"type\":\"SourceReport\",\"ranges\":[" |
488 | |
489 | // One range compiled with no callsites (helper0). |
490 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
491 | "\"callSites\":[]}," |
492 | |
493 | // One range not compiled (helper1). |
494 | "{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false}," |
495 | |
496 | // One range compiled with one callsite (main). |
497 | "{\"scriptIndex\":0,\"startPos\":26,\"endPos\":48,\"compiled\":true," |
498 | "\"callSites\":[" |
499 | "{\"name\":\"helper0\",\"tokenPos\":37,\"cacheEntries\":[" |
500 | "{\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\"," |
501 | "\"name\":\"helper0\",\"owner\":{\"type\":\"@Library\",\"fixedId\":true," |
502 | "\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}," |
503 | "\"_kind\":\"RegularFunction\",\"static\":true,\"const\":false," |
504 | "\"_intrinsic\":false,\"_native\":false},\"count\":1}]}]}]," |
505 | |
506 | // One script in the script table. |
507 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
508 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
509 | buffer); |
510 | } |
511 | |
512 | ISOLATE_UNIT_TEST_CASE(SourceReport_CallSites_PolymorphicCall) { |
513 | char buffer[1024]; |
514 | const char* kScript = |
515 | "class Common {\n" |
516 | " func() {}\n" |
517 | "}\n" |
518 | "class Uncommon {\n" |
519 | " func() {}\n" |
520 | "}\n" |
521 | "helper(arg) {\n" |
522 | " arg.func();\n" |
523 | "}\n" |
524 | "main() {\n" |
525 | " Common common = new Common();\n" |
526 | " Uncommon uncommon = new Uncommon();\n" |
527 | " helper(common);\n" |
528 | " helper(common);\n" |
529 | " helper(uncommon);\n" |
530 | "}" ; |
531 | |
532 | Library& lib = Library::Handle(); |
533 | lib ^= ExecuteScript(kScript); |
534 | ASSERT(!lib.IsNull()); |
535 | const Script& script = |
536 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
537 | const Function& helper = Function::Handle( |
538 | lib.LookupLocalFunction(String::Handle(String::New("helper" )))); |
539 | |
540 | SourceReport report(SourceReport::kCallSites); |
541 | JSONStream js; |
542 | report.PrintJSON(&js, script, helper.token_pos(), helper.end_token_pos()); |
543 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
544 | ElideJSONSubstring("libraries" , buffer, buffer); |
545 | EXPECT_STREQ( |
546 | "{\"type\":\"SourceReport\",\"ranges\":[" |
547 | |
548 | // One range... |
549 | "{\"scriptIndex\":0,\"startPos\":60,\"endPos\":88,\"compiled\":true," |
550 | |
551 | // With one call site... |
552 | "\"callSites\":[{\"name\":\"dyn:func\",\"tokenPos\":80,\"cacheEntries\":[" |
553 | |
554 | // First receiver: "Common", called twice. |
555 | "{\"receiver\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
556 | "\"name\":\"Common\"}," |
557 | |
558 | "\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\"," |
559 | "\"name\":\"func\"," |
560 | "\"owner\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
561 | "\"name\":\"Common\"},\"_kind\":\"RegularFunction\"," |
562 | "\"static\":false,\"const\":false,\"_intrinsic\":false," |
563 | "\"_native\":false}," |
564 | |
565 | "\"count\":2}," |
566 | |
567 | // Second receiver: "Uncommon", called once. |
568 | "{\"receiver\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
569 | "\"name\":\"Uncommon\"}," |
570 | |
571 | "\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\"," |
572 | "\"name\":\"func\"," |
573 | "\"owner\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
574 | "\"name\":\"Uncommon\"},\"_kind\":\"RegularFunction\"," |
575 | "\"static\":false,\"const\":false,\"_intrinsic\":false," |
576 | "\"_native\":false}," |
577 | |
578 | "\"count\":1}]}]}]," |
579 | |
580 | // One script in the script table. |
581 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
582 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
583 | buffer); |
584 | } |
585 | |
586 | ISOLATE_UNIT_TEST_CASE(SourceReport_MultipleReports) { |
587 | char buffer[1024]; |
588 | const char* kScript = |
589 | "helper0() {}\n" |
590 | "helper1() {}\n" |
591 | "main() {\n" |
592 | " helper0();\n" |
593 | "}" ; |
594 | |
595 | Library& lib = Library::Handle(); |
596 | lib ^= ExecuteScript(kScript); |
597 | ASSERT(!lib.IsNull()); |
598 | const Script& script = |
599 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
600 | |
601 | SourceReport report(SourceReport::kCallSites | SourceReport::kCoverage); |
602 | JSONStream js; |
603 | report.PrintJSON(&js, script); |
604 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
605 | ElideJSONSubstring("libraries" , buffer, buffer); |
606 | EXPECT_STREQ( |
607 | "{\"type\":\"SourceReport\",\"ranges\":[" |
608 | |
609 | // One range compiled with no callsites (helper0). |
610 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
611 | "\"callSites\":[]," |
612 | "\"coverage\":{\"hits\":[0],\"misses\":[]}}," |
613 | |
614 | // One range not compiled (helper1). |
615 | "{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false}," |
616 | |
617 | // One range compiled with one callsite (main). |
618 | "{\"scriptIndex\":0,\"startPos\":26,\"endPos\":48,\"compiled\":true," |
619 | "\"callSites\":[" |
620 | "{\"name\":\"helper0\",\"tokenPos\":37,\"cacheEntries\":[" |
621 | "{\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\"," |
622 | "\"name\":\"helper0\",\"owner\":{\"type\":\"@Library\",\"fixedId\":true," |
623 | "\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}," |
624 | "\"_kind\":\"RegularFunction\",\"static\":true,\"const\":false," |
625 | "\"_intrinsic\":false,\"_native\":false},\"count\":1}]}]," |
626 | "\"coverage\":{\"hits\":[26,37],\"misses\":[]}}]," |
627 | |
628 | // One script in the script table. |
629 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
630 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
631 | buffer); |
632 | } |
633 | |
634 | ISOLATE_UNIT_TEST_CASE(SourceReport_PossibleBreakpoints_Simple) { |
635 | char buffer[1024]; |
636 | const char* kScript = |
637 | "helper0() {}\n" |
638 | "helper1() {}\n" |
639 | "main() {\n" |
640 | " if (true) {\n" |
641 | " helper0();\n" |
642 | " } else {\n" |
643 | " helper1();\n" |
644 | " }\n" |
645 | "}" ; |
646 | |
647 | Library& lib = Library::Handle(); |
648 | lib ^= ExecuteScript(kScript); |
649 | ASSERT(!lib.IsNull()); |
650 | const Script& script = |
651 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
652 | |
653 | SourceReport report(SourceReport::kPossibleBreakpoints); |
654 | JSONStream js; |
655 | report.PrintJSON(&js, script); |
656 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
657 | ElideJSONSubstring("libraries" , buffer, buffer); |
658 | EXPECT_STREQ( |
659 | "{\"type\":\"SourceReport\",\"ranges\":[" |
660 | |
661 | // helper0. |
662 | "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true," |
663 | "\"possibleBreakpoints\":[7,11]}," |
664 | |
665 | // One range not compiled (helper1). |
666 | "{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false}," |
667 | |
668 | // main. |
669 | "{\"scriptIndex\":0,\"startPos\":26,\"endPos\":94,\"compiled\":true," |
670 | "\"possibleBreakpoints\":[30,53,79,94]}]," |
671 | |
672 | // Only one script in the script table. |
673 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
674 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
675 | buffer); |
676 | } |
677 | |
678 | ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_Issue35453_NoSuchMethod) { |
679 | char buffer[1024]; |
680 | const char* kScript = |
681 | "class Foo {\n" |
682 | " void bar() {}\n" |
683 | "}\n" |
684 | "class Unused implements Foo {\n" |
685 | " dynamic noSuchMethod(_) {}\n" |
686 | "}\n" |
687 | "void main() {\n" |
688 | " Foo().bar();\n" |
689 | "}\n" ; |
690 | |
691 | Library& lib = Library::Handle(); |
692 | lib ^= ExecuteScript(kScript); |
693 | ASSERT(!lib.IsNull()); |
694 | const Script& script = |
695 | Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib" )))); |
696 | |
697 | SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile); |
698 | JSONStream js; |
699 | report.PrintJSON(&js, script); |
700 | ElideJSONSubstring("classes" , js.ToCString(), buffer); |
701 | ElideJSONSubstring("libraries" , buffer, buffer); |
702 | EXPECT_STREQ( |
703 | "{\"type\":\"SourceReport\",\"ranges\":[" |
704 | |
705 | // Foo is hit. |
706 | "{\"scriptIndex\":0,\"startPos\":14,\"endPos\":26,\"compiled\":true," |
707 | "\"coverage\":{\"hits\":[14],\"misses\":[]}}," |
708 | |
709 | // Unused is missed. |
710 | "{\"scriptIndex\":0,\"startPos\":62,\"endPos\":87,\"compiled\":true," |
711 | "\"coverage\":{\"hits\":[],\"misses\":[62]}}," |
712 | |
713 | // Main is hit. |
714 | "{\"scriptIndex\":0,\"startPos\":91,\"endPos\":120,\"compiled\":true," |
715 | "\"coverage\":{\"hits\":[91,107,113],\"misses\":[]}}]," |
716 | |
717 | // Only one script in the script table. |
718 | "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
719 | "\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}" , |
720 | buffer); |
721 | } |
722 | |
723 | #endif // !PRODUCT |
724 | |
725 | } // namespace dart |
726 | |