From 3c0ffbb1443d019b0748626d4c9e536373493f2e Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 27 Jan 2023 12:02:54 -0800 Subject: [PATCH 1/3] Validate only needed summaries in expression_compiler_service --- dwds/lib/src/services/expression_compiler_service.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dwds/lib/src/services/expression_compiler_service.dart b/dwds/lib/src/services/expression_compiler_service.dart index 74cd13510..e94be84c3 100644 --- a/dwds/lib/src/services/expression_compiler_service.dart +++ b/dwds/lib/src/services/expression_compiler_service.dart @@ -71,7 +71,12 @@ class _Compiler { List experiments, bool verbose, ) async { - sdkConfiguration.validate(); + sdkConfiguration.validateSdkDir(); + if (soundNullSafety) { + sdkConfiguration.validateSoundSummaries(); + } else { + sdkConfiguration.validateWeakSummaries(); + } final librariesUri = sdkConfiguration.librariesUri!; final workerUri = sdkConfiguration.compilerWorkerUri!; From 44a758a32a0aabfbc4c35a90942829844f3d0e4d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 17 Feb 2023 16:56:16 -0800 Subject: [PATCH 2/3] Add mode instance inspection tests --- .../instances/instance_inspection_common.dart | 175 +++++++++ .../instances/instance_inspection_test.dart | 211 +++++++++++ dwds/test/{ => instances}/instance_test.dart | 12 +- dwds/test/{ => instances}/objects_test.dart | 0 .../record_inspection_test.dart} | 339 ++++++------------ fixtures/_testPackageSound/web/main.dart | 19 +- 6 files changed, 515 insertions(+), 241 deletions(-) create mode 100644 dwds/test/instances/instance_inspection_common.dart create mode 100644 dwds/test/instances/instance_inspection_test.dart rename dwds/test/{ => instances}/instance_test.dart (96%) rename dwds/test/{ => instances}/objects_test.dart (100%) rename dwds/test/{records_test.dart => instances/record_inspection_test.dart} (58%) diff --git a/dwds/test/instances/instance_inspection_common.dart b/dwds/test/instances/instance_inspection_common.dart new file mode 100644 index 000000000..9711283db --- /dev/null +++ b/dwds/test/instances/instance_inspection_common.dart @@ -0,0 +1,175 @@ +// Copyright (c) 2020, 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. + +@TestOn('vm') +@Timeout(Duration(minutes: 2)) + +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; + +import '../fixtures/context.dart'; + +class TestInspector { + TestInspector(this.context); + TestContext context; + + VmServiceInterface get service => context.debugConnection.vmService; + + Future onBreakPoint( + Stream stream, + String isolateId, + ScriptRef script, + String breakPointId, + Future Function(Event event) body, + ) async { + Breakpoint? bp; + try { + final line = + await context.findBreakpointLine(breakPointId, isolateId, script); + bp = await service.addBreakpointWithScriptUri( + isolateId, script.uri!, line); + + final event = + await stream.firstWhere((e) => e.kind == EventKind.kPauseBreakpoint); + + await body(event); + } finally { + // Remove breakpoint so it doesn't impact other tests or retries. + if (bp != null) { + await service.removeBreakpoint(isolateId, bp.id!); + } + } + } + + Future getFields( + String isolateId, + InstanceRef instanceRef, { + int? offset, + int? count, + }) async { + final instanceId = instanceRef.id!; + final instanceKind = instanceRef.kind; + + final result = await service.getObject( + isolateId, + instanceId, + offset: offset, + count: count, + ); + + expect(result, isA()); + final instance = result as Instance; + expect(instance.kind, instanceKind); + + final fields = instance.fields; + final associations = instance.associations; + final elements = instance.elements; + + Map? fieldRefs; + if (fields != null) { + fieldRefs = _boundFieldsToMap(fields); + } else if (associations != null) { + fieldRefs = _associationsToMap(associations); + } else if (elements != null) { + fieldRefs = _elementsToMap(elements); + } else { + fieldRefs = {}; + } + + final fieldValues = {}; + for (var p in fieldRefs.entries) { + fieldValues[p.key] = + _getValue(p.value) ?? await getFields(isolateId, p.value); + } + return elements == null ? fieldValues : fieldValues.values.toList(); + } + + Future getInstanceRef( + String isolateId, + int frame, + String expression, + ) async { + final result = await service.evaluateInFrame( + isolateId, + frame, + expression, + ); + expect(result, isA()); + return result as InstanceRef; + } + + Future getInstance( + String isolateId, + int frame, + String expression, + ) async { + final instanceRef = await getInstanceRef( + isolateId, + frame, + expression, + ); + + expect(instanceRef.id, isNotNull); + final result = await service.getObject( + isolateId, + instanceRef.id!, + ); + + expect(result, isA()); + return result as Instance; + } +} + +Map _associationsToMap( + Iterable associations) => + Map.fromEntries( + associations.map((e) => MapEntry(e.key.valueAsString, e.value))); + +Map _boundFieldsToMap(Iterable fields) => + Map.fromEntries(fields + .where((e) => e.name != null) + .map((e) => MapEntry(e.name, e.value))); + +Map _elementsToMap(List fields) => + Map.fromEntries(fields + .where((e) => e != null) + .map((e) => MapEntry(fields.indexOf(e), e!))); + +Matcher matchPrimitiveInstance( + {required String kind, required dynamic value}) => + isA() + .having((e) => e.kind, 'kind', kind) + .having((e) => _getValue(e), 'value', value); + +Matcher matchPlainInstance({required String type}) => isA() + .having((e) => e.kind, 'kind', InstanceKind.kPlainInstance) + .having((e) => e.classRef!.name, 'classRef.name', type); + +Matcher matchListInstance({required String type}) => isA() + .having((e) => e.kind, 'kind', InstanceKind.kList) + .having((e) => e.classRef!.name, 'classRef.name', type); + +Matcher matchMapInstance({required String type}) => isA() + .having((e) => e.kind, 'kind', InstanceKind.kMap) + .having((e) => e.classRef!.name, 'classRef.name', type); + +Matcher matchRecordInstance({required int length, required String type}) => + isA() + .having((e) => e.kind, 'kind', InstanceKind.kRecord) + .having((e) => e.length, 'length', length) + .having((e) => e.classRef!.name, 'classRef.name', type); + +Object? _getValue(InstanceRef instanceRef) { + switch (instanceRef.kind) { + case InstanceKind.kBool: + return instanceRef.valueAsString == 'true'; + case InstanceKind.kDouble: + case InstanceKind.kInt: + return int.parse(instanceRef.valueAsString!); + case InstanceKind.kString: + return instanceRef.valueAsString; + default: + return null; + } +} diff --git a/dwds/test/instances/instance_inspection_test.dart b/dwds/test/instances/instance_inspection_test.dart new file mode 100644 index 000000000..ca8b35259 --- /dev/null +++ b/dwds/test/instances/instance_inspection_test.dart @@ -0,0 +1,211 @@ +// Copyright (c) 2020, 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. + +@TestOn('vm') +@Timeout(Duration(minutes: 2)) + +import 'package:dwds/src/connections/debug_connection.dart'; +import 'package:dwds/src/services/chrome_proxy_service.dart'; +import 'package:test/test.dart'; +import 'package:test_common/logging.dart'; +import 'package:vm_service/vm_service.dart'; + +import '../fixtures/context.dart'; +import 'instance_inspection_common.dart'; + +class TestSetup { + TestContext context; + + TestSetup.sound() + : context = TestContext.withSoundNullSafety( + packageName: '_testPackageSound', + webAssetsPath: 'web', + dartEntryFileName: 'main.dart', + htmlEntryFileName: 'index.html', + ); + + ChromeProxyService get service => + fetchChromeProxyService(context.debugConnection); +} + +void main() async { + // Enable verbose logging for debugging. + final debug = false; + + for (var compilationMode in CompilationMode.values) { + await _runTests( + compilationMode: compilationMode, + debug: debug, + ); + } +} + +Future _runTests({ + required CompilationMode compilationMode, + required bool debug, +}) async { + final setup = TestSetup.sound(); + final context = setup.context; + 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('$compilationMode |', () { + setUpAll(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['records'], + ); + service = setup.service; + + 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(type: 'MainClass')); + + expect(await getFields(instanceRef), {'_field': 1, 'field': 2}); + + // Offsets and counts are ignored for plain object fields. + 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)); + }); + }); + }); + }); +} diff --git a/dwds/test/instance_test.dart b/dwds/test/instances/instance_test.dart similarity index 96% rename from dwds/test/instance_test.dart rename to dwds/test/instances/instance_test.dart index c5f279703..8a1e4d60c 100644 --- a/dwds/test/instance_test.dart +++ b/dwds/test/instances/instance_test.dart @@ -13,8 +13,8 @@ import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; -import 'fixtures/context.dart'; -import 'fixtures/utilities.dart'; +import '../fixtures/context.dart'; +import '../fixtures/utilities.dart'; final context = TestContext.withSoundNullSafety( webAssetsPath: webCompatiblePath(['example', 'scopes']), @@ -139,9 +139,9 @@ void main() { final classRef = instance.classRef!; expect(classRef, isNotNull); expect(classRef.name, 'MyTestClass'); - final fieldNames = + final boundFieldNames = instance.fields!.map((boundField) => boundField.decl!.name).toList(); - expect(fieldNames, [ + expect(boundFieldNames, [ '_privateField', 'abstractField', 'closure', @@ -151,7 +151,11 @@ void main() { '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); } }); diff --git a/dwds/test/objects_test.dart b/dwds/test/instances/objects_test.dart similarity index 100% rename from dwds/test/objects_test.dart rename to dwds/test/instances/objects_test.dart diff --git a/dwds/test/records_test.dart b/dwds/test/instances/record_inspection_test.dart similarity index 58% rename from dwds/test/records_test.dart rename to dwds/test/instances/record_inspection_test.dart index 57bd2da7b..3990a385f 100644 --- a/dwds/test/records_test.dart +++ b/dwds/test/instances/record_inspection_test.dart @@ -11,7 +11,8 @@ import 'package:test/test.dart'; import 'package:test_common/logging.dart'; import 'package:vm_service/vm_service.dart'; -import 'fixtures/context.dart'; +import '../fixtures/context.dart'; +import 'instance_inspection_common.dart'; class TestSetup { TestContext context; @@ -51,19 +52,21 @@ Future _runTests({ late String isolateId; late ScriptRef mainScript; - onBreakPoint(breakPointId, body) => - _onBreakPoint(setup, stream, isolateId, mainScript, breakPointId, body); + final testInspector = TestInspector(context); + + onBreakPoint(breakPointId, body) => testInspector.onBreakPoint( + stream, isolateId, mainScript, breakPointId, body); getInstance(frame, expression) => - _getInstance(service, isolateId, frame, expression); + testInspector.getInstance(isolateId, frame, expression); getObject(instanceId) => service.getObject(isolateId, instanceId); getInstanceRef(frame, expression) => - _getInstanceRef(service, isolateId, frame, expression); + testInspector.getInstanceRef(isolateId, frame, expression); - getFields(instanceRef, {offset, count}) => - _getFields(service, isolateId, instanceRef, offset: offset, count: count); + getFields(instanceRef, {offset, count}) => testInspector + .getFields(isolateId, instanceRef, offset: offset, count: count); group('$compilationMode |', () { setUpAll(() async { @@ -99,18 +102,18 @@ Future _runTests({ final instanceId = instanceRef.id!; expect(await getObject(instanceId), - _matchRecordInstance(length: 2, type: 'RecordType(bool, int)')); + matchRecordInstance(length: 2, type: 'RecordType(bool, int)')); - 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), {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: 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), {}); }); }); @@ -119,10 +122,10 @@ Future _runTests({ await onBreakPoint('printSimpleLocal', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), - _matchPrimitiveInstance(kind: InstanceKind.kBool, value: 'true')); + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); expect(await getInstance(frame, r'record.$2'), - _matchPrimitiveInstance(kind: InstanceKind.kDouble, value: '3')); + matchPrimitiveInstance(kind: InstanceKind.kDouble, value: 3)); }); }); @@ -135,20 +138,20 @@ Future _runTests({ expect( await getObject(instanceId), - _matchRecordInstance( + matchRecordInstance( length: 2, type: 'RecordType(bool, {String cat})')); - expect(await getFields(instanceRef), {1: 'true', 'cat': 'Vasya'}); - expect(await getFields(instanceRef, offset: 0), - {1: 'true', 'cat': 'Vasya'}); + 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: 1), {1: true}); expect(await getFields(instanceRef, offset: 0, count: 2), - {1: 'true', 'cat': 'Vasya'}); + {1: true, 'cat': 'Vasya'}); expect(await getFields(instanceRef, offset: 0, count: 5), - {1: 'true', 'cat': 'Vasya'}); + {1: true, 'cat': 'Vasya'}); expect(await getFields(instanceRef, offset: 2, count: 5), {}); }); }); @@ -157,12 +160,10 @@ Future _runTests({ await onBreakPoint('printSimpleNamedLocal', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), - _matchPrimitiveInstance(kind: InstanceKind.kBool, value: 'true')); + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); - expect( - await getInstance(frame, r'record.cat'), - _matchPrimitiveInstance( - kind: InstanceKind.kString, value: 'Vasya')); + expect(await getInstance(frame, r'record.cat'), + matchPrimitiveInstance(kind: InstanceKind.kString, value: 'Vasya')); }); }); @@ -174,41 +175,41 @@ Future _runTests({ final instanceId = instanceRef.id!; expect( await getObject(instanceId), - _matchRecordInstance( + matchRecordInstance( length: 3, type: 'RecordType(bool, int, IdentityMap)')); expect(await getFields(instanceRef), { - 1: 'true', - 2: '3', - 3: {'a': '1', 'b': '5'} + 1: true, + 2: 3, + 3: {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 0), { - 1: 'true', - 2: '3', - 3: {'a': '1', 'b': '5'} + 1: true, + 2: 3, + 3: {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 1), { - 2: '3', - 3: {'a': '1', 'b': '5'} + 2: 3, + 3: {'a': 1, 'b': 5} }); - expect(await getFields(instanceRef, offset: 1, count: 1), {2: '3'}); + 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'} + 2: 3, + 3: {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 2), { - 3: {'a': '1', 'b': '5'} + 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: 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'} + 1: true, + 2: 3, + 3: {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 3, count: 5), {}); }); @@ -218,14 +219,14 @@ Future _runTests({ await onBreakPoint('printComplexLocal', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), - _matchPrimitiveInstance(kind: InstanceKind.kBool, value: 'true')); + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); expect(await getInstance(frame, r'record.$2'), - _matchPrimitiveInstance(kind: InstanceKind.kDouble, value: '3')); + 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'}); + expect(await getFields(third), {'a': 1, 'b': 5}); }); }); @@ -237,42 +238,42 @@ Future _runTests({ final instanceId = instanceRef.id!; expect( await getObject(instanceId), - _matchRecordInstance( + matchRecordInstance( length: 3, type: 'RecordType(bool, int, {IdentityMap array})')); expect(await getFields(instanceRef), { - 1: 'true', - 2: '3', - 'array': {'a': '1', 'b': '5'} + 1: true, + 2: 3, + 'array': {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 0), { - 1: 'true', - 2: '3', - 'array': {'a': '1', 'b': '5'} + 1: true, + 2: 3, + 'array': {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 1), { - 2: '3', - 'array': {'a': '1', 'b': '5'} + 2: 3, + 'array': {'a': 1, 'b': 5} }); - expect(await getFields(instanceRef, offset: 1, count: 1), {2: '3'}); + 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'} + 2: 3, + 'array': {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 2), { - 'array': {'a': '1', 'b': '5'} + '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: 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'} + 1: true, + 2: 3, + 'array': {'a': 1, 'b': 5} }); expect(await getFields(instanceRef, offset: 3, count: 5), {}); }); @@ -282,14 +283,14 @@ Future _runTests({ await onBreakPoint('printComplexNamedLocal', (event) async { final frame = event.topFrame!.index!; expect(await getInstance(frame, r'record.$1'), - _matchPrimitiveInstance(kind: InstanceKind.kBool, value: 'true')); + matchPrimitiveInstance(kind: InstanceKind.kBool, value: true)); expect(await getInstance(frame, r'record.$2'), - _matchPrimitiveInstance(kind: InstanceKind.kDouble, value: '3')); + 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'}); + expect(await getFields(third), {'a': 1, 'b': 5}); }); }); @@ -301,30 +302,30 @@ Future _runTests({ final instanceId = instanceRef.id!; expect( await getObject(instanceId), - _matchRecordInstance( + matchRecordInstance( length: 2, type: 'RecordType(bool, RecordType(bool, int))')); expect(await getFields(instanceRef), { - 1: 'true', - 2: {1: 'false', 2: '5'} + 1: true, + 2: {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 0), { - 1: 'true', - 2: {1: 'false', 2: '5'} + 1: true, + 2: {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 1), { - 2: {1: 'false', 2: '5'} + 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: 1), {1: true}); expect(await getFields(instanceRef, offset: 0, count: 2), { - 1: 'true', - 2: {1: 'false', 2: '5'} + 1: true, + 2: {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 0, count: 5), { - 1: 'true', - 2: {1: 'false', 2: '5'} + 1: true, + 2: {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 2, count: 5), {}); }); @@ -337,10 +338,10 @@ Future _runTests({ final instanceId = instanceRef.id!; expect(await getObject(instanceId), - _matchRecordInstance(length: 2, type: 'RecordType(bool, int)')); + matchRecordInstance(length: 2, type: 'RecordType(bool, int)')); - expect(await getFields(instanceRef), {1: 'false', 2: '5'}); - expect(await getFields(instanceRef, offset: 0), {1: 'false', 2: '5'}); + expect(await getFields(instanceRef), {1: false, 2: 5}); + expect(await getFields(instanceRef, offset: 0), {1: false, 2: 5}); }); }); @@ -352,37 +353,37 @@ Future _runTests({ final instanceId = instanceRef.id!; expect( await getObject(instanceId), - _matchRecordInstance( + matchRecordInstance( length: 2, type: 'RecordType(bool, {RecordType(bool, int) inner})')); expect(await getFields(instanceRef), { - 1: 'true', - 'inner': {1: 'false', 2: '5'} + 1: true, + 'inner': {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 0), { - 1: 'true', - 'inner': {1: 'false', 2: '5'} + 1: true, + 'inner': {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 1), { - 'inner': {1: 'false', 2: '5'} + 'inner': {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 1, count: 1), { - 'inner': {1: 'false', 2: '5'} + 'inner': {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 1, count: 2), { - 'inner': {1: 'false', 2: '5'} + '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: 1), {1: true}); expect(await getFields(instanceRef, offset: 0, count: 2), { - 1: 'true', - 'inner': {1: 'false', 2: '5'} + 1: true, + 'inner': {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 0, count: 5), { - 1: 'true', - 'inner': {1: 'false', 2: '5'} + 1: true, + 'inner': {1: false, 2: 5} }); expect(await getFields(instanceRef, offset: 2, count: 5), {}); }); @@ -395,141 +396,11 @@ Future _runTests({ final instanceId = instanceRef.id!; expect(await getObject(instanceId), - _matchRecordInstance(length: 2, type: 'RecordType(bool, int)')); + matchRecordInstance(length: 2, type: 'RecordType(bool, int)')); - expect(await getFields(instanceRef), {1: 'false', 2: '5'}); - expect(await getFields(instanceRef, offset: 0), {1: 'false', 2: '5'}); + expect(await getFields(instanceRef), {1: false, 2: 5}); + expect(await getFields(instanceRef, offset: 0), {1: false, 2: 5}); }); }); }); } - -Future _onBreakPoint( - TestSetup setup, - Stream stream, - String isolateId, - ScriptRef script, - String breakPointId, - Future Function(Event event) body, -) async { - final context = setup.context; - final service = setup.service; - - Breakpoint? bp; - try { - final line = - await context.findBreakpointLine(breakPointId, isolateId, script); - bp = await service.addBreakpointWithScriptUri(isolateId, script.uri!, line); - - final event = - await stream.firstWhere((e) => e.kind == EventKind.kPauseBreakpoint); - - await body(event); - } finally { - // Remove breakpoint so it doesn't impact other tests or retries. - if (bp != null) { - await service.removeBreakpoint(isolateId, bp.id!); - } - } -} - -Future> _getFields( - VmServiceInterface service, - String isolateId, - InstanceRef instanceRef, { - int? offset, - int? count, -}) async { - final instanceId = instanceRef.id!; - final instanceKind = instanceRef.kind; - - final result = await service.getObject( - isolateId, - instanceId, - offset: offset, - count: count, - ); - - expect(result, isA()); - final instance = result as Instance; - expect(instance.kind, instanceKind); - - final fields = instance.fields; - final associations = instance.associations; - - Map? fieldRefs; - if (fields != null) { - fieldRefs = _boundFieldsToMap(fields); - } else if (associations != null) { - fieldRefs = _associationsToMap(associations); - } else { - fieldRefs = {}; - } - - final fieldValues = {}; - for (var p in fieldRefs.entries) { - fieldValues[p.key] = - p.value.valueAsString ?? await _getFields(service, isolateId, p.value); - } - return fieldValues; -} - -Future _getInstanceRef( - VmServiceInterface service, - String isolateId, - int frame, - String expression, -) async { - final result = await service.evaluateInFrame( - isolateId, - frame, - expression, - ); - expect(result, isA()); - return result as InstanceRef; -} - -Future _getInstance( - VmServiceInterface service, - String isolateId, - int frame, - String expression, -) async { - final instanceRef = await _getInstanceRef( - service, - isolateId, - frame, - expression, - ); - - expect(instanceRef.id, isNotNull); - final result = await service.getObject( - isolateId, - instanceRef.id!, - ); - - expect(result, isA()); - return result as Instance; -} - -Map _associationsToMap( - Iterable associations) => - Map.fromEntries( - associations.map((e) => MapEntry(e.key.valueAsString, e.value))); - -Map _boundFieldsToMap(Iterable fields) => - Map.fromEntries(fields - .where((e) => e.name != null) - .map((e) => MapEntry(e.name, e.value))); - -Matcher _matchPrimitiveInstance( - {required String kind, required dynamic value}) => - isA() - .having((e) => e.kind, 'kind', kind) - .having((e) => e.valueAsString, 'value', value); - -Matcher _matchRecordInstance({required int length, required String type}) => - isA() - .having((e) => e.kind, 'kind', InstanceKind.kRecord) - .having((e) => e.length, 'length', length) - .having((e) => e.classRef!.name, 'classRef.name', type); diff --git a/fixtures/_testPackageSound/web/main.dart b/fixtures/_testPackageSound/web/main.dart index c7cf7c6d6..a769d35e8 100644 --- a/fixtures/_testPackageSound/web/main.dart +++ b/fixtures/_testPackageSound/web/main.dart @@ -43,6 +43,8 @@ void main() async { printObjectMultiLine(); // Breakpoint: callPrintObjectMultiLine printNestedObjectsMultiLine(); // Breakpoint: callPrintEnclosingFunctionMultiLine printStream(); // Breakpoint: callPrintStream + printList(); + printMap(); }); document.body?.appendText(concatenate('Program', ' is running!')); @@ -73,7 +75,7 @@ void printFieldFromLibraryPartClass() { } void printFieldMain() { - var instance = MainClass(1); + var instance = MainClass(2,1); print('$instance'); // Breakpoint: printFieldMain } @@ -138,16 +140,27 @@ void printStream() { subscription.cancel(); // Breakpoint: printStream } +void printList() { + final list = [0, 1, 2]; + print(list); // Breakpoint: printList +} + +void printMap() { + final map = {'a': 1, 'b': 2, 'c': 3}; + print(map); // Breakpoint: printMap +} + ClassWithMethod createObject() { return ClassWithMethod(0); // Breakpoint: createObjectWithMethod } class MainClass { + final int field; final int _field; - MainClass(this._field); // Breakpoint: newMainClass + MainClass(this.field, this._field); // Breakpoint: newMainClass @override - String toString() => '$_field'; + String toString() => '$field, $_field'; } class EnclosedClass { From 4b12977a179542c4041a9bf4996baac9aa6d69b4 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 17 Feb 2023 17:40:31 -0800 Subject: [PATCH 3/3] Fix failing tests --- dwds/test/evaluate_common.dart | 22 +++++++++---------- .../instances/instance_inspection_test.dart | 5 +++++ fixtures/_testPackage/web/main.dart | 7 +++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dwds/test/evaluate_common.dart b/dwds/test/evaluate_common.dart index 7da5c2606..2143864ee 100644 --- a/dwds/test/evaluate_common.dart +++ b/dwds/test/evaluate_common.dart @@ -181,7 +181,7 @@ void testAll({ .firstWhere((event) => event.kind == EventKind.kPauseBreakpoint); final object = await setup.service.evaluateInFrame( - isolateId, event.topFrame!.index!, 'MainClass(0)'); + isolateId, event.topFrame!.index!, 'MainClass(1,0)'); final param = object as InstanceRef; final result = await setup.service.evaluateInFrame( @@ -578,29 +578,29 @@ void testAll({ test('in parallel (in a batch)', () async { final library = isolate.rootLib!; final evaluation1 = setup.service - .evaluate(isolateId, library.id!, 'MainClass(0).toString()'); + .evaluate(isolateId, library.id!, 'MainClass(1,0).toString()'); final evaluation2 = setup.service - .evaluate(isolateId, library.id!, 'MainClass(1).toString()'); + .evaluate(isolateId, library.id!, 'MainClass(1,1).toString()'); final results = await Future.wait([evaluation1, evaluation2]); expect( results[0], const TypeMatcher().having( - (instance) => instance.valueAsString, 'valueAsString', '0')); + (instance) => instance.valueAsString, 'valueAsString', '1, 0')); expect( results[1], const TypeMatcher().having( - (instance) => instance.valueAsString, 'valueAsString', '1')); + (instance) => instance.valueAsString, 'valueAsString', '1, 1')); }); test('in parallel (in a batch) handles errors', () async { final library = isolate.rootLib!; final missingLibId = ''; final evaluation1 = setup.service - .evaluate(isolateId, missingLibId, 'MainClass(0).toString()'); + .evaluate(isolateId, missingLibId, 'MainClass(1,0).toString()'); final evaluation2 = setup.service - .evaluate(isolateId, library.id!, 'MainClass(1).toString()'); + .evaluate(isolateId, library.id!, 'MainClass(1,1).toString()'); final results = await Future.wait([evaluation1, evaluation2]); @@ -623,7 +623,7 @@ void testAll({ test('with scope override', () async { final library = isolate.rootLib!; final object = await setup.service - .evaluate(isolateId, library.id!, 'MainClass(0)'); + .evaluate(isolateId, library.id!, 'MainClass(1,0)'); final param = object as InstanceRef; final result = await setup.service.evaluate( @@ -633,18 +633,18 @@ void testAll({ expect( result, const TypeMatcher().having( - (instance) => instance.valueAsString, 'valueAsString', '0')); + (instance) => instance.valueAsString, 'valueAsString', '1, 0')); }); test('uses symbol from the same library', () async { final library = isolate.rootLib!; final result = await setup.service - .evaluate(isolateId, library.id!, 'MainClass(0).toString()'); + .evaluate(isolateId, library.id!, 'MainClass(1,0).toString()'); expect( result, const TypeMatcher().having( - (instance) => instance.valueAsString, 'valueAsString', '0')); + (instance) => instance.valueAsString, 'valueAsString', '1, 0')); }); test('uses symbol from another library', () async { diff --git a/dwds/test/instances/instance_inspection_test.dart b/dwds/test/instances/instance_inspection_test.dart index ca8b35259..082e8245d 100644 --- a/dwds/test/instances/instance_inspection_test.dart +++ b/dwds/test/instances/instance_inspection_test.dart @@ -108,6 +108,11 @@ Future _runTests({ expect(await getFields(instanceRef), {'_field': 1, 'field': 2}); // Offsets and counts are ignored for plain object fields. + + // DevTools calls [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), diff --git a/fixtures/_testPackage/web/main.dart b/fixtures/_testPackage/web/main.dart index c981099f2..967518da6 100644 --- a/fixtures/_testPackage/web/main.dart +++ b/fixtures/_testPackage/web/main.dart @@ -65,7 +65,7 @@ void printFieldFromLibraryPartClass() { } void printFieldMain() { - var instance = MainClass(1); + var instance = MainClass(2,1); print('$instance'); // Breakpoint: printFieldMain } @@ -135,11 +135,12 @@ ClassWithMethod createObject() { } class MainClass { + final int field; final int _field; - MainClass(this._field); // Breakpoint: newMainClass + MainClass(this.field, this._field); // Breakpoint: newMainClass @override - String toString() => '$_field'; + String toString() => '$field, $_field'; } class EnclosedClass {