Skip to content

Commit ae7eb80

Browse files
author
Anna Gringauze
authored
Add tests for object inspection (#1973)
* Validate only needed summaries in expression_compiler_service * Add mode instance inspection tests * Fix failing tests
1 parent 7d80a2c commit ae7eb80

File tree

8 files changed

+535
-255
lines changed

8 files changed

+535
-255
lines changed

dwds/test/evaluate_common.dart

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ void testAll({
181181
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
182182

183183
final object = await setup.service.evaluateInFrame(
184-
isolateId, event.topFrame!.index!, 'MainClass(0)');
184+
isolateId, event.topFrame!.index!, 'MainClass(1,0)');
185185

186186
final param = object as InstanceRef;
187187
final result = await setup.service.evaluateInFrame(
@@ -578,29 +578,29 @@ void testAll({
578578
test('in parallel (in a batch)', () async {
579579
final library = isolate.rootLib!;
580580
final evaluation1 = setup.service
581-
.evaluate(isolateId, library.id!, 'MainClass(0).toString()');
581+
.evaluate(isolateId, library.id!, 'MainClass(1,0).toString()');
582582
final evaluation2 = setup.service
583-
.evaluate(isolateId, library.id!, 'MainClass(1).toString()');
583+
.evaluate(isolateId, library.id!, 'MainClass(1,1).toString()');
584584

585585
final results = await Future.wait([evaluation1, evaluation2]);
586586
expect(
587587
results[0],
588588
const TypeMatcher<InstanceRef>().having(
589-
(instance) => instance.valueAsString, 'valueAsString', '0'));
589+
(instance) => instance.valueAsString, 'valueAsString', '1, 0'));
590590

591591
expect(
592592
results[1],
593593
const TypeMatcher<InstanceRef>().having(
594-
(instance) => instance.valueAsString, 'valueAsString', '1'));
594+
(instance) => instance.valueAsString, 'valueAsString', '1, 1'));
595595
});
596596

597597
test('in parallel (in a batch) handles errors', () async {
598598
final library = isolate.rootLib!;
599599
final missingLibId = '';
600600
final evaluation1 = setup.service
601-
.evaluate(isolateId, missingLibId, 'MainClass(0).toString()');
601+
.evaluate(isolateId, missingLibId, 'MainClass(1,0).toString()');
602602
final evaluation2 = setup.service
603-
.evaluate(isolateId, library.id!, 'MainClass(1).toString()');
603+
.evaluate(isolateId, library.id!, 'MainClass(1,1).toString()');
604604

605605
final results = await Future.wait([evaluation1, evaluation2]);
606606

@@ -623,7 +623,7 @@ void testAll({
623623
test('with scope override', () async {
624624
final library = isolate.rootLib!;
625625
final object = await setup.service
626-
.evaluate(isolateId, library.id!, 'MainClass(0)');
626+
.evaluate(isolateId, library.id!, 'MainClass(1,0)');
627627

628628
final param = object as InstanceRef;
629629
final result = await setup.service.evaluate(
@@ -633,18 +633,18 @@ void testAll({
633633
expect(
634634
result,
635635
const TypeMatcher<InstanceRef>().having(
636-
(instance) => instance.valueAsString, 'valueAsString', '0'));
636+
(instance) => instance.valueAsString, 'valueAsString', '1, 0'));
637637
});
638638

639639
test('uses symbol from the same library', () async {
640640
final library = isolate.rootLib!;
641641
final result = await setup.service
642-
.evaluate(isolateId, library.id!, 'MainClass(0).toString()');
642+
.evaluate(isolateId, library.id!, 'MainClass(1,0).toString()');
643643

644644
expect(
645645
result,
646646
const TypeMatcher<InstanceRef>().having(
647-
(instance) => instance.valueAsString, 'valueAsString', '0'));
647+
(instance) => instance.valueAsString, 'valueAsString', '1, 0'));
648648
});
649649

650650
test('uses symbol from another library', () async {
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
@TestOn('vm')
6+
@Timeout(Duration(minutes: 2))
7+
8+
import 'package:test/test.dart';
9+
import 'package:vm_service/vm_service.dart';
10+
11+
import '../fixtures/context.dart';
12+
13+
class TestInspector {
14+
TestInspector(this.context);
15+
TestContext context;
16+
17+
VmServiceInterface get service => context.debugConnection.vmService;
18+
19+
Future<void> onBreakPoint(
20+
Stream<Event> stream,
21+
String isolateId,
22+
ScriptRef script,
23+
String breakPointId,
24+
Future<void> Function(Event event) body,
25+
) async {
26+
Breakpoint? bp;
27+
try {
28+
final line =
29+
await context.findBreakpointLine(breakPointId, isolateId, script);
30+
bp = await service.addBreakpointWithScriptUri(
31+
isolateId, script.uri!, line);
32+
33+
final event =
34+
await stream.firstWhere((e) => e.kind == EventKind.kPauseBreakpoint);
35+
36+
await body(event);
37+
} finally {
38+
// Remove breakpoint so it doesn't impact other tests or retries.
39+
if (bp != null) {
40+
await service.removeBreakpoint(isolateId, bp.id!);
41+
}
42+
}
43+
}
44+
45+
Future<dynamic> getFields(
46+
String isolateId,
47+
InstanceRef instanceRef, {
48+
int? offset,
49+
int? count,
50+
}) async {
51+
final instanceId = instanceRef.id!;
52+
final instanceKind = instanceRef.kind;
53+
54+
final result = await service.getObject(
55+
isolateId,
56+
instanceId,
57+
offset: offset,
58+
count: count,
59+
);
60+
61+
expect(result, isA<Instance>());
62+
final instance = result as Instance;
63+
expect(instance.kind, instanceKind);
64+
65+
final fields = instance.fields;
66+
final associations = instance.associations;
67+
final elements = instance.elements;
68+
69+
Map<dynamic, InstanceRef>? fieldRefs;
70+
if (fields != null) {
71+
fieldRefs = _boundFieldsToMap(fields);
72+
} else if (associations != null) {
73+
fieldRefs = _associationsToMap(associations);
74+
} else if (elements != null) {
75+
fieldRefs = _elementsToMap(elements);
76+
} else {
77+
fieldRefs = {};
78+
}
79+
80+
final fieldValues = <dynamic, Object?>{};
81+
for (var p in fieldRefs.entries) {
82+
fieldValues[p.key] =
83+
_getValue(p.value) ?? await getFields(isolateId, p.value);
84+
}
85+
return elements == null ? fieldValues : fieldValues.values.toList();
86+
}
87+
88+
Future<InstanceRef> getInstanceRef(
89+
String isolateId,
90+
int frame,
91+
String expression,
92+
) async {
93+
final result = await service.evaluateInFrame(
94+
isolateId,
95+
frame,
96+
expression,
97+
);
98+
expect(result, isA<InstanceRef>());
99+
return result as InstanceRef;
100+
}
101+
102+
Future<Instance> getInstance(
103+
String isolateId,
104+
int frame,
105+
String expression,
106+
) async {
107+
final instanceRef = await getInstanceRef(
108+
isolateId,
109+
frame,
110+
expression,
111+
);
112+
113+
expect(instanceRef.id, isNotNull);
114+
final result = await service.getObject(
115+
isolateId,
116+
instanceRef.id!,
117+
);
118+
119+
expect(result, isA<Instance>());
120+
return result as Instance;
121+
}
122+
}
123+
124+
Map<String, InstanceRef> _associationsToMap(
125+
Iterable<MapAssociation> associations) =>
126+
Map.fromEntries(
127+
associations.map((e) => MapEntry(e.key.valueAsString, e.value)));
128+
129+
Map<dynamic, InstanceRef> _boundFieldsToMap(Iterable<BoundField> fields) =>
130+
Map.fromEntries(fields
131+
.where((e) => e.name != null)
132+
.map((e) => MapEntry(e.name, e.value)));
133+
134+
Map<dynamic, InstanceRef> _elementsToMap(List<dynamic> fields) =>
135+
Map.fromEntries(fields
136+
.where((e) => e != null)
137+
.map((e) => MapEntry(fields.indexOf(e), e!)));
138+
139+
Matcher matchPrimitiveInstance(
140+
{required String kind, required dynamic value}) =>
141+
isA<Instance>()
142+
.having((e) => e.kind, 'kind', kind)
143+
.having((e) => _getValue(e), 'value', value);
144+
145+
Matcher matchPlainInstance({required String type}) => isA<Instance>()
146+
.having((e) => e.kind, 'kind', InstanceKind.kPlainInstance)
147+
.having((e) => e.classRef!.name, 'classRef.name', type);
148+
149+
Matcher matchListInstance({required String type}) => isA<Instance>()
150+
.having((e) => e.kind, 'kind', InstanceKind.kList)
151+
.having((e) => e.classRef!.name, 'classRef.name', type);
152+
153+
Matcher matchMapInstance({required String type}) => isA<Instance>()
154+
.having((e) => e.kind, 'kind', InstanceKind.kMap)
155+
.having((e) => e.classRef!.name, 'classRef.name', type);
156+
157+
Matcher matchRecordInstance({required int length, required String type}) =>
158+
isA<Instance>()
159+
.having((e) => e.kind, 'kind', InstanceKind.kRecord)
160+
.having((e) => e.length, 'length', length)
161+
.having((e) => e.classRef!.name, 'classRef.name', type);
162+
163+
Object? _getValue(InstanceRef instanceRef) {
164+
switch (instanceRef.kind) {
165+
case InstanceKind.kBool:
166+
return instanceRef.valueAsString == 'true';
167+
case InstanceKind.kDouble:
168+
case InstanceKind.kInt:
169+
return int.parse(instanceRef.valueAsString!);
170+
case InstanceKind.kString:
171+
return instanceRef.valueAsString;
172+
default:
173+
return null;
174+
}
175+
}

0 commit comments

Comments
 (0)