[ Service ] Added service tests for enhanced enums

Fixes https://github.com/dart-lang/sdk/issues/47870

TEST=pkg/vm_service/test/enhanced_enum_test.dart

Change-Id: Ifa55c894f331452d1d88131d892d6c153dcbe8cf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/232225
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/vm_service/test/breakpoint_in_enhanced_enums_test.dart b/pkg/vm_service/test/breakpoint_in_enhanced_enums_test.dart
new file mode 100644
index 0000000..ebedeeb
--- /dev/null
+++ b/pkg/vm_service/test/breakpoint_in_enhanced_enums_test.dart
@@ -0,0 +1,111 @@
+// Copyright (c) 2022, 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.
+//
+// SharedOptions=--enable-experiment=enhanced-enums
+
+// ignore_for_file: experiment_not_enabled
+// @dart=2.17
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+const int LINE_A = 24;
+const int LINE_B = LINE_A + 11;
+const int LINE_C = LINE_B + 4;
+const int LINE_D = LINE_C + 4;
+const int LINE_E = LINE_D + 5;
+const int LINE_F = LINE_E + 4;
+const int LINE_G = LINE_F + 5;
+const int LINE_H = LINE_G + 4;
+
+mixin M on Object {
+  int mixedInMethod() {
+    print('mixedInMethod'); // LINE_A
+    return 0;
+  }
+}
+
+enum E with M {
+  e1,
+  e2,
+  e3;
+
+  void instanceMethod() {
+    print('instanceMethod'); // LINE_B
+  }
+
+  static void staticMethod() {
+    print('staticMethod'); // LINE_C
+  }
+
+  int get getter {
+    print('getter'); // LINE_D
+    return 0;
+  }
+
+  set setter(int x) {
+    print('setter'); // LINE_E
+  }
+
+  static int get staticGetter {
+    print('staticGetter'); // LINE_F
+    return 0;
+  }
+
+  static set staticSetter(int x) {
+    print('staticSetter'); // LINE_G
+  }
+
+  String toString() {
+    print('overriden toString'); // LINE_H
+    return '';
+  }
+}
+
+void testMain() {
+  E.staticMethod();
+  E.staticGetter;
+  E.staticSetter = 42;
+  final e = E.e1;
+  e.mixedInMethod();
+  e.instanceMethod();
+  e.getter;
+  e.setter = 42;
+  e.toString();
+}
+
+const lines = <int>[
+  LINE_C,
+  LINE_F,
+  LINE_G,
+  LINE_A,
+  LINE_B,
+  LINE_D,
+  LINE_E,
+  LINE_H,
+];
+
+const fileName = 'breakpoint_in_enhanced_enums_test.dart';
+final expected = <String>[
+  for (final line in lines) '$fileName:$line:5',
+];
+
+final stops = <String>[];
+
+final tests = <IsolateTest>[
+  hasPausedAtStart,
+  for (final line in lines) setBreakpointAtLine(line),
+  resumeProgramRecordingStops(stops, false),
+  checkRecordedStops(stops, expected),
+];
+
+main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      fileName,
+      testeeConcurrent: testMain,
+      pause_on_start: true,
+      pause_on_exit: true,
+      experiments: ['enhanced-enums'],
+    );
diff --git a/pkg/vm_service/test/enhanced_enum_test.dart b/pkg/vm_service/test/enhanced_enum_test.dart
new file mode 100644
index 0000000..633caa5
--- /dev/null
+++ b/pkg/vm_service/test/enhanced_enum_test.dart
@@ -0,0 +1,296 @@
+// Copyright (c) 2022, 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.
+//
+// SharedOptions=--enable-experiment=enhanced-enums
+
+// ignore_for_file: experiment_not_enabled
+// @dart=2.17
+
+import 'dart:developer';
+
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+class I1 {
+  int interfaceMethod1() => 0;
+  int get interfaceGetter1 => 0;
+  set interfaceSetter1(int value) {}
+}
+
+abstract class I2 {
+  int interfaceMethod2();
+  int get interfaceGetter2;
+  set interfaceSetter2(int value);
+}
+
+mixin M on Object {
+  int mixedInMethod() => 42;
+}
+
+enum E with M implements I1, I2 {
+  e1,
+  e2,
+  e3;
+
+  int interfaceMethod1() => 42;
+  int get interfaceGetter1 => 42;
+  set interfaceSetter1(int value) {}
+  int interfaceMethod2() => 42;
+  int get interfaceGetter2 => 42;
+  set interfaceSetter2(int value) {}
+
+  static int staticMethod() => 42;
+  static int get staticGetter => _staticField;
+  static set staticSetter(int x) => _staticField = x;
+  static int _staticField = 0;
+}
+
+enum F<T> {
+  f1<int>(1),
+  f2('foo'),
+  f3(<String, dynamic>{});
+
+  const F(this.value);
+
+  void debugMethod() {
+    debugger();
+  }
+
+  final T value;
+
+  String toString() => 'OVERRIDE ${value.toString()}';
+}
+
+void testMain() {
+  debugger();
+  F.f1.debugMethod();
+}
+
+Future<void> expectError(func) async {
+  bool gotException = false;
+  try {
+    await func();
+    fail('Failed to throw');
+  } on RPCError catch (e) {
+    expect(e.code, 113); // Compile time error.
+    gotException = true;
+  }
+  expect(gotException, true);
+}
+
+late final String isolateId;
+late final Isolate isolate;
+late final String rootLibraryId;
+late final Class enumECls;
+late final String enumEClsId;
+late final Class enumFCls;
+late final String enumFClsId;
+
+final tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (VmService service, IsolateRef isolateRef) async {
+    // Initialization.
+    isolateId = isolateRef.id!;
+    isolate = await service.getIsolate(isolateId);
+    rootLibraryId = isolate.rootLib!.id!;
+    final rootLibrary = await service.getObject(
+      isolateId,
+      rootLibraryId,
+    ) as Library;
+
+    final enumERef = rootLibrary.classes!.firstWhere((c) => c.name == 'E');
+    enumECls = await service.getObject(isolateId, enumERef.id!) as Class;
+    enumEClsId = enumECls.id!;
+
+    final enumFRef = rootLibrary.classes!.firstWhere((c) => c.name == 'F');
+    enumFCls = await service.getObject(isolateId, enumFRef.id!) as Class;
+    enumFClsId = enumFCls.id!;
+  },
+  (VmService service, _) async {
+    // Check all functions and fields are found.
+    expect(
+      enumECls.functions!.map((f) => f.name),
+      containsAll([
+        'e1',
+        'e2',
+        'e3',
+        'values',
+        'toString',
+        'interfaceSetter1=',
+        'interfaceGetter1',
+        'interfaceSetter2=',
+        'interfaceGetter2',
+        'interfaceMethod1',
+        'interfaceMethod2',
+        'staticGetter',
+        'staticSetter=',
+      ]),
+    );
+    expect(
+      enumECls.fields!.map((f) => f.name),
+      containsAll([
+        'e1',
+        'e2',
+        'e3',
+        'values',
+        '_staticField',
+      ]),
+    );
+  },
+  (VmService service, _) async {
+    // Ensure attempting to create an instance of an Enum fails.
+    await expectError(() => service.evaluate(isolateId, rootLibraryId, 'E()'));
+    await expectError(
+      () => service.evaluate(isolateId, rootLibraryId, 'E(10, "staticGetter")'),
+    );
+  },
+  (VmService service, _) async {
+    // Ensure we can evaluate enum values in the context of the enum Class.
+    dynamic result = await service.evaluate(isolateId, enumEClsId, 'e1');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'E');
+    result = await service.evaluate(isolateId, result.id!, 'name');
+    expect(result.valueAsString, 'e1');
+
+    result = await service.evaluate(isolateId, enumEClsId, 'e2');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'E');
+    result = await service.evaluate(isolateId, result.id!, 'name');
+    expect(result.valueAsString, 'e2');
+
+    result = await service.evaluate(isolateId, enumEClsId, 'e3');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'E');
+    result = await service.evaluate(isolateId, result.id!, 'name');
+    expect(result.valueAsString, 'e3');
+  },
+  (VmService service, _) async {
+    // Ensure we can evaluate enum values in the context of the library.
+    dynamic result = await service.evaluate(isolateId, rootLibraryId, 'E.e1');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'E');
+    result = await service.evaluate(isolateId, result.id!, 'name');
+    expect(result.valueAsString, 'e1');
+
+    result = await service.evaluate(isolateId, rootLibraryId, 'E.e2');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'E');
+    result = await service.evaluate(isolateId, result.id!, 'name');
+    expect(result.valueAsString, 'e2');
+
+    result = await service.evaluate(isolateId, rootLibraryId, 'E.e3');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'E');
+    result = await service.evaluate(isolateId, result.id!, 'name');
+    expect(result.valueAsString, 'e3');
+  },
+  (VmService service, _) async {
+    // Ensure we can evaluate instance getters and methods.
+    dynamic e1 = await service.evaluate(isolateId, enumEClsId, 'e1');
+    expect(e1, isA<InstanceRef>());
+    final e1Id = e1.id!;
+
+    dynamic result = await service.evaluate(isolateId, e1Id, 'interfaceGetter1');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.evaluate(isolateId, e1Id, 'interfaceGetter2');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.evaluate(isolateId, e1Id, 'interfaceMethod1()');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.evaluate(isolateId, e1Id, 'interfaceMethod2()');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.evaluate(isolateId, e1Id, 'mixedInMethod()');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.evaluate(isolateId, e1Id, 'toString()');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, 'E.e1');
+  },
+  (VmService service, _) async {
+    // Ensure we can evaluate static getters and methods.
+    dynamic result = await service.evaluate(isolateId, enumEClsId, 'staticGetter');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '0');
+
+    result = await service.evaluate(isolateId, enumEClsId, 'staticMethod()');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+  },
+  (VmService service, _) async {
+    // Ensure we can invoke instance methods.
+    dynamic e1 = await service.evaluate(isolateId, enumEClsId, 'e1');
+    expect(e1, isA<InstanceRef>());
+    final e1Id = e1.id!;
+
+    dynamic result = await service.invoke(isolateId, e1Id, 'interfaceMethod1', []);
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.invoke(isolateId, e1Id, 'interfaceMethod2', []);
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.invoke(isolateId, e1Id, 'mixedInMethod', []);
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+
+    result = await service.invoke(isolateId, e1Id, 'toString', []);
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, 'E.e1');
+  },
+  (VmService service, _) async {
+    // Ensure we can invoke static methods.
+    dynamic result = await service.evaluate(isolateId, enumEClsId, 'staticMethod()');
+    expect(result, isA<InstanceRef>());
+    expect(result.valueAsString, '42');
+  },
+  (VmService service, _) async {
+    // Ensure we can evaluate enums user defined properties.
+    dynamic result = await service.evaluate(isolateId, rootLibraryId, 'F.f1');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'F');
+    result = await service.evaluate(isolateId, result.id!, 'value');
+    expect(result.valueAsString, '1');
+
+    result = await service.evaluate(isolateId, rootLibraryId, 'F.f2');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'F');
+    result = await service.evaluate(isolateId, result.id!, 'value');
+    expect(result.valueAsString, 'foo');
+
+    result = await service.evaluate(isolateId, rootLibraryId, 'F.f3');
+    expect(result, isA<InstanceRef>());
+    expect(result.classRef.name, 'F');
+    result = await service.evaluate(isolateId, result.id!, 'value');
+    expect(result.kind, 'Map');
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (VmService service, _) async {
+    dynamic result = await service.evaluateInFrame(isolateId, 0, 'T.toString()');
+    expect(result.valueAsString, 'int');
+
+    result = await service.evaluateInFrame(isolateId, 0, 'value');
+    expect(result.kind, 'Int');
+  },
+];
+
+main([args = const <String>[]]) => runIsolateTests(
+      args,
+      tests,
+      'enhanced_enum_test.dart',
+      testeeConcurrent: testMain,
+      experiments: ['enhanced-enums'],
+    );