OLD | NEW |
(Empty) | |
| 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 static RawObject* ExecuteScript(const char* script) { |
| 12 Dart_Handle h_lib = TestCase::LoadTestScript(script, NULL); |
| 13 EXPECT_VALID(h_lib); |
| 14 Library& lib = Library::Handle(); |
| 15 lib ^= Api::UnwrapHandle(h_lib); |
| 16 EXPECT(!lib.IsNull()); |
| 17 Dart_Handle result = Dart_Invoke(h_lib, NewString("main"), 0, NULL); |
| 18 EXPECT_VALID(result); |
| 19 return Api::UnwrapHandle(h_lib); |
| 20 } |
| 21 |
| 22 |
| 23 TEST_CASE(SourceReport_Coverage_NoCalls) { |
| 24 char buffer[1024]; |
| 25 const char* kScript = |
| 26 "main() {\n" |
| 27 "}"; |
| 28 |
| 29 Library& lib = Library::Handle(); |
| 30 lib ^= ExecuteScript(kScript); |
| 31 ASSERT(!lib.IsNull()); |
| 32 const Script& script = Script::Handle(lib.LookupScript( |
| 33 String::Handle(String::New("test-lib")))); |
| 34 |
| 35 SourceReport report(SourceReport::kCoverage); |
| 36 JSONStream js; |
| 37 report.PrintJSONForScript(&js, script); |
| 38 ElideJSONSubstring("libraries", js.ToCString(), buffer); |
| 39 EXPECT_STREQ( |
| 40 "{\"type\":\"SourceReport\",\"ranges\":" |
| 41 |
| 42 // One compiled range, no hits or misses. |
| 43 "[{\"scriptIndex\":0,\"startPos\":0,\"endPos\":5,\"compiled\":true," |
| 44 "\"coverage\":{\"hits\":[],\"misses\":[]}}]," |
| 45 |
| 46 // One script in the script table. |
| 47 "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
| 48 "\"uri\":\"test-lib\",\"_kind\":\"script\"}]}", |
| 49 buffer); |
| 50 } |
| 51 |
| 52 |
| 53 TEST_CASE(SourceReport_Coverage_SimpleCall) { |
| 54 char buffer[1024]; |
| 55 const char* kScript = |
| 56 "helper0() {}\n" |
| 57 "helper1() {}\n" |
| 58 "main() {\n" |
| 59 " if (true) {\n" |
| 60 " helper0();\n" |
| 61 " } else {\n" |
| 62 " helper1();\n" |
| 63 " }\n" |
| 64 "}"; |
| 65 |
| 66 Library& lib = Library::Handle(); |
| 67 lib ^= ExecuteScript(kScript); |
| 68 ASSERT(!lib.IsNull()); |
| 69 const Script& script = Script::Handle(lib.LookupScript( |
| 70 String::Handle(String::New("test-lib")))); |
| 71 |
| 72 SourceReport report(SourceReport::kCoverage); |
| 73 JSONStream js; |
| 74 report.PrintJSONForScript(&js, script); |
| 75 ElideJSONSubstring("classes", js.ToCString(), buffer); |
| 76 ElideJSONSubstring("libraries", buffer, buffer); |
| 77 EXPECT_STREQ( |
| 78 "{\"type\":\"SourceReport\",\"ranges\":[" |
| 79 |
| 80 // One range compiled with no hits or misses (helper0). |
| 81 "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true," |
| 82 "\"coverage\":{\"hits\":[],\"misses\":[]}}," |
| 83 |
| 84 // One range not compiled (helper1). |
| 85 "{\"scriptIndex\":0,\"startPos\":6,\"endPos\":10,\"compiled\":false}," |
| 86 |
| 87 // One range with a hit and a miss (main). |
| 88 "{\"scriptIndex\":0,\"startPos\":12,\"endPos\":39,\"compiled\":true," |
| 89 "\"coverage\":{\"hits\":[23],\"misses\":[32]}}]," |
| 90 |
| 91 // Only one script in the script table. |
| 92 "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
| 93 "\"uri\":\"test-lib\",\"_kind\":\"script\"}]}", |
| 94 buffer); |
| 95 } |
| 96 |
| 97 |
| 98 TEST_CASE(SourceReport_Coverage_ForceCompile) { |
| 99 char buffer[1024]; |
| 100 const char* kScript = |
| 101 "helper0() {}\n" |
| 102 "helper1() {}\n" |
| 103 "main() {\n" |
| 104 " if (true) {\n" |
| 105 " helper0();\n" |
| 106 " } else {\n" |
| 107 " helper1();\n" |
| 108 " }\n" |
| 109 "}"; |
| 110 |
| 111 Library& lib = Library::Handle(); |
| 112 lib ^= ExecuteScript(kScript); |
| 113 ASSERT(!lib.IsNull()); |
| 114 const Script& script = Script::Handle(lib.LookupScript( |
| 115 String::Handle(String::New("test-lib")))); |
| 116 |
| 117 SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile); |
| 118 JSONStream js; |
| 119 report.PrintJSONForScript(&js, script); |
| 120 ElideJSONSubstring("classes", js.ToCString(), buffer); |
| 121 ElideJSONSubstring("libraries", buffer, buffer); |
| 122 EXPECT_STREQ( |
| 123 "{\"type\":\"SourceReport\",\"ranges\":[" |
| 124 |
| 125 // One range compiled with no hits or misses (helper0). |
| 126 "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true," |
| 127 "\"coverage\":{\"hits\":[],\"misses\":[]}}," |
| 128 |
| 129 // This range is compiled even though it wasn't called (helper1). |
| 130 "{\"scriptIndex\":0,\"startPos\":6,\"endPos\":10,\"compiled\":true," |
| 131 "\"coverage\":{\"hits\":[],\"misses\":[]}}," |
| 132 |
| 133 // One range with a hit and a miss (main). |
| 134 "{\"scriptIndex\":0,\"startPos\":12,\"endPos\":39,\"compiled\":true," |
| 135 "\"coverage\":{\"hits\":[23],\"misses\":[32]}}]," |
| 136 |
| 137 // Only one script in the script table. |
| 138 "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
| 139 "\"uri\":\"test-lib\",\"_kind\":\"script\"}]}", |
| 140 buffer); |
| 141 } |
| 142 |
| 143 |
| 144 TEST_CASE(SourceReport_Coverage_NestedFunctions) { |
| 145 char buffer[1024]; |
| 146 const char* kScript = |
| 147 "helper0() {\n" |
| 148 " nestedHelper0() {}\n" |
| 149 " nestedHelper1() {}\n" |
| 150 " nestedHelper0();\n" |
| 151 "}\n" |
| 152 "helper1() {}\n" |
| 153 "main() {\n" |
| 154 " if (true) {\n" |
| 155 " helper0();\n" |
| 156 " } else {\n" |
| 157 " helper1();\n" |
| 158 " }\n" |
| 159 "}"; |
| 160 |
| 161 Library& lib = Library::Handle(); |
| 162 lib ^= ExecuteScript(kScript); |
| 163 ASSERT(!lib.IsNull()); |
| 164 const Script& script = Script::Handle(lib.LookupScript( |
| 165 String::Handle(String::New("test-lib")))); |
| 166 |
| 167 SourceReport report(SourceReport::kCoverage); |
| 168 JSONStream js; |
| 169 report.PrintJSONForScript(&js, script); |
| 170 ElideJSONSubstring("classes", js.ToCString(), buffer); |
| 171 ElideJSONSubstring("libraries", buffer, buffer); |
| 172 EXPECT_STREQ( |
| 173 "{\"type\":\"SourceReport\",\"ranges\":[" |
| 174 |
| 175 // One range compiled with one hit (helper0). |
| 176 "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":22,\"compiled\":true," |
| 177 "\"coverage\":{\"hits\":[18],\"misses\":[]}}," |
| 178 |
| 179 // One range not compiled (helper1). |
| 180 "{\"scriptIndex\":0,\"startPos\":24,\"endPos\":28,\"compiled\":false}," |
| 181 |
| 182 // One range with a hit and a miss (main). |
| 183 "{\"scriptIndex\":0,\"startPos\":30,\"endPos\":57,\"compiled\":true," |
| 184 "\"coverage\":{\"hits\":[41],\"misses\":[50]}}," |
| 185 |
| 186 // Nested range compiled (nestedHelper0). |
| 187 "{\"scriptIndex\":0,\"startPos\":5,\"endPos\":9,\"compiled\":true," |
| 188 "\"coverage\":{\"hits\":[],\"misses\":[]}}," |
| 189 |
| 190 // Nested range not compiled (nestedHelper1). |
| 191 "{\"scriptIndex\":0,\"startPos\":11,\"endPos\":15,\"compiled\":false}]," |
| 192 |
| 193 // Only one script in the script table. |
| 194 "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
| 195 "\"uri\":\"test-lib\",\"_kind\":\"script\"}]}", |
| 196 buffer); |
| 197 } |
| 198 |
| 199 |
| 200 TEST_CASE(SourceReport_Coverage_RestrictedRange) { |
| 201 char buffer[1024]; |
| 202 const char* kScript = |
| 203 "helper0() {\n" |
| 204 " nestedHelper0() {}\n" |
| 205 " nestedHelper1() {}\n" |
| 206 " nestedHelper0();\n" |
| 207 "}\n" |
| 208 "helper1() {}\n" |
| 209 "main() {\n" |
| 210 " if (true) {\n" |
| 211 " helper0();\n" |
| 212 " } else {\n" |
| 213 " helper1();\n" |
| 214 " }\n" |
| 215 "}"; |
| 216 |
| 217 Library& lib = Library::Handle(); |
| 218 lib ^= ExecuteScript(kScript); |
| 219 ASSERT(!lib.IsNull()); |
| 220 const Script& script = Script::Handle(lib.LookupScript( |
| 221 String::Handle(String::New("test-lib")))); |
| 222 const Function& helper = Function::Handle( |
| 223 lib.LookupLocalFunction(String::Handle(String::New("helper0")))); |
| 224 |
| 225 SourceReport report(SourceReport::kCoverage); |
| 226 JSONStream js; |
| 227 // Restrict the report to only helper0 and it's nested functions. |
| 228 report.PrintJSONForScript(&js, script, |
| 229 helper.token_pos(), helper.end_token_pos()); |
| 230 ElideJSONSubstring("classes", js.ToCString(), buffer); |
| 231 ElideJSONSubstring("libraries", buffer, buffer); |
| 232 EXPECT_STREQ( |
| 233 "{\"type\":\"SourceReport\",\"ranges\":[" |
| 234 |
| 235 // One range compiled with one hit (helper0). |
| 236 "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":22,\"compiled\":true," |
| 237 "\"coverage\":{\"hits\":[18],\"misses\":[]}}," |
| 238 |
| 239 // Nested range compiled (nestedHelper0). |
| 240 "{\"scriptIndex\":0,\"startPos\":5,\"endPos\":9,\"compiled\":true," |
| 241 "\"coverage\":{\"hits\":[],\"misses\":[]}}," |
| 242 |
| 243 // Nested range not compiled (nestedHelper1). |
| 244 "{\"scriptIndex\":0,\"startPos\":11,\"endPos\":15,\"compiled\":false}]," |
| 245 |
| 246 // Only one script in the script table. |
| 247 "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
| 248 "\"uri\":\"test-lib\",\"_kind\":\"script\"}]}", |
| 249 buffer); |
| 250 } |
| 251 |
| 252 |
| 253 TEST_CASE(SourceReport_Coverage_AllFunctions) { |
| 254 const char* kScript = |
| 255 "helper0() {}\n" |
| 256 "helper1() {}\n" |
| 257 "main() {\n" |
| 258 " if (true) {\n" |
| 259 " helper0();\n" |
| 260 " } else {\n" |
| 261 " helper1();\n" |
| 262 " }\n" |
| 263 "}"; |
| 264 |
| 265 Library& lib = Library::Handle(); |
| 266 lib ^= ExecuteScript(kScript); |
| 267 ASSERT(!lib.IsNull()); |
| 268 |
| 269 SourceReport report(SourceReport::kCoverage); |
| 270 JSONStream js; |
| 271 |
| 272 // We generate a report with all functions in the VM. |
| 273 report.PrintJSON(&js); |
| 274 const char* result = js.ToCString(); |
| 275 |
| 276 // Sanity check the header. |
| 277 EXPECT_SUBSTRING("{\"type\":\"SourceReport\",\"ranges\":[", result); |
| 278 |
| 279 // Make sure that the main function was found. |
| 280 EXPECT_SUBSTRING( |
| 281 "\"startPos\":12,\"endPos\":39,\"compiled\":true," |
| 282 "\"coverage\":{\"hits\":[23],\"misses\":[32]}", |
| 283 result); |
| 284 |
| 285 // More than one script is referenced in the report. |
| 286 EXPECT_SUBSTRING("\"scriptIndex\":0", result); |
| 287 EXPECT_SUBSTRING("\"scriptIndex\":1", result); |
| 288 EXPECT_SUBSTRING("\"scriptIndex\":2", result); |
| 289 } |
| 290 |
| 291 |
| 292 TEST_CASE(SourceReport_CallSites_SimpleCall) { |
| 293 char buffer[1024]; |
| 294 const char* kScript = |
| 295 "helper0() {}\n" |
| 296 "helper1() {}\n" |
| 297 "main() {\n" |
| 298 " helper0();\n" |
| 299 "}"; |
| 300 |
| 301 Library& lib = Library::Handle(); |
| 302 lib ^= ExecuteScript(kScript); |
| 303 ASSERT(!lib.IsNull()); |
| 304 const Script& script = Script::Handle(lib.LookupScript( |
| 305 String::Handle(String::New("test-lib")))); |
| 306 |
| 307 SourceReport report(SourceReport::kCallSites); |
| 308 JSONStream js; |
| 309 report.PrintJSONForScript(&js, script); |
| 310 ElideJSONSubstring("classes", js.ToCString(), buffer); |
| 311 ElideJSONSubstring("libraries", buffer, buffer); |
| 312 EXPECT_STREQ( |
| 313 "{\"type\":\"SourceReport\",\"ranges\":[" |
| 314 |
| 315 // One range compiled with no callsites (helper0). |
| 316 "{\"scriptIndex\":0,\"startPos\":0,\"endPos\":4,\"compiled\":true," |
| 317 "\"callSites\":[]}," |
| 318 |
| 319 // One range not compiled (helper1). |
| 320 "{\"scriptIndex\":0,\"startPos\":6,\"endPos\":10,\"compiled\":false}," |
| 321 |
| 322 // One range compiled with one callsite (main). |
| 323 "{\"scriptIndex\":0,\"startPos\":12,\"endPos\":22,\"compiled\":true," |
| 324 "\"callSites\":[" |
| 325 "{\"name\":\"helper0\",\"tokenPos\":17,\"cacheEntries\":[" |
| 326 "{\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\"," |
| 327 "\"name\":\"helper0\",\"owner\":{\"type\":\"@Library\",\"fixedId\":true," |
| 328 "\"id\":\"\",\"name\":\"\",\"uri\":\"test-lib\"}," |
| 329 "\"_kind\":\"RegularFunction\",\"static\":true,\"const\":false," |
| 330 "\"_intrinsic\":false,\"_native\":false},\"count\":1}]}]}]," |
| 331 |
| 332 // One script in the script table. |
| 333 "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
| 334 "\"uri\":\"test-lib\",\"_kind\":\"script\"}]}", |
| 335 buffer); |
| 336 } |
| 337 |
| 338 TEST_CASE(SourceReport_CallSites_PolymorphicCall) { |
| 339 char buffer[1024]; |
| 340 const char* kScript = |
| 341 "class Common {\n" |
| 342 " func() {}\n" |
| 343 "}\n" |
| 344 "class Uncommon {\n" |
| 345 " func() {}\n" |
| 346 "}\n" |
| 347 "helper(arg) {\n" |
| 348 " arg.func();\n" |
| 349 "}\n" |
| 350 "main() {\n" |
| 351 " Common common = new Common();\n" |
| 352 " Uncommon uncommon = new Uncommon();\n" |
| 353 " helper(common);\n" |
| 354 " helper(common);\n" |
| 355 " helper(uncommon);\n" |
| 356 "}"; |
| 357 |
| 358 Library& lib = Library::Handle(); |
| 359 lib ^= ExecuteScript(kScript); |
| 360 ASSERT(!lib.IsNull()); |
| 361 const Script& script = Script::Handle(lib.LookupScript( |
| 362 String::Handle(String::New("test-lib")))); |
| 363 const Function& helper = Function::Handle( |
| 364 lib.LookupLocalFunction(String::Handle(String::New("helper")))); |
| 365 |
| 366 SourceReport report(SourceReport::kCallSites); |
| 367 JSONStream js; |
| 368 report.PrintJSONForScript(&js, script, |
| 369 helper.token_pos(), helper.end_token_pos()); |
| 370 ElideJSONSubstring("classes", js.ToCString(), buffer); |
| 371 ElideJSONSubstring("libraries", buffer, buffer); |
| 372 EXPECT_STREQ( |
| 373 "{\"type\":\"SourceReport\",\"ranges\":[" |
| 374 |
| 375 // One range... |
| 376 "{\"scriptIndex\":0,\"startPos\":24,\"endPos\":37,\"compiled\":true," |
| 377 |
| 378 // With one call site... |
| 379 "\"callSites\":[{\"name\":\"func\",\"tokenPos\":32,\"cacheEntries\":[" |
| 380 |
| 381 // First receiver: "Common", called twice. |
| 382 "{\"receiver\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
| 383 "\"name\":\"Common\"}," |
| 384 |
| 385 "\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\"," |
| 386 "\"name\":\"func\"," |
| 387 "\"owner\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
| 388 "\"name\":\"Common\"},\"_kind\":\"RegularFunction\"," |
| 389 "\"static\":false,\"const\":false,\"_intrinsic\":false," |
| 390 "\"_native\":false}," |
| 391 |
| 392 "\"count\":2}," |
| 393 |
| 394 // Second receiver: "Uncommon", called once. |
| 395 "{\"receiver\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
| 396 "\"name\":\"Uncommon\"}," |
| 397 |
| 398 "\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\"," |
| 399 "\"name\":\"func\"," |
| 400 "\"owner\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\"," |
| 401 "\"name\":\"Uncommon\"},\"_kind\":\"RegularFunction\"," |
| 402 "\"static\":false,\"const\":false,\"_intrinsic\":false," |
| 403 "\"_native\":false}," |
| 404 |
| 405 "\"count\":1}]}]}]," |
| 406 |
| 407 // One script in the script table. |
| 408 "\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\"," |
| 409 "\"uri\":\"test-lib\",\"_kind\":\"script\"}]}", |
| 410 buffer); |
| 411 } |
| 412 |
| 413 } // namespace dart |
OLD | NEW |