Skip to content

Commit 87bd77f

Browse files
author
Anna Gringauze
authored
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
1 parent afa14e2 commit 87bd77f

14 files changed

+1906
-1854
lines changed
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
// Copyright (c) 2023, 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+
import 'package:dwds/src/debugging/inspector.dart';
6+
import 'package:dwds/src/loaders/strategy.dart';
7+
import 'package:test/test.dart';
8+
import 'package:test_common/logging.dart';
9+
import 'package:test_common/test_sdk_configuration.dart';
10+
import 'package:vm_service/vm_service.dart';
11+
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
12+
13+
import '../../fixtures/context.dart';
14+
import '../../fixtures/project.dart';
15+
16+
void runTests({
17+
required TestSdkConfigurationProvider provider,
18+
required CompilationMode compilationMode,
19+
required bool debug,
20+
}) {
21+
final context =
22+
TestContext(TestProject.testScopesWithSoundNullSafety, provider);
23+
24+
late AppInspector inspector;
25+
26+
group('$compilationMode |', () {
27+
setUpAll(() async {
28+
setCurrentLogWriter(debug: debug);
29+
await context.setUp(compilationMode: compilationMode);
30+
final chromeProxyService = context.service;
31+
inspector = chromeProxyService.inspector;
32+
});
33+
34+
tearDownAll(() async {
35+
await context.tearDown();
36+
});
37+
38+
final url = 'org-dartlang-app:///example/scopes/main.dart';
39+
40+
String libraryName(CompilationMode compilationMode) =>
41+
compilationMode == CompilationMode.frontendServer
42+
? "example/scopes/main.dart"
43+
: "example/scopes/main";
44+
45+
String libraryVariableExpression(
46+
String variable,
47+
CompilationMode compilationMode,
48+
) =>
49+
'${globalLoadStrategy.loadModuleSnippet}("dart_sdk").dart.'
50+
'getModuleLibraries("${libraryName(compilationMode)}")'
51+
'["$url"]["$variable"];';
52+
53+
String interceptorsNewExpression(String type) =>
54+
"require('dart_sdk')._interceptors.$type['_#new#tearOff']()";
55+
56+
/// A reference to the the variable `libraryPublicFinal`, an instance of
57+
/// `MyTestClass`.
58+
Future<RemoteObject> libraryPublicFinal(CompilationMode compilationMode) =>
59+
inspector.jsEvaluate(
60+
libraryVariableExpression('libraryPublicFinal', compilationMode),
61+
);
62+
63+
/// A reference to the the variable `libraryPublic`, a List of Strings.
64+
Future<RemoteObject> libraryPublic(CompilationMode compilationMode) =>
65+
inspector.jsEvaluate(
66+
libraryVariableExpression('libraryPublic', compilationMode),
67+
);
68+
69+
group('instanceRef', () {
70+
setUp(() => setCurrentLogWriter(debug: debug));
71+
72+
test('for a null', () async {
73+
final remoteObject = await libraryPublicFinal(compilationMode);
74+
final nullVariable =
75+
await inspector.loadField(remoteObject, 'notFinal');
76+
final ref = await inspector.instanceRefFor(nullVariable);
77+
expect(ref!.valueAsString, 'null');
78+
expect(ref.kind, InstanceKind.kNull);
79+
final classRef = ref.classRef!;
80+
expect(classRef.name, 'Null');
81+
expect(classRef.id, 'classes|dart:core|Null');
82+
expect(inspector.isDisplayableObject(ref), isTrue);
83+
});
84+
85+
test('for a double', () async {
86+
final remoteObject = await libraryPublicFinal(compilationMode);
87+
final count = await inspector.loadField(remoteObject, 'count');
88+
final ref = await inspector.instanceRefFor(count);
89+
expect(ref!.valueAsString, '0');
90+
expect(ref.kind, InstanceKind.kDouble);
91+
final classRef = ref.classRef!;
92+
expect(classRef.name, 'Double');
93+
expect(classRef.id, 'classes|dart:core|Double');
94+
expect(inspector.isDisplayableObject(ref), isTrue);
95+
});
96+
97+
test('for a class', () async {
98+
final remoteObject = await libraryPublicFinal(compilationMode);
99+
final count = await inspector.loadField(remoteObject, 'myselfField');
100+
final ref = await inspector.instanceRefFor(count);
101+
expect(ref!.kind, InstanceKind.kPlainInstance);
102+
final classRef = ref.classRef!;
103+
expect(classRef.name, 'MyTestClass<dynamic>');
104+
expect(
105+
classRef.id,
106+
'classes|org-dartlang-app:///example/scopes/main.dart'
107+
'|MyTestClass<dynamic>');
108+
expect(inspector.isDisplayableObject(ref), isTrue);
109+
});
110+
111+
test('for closure', () async {
112+
final remoteObject = await libraryPublicFinal(compilationMode);
113+
final properties =
114+
await inspector.getProperties(remoteObject.objectId!);
115+
final closure =
116+
properties.firstWhere((property) => property.name == 'closure');
117+
final ref = await inspector.instanceRefFor(closure.value!);
118+
final functionName = ref!.closureFunction!.name;
119+
// Older SDKs do not contain function names
120+
if (functionName != 'Closure') {
121+
expect(functionName, 'someFunction');
122+
}
123+
expect(ref.kind, InstanceKind.kClosure);
124+
expect(inspector.isDisplayableObject(ref), isTrue);
125+
});
126+
127+
test('for a list', () async {
128+
final remoteObject = await libraryPublic(compilationMode);
129+
final ref = await inspector.instanceRefFor(remoteObject);
130+
expect(ref!.length, greaterThan(0));
131+
expect(ref.kind, InstanceKind.kList);
132+
expect(ref.classRef!.name, 'List<String>');
133+
expect(inspector.isDisplayableObject(ref), isTrue);
134+
});
135+
136+
test('for map', () async {
137+
final remoteObject = await inspector
138+
.jsEvaluate(libraryVariableExpression('map', compilationMode));
139+
final ref = await inspector.instanceRefFor(remoteObject);
140+
expect(ref!.length, 2);
141+
expect(ref.kind, InstanceKind.kMap);
142+
expect(ref.classRef!.name, 'LinkedMap<Object, Object>');
143+
expect(inspector.isDisplayableObject(ref), isTrue);
144+
});
145+
146+
test('for an IdentityMap', () async {
147+
final remoteObject = await inspector.jsEvaluate(
148+
libraryVariableExpression('identityMap', compilationMode),
149+
);
150+
final ref = await inspector.instanceRefFor(remoteObject);
151+
expect(ref!.length, 2);
152+
expect(ref.kind, InstanceKind.kMap);
153+
expect(ref.classRef!.name, 'IdentityMap<String, int>');
154+
expect(inspector.isDisplayableObject(ref), isTrue);
155+
});
156+
157+
test('for a native JavaScript error', () async {
158+
final remoteObject = await inspector
159+
.jsEvaluate(interceptorsNewExpression('NativeError'));
160+
final ref = await inspector.instanceRefFor(remoteObject);
161+
expect(ref!.kind, InstanceKind.kPlainInstance);
162+
expect(ref.classRef!.name, 'NativeError');
163+
expect(inspector.isDisplayableObject(ref), isFalse);
164+
expect(inspector.isNativeJsError(ref), isTrue);
165+
expect(inspector.isNativeJsObject(ref), isFalse);
166+
});
167+
168+
test('for a native JavaScript type error', () async {
169+
final remoteObject = await inspector
170+
.jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError'));
171+
final ref = await inspector.instanceRefFor(remoteObject);
172+
expect(ref!.kind, InstanceKind.kPlainInstance);
173+
expect(ref.classRef!.name, 'JSNoSuchMethodError');
174+
expect(inspector.isDisplayableObject(ref), isFalse);
175+
expect(inspector.isNativeJsError(ref), isTrue);
176+
expect(inspector.isNativeJsObject(ref), isFalse);
177+
});
178+
179+
test('for a native JavaScript object', () async {
180+
final remoteObject = await inspector
181+
.jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject'));
182+
final ref = await inspector.instanceRefFor(remoteObject);
183+
expect(ref!.kind, InstanceKind.kPlainInstance);
184+
expect(ref.classRef!.name, 'LegacyJavaScriptObject');
185+
expect(inspector.isDisplayableObject(ref), isFalse);
186+
expect(inspector.isNativeJsError(ref), isFalse);
187+
expect(inspector.isNativeJsObject(ref), isTrue);
188+
});
189+
});
190+
191+
group('instance', () {
192+
setUp(() => setCurrentLogWriter(debug: debug));
193+
test('for class object', () async {
194+
final remoteObject = await libraryPublicFinal(compilationMode);
195+
final instance = await inspector.instanceFor(remoteObject);
196+
expect(instance!.kind, InstanceKind.kPlainInstance);
197+
final classRef = instance.classRef!;
198+
expect(classRef, isNotNull);
199+
expect(classRef.name, 'MyTestClass<dynamic>');
200+
final boundFieldNames = instance.fields!
201+
.map((boundField) => boundField.decl!.name)
202+
.toList();
203+
expect(boundFieldNames, [
204+
'_privateField',
205+
'abstractField',
206+
'closure',
207+
'count',
208+
'message',
209+
'myselfField',
210+
'notFinal',
211+
'tornOff',
212+
]);
213+
final fieldNames =
214+
instance.fields!.map((boundField) => boundField.name).toList();
215+
expect(boundFieldNames, fieldNames);
216+
for (var field in instance.fields!) {
217+
expect(field.name, isNotNull);
218+
expect(field.decl!.declaredType, isNotNull);
219+
}
220+
expect(inspector.isDisplayableObject(instance), isTrue);
221+
});
222+
223+
test('for closure', () async {
224+
final remoteObject = await libraryPublicFinal(compilationMode);
225+
final properties =
226+
await inspector.getProperties(remoteObject.objectId!);
227+
final closure =
228+
properties.firstWhere((property) => property.name == 'closure');
229+
final instance = await inspector.instanceFor(closure.value!);
230+
expect(instance!.kind, InstanceKind.kClosure);
231+
expect(instance.classRef!.name, 'Closure');
232+
expect(inspector.isDisplayableObject(instance), isTrue);
233+
});
234+
235+
test('for a nested class', () async {
236+
final libraryRemoteObject = await libraryPublicFinal(compilationMode);
237+
final fieldRemoteObject =
238+
await inspector.loadField(libraryRemoteObject, 'myselfField');
239+
final instance = await inspector.instanceFor(fieldRemoteObject);
240+
expect(instance!.kind, InstanceKind.kPlainInstance);
241+
final classRef = instance.classRef!;
242+
expect(classRef, isNotNull);
243+
expect(classRef.name, 'MyTestClass<dynamic>');
244+
expect(inspector.isDisplayableObject(instance), isTrue);
245+
});
246+
247+
test('for a list', () async {
248+
final remote = await libraryPublic(compilationMode);
249+
final instance = await inspector.instanceFor(remote);
250+
expect(instance!.kind, InstanceKind.kList);
251+
final classRef = instance.classRef!;
252+
expect(classRef, isNotNull);
253+
expect(classRef.name, 'List<String>');
254+
final first = instance.elements![0];
255+
expect(first.valueAsString, 'library');
256+
expect(inspector.isDisplayableObject(instance), isTrue);
257+
});
258+
259+
test('for a map', () async {
260+
final remote = await inspector
261+
.jsEvaluate(libraryVariableExpression('map', compilationMode));
262+
final instance = await inspector.instanceFor(remote);
263+
expect(instance!.kind, InstanceKind.kMap);
264+
final classRef = instance.classRef!;
265+
expect(classRef.name, 'LinkedMap<Object, Object>');
266+
final first = instance.associations![0].value as InstanceRef;
267+
expect(first.kind, InstanceKind.kList);
268+
expect(first.length, 3);
269+
final second = instance.associations![1].value as InstanceRef;
270+
expect(second.kind, InstanceKind.kString);
271+
expect(second.valueAsString, 'something');
272+
expect(inspector.isDisplayableObject(instance), isTrue);
273+
});
274+
275+
test('for an identityMap', () async {
276+
final remote = await inspector.jsEvaluate(
277+
libraryVariableExpression('identityMap', compilationMode),
278+
);
279+
final instance = await inspector.instanceFor(remote);
280+
expect(instance!.kind, InstanceKind.kMap);
281+
final classRef = instance.classRef!;
282+
expect(classRef.name, 'IdentityMap<String, int>');
283+
final first = instance.associations![0].value;
284+
expect(first.valueAsString, '1');
285+
expect(inspector.isDisplayableObject(instance), isTrue);
286+
});
287+
288+
test('for a class that implements List', () async {
289+
// The VM only uses kind List for SDK lists, and we follow that.
290+
final remote = await inspector
291+
.jsEvaluate(libraryVariableExpression('notAList', compilationMode));
292+
final instance = await inspector.instanceFor(remote);
293+
expect(instance!.kind, InstanceKind.kPlainInstance);
294+
final classRef = instance.classRef!;
295+
expect(classRef.name, 'NotReallyAList');
296+
expect(instance.elements, isNull);
297+
final field = instance.fields!.first;
298+
expect(field.decl!.name, '_internal');
299+
expect(inspector.isDisplayableObject(instance), isTrue);
300+
});
301+
302+
test('for a native JavaScript error', () async {
303+
final remoteObject = await inspector
304+
.jsEvaluate(interceptorsNewExpression('NativeError'));
305+
final instance = await inspector.instanceFor(remoteObject);
306+
expect(instance!.kind, InstanceKind.kPlainInstance);
307+
expect(instance.classRef!.name, 'NativeError');
308+
expect(inspector.isDisplayableObject(instance), isFalse);
309+
expect(inspector.isNativeJsError(instance), isTrue);
310+
expect(inspector.isNativeJsObject(instance), isFalse);
311+
});
312+
313+
test('for a native JavaScript type error', () async {
314+
final remoteObject = await inspector
315+
.jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError'));
316+
final instance = await inspector.instanceFor(remoteObject);
317+
expect(instance!.kind, InstanceKind.kPlainInstance);
318+
expect(instance.classRef!.name, 'JSNoSuchMethodError');
319+
expect(inspector.isDisplayableObject(instance), isFalse);
320+
expect(inspector.isNativeJsError(instance), isTrue);
321+
expect(inspector.isNativeJsObject(instance), isFalse);
322+
});
323+
324+
test('for a native JavaScript object', () async {
325+
final remoteObject = await inspector
326+
.jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject'));
327+
final instance = await inspector.instanceFor(remoteObject);
328+
expect(instance!.kind, InstanceKind.kPlainInstance);
329+
expect(instance.classRef!.name, 'LegacyJavaScriptObject');
330+
expect(inspector.isDisplayableObject(instance), isFalse);
331+
expect(inspector.isNativeJsError(instance), isFalse);
332+
expect(inspector.isNativeJsObject(instance), isTrue);
333+
});
334+
});
335+
});
336+
}

0 commit comments

Comments
 (0)