| // Copyright (c) 2023, 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. |
| |
| import 'dart:async'; |
| import 'dart:developer'; |
| // ignore: library_prefixes |
| import 'dart:isolate' as I; |
| |
| import 'package:vm_service/vm_service.dart'; |
| |
| import 'common/service_test_common.dart'; |
| import 'common/test_helper.dart'; |
| |
| // AUTOGENERATED START |
| // |
| // Update these constants by running: |
| // |
| // dart pkg/vm_service/test/update_line_numbers.dart <test.dart> |
| // |
| const LINE_A = 29; |
| const LINE_B = 37; |
| const LINE_C = 39; |
| const LINE_D = 41; |
| // AUTOGENERATED END |
| |
| void isolate(I.SendPort port) { |
| final receive = I.RawReceivePort((_) { |
| debugger(); // LINE_A |
| throw Exception(); |
| }); |
| port.send(receive.sendPort); |
| } |
| |
| void test() { |
| final receive = I.RawReceivePort((port) { |
| debugger(); // LINE_B |
| port.send(null); |
| debugger(); // LINE_C |
| port.send(null); |
| debugger(); // LINE_D |
| }); |
| I.Isolate.spawn(isolate, receive.sendPort); |
| } |
| |
| late final IsolateRef firstIsolate; |
| late final IsolateRef secondIsolate; |
| |
| final tests = <IsolateTest>[ |
| hasPausedAtStart, |
| (VmService service, IsolateRef isolateRef) async { |
| firstIsolate = isolateRef; |
| |
| // Capture the second isolate when it spawns. |
| final completer = Completer<void>(); |
| late final StreamSubscription sub; |
| sub = service.onDebugEvent.listen((event) async { |
| if (event.kind == EventKind.kPauseStart) { |
| secondIsolate = event.isolate!; |
| await sub.cancel(); |
| await service.streamCancel(EventStreams.kDebug); |
| completer.complete(); |
| } |
| }); |
| await service.streamListen(EventStreams.kDebug); |
| |
| // Resume and wait for the second isolate to spawn. |
| await resumeIsolate(service, firstIsolate); |
| await completer.future; |
| |
| // Resume the second isolate. |
| await resumeIsolate(service, secondIsolate); |
| |
| // First isolate should pause at LINE_B. |
| await hasStoppedAtBreakpoint(service, firstIsolate); |
| await stoppedAtLine(LINE_B)(service, firstIsolate); |
| await resumeIsolate(service, firstIsolate); |
| |
| // First isolate should pause at LINE_C and second isolate should pause |
| // at LINE_A. |
| // |
| // Note: the ordering of the following four checks doesn't matter as |
| // `hasStoppedAtBreakpoint` and `stoppedAtLine` both handle breakpoint |
| // events received on the debug stream while also checking whether an |
| // isolate already hit the breakpoint. However, interleaving these using |
| // Future.wait() may cause the debug stream to be cancelled after one of |
| // the checks completes and the other check is waiting on an event, causing |
| // the test to hang. |
| await hasStoppedAtBreakpoint(service, firstIsolate); |
| await stoppedAtLine(LINE_C)(service, firstIsolate); |
| await hasStoppedAtBreakpoint(service, secondIsolate); |
| await stoppedAtLine(LINE_A)(service, secondIsolate); |
| |
| // Resume the second isolate. |
| await resumeIsolate(service, secondIsolate); |
| |
| // The second isolate should exit due to an exception. |
| await hasStoppedAtExit(service, secondIsolate); |
| |
| // Resume the first isolate. |
| await resumeIsolate(service, firstIsolate); |
| |
| // The first isolate should pause at LINE_D. |
| await hasStoppedAtBreakpoint(service, firstIsolate); |
| await stoppedAtLine(LINE_D)(service, firstIsolate); |
| }, |
| ]; |
| |
| void main([args = const <String>[]]) => runIsolateTests( |
| args, |
| tests, |
| 'issue_30555_test.dart', |
| pauseOnStart: true, |
| pauseOnExit: true, |
| testeeConcurrent: test, |
| ); |