Skip to content

Add tests for object inspection #1973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions dwds/test/evaluate_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '0'));
(instance) => instance.valueAsString, 'valueAsString', '1, 0'));

expect(
results[1],
const TypeMatcher<InstanceRef>().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]);

Expand All @@ -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(
Expand All @@ -633,18 +633,18 @@ void testAll({
expect(
result,
const TypeMatcher<InstanceRef>().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<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '0'));
(instance) => instance.valueAsString, 'valueAsString', '1, 0'));
});

test('uses symbol from another library', () async {
Expand Down
175 changes: 175 additions & 0 deletions dwds/test/instances/instance_inspection_common.dart
Original file line number Diff line number Diff line change
@@ -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<void> onBreakPoint(
Stream<Event> stream,
String isolateId,
ScriptRef script,
String breakPointId,
Future<void> 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<dynamic> 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<Instance>());
final instance = result as Instance;
expect(instance.kind, instanceKind);

final fields = instance.fields;
final associations = instance.associations;
final elements = instance.elements;

Map<dynamic, InstanceRef>? 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 = <dynamic, Object?>{};
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<InstanceRef> getInstanceRef(
String isolateId,
int frame,
String expression,
) async {
final result = await service.evaluateInFrame(
isolateId,
frame,
expression,
);
expect(result, isA<InstanceRef>());
return result as InstanceRef;
}

Future<Instance> 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<Instance>());
return result as Instance;
}
}

Map<String, InstanceRef> _associationsToMap(
Iterable<MapAssociation> associations) =>
Map.fromEntries(
associations.map((e) => MapEntry(e.key.valueAsString, e.value)));

Map<dynamic, InstanceRef> _boundFieldsToMap(Iterable<BoundField> fields) =>
Map.fromEntries(fields
.where((e) => e.name != null)
.map((e) => MapEntry(e.name, e.value)));

Map<dynamic, InstanceRef> _elementsToMap(List<dynamic> fields) =>
Map.fromEntries(fields
.where((e) => e != null)
.map((e) => MapEntry(fields.indexOf(e), e!)));

Matcher matchPrimitiveInstance(
{required String kind, required dynamic value}) =>
isA<Instance>()
.having((e) => e.kind, 'kind', kind)
.having((e) => _getValue(e), 'value', value);

Matcher matchPlainInstance({required String type}) => isA<Instance>()
.having((e) => e.kind, 'kind', InstanceKind.kPlainInstance)
.having((e) => e.classRef!.name, 'classRef.name', type);

Matcher matchListInstance({required String type}) => isA<Instance>()
.having((e) => e.kind, 'kind', InstanceKind.kList)
.having((e) => e.classRef!.name, 'classRef.name', type);

Matcher matchMapInstance({required String type}) => isA<Instance>()
.having((e) => e.kind, 'kind', InstanceKind.kMap)
.having((e) => e.classRef!.name, 'classRef.name', type);

Matcher matchRecordInstance({required int length, required String type}) =>
isA<Instance>()
.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;
}
}
Loading