[ 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'],
+ );