[ package:vm_service ] Migrate Observatory tests (i* -> l*)

Change-Id: I3c60484af4d8fff1b5742bbe57f2c318d3cb0e43
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/333180
Reviewed-by: Derek Xu <derekx@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 92e8c62..44f31c9 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -17,6 +17,7 @@
   async: any
   collection: any
   lints: any
+  logging: any
   markdown: any
   path: any
   pub_semver: any
diff --git a/pkg/vm_service/test/common/service_test_common.dart b/pkg/vm_service/test/common/service_test_common.dart
index c4a675b..0e8c265 100644
--- a/pkg/vm_service/test/common/service_test_common.dart
+++ b/pkg/vm_service/test/common/service_test_common.dart
@@ -527,3 +527,38 @@
     expect(result.kind!, kind);
   }
 }
+
+IsolateTest hasLocalVarInTopStackFrame(String varName) {
+  return (VmService service, IsolateRef isolateRef) async {
+    print("Checking we have variable '$varName' in the top frame");
+
+    final isolateId = isolateRef.id!;
+    // Make sure that the isolate has stopped.
+    final isolate = await service.getIsolate(isolateId);
+    expect(isolate.pauseEvent, isNotNull);
+    expect(isolate.pauseEvent!.kind, isNot(EventKind.kResume));
+
+    final stack = await service.getStack(isolateId);
+    final frames = stack.frames!;
+    expect(frames.length, greaterThanOrEqualTo(1));
+
+    final top = frames[0];
+    final vars = top.vars!;
+    for (final variable in vars) {
+      if (variable.name == varName) {
+        return;
+      }
+    }
+    final sb = StringBuffer();
+    sb.write('Expected to find $varName in top awaiter stack frame, found ');
+    if (vars.isEmpty) {
+      sb.writeln('no variables');
+    } else {
+      sb.writeln('these instead:');
+      for (var variable in vars) {
+        sb.writeln('\t${variable.name}');
+      }
+    }
+    throw sb.toString();
+  };
+}
diff --git a/pkg/vm_service/test/implicit_getter_setter_test.dart b/pkg/vm_service/test/implicit_getter_setter_test.dart
new file mode 100644
index 0000000..6c6d8c3
--- /dev/null
+++ b/pkg/vm_service/test/implicit_getter_setter_test.dart
@@ -0,0 +1,122 @@
+// 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 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/test_helper.dart';
+
+class A {
+  double field = 0.0;
+}
+
+void script() {
+  for (int i = 0; i < 10; i++) {
+    A();
+  }
+}
+
+Future<void> testGetter(VmService service, IsolateRef isolateRef) async {
+  final isolateId = isolateRef.id!;
+  final isolate = await service.getIsolate(isolateId);
+  final rootLib = await service.getObject(
+    isolateId,
+    isolate.rootLib!.id!,
+  ) as Library;
+  expect(rootLib.classes!.length, 1);
+
+  final classA = await service.getObject(
+    isolateId,
+    rootLib.classes![0].id!,
+  ) as Class;
+  expect(classA.name, 'A');
+  // Find getter.
+  FuncRef? getterFuncRef;
+  for (final function in classA.functions!) {
+    if (function.name == 'field') {
+      getterFuncRef = function;
+      break;
+    }
+  }
+  expect(getterFuncRef, isNotNull);
+
+  final getterFunc = await service.getObject(
+    isolateId,
+    getterFuncRef!.id!,
+  ) as Func;
+  final fieldRef = FieldRef.parse(getterFunc.json!['_field']);
+  expect(fieldRef, isNotNull);
+  final field = await service.getObject(
+    isolateId,
+    fieldRef!.id!,
+  ) as Field;
+  expect(field, isNotNull);
+  expect(field.name, 'field');
+
+  final classDoubleRef = Class.parse(field.json!['_guardClass']);
+  expect(classDoubleRef, isNotNull);
+  final classDouble = await service.getObject(
+    isolateId,
+    classDoubleRef!.id!,
+  ) as Class;
+  expect(classDouble.name, '_Double');
+}
+
+Future<void> testSetter(VmService service, IsolateRef isolateRef) async {
+  final isolateId = isolateRef.id!;
+  final isolate = await service.getIsolate(isolateId);
+  final rootLib = await service.getObject(
+    isolateId,
+    isolate.rootLib!.id!,
+  ) as Library;
+  expect(rootLib.classes!.length, 1);
+
+  final classA = await service.getObject(
+    isolateId,
+    rootLib.classes![0].id!,
+  ) as Class;
+  expect(classA.name, 'A');
+  // Find setter.
+  FuncRef? setterFuncRef;
+  for (final function in classA.functions!) {
+    if (function.name == 'field=') {
+      setterFuncRef = function;
+      break;
+    }
+  }
+  expect(setterFuncRef, isNotNull);
+  final setterFunc = await service.getObject(
+    isolateId,
+    setterFuncRef!.id!,
+  ) as Func;
+
+  final fieldRef = FieldRef.parse(setterFunc.json!['_field']);
+  final field = await service.getObject(
+    isolateId,
+    fieldRef!.id!,
+  ) as Field;
+  expect(field, isNotNull);
+  expect(field.name, 'field');
+
+  final classDoubleRef = Class.parse(field.json!['_guardClass']);
+  expect(classDoubleRef, isNotNull);
+  final classDouble = await service.getObject(
+    isolateId,
+    classDoubleRef!.id!,
+  ) as Class;
+  expect(classDouble.name, '_Double');
+}
+
+final tests = <IsolateTest>[
+  testGetter,
+  testSetter,
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'implicit_getter_setter_test.dart',
+      testeeBefore: script,
+    );
diff --git a/pkg/vm_service/test/inbound_references_test.dart b/pkg/vm_service/test/inbound_references_test.dart
new file mode 100644
index 0000000..c4239bc
--- /dev/null
+++ b/pkg/vm_service/test/inbound_references_test.dart
@@ -0,0 +1,77 @@
+// 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 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/test_helper.dart';
+
+@pragma("vm:entry-point") // Prevent obfuscation
+class Node {
+  // Make sure this field is not removed by the tree shaker.
+  @pragma("vm:entry-point") // Prevent obfuscation
+  var edge;
+}
+
+class Edge {}
+
+@pragma("vm:entry-point") // Prevent obfuscation
+var n, e, array;
+
+void script() {
+  n = Node();
+  e = Edge();
+  n.edge = e;
+  array = List<dynamic>.filled(2, null);
+  array[0] = n;
+  array[1] = e;
+}
+
+final tests = <IsolateTest>[
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    final isolate = await service.getIsolate(isolateId);
+    final rootLib = await service.getObject(
+      isolateId,
+      isolate.rootLib!.id!,
+    ) as Library;
+    final fieldRef = rootLib.variables!.where((v) => v.name == 'e').single;
+    final field = await service.getObject(isolateId, fieldRef.id!) as Field;
+    final e = field.staticValue! as InstanceRef;
+    final response = await service.getInboundReferences(
+      isolateId,
+      e.id!,
+      100,
+    );
+    final references = response.references!;
+
+    void hasReferenceSuchThat(bool Function(InboundReference) predicate) {
+      expect(references.any(predicate), isTrue);
+    }
+
+    // Assert inst is referenced by at least n, array, and the top-level
+    // field e.
+    hasReferenceSuchThat((r) =>
+        r.parentField != null &&
+        r.parentField!.name == 'edge' &&
+        r.source is InstanceRef &&
+        (r.source as InstanceRef).classRef!.name == 'Node');
+    hasReferenceSuchThat(
+      (r) =>
+          r.parentListIndex == 1 &&
+          r.source is InstanceRef &&
+          (r.source as InstanceRef).kind == InstanceKind.kList,
+    );
+    hasReferenceSuchThat(
+      (r) => r.source is FieldRef && (r.source as FieldRef).name == 'e',
+    );
+  }
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'inbound_references_test.dart',
+      testeeBefore: script,
+    );
diff --git a/pkg/vm_service/test/instance_field_order_rpc_test.dart b/pkg/vm_service/test/instance_field_order_rpc_test.dart
new file mode 100644
index 0000000..b10ce24
--- /dev/null
+++ b/pkg/vm_service/test/instance_field_order_rpc_test.dart
@@ -0,0 +1,64 @@
+// 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 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/test_helper.dart';
+
+class Super {
+  // Make sure these fields are not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  final z = 1;
+  @pragma("vm:entry-point")
+  final y = 2;
+}
+
+class Sub extends Super {
+  @pragma("vm:entry-point")
+  final y = 3;
+  @pragma("vm:entry-point")
+  final x = 4;
+}
+
+@pragma("vm:entry-point")
+Sub getSub() => Sub();
+
+final tests = <IsolateTest>[
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    final isolate = await service.getIsolate(isolateId);
+    // Call eval to get a Dart list.
+    final evalResult = await service.invoke(
+      isolateId,
+      isolate.rootLib!.id!,
+      'getSub',
+      [],
+    ) as InstanceRef;
+    final result = await service.getObject(
+      isolateId,
+      evalResult.id!,
+    ) as Instance;
+
+    expect(result.kind, InstanceKind.kPlainInstance);
+    expect(result.classRef!.name, 'Sub');
+    expect(result.size, isPositive);
+    final fields = result.fields!;
+    expect(fields.length, 4);
+    expect(fields[0].decl!.name, 'z');
+    expect(fields[0].value.valueAsString, '1');
+    expect(fields[1].decl!.name, 'y');
+    expect(fields[1].value.valueAsString, '2');
+    expect(fields[2].decl!.name, 'y');
+    expect(fields[2].value.valueAsString, '3');
+    expect(fields[3].decl!.name, 'x');
+    expect(fields[3].value.valueAsString, '4');
+  },
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'instance_field_order_rpc_test.dart',
+    );
diff --git a/pkg/vm_service/test/invoke_skip_breakpoint.dart b/pkg/vm_service/test/invoke_skip_breakpoint.dart
new file mode 100644
index 0000000..1c8c11b
--- /dev/null
+++ b/pkg/vm_service/test/invoke_skip_breakpoint.dart
@@ -0,0 +1,56 @@
+// 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:developer';
+
+import 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const int LINE_A = 17;
+const int LINE_B = LINE_A + 5;
+
+String bar() {
+  print('bar'); // LINE_A
+  return 'bar';
+}
+
+void testMain() {
+  debugger(); // LINE_B
+  bar();
+  print('Done');
+}
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  setBreakpointAtLine(LINE_A),
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    final isolate = await service.getIsolate(isolateId);
+    final result = await service.invoke(
+      isolateId,
+      isolate.rootLib!.id!,
+      'bar',
+      [],
+      disableBreakpoints: true,
+    ) as InstanceRef;
+    expect(result.valueAsString, 'bar');
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'invoke_skip_breakpoint.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/test/isolate_lifecycle_test.dart b/pkg/vm_service/test/isolate_lifecycle_test.dart
new file mode 100644
index 0000000..eb955b1
--- /dev/null
+++ b/pkg/vm_service/test/isolate_lifecycle_test.dart
@@ -0,0 +1,150 @@
+// 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';
+import 'dart:isolate' as I;
+
+import 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const LINE_A = 24;
+
+final spawnCount = 4;
+final resumeCount = spawnCount ~/ 2;
+final isolates = [];
+
+void spawnEntry(int i) {}
+
+Future<void> during() async {
+  debugger(); // LINE_A
+  // Spawn spawnCount long lived isolates.
+  for (int i = 0; i < spawnCount; i++) {
+    final isolate = await I.Isolate.spawn(spawnEntry, i);
+    isolates.add(isolate);
+  }
+  print('spawned all isolates');
+}
+
+Future<int> numPaused(VmService service) async {
+  final vm = await service.getVM();
+  int paused = 0;
+  for (final isolateRef in vm.isolates!) {
+    final isolate = await service.getIsolate(isolateRef.id!);
+    if (isolate.pauseEvent != null &&
+        isolate.pauseEvent!.kind == EventKind.kPauseExit) {
+      paused++;
+    }
+  }
+  return paused;
+}
+
+final tests = <VMTest>[
+  (VmService service) async {
+    final vm = await service.getVM();
+    final isolates = vm.isolates!;
+    expect(isolates.length, 1);
+    await hasStoppedAtBreakpoint(service, isolates[0]);
+    await stoppedAtLine(LINE_A)(service, isolates[0]);
+  },
+  (VmService service) async {
+    int startCount = 0;
+    int runnableCount = 0;
+
+    final completer = Completer<void>();
+    late final StreamSubscription sub;
+    sub = service.onIsolateEvent.listen((event) async {
+      if (event.kind == EventKind.kIsolateStart) {
+        startCount++;
+      }
+      if (event.kind == EventKind.kIsolateRunnable) {
+        runnableCount++;
+      }
+      if (runnableCount == spawnCount) {
+        sub.cancel();
+        await service.streamCancel(EventStreams.kIsolate);
+        completer.complete();
+      }
+    });
+    await service.streamListen(EventStreams.kIsolate);
+
+    VM vm = await service.getVM();
+    expect(vm.isolates!.length, 1);
+
+    // Resume and wait for the isolates to spawn.
+    await service.resume(vm.isolates![0].id!);
+    await completer.future;
+    expect(startCount, spawnCount);
+    expect(runnableCount, spawnCount);
+    vm = await service.getVM();
+    expect(vm.isolates!.length, spawnCount + 1);
+  },
+  (VmService service) async {
+    final completer = Completer<void>();
+    if (await numPaused(service) < (spawnCount + 1)) {
+      late final StreamSubscription sub;
+      sub = service.onDebugEvent.listen((event) async {
+        if (event.kind == EventKind.kPauseExit) {
+          if (await numPaused(service) == (spawnCount + 1)) {
+            sub.cancel();
+            await service.streamCancel(EventStreams.kDebug);
+            completer.complete();
+          }
+        }
+      });
+      await service.streamListen(EventStreams.kDebug);
+      await completer.future;
+    }
+    expect(await numPaused(service), spawnCount + 1);
+  },
+  (VmService service) async {
+    int resumedReceived = 0;
+    final completer = Completer<void>();
+    late final StreamSubscription sub;
+    sub = service.onIsolateEvent.listen((event) async {
+      if (event.kind == EventKind.kIsolateExit) {
+        resumedReceived++;
+        if (resumedReceived >= resumeCount) {
+          sub.cancel();
+          await service.streamCancel(EventStreams.kIsolate);
+          completer.complete();
+        }
+      }
+    });
+    await service.streamListen(EventStreams.kIsolate);
+
+    // Resume a subset of the isolates.
+    int resumesIssued = 0;
+
+    final vm = await service.getVM();
+    final isolateList = vm.isolates!;
+    for (final isolate in isolateList) {
+      if (isolate.name!.endsWith('main')) {
+        continue;
+      }
+      try {
+        resumesIssued++;
+        await service.resume(isolate.id!);
+      } catch (_) {}
+      if (resumesIssued == resumeCount) {
+        break;
+      }
+    }
+    await completer.future;
+  },
+  (VmService service) async {
+    expect(await numPaused(service), spawnCount + 1 - resumeCount);
+  },
+];
+
+void main([args = const <String>[]]) => runVMTests(
+      args,
+      tests,
+      'isolate_lifecycle_test.dart',
+      testeeConcurrent: during,
+      pause_on_exit: true,
+    );
diff --git a/pkg/vm_service/test/issue_25465_test.dart b/pkg/vm_service/test/issue_25465_test.dart
new file mode 100644
index 0000000..f88eb8b
--- /dev/null
+++ b/pkg/vm_service/test/issue_25465_test.dart
@@ -0,0 +1,60 @@
+// 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 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/test_helper.dart';
+import 'common/service_test_common.dart';
+
+const int LINE_A = 15;
+const int LINE_B = LINE_A + 1;
+
+testMain() {
+  final foo; // LINE_A
+  foo = 42; // LINE_B
+  print(foo);
+}
+
+final tests = <IsolateTest>[
+  hasPausedAtStart,
+  // Add breakpoints.
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    final isolate = await service.getIsolate(isolateId);
+    final rootLib = await service.getObject(
+      isolateId,
+      isolate.rootLib!.id!,
+    ) as Library;
+    final scriptId = rootLib.scripts![0].id!;
+
+    final bpt1 = await service.addBreakpoint(isolateId, scriptId, LINE_A);
+    final bpt2 = await service.addBreakpoint(isolateId, scriptId, LINE_B);
+    expect(bpt1.location!.line, LINE_A);
+    expect(bpt2.location!.line, LINE_B);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    final isolate = await service.getIsolate(isolateId);
+    final breakpoints = isolate.breakpoints!;
+    expect(breakpoints.length, 2);
+    for (final bpt in isolate.breakpoints!) {
+      await service.removeBreakpoint(isolateId, bpt.id!);
+    }
+  },
+  stepOver,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'issue_25465_test.dart',
+      testeeConcurrent: testMain,
+      pause_on_start: true,
+    );
diff --git a/pkg/vm_service/test/issue_27238_test.dart b/pkg/vm_service/test/issue_27238_test.dart
new file mode 100644
index 0000000..ceb7b3a
--- /dev/null
+++ b/pkg/vm_service/test/issue_27238_test.dart
@@ -0,0 +1,56 @@
+// 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.
+// VMOptions=--verbose_debug
+
+import 'dart:async';
+import 'dart:developer';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const int LINE_0 = 20;
+const int LINE_A = LINE_0 + 1;
+const int LINE_B = LINE_A + 3;
+const int LINE_C = LINE_B + 1;
+const int LINE_D = LINE_C + 2;
+const int LINE_E = LINE_D + 1;
+
+testMain() async {
+  debugger(); // LINE_0.
+  final future1 = Future.value(); // LINE_A.
+  final future2 = Future.value();
+
+  await future1; // LINE_B.
+  await future2; // LINE_C.
+
+  print('foo1'); // LINE_D.
+  print('foo2'); // LINE_E.
+}
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_0),
+  stepOver,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  smartNext,
+  hasStoppedAtBreakpoint,
+  smartNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  smartNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  smartNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D),
+  resumeIsolate,
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'issue_27238_test.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/test/issue_27287_test.dart b/pkg/vm_service/test/issue_27287_test.dart
new file mode 100644
index 0000000..4f3cd01
--- /dev/null
+++ b/pkg/vm_service/test/issue_27287_test.dart
@@ -0,0 +1,41 @@
+// 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.
+// VMOptions=--verbose_debug
+
+import 'dart:developer';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const int LINE_0 = 18;
+const int LINE_A = LINE_0 + 1;
+const int LINE_B = LINE_A + 1;
+
+late int libVariable;
+
+void testMain() {
+  debugger(); // LINE_0
+  print('Before'); // LINE_A
+  libVariable = 0; // LINE_B
+  print('and after');
+}
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_0),
+  stepOver,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  stepOver,
+  // Check that debugger stops at assignment to top-level variable.
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'issue_27287_test.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/test/issue_30555_test.dart b/pkg/vm_service/test/issue_30555_test.dart
new file mode 100644
index 0000000..86f44a4
--- /dev/null
+++ b/pkg/vm_service/test/issue_30555_test.dart
@@ -0,0 +1,104 @@
+// 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';
+import 'dart:isolate' as I;
+
+import 'package:vm_service/vm_service.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const LINE_A = 21;
+const LINE_B = LINE_A + 8;
+const LINE_C = LINE_B + 2;
+const LINE_D = LINE_C + 2;
+
+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.
+    await Future.wait([
+      hasStoppedAtBreakpoint(service, firstIsolate).then(
+        (_) => stoppedAtLine(LINE_C)(service, firstIsolate),
+      ),
+      hasStoppedAtBreakpoint(service, secondIsolate).then(
+        (_) => 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',
+      pause_on_start: true,
+      pause_on_exit: true,
+      testeeConcurrent: test,
+    );
diff --git a/pkg/vm_service/test/kill_paused_test.dart b/pkg/vm_service/test/kill_paused_test.dart
new file mode 100644
index 0000000..715ae44
--- /dev/null
+++ b/pkg/vm_service/test/kill_paused_test.dart
@@ -0,0 +1,36 @@
+// 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:developer';
+
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+import 'common/service_test_common.dart';
+
+const LINE_A = 15;
+
+void testMain() {
+  debugger(); // LINE_A
+  print('1');
+  while (true) {}
+}
+
+final tests = <IsolateTest>[
+  // Stopped at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  // Kill the app.
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    await service.kill(isolateId);
+  }
+];
+
+void main([args = const <String>[]]) async => runIsolateTests(
+      args,
+      tests,
+      'kill_paused_test.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/test/kill_running_test.dart b/pkg/vm_service/test/kill_running_test.dart
new file mode 100644
index 0000000..3e89da4
--- /dev/null
+++ b/pkg/vm_service/test/kill_running_test.dart
@@ -0,0 +1,27 @@
+// 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 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+void testMain() {
+  print('1');
+  while (true) {}
+}
+
+final tests = <IsolateTest>[
+  // Kill the app.
+  (VmService service, IsolateRef isolateRef) async {
+    final isolateId = isolateRef.id!;
+    await service.kill(isolateId);
+  }
+];
+
+void main([args = const <String>[]]) async => runIsolateTests(
+      args,
+      tests,
+      'kill_running_test.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/test/local_variable_declaration_test.dart b/pkg/vm_service/test/local_variable_declaration_test.dart
new file mode 100644
index 0000000..2d4eaa9
--- /dev/null
+++ b/pkg/vm_service/test/local_variable_declaration_test.dart
@@ -0,0 +1,177 @@
+// 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.
+// VMOptions=--verbose_debug
+
+import 'dart:developer';
+
+import 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const LINE_A = 19;
+const LINE_B = LINE_A + 9;
+const LINE_C = LINE_B + 5;
+
+void testParameters(int jjjj, int oooo, [int? hhhh, int? nnnn]) {
+  debugger(); // LINE_A
+}
+
+void testMain() {
+  // ignore: unused_local_variable
+  int? xxx, yyyy, zzzzz;
+  for (int i = 0; i < 1; i++) {
+    // ignore: unused_local_variable
+    final foo = () {};
+    debugger(); // LINE_B
+  }
+  final bar = () {
+    print(xxx);
+    print(yyyy);
+    debugger(); // LINE_C
+  };
+  bar();
+  testParameters(0, 0);
+}
+
+String? getLine(Script script, int line) {
+  final index = line - script.lineOffset! - 1;
+  final lines = script.source!.split('\n');
+  if (lines.length < index) {
+    return null;
+  }
+  return lines[index];
+}
+
+String? getToken(Script script, int tokenPos) {
+  final line = script.getLineNumberFromTokenPos(tokenPos);
+  int? col = script.getColumnNumberFromTokenPos(tokenPos);
+  if ((line == null) || (col == null)) {
+    return null;
+  }
+  // Line and column numbers start at 1 in the VM.
+  --col;
+  final sourceLine = getLine(script, line);
+  if (sourceLine == null) {
+    return null;
+  }
+  final length = guessTokenLength(script, line, col);
+  if (length == null) {
+    return sourceLine.substring(col);
+  }
+  return sourceLine.substring(col, col + length);
+}
+
+bool _isOperatorChar(int c) {
+  switch (c) {
+    case 25: // %
+    case 26: // &
+    case 42: // *
+    case 43: // +
+    case 45: // -:
+    case 47: // /
+    case 60: // <
+    case 61: // =
+    case 62: // >
+    case 94: // ^
+    case 124: // |
+    case 126: // ~
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool _isInitialIdentifierChar(int c) {
+  if (c >= 65 && c <= 90) return true; // Upper
+  if (c >= 97 && c <= 122) return true; // Lower
+  if (c == 95) return true; // Underscore
+  if (c == 36) return true; // Dollar
+  return false;
+}
+
+bool _isIdentifierChar(int c) {
+  if (_isInitialIdentifierChar(c)) return true;
+  return c >= 48 && c <= 57; // Digit
+}
+
+int? guessTokenLength(Script script, int line, int column) {
+  String source = getLine(script, line)!;
+
+  int pos = column;
+  if (pos >= source.length) {
+    return null;
+  }
+
+  final c = source.codeUnitAt(pos);
+  if (c == 123) return 1; // { - Map literal
+
+  if (c == 91) return 1; // [ - List literal, index, index assignment
+
+  if (c == 40) return 1; // ( - Closure call
+
+  if (_isOperatorChar(c)) {
+    while (++pos < source.length && _isOperatorChar(source.codeUnitAt(pos)));
+    return pos - column;
+  }
+
+  if (_isInitialIdentifierChar(c)) {
+    while (++pos < source.length && _isIdentifierChar(source.codeUnitAt(pos)));
+    return pos - column;
+  }
+
+  return null;
+}
+
+Future<void> verifyVariables(VmService service, IsolateRef isolateRef) async {
+  final isolateId = isolateRef.id!;
+  final stack = await service.getStack(isolateId);
+  final frames = stack.frames!;
+  expect(frames.length, greaterThanOrEqualTo(1));
+  // Grab the top frame.
+  final frame = frames.first;
+  // Grab the script.
+  final script = await service.getObject(
+    isolateId,
+    frame.location!.script!.id!,
+  ) as Script;
+
+  // Ensure that the token at each declaration position is the name of the
+  // variable.
+  final variables = frame.vars!;
+  for (final variable in variables) {
+    final declarationTokenPos = variable.declarationTokenPos!;
+    final name = variable.name!;
+    final token = getToken(script, declarationTokenPos);
+    // When running from an appjit snapshot, sources aren't available so the returned token will
+    // be null.
+    if (token != null) {
+      expect(name, token);
+    }
+  }
+}
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  verifyVariables,
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  // We have stopped in the anonymous closure assigned to bar. Verify that
+  // variables captured in the context have valid declaration positions.
+  verifyVariables,
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  verifyVariables,
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'local_variable_declaration_test.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/test/local_variable_in_awaiter_async_frame_test.dart b/pkg/vm_service/test/local_variable_in_awaiter_async_frame_test.dart
new file mode 100644
index 0000000..1207d22
--- /dev/null
+++ b/pkg/vm_service/test/local_variable_in_awaiter_async_frame_test.dart
@@ -0,0 +1,44 @@
+// 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:developer';
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const LINE_A = 13;
+const LINE_B = 14;
+
+Future<String> testFunction(String caption) async {
+  await Future.delayed(Duration(milliseconds: 1)); // LINE_A
+  return caption; // LINE_B
+}
+
+Future<void> testMain() async {
+  debugger();
+  final str = await testFunction('The caption');
+  print(str);
+}
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  setBreakpointAtLine(LINE_A),
+  setBreakpointAtLine(LINE_B),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  hasLocalVarInTopStackFrame('caption'),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  hasLocalVarInTopStackFrame('caption'),
+  hasLocalVarInTopStackFrame('caption'),
+  resumeIsolate,
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'local_variable_in_awaiter_async_frame_test.dart',
+      testeeConcurrent: testMain,
+    );
diff --git a/pkg/vm_service/test/logging_test.dart b/pkg/vm_service/test/logging_test.dart
new file mode 100644
index 0000000..5181b20
--- /dev/null
+++ b/pkg/vm_service/test/logging_test.dart
@@ -0,0 +1,65 @@
+// 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:developer';
+
+import 'package:vm_service/vm_service.dart';
+import 'package:test/test.dart';
+import 'package:logging/logging.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const LINE_A = 32;
+const LINE_B = LINE_A + 2;
+
+void init() {
+  Logger.root.level = Level.ALL;
+  Logger.root.onRecord.listen((logRecord) {
+    log(logRecord.message,
+        time: logRecord.time,
+        sequenceNumber: logRecord.sequenceNumber,
+        level: logRecord.level.value,
+        name: logRecord.loggerName,
+        zone: null,
+        error: logRecord.error,
+        stackTrace: logRecord.stackTrace);
+  });
+}
+
+void run() {
+  debugger();
+  Logger.root.fine('Hey Buddy!');
+  debugger();
+  Logger.root.info('YES');
+}
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolateAndAwaitEvent(EventStreams.kLogging, (event) {
+    expect(event.kind, EventKind.kLogging);
+    expect(event.logRecord!.sequenceNumber, 0);
+    expect(event.logRecord!.message!.valueAsString, 'Hey Buddy!');
+    expect(event.logRecord!.level, Level.FINE.value);
+    expect(event.logRecord!.time, isNotNull);
+  }),
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolateAndAwaitEvent(EventStreams.kLogging, (event) {
+    expect(event.kind, EventKind.kLogging);
+    expect(event.logRecord!.sequenceNumber, 1);
+    expect(event.logRecord!.message!.valueAsString, 'YES');
+    expect(event.logRecord!.level, Level.INFO.value);
+    expect(event.logRecord!.time, isNotNull);
+  }),
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'logging_test.dart',
+      testeeBefore: init,
+      testeeConcurrent: run,
+    );