Index: runtime/vm/service_test.cc |
diff --git a/runtime/vm/service_test.cc b/runtime/vm/service_test.cc |
index 20863b07525134efa8da3b3e7a9a281805344122..c585ca821e41ae501356be318d9d9c28d240333e 100644 |
--- a/runtime/vm/service_test.cc |
+++ b/runtime/vm/service_test.cc |
@@ -1074,7 +1074,7 @@ TEST_CASE(Service_Classes) { |
Service::HandleIsolateMessage(isolate, service_msg); |
handler.HandleNextMessage(); |
ExpectSubstringF(handler.msg(), |
- "{\"type\":\"Error\",\"id\":\"\",\"message\":\"Invalid sub collection x\"," |
+ "{\"type\":\"Error\",\"id\":\"\",\"message\":\"Invalid sub command x\"," |
"\"request\":" |
"{\"arguments\":[\"classes\",\"%" Pd "\",\"functions\",\"b\",\"x\"]," |
"\"option_keys\":[],\"option_values\":[]}}", cid); |
@@ -1120,6 +1120,127 @@ TEST_CASE(Service_Classes) { |
} |
+TEST_CASE(Service_SetSource) { |
+ const char* kScript = |
+ "var port;\n" // Set to our mock port by C++. |
+ "\n" |
+ "class A {\n" |
+ " a() { return 1; }\n" |
+ " b() { return 0; }\n" |
+ " c(String f) { return f.length; }\n" |
+ "}\n" |
+ "main() {\n" |
+ " var z = new A();\n" |
+ " return z.a();\n" |
+ "}\n" |
+ "runB() {\n" |
+ " var z = new A();\n" |
+ " return z.b();\n" |
+ "}\n" |
+ "runC() {\n" |
+ " var z = new A();\n" |
+ " return z.c();\n" |
+ "}\n"; |
+ |
+ Isolate* isolate = Isolate::Current(); |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ Library& vmlib = Library::Handle(); |
+ vmlib ^= Api::UnwrapHandle(lib); |
+ EXPECT(!vmlib.IsNull()); |
+ Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
+ EXPECT_VALID(result); |
+ const Class& class_a = Class::Handle(GetClass(vmlib, "A")); |
+ EXPECT(!class_a.IsNull()); |
+ intptr_t cid = class_a.id(); |
+ |
+ // Build a mock message handler and wrap it in a dart port. |
+ ServiceTestMessageHandler handler; |
+ Dart_Port port_id = PortMap::CreatePort(&handler); |
+ Dart_Handle port = Api::NewHandle(isolate, SendPort::New(port_id)); |
+ EXPECT_VALID(port); |
+ EXPECT_VALID(Dart_SetField(lib, NewString("port"), port)); |
+ |
+ Array& service_msg = Array::Handle(); |
+ |
+ // Request the class A over the service. |
+ service_msg = EvalF(lib, "[0, port, ['classes', '%" Pd "'], [], []]", cid); |
+ Service::HandleIsolateMessage(isolate, service_msg); |
+ handler.HandleNextMessage(); |
+ EXPECT_SUBSTRING("\"type\":\"Class\"", handler.msg()); |
+ ExpectSubstringF(handler.msg(), |
+ "\"id\":\"classes\\/%" Pd "\",\"name\":\"A\",", cid); |
+ ExpectSubstringF(handler.msg(), "\"allocationStats\":"); |
+ ExpectSubstringF(handler.msg(), "\"tokenPos\":"); |
+ ExpectSubstringF(handler.msg(), "\"endTokenPos\":"); |
+ |
+ // Request function 'b' from class A. |
+ service_msg = EvalF(lib, |
+ "[0, port, ['classes', '%" Pd "', 'functions', 'b']," |
+ "[], []]", cid); |
+ Service::HandleIsolateMessage(isolate, service_msg); |
+ handler.HandleNextMessage(); |
+ EXPECT_SUBSTRING("\"type\":\"Function\"", handler.msg()); |
+ ExpectSubstringF(handler.msg(), |
+ "\"id\":\"classes\\/%" Pd "\\/functions\\/b\"," |
+ "\"name\":\"b\",", cid); |
+ |
+ // Invalid set source of function 'b' from class A. |
+ service_msg = EvalF( |
+ lib, |
+ "[0, port, ['classes', '%" Pd "', 'functions', 'b', 'set_source']," |
+ "[], []]", cid); |
+ Service::HandleIsolateMessage(isolate, service_msg); |
+ handler.HandleNextMessage(); |
+ EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg()); |
+ EXPECT_SUBSTRING("set_source expects a 'source' option", handler.msg()); |
+ |
+ // Set source (with syntax error) of function 'b' from class A. |
+ service_msg = EvalF( |
+ lib, |
+ "[0, port, ['classes', '%" Pd "', 'functions', 'b', 'set_source']," |
+ "['source'], ['b() { return 4 }']]", cid); |
+ Service::HandleIsolateMessage(isolate, service_msg); |
+ handler.HandleNextMessage(); |
+ EXPECT_SUBSTRING("\"type\":\"Error\"", handler.msg()); |
+ |
+ // Set source of function 'b' from class A. |
+ service_msg = EvalF( |
+ lib, |
+ "[0, port, ['classes', '%" Pd "', 'functions', 'b', 'set_source']," |
+ "['source'], ['b() { return 4; }']]", cid); |
+ Service::HandleIsolateMessage(isolate, service_msg); |
+ handler.HandleNextMessage(); |
+ EXPECT_SUBSTRING("Success", handler.msg()); |
+ |
+ // Run function 'b' see that it is executing replaced code. |
+ result = Dart_Invoke(lib, NewString("runB"), 0, NULL); |
+ EXPECT_VALID(result); |
+ ASSERT(Dart_IsInteger(result)); |
+ int64_t r; |
+ result = Dart_IntegerToInt64(result, &r); |
+ EXPECT_VALID(result); |
+ EXPECT_EQ(4, r); |
+ |
+ // Set source of function 'c' from class A, changing its signature. |
+ service_msg = EvalF( |
+ lib, |
+ "[0, port, ['classes', '%" Pd "', 'functions', 'c', 'set_source']," |
+ "['source'], ['c() { return 99; }']]", cid); |
+ Service::HandleIsolateMessage(isolate, service_msg); |
+ handler.HandleNextMessage(); |
+ EXPECT_SUBSTRING("Success", handler.msg()); |
+ |
+ // Run function 'c' see that it is executing replaced code. |
+ result = Dart_Invoke(lib, NewString("runC"), 0, NULL); |
+ EXPECT_VALID(result); |
+ ASSERT(Dart_IsInteger(result)); |
+ result = Dart_IntegerToInt64(result, &r); |
+ EXPECT_VALID(result); |
+ EXPECT_EQ(99, r); |
+} |
+ |
+ |
TEST_CASE(Service_Types) { |
const char* kScript = |
"var port;\n" // Set to our mock port by C++. |