52cfd29cbb
The RecordCoverage instruction has an A/E encoding. The A argument is the type of coverage being recorded, whereas the E argument is the logical index into the coverage array for updating whether that source position has been hit. Also adds new metadata to the bytecode component for the coverage arrays associated with bytecode containing RecordCoverage instructions and a new runtime entry for lazily allocate the coverage array for an interpreted function when needed. The type of coverage is encoded in the RecordCoverage instruction, despite being redundant with the information in the coverage array, so that checking whether that type of coverage is currently enabled at runtime doesn't require either accessing the coverage array (which may be lazily allocated), forcing allocation of the coverage array just to discover that type of coverage is currently disabled, or reading the serialized bytecode component to avoid that forced allocation. ------ Other changes: Source reporting now treats unexecuted interpreted functions when not forcing compilation as if they were uncompiled native functions, so that the source report from running the same code gives the same result whether using the interpreter or the native compiler. Bytecode closures are no longer skipped in source reports. Previously any closure without a context scope was skipped, but bytecode closures don't have those. TEST=vm/cc/SourceReport_Coverage Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try Change-Id: I7557e5dd4c98331c7ca2f5c867dd5f6d03e9d756 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501520 Reviewed-by: Alexander Markov <alexmarkov@google.com>
1784 lines
60 KiB
C++
1784 lines
60 KiB
C++
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/source_report.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/unit_test.h"
|
|
|
|
namespace dart {
|
|
|
|
#ifndef PRODUCT
|
|
|
|
static ObjectPtr ExecuteScript(const char* script) {
|
|
Dart_Handle lib;
|
|
{
|
|
TransitionVMToNative transition(Thread::Current());
|
|
lib = TestCase::LoadTestScript(script, nullptr);
|
|
EXPECT_VALID(lib);
|
|
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr);
|
|
EXPECT_VALID(result);
|
|
}
|
|
return Api::UnwrapHandle(lib);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_NoCalls) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"main() {\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("libraries", json_str, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":"
|
|
|
|
// One compiled range, one hit at function declaration.
|
|
"[{\"scriptIndex\":0,\"startPos\":0,\"endPos\":9,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}}],"
|
|
|
|
// One script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_Filters_single) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"main() {\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
|
|
GrowableObjectArray& filters =
|
|
GrowableObjectArray::Handle(GrowableObjectArray::New());
|
|
filters.Add(String::Handle(String::New(RESOLVED_USER_TEST_URI)));
|
|
SourceReport report(SourceReport::kCoverage, filters);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, Script::Handle(Script::null()));
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("libraries", json_str, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":"
|
|
|
|
// One compiled range, one hit at function declaration.
|
|
"[{\"scriptIndex\":0,\"startPos\":0,\"endPos\":9,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}}],"
|
|
|
|
// One script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_Filters_empty) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"main() {\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
|
|
GrowableObjectArray& filters =
|
|
GrowableObjectArray::Handle(GrowableObjectArray::New());
|
|
filters.Add(String::Handle(String::New("foo:bar/")));
|
|
SourceReport report(SourceReport::kCoverage, filters);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, Script::Handle(Script::null()));
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("libraries", json_str, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":"
|
|
|
|
// No compiled range.
|
|
"[],"
|
|
|
|
// No script.
|
|
"\"scripts\":[]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_SimpleCall) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" if (true) {\n"
|
|
" helper0();\n"
|
|
" } else {\n"
|
|
" helper1();\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// One range compiled with one hit at function declaration (helper0).
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
|
|
|
|
// One range not compiled (helper1).
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false},"
|
|
|
|
// One range with two hits and a miss (main).
|
|
"{\"scriptIndex\":0,\"startPos\":26,\"endPos\":94,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[26,53],\"misses\":[79]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_ForceCompile) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" if (true) {\n"
|
|
" helper0();\n"
|
|
" } else {\n"
|
|
" helper1();\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// One range compiled with one hit at function declaration (helper0).
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
|
|
|
|
// This range is compiled even though it wasn't called (helper1).
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[13]}},"
|
|
|
|
// One range with two hits and a miss (main).
|
|
"{\"scriptIndex\":0,\"startPos\":26,\"endPos\":94,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[26,53],\"misses\":[79]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_NoForceCompile) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"class Unused {\n"
|
|
" helper1() { helper0(); }\n"
|
|
"}\n"
|
|
"main() {\n"
|
|
" helper0();\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// UnusedClass is not compiled.
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":55,\"compiled\":false},"
|
|
|
|
// helper0 is compiled.
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
|
|
|
|
// One range with two hits (main).
|
|
"{\"scriptIndex\":0,\"startPos\":57,\"endPos\":79,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[57,68],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_UnusedClass_ForceCompile) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"class Unused {\n"
|
|
" helper1() { helper0(); }\n"
|
|
"}\n"
|
|
"main() {\n"
|
|
" helper0();\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// UnusedClass.helper1 is compiled.
|
|
"{\"scriptIndex\":0,\"startPos\":30,\"endPos\":53,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[30,42]}},"
|
|
|
|
// helper0 is compiled.
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
|
|
|
|
// One range with two hits (main).
|
|
"{\"scriptIndex\":0,\"startPos\":57,\"endPos\":79,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[57,68],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_LibrariesAlreadyCompiled) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"class Unused {\n"
|
|
" helper1() { helper0(); }\n"
|
|
"}\n"
|
|
"main() {\n"
|
|
" helper0();\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
Zone* zone = thread->zone();
|
|
ZoneCStringSet* libraries_already_compiled = new (zone) ZoneCStringSet(zone);
|
|
libraries_already_compiled->Insert(RESOLVED_USER_TEST_URI);
|
|
SourceReport report(SourceReport::kCoverage, GrowableObjectArray::Handle(),
|
|
libraries_already_compiled, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// UnusedClass is not compiled.
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":55,\"compiled\":false},"
|
|
|
|
// helper0 is compiled.
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
|
|
|
|
// One range with two hits (main).
|
|
"{\"scriptIndex\":0,\"startPos\":57,\"endPos\":79,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[57,68],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_NestedFunctions) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {\n"
|
|
" nestedHelper0() {}\n"
|
|
" nestedHelper1() {}\n"
|
|
" nestedHelper0();\n"
|
|
"}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" if (true) {\n"
|
|
" helper0();\n"
|
|
" } else {\n"
|
|
" helper1();\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// One range compiled with one hit (helper0).
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":73,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0,56],\"misses\":[]}},"
|
|
|
|
// One range not compiled (helper1).
|
|
"{\"scriptIndex\":0,\"startPos\":75,\"endPos\":86,\"compiled\":false},"
|
|
|
|
// One range with two hits and a miss (main).
|
|
"{\"scriptIndex\":0,\"startPos\":88,\"endPos\":156,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[88,115],\"misses\":[141]}},"
|
|
|
|
// Nested range compiled (nestedHelper0).
|
|
"{\"scriptIndex\":0,\"startPos\":14,\"endPos\":31,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[14],\"misses\":[]}},"
|
|
|
|
// Nested range not compiled (nestedHelper1).
|
|
"{\"scriptIndex\":0,\"startPos\":35,\"endPos\":52,\"compiled\":false}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_RestrictedRange) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {\n"
|
|
" nestedHelper0() {}\n"
|
|
" nestedHelper1() {}\n"
|
|
" nestedHelper0();\n"
|
|
"}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" if (true) {\n"
|
|
" helper0();\n"
|
|
" } else {\n"
|
|
" helper1();\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
const Function& helper = Function::Handle(
|
|
lib.LookupFunctionAllowPrivate(String::Handle(String::New("helper0"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
// Restrict the report to only helper0 and it's nested functions.
|
|
report.PrintJSON(&js, script, helper.token_pos(), helper.end_token_pos());
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// One range compiled with one hit (helper0).
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":73,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0,56],\"misses\":[]}},"
|
|
|
|
// Nested range compiled (nestedHelper0).
|
|
"{\"scriptIndex\":0,\"startPos\":14,\"endPos\":31,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[14],\"misses\":[]}},"
|
|
|
|
// Nested range not compiled (nestedHelper1).
|
|
"{\"scriptIndex\":0,\"startPos\":35,\"endPos\":52,\"compiled\":false}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_AllFunctions) {
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" if (true) {\n"
|
|
" helper0();\n"
|
|
" } else {\n"
|
|
" helper1();\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
|
|
// We generate a report with all functions in the VM.
|
|
Script& null_script = Script::Handle();
|
|
report.PrintJSON(&js, null_script);
|
|
const char* result = js.ToCString();
|
|
|
|
// Sanity check the header.
|
|
EXPECT_SUBSTRING("{\"type\":\"SourceReport\",\"ranges\":[", result);
|
|
|
|
// Make sure that the main function was found.
|
|
EXPECT_SUBSTRING(
|
|
"\"startPos\":26,\"endPos\":94,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[26,53],\"misses\":[79]}",
|
|
result);
|
|
|
|
// More than one script is referenced in the report.
|
|
EXPECT_SUBSTRING("\"scriptIndex\":0", result);
|
|
EXPECT_SUBSTRING("\"scriptIndex\":1", result);
|
|
EXPECT_SUBSTRING("\"scriptIndex\":2", result);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_AllFunctions_ForceCompile) {
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" if (true) {\n"
|
|
" helper0();\n"
|
|
" } else {\n"
|
|
" helper1();\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
js.set_id_zone(thread->isolate()->EnsureDefaultServiceIdZone());
|
|
|
|
// We generate a report with all functions in the VM.
|
|
Script& null_script = Script::Handle();
|
|
report.PrintJSON(&js, null_script);
|
|
const char* result = js.ToCString();
|
|
|
|
// Sanity check the header.
|
|
EXPECT_SUBSTRING("{\"type\":\"SourceReport\",\"ranges\":[", result);
|
|
|
|
// Make sure that the main function was found.
|
|
EXPECT_SUBSTRING(
|
|
"\"startPos\":26,\"endPos\":94,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[26,53],\"misses\":[79]}",
|
|
result);
|
|
|
|
// More than one script is referenced in the report.
|
|
EXPECT_SUBSTRING("\"scriptIndex\":0", result);
|
|
EXPECT_SUBSTRING("\"scriptIndex\":1", result);
|
|
EXPECT_SUBSTRING("\"scriptIndex\":2", result);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_CallSites_SimpleCall) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 2048;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" helper0();\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCallSites);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// One range compiled with no callsites (helper0).
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"callSites\":[]},"
|
|
|
|
// One range not compiled (helper1).
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false},"
|
|
|
|
// One range compiled with one callsite (main).
|
|
"{\"scriptIndex\":0,\"startPos\":26,\"endPos\":48,\"compiled\":true,"
|
|
"\"callSites\":["
|
|
"{\"name\":\"helper0\",\"tokenPos\":37,\"cacheEntries\":["
|
|
"{\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"helper0\",\"owner\":{\"type\":\"@Library\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"},"
|
|
"\"_kind\":\"RegularFunction\",\"static\":true,\"const\":false,"
|
|
"\"implicit\":false,\"abstract\":false,"
|
|
"\"_intrinsic\":false,\"_native\":false,\"isGetter\":false,"
|
|
"\"isSetter\":false,\"location\":{\"type\":"
|
|
"\"SourceLocation\",\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"},"
|
|
"\"tokenPos\":0,\"endTokenPos\":11,\"line\":1,\"column\":1}},\"count\":1}"
|
|
"]}]}],"
|
|
|
|
// One script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_CallSites_PolymorphicCall) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 4096;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"class Common {\n"
|
|
" func() {}\n"
|
|
"}\n"
|
|
"class Uncommon {\n"
|
|
" func() {}\n"
|
|
"}\n"
|
|
"helper(arg) {\n"
|
|
" arg.func();\n"
|
|
"}\n"
|
|
"main() {\n"
|
|
" Common common = new Common();\n"
|
|
" Uncommon uncommon = new Uncommon();\n"
|
|
" helper(common);\n"
|
|
" helper(common);\n"
|
|
" helper(uncommon);\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
const Function& helper = Function::Handle(
|
|
lib.LookupFunctionAllowPrivate(String::Handle(String::New("helper"))));
|
|
|
|
SourceReport report(SourceReport::kCallSites);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script, helper.token_pos(), helper.end_token_pos());
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// One range...
|
|
"{\"scriptIndex\":0,\"startPos\":60,\"endPos\":88,\"compiled\":true,"
|
|
|
|
// With one call site...
|
|
"\"callSites\":[{\"name\":\"dyn:func\",\"tokenPos\":80,\"cacheEntries\":["
|
|
|
|
// First receiver: "Common", called twice.
|
|
"{\"receiver\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"Common\","
|
|
"\"location\":{\"type\":\"SourceLocation\","
|
|
"\"script\":{\"type\":\"@Script\","
|
|
"\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\","
|
|
"\"_kind\":\"kernel\"},\"tokenPos\":0,\"endTokenPos\":27,\"line\":1,"
|
|
"\"column\":1},"
|
|
"\"library\":{\"type\":\"@Library\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}},"
|
|
|
|
"\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"func\","
|
|
"\"owner\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"Common\","
|
|
"\"location\":{\"type\":\"SourceLocation\","
|
|
"\"script\":{\"type\":\"@Script\","
|
|
"\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\","
|
|
"\"_kind\":\"kernel\"},\"tokenPos\":0,\"endTokenPos\":27,\"line\":1,"
|
|
"\"column\":1},"
|
|
"\"library\":{\"type\":\"@Library\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}"
|
|
"},\"_kind\":\"RegularFunction\","
|
|
"\"static\":false,\"const\":false,\"implicit\":false,\"abstract\":"
|
|
"false,\"_intrinsic\":false,"
|
|
"\"_native\":false,\"isGetter\":false,\"isSetter\":false,"
|
|
"\"location\":{\"type\":\"SourceLocation\","
|
|
"\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\","
|
|
"\"_kind\":\"kernel\"},\"tokenPos\":17,\"endTokenPos\":25,\"line\":2,"
|
|
"\"column\":3}},"
|
|
|
|
"\"count\":2},"
|
|
|
|
// Second receiver: "Uncommon", called once.
|
|
"{\"receiver\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"Uncommon\","
|
|
"\"location\":{\"type\":\"SourceLocation\","
|
|
"\"script\":{\"type\":\"@Script\","
|
|
"\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\","
|
|
"\"_kind\":\"kernel\"},\"tokenPos\":29,\"endTokenPos\":58,\"line\":4,"
|
|
"\"column\":1},"
|
|
"\"library\":{\"type\":\"@Library\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}},"
|
|
|
|
"\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"func\","
|
|
"\"owner\":{\"type\":\"@Class\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"Uncommon\","
|
|
"\"location\":{\"type\":\"SourceLocation\","
|
|
"\"script\":{\"type\":\"@Script\","
|
|
"\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\","
|
|
"\"_kind\":\"kernel\"},\"tokenPos\":29,\"endTokenPos\":58,\"line\":4,"
|
|
"\"column\":1},"
|
|
"\"library\":{\"type\":\"@Library\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"}"
|
|
"},\"_kind\":\"RegularFunction\","
|
|
"\"static\":false,\"const\":false,\"implicit\":false,\"abstract\":"
|
|
"false,\"_intrinsic\":false,"
|
|
"\"_native\":false,\"isGetter\":false,\"isSetter\":false,"
|
|
"\"location\":{\"type\":\"SourceLocation\","
|
|
"\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\","
|
|
"\"_kind\":\"kernel\"},\"tokenPos\":48,\"endTokenPos\":56,\"line\":5,"
|
|
"\"column\":3}},"
|
|
|
|
"\"count\":1}]}]}],"
|
|
|
|
// One script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_MultipleReports) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 2048;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" helper0();\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCallSites | SourceReport::kCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// One range compiled with no callsites (helper0).
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"callSites\":[],"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
|
|
|
|
// One range not compiled (helper1).
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false},"
|
|
|
|
// One range compiled with one callsite (main)m
|
|
"{\"scriptIndex\":0,\"startPos\":26,\"endPos\":48,\"compiled\":true,"
|
|
"\"callSites\":[{\"name\":\"helper0\",\"tokenPos\":37,\"cacheEntries\":[{"
|
|
"\"target\":{\"type\":\"@Function\",\"fixedId\":true,\"id\":\"\","
|
|
"\"name\":\"helper0\",\"owner\":{\"type\":\"@Library\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"name\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\"},\"_"
|
|
"kind\":\"RegularFunction\",\"static\":true,\"const\":false,\"implicit\":"
|
|
"false,\"abstract\":false,\"_"
|
|
"intrinsic\":false,\"_native\":false,\"isGetter\":false,"
|
|
"\"isSetter\":false,\"location\":{\"type\":"
|
|
"\"SourceLocation\",\"script\":{\"type\":\"@Script\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"},"
|
|
"\"tokenPos\":0,\"endTokenPos\":11,\"line\":1,\"column\":1}},\"count\":1}"
|
|
"]}],\"coverage\":{"
|
|
"\"hits\":[26,37],\"misses\":[]}}],"
|
|
|
|
// One script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_PossibleBreakpoints_Simple) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"helper0() {}\n"
|
|
"helper1() {}\n"
|
|
"main() {\n"
|
|
" if (true) {\n"
|
|
" helper0();\n"
|
|
" } else {\n"
|
|
" helper1();\n"
|
|
" }\n"
|
|
"}";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kPossibleBreakpoints);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// helper0.
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":11,\"compiled\":true,"
|
|
"\"possibleBreakpoints\":[7,11]},"
|
|
|
|
// One range not compiled (helper1).
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":24,\"compiled\":false},"
|
|
|
|
// main.
|
|
"{\"scriptIndex\":0,\"startPos\":26,\"endPos\":94,\"compiled\":true,"
|
|
"\"possibleBreakpoints\":[30,53,79,94]}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_Issue35453_NoSuchMethod) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"class Foo {\n"
|
|
" void bar() {}\n"
|
|
"}\n"
|
|
"class Unused implements Foo {\n"
|
|
" dynamic noSuchMethod(_) {}\n"
|
|
"}\n"
|
|
"void main() {\n"
|
|
" Foo().bar();\n"
|
|
"}\n";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// Foo is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":14,\"endPos\":26,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[14],\"misses\":[]}},"
|
|
|
|
// Unused is missed.
|
|
"{\"scriptIndex\":0,\"startPos\":62,\"endPos\":87,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[62]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":91,\"endPos\":120,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[91,107,113],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_Issue47017_Assert) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"void foo(Object? bar) {\n"
|
|
" assert(bar == null);\n"
|
|
"}\n"
|
|
"void main() {\n"
|
|
" foo(null);\n"
|
|
"}\n";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_asserts = IsolateGroup::Current()->asserts();
|
|
IsolateGroup::Current()->set_asserts(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_asserts(old_asserts);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// Foo is hit, and the assert is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":47,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0,33],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":49,\"endPos\":76,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[49,65],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_Issue47021_StaticOnlyClasses) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 4096;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"abstract class AllStatic {\n"
|
|
" AllStatic._();\n"
|
|
" static int test() => 123;\n"
|
|
" static int foo = 456;\n"
|
|
"}\n"
|
|
"class NotAbstract {\n"
|
|
" NotAbstract._();\n"
|
|
" static int test() => 123;\n"
|
|
" static int foo = 456;\n"
|
|
"}\n"
|
|
"abstract class NotConstructor {\n"
|
|
" void _() {}\n"
|
|
" static int test() => 123;\n"
|
|
"}\n"
|
|
"abstract class NotPrivate {\n"
|
|
" NotPrivate();\n"
|
|
" static int test() => 123;\n"
|
|
"}\n"
|
|
"abstract class HasParams {\n"
|
|
" HasParams._(int i);\n"
|
|
" static int test() => 123;\n"
|
|
"}\n"
|
|
"abstract class HasFields {\n"
|
|
" HasFields._();\n"
|
|
" static int test() => 123;\n"
|
|
" int foo = 0;\n"
|
|
"}\n"
|
|
"abstract class HasNonStaticFunction {\n"
|
|
" HasNonStaticFunction._();\n"
|
|
" static int test() => 123;\n"
|
|
" int foo() => 456;\n"
|
|
"}\n"
|
|
"abstract class HasSubclass {\n"
|
|
" HasSubclass._();\n"
|
|
" static int test() => 123;\n"
|
|
" static int foo = 456;\n"
|
|
"}\n"
|
|
"abstract class Subclass extends HasSubclass {\n"
|
|
" Subclass() : super._();\n"
|
|
"}\n"
|
|
"void main() {\n"
|
|
" AllStatic.test();\n"
|
|
" NotAbstract.test();\n"
|
|
" NotConstructor.test();\n"
|
|
" NotPrivate.test();\n"
|
|
" HasParams.test();\n"
|
|
" HasFields.test();\n"
|
|
" HasNonStaticFunction.test();\n"
|
|
" HasSubclass.test();\n"
|
|
"}\n";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// Subclass() is missed.
|
|
"{\"scriptIndex\":0,\"startPos\":775,\"endPos\":797,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[775,794]}},"
|
|
|
|
// AllStatic.test() is hit. AllStatic._() is ignored (would be pos: 29).
|
|
"{\"scriptIndex\":0,\"startPos\":46,\"endPos\":70,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[46],\"misses\":[]}},"
|
|
|
|
// HasSubclass._() is missed, not ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":656,\"endPos\":671,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[656]}},"
|
|
|
|
// HasSubclass.test() is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":675,\"endPos\":699,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[675],\"misses\":[]}},"
|
|
|
|
// HasParams._(int i) is missed, not ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":370,\"endPos\":388,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[370]}},"
|
|
|
|
// HasParams.test() is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":392,\"endPos\":416,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[392],\"misses\":[]}},"
|
|
|
|
// NotAbstract._() is missed, not ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":120,\"endPos\":135,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[120]}},"
|
|
|
|
// NotAbstract.test() is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":139,\"endPos\":163,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[139],\"misses\":[]}},"
|
|
|
|
// HasFields._() is missed, not ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":449,\"endPos\":462,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[449]}},"
|
|
|
|
// HasFields.test() is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":466,\"endPos\":490,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[466],\"misses\":[]}},"
|
|
|
|
// NotPrivate() is missed, not ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":297,\"endPos\":309,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[297]}},"
|
|
|
|
// NotPrivate.test() is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":313,\"endPos\":337,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[313],\"misses\":[]}},"
|
|
|
|
// HasNonStaticFunction._() is missed, not ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":549,\"endPos\":573,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[549]}},"
|
|
|
|
// HasNonStaticFunction.test() is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":577,\"endPos\":601,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[577],\"misses\":[]}},"
|
|
|
|
// HasNonStaticFunction.foo() is missed.
|
|
"{\"scriptIndex\":0,\"startPos\":605,\"endPos\":621,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[605]}},"
|
|
|
|
// NotConstructor._() is missed, not ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":225,\"endPos\":235,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[],\"misses\":[225]}},"
|
|
|
|
// NotConstructor.test() is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":239,\"endPos\":263,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[239],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":801,\"endPos\":996,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":"
|
|
"[801,827,849,874,895,915,935,966,988],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_IssueCov341_LateFinalVars) {
|
|
// https://github.com/dart-lang/coverage/issues/341
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"int foo(bool bar) {\n"
|
|
" late final int baz;\n"
|
|
" if (bar) {\n"
|
|
" baz = 123;\n"
|
|
" } else {\n"
|
|
" baz = 456;\n"
|
|
" }\n"
|
|
" return baz;\n"
|
|
"}\n"
|
|
"main() {\n"
|
|
" foo(true);\n"
|
|
" foo(false);\n"
|
|
"}\n";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// foo is hit, but the late variable sets and gets are ignored.
|
|
"{\"scriptIndex\":0,\"startPos\":0,\"endPos\":114,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[0],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":116,\"endPos\":152,\"compiled\":true,\""
|
|
"coverage\":{\"hits\":[116,127,140],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_IssueCov386_EnhancedEnums) {
|
|
// https://github.com/dart-lang/coverage/issues/386
|
|
// https://github.com/dart-lang/coverage/issues/377
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript =
|
|
"enum FoodType {\n"
|
|
" candy();\n"
|
|
" const FoodType();\n"
|
|
" factory FoodType.candyFactory() => candy;\n"
|
|
"}\n"
|
|
"void main() {\n"
|
|
" final food = FoodType.candyFactory();\n"
|
|
"}\n";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":49,\"endPos\":89,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[49],\"misses\":[]}},"
|
|
|
|
// The enum's constructor, and toString, are not included in the hitmap,
|
|
// but the factory is included.
|
|
"{\"scriptIndex\":0,\"startPos\":93,\"endPos\":147,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[93,131],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Regress95008_RedirectingFactory) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
class A {
|
|
A();
|
|
factory A.foo(int i) = B; // LINE_A
|
|
}
|
|
|
|
class B extends A {
|
|
int i;
|
|
B(this.i); // LINE_B
|
|
}
|
|
|
|
main() {
|
|
A.foo(42);
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// A()
|
|
"{\"scriptIndex\":0,\"startPos\":13,\"endPos\":16,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[13],\"misses\":[]}},"
|
|
|
|
// B()
|
|
"{\"scriptIndex\":0,\"startPos\":90,\"endPos\":99,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[90],\"misses\":[]}},"
|
|
|
|
// main
|
|
"{\"scriptIndex\":0,\"startPos\":114,\"endPos\":136,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[114,127],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Regress53519_Destructuring) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
main() {
|
|
destructure({
|
|
'hello': 'world',
|
|
'count': [1, 2, 3],
|
|
});
|
|
}
|
|
|
|
String destructure(Map<String, dynamic> map) {
|
|
final {'hello': world, 'count': count} = map;
|
|
return 'Hello $world, count: $count';
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
lib ^= ExecuteScript(kScript);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// main
|
|
"{\"scriptIndex\":0,\"startPos\":1,\"endPos\":78,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[1,12,24,61],\"misses\":[]}},"
|
|
|
|
// destructure
|
|
"{\"scriptIndex\":0,\"startPos\":81,\"endPos\":216,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[81,144,160,185],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_BranchCoverage_if) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
int ifTest(int x) {
|
|
if (x > 0) {
|
|
if (x > 10) {
|
|
return 10;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
main() {
|
|
ifTest(1);
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_branch_coverage = IsolateGroup::Current()->branch_coverage();
|
|
IsolateGroup::Current()->set_branch_coverage(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_branch_coverage(old_branch_coverage);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kBranchCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// In ifTest, the outer true case is hit, the inner true case is missed,
|
|
// the inner false case is hit, and the outer false case is missed.
|
|
"{\"scriptIndex\":0,\"startPos\":1,\"endPos\":135,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[1,34,82],\"misses\":[52,115]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":138,\"endPos\":160,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[138],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_BranchCoverage_loops) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
int loopTest() {
|
|
var x = 0;
|
|
|
|
while (x < 10) {
|
|
++x;
|
|
}
|
|
|
|
do {
|
|
++x;
|
|
} while (false);
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
++x;
|
|
}
|
|
|
|
for (final i in [1, 2, 3]) {
|
|
++x;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
main() {
|
|
loopTest();
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_branch_coverage = IsolateGroup::Current()->branch_coverage();
|
|
IsolateGroup::Current()->set_branch_coverage(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_branch_coverage(old_branch_coverage);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kBranchCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// In loopTest, the while loop, do-while loop, for loop, and for-in loop
|
|
// are all hit.
|
|
"{\"scriptIndex\":0,\"startPos\":1,\"endPos\":205,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[1,49,70,132,177],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":208,\"endPos\":231,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[208],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_BranchCoverage_switch) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
int switchTest(int x) {
|
|
switch (x) {
|
|
case 0:
|
|
return 10;
|
|
case 1:
|
|
return 20;
|
|
default:
|
|
return 30;
|
|
}
|
|
}
|
|
|
|
main() {
|
|
switchTest(1);
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_branch_coverage = IsolateGroup::Current()->branch_coverage();
|
|
IsolateGroup::Current()->set_branch_coverage(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_branch_coverage(old_branch_coverage);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kBranchCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// In switchTest, the 1 case is hit and the others are missed.
|
|
"{\"scriptIndex\":0,\"startPos\":1,\"endPos\":132,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[1,73],\"misses\":[44,102]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":135,\"endPos\":161,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[135],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_BranchCoverage_try) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
void tryTestInner() {
|
|
try {
|
|
throw "abc";
|
|
} catch (e) {
|
|
} finally {
|
|
}
|
|
|
|
try {
|
|
throw "def";
|
|
} finally {
|
|
}
|
|
}
|
|
|
|
void tryTestOuter() {
|
|
try {
|
|
tryTestInner();
|
|
} catch (e) {
|
|
}
|
|
}
|
|
|
|
main() {
|
|
tryTestOuter();
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_branch_coverage = IsolateGroup::Current()->branch_coverage();
|
|
IsolateGroup::Current()->set_branch_coverage(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_branch_coverage(old_branch_coverage);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kBranchCoverage);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// In tryTestInner, the try/catch/finally and the try/finally are all hit,
|
|
// and the try/finally rethrows its exception.
|
|
"{\"scriptIndex\":0,\"startPos\":1,\"endPos\":126,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[1,29,62,76,89,120],\"misses\":[]}},"
|
|
|
|
// In tryTestOuter, the exception thrown by tryTestInner causes both the
|
|
// try and the catch to be hit.
|
|
"{\"scriptIndex\":0,\"startPos\":129,\"endPos\":199,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[129,157,193],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":202,\"endPos\":229,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[202],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\","
|
|
"\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_Coverage_constConstructor) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
abstract class SuperClass {
|
|
const SuperClass();
|
|
}
|
|
|
|
class Class extends SuperClass {
|
|
const Class() :
|
|
super();
|
|
int method() => 123;
|
|
}
|
|
|
|
main() {
|
|
const Class().method();
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_branch_coverage = IsolateGroup::Current()->branch_coverage();
|
|
IsolateGroup::Current()->set_branch_coverage(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_branch_coverage(old_branch_coverage);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kCoverage, SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// The super class constructor is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":31,\"endPos\":49,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[31],\"misses\":[]}},"
|
|
|
|
// The class constructor is hit, as well as the super() call.
|
|
"{\"scriptIndex\":0,\"startPos\":89,\"endPos\":118,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[89,111],\"misses\":[]}},"
|
|
|
|
// The method is called.
|
|
"{\"scriptIndex\":0,\"startPos\":122,\"endPos\":141,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[122],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":146,\"endPos\":181,\"compiled\":true,"
|
|
"\"coverage\":{\"hits\":[146,171],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,"
|
|
"\"id\":\"\",\"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_BranchCoverage_constConstructor) {
|
|
// WARNING: This MUST be big enough for the serialized JSON string.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
abstract class SuperClass {
|
|
const SuperClass();
|
|
}
|
|
|
|
class Class extends SuperClass {
|
|
const Class() :
|
|
super();
|
|
int method() => 123;
|
|
}
|
|
|
|
main() {
|
|
const Class().method();
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_branch_coverage = IsolateGroup::Current()->branch_coverage();
|
|
IsolateGroup::Current()->set_branch_coverage(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_branch_coverage(old_branch_coverage);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kBranchCoverage,
|
|
SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// The super class constructor is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":31,\"endPos\":49,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[31],\"misses\":[]}},"
|
|
|
|
// The class constructor is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":89,\"endPos\":118,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[89],\"misses\":[]}},"
|
|
|
|
// The method is called.
|
|
"{\"scriptIndex\":0,\"startPos\":122,\"endPos\":141,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[122],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":146,\"endPos\":181,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[146],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\",\""
|
|
"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
buffer);
|
|
}
|
|
|
|
ISOLATE_UNIT_TEST_CASE(SourceReport_BranchCoverage_finallyEdgeCases) {
|
|
// Regression test for https://github.com/dart-lang/tools/issues/2241
|
|
// The cases covered in this test are the same as those listed in
|
|
// StreamingFlowGraphBuilder::BuildTryFinally.
|
|
const int kBufferSize = 1024;
|
|
char buffer[kBufferSize];
|
|
const char* kScript = R"(
|
|
int viaReturn() {
|
|
try {
|
|
return 6 * 7;
|
|
} finally {
|
|
}
|
|
}
|
|
|
|
void viaBreak() {
|
|
for (int i = 0; i < 10; ++i) {
|
|
try {
|
|
break;
|
|
} finally {
|
|
}
|
|
}
|
|
}
|
|
|
|
void viaContinue() {
|
|
for (int i = 0; i < 10; ++i) {
|
|
try {
|
|
continue;
|
|
} finally {
|
|
}
|
|
}
|
|
}
|
|
|
|
int viaSwitchContinue(int x) {
|
|
switch (x) {
|
|
case 123:
|
|
try {
|
|
continue otherCase;
|
|
} finally {
|
|
}
|
|
otherCase:
|
|
case 456:
|
|
return 789;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void viaOpenBody() {
|
|
try {
|
|
} finally {
|
|
}
|
|
}
|
|
|
|
void viaThrow() {
|
|
try {
|
|
throw 123;
|
|
} catch (e) {
|
|
} finally {
|
|
}
|
|
}
|
|
|
|
main() {
|
|
viaReturn();
|
|
viaBreak();
|
|
viaContinue();
|
|
viaSwitchContinue(123);
|
|
viaOpenBody();
|
|
viaThrow();
|
|
}
|
|
)";
|
|
|
|
Library& lib = Library::Handle();
|
|
const bool old_branch_coverage = IsolateGroup::Current()->branch_coverage();
|
|
IsolateGroup::Current()->set_branch_coverage(true);
|
|
lib ^= ExecuteScript(kScript);
|
|
IsolateGroup::Current()->set_branch_coverage(old_branch_coverage);
|
|
ASSERT(!lib.IsNull());
|
|
const Script& script =
|
|
Script::Handle(lib.LookupScript(String::Handle(String::New("test-lib"))));
|
|
|
|
SourceReport report(SourceReport::kBranchCoverage,
|
|
SourceReport::kForceCompile);
|
|
JSONStream js;
|
|
report.PrintJSON(&js, script);
|
|
const char* json_str = js.ToCString();
|
|
ASSERT(strlen(json_str) < kBufferSize);
|
|
ElideJSONSubstring("classes", json_str, buffer);
|
|
ElideJSONSubstring("libraries", buffer, buffer);
|
|
EXPECT_STREQ(
|
|
"{\"type\":\"SourceReport\",\"ranges\":["
|
|
|
|
// viaReturn's finally is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":1,\"endPos\":63,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[1,25,57],\"misses\":[]}},"
|
|
|
|
// viaBreak's finally is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":66,\"endPos\":166,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[66,115,125,154],\"misses\":[]}},"
|
|
|
|
// viaContinue's finally is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":169,\"endPos\":275,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[169,221,231,263],\"misses\":[]}},"
|
|
|
|
// viaSwitchContinue's finally is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":278,\"endPos\":467,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[278,328,348,394,408],\"misses\":[]}},"
|
|
|
|
// viaOpenBody's finally is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":470,\"endPos\":517,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[470,497,511],\"misses\":[]}},"
|
|
|
|
// viaThrow's finally is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":520,\"endPos\":595,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[520,544,575,589],\"misses\":[]}},"
|
|
|
|
// Main is hit.
|
|
"{\"scriptIndex\":0,\"startPos\":598,\"endPos\":710,\"compiled\":true,"
|
|
"\"branchCoverage\":{\"hits\":[598],\"misses\":[]}}],"
|
|
|
|
// Only one script in the script table.
|
|
"\"scripts\":[{\"type\":\"@Script\",\"fixedId\":true,\"id\":\"\",\""
|
|
"uri\":\"file:\\/\\/\\/test-lib\",\"_kind\":\"kernel\"}]}",
|
|
|
|
buffer);
|
|
}
|
|
|
|
#endif // !PRODUCT
|
|
|
|
} // namespace dart
|