blob: 0525cf8d7e750cfb43d28660f5f2b575536fcf21 [file] [log] [blame]
// Copyright (c) 2019, 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.
// @dart = 2.9
import 'package:dwds/src/connections/debug_connection.dart';
import 'package:dwds/src/debugging/debugger.dart';
import 'package:dwds/src/debugging/inspector.dart';
import 'package:dwds/src/debugging/instance.dart';
import 'package:dwds/src/loaders/strategy.dart';
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';
final context = TestContext(
directory: '../example', path: 'scopes.html', pathToServe: 'web');
WipConnection get tabConnection => context.tabConnection;
void main() {
AppInspector inspector;
Debugger debugger;
InstanceHelper instanceHelper;
setUpAll(() async {
await context.setUp();
var chromeProxyService = fetchChromeProxyService(context.debugConnection);
inspector = chromeProxyService.appInspectorProvider();
debugger = inspector.debugger;
instanceHelper = inspector.instanceHelper;
});
tearDownAll(() async {
await context.tearDown();
});
final url = 'org-dartlang-app:///web/scopes_main.dart';
String libraryVariableExpression(String variable) =>
'${globalLoadStrategy.loadModuleSnippet}("dart_sdk").dart.getModuleLibraries("web/scopes_main")'
'["$url"]["$variable"];';
/// A reference to the the variable `libraryPublicFinal`, an instance of
/// `MyTestClass`.
Future<RemoteObject> libraryPublicFinal() =>
inspector.jsEvaluate(libraryVariableExpression('libraryPublicFinal'));
/// A reference to the the variable `libraryPublic`, a List of Strings.
Future<RemoteObject> libraryPublic() =>
inspector.jsEvaluate(libraryVariableExpression('libraryPublic'));
group('instanceRef', () {
test('for a null', () async {
var remoteObject = await libraryPublicFinal();
var nullVariable = await inspector.loadField(remoteObject, 'notFinal');
var ref = await instanceHelper.instanceRefFor(nullVariable);
expect(ref.valueAsString, 'null');
expect(ref.kind, InstanceKind.kNull);
var classRef = ref.classRef;
expect(classRef.name, 'Null');
expect(classRef.id, 'classes|dart:core|Null');
});
test('for a double', () async {
var remoteObject = await libraryPublicFinal();
var count = await inspector.loadField(remoteObject, 'count');
var ref = await instanceHelper.instanceRefFor(count);
expect(ref.valueAsString, '0');
expect(ref.kind, InstanceKind.kDouble);
var classRef = ref.classRef;
expect(classRef.name, 'Double');
expect(classRef.id, 'classes|dart:core|Double');
});
test('for a class', () async {
var remoteObject = await libraryPublicFinal();
var count = await inspector.loadField(remoteObject, 'myselfField');
var ref = await instanceHelper.instanceRefFor(count);
expect(ref.kind, InstanceKind.kPlainInstance);
var classRef = ref.classRef;
expect(classRef.name, 'MyTestClass<dynamic>');
expect(
classRef.id,
'classes|org-dartlang-app:///web/scopes_main.dart'
'|MyTestClass<dynamic>');
});
test('for closure', () async {
var remoteObject = await libraryPublicFinal();
var properties = await debugger.getProperties(remoteObject.objectId);
var closure =
properties.firstWhere((property) => property.name == 'closure');
var instanceRef = await instanceHelper.instanceRefFor(closure.value);
var functionName = instanceRef.closureFunction.name;
// Older SDKs do not contain function names
if (functionName != 'Closure') {
expect(functionName, 'someFunction');
}
expect(instanceRef.kind, InstanceKind.kClosure);
});
test('for a list', () async {
var remoteObject = await libraryPublic();
var ref = await instanceHelper.instanceRefFor(remoteObject);
expect(ref.length, greaterThan(0));
expect(ref.kind, InstanceKind.kList);
expect(ref.classRef.name, 'List<String>');
});
test('for map', () async {
var remoteObject =
await inspector.jsEvaluate(libraryVariableExpression('map'));
var ref = await instanceHelper.instanceRefFor(remoteObject);
expect(ref.length, 2);
expect(ref.kind, InstanceKind.kMap);
expect(ref.classRef.name, 'LinkedMap<Object, Object>');
});
test('for an IdentityMap', () async {
var remoteObject =
await inspector.jsEvaluate(libraryVariableExpression('identityMap'));
var ref = await instanceHelper.instanceRefFor(remoteObject);
expect(ref.length, 2);
expect(ref.kind, InstanceKind.kMap);
expect(ref.classRef.name, 'IdentityMap<String, int>');
});
});
group('instance', () {
test('for class object', () async {
var remoteObject = await libraryPublicFinal();
var instance = await instanceHelper.instanceFor(remoteObject);
expect(instance.kind, InstanceKind.kPlainInstance);
var classRef = instance.classRef;
expect(classRef, isNotNull);
expect(classRef.name, 'MyTestClass<dynamic>');
var fieldNames =
instance.fields.map((boundField) => boundField.decl.name).toList();
expect(fieldNames, [
'_privateField',
'abstractField',
'closure',
'count',
'message',
'myselfField',
'notFinal',
'tornOff',
]);
for (var field in instance.fields) {
expect(field.decl.declaredType, isNotNull);
}
});
test('for closure', () async {
var remoteObject = await libraryPublicFinal();
var properties = await debugger.getProperties(remoteObject.objectId);
var closure =
properties.firstWhere((property) => property.name == 'closure');
var instance = await instanceHelper.instanceFor(closure.value);
expect(instance.kind, InstanceKind.kClosure);
expect(instance.classRef.name, 'Closure');
});
test('for a nested class', () async {
var libraryRemoteObject = await libraryPublicFinal();
var fieldRemoteObject =
await inspector.loadField(libraryRemoteObject, 'myselfField');
var instance = await instanceHelper.instanceFor(fieldRemoteObject);
expect(instance.kind, InstanceKind.kPlainInstance);
var classRef = instance.classRef;
expect(classRef, isNotNull);
expect(classRef.name, 'MyTestClass<dynamic>');
});
test('for a list', () async {
var remote = await libraryPublic();
var instance = await instanceHelper.instanceFor(remote);
expect(instance.kind, InstanceKind.kList);
var classRef = instance.classRef;
expect(classRef, isNotNull);
expect(classRef.name, 'List<String>');
var first = instance.elements[0];
expect(first.valueAsString, 'library');
});
test('for a map', () async {
var remote = await inspector.jsEvaluate(libraryVariableExpression('map'));
var instance = await instanceHelper.instanceFor(remote);
expect(instance.kind, InstanceKind.kMap);
var classRef = instance.classRef;
expect(classRef.name, 'LinkedMap<Object, Object>');
var first = instance.associations[0].value as InstanceRef;
expect(first.kind, InstanceKind.kList);
expect(first.length, 3);
var second = instance.associations[1].value as InstanceRef;
expect(second.kind, InstanceKind.kString);
expect(second.valueAsString, 'something');
});
test('for an identityMap', () async {
var remote =
await inspector.jsEvaluate(libraryVariableExpression('identityMap'));
var instance = await instanceHelper.instanceFor(remote);
expect(instance.kind, InstanceKind.kMap);
var classRef = instance.classRef;
expect(classRef.name, 'IdentityMap<String, int>');
var first = instance.associations[0].value;
expect(first.valueAsString, '1');
});
test('for a class that implements List', () async {
// The VM only uses kind List for SDK lists, and we follow that.
var remote =
await inspector.jsEvaluate(libraryVariableExpression('notAList'));
var instance = await instanceHelper.instanceFor(remote);
expect(instance.kind, InstanceKind.kPlainInstance);
var classRef = instance.classRef;
expect(classRef.name, 'NotReallyAList');
expect(instance.elements, isNull);
var field = instance.fields.first;
expect(field.decl.name, '_internal');
});
});
}