| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, 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 library service_test_common; |
| 6 |
| 7 import 'dart:async'; |
| 8 import 'package:observatory/service_common.dart'; |
| 9 import 'package:unittest/unittest.dart'; |
| 10 |
| 11 typedef Future IsolateTest(Isolate isolate); |
| 12 typedef Future VMTest(VM vm); |
| 13 |
| 14 Future asyncStepOver(Isolate isolate) async { |
| 15 final Completer pausedAtSyntheticBreakpoint = new Completer(); |
| 16 StreamSubscription subscription; |
| 17 |
| 18 // Cancel the subscription. |
| 19 cancelSubscription() { |
| 20 if (subscription != null) { |
| 21 subscription.cancel(); |
| 22 subscription = null; |
| 23 } |
| 24 } |
| 25 |
| 26 // Complete futures with with error. |
| 27 completeError(error) { |
| 28 if (!pausedAtSyntheticBreakpoint.isCompleted) { |
| 29 pausedAtSyntheticBreakpoint.completeError(error); |
| 30 } |
| 31 } |
| 32 |
| 33 // Subscribe to the debugger event stream. |
| 34 Stream stream; |
| 35 try { |
| 36 stream = await isolate.vm.getEventStream(VM.kDebugStream); |
| 37 } catch (e) { |
| 38 completeError(e); |
| 39 return pausedAtSyntheticBreakpoint.future; |
| 40 } |
| 41 |
| 42 Breakpoint syntheticBreakpoint; |
| 43 |
| 44 subscription = stream.listen((ServiceEvent event) async { |
| 45 // Synthetic breakpoint add event. This is the first event we will |
| 46 // receive. |
| 47 bool isAdd = (event.kind == ServiceEvent.kBreakpointAdded) && |
| 48 (event.breakpoint.isSyntheticAsyncContinuation) && |
| 49 (event.owner == isolate); |
| 50 // Resume after synthetic breakpoint added. This is the second event |
| 51 // we will recieve. |
| 52 bool isResume = (event.kind == ServiceEvent.kResume) && |
| 53 (syntheticBreakpoint != null) && |
| 54 (event.owner == isolate); |
| 55 // Paused at synthetic breakpoint. This is the third event we will |
| 56 // receive. |
| 57 bool isPaused = (event.kind == ServiceEvent.kPauseBreakpoint) && |
| 58 (syntheticBreakpoint != null) && |
| 59 (event.breakpoint == syntheticBreakpoint); |
| 60 if (isAdd) { |
| 61 syntheticBreakpoint = event.breakpoint; |
| 62 } else if (isResume) { |
| 63 } else if (isPaused) { |
| 64 pausedAtSyntheticBreakpoint.complete(isolate); |
| 65 syntheticBreakpoint = null; |
| 66 cancelSubscription(); |
| 67 } |
| 68 }); |
| 69 |
| 70 // Issue the step OverAwait command. |
| 71 try { |
| 72 await isolate.stepOverAsyncSuspension(); |
| 73 } catch (e) { |
| 74 // This can fail when another client issued the same resume command |
| 75 // or another client has moved the isolate forward. |
| 76 cancelSubscription(); |
| 77 completeError(e); |
| 78 } |
| 79 |
| 80 return pausedAtSyntheticBreakpoint.future; |
| 81 } |
| 82 |
| 83 |
| 84 Future<Isolate> hasPausedFor(Isolate isolate, String kind) { |
| 85 // Set up a listener to wait for breakpoint events. |
| 86 Completer completer = new Completer(); |
| 87 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { |
| 88 var subscription; |
| 89 subscription = stream.listen((ServiceEvent event) { |
| 90 if (event.kind == kind) { |
| 91 print('Paused with $kind'); |
| 92 subscription.cancel(); |
| 93 if (completer != null) { |
| 94 // Reload to update isolate.pauseEvent. |
| 95 completer.complete(isolate.reload()); |
| 96 completer = null; |
| 97 } |
| 98 } |
| 99 }); |
| 100 |
| 101 // Pause may have happened before we subscribed. |
| 102 isolate.reload().then((_) { |
| 103 if ((isolate.pauseEvent != null) && |
| 104 (isolate.pauseEvent.kind == kind)) { |
| 105 // Already waiting at a breakpoint. |
| 106 print('Paused with $kind'); |
| 107 subscription.cancel(); |
| 108 if (completer != null) { |
| 109 completer.complete(isolate); |
| 110 completer = null; |
| 111 } |
| 112 } |
| 113 }); |
| 114 }); |
| 115 |
| 116 return completer.future; // Will complete when breakpoint hit. |
| 117 } |
| 118 |
| 119 Future<Isolate> hasStoppedAtBreakpoint(Isolate isolate) { |
| 120 return hasPausedFor(isolate, ServiceEvent.kPauseBreakpoint); |
| 121 } |
| 122 |
| 123 Future<Isolate> hasStoppedWithUnhandledException(Isolate isolate) { |
| 124 return hasPausedFor(isolate, ServiceEvent.kPauseException); |
| 125 } |
| 126 |
| 127 Future<Isolate> hasPausedAtStart(Isolate isolate) { |
| 128 return hasPausedFor(isolate, ServiceEvent.kPauseStart); |
| 129 } |
| 130 |
| 131 // Currying is your friend. |
| 132 IsolateTest setBreakpointAtLine(int line) { |
| 133 return (Isolate isolate) async { |
| 134 print("Setting breakpoint for line $line"); |
| 135 Library lib = await isolate.rootLibrary.load(); |
| 136 Script script = lib.scripts.single; |
| 137 |
| 138 Breakpoint bpt = await isolate.addBreakpoint(script, line); |
| 139 print("Breakpoint is $bpt"); |
| 140 expect(bpt, isNotNull); |
| 141 expect(bpt is Breakpoint, isTrue); |
| 142 }; |
| 143 } |
| 144 |
| 145 IsolateTest stoppedAtLine(int line) { |
| 146 return (Isolate isolate) async { |
| 147 print("Checking we are at line $line"); |
| 148 |
| 149 ServiceMap stack = await isolate.getStack(); |
| 150 expect(stack.type, equals('Stack')); |
| 151 |
| 152 List<Frame> frames = stack['frames']; |
| 153 expect(frames.length, greaterThanOrEqualTo(1)); |
| 154 |
| 155 Frame top = frames[0]; |
| 156 Script script = await top.location.script.load(); |
| 157 int actualLine = script.tokenToLine(top.location.tokenPos); |
| 158 if (actualLine != line) { |
| 159 var sb = new StringBuffer(); |
| 160 sb.write("Expected to be at line $line but actually at line $actualLine"); |
| 161 sb.write("\nFull stack trace:\n"); |
| 162 for (Frame f in stack['frames']) { |
| 163 sb.write(" $f [${await f.location.getLine()}]\n"); |
| 164 } |
| 165 throw sb.toString(); |
| 166 } |
| 167 }; |
| 168 } |
| 169 |
| 170 |
| 171 Future<Isolate> resumeIsolate(Isolate isolate) { |
| 172 Completer completer = new Completer(); |
| 173 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { |
| 174 var subscription; |
| 175 subscription = stream.listen((ServiceEvent event) { |
| 176 if (event.kind == ServiceEvent.kResume) { |
| 177 subscription.cancel(); |
| 178 completer.complete(); |
| 179 } |
| 180 }); |
| 181 }); |
| 182 isolate.resume(); |
| 183 return completer.future; |
| 184 } |
| 185 |
| 186 |
| 187 Future resumeAndAwaitEvent(Isolate isolate, stream, onEvent) async { |
| 188 Completer completer = new Completer(); |
| 189 var sub; |
| 190 sub = await isolate.vm.listenEventStream( |
| 191 stream, |
| 192 (ServiceEvent event) { |
| 193 var r = onEvent(event); |
| 194 if (r is! Future) { |
| 195 r = new Future.value(r); |
| 196 } |
| 197 r.then((x) => sub.cancel().then((_) { |
| 198 completer.complete(); |
| 199 })); |
| 200 }); |
| 201 await isolate.resume(); |
| 202 return completer.future; |
| 203 } |
| 204 |
| 205 IsolateTest resumeIsolateAndAwaitEvent(stream, onEvent) { |
| 206 return (Isolate isolate) async => |
| 207 resumeAndAwaitEvent(isolate, stream, onEvent); |
| 208 } |
| 209 |
| 210 |
| 211 Future<Isolate> stepOver(Isolate isolate) async { |
| 212 await isolate.stepOver(); |
| 213 return hasStoppedAtBreakpoint(isolate); |
| 214 } |
| 215 |
| 216 Future<Class> getClassFromRootLib(Isolate isolate, String className) async { |
| 217 Library rootLib = await isolate.rootLibrary.load(); |
| 218 for (var i = 0; i < rootLib.classes.length; i++) { |
| 219 Class cls = rootLib.classes[i]; |
| 220 if (cls.name == className) { |
| 221 return cls; |
| 222 } |
| 223 } |
| 224 return null; |
| 225 } |
| 226 |
| 227 |
| 228 Future<Instance> rootLibraryFieldValue(Isolate isolate, |
| 229 String fieldName) async { |
| 230 Library rootLib = await isolate.rootLibrary.load(); |
| 231 Field field = rootLib.variables.singleWhere((v) => v.name == fieldName); |
| 232 await field.load(); |
| 233 Instance value = field.staticValue; |
| 234 await value.load(); |
| 235 return value; |
| 236 } |
| OLD | NEW |