OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library service_test_common; | 5 library service_test_common; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io' show Platform; |
8 import 'package:observatory/models.dart' as M; | 9 import 'package:observatory/models.dart' as M; |
9 import 'package:observatory/service_common.dart'; | 10 import 'package:observatory/service_common.dart'; |
10 import 'package:unittest/unittest.dart'; | 11 import 'package:unittest/unittest.dart'; |
11 | 12 |
12 typedef Future IsolateTest(Isolate isolate); | 13 typedef Future IsolateTest(Isolate isolate); |
13 typedef Future VMTest(VM vm); | 14 typedef Future VMTest(VM vm); |
14 | 15 |
15 Map<String, StreamSubscription> streamSubscriptions = {}; | 16 Map<String, StreamSubscription> streamSubscriptions = {}; |
16 | 17 |
17 Future subscribeToStream(VM vm, String streamName, onEvent) async { | 18 Future subscribeToStream(VM vm, String streamName, onEvent) async { |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 completeError(e); | 93 completeError(e); |
93 return pausedAtSyntheticBreakpoint.future; | 94 return pausedAtSyntheticBreakpoint.future; |
94 } | 95 } |
95 | 96 |
96 Breakpoint syntheticBreakpoint; | 97 Breakpoint syntheticBreakpoint; |
97 | 98 |
98 subscription = stream.listen((ServiceEvent event) async { | 99 subscription = stream.listen((ServiceEvent event) async { |
99 // Synthetic breakpoint add event. This is the first event we will | 100 // Synthetic breakpoint add event. This is the first event we will |
100 // receive. | 101 // receive. |
101 bool isAdd = (event.kind == ServiceEvent.kBreakpointAdded) && | 102 bool isAdd = (event.kind == ServiceEvent.kBreakpointAdded) && |
102 (event.breakpoint.isSyntheticAsyncContinuation) && | 103 (event.breakpoint.isSyntheticAsyncContinuation) && |
103 (event.owner == isolate); | 104 (event.owner == isolate); |
104 // Resume after synthetic breakpoint added. This is the second event | 105 // Resume after synthetic breakpoint added. This is the second event |
105 // we will recieve. | 106 // we will recieve. |
106 bool isResume = (event.kind == ServiceEvent.kResume) && | 107 bool isResume = (event.kind == ServiceEvent.kResume) && |
107 (syntheticBreakpoint != null) && | 108 (syntheticBreakpoint != null) && |
108 (event.owner == isolate); | 109 (event.owner == isolate); |
109 // Paused at synthetic breakpoint. This is the third event we will | 110 // Paused at synthetic breakpoint. This is the third event we will |
110 // receive. | 111 // receive. |
111 bool isPaused = (event.kind == ServiceEvent.kPauseBreakpoint) && | 112 bool isPaused = (event.kind == ServiceEvent.kPauseBreakpoint) && |
112 (syntheticBreakpoint != null) && | 113 (syntheticBreakpoint != null) && |
113 (event.breakpoint == syntheticBreakpoint); | 114 (event.breakpoint == syntheticBreakpoint); |
114 if (isAdd) { | 115 if (isAdd) { |
115 syntheticBreakpoint = event.breakpoint; | 116 syntheticBreakpoint = event.breakpoint; |
116 } else if (isResume) { | 117 } else if (isResume) {} else if (isPaused) { |
117 } else if (isPaused) { | |
118 pausedAtSyntheticBreakpoint.complete(isolate); | 118 pausedAtSyntheticBreakpoint.complete(isolate); |
119 syntheticBreakpoint = null; | 119 syntheticBreakpoint = null; |
120 cancelSubscription(); | 120 cancelSubscription(); |
121 } | 121 } |
122 }); | 122 }); |
123 | 123 |
124 // Issue the step OverAwait command. | 124 // Issue the step OverAwait command. |
125 try { | 125 try { |
126 await isolate.stepOverAsyncSuspension(); | 126 await isolate.stepOverAsyncSuspension(); |
127 } catch (e) { | 127 } catch (e) { |
(...skipping 22 matching lines...) Expand all Loading... |
150 return false; | 150 return false; |
151 } | 151 } |
152 } | 152 } |
153 | 153 |
154 Future<Isolate> hasPausedFor(Isolate isolate, String kind) { | 154 Future<Isolate> hasPausedFor(Isolate isolate, String kind) { |
155 // Set up a listener to wait for breakpoint events. | 155 // Set up a listener to wait for breakpoint events. |
156 Completer completer = new Completer(); | 156 Completer completer = new Completer(); |
157 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { | 157 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { |
158 var subscription; | 158 var subscription; |
159 subscription = stream.listen((ServiceEvent event) { | 159 subscription = stream.listen((ServiceEvent event) { |
160 if ((isolate == event.isolate) && (event.kind == kind)) { | 160 if ((isolate == event.isolate) && (event.kind == kind)) { |
161 if (completer != null) { | 161 if (completer != null) { |
162 // Reload to update isolate.pauseEvent. | 162 // Reload to update isolate.pauseEvent. |
163 print('Paused with $kind'); | 163 print('Paused with $kind'); |
164 subscription.cancel(); | 164 subscription.cancel(); |
165 completer.complete(isolate.reload()); | 165 completer.complete(isolate.reload()); |
166 completer = null; | 166 completer = null; |
167 } | |
168 } | 167 } |
| 168 } |
169 }); | 169 }); |
170 | 170 |
171 // Pause may have happened before we subscribed. | 171 // Pause may have happened before we subscribed. |
172 isolate.reload().then((_) { | 172 isolate.reload().then((_) { |
173 if ((isolate.pauseEvent != null) && | 173 if ((isolate.pauseEvent != null) && |
174 isEventOfKind(isolate.pauseEvent, kind)) { | 174 isEventOfKind(isolate.pauseEvent, kind)) { |
175 // Already waiting at a breakpoint. | 175 // Already waiting at a breakpoint. |
176 if (completer != null) { | 176 if (completer != null) { |
177 print('Paused with $kind'); | 177 print('Paused with $kind'); |
178 subscription.cancel(); | 178 subscription.cancel(); |
179 completer.complete(isolate); | 179 completer.complete(isolate); |
180 completer = null; | 180 completer = null; |
181 } | 181 } |
182 } | 182 } |
183 }); | 183 }); |
184 }); | 184 }); |
185 | 185 |
186 return completer.future; // Will complete when breakpoint hit. | 186 return completer.future; // Will complete when breakpoint hit. |
187 } | 187 } |
188 | 188 |
189 Future<Isolate> hasStoppedAtBreakpoint(Isolate isolate) { | 189 Future<Isolate> hasStoppedAtBreakpoint(Isolate isolate) { |
190 return hasPausedFor(isolate, ServiceEvent.kPauseBreakpoint); | 190 return hasPausedFor(isolate, ServiceEvent.kPauseBreakpoint); |
191 } | 191 } |
192 | 192 |
193 Future<Isolate> hasStoppedPostRequest(Isolate isolate) { | 193 Future<Isolate> hasStoppedPostRequest(Isolate isolate) { |
194 return hasPausedFor(isolate, ServiceEvent.kPausePostRequest); | 194 return hasPausedFor(isolate, ServiceEvent.kPausePostRequest); |
195 } | 195 } |
196 | 196 |
197 Future<Isolate> hasStoppedWithUnhandledException(Isolate isolate) { | 197 Future<Isolate> hasStoppedWithUnhandledException(Isolate isolate) { |
198 return hasPausedFor(isolate, ServiceEvent.kPauseException); | 198 return hasPausedFor(isolate, ServiceEvent.kPauseException); |
199 } | 199 } |
200 | 200 |
201 Future<Isolate> hasStoppedAtExit(Isolate isolate) { | 201 Future<Isolate> hasStoppedAtExit(Isolate isolate) { |
202 return hasPausedFor(isolate, ServiceEvent.kPauseExit); | 202 return hasPausedFor(isolate, ServiceEvent.kPauseExit); |
203 } | 203 } |
204 | 204 |
205 Future<Isolate> hasPausedAtStart(Isolate isolate) { | 205 Future<Isolate> hasPausedAtStart(Isolate isolate) { |
206 return hasPausedFor(isolate, ServiceEvent.kPauseStart); | 206 return hasPausedFor(isolate, ServiceEvent.kPauseStart); |
207 } | 207 } |
208 | 208 |
209 Future<Isolate> markDartColonLibrariesDebuggable(Isolate isolate) async { | 209 Future<Isolate> markDartColonLibrariesDebuggable(Isolate isolate) async { |
210 await isolate.reload(); | 210 await isolate.reload(); |
211 for (Library lib in isolate.libraries) { | 211 for (Library lib in isolate.libraries) { |
212 await lib.load(); | 212 await lib.load(); |
213 if (lib.uri.startsWith('dart:') && | 213 if (lib.uri.startsWith('dart:') && !lib.uri.startsWith('dart:_')) { |
214 !lib.uri.startsWith('dart:_')) { | |
215 var setDebugParams = { | 214 var setDebugParams = { |
216 'libraryId': lib.id, | 215 'libraryId': lib.id, |
217 'isDebuggable': true, | 216 'isDebuggable': true, |
218 }; | 217 }; |
219 Map<String, dynamic> result = | 218 Map<String, dynamic> result = await isolate.invokeRpcNoUpgrade( |
220 await isolate.invokeRpcNoUpgrade('setLibraryDebuggable', | 219 'setLibraryDebuggable', setDebugParams); |
221 setDebugParams); | |
222 } | 220 } |
223 } | 221 } |
224 return isolate; | 222 return isolate; |
225 } | 223 } |
226 | 224 |
227 IsolateTest reloadSources([bool pause = false]) { | 225 IsolateTest reloadSources([bool pause = false]) { |
228 return (Isolate isolate) async { | 226 return (Isolate isolate) async { |
229 Map<String, dynamic> params = <String, dynamic>{ }; | 227 Map<String, dynamic> params = <String, dynamic>{}; |
230 if (pause == true) { | 228 if (pause == true) { |
231 params['pause'] = pause; | 229 params['pause'] = pause; |
232 } | 230 } |
233 return isolate.invokeRpc('reloadSources', params); | 231 return isolate.invokeRpc('reloadSources', params); |
234 }; | 232 }; |
235 } | 233 } |
236 | 234 |
237 // Currying is your friend. | 235 // Currying is your friend. |
238 IsolateTest setBreakpointAtLine(int line) { | 236 IsolateTest setBreakpointAtLine(int line) { |
239 return (Isolate isolate) async { | 237 return (Isolate isolate) async { |
240 print("Setting breakpoint for line $line"); | 238 print("Setting breakpoint for line $line"); |
241 Library lib = await isolate.rootLibrary.load(); | 239 Library lib = await isolate.rootLibrary.load(); |
242 Script script = lib.scripts.single; | 240 Script script = lib.scripts.single; |
243 | 241 |
244 Breakpoint bpt = await isolate.addBreakpoint(script, line); | 242 Breakpoint bpt = await isolate.addBreakpoint(script, line); |
(...skipping 27 matching lines...) Expand all Loading... |
272 for (Frame f in stack['frames']) { | 270 for (Frame f in stack['frames']) { |
273 sb.write(" $f [${await f.location.getLine()}]\n"); | 271 sb.write(" $f [${await f.location.getLine()}]\n"); |
274 } | 272 } |
275 throw sb.toString(); | 273 throw sb.toString(); |
276 } else { | 274 } else { |
277 print('Program is stopped at line: $line'); | 275 print('Program is stopped at line: $line'); |
278 } | 276 } |
279 }; | 277 }; |
280 } | 278 } |
281 | 279 |
282 | 280 IsolateTest stoppedInFunction(String functionName, |
283 IsolateTest stoppedInFunction(String functionName, {bool contains: false}) { | 281 {bool contains: false, bool includeOwner: false}) { |
284 return (Isolate isolate) async { | 282 return (Isolate isolate) async { |
285 print("Checking we are in function: $functionName"); | 283 print("Checking we are in function: $functionName"); |
286 | 284 |
287 ServiceMap stack = await isolate.getStack(); | 285 ServiceMap stack = await isolate.getStack(); |
288 expect(stack.type, equals('Stack')); | 286 expect(stack.type, equals('Stack')); |
289 | 287 |
290 List<Frame> frames = stack['frames']; | 288 List<Frame> frames = stack['frames']; |
291 expect(frames.length, greaterThanOrEqualTo(1)); | 289 expect(frames.length, greaterThanOrEqualTo(1)); |
292 | 290 |
293 Frame topFrame = stack['frames'][0]; | 291 Frame topFrame = stack['frames'][0]; |
294 ServiceFunction function = await topFrame.function.load(); | 292 ServiceFunction function = await topFrame.function.load(); |
| 293 String name = function.name; |
| 294 if (includeOwner) { |
| 295 ServiceFunction owner = |
| 296 await (function.dartOwner as ServiceObject).load(); |
| 297 name = '${owner.name}.$name'; |
| 298 } |
295 final bool matches = | 299 final bool matches = |
296 contains ? function.name.contains(functionName) : | 300 contains ? name.contains(functionName) : name == functionName; |
297 function.name == functionName; | |
298 if (!matches) { | 301 if (!matches) { |
299 StringBuffer sb = new StringBuffer(); | 302 StringBuffer sb = new StringBuffer(); |
300 sb.write("Expected to be in function $functionName but " | 303 sb.write("Expected to be in function $functionName but " |
301 "actually in function ${function.name}"); | 304 "actually in function $name"); |
302 sb.write("\nFull stack trace:\n"); | 305 sb.write("\nFull stack trace:\n"); |
303 for (Frame f in stack['frames']) { | 306 for (Frame f in stack['frames']) { |
304 await f.function.load(); | 307 await f.function.load(); |
305 await (f.function.dartOwner as ServiceObject).load(); | 308 await (f.function.dartOwner as ServiceObject).load(); |
306 String name = f.function.name; | 309 String name = f.function.name; |
307 String ownerName = (f.function.dartOwner as ServiceObject).name; | 310 String ownerName = (f.function.dartOwner as ServiceObject).name; |
308 sb.write(" $f [$name] [$ownerName]\n"); | 311 sb.write(" $f [$name] [$ownerName]\n"); |
309 } | 312 } |
310 throw sb.toString(); | 313 throw sb.toString(); |
311 } else { | 314 } else { |
312 print('Program is stopped in function: $functionName'); | 315 print('Program is stopped in function: $functionName'); |
313 } | 316 } |
314 }; | 317 }; |
315 } | 318 } |
316 | 319 |
317 | |
318 Future<Isolate> resumeIsolate(Isolate isolate) { | 320 Future<Isolate> resumeIsolate(Isolate isolate) { |
319 Completer completer = new Completer(); | 321 Completer completer = new Completer(); |
320 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { | 322 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { |
321 var subscription; | 323 var subscription; |
322 subscription = stream.listen((ServiceEvent event) { | 324 subscription = stream.listen((ServiceEvent event) { |
323 if (event.kind == ServiceEvent.kResume) { | 325 if (event.kind == ServiceEvent.kResume) { |
324 subscription.cancel(); | 326 subscription.cancel(); |
325 completer.complete(); | 327 completer.complete(); |
326 } | 328 } |
327 }); | 329 }); |
328 }); | 330 }); |
329 isolate.resume(); | 331 isolate.resume(); |
330 return completer.future; | 332 return completer.future; |
331 } | 333 } |
332 | 334 |
333 | |
334 Future resumeAndAwaitEvent(Isolate isolate, stream, onEvent) async { | 335 Future resumeAndAwaitEvent(Isolate isolate, stream, onEvent) async { |
335 Completer completer = new Completer(); | 336 Completer completer = new Completer(); |
336 var sub; | 337 var sub; |
337 sub = await isolate.vm.listenEventStream( | 338 sub = await isolate.vm.listenEventStream(stream, (ServiceEvent event) { |
338 stream, | 339 var r = onEvent(event); |
339 (ServiceEvent event) { | 340 if (r is! Future) { |
340 var r = onEvent(event); | 341 r = new Future.value(r); |
341 if (r is! Future) { | 342 } |
342 r = new Future.value(r); | 343 r.then((x) => sub.cancel().then((_) { |
343 } | 344 completer.complete(); |
344 r.then((x) => sub.cancel().then((_) { | 345 })); |
345 completer.complete(); | 346 }); |
346 })); | |
347 }); | |
348 await isolate.resume(); | 347 await isolate.resume(); |
349 return completer.future; | 348 return completer.future; |
350 } | 349 } |
351 | 350 |
352 IsolateTest resumeIsolateAndAwaitEvent(stream, onEvent) { | 351 IsolateTest resumeIsolateAndAwaitEvent(stream, onEvent) { |
353 return (Isolate isolate) async => | 352 return (Isolate isolate) async => |
354 resumeAndAwaitEvent(isolate, stream, onEvent); | 353 resumeAndAwaitEvent(isolate, stream, onEvent); |
355 } | 354 } |
356 | 355 |
357 | |
358 Future<Isolate> stepOver(Isolate isolate) async { | 356 Future<Isolate> stepOver(Isolate isolate) async { |
359 await isolate.stepOver(); | 357 await isolate.stepOver(); |
360 return hasStoppedAtBreakpoint(isolate); | 358 return hasStoppedAtBreakpoint(isolate); |
361 } | 359 } |
362 | 360 |
363 Future<Isolate> stepInto(Isolate isolate) async { | 361 Future<Isolate> stepInto(Isolate isolate) async { |
364 await isolate.stepInto(); | 362 await isolate.stepInto(); |
365 return hasStoppedAtBreakpoint(isolate); | 363 return hasStoppedAtBreakpoint(isolate); |
366 } | 364 } |
367 | 365 |
368 Future<Isolate> stepOut(Isolate isolate) async { | 366 Future<Isolate> stepOut(Isolate isolate) async { |
369 await isolate.stepOut(); | 367 await isolate.stepOut(); |
370 return hasStoppedAtBreakpoint(isolate); | 368 return hasStoppedAtBreakpoint(isolate); |
371 } | 369 } |
372 | 370 |
373 | |
374 Future isolateIsRunning(Isolate isolate) async { | 371 Future isolateIsRunning(Isolate isolate) async { |
375 await isolate.reload(); | 372 await isolate.reload(); |
376 expect(isolate.running, true); | 373 expect(isolate.running, true); |
377 } | 374 } |
378 | 375 |
379 Future<Class> getClassFromRootLib(Isolate isolate, String className) async { | 376 Future<Class> getClassFromRootLib(Isolate isolate, String className) async { |
380 Library rootLib = await isolate.rootLibrary.load(); | 377 Library rootLib = await isolate.rootLibrary.load(); |
381 for (var i = 0; i < rootLib.classes.length; i++) { | 378 for (var i = 0; i < rootLib.classes.length; i++) { |
382 Class cls = rootLib.classes[i]; | 379 Class cls = rootLib.classes[i]; |
383 if (cls.name == className) { | 380 if (cls.name == className) { |
384 return cls; | 381 return cls; |
385 } | 382 } |
386 } | 383 } |
387 return null; | 384 return null; |
388 } | 385 } |
389 | 386 |
390 | 387 Future<Instance> rootLibraryFieldValue( |
391 Future<Instance> rootLibraryFieldValue(Isolate isolate, | 388 Isolate isolate, String fieldName) async { |
392 String fieldName) async { | |
393 Library rootLib = await isolate.rootLibrary.load(); | 389 Library rootLib = await isolate.rootLibrary.load(); |
394 Field field = rootLib.variables.singleWhere((v) => v.name == fieldName); | 390 Field field = rootLib.variables.singleWhere((v) => v.name == fieldName); |
395 await field.load(); | 391 await field.load(); |
396 Instance value = field.staticValue; | 392 Instance value = field.staticValue; |
397 await value.load(); | 393 await value.load(); |
398 return value; | 394 return value; |
399 } | 395 } |
400 | 396 |
401 IsolateTest runStepThroughProgramRecordingStops(List<String> recordStops) { | 397 IsolateTest runStepThroughProgramRecordingStops(List<String> recordStops) { |
402 return (Isolate isolate) async { | 398 return (Isolate isolate) async { |
(...skipping 15 matching lines...) Expand all Loading... |
418 // We are at the exit: The test is done. | 414 // We are at the exit: The test is done. |
419 await cancelStreamSubscription(VM.kDebugStream); | 415 await cancelStreamSubscription(VM.kDebugStream); |
420 completer.complete(); | 416 completer.complete(); |
421 } | 417 } |
422 }); | 418 }); |
423 isolate.resume(); | 419 isolate.resume(); |
424 return completer.future; | 420 return completer.future; |
425 }; | 421 }; |
426 } | 422 } |
427 | 423 |
| 424 IsolateTest runStepIntoThroughProgramRecordingStops(List<String> recordStops) { |
| 425 return (Isolate isolate) async { |
| 426 Completer completer = new Completer(); |
| 427 |
| 428 await subscribeToStream(isolate.vm, VM.kDebugStream, |
| 429 (ServiceEvent event) async { |
| 430 if (event.kind == ServiceEvent.kPauseBreakpoint) { |
| 431 await isolate.reload(); |
| 432 // We are paused: Step into further. |
| 433 Frame frame = isolate.topFrame; |
| 434 recordStops.add(await frame.location.toUserString()); |
| 435 isolate.stepInto(); |
| 436 } else if (event.kind == ServiceEvent.kPauseExit) { |
| 437 // We are at the exit: The test is done. |
| 438 await cancelStreamSubscription(VM.kDebugStream); |
| 439 completer.complete(); |
| 440 } |
| 441 }); |
| 442 isolate.resume(); |
| 443 return completer.future; |
| 444 }; |
| 445 } |
| 446 |
428 IsolateTest checkRecordedStops( | 447 IsolateTest checkRecordedStops( |
429 List<String> recordStops, List<String> expectedStops) { | 448 List<String> recordStops, List<String> expectedStops, |
| 449 {bool removeDuplicates = false, |
| 450 bool debugPrint = false, |
| 451 String debugPrintFile, |
| 452 int debugPrintLine}) { |
430 return (Isolate isolate) async { | 453 return (Isolate isolate) async { |
| 454 if (debugPrint) { |
| 455 for (int i = 0; i < recordStops.length; i++) { |
| 456 String line = recordStops[i]; |
| 457 String output = line; |
| 458 int firstColon = line.indexOf(":"); |
| 459 int lastColon = line.lastIndexOf(":"); |
| 460 if (debugPrintFile != null && |
| 461 debugPrintLine != null && |
| 462 firstColon > 0 && |
| 463 lastColon > 0) { |
| 464 int lineNumber = int.parse(line.substring(firstColon + 1, lastColon)); |
| 465 int relativeLineNumber = lineNumber - debugPrintLine; |
| 466 var columnNumber = line.substring(lastColon + 1); |
| 467 var file = line.substring(0, firstColon); |
| 468 if (file == debugPrintFile) { |
| 469 output = '\$file:\${LINE+$relativeLineNumber}:$columnNumber'; |
| 470 } |
| 471 } |
| 472 String comma = i == recordStops.length - 1 ? "" : ","; |
| 473 print('"$output"$comma'); |
| 474 } |
| 475 } |
| 476 if (removeDuplicates) { |
| 477 recordStops = removeAdjacentDuplicates(recordStops); |
| 478 expectedStops = removeAdjacentDuplicates(expectedStops); |
| 479 } |
| 480 |
431 int end = recordStops.length < expectedStops.length | 481 int end = recordStops.length < expectedStops.length |
432 ? recordStops.length | 482 ? recordStops.length |
433 : expectedStops.length; | 483 : expectedStops.length; |
434 for (int i = 0; i < end; ++i) { | 484 for (int i = 0; i < end; ++i) { |
435 expect(recordStops[i], expectedStops[i]); | 485 expect(recordStops[i], expectedStops[i]); |
436 } | 486 } |
437 | 487 |
438 expect(recordStops.length >= expectedStops.length, true, | 488 expect(recordStops.length >= expectedStops.length, true, |
439 reason: "Expects at least ${expectedStops.length} breaks."); | 489 reason: "Expects at least ${expectedStops.length} breaks."); |
440 }; | 490 }; |
441 } | 491 } |
| 492 |
| 493 List<String> removeAdjacentDuplicates(List<String> fromList) { |
| 494 List<String> result = <String>[]; |
| 495 String latestLine; |
| 496 for (String s in fromList) { |
| 497 if (s == latestLine) continue; |
| 498 latestLine = s; |
| 499 result.add(s); |
| 500 } |
| 501 return result; |
| 502 } |
| 503 |
| 504 bool isKernel() { |
| 505 for (String argument in Platform.executableArguments) { |
| 506 if (argument.startsWith("--dfe=")) return true; |
| 507 } |
| 508 return false; |
| 509 } |
| 510 |
| 511 E ifKernel<E>(E then, E otherwise) { |
| 512 if (isKernel()) return then; |
| 513 return otherwise; |
| 514 } |
| 515 |
| 516 void ifKernelExecute(Function kernelFunction, Function nonKernelFunction) { |
| 517 if (isKernel()) { |
| 518 kernelFunction(); |
| 519 } else { |
| 520 nonKernelFunction(); |
| 521 } |
| 522 } |
| 523 |
| 524 void nonKernelExecute(Function nonKernelFunction) { |
| 525 if (!isKernel()) { |
| 526 nonKernelFunction(); |
| 527 } |
| 528 } |
| 529 |
| 530 void kernelExecute(Function kernelFunction) { |
| 531 if (isKernel()) { |
| 532 kernelFunction(); |
| 533 } |
| 534 } |
OLD | NEW |