From 87bd77ffa070c66a4072848ed93c05410bf0c01a Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 21 Jul 2023 12:51:29 -0400 Subject: [PATCH] Split out common parts of instance tests in preparation for adding canary tests (#2176) * Refactor instance tests * Split out the common instance tests * Move object tests out instance tests --- .../instances/common/instance_common.dart | 336 +++++++++++ .../common/instance_inspection_common.dart | 275 +++++++++ .../common/patterns_inspection_common.dart | 170 ++++++ .../common/record_inspection_common.dart | 537 ++++++++++++++++++ .../common/record_type_inspection_common.dart | 350 ++++++++++++ .../test/instances/common/test_inspector.dart | 3 - .../common/type_inspection_common.dart | 226 ++++++++ .../instances/instance_inspection_test.dart | 269 +-------- dwds/test/instances/instance_test.dart | 331 +---------- .../instances/patterns_inspection_test.dart | 165 +----- .../instances/record_inspection_test.dart | 532 +---------------- .../record_type_inspection_test.dart | 345 +---------- dwds/test/instances/type_inspection_test.dart | 221 +------ dwds/test/{instances => }/objects_test.dart | 0 14 files changed, 1906 insertions(+), 1854 deletions(-) create mode 100644 dwds/test/instances/common/instance_common.dart create mode 100644 dwds/test/instances/common/instance_inspection_common.dart create mode 100644 dwds/test/instances/common/patterns_inspection_common.dart create mode 100644 dwds/test/instances/common/record_inspection_common.dart create mode 100644 dwds/test/instances/common/record_type_inspection_common.dart create mode 100644 dwds/test/instances/common/type_inspection_common.dart rename dwds/test/{instances => }/objects_test.dart (100%) diff --git a/dwds/test/instances/common/instance_common.dart b/dwds/test/instances/common/instance_common.dart new file mode 100644 index 000000000..fc8013dd0 --- /dev/null +++ b/dwds/test/instances/common/instance_common.dart @@ -0,0 +1,336 @@ +// 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:dwds/src/debugging/inspector.dart'; +import 'package:dwds/src/loaders/strategy.dart'; +import 'package:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:test_common/test_sdk_configuration.dart'; +import 'package:vm_service/vm_service.dart'; +import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; + +import '../../fixtures/context.dart'; +import '../../fixtures/project.dart'; + +void runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required bool debug, +}) { + final context = + TestContext(TestProject.testScopesWithSoundNullSafety, provider); + + late AppInspector inspector; + + group('$compilationMode |', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp(compilationMode: compilationMode); + final chromeProxyService = context.service; + inspector = chromeProxyService.inspector; + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + final url = 'org-dartlang-app:///example/scopes/main.dart'; + + String libraryName(CompilationMode compilationMode) => + compilationMode == CompilationMode.frontendServer + ? "example/scopes/main.dart" + : "example/scopes/main"; + + String libraryVariableExpression( + String variable, + CompilationMode compilationMode, + ) => + '${globalLoadStrategy.loadModuleSnippet}("dart_sdk").dart.' + 'getModuleLibraries("${libraryName(compilationMode)}")' + '["$url"]["$variable"];'; + + String interceptorsNewExpression(String type) => + "require('dart_sdk')._interceptors.$type['_#new#tearOff']()"; + + /// A reference to the the variable `libraryPublicFinal`, an instance of + /// `MyTestClass`. + Future libraryPublicFinal(CompilationMode compilationMode) => + inspector.jsEvaluate( + libraryVariableExpression('libraryPublicFinal', compilationMode), + ); + + /// A reference to the the variable `libraryPublic`, a List of Strings. + Future libraryPublic(CompilationMode compilationMode) => + inspector.jsEvaluate( + libraryVariableExpression('libraryPublic', compilationMode), + ); + + group('instanceRef', () { + setUp(() => setCurrentLogWriter(debug: debug)); + + test('for a null', () async { + final remoteObject = await libraryPublicFinal(compilationMode); + final nullVariable = + await inspector.loadField(remoteObject, 'notFinal'); + final ref = await inspector.instanceRefFor(nullVariable); + expect(ref!.valueAsString, 'null'); + expect(ref.kind, InstanceKind.kNull); + final classRef = ref.classRef!; + expect(classRef.name, 'Null'); + expect(classRef.id, 'classes|dart:core|Null'); + expect(inspector.isDisplayableObject(ref), isTrue); + }); + + test('for a double', () async { + final remoteObject = await libraryPublicFinal(compilationMode); + final count = await inspector.loadField(remoteObject, 'count'); + final ref = await inspector.instanceRefFor(count); + expect(ref!.valueAsString, '0'); + expect(ref.kind, InstanceKind.kDouble); + final classRef = ref.classRef!; + expect(classRef.name, 'Double'); + expect(classRef.id, 'classes|dart:core|Double'); + expect(inspector.isDisplayableObject(ref), isTrue); + }); + + test('for a class', () async { + final remoteObject = await libraryPublicFinal(compilationMode); + final count = await inspector.loadField(remoteObject, 'myselfField'); + final ref = await inspector.instanceRefFor(count); + expect(ref!.kind, InstanceKind.kPlainInstance); + final classRef = ref.classRef!; + expect(classRef.name, 'MyTestClass'); + expect( + classRef.id, + 'classes|org-dartlang-app:///example/scopes/main.dart' + '|MyTestClass'); + expect(inspector.isDisplayableObject(ref), isTrue); + }); + + test('for closure', () async { + final remoteObject = await libraryPublicFinal(compilationMode); + final properties = + await inspector.getProperties(remoteObject.objectId!); + final closure = + properties.firstWhere((property) => property.name == 'closure'); + final ref = await inspector.instanceRefFor(closure.value!); + final functionName = ref!.closureFunction!.name; + // Older SDKs do not contain function names + if (functionName != 'Closure') { + expect(functionName, 'someFunction'); + } + expect(ref.kind, InstanceKind.kClosure); + expect(inspector.isDisplayableObject(ref), isTrue); + }); + + test('for a list', () async { + final remoteObject = await libraryPublic(compilationMode); + final ref = await inspector.instanceRefFor(remoteObject); + expect(ref!.length, greaterThan(0)); + expect(ref.kind, InstanceKind.kList); + expect(ref.classRef!.name, 'List'); + expect(inspector.isDisplayableObject(ref), isTrue); + }); + + test('for map', () async { + final remoteObject = await inspector + .jsEvaluate(libraryVariableExpression('map', compilationMode)); + final ref = await inspector.instanceRefFor(remoteObject); + expect(ref!.length, 2); + expect(ref.kind, InstanceKind.kMap); + expect(ref.classRef!.name, 'LinkedMap'); + expect(inspector.isDisplayableObject(ref), isTrue); + }); + + test('for an IdentityMap', () async { + final remoteObject = await inspector.jsEvaluate( + libraryVariableExpression('identityMap', compilationMode), + ); + final ref = await inspector.instanceRefFor(remoteObject); + expect(ref!.length, 2); + expect(ref.kind, InstanceKind.kMap); + expect(ref.classRef!.name, 'IdentityMap'); + expect(inspector.isDisplayableObject(ref), isTrue); + }); + + test('for a native JavaScript error', () async { + final remoteObject = await inspector + .jsEvaluate(interceptorsNewExpression('NativeError')); + final ref = await inspector.instanceRefFor(remoteObject); + expect(ref!.kind, InstanceKind.kPlainInstance); + expect(ref.classRef!.name, 'NativeError'); + expect(inspector.isDisplayableObject(ref), isFalse); + expect(inspector.isNativeJsError(ref), isTrue); + expect(inspector.isNativeJsObject(ref), isFalse); + }); + + test('for a native JavaScript type error', () async { + final remoteObject = await inspector + .jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError')); + final ref = await inspector.instanceRefFor(remoteObject); + expect(ref!.kind, InstanceKind.kPlainInstance); + expect(ref.classRef!.name, 'JSNoSuchMethodError'); + expect(inspector.isDisplayableObject(ref), isFalse); + expect(inspector.isNativeJsError(ref), isTrue); + expect(inspector.isNativeJsObject(ref), isFalse); + }); + + test('for a native JavaScript object', () async { + final remoteObject = await inspector + .jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject')); + final ref = await inspector.instanceRefFor(remoteObject); + expect(ref!.kind, InstanceKind.kPlainInstance); + expect(ref.classRef!.name, 'LegacyJavaScriptObject'); + expect(inspector.isDisplayableObject(ref), isFalse); + expect(inspector.isNativeJsError(ref), isFalse); + expect(inspector.isNativeJsObject(ref), isTrue); + }); + }); + + group('instance', () { + setUp(() => setCurrentLogWriter(debug: debug)); + test('for class object', () async { + final remoteObject = await libraryPublicFinal(compilationMode); + final instance = await inspector.instanceFor(remoteObject); + expect(instance!.kind, InstanceKind.kPlainInstance); + final classRef = instance.classRef!; + expect(classRef, isNotNull); + expect(classRef.name, 'MyTestClass'); + final boundFieldNames = instance.fields! + .map((boundField) => boundField.decl!.name) + .toList(); + expect(boundFieldNames, [ + '_privateField', + 'abstractField', + 'closure', + 'count', + 'message', + 'myselfField', + 'notFinal', + 'tornOff', + ]); + final fieldNames = + instance.fields!.map((boundField) => boundField.name).toList(); + expect(boundFieldNames, fieldNames); + for (var field in instance.fields!) { + expect(field.name, isNotNull); + expect(field.decl!.declaredType, isNotNull); + } + expect(inspector.isDisplayableObject(instance), isTrue); + }); + + test('for closure', () async { + final remoteObject = await libraryPublicFinal(compilationMode); + final properties = + await inspector.getProperties(remoteObject.objectId!); + final closure = + properties.firstWhere((property) => property.name == 'closure'); + final instance = await inspector.instanceFor(closure.value!); + expect(instance!.kind, InstanceKind.kClosure); + expect(instance.classRef!.name, 'Closure'); + expect(inspector.isDisplayableObject(instance), isTrue); + }); + + test('for a nested class', () async { + final libraryRemoteObject = await libraryPublicFinal(compilationMode); + final fieldRemoteObject = + await inspector.loadField(libraryRemoteObject, 'myselfField'); + final instance = await inspector.instanceFor(fieldRemoteObject); + expect(instance!.kind, InstanceKind.kPlainInstance); + final classRef = instance.classRef!; + expect(classRef, isNotNull); + expect(classRef.name, 'MyTestClass'); + expect(inspector.isDisplayableObject(instance), isTrue); + }); + + test('for a list', () async { + final remote = await libraryPublic(compilationMode); + final instance = await inspector.instanceFor(remote); + expect(instance!.kind, InstanceKind.kList); + final classRef = instance.classRef!; + expect(classRef, isNotNull); + expect(classRef.name, 'List'); + final first = instance.elements![0]; + expect(first.valueAsString, 'library'); + expect(inspector.isDisplayableObject(instance), isTrue); + }); + + test('for a map', () async { + final remote = await inspector + .jsEvaluate(libraryVariableExpression('map', compilationMode)); + final instance = await inspector.instanceFor(remote); + expect(instance!.kind, InstanceKind.kMap); + final classRef = instance.classRef!; + expect(classRef.name, 'LinkedMap'); + final first = instance.associations![0].value as InstanceRef; + expect(first.kind, InstanceKind.kList); + expect(first.length, 3); + final second = instance.associations![1].value as InstanceRef; + expect(second.kind, InstanceKind.kString); + expect(second.valueAsString, 'something'); + expect(inspector.isDisplayableObject(instance), isTrue); + }); + + test('for an identityMap', () async { + final remote = await inspector.jsEvaluate( + libraryVariableExpression('identityMap', compilationMode), + ); + final instance = await inspector.instanceFor(remote); + expect(instance!.kind, InstanceKind.kMap); + final classRef = instance.classRef!; + expect(classRef.name, 'IdentityMap'); + final first = instance.associations![0].value; + expect(first.valueAsString, '1'); + expect(inspector.isDisplayableObject(instance), isTrue); + }); + + test('for a class that implements List', () async { + // The VM only uses kind List for SDK lists, and we follow that. + final remote = await inspector + .jsEvaluate(libraryVariableExpression('notAList', compilationMode)); + final instance = await inspector.instanceFor(remote); + expect(instance!.kind, InstanceKind.kPlainInstance); + final classRef = instance.classRef!; + expect(classRef.name, 'NotReallyAList'); + expect(instance.elements, isNull); + final field = instance.fields!.first; + expect(field.decl!.name, '_internal'); + expect(inspector.isDisplayableObject(instance), isTrue); + }); + + test('for a native JavaScript error', () async { + final remoteObject = await inspector + .jsEvaluate(interceptorsNewExpression('NativeError')); + final instance = await inspector.instanceFor(remoteObject); + expect(instance!.kind, InstanceKind.kPlainInstance); + expect(instance.classRef!.name, 'NativeError'); + expect(inspector.isDisplayableObject(instance), isFalse); + expect(inspector.isNativeJsError(instance), isTrue); + expect(inspector.isNativeJsObject(instance), isFalse); + }); + + test('for a native JavaScript type error', () async { + final remoteObject = await inspector + .jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError')); + final instance = await inspector.instanceFor(remoteObject); + expect(instance!.kind, InstanceKind.kPlainInstance); + expect(instance.classRef!.name, 'JSNoSuchMethodError'); + expect(inspector.isDisplayableObject(instance), isFalse); + expect(inspector.isNativeJsError(instance), isTrue); + expect(inspector.isNativeJsObject(instance), isFalse); + }); + + test('for a native JavaScript object', () async { + final remoteObject = await inspector + .jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject')); + final instance = await inspector.instanceFor(remoteObject); + expect(instance!.kind, InstanceKind.kPlainInstance); + expect(instance.classRef!.name, 'LegacyJavaScriptObject'); + expect(inspector.isDisplayableObject(instance), isFalse); + expect(inspector.isNativeJsError(instance), isFalse); + expect(inspector.isNativeJsObject(instance), isTrue); + }); + }); + }); +} diff --git a/dwds/test/instances/common/instance_inspection_common.dart b/dwds/test/instances/common/instance_inspection_common.dart new file mode 100644 index 000000000..94e2d1b0e --- /dev/null +++ b/dwds/test/instances/common/instance_inspection_common.dart @@ -0,0 +1,275 @@ +// 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:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:test_common/test_sdk_configuration.dart'; +import 'package:vm_service/vm_service.dart'; + +import '../../fixtures/context.dart'; +import '../../fixtures/project.dart'; +import 'test_inspector.dart'; + +Future runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required NullSafety nullSafetyMode, + required bool debug, +}) async { + final testPackage = nullSafetyMode == NullSafety.sound + ? TestProject.testPackageWithSoundNullSafety() + : TestProject.testPackageWithWeakNullSafety(); + final context = TestContext(testPackage, provider); + late VmServiceInterface service; + late Stream stream; + late String isolateId; + late ScriptRef mainScript; + + final testInspector = TestInspector(context); + + onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( + stream, + isolateId, + mainScript, + breakPointId, + body, + ); + + getInstance(frame, expression) => + testInspector.getInstance(isolateId, frame, expression); + + getObject(instanceId) => service.getObject(isolateId, instanceId); + + getInstanceRef(frame, expression) => + testInspector.getInstanceRef(isolateId, frame, expression); + + getFields(instanceRef, {offset, count}) => testInspector + .getFields(isolateId, instanceRef, offset: offset, count: count); + + group('$nullSafetyMode |', () { + group('$compilationMode |', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['records'], + ); + service = context.debugConnection.vmService; + + final vm = await service.getVM(); + isolateId = vm.isolates!.first.id!; + final scripts = await service.getScripts(isolateId); + + await service.streamListen('Debug'); + stream = service.onEvent('Debug'); + + mainScript = scripts.scripts! + .firstWhere((each) => each.uri!.contains('main.dart')); + }); + + tearDownAll(context.tearDown); + + setUp(() => setCurrentLogWriter(debug: debug)); + tearDown(() => service.resume(isolateId)); + + group('Object |', () { + test('type and fields', () async { + await onBreakPoint('printFieldMain', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'instance'); + + final instanceId = instanceRef.id!; + expect( + await getObject(instanceId), + matchPlainInstance( + libraryId: 'org-dartlang-app:///web/main.dart', + type: 'MainClass', + ), + ); + + expect(await getFields(instanceRef), {'_field': 1, 'field': 2}); + + // Offsets and counts are ignored for plain object fields. + + // DevTools calls [VmServiceInterface.getObject] with offset=0 + // and count=0 and expects all fields to be returned. + expect( + await getFields(instanceRef, offset: 0, count: 0), + {'_field': 1, 'field': 2}, + ); + expect( + await getFields(instanceRef, offset: 0), + {'_field': 1, 'field': 2}, + ); + expect( + await getFields(instanceRef, offset: 0, count: 1), + {'_field': 1, 'field': 2}, + ); + expect( + await getFields(instanceRef, offset: 1), + {'_field': 1, 'field': 2}, + ); + expect( + await getFields(instanceRef, offset: 1, count: 0), + {'_field': 1, 'field': 2}, + ); + expect( + await getFields(instanceRef, offset: 1, count: 3), + {'_field': 1, 'field': 2}, + ); + }); + }); + + test('field access', () async { + await onBreakPoint('printFieldMain', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r'instance.field'), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 2), + ); + + expect( + await getInstance(frame, r'instance._field'), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), + ); + }); + }); + }); + + group('List |', () { + test('type and fields', () async { + await onBreakPoint('printList', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'list'); + + final instanceId = instanceRef.id!; + expect( + await getObject(instanceId), + matchListInstance(type: 'List'), + ); + + expect(await getFields(instanceRef), [0, 1, 2]); + expect(await getFields(instanceRef, offset: 1, count: 0), []); + expect(await getFields(instanceRef, offset: 0), [0, 1, 2]); + expect(await getFields(instanceRef, offset: 0, count: 1), [0]); + expect(await getFields(instanceRef, offset: 1), [1, 2]); + expect(await getFields(instanceRef, offset: 1, count: 1), [1]); + expect(await getFields(instanceRef, offset: 1, count: 3), [1, 2]); + expect(await getFields(instanceRef, offset: 3, count: 3), []); + }); + }); + + test('Element access', () async { + await onBreakPoint('printList', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r'list[0]'), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 0), + ); + + expect( + await getInstance(frame, r"list[1]"), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), + ); + + expect( + await getInstance(frame, r"list[2]"), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 2), + ); + }); + }); + }); + + group('Map |', () { + test('type and fields', () async { + await onBreakPoint('printMap', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'map'); + + final instanceId = instanceRef.id!; + expect( + await getObject(instanceId), + matchMapInstance(type: 'IdentityMap'), + ); + + expect(await getFields(instanceRef), {'a': 1, 'b': 2, 'c': 3}); + + expect(await getFields(instanceRef, offset: 1, count: 0), {}); + expect( + await getFields(instanceRef, offset: 0), + {'a': 1, 'b': 2, 'c': 3}, + ); + expect(await getFields(instanceRef, offset: 0, count: 1), {'a': 1}); + expect(await getFields(instanceRef, offset: 1), {'b': 2, 'c': 3}); + expect(await getFields(instanceRef, offset: 1, count: 1), {'b': 2}); + expect( + await getFields(instanceRef, offset: 1, count: 3), + {'b': 2, 'c': 3}, + ); + expect(await getFields(instanceRef, offset: 3, count: 3), {}); + }); + }); + + test('Element access', () async { + await onBreakPoint('printMap', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r"map['a']"), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), + ); + + expect( + await getInstance(frame, r"map['b']"), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 2), + ); + + expect( + await getInstance(frame, r"map['c']"), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), + ); + }); + }); + }); + + group('Set |', () { + test('type and fields', () async { + await onBreakPoint('printSet', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'mySet'); + + final instanceId = instanceRef.id!; + expect( + await getObject(instanceId), + matchSetInstance(type: '_HashSet'), + ); + + expect(await getFields(instanceRef), [1, 4, 5, 7]); + expect(await getFields(instanceRef, offset: 0), [1, 4, 5, 7]); + expect(await getFields(instanceRef, offset: 1, count: 2), [4, 5]); + expect(await getFields(instanceRef, offset: 2), [5, 7]); + expect(await getFields(instanceRef, offset: 2, count: 10), [5, 7]); + expect(await getFields(instanceRef, offset: 1, count: 0), []); + expect(await getFields(instanceRef, offset: 10, count: 2), []); + }); + }); + + test('Element access', () async { + await onBreakPoint('printSet', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r"mySet.first"), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), + ); + expect( + await getInstance(frame, r"mySet.last"), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 7), + ); + }); + }); + }); + }); + }); +} diff --git a/dwds/test/instances/common/patterns_inspection_common.dart b/dwds/test/instances/common/patterns_inspection_common.dart new file mode 100644 index 000000000..94ed57917 --- /dev/null +++ b/dwds/test/instances/common/patterns_inspection_common.dart @@ -0,0 +1,170 @@ +// 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:io'; + +import 'package:pub_semver/pub_semver.dart' as semver; +import 'package:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:test_common/test_sdk_configuration.dart'; +import 'package:vm_service/vm_service.dart'; + +import '../../fixtures/context.dart'; +import '../../fixtures/project.dart'; +import 'test_inspector.dart'; + +Future runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required bool debug, +}) async { + final context = + TestContext(TestProject.testExperimentWithSoundNullSafety, provider); + final testInspector = TestInspector(context); + + late VmServiceInterface service; + late Stream stream; + late String isolateId; + late ScriptRef mainScript; + + onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( + stream, + isolateId, + mainScript, + breakPointId, + body, + ); + + getInstanceRef(frame, expression) => + testInspector.getInstanceRef(isolateId, frame, expression); + + getFields(instanceRef, {offset, count}) => testInspector + .getFields(isolateId, instanceRef, offset: offset, count: count); + + getFrameVariables(Frame frame) => + testInspector.getFrameVariables(isolateId, frame); + + group( + '$compilationMode |', + () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['records', 'patterns'], + ); + service = context.debugConnection.vmService; + + final vm = await service.getVM(); + isolateId = vm.isolates!.first.id!; + final scripts = await service.getScripts(isolateId); + + await service.streamListen('Debug'); + stream = service.onEvent('Debug'); + + mainScript = scripts.scripts! + .firstWhere((each) => each.uri!.contains('main.dart')); + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + setUp(() => setCurrentLogWriter(debug: debug)); + tearDown(() => service.resume(isolateId)); + + test('pattern match case 1', () async { + await onBreakPoint('testPatternCase1', (event) async { + final frame = event.topFrame!; + + expect(await getFrameVariables(frame), { + 'obj': matchListInstance(type: 'List'), + 'a': matchPrimitiveInstance(kind: InstanceKind.kString, value: 'a'), + 'n': matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), + }); + }); + }); + + test('pattern match case 2', () async { + await onBreakPoint('testPatternCase2', (event) async { + final frame = event.topFrame!; + + expect(await getFrameVariables(frame), { + 'obj': matchListInstance(type: 'List'), + 'a': matchPrimitiveInstance(kind: InstanceKind.kString, value: 'b'), + 'n': + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3.14), + }); + }); + }); + + test('pattern match default case', () async { + await onBreakPoint('testPatternDefault', (event) async { + final frame = event.topFrame!; + final frameIndex = frame.index!; + final instanceRef = await getInstanceRef(frameIndex, 'obj'); + expect(await getFields(instanceRef), [0, 1]); + + expect(await getFrameVariables(frame), { + 'obj': matchListInstance(type: 'List'), + }); + }); + }); + + test('stepping through pattern match', () async { + await onBreakPoint('callTestPattern1', (Event event) async { + var previousLocation = event.topFrame!.location; + for (var step in [ + // Make sure we step into the callee. + for (var i = 0; i < 4; i++) 'Into', + // Make a few steps inside the callee. + for (var i = 0; i < 4; i++) 'Over', + ]) { + await service.resume(isolateId, step: step); + + event = await stream + .firstWhere((e) => e.kind == EventKind.kPauseInterrupted); + + if (step == 'Over') { + expect(event.topFrame!.code!.name, 'testPattern'); + } + + final location = event.topFrame!.location; + expect(location, isNot(equals(previousLocation))); + previousLocation = location; + } + }); + }); + + test('before instantiation of pattern-matching variables', () async { + await onBreakPoint('testPattern2Case1', (event) async { + final frame = event.topFrame!; + + expect( + await getFrameVariables(frame), + {'dog': matchPrimitiveInstance(kind: 'String', value: 'Prismo')}, + ); + }); + }); + + test('after instantiation of pattern-matching variables', () async { + await onBreakPoint('testPattern2Case2', (event) async { + final frame = event.topFrame!; + + expect(await getFrameVariables(frame), { + 'dog': matchPrimitiveInstance(kind: 'String', value: 'Prismo'), + 'cats': matchListInstance(type: 'List'), + 'firstCat': + matchPrimitiveInstance(kind: 'String', value: 'Garfield'), + 'secondCat': matchPrimitiveInstance(kind: 'String', value: 'Tom'), + }); + }); + }); + }, // TODO(annagrin): Remove when dart 3.0 is stable. + skip: semver.Version.parse(Platform.version.split(' ')[0]) < + semver.Version.parse('3.0.0-351.0.dev'), + ); +} diff --git a/dwds/test/instances/common/record_inspection_common.dart b/dwds/test/instances/common/record_inspection_common.dart new file mode 100644 index 000000000..833e56201 --- /dev/null +++ b/dwds/test/instances/common/record_inspection_common.dart @@ -0,0 +1,537 @@ +// 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:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:test_common/test_sdk_configuration.dart'; +import 'package:vm_service/vm_service.dart'; + +import '../../fixtures/context.dart'; +import '../../fixtures/project.dart'; +import 'test_inspector.dart'; + +Future runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required bool debug, +}) async { + final context = + TestContext(TestProject.testExperimentWithSoundNullSafety, provider); + final testInspector = TestInspector(context); + + late VmServiceInterface service; + late Stream stream; + late String isolateId; + late ScriptRef mainScript; + + onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( + stream, + isolateId, + mainScript, + breakPointId, + body, + ); + + getInstance(frame, expression) => + testInspector.getInstance(isolateId, frame, expression); + + getObject(instanceId) => service.getObject(isolateId, instanceId); + + getInstanceRef(frame, expression) => + testInspector.getInstanceRef(isolateId, frame, expression); + + getFields(instanceRef, {offset, count, depth = -1}) => + testInspector.getFields( + isolateId, + instanceRef, + offset: offset, + count: count, + depth: depth, + ); + + group('$compilationMode |', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['records', 'patterns'], + ); + service = context.debugConnection.vmService; + + final vm = await service.getVM(); + isolateId = vm.isolates!.first.id!; + final scripts = await service.getScripts(isolateId); + + await service.streamListen('Debug'); + stream = service.onEvent('Debug'); + + mainScript = scripts.scripts! + .firstWhere((each) => each.uri!.contains('main.dart')); + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + setUp(() => setCurrentLogWriter(debug: debug)); + tearDown(() => service.resume(isolateId)); + + test('simple record display', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + + final instanceRef = await getInstanceRef(frame, 'record'); + final classId = instanceRef.classRef!.id; + + expect(await getObject(classId), matchRecordClass); + + final stringRef = await getInstanceRef(frame, 'record.toString()'); + final stringRefId = stringRef.id!; + + expect( + await getObject(stringRefId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(true, 3)', + ), + ); + }); + }); + + test('simple records', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record'); + final instanceId = instanceRef.id!; + + expect(instanceRef, matchRecordInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordInstance(length: 2)); + + expect(await getFields(instanceRef), {1: true, 2: 3}); + expect(await getFields(instanceRef, offset: 0), {1: true, 2: 3}); + expect(await getFields(instanceRef, offset: 1), {2: 3}); + expect(await getFields(instanceRef, offset: 2), {}); + expect(await getFields(instanceRef, offset: 0, count: 0), {}); + expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); + expect( + await getFields(instanceRef, offset: 0, count: 2), + {1: true, 2: 3}, + ); + expect( + await getFields(instanceRef, offset: 0, count: 5), + {1: true, 2: 3}, + ); + expect(await getFields(instanceRef, offset: 2, count: 5), {}); + }); + }); + + test('simple records, field access', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r'record.$1'), + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), + ); + + expect( + await getInstance(frame, r'record.$2'), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), + ); + }); + }); + + test('simple records with named fields display', () async { + await onBreakPoint('printSimpleNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + + final instanceRef = await getInstanceRef(frame, 'record'); + final classId = instanceRef.classRef!.id; + + expect(await getObject(classId), matchRecordClass); + + final stringRef = await getInstanceRef(frame, 'record.toString()'); + final stringId = stringRef.id!; + + expect( + await getObject(stringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(true, cat: Vasya)', + ), + ); + }); + }); + + test('simple records with named fields', () async { + await onBreakPoint('printSimpleNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record'); + + final instanceId = instanceRef.id!; + expect(instanceRef, matchRecordInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordInstance(length: 2)); + + expect(await getFields(instanceRef), {1: true, 'cat': 'Vasya'}); + expect( + await getFields(instanceRef, offset: 0), + {1: true, 'cat': 'Vasya'}, + ); + expect(await getFields(instanceRef, offset: 1), {'cat': 'Vasya'}); + expect(await getFields(instanceRef, offset: 2), {}); + expect(await getFields(instanceRef, offset: 0, count: 0), {}); + expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); + expect( + await getFields(instanceRef, offset: 0, count: 2), + {1: true, 'cat': 'Vasya'}, + ); + expect( + await getFields(instanceRef, offset: 0, count: 5), + {1: true, 'cat': 'Vasya'}, + ); + expect(await getFields(instanceRef, offset: 2, count: 5), {}); + }); + }); + + test('simple records with named fields, field access', () async { + await onBreakPoint('printSimpleNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r'record.$1'), + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), + ); + + expect( + await getInstance(frame, r'record.cat'), + matchPrimitiveInstance(kind: InstanceKind.kString, value: 'Vasya'), + ); + }); + }); + + test('complex records display', () async { + await onBreakPoint('printComplexLocalRecord', (event) async { + final frame = event.topFrame!.index!; + + final instanceRef = await getInstanceRef(frame, 'record'); + final classId = instanceRef.classRef!.id; + + expect(await getObject(classId), matchRecordClass); + + final stringRef = await getInstanceRef(frame, 'record.toString()'); + final stringId = stringRef.id!; + + expect( + await getObject(stringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(true, 3, {a: 1, b: 5})', + ), + ); + }); + }); + + test('complex records', () async { + await onBreakPoint('printComplexLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record'); + + final instanceId = instanceRef.id!; + expect(instanceRef, matchRecordInstanceRef(length: 3)); + expect(await getObject(instanceId), matchRecordInstance(length: 3)); + + expect(await getFields(instanceRef), { + 1: true, + 2: 3, + 3: {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 0), { + 1: true, + 2: 3, + 3: {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 1), { + 2: 3, + 3: {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 1, count: 1), {2: 3}); + expect(await getFields(instanceRef, offset: 1, count: 2), { + 2: 3, + 3: {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 2), { + 3: {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 3), {}); + expect(await getFields(instanceRef, offset: 0, count: 0), {}); + expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); + expect( + await getFields(instanceRef, offset: 0, count: 2), + {1: true, 2: 3}, + ); + expect(await getFields(instanceRef, offset: 0, count: 5), { + 1: true, + 2: 3, + 3: {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 3, count: 5), {}); + }); + }); + + test('complex records, field access', () async { + await onBreakPoint('printComplexLocalRecord', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r'record.$1'), + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), + ); + + expect( + await getInstance(frame, r'record.$2'), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), + ); + + final third = await getInstanceRef(frame, r'record.$3'); + expect(third.kind, InstanceKind.kMap); + expect(await getFields(third), {'a': 1, 'b': 5}); + }); + }); + + test('complex records with named fields display', () async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + + final instanceRef = await getInstanceRef(frame, 'record'); + final classId = instanceRef.classRef!.id; + + expect(await getObject(classId), matchRecordClass); + + final stringRef = await getInstanceRef(frame, 'record.toString()'); + final stringId = stringRef.id!; + + expect( + await getObject(stringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(true, 3, array: {a: 1, b: 5})', + ), + ); + }); + }); + + test('complex records with named fields', () async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record'); + + final instanceId = instanceRef.id!; + expect(instanceRef, matchRecordInstanceRef(length: 3)); + expect(await getObject(instanceId), matchRecordInstance(length: 3)); + + expect(await getFields(instanceRef), { + 1: true, + 2: 3, + 'array': {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 0), { + 1: true, + 2: 3, + 'array': {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 1), { + 2: 3, + 'array': {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 1, count: 1), {2: 3}); + expect(await getFields(instanceRef, offset: 1, count: 2), { + 2: 3, + 'array': {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 2), { + 'array': {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 3), {}); + expect(await getFields(instanceRef, offset: 0, count: 0), {}); + expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); + expect( + await getFields(instanceRef, offset: 0, count: 2), + {1: true, 2: 3}, + ); + expect(await getFields(instanceRef, offset: 0, count: 5), { + 1: true, + 2: 3, + 'array': {'a': 1, 'b': 5}, + }); + expect(await getFields(instanceRef, offset: 3, count: 5), {}); + }); + }); + + test('complex records with named fields, field access', () async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + expect( + await getInstance(frame, r'record.$1'), + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), + ); + + expect( + await getInstance(frame, r'record.$2'), + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), + ); + + final third = await getInstanceRef(frame, r'record.array'); + expect(third.kind, InstanceKind.kMap); + expect(await getFields(third), {'a': 1, 'b': 5}); + }); + }); + + test('nested records display', () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + + final instanceRef = await getInstanceRef(frame, 'record'); + final classId = instanceRef.classRef!.id; + + expect(await getObject(classId), matchRecordClass); + + final stringRef = await getInstanceRef(frame, 'record.toString()'); + final stringId = stringRef.id!; + + expect( + await getObject(stringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(true, (false, 5))', + ), + ); + }); + }); + + test('nested records', () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record'); + + final instanceId = instanceRef.id!; + expect(instanceRef, matchRecordInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordInstance(length: 2)); + + expect(await getFields(instanceRef), { + 1: true, + 2: {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 0), { + 1: true, + 2: {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 1), { + 2: {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 2), {}); + expect(await getFields(instanceRef, offset: 0, count: 0), {}); + expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); + expect(await getFields(instanceRef, offset: 0, count: 2), { + 1: true, + 2: {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 0, count: 5), { + 1: true, + 2: {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 2, count: 5), {}); + }); + }); + + test('nested records, field access', () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, r'record.$2'); + + final instanceId = instanceRef.id!; + expect(instanceRef, matchRecordInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordInstance(length: 2)); + + expect(await getFields(instanceRef), {1: false, 2: 5}); + expect(await getFields(instanceRef, offset: 0), {1: false, 2: 5}); + }); + }); + + test('nested records with named fields display', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + + final instanceRef = await getInstanceRef(frame, 'record'); + final classId = instanceRef.classRef!.id; + + expect(await getObject(classId), matchRecordClass); + + final stringRef = await getInstanceRef(frame, 'record.toString()'); + final stringId = stringRef.id!; + + expect( + await getObject(stringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(true, inner: (false, 5))', + ), + ); + }); + }); + + test('nested records with named fields', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record'); + + final instanceId = instanceRef.id!; + expect(instanceRef, matchRecordInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordInstance(length: 2)); + + expect(await getFields(instanceRef), { + 1: true, + 'inner': {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 0), { + 1: true, + 'inner': {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 1), { + 'inner': {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 1, count: 1), { + 'inner': {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 1, count: 2), { + 'inner': {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 2), {}); + expect(await getFields(instanceRef, offset: 0, count: 0), {}); + expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); + expect(await getFields(instanceRef, offset: 0, count: 2), { + 1: true, + 'inner': {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 0, count: 5), { + 1: true, + 'inner': {1: false, 2: 5}, + }); + expect(await getFields(instanceRef, offset: 2, count: 5), {}); + }); + }); + + test('nested records with named fields, field access', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, r'record.inner'); + + final instanceId = instanceRef.id!; + expect(instanceRef, matchRecordInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordInstance(length: 2)); + + expect(await getFields(instanceRef), {1: false, 2: 5}); + expect(await getFields(instanceRef, offset: 0), {1: false, 2: 5}); + }); + }); + }); +} diff --git a/dwds/test/instances/common/record_type_inspection_common.dart b/dwds/test/instances/common/record_type_inspection_common.dart new file mode 100644 index 000000000..f39f7ddef --- /dev/null +++ b/dwds/test/instances/common/record_type_inspection_common.dart @@ -0,0 +1,350 @@ +// 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:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:test_common/test_sdk_configuration.dart'; +import 'package:vm_service/vm_service.dart'; + +import '../../fixtures/context.dart'; +import '../../fixtures/project.dart'; +import 'test_inspector.dart'; + +Future runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required bool debug, +}) async { + final context = + TestContext(TestProject.testExperimentWithSoundNullSafety, provider); + final testInspector = TestInspector(context); + + late VmServiceInterface service; + late Stream stream; + late String isolateId; + late ScriptRef mainScript; + + onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( + stream, + isolateId, + mainScript, + breakPointId, + body, + ); + + getObject(instanceId) => service.getObject(isolateId, instanceId); + + getInstanceRef(frame, expression) => + testInspector.getInstanceRef(isolateId, frame, expression); + + getDisplayedFields(InstanceRef ref) => + testInspector.getDisplayedFields(isolateId, ref); + + getElements(String instanceId) => + testInspector.getElements(isolateId, instanceId); + + group('$compilationMode |', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['records', 'patterns'], + ); + service = context.debugConnection.vmService; + + final vm = await service.getVM(); + isolateId = vm.isolates!.first.id!; + final scripts = await service.getScripts(isolateId); + + await service.streamListen('Debug'); + stream = service.onEvent('Debug'); + + mainScript = scripts.scripts! + .firstWhere((each) => each.uri!.contains('main.dart')); + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + setUp(() => setCurrentLogWriter(debug: debug)); + tearDown(() => service.resume(isolateId)); + + test('simple record type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordTypeInstance(length: 2)); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }); + + test('simple record type elements', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + expect( + await getElements(instanceId), + [matchTypeInstance('bool'), matchTypeInstance('int')], + ); + expect( + await getDisplayedFields(instanceRef), + ['bool', 'int'], + ); + }); + }); + + test('simple record type display', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final typeStringRef = + await getInstanceRef(frame, 'record.runtimeType.toString()'); + final typeStringId = typeStringRef.id!; + + expect( + await getObject(typeStringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(bool, int)', + ), + ); + }); + }); + + test('complex record type', () async { + await onBreakPoint('printComplexLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + expect(instanceRef, matchRecordTypeInstanceRef(length: 3)); + expect(await getObject(instanceId), matchRecordTypeInstance(length: 3)); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }); + + test('complex record type elements', () async { + await onBreakPoint('printComplexLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + expect( + await getElements(instanceId), + [ + matchTypeInstance('bool'), + matchTypeInstance('int'), + matchTypeInstance('IdentityMap'), + ], + ); + expect( + await getDisplayedFields(instanceRef), + ['bool', 'int', 'IdentityMap'], + ); + }); + }); + + test('complex record type display', () async { + await onBreakPoint('printComplexLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final typeStringRef = + await getInstanceRef(frame, 'record.runtimeType.toString()'); + final typeStringId = typeStringRef.id!; + + expect( + await getObject(typeStringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(bool, int, IdentityMap)', + ), + ); + }); + }); + + test('complex record type with named fields ', () async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + expect(instanceRef, matchRecordTypeInstanceRef(length: 3)); + expect(await getObject(instanceId), matchRecordTypeInstance(length: 3)); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }); + + test('complex record type with named fields elements', () async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + expect( + await getElements(instanceId), + [ + matchTypeInstance('bool'), + matchTypeInstance('int'), + matchTypeInstance('IdentityMap'), + ], + ); + + expect( + await getDisplayedFields(instanceRef), + ['bool', 'int', 'IdentityMap'], + ); + }); + }); + + test('complex record type with named fields display', () async { + await onBreakPoint('printComplexNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final typeStringRef = + await getInstanceRef(frame, 'record.runtimeType.toString()'); + final typeStringId = typeStringRef.id!; + + expect( + await getObject(typeStringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(bool, int, {IdentityMap array})', + ), + ); + }); + }); + + test('nested record type', () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + expect(await getObject(instanceId), matchRecordTypeInstance(length: 2)); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }); + + test('nested record type elements', () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + final elements = await getElements(instanceId); + expect( + elements, + [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], + ); + expect( + await getElements(elements[1].id!), + [matchTypeInstance('bool'), matchTypeInstance('int')], + ); + expect( + await getDisplayedFields(instanceRef), + ['bool', '(bool, int)'], + ); + expect( + await getDisplayedFields(elements[1]), + ['bool', 'int'], + ); + }); + }); + + test('nested record type display', () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final typeStringRef = + await getInstanceRef(frame, 'record.runtimeType.toString()'); + final typeStringId = typeStringRef.id!; + + expect( + await getObject(typeStringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(bool, (bool, int))', + ), + ); + }); + }); + + test('nested record type with named fields', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + expect(instance, matchRecordTypeInstance(length: 2)); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }); + + test('nested record type with named fields elements', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + final elements = await getElements(instanceId); + expect( + elements, + [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], + ); + expect( + await getElements(elements[1].id!), + [matchTypeInstance('bool'), matchTypeInstance('int')], + ); + expect( + await getDisplayedFields(instanceRef), + ['bool', '(bool, int)'], + ); + expect( + await getDisplayedFields(elements[1]), + ['bool', 'int'], + ); + }); + }); + + test('nested record type with named fields display', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instance = await getObject(instanceRef.id!); + final typeClassId = instance.classRef!.id; + + expect(await getObject(typeClassId), matchRecordTypeClass); + + final typeStringRef = + await getInstanceRef(frame, 'record.runtimeType.toString()'); + final typeStringId = typeStringRef.id!; + + expect( + await getObject(typeStringId), + matchPrimitiveInstance( + kind: InstanceKind.kString, + value: '(bool, {(bool, int) inner})', + ), + ); + }); + }); + }); +} diff --git a/dwds/test/instances/common/test_inspector.dart b/dwds/test/instances/common/test_inspector.dart index e79240ee9..3ce8e3694 100644 --- a/dwds/test/instances/common/test_inspector.dart +++ b/dwds/test/instances/common/test_inspector.dart @@ -2,9 +2,6 @@ // 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. -@TestOn('vm') -@Timeout(Duration(minutes: 2)) - import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; diff --git a/dwds/test/instances/common/type_inspection_common.dart b/dwds/test/instances/common/type_inspection_common.dart new file mode 100644 index 000000000..864cb9a56 --- /dev/null +++ b/dwds/test/instances/common/type_inspection_common.dart @@ -0,0 +1,226 @@ +// 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:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:test_common/test_sdk_configuration.dart'; +import 'package:vm_service/vm_service.dart'; + +import '../../fixtures/context.dart'; +import '../../fixtures/project.dart'; +import 'test_inspector.dart'; + +Future runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required bool debug, +}) async { + final context = + TestContext(TestProject.testExperimentWithSoundNullSafety, provider); + final testInspector = TestInspector(context); + + late VmServiceInterface service; + late Stream stream; + late String isolateId; + late ScriptRef mainScript; + + onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( + stream, + isolateId, + mainScript, + breakPointId, + body, + ); + + getObject(instanceId) => service.getObject(isolateId, instanceId); + + getDisplayedFields(instanceRef) => + testInspector.getDisplayedFields(isolateId, instanceRef); + + getInstanceRef(frame, expression) => + testInspector.getInstanceRef(isolateId, frame, expression); + + getFields(instanceRef, {offset, count, depth = -1}) => + testInspector.getFields( + isolateId, + instanceRef, + offset: offset, + count: count, + depth: depth, + ); + + getElements(String instanceId) => + testInspector.getElements(isolateId, instanceId); + + final matchTypeObject = { + 'hashCode': matchPrimitiveInstanceRef(kind: InstanceKind.kDouble), + 'runtimeType': matchTypeInstanceRef('Type'), + }; + + final matchDisplayedTypeObject = [ + matches('[0-9]*'), + 'Type', + ]; + + group('$compilationMode |', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['records', 'patterns'], + ); + service = context.debugConnection.vmService; + + final vm = await service.getVM(); + isolateId = vm.isolates!.first.id!; + final scripts = await service.getScripts(isolateId); + + await service.streamListen('Debug'); + stream = service.onEvent('Debug'); + + mainScript = scripts.scripts! + .firstWhere((each) => each.uri!.contains('main.dart')); + }); + + tearDownAll(() async { + await context.tearDown(); + }); + + setUp(() => setCurrentLogWriter(debug: debug)); + tearDown(() => service.resume(isolateId)); + + test('String type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, "'1'.runtimeType"); + expect(instanceRef, matchTypeInstanceRef('String')); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchTypeInstance('String')); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchTypeClass); + expect(await getFields(instanceRef, depth: 1), matchTypeObject); + expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); + }); + }); + + test('int type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, '1.runtimeType'); + expect(instanceRef, matchTypeInstanceRef('int')); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchTypeInstance('int')); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchTypeClass); + expect(await getFields(instanceRef, depth: 1), matchTypeObject); + expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); + }); + }); + + test('list type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, '[].runtimeType'); + expect(instanceRef, matchTypeInstanceRef('List')); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchTypeInstance('List')); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchTypeClass); + expect(await getFields(instanceRef, depth: 1), matchTypeObject); + expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); + }); + }); + + test('map type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = + await getInstanceRef(frame, '{}.runtimeType'); + expect(instanceRef, matchTypeInstanceRef('IdentityMap')); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchTypeInstance('IdentityMap')); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchTypeClass); + expect(await getFields(instanceRef, depth: 1), matchTypeObject); + expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); + }); + }); + + test('set type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, '{}.runtimeType'); + expect(instanceRef, matchTypeInstanceRef('_IdentityHashSet')); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchTypeInstance('_IdentityHashSet')); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchTypeClass); + expect(await getFields(instanceRef, depth: 1), matchTypeObject); + expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); + }); + }); + + test('record type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, "(0,'a').runtimeType"); + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchRecordTypeInstance(length: 2)); + expect( + await getElements(instanceId), + [matchTypeInstance('int'), matchTypeInstance('String')], + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + expect( + await getFields(instanceRef, depth: 2), + {1: matchTypeObject, 2: matchTypeObject}, + ); + expect( + await getDisplayedFields(instanceRef), + ['int', 'String'], + ); + }); + }); + + test('class type', () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = + await getInstanceRef(frame, "Uri.file('').runtimeType"); + expect(instanceRef, matchTypeInstanceRef('_Uri')); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchTypeInstance('_Uri')); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchTypeClass); + expect(await getFields(instanceRef, depth: 1), matchTypeObject); + expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); + }); + }); + }); +} diff --git a/dwds/test/instances/instance_inspection_test.dart b/dwds/test/instances/instance_inspection_test.dart index 1f18be834..ff8ddc422 100644 --- a/dwds/test/instances/instance_inspection_test.dart +++ b/dwds/test/instances/instance_inspection_test.dart @@ -7,13 +7,11 @@ @Timeout(Duration(minutes: 2)) import 'package:test/test.dart'; -import 'package:test_common/logging.dart'; import 'package:test_common/test_sdk_configuration.dart'; -import 'package:vm_service/vm_service.dart'; import '../fixtures/context.dart'; import '../fixtures/project.dart'; -import 'common/test_inspector.dart'; +import 'common/instance_inspection_common.dart'; void main() async { // Enable verbose logging for debugging. @@ -24,7 +22,7 @@ void main() async { for (var compilationMode in CompilationMode.values) { for (var nullSafetyMode in NullSafety.values) { - await _runTests( + await runTests( provider: provider, compilationMode: compilationMode, nullSafetyMode: nullSafetyMode, @@ -33,266 +31,3 @@ void main() async { } } } - -Future _runTests({ - required TestSdkConfigurationProvider provider, - required CompilationMode compilationMode, - required NullSafety nullSafetyMode, - required bool debug, -}) async { - final testPackage = nullSafetyMode == NullSafety.sound - ? TestProject.testPackageWithSoundNullSafety() - : TestProject.testPackageWithWeakNullSafety(); - final context = TestContext(testPackage, provider); - late VmServiceInterface service; - late Stream stream; - late String isolateId; - late ScriptRef mainScript; - - final testInspector = TestInspector(context); - - onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( - stream, - isolateId, - mainScript, - breakPointId, - body, - ); - - getInstance(frame, expression) => - testInspector.getInstance(isolateId, frame, expression); - - getObject(instanceId) => service.getObject(isolateId, instanceId); - - getInstanceRef(frame, expression) => - testInspector.getInstanceRef(isolateId, frame, expression); - - getFields(instanceRef, {offset, count}) => testInspector - .getFields(isolateId, instanceRef, offset: offset, count: count); - - group('$nullSafetyMode |', () { - group('$compilationMode |', () { - setUpAll(() async { - setCurrentLogWriter(debug: debug); - await context.setUp( - compilationMode: compilationMode, - enableExpressionEvaluation: true, - verboseCompiler: debug, - experiments: ['records'], - ); - service = context.debugConnection.vmService; - - final vm = await service.getVM(); - isolateId = vm.isolates!.first.id!; - final scripts = await service.getScripts(isolateId); - - await service.streamListen('Debug'); - stream = service.onEvent('Debug'); - - mainScript = scripts.scripts! - .firstWhere((each) => each.uri!.contains('main.dart')); - }); - - tearDownAll(context.tearDown); - - setUp(() => setCurrentLogWriter(debug: debug)); - tearDown(() => service.resume(isolateId)); - - group('Object |', () { - test('type and fields', () async { - await onBreakPoint('printFieldMain', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'instance'); - - final instanceId = instanceRef.id!; - expect( - await getObject(instanceId), - matchPlainInstance( - libraryId: 'org-dartlang-app:///web/main.dart', - type: 'MainClass', - ), - ); - - expect(await getFields(instanceRef), {'_field': 1, 'field': 2}); - - // Offsets and counts are ignored for plain object fields. - - // DevTools calls [VmServiceInterface.getObject] with offset=0 - // and count=0 and expects all fields to be returned. - expect( - await getFields(instanceRef, offset: 0, count: 0), - {'_field': 1, 'field': 2}, - ); - expect( - await getFields(instanceRef, offset: 0), - {'_field': 1, 'field': 2}, - ); - expect( - await getFields(instanceRef, offset: 0, count: 1), - {'_field': 1, 'field': 2}, - ); - expect( - await getFields(instanceRef, offset: 1), - {'_field': 1, 'field': 2}, - ); - expect( - await getFields(instanceRef, offset: 1, count: 0), - {'_field': 1, 'field': 2}, - ); - expect( - await getFields(instanceRef, offset: 1, count: 3), - {'_field': 1, 'field': 2}, - ); - }); - }); - - test('field access', () async { - await onBreakPoint('printFieldMain', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r'instance.field'), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 2), - ); - - expect( - await getInstance(frame, r'instance._field'), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), - ); - }); - }); - }); - - group('List |', () { - test('type and fields', () async { - await onBreakPoint('printList', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'list'); - - final instanceId = instanceRef.id!; - expect( - await getObject(instanceId), - matchListInstance(type: 'List'), - ); - - expect(await getFields(instanceRef), [0, 1, 2]); - expect(await getFields(instanceRef, offset: 1, count: 0), []); - expect(await getFields(instanceRef, offset: 0), [0, 1, 2]); - expect(await getFields(instanceRef, offset: 0, count: 1), [0]); - expect(await getFields(instanceRef, offset: 1), [1, 2]); - expect(await getFields(instanceRef, offset: 1, count: 1), [1]); - expect(await getFields(instanceRef, offset: 1, count: 3), [1, 2]); - expect(await getFields(instanceRef, offset: 3, count: 3), []); - }); - }); - - test('Element access', () async { - await onBreakPoint('printList', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r'list[0]'), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 0), - ); - - expect( - await getInstance(frame, r"list[1]"), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), - ); - - expect( - await getInstance(frame, r"list[2]"), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 2), - ); - }); - }); - }); - - group('Map |', () { - test('type and fields', () async { - await onBreakPoint('printMap', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'map'); - - final instanceId = instanceRef.id!; - expect( - await getObject(instanceId), - matchMapInstance(type: 'IdentityMap'), - ); - - expect(await getFields(instanceRef), {'a': 1, 'b': 2, 'c': 3}); - - expect(await getFields(instanceRef, offset: 1, count: 0), {}); - expect( - await getFields(instanceRef, offset: 0), - {'a': 1, 'b': 2, 'c': 3}, - ); - expect(await getFields(instanceRef, offset: 0, count: 1), {'a': 1}); - expect(await getFields(instanceRef, offset: 1), {'b': 2, 'c': 3}); - expect(await getFields(instanceRef, offset: 1, count: 1), {'b': 2}); - expect( - await getFields(instanceRef, offset: 1, count: 3), - {'b': 2, 'c': 3}, - ); - expect(await getFields(instanceRef, offset: 3, count: 3), {}); - }); - }); - - test('Element access', () async { - await onBreakPoint('printMap', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r"map['a']"), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), - ); - - expect( - await getInstance(frame, r"map['b']"), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 2), - ); - - expect( - await getInstance(frame, r"map['c']"), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), - ); - }); - }); - }); - - group('Set |', () { - test('type and fields', () async { - await onBreakPoint('printSet', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'mySet'); - - final instanceId = instanceRef.id!; - expect( - await getObject(instanceId), - matchSetInstance(type: '_HashSet'), - ); - - expect(await getFields(instanceRef), [1, 4, 5, 7]); - expect(await getFields(instanceRef, offset: 0), [1, 4, 5, 7]); - expect(await getFields(instanceRef, offset: 1, count: 2), [4, 5]); - expect(await getFields(instanceRef, offset: 2), [5, 7]); - expect(await getFields(instanceRef, offset: 2, count: 10), [5, 7]); - expect(await getFields(instanceRef, offset: 1, count: 0), []); - expect(await getFields(instanceRef, offset: 10, count: 2), []); - }); - }); - - test('Element access', () async { - await onBreakPoint('printSet', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r"mySet.first"), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), - ); - expect( - await getInstance(frame, r"mySet.last"), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 7), - ); - }); - }); - }); - }); - }); -} diff --git a/dwds/test/instances/instance_test.dart b/dwds/test/instances/instance_test.dart index 6a37ee94d..cbac97309 100644 --- a/dwds/test/instances/instance_test.dart +++ b/dwds/test/instances/instance_test.dart @@ -5,16 +5,11 @@ @Tags(['daily']) @Timeout(Duration(minutes: 2)) -import 'package:dwds/src/debugging/inspector.dart'; -import 'package:dwds/src/loaders/strategy.dart'; import 'package:test/test.dart'; -import 'package:test_common/logging.dart'; import 'package:test_common/test_sdk_configuration.dart'; -import 'package:vm_service/vm_service.dart'; -import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; import '../fixtures/context.dart'; -import '../fixtures/project.dart'; +import 'common/instance_common.dart'; void main() { // Enable verbose logging for debugging. @@ -24,332 +19,10 @@ void main() { tearDownAll(provider.dispose); for (var compilationMode in CompilationMode.values) { - _runTests( + runTests( provider: provider, compilationMode: compilationMode, debug: debug, ); } } - -void _runTests({ - required TestSdkConfigurationProvider provider, - required CompilationMode compilationMode, - required bool debug, -}) { - final context = - TestContext(TestProject.testScopesWithSoundNullSafety, provider); - - late AppInspector inspector; - - group('$compilationMode |', () { - setUpAll(() async { - setCurrentLogWriter(debug: debug); - await context.setUp(compilationMode: compilationMode); - final chromeProxyService = context.service; - inspector = chromeProxyService.inspector; - }); - - tearDownAll(() async { - await context.tearDown(); - }); - - final url = 'org-dartlang-app:///example/scopes/main.dart'; - - String libraryName(CompilationMode compilationMode) => - compilationMode == CompilationMode.frontendServer - ? "example/scopes/main.dart" - : "example/scopes/main"; - - String libraryVariableExpression( - String variable, - CompilationMode compilationMode, - ) => - '${globalLoadStrategy.loadModuleSnippet}("dart_sdk").dart.' - 'getModuleLibraries("${libraryName(compilationMode)}")' - '["$url"]["$variable"];'; - - String interceptorsNewExpression(String type) => - "require('dart_sdk')._interceptors.$type['_#new#tearOff']()"; - - /// A reference to the the variable `libraryPublicFinal`, an instance of - /// `MyTestClass`. - Future libraryPublicFinal(CompilationMode compilationMode) => - inspector.jsEvaluate( - libraryVariableExpression('libraryPublicFinal', compilationMode), - ); - - /// A reference to the the variable `libraryPublic`, a List of Strings. - Future libraryPublic(CompilationMode compilationMode) => - inspector.jsEvaluate( - libraryVariableExpression('libraryPublic', compilationMode), - ); - - group('instanceRef', () { - setUp(() => setCurrentLogWriter(debug: debug)); - - test('for a null', () async { - final remoteObject = await libraryPublicFinal(compilationMode); - final nullVariable = - await inspector.loadField(remoteObject, 'notFinal'); - final ref = await inspector.instanceRefFor(nullVariable); - expect(ref!.valueAsString, 'null'); - expect(ref.kind, InstanceKind.kNull); - final classRef = ref.classRef!; - expect(classRef.name, 'Null'); - expect(classRef.id, 'classes|dart:core|Null'); - expect(inspector.isDisplayableObject(ref), isTrue); - }); - - test('for a double', () async { - final remoteObject = await libraryPublicFinal(compilationMode); - final count = await inspector.loadField(remoteObject, 'count'); - final ref = await inspector.instanceRefFor(count); - expect(ref!.valueAsString, '0'); - expect(ref.kind, InstanceKind.kDouble); - final classRef = ref.classRef!; - expect(classRef.name, 'Double'); - expect(classRef.id, 'classes|dart:core|Double'); - expect(inspector.isDisplayableObject(ref), isTrue); - }); - - test('for a class', () async { - final remoteObject = await libraryPublicFinal(compilationMode); - final count = await inspector.loadField(remoteObject, 'myselfField'); - final ref = await inspector.instanceRefFor(count); - expect(ref!.kind, InstanceKind.kPlainInstance); - final classRef = ref.classRef!; - expect(classRef.name, 'MyTestClass'); - expect( - classRef.id, - 'classes|org-dartlang-app:///example/scopes/main.dart' - '|MyTestClass'); - expect(inspector.isDisplayableObject(ref), isTrue); - }); - - test('for closure', () async { - final remoteObject = await libraryPublicFinal(compilationMode); - final properties = - await inspector.getProperties(remoteObject.objectId!); - final closure = - properties.firstWhere((property) => property.name == 'closure'); - final ref = await inspector.instanceRefFor(closure.value!); - final functionName = ref!.closureFunction!.name; - // Older SDKs do not contain function names - if (functionName != 'Closure') { - expect(functionName, 'someFunction'); - } - expect(ref.kind, InstanceKind.kClosure); - expect(inspector.isDisplayableObject(ref), isTrue); - }); - - test('for a list', () async { - final remoteObject = await libraryPublic(compilationMode); - final ref = await inspector.instanceRefFor(remoteObject); - expect(ref!.length, greaterThan(0)); - expect(ref.kind, InstanceKind.kList); - expect(ref.classRef!.name, 'List'); - expect(inspector.isDisplayableObject(ref), isTrue); - }); - - test('for map', () async { - final remoteObject = await inspector - .jsEvaluate(libraryVariableExpression('map', compilationMode)); - final ref = await inspector.instanceRefFor(remoteObject); - expect(ref!.length, 2); - expect(ref.kind, InstanceKind.kMap); - expect(ref.classRef!.name, 'LinkedMap'); - expect(inspector.isDisplayableObject(ref), isTrue); - }); - - test('for an IdentityMap', () async { - final remoteObject = await inspector.jsEvaluate( - libraryVariableExpression('identityMap', compilationMode), - ); - final ref = await inspector.instanceRefFor(remoteObject); - expect(ref!.length, 2); - expect(ref.kind, InstanceKind.kMap); - expect(ref.classRef!.name, 'IdentityMap'); - expect(inspector.isDisplayableObject(ref), isTrue); - }); - - test('for a native JavaScript error', () async { - final remoteObject = await inspector - .jsEvaluate(interceptorsNewExpression('NativeError')); - final ref = await inspector.instanceRefFor(remoteObject); - expect(ref!.kind, InstanceKind.kPlainInstance); - expect(ref.classRef!.name, 'NativeError'); - expect(inspector.isDisplayableObject(ref), isFalse); - expect(inspector.isNativeJsError(ref), isTrue); - expect(inspector.isNativeJsObject(ref), isFalse); - }); - - test('for a native JavaScript type error', () async { - final remoteObject = await inspector - .jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError')); - final ref = await inspector.instanceRefFor(remoteObject); - expect(ref!.kind, InstanceKind.kPlainInstance); - expect(ref.classRef!.name, 'JSNoSuchMethodError'); - expect(inspector.isDisplayableObject(ref), isFalse); - expect(inspector.isNativeJsError(ref), isTrue); - expect(inspector.isNativeJsObject(ref), isFalse); - }); - - test('for a native JavaScript object', () async { - final remoteObject = await inspector - .jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject')); - final ref = await inspector.instanceRefFor(remoteObject); - expect(ref!.kind, InstanceKind.kPlainInstance); - expect(ref.classRef!.name, 'LegacyJavaScriptObject'); - expect(inspector.isDisplayableObject(ref), isFalse); - expect(inspector.isNativeJsError(ref), isFalse); - expect(inspector.isNativeJsObject(ref), isTrue); - }); - }); - - group('instance', () { - setUp(() => setCurrentLogWriter(debug: debug)); - test('for class object', () async { - final remoteObject = await libraryPublicFinal(compilationMode); - final instance = await inspector.instanceFor(remoteObject); - expect(instance!.kind, InstanceKind.kPlainInstance); - final classRef = instance.classRef!; - expect(classRef, isNotNull); - expect(classRef.name, 'MyTestClass'); - final boundFieldNames = instance.fields! - .map((boundField) => boundField.decl!.name) - .toList(); - expect(boundFieldNames, [ - '_privateField', - 'abstractField', - 'closure', - 'count', - 'message', - 'myselfField', - 'notFinal', - 'tornOff', - ]); - final fieldNames = - instance.fields!.map((boundField) => boundField.name).toList(); - expect(boundFieldNames, fieldNames); - for (var field in instance.fields!) { - expect(field.name, isNotNull); - expect(field.decl!.declaredType, isNotNull); - } - expect(inspector.isDisplayableObject(instance), isTrue); - }); - - test('for closure', () async { - final remoteObject = await libraryPublicFinal(compilationMode); - final properties = - await inspector.getProperties(remoteObject.objectId!); - final closure = - properties.firstWhere((property) => property.name == 'closure'); - final instance = await inspector.instanceFor(closure.value!); - expect(instance!.kind, InstanceKind.kClosure); - expect(instance.classRef!.name, 'Closure'); - expect(inspector.isDisplayableObject(instance), isTrue); - }); - - test('for a nested class', () async { - final libraryRemoteObject = await libraryPublicFinal(compilationMode); - final fieldRemoteObject = - await inspector.loadField(libraryRemoteObject, 'myselfField'); - final instance = await inspector.instanceFor(fieldRemoteObject); - expect(instance!.kind, InstanceKind.kPlainInstance); - final classRef = instance.classRef!; - expect(classRef, isNotNull); - expect(classRef.name, 'MyTestClass'); - expect(inspector.isDisplayableObject(instance), isTrue); - }); - - test('for a list', () async { - final remote = await libraryPublic(compilationMode); - final instance = await inspector.instanceFor(remote); - expect(instance!.kind, InstanceKind.kList); - final classRef = instance.classRef!; - expect(classRef, isNotNull); - expect(classRef.name, 'List'); - final first = instance.elements![0]; - expect(first.valueAsString, 'library'); - expect(inspector.isDisplayableObject(instance), isTrue); - }); - - test('for a map', () async { - final remote = await inspector - .jsEvaluate(libraryVariableExpression('map', compilationMode)); - final instance = await inspector.instanceFor(remote); - expect(instance!.kind, InstanceKind.kMap); - final classRef = instance.classRef!; - expect(classRef.name, 'LinkedMap'); - final first = instance.associations![0].value as InstanceRef; - expect(first.kind, InstanceKind.kList); - expect(first.length, 3); - final second = instance.associations![1].value as InstanceRef; - expect(second.kind, InstanceKind.kString); - expect(second.valueAsString, 'something'); - expect(inspector.isDisplayableObject(instance), isTrue); - }); - - test('for an identityMap', () async { - final remote = await inspector.jsEvaluate( - libraryVariableExpression('identityMap', compilationMode), - ); - final instance = await inspector.instanceFor(remote); - expect(instance!.kind, InstanceKind.kMap); - final classRef = instance.classRef!; - expect(classRef.name, 'IdentityMap'); - final first = instance.associations![0].value; - expect(first.valueAsString, '1'); - expect(inspector.isDisplayableObject(instance), isTrue); - }); - - test('for a class that implements List', () async { - // The VM only uses kind List for SDK lists, and we follow that. - final remote = await inspector - .jsEvaluate(libraryVariableExpression('notAList', compilationMode)); - final instance = await inspector.instanceFor(remote); - expect(instance!.kind, InstanceKind.kPlainInstance); - final classRef = instance.classRef!; - expect(classRef.name, 'NotReallyAList'); - expect(instance.elements, isNull); - final field = instance.fields!.first; - expect(field.decl!.name, '_internal'); - expect(inspector.isDisplayableObject(instance), isTrue); - }); - - test('for a native JavaScript error', () async { - final remoteObject = await inspector - .jsEvaluate(interceptorsNewExpression('NativeError')); - final instance = await inspector.instanceFor(remoteObject); - expect(instance!.kind, InstanceKind.kPlainInstance); - expect(instance.classRef!.name, 'NativeError'); - expect(inspector.isDisplayableObject(instance), isFalse); - expect(inspector.isNativeJsError(instance), isTrue); - expect(inspector.isNativeJsObject(instance), isFalse); - }); - - test('for a native JavaScript type error', () async { - final remoteObject = await inspector - .jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError')); - final instance = await inspector.instanceFor(remoteObject); - expect(instance!.kind, InstanceKind.kPlainInstance); - expect(instance.classRef!.name, 'JSNoSuchMethodError'); - expect(inspector.isDisplayableObject(instance), isFalse); - expect(inspector.isNativeJsError(instance), isTrue); - expect(inspector.isNativeJsObject(instance), isFalse); - }); - - test('for a native JavaScript object', () async { - final remoteObject = await inspector - .jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject')); - final instance = await inspector.instanceFor(remoteObject); - expect(instance!.kind, InstanceKind.kPlainInstance); - expect(instance.classRef!.name, 'LegacyJavaScriptObject'); - expect(inspector.isDisplayableObject(instance), isFalse); - expect(inspector.isNativeJsError(instance), isFalse); - expect(inspector.isNativeJsObject(instance), isTrue); - }); - }); - }); -} diff --git a/dwds/test/instances/patterns_inspection_test.dart b/dwds/test/instances/patterns_inspection_test.dart index 9b972366e..a47d51bb7 100644 --- a/dwds/test/instances/patterns_inspection_test.dart +++ b/dwds/test/instances/patterns_inspection_test.dart @@ -6,17 +6,11 @@ @TestOn('vm') @Timeout(Duration(minutes: 2)) -import 'dart:io'; - -import 'package:pub_semver/pub_semver.dart' as semver; import 'package:test/test.dart'; -import 'package:test_common/logging.dart'; import 'package:test_common/test_sdk_configuration.dart'; -import 'package:vm_service/vm_service.dart'; import '../fixtures/context.dart'; -import '../fixtures/project.dart'; -import 'common/test_inspector.dart'; +import 'common/patterns_inspection_common.dart'; void main() async { // Enable verbose logging for debugging. @@ -26,165 +20,10 @@ void main() async { tearDownAll(provider.dispose); for (var compilationMode in CompilationMode.values) { - await _runTests( + await runTests( provider: provider, compilationMode: compilationMode, debug: debug, ); } } - -Future _runTests({ - required TestSdkConfigurationProvider provider, - required CompilationMode compilationMode, - required bool debug, -}) async { - final context = - TestContext(TestProject.testExperimentWithSoundNullSafety, provider); - final testInspector = TestInspector(context); - - late VmServiceInterface service; - late Stream stream; - late String isolateId; - late ScriptRef mainScript; - - onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( - stream, - isolateId, - mainScript, - breakPointId, - body, - ); - - getInstanceRef(frame, expression) => - testInspector.getInstanceRef(isolateId, frame, expression); - - getFields(instanceRef, {offset, count}) => testInspector - .getFields(isolateId, instanceRef, offset: offset, count: count); - - getFrameVariables(Frame frame) => - testInspector.getFrameVariables(isolateId, frame); - - group( - '$compilationMode |', - () { - setUpAll(() async { - setCurrentLogWriter(debug: debug); - await context.setUp( - compilationMode: compilationMode, - enableExpressionEvaluation: true, - verboseCompiler: debug, - experiments: ['records', 'patterns'], - ); - service = context.debugConnection.vmService; - - final vm = await service.getVM(); - isolateId = vm.isolates!.first.id!; - final scripts = await service.getScripts(isolateId); - - await service.streamListen('Debug'); - stream = service.onEvent('Debug'); - - mainScript = scripts.scripts! - .firstWhere((each) => each.uri!.contains('main.dart')); - }); - - tearDownAll(() async { - await context.tearDown(); - }); - - setUp(() => setCurrentLogWriter(debug: debug)); - tearDown(() => service.resume(isolateId)); - - test('pattern match case 1', () async { - await onBreakPoint('testPatternCase1', (event) async { - final frame = event.topFrame!; - - expect(await getFrameVariables(frame), { - 'obj': matchListInstance(type: 'List'), - 'a': matchPrimitiveInstance(kind: InstanceKind.kString, value: 'a'), - 'n': matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 1), - }); - }); - }); - - test('pattern match case 2', () async { - await onBreakPoint('testPatternCase2', (event) async { - final frame = event.topFrame!; - - expect(await getFrameVariables(frame), { - 'obj': matchListInstance(type: 'List'), - 'a': matchPrimitiveInstance(kind: InstanceKind.kString, value: 'b'), - 'n': - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3.14), - }); - }); - }); - - test('pattern match default case', () async { - await onBreakPoint('testPatternDefault', (event) async { - final frame = event.topFrame!; - final frameIndex = frame.index!; - final instanceRef = await getInstanceRef(frameIndex, 'obj'); - expect(await getFields(instanceRef), [0, 1]); - - expect(await getFrameVariables(frame), { - 'obj': matchListInstance(type: 'List'), - }); - }); - }); - - test('stepping through pattern match', () async { - await onBreakPoint('callTestPattern1', (Event event) async { - var previousLocation = event.topFrame!.location; - for (var step in [ - // Make sure we step into the callee. - for (var i = 0; i < 4; i++) 'Into', - // Make a few steps inside the callee. - for (var i = 0; i < 4; i++) 'Over', - ]) { - await service.resume(isolateId, step: step); - - event = await stream - .firstWhere((e) => e.kind == EventKind.kPauseInterrupted); - - if (step == 'Over') { - expect(event.topFrame!.code!.name, 'testPattern'); - } - - final location = event.topFrame!.location; - expect(location, isNot(equals(previousLocation))); - previousLocation = location; - } - }); - }); - - test('before instantiation of pattern-matching variables', () async { - await onBreakPoint('testPattern2Case1', (event) async { - final frame = event.topFrame!; - - expect( - await getFrameVariables(frame), - {'dog': matchPrimitiveInstance(kind: 'String', value: 'Prismo')}, - ); - }); - }); - - test('after instantiation of pattern-matching variables', () async { - await onBreakPoint('testPattern2Case2', (event) async { - final frame = event.topFrame!; - - expect(await getFrameVariables(frame), { - 'dog': matchPrimitiveInstance(kind: 'String', value: 'Prismo'), - 'cats': matchListInstance(type: 'List'), - 'firstCat': - matchPrimitiveInstance(kind: 'String', value: 'Garfield'), - 'secondCat': matchPrimitiveInstance(kind: 'String', value: 'Tom'), - }); - }); - }); - }, // TODO(annagrin): Remove when dart 3.0 is stable. - skip: semver.Version.parse(Platform.version.split(' ')[0]) < - semver.Version.parse('3.0.0-351.0.dev'), - ); -} diff --git a/dwds/test/instances/record_inspection_test.dart b/dwds/test/instances/record_inspection_test.dart index a9f26ae01..f80541e90 100644 --- a/dwds/test/instances/record_inspection_test.dart +++ b/dwds/test/instances/record_inspection_test.dart @@ -7,13 +7,10 @@ @Timeout(Duration(minutes: 2)) import 'package:test/test.dart'; -import 'package:test_common/logging.dart'; import 'package:test_common/test_sdk_configuration.dart'; -import 'package:vm_service/vm_service.dart'; import '../fixtures/context.dart'; -import '../fixtures/project.dart'; -import 'common/test_inspector.dart'; +import 'common/record_inspection_common.dart'; void main() async { // Enable verbose logging for debugging. @@ -23,535 +20,10 @@ void main() async { tearDownAll(provider.dispose); for (var compilationMode in CompilationMode.values) { - await _runTests( + await runTests( provider: provider, compilationMode: compilationMode, debug: debug, ); } } - -Future _runTests({ - required TestSdkConfigurationProvider provider, - required CompilationMode compilationMode, - required bool debug, -}) async { - final context = - TestContext(TestProject.testExperimentWithSoundNullSafety, provider); - final testInspector = TestInspector(context); - - late VmServiceInterface service; - late Stream stream; - late String isolateId; - late ScriptRef mainScript; - - onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( - stream, - isolateId, - mainScript, - breakPointId, - body, - ); - - getInstance(frame, expression) => - testInspector.getInstance(isolateId, frame, expression); - - getObject(instanceId) => service.getObject(isolateId, instanceId); - - getInstanceRef(frame, expression) => - testInspector.getInstanceRef(isolateId, frame, expression); - - getFields(instanceRef, {offset, count, depth = -1}) => - testInspector.getFields( - isolateId, - instanceRef, - offset: offset, - count: count, - depth: depth, - ); - - group('$compilationMode |', () { - setUpAll(() async { - setCurrentLogWriter(debug: debug); - await context.setUp( - compilationMode: compilationMode, - enableExpressionEvaluation: true, - verboseCompiler: debug, - experiments: ['records', 'patterns'], - ); - service = context.debugConnection.vmService; - - final vm = await service.getVM(); - isolateId = vm.isolates!.first.id!; - final scripts = await service.getScripts(isolateId); - - await service.streamListen('Debug'); - stream = service.onEvent('Debug'); - - mainScript = scripts.scripts! - .firstWhere((each) => each.uri!.contains('main.dart')); - }); - - tearDownAll(() async { - await context.tearDown(); - }); - - setUp(() => setCurrentLogWriter(debug: debug)); - tearDown(() => service.resume(isolateId)); - - test('simple record display', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - - final instanceRef = await getInstanceRef(frame, 'record'); - final classId = instanceRef.classRef!.id; - - expect(await getObject(classId), matchRecordClass); - - final stringRef = await getInstanceRef(frame, 'record.toString()'); - final stringRefId = stringRef.id!; - - expect( - await getObject(stringRefId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(true, 3)', - ), - ); - }); - }); - - test('simple records', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record'); - final instanceId = instanceRef.id!; - - expect(instanceRef, matchRecordInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordInstance(length: 2)); - - expect(await getFields(instanceRef), {1: true, 2: 3}); - expect(await getFields(instanceRef, offset: 0), {1: true, 2: 3}); - expect(await getFields(instanceRef, offset: 1), {2: 3}); - expect(await getFields(instanceRef, offset: 2), {}); - expect(await getFields(instanceRef, offset: 0, count: 0), {}); - expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); - expect( - await getFields(instanceRef, offset: 0, count: 2), - {1: true, 2: 3}, - ); - expect( - await getFields(instanceRef, offset: 0, count: 5), - {1: true, 2: 3}, - ); - expect(await getFields(instanceRef, offset: 2, count: 5), {}); - }); - }); - - test('simple records, field access', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r'record.$1'), - matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), - ); - - expect( - await getInstance(frame, r'record.$2'), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), - ); - }); - }); - - test('simple records with named fields display', () async { - await onBreakPoint('printSimpleNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - - final instanceRef = await getInstanceRef(frame, 'record'); - final classId = instanceRef.classRef!.id; - - expect(await getObject(classId), matchRecordClass); - - final stringRef = await getInstanceRef(frame, 'record.toString()'); - final stringId = stringRef.id!; - - expect( - await getObject(stringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(true, cat: Vasya)', - ), - ); - }); - }); - - test('simple records with named fields', () async { - await onBreakPoint('printSimpleNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record'); - - final instanceId = instanceRef.id!; - expect(instanceRef, matchRecordInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordInstance(length: 2)); - - expect(await getFields(instanceRef), {1: true, 'cat': 'Vasya'}); - expect( - await getFields(instanceRef, offset: 0), - {1: true, 'cat': 'Vasya'}, - ); - expect(await getFields(instanceRef, offset: 1), {'cat': 'Vasya'}); - expect(await getFields(instanceRef, offset: 2), {}); - expect(await getFields(instanceRef, offset: 0, count: 0), {}); - expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); - expect( - await getFields(instanceRef, offset: 0, count: 2), - {1: true, 'cat': 'Vasya'}, - ); - expect( - await getFields(instanceRef, offset: 0, count: 5), - {1: true, 'cat': 'Vasya'}, - ); - expect(await getFields(instanceRef, offset: 2, count: 5), {}); - }); - }); - - test('simple records with named fields, field access', () async { - await onBreakPoint('printSimpleNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r'record.$1'), - matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), - ); - - expect( - await getInstance(frame, r'record.cat'), - matchPrimitiveInstance(kind: InstanceKind.kString, value: 'Vasya'), - ); - }); - }); - - test('complex records display', () async { - await onBreakPoint('printComplexLocalRecord', (event) async { - final frame = event.topFrame!.index!; - - final instanceRef = await getInstanceRef(frame, 'record'); - final classId = instanceRef.classRef!.id; - - expect(await getObject(classId), matchRecordClass); - - final stringRef = await getInstanceRef(frame, 'record.toString()'); - final stringId = stringRef.id!; - - expect( - await getObject(stringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(true, 3, {a: 1, b: 5})', - ), - ); - }); - }); - - test('complex records', () async { - await onBreakPoint('printComplexLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record'); - - final instanceId = instanceRef.id!; - expect(instanceRef, matchRecordInstanceRef(length: 3)); - expect(await getObject(instanceId), matchRecordInstance(length: 3)); - - expect(await getFields(instanceRef), { - 1: true, - 2: 3, - 3: {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 0), { - 1: true, - 2: 3, - 3: {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 1), { - 2: 3, - 3: {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 1, count: 1), {2: 3}); - expect(await getFields(instanceRef, offset: 1, count: 2), { - 2: 3, - 3: {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 2), { - 3: {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 3), {}); - expect(await getFields(instanceRef, offset: 0, count: 0), {}); - expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); - expect( - await getFields(instanceRef, offset: 0, count: 2), - {1: true, 2: 3}, - ); - expect(await getFields(instanceRef, offset: 0, count: 5), { - 1: true, - 2: 3, - 3: {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 3, count: 5), {}); - }); - }); - - test('complex records, field access', () async { - await onBreakPoint('printComplexLocalRecord', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r'record.$1'), - matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), - ); - - expect( - await getInstance(frame, r'record.$2'), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), - ); - - final third = await getInstanceRef(frame, r'record.$3'); - expect(third.kind, InstanceKind.kMap); - expect(await getFields(third), {'a': 1, 'b': 5}); - }); - }); - - test('complex records with named fields display', () async { - await onBreakPoint('printComplexNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - - final instanceRef = await getInstanceRef(frame, 'record'); - final classId = instanceRef.classRef!.id; - - expect(await getObject(classId), matchRecordClass); - - final stringRef = await getInstanceRef(frame, 'record.toString()'); - final stringId = stringRef.id!; - - expect( - await getObject(stringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(true, 3, array: {a: 1, b: 5})', - ), - ); - }); - }); - - test('complex records with named fields', () async { - await onBreakPoint('printComplexNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record'); - - final instanceId = instanceRef.id!; - expect(instanceRef, matchRecordInstanceRef(length: 3)); - expect(await getObject(instanceId), matchRecordInstance(length: 3)); - - expect(await getFields(instanceRef), { - 1: true, - 2: 3, - 'array': {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 0), { - 1: true, - 2: 3, - 'array': {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 1), { - 2: 3, - 'array': {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 1, count: 1), {2: 3}); - expect(await getFields(instanceRef, offset: 1, count: 2), { - 2: 3, - 'array': {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 2), { - 'array': {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 3), {}); - expect(await getFields(instanceRef, offset: 0, count: 0), {}); - expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); - expect( - await getFields(instanceRef, offset: 0, count: 2), - {1: true, 2: 3}, - ); - expect(await getFields(instanceRef, offset: 0, count: 5), { - 1: true, - 2: 3, - 'array': {'a': 1, 'b': 5}, - }); - expect(await getFields(instanceRef, offset: 3, count: 5), {}); - }); - }); - - test('complex records with named fields, field access', () async { - await onBreakPoint('printComplexNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - expect( - await getInstance(frame, r'record.$1'), - matchPrimitiveInstance(kind: InstanceKind.kBool, value: true), - ); - - expect( - await getInstance(frame, r'record.$2'), - matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3), - ); - - final third = await getInstanceRef(frame, r'record.array'); - expect(third.kind, InstanceKind.kMap); - expect(await getFields(third), {'a': 1, 'b': 5}); - }); - }); - - test('nested records display', () async { - await onBreakPoint('printNestedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - - final instanceRef = await getInstanceRef(frame, 'record'); - final classId = instanceRef.classRef!.id; - - expect(await getObject(classId), matchRecordClass); - - final stringRef = await getInstanceRef(frame, 'record.toString()'); - final stringId = stringRef.id!; - - expect( - await getObject(stringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(true, (false, 5))', - ), - ); - }); - }); - - test('nested records', () async { - await onBreakPoint('printNestedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record'); - - final instanceId = instanceRef.id!; - expect(instanceRef, matchRecordInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordInstance(length: 2)); - - expect(await getFields(instanceRef), { - 1: true, - 2: {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 0), { - 1: true, - 2: {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 1), { - 2: {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 2), {}); - expect(await getFields(instanceRef, offset: 0, count: 0), {}); - expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); - expect(await getFields(instanceRef, offset: 0, count: 2), { - 1: true, - 2: {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 0, count: 5), { - 1: true, - 2: {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 2, count: 5), {}); - }); - }); - - test('nested records, field access', () async { - await onBreakPoint('printNestedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, r'record.$2'); - - final instanceId = instanceRef.id!; - expect(instanceRef, matchRecordInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordInstance(length: 2)); - - expect(await getFields(instanceRef), {1: false, 2: 5}); - expect(await getFields(instanceRef, offset: 0), {1: false, 2: 5}); - }); - }); - - test('nested records with named fields display', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - - final instanceRef = await getInstanceRef(frame, 'record'); - final classId = instanceRef.classRef!.id; - - expect(await getObject(classId), matchRecordClass); - - final stringRef = await getInstanceRef(frame, 'record.toString()'); - final stringId = stringRef.id!; - - expect( - await getObject(stringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(true, inner: (false, 5))', - ), - ); - }); - }); - - test('nested records with named fields', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record'); - - final instanceId = instanceRef.id!; - expect(instanceRef, matchRecordInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordInstance(length: 2)); - - expect(await getFields(instanceRef), { - 1: true, - 'inner': {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 0), { - 1: true, - 'inner': {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 1), { - 'inner': {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 1, count: 1), { - 'inner': {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 1, count: 2), { - 'inner': {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 2), {}); - expect(await getFields(instanceRef, offset: 0, count: 0), {}); - expect(await getFields(instanceRef, offset: 0, count: 1), {1: true}); - expect(await getFields(instanceRef, offset: 0, count: 2), { - 1: true, - 'inner': {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 0, count: 5), { - 1: true, - 'inner': {1: false, 2: 5}, - }); - expect(await getFields(instanceRef, offset: 2, count: 5), {}); - }); - }); - - test('nested records with named fields, field access', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, r'record.inner'); - - final instanceId = instanceRef.id!; - expect(instanceRef, matchRecordInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordInstance(length: 2)); - - expect(await getFields(instanceRef), {1: false, 2: 5}); - expect(await getFields(instanceRef, offset: 0), {1: false, 2: 5}); - }); - }); - }); -} diff --git a/dwds/test/instances/record_type_inspection_test.dart b/dwds/test/instances/record_type_inspection_test.dart index 09a4b2140..74c8dede4 100644 --- a/dwds/test/instances/record_type_inspection_test.dart +++ b/dwds/test/instances/record_type_inspection_test.dart @@ -7,13 +7,10 @@ @Timeout(Duration(minutes: 2)) import 'package:test/test.dart'; -import 'package:test_common/logging.dart'; import 'package:test_common/test_sdk_configuration.dart'; -import 'package:vm_service/vm_service.dart'; import '../fixtures/context.dart'; -import '../fixtures/project.dart'; -import 'common/test_inspector.dart'; +import 'common/record_type_inspection_common.dart'; void main() async { // Enable verbose logging for debugging. @@ -23,348 +20,10 @@ void main() async { tearDownAll(provider.dispose); for (var compilationMode in CompilationMode.values) { - await _runTests( + await runTests( provider: provider, compilationMode: compilationMode, debug: debug, ); } } - -Future _runTests({ - required TestSdkConfigurationProvider provider, - required CompilationMode compilationMode, - required bool debug, -}) async { - final context = - TestContext(TestProject.testExperimentWithSoundNullSafety, provider); - final testInspector = TestInspector(context); - - late VmServiceInterface service; - late Stream stream; - late String isolateId; - late ScriptRef mainScript; - - onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( - stream, - isolateId, - mainScript, - breakPointId, - body, - ); - - getObject(instanceId) => service.getObject(isolateId, instanceId); - - getInstanceRef(frame, expression) => - testInspector.getInstanceRef(isolateId, frame, expression); - - getDisplayedFields(InstanceRef ref) => - testInspector.getDisplayedFields(isolateId, ref); - - getElements(String instanceId) => - testInspector.getElements(isolateId, instanceId); - - group('$compilationMode |', () { - setUpAll(() async { - setCurrentLogWriter(debug: debug); - await context.setUp( - compilationMode: compilationMode, - enableExpressionEvaluation: true, - verboseCompiler: debug, - experiments: ['records', 'patterns'], - ); - service = context.debugConnection.vmService; - - final vm = await service.getVM(); - isolateId = vm.isolates!.first.id!; - final scripts = await service.getScripts(isolateId); - - await service.streamListen('Debug'); - stream = service.onEvent('Debug'); - - mainScript = scripts.scripts! - .firstWhere((each) => each.uri!.contains('main.dart')); - }); - - tearDownAll(() async { - await context.tearDown(); - }); - - setUp(() => setCurrentLogWriter(debug: debug)); - tearDown(() => service.resume(isolateId)); - - test('simple record type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordTypeInstance(length: 2)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); - - test('simple record type elements', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - expect( - await getElements(instanceId), - [matchTypeInstance('bool'), matchTypeInstance('int')], - ); - expect( - await getDisplayedFields(instanceRef), - ['bool', 'int'], - ); - }); - }); - - test('simple record type display', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final typeStringRef = - await getInstanceRef(frame, 'record.runtimeType.toString()'); - final typeStringId = typeStringRef.id!; - - expect( - await getObject(typeStringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(bool, int)', - ), - ); - }); - }); - - test('complex record type', () async { - await onBreakPoint('printComplexLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - expect(instanceRef, matchRecordTypeInstanceRef(length: 3)); - expect(await getObject(instanceId), matchRecordTypeInstance(length: 3)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); - - test('complex record type elements', () async { - await onBreakPoint('printComplexLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - expect( - await getElements(instanceId), - [ - matchTypeInstance('bool'), - matchTypeInstance('int'), - matchTypeInstance('IdentityMap'), - ], - ); - expect( - await getDisplayedFields(instanceRef), - ['bool', 'int', 'IdentityMap'], - ); - }); - }); - - test('complex record type display', () async { - await onBreakPoint('printComplexLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final typeStringRef = - await getInstanceRef(frame, 'record.runtimeType.toString()'); - final typeStringId = typeStringRef.id!; - - expect( - await getObject(typeStringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(bool, int, IdentityMap)', - ), - ); - }); - }); - - test('complex record type with named fields ', () async { - await onBreakPoint('printComplexNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - expect(instanceRef, matchRecordTypeInstanceRef(length: 3)); - expect(await getObject(instanceId), matchRecordTypeInstance(length: 3)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); - - test('complex record type with named fields elements', () async { - await onBreakPoint('printComplexNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - expect( - await getElements(instanceId), - [ - matchTypeInstance('bool'), - matchTypeInstance('int'), - matchTypeInstance('IdentityMap'), - ], - ); - - expect( - await getDisplayedFields(instanceRef), - ['bool', 'int', 'IdentityMap'], - ); - }); - }); - - test('complex record type with named fields display', () async { - await onBreakPoint('printComplexNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final typeStringRef = - await getInstanceRef(frame, 'record.runtimeType.toString()'); - final typeStringId = typeStringRef.id!; - - expect( - await getObject(typeStringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(bool, int, {IdentityMap array})', - ), - ); - }); - }); - - test('nested record type', () async { - await onBreakPoint('printNestedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordTypeInstance(length: 2)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); - - test('nested record type elements', () async { - await onBreakPoint('printNestedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - final elements = await getElements(instanceId); - expect( - elements, - [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], - ); - expect( - await getElements(elements[1].id!), - [matchTypeInstance('bool'), matchTypeInstance('int')], - ); - expect( - await getDisplayedFields(instanceRef), - ['bool', '(bool, int)'], - ); - expect( - await getDisplayedFields(elements[1]), - ['bool', 'int'], - ); - }); - }); - - test('nested record type display', () async { - await onBreakPoint('printNestedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final typeStringRef = - await getInstanceRef(frame, 'record.runtimeType.toString()'); - final typeStringId = typeStringRef.id!; - - expect( - await getObject(typeStringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(bool, (bool, int))', - ), - ); - }); - }); - - test('nested record type with named fields', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - expect(instance, matchRecordTypeInstance(length: 2)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); - - test('nested record type with named fields elements', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - final elements = await getElements(instanceId); - expect( - elements, - [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], - ); - expect( - await getElements(elements[1].id!), - [matchTypeInstance('bool'), matchTypeInstance('int')], - ); - expect( - await getDisplayedFields(instanceRef), - ['bool', '(bool, int)'], - ); - expect( - await getDisplayedFields(elements[1]), - ['bool', 'int'], - ); - }); - }); - - test('nested record type with named fields display', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instance = await getObject(instanceRef.id!); - final typeClassId = instance.classRef!.id; - - expect(await getObject(typeClassId), matchRecordTypeClass); - - final typeStringRef = - await getInstanceRef(frame, 'record.runtimeType.toString()'); - final typeStringId = typeStringRef.id!; - - expect( - await getObject(typeStringId), - matchPrimitiveInstance( - kind: InstanceKind.kString, - value: '(bool, {(bool, int) inner})', - ), - ); - }); - }); - }); -} diff --git a/dwds/test/instances/type_inspection_test.dart b/dwds/test/instances/type_inspection_test.dart index 1b09795d5..8ddf26124 100644 --- a/dwds/test/instances/type_inspection_test.dart +++ b/dwds/test/instances/type_inspection_test.dart @@ -7,13 +7,10 @@ @Timeout(Duration(minutes: 2)) import 'package:test/test.dart'; -import 'package:test_common/logging.dart'; import 'package:test_common/test_sdk_configuration.dart'; -import 'package:vm_service/vm_service.dart'; import '../fixtures/context.dart'; -import '../fixtures/project.dart'; -import 'common/test_inspector.dart'; +import 'common/type_inspection_common.dart'; void main() async { // Enable verbose logging for debugging. @@ -23,224 +20,10 @@ void main() async { tearDownAll(provider.dispose); for (var compilationMode in CompilationMode.values) { - await _runTests( + await runTests( provider: provider, compilationMode: compilationMode, debug: debug, ); } } - -Future _runTests({ - required TestSdkConfigurationProvider provider, - required CompilationMode compilationMode, - required bool debug, -}) async { - final context = - TestContext(TestProject.testExperimentWithSoundNullSafety, provider); - final testInspector = TestInspector(context); - - late VmServiceInterface service; - late Stream stream; - late String isolateId; - late ScriptRef mainScript; - - onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( - stream, - isolateId, - mainScript, - breakPointId, - body, - ); - - getObject(instanceId) => service.getObject(isolateId, instanceId); - - getDisplayedFields(instanceRef) => - testInspector.getDisplayedFields(isolateId, instanceRef); - - getInstanceRef(frame, expression) => - testInspector.getInstanceRef(isolateId, frame, expression); - - getFields(instanceRef, {offset, count, depth = -1}) => - testInspector.getFields( - isolateId, - instanceRef, - offset: offset, - count: count, - depth: depth, - ); - - getElements(String instanceId) => - testInspector.getElements(isolateId, instanceId); - - final matchTypeObject = { - 'hashCode': matchPrimitiveInstanceRef(kind: InstanceKind.kDouble), - 'runtimeType': matchTypeInstanceRef('Type'), - }; - - final matchDisplayedTypeObject = [ - matches('[0-9]*'), - 'Type', - ]; - - group('$compilationMode |', () { - setUpAll(() async { - setCurrentLogWriter(debug: debug); - await context.setUp( - compilationMode: compilationMode, - enableExpressionEvaluation: true, - verboseCompiler: debug, - experiments: ['records', 'patterns'], - ); - service = context.debugConnection.vmService; - - final vm = await service.getVM(); - isolateId = vm.isolates!.first.id!; - final scripts = await service.getScripts(isolateId); - - await service.streamListen('Debug'); - stream = service.onEvent('Debug'); - - mainScript = scripts.scripts! - .firstWhere((each) => each.uri!.contains('main.dart')); - }); - - tearDownAll(() async { - await context.tearDown(); - }); - - setUp(() => setCurrentLogWriter(debug: debug)); - tearDown(() => service.resume(isolateId)); - - test('String type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, "'1'.runtimeType"); - expect(instanceRef, matchTypeInstanceRef('String')); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchTypeInstance('String')); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchTypeClass); - expect(await getFields(instanceRef, depth: 1), matchTypeObject); - expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); - }); - }); - - test('int type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, '1.runtimeType'); - expect(instanceRef, matchTypeInstanceRef('int')); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchTypeInstance('int')); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchTypeClass); - expect(await getFields(instanceRef, depth: 1), matchTypeObject); - expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); - }); - }); - - test('list type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, '[].runtimeType'); - expect(instanceRef, matchTypeInstanceRef('List')); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchTypeInstance('List')); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchTypeClass); - expect(await getFields(instanceRef, depth: 1), matchTypeObject); - expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); - }); - }); - - test('map type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = - await getInstanceRef(frame, '{}.runtimeType'); - expect(instanceRef, matchTypeInstanceRef('IdentityMap')); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchTypeInstance('IdentityMap')); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchTypeClass); - expect(await getFields(instanceRef, depth: 1), matchTypeObject); - expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); - }); - }); - - test('set type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, '{}.runtimeType'); - expect(instanceRef, matchTypeInstanceRef('_IdentityHashSet')); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchTypeInstance('_IdentityHashSet')); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchTypeClass); - expect(await getFields(instanceRef, depth: 1), matchTypeObject); - expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); - }); - }); - - test('record type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, "(0,'a').runtimeType"); - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchRecordTypeInstance(length: 2)); - expect( - await getElements(instanceId), - [matchTypeInstance('int'), matchTypeInstance('String')], - ); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - expect( - await getFields(instanceRef, depth: 2), - {1: matchTypeObject, 2: matchTypeObject}, - ); - expect( - await getDisplayedFields(instanceRef), - ['int', 'String'], - ); - }); - }); - - test('class type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = - await getInstanceRef(frame, "Uri.file('').runtimeType"); - expect(instanceRef, matchTypeInstanceRef('_Uri')); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchTypeInstance('_Uri')); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchTypeClass); - expect(await getFields(instanceRef, depth: 1), matchTypeObject); - expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject); - }); - }); - }); -} diff --git a/dwds/test/instances/objects_test.dart b/dwds/test/objects_test.dart similarity index 100% rename from dwds/test/instances/objects_test.dart rename to dwds/test/objects_test.dart