Skip to content

Split out common parts of instance tests in preparation for adding canary tests #2176

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 4 commits into from
Jul 21, 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
336 changes: 336 additions & 0 deletions dwds/test/instances/common/instance_common.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
// Copyright (c) 2023, 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.

import 'package:dwds/src/debugging/inspector.dart';
import 'package:dwds/src/loaders/strategy.dart';
import 'package:test/test.dart';
import 'package:test_common/logging.dart';
import 'package:test_common/test_sdk_configuration.dart';
import 'package:vm_service/vm_service.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';

import '../../fixtures/context.dart';
import '../../fixtures/project.dart';

void runTests({
required TestSdkConfigurationProvider provider,
required CompilationMode compilationMode,
required bool debug,
}) {
final context =
TestContext(TestProject.testScopesWithSoundNullSafety, provider);

late AppInspector inspector;

group('$compilationMode |', () {
setUpAll(() async {
setCurrentLogWriter(debug: debug);
await context.setUp(compilationMode: compilationMode);
final chromeProxyService = context.service;
inspector = chromeProxyService.inspector;
});

tearDownAll(() async {
await context.tearDown();
});

final url = 'org-dartlang-app:///example/scopes/main.dart';

String libraryName(CompilationMode compilationMode) =>
compilationMode == CompilationMode.frontendServer
? "example/scopes/main.dart"
: "example/scopes/main";

String libraryVariableExpression(
String variable,
CompilationMode compilationMode,
) =>
'${globalLoadStrategy.loadModuleSnippet}("dart_sdk").dart.'
'getModuleLibraries("${libraryName(compilationMode)}")'
'["$url"]["$variable"];';

String interceptorsNewExpression(String type) =>
"require('dart_sdk')._interceptors.$type['_#new#tearOff']()";

/// A reference to the the variable `libraryPublicFinal`, an instance of
/// `MyTestClass`.
Future<RemoteObject> libraryPublicFinal(CompilationMode compilationMode) =>
inspector.jsEvaluate(
libraryVariableExpression('libraryPublicFinal', compilationMode),
);

/// A reference to the the variable `libraryPublic`, a List of Strings.
Future<RemoteObject> libraryPublic(CompilationMode compilationMode) =>
inspector.jsEvaluate(
libraryVariableExpression('libraryPublic', compilationMode),
);

group('instanceRef', () {
setUp(() => setCurrentLogWriter(debug: debug));

test('for a null', () async {
final remoteObject = await libraryPublicFinal(compilationMode);
final nullVariable =
await inspector.loadField(remoteObject, 'notFinal');
final ref = await inspector.instanceRefFor(nullVariable);
expect(ref!.valueAsString, 'null');
expect(ref.kind, InstanceKind.kNull);
final classRef = ref.classRef!;
expect(classRef.name, 'Null');
expect(classRef.id, 'classes|dart:core|Null');
expect(inspector.isDisplayableObject(ref), isTrue);
});

test('for a double', () async {
final remoteObject = await libraryPublicFinal(compilationMode);
final count = await inspector.loadField(remoteObject, 'count');
final ref = await inspector.instanceRefFor(count);
expect(ref!.valueAsString, '0');
expect(ref.kind, InstanceKind.kDouble);
final classRef = ref.classRef!;
expect(classRef.name, 'Double');
expect(classRef.id, 'classes|dart:core|Double');
expect(inspector.isDisplayableObject(ref), isTrue);
});

test('for a class', () async {
final remoteObject = await libraryPublicFinal(compilationMode);
final count = await inspector.loadField(remoteObject, 'myselfField');
final ref = await inspector.instanceRefFor(count);
expect(ref!.kind, InstanceKind.kPlainInstance);
final classRef = ref.classRef!;
expect(classRef.name, 'MyTestClass<dynamic>');
expect(
classRef.id,
'classes|org-dartlang-app:///example/scopes/main.dart'
'|MyTestClass<dynamic>');
expect(inspector.isDisplayableObject(ref), isTrue);
});

test('for closure', () async {
final remoteObject = await libraryPublicFinal(compilationMode);
final properties =
await inspector.getProperties(remoteObject.objectId!);
final closure =
properties.firstWhere((property) => property.name == 'closure');
final ref = await inspector.instanceRefFor(closure.value!);
final functionName = ref!.closureFunction!.name;
// Older SDKs do not contain function names
if (functionName != 'Closure') {
expect(functionName, 'someFunction');
}
expect(ref.kind, InstanceKind.kClosure);
expect(inspector.isDisplayableObject(ref), isTrue);
});

test('for a list', () async {
final remoteObject = await libraryPublic(compilationMode);
final ref = await inspector.instanceRefFor(remoteObject);
expect(ref!.length, greaterThan(0));
expect(ref.kind, InstanceKind.kList);
expect(ref.classRef!.name, 'List<String>');
expect(inspector.isDisplayableObject(ref), isTrue);
});

test('for map', () async {
final remoteObject = await inspector
.jsEvaluate(libraryVariableExpression('map', compilationMode));
final ref = await inspector.instanceRefFor(remoteObject);
expect(ref!.length, 2);
expect(ref.kind, InstanceKind.kMap);
expect(ref.classRef!.name, 'LinkedMap<Object, Object>');
expect(inspector.isDisplayableObject(ref), isTrue);
});

test('for an IdentityMap', () async {
final remoteObject = await inspector.jsEvaluate(
libraryVariableExpression('identityMap', compilationMode),
);
final ref = await inspector.instanceRefFor(remoteObject);
expect(ref!.length, 2);
expect(ref.kind, InstanceKind.kMap);
expect(ref.classRef!.name, 'IdentityMap<String, int>');
expect(inspector.isDisplayableObject(ref), isTrue);
});

test('for a native JavaScript error', () async {
final remoteObject = await inspector
.jsEvaluate(interceptorsNewExpression('NativeError'));
final ref = await inspector.instanceRefFor(remoteObject);
expect(ref!.kind, InstanceKind.kPlainInstance);
expect(ref.classRef!.name, 'NativeError');
expect(inspector.isDisplayableObject(ref), isFalse);
expect(inspector.isNativeJsError(ref), isTrue);
expect(inspector.isNativeJsObject(ref), isFalse);
});

test('for a native JavaScript type error', () async {
final remoteObject = await inspector
.jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError'));
final ref = await inspector.instanceRefFor(remoteObject);
expect(ref!.kind, InstanceKind.kPlainInstance);
expect(ref.classRef!.name, 'JSNoSuchMethodError');
expect(inspector.isDisplayableObject(ref), isFalse);
expect(inspector.isNativeJsError(ref), isTrue);
expect(inspector.isNativeJsObject(ref), isFalse);
});

test('for a native JavaScript object', () async {
final remoteObject = await inspector
.jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject'));
final ref = await inspector.instanceRefFor(remoteObject);
expect(ref!.kind, InstanceKind.kPlainInstance);
expect(ref.classRef!.name, 'LegacyJavaScriptObject');
expect(inspector.isDisplayableObject(ref), isFalse);
expect(inspector.isNativeJsError(ref), isFalse);
expect(inspector.isNativeJsObject(ref), isTrue);
});
});

group('instance', () {
setUp(() => setCurrentLogWriter(debug: debug));
test('for class object', () async {
final remoteObject = await libraryPublicFinal(compilationMode);
final instance = await inspector.instanceFor(remoteObject);
expect(instance!.kind, InstanceKind.kPlainInstance);
final classRef = instance.classRef!;
expect(classRef, isNotNull);
expect(classRef.name, 'MyTestClass<dynamic>');
final boundFieldNames = instance.fields!
.map((boundField) => boundField.decl!.name)
.toList();
expect(boundFieldNames, [
'_privateField',
'abstractField',
'closure',
'count',
'message',
'myselfField',
'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);
}
expect(inspector.isDisplayableObject(instance), isTrue);
});

test('for closure', () async {
final remoteObject = await libraryPublicFinal(compilationMode);
final properties =
await inspector.getProperties(remoteObject.objectId!);
final closure =
properties.firstWhere((property) => property.name == 'closure');
final instance = await inspector.instanceFor(closure.value!);
expect(instance!.kind, InstanceKind.kClosure);
expect(instance.classRef!.name, 'Closure');
expect(inspector.isDisplayableObject(instance), isTrue);
});

test('for a nested class', () async {
final libraryRemoteObject = await libraryPublicFinal(compilationMode);
final fieldRemoteObject =
await inspector.loadField(libraryRemoteObject, 'myselfField');
final instance = await inspector.instanceFor(fieldRemoteObject);
expect(instance!.kind, InstanceKind.kPlainInstance);
final classRef = instance.classRef!;
expect(classRef, isNotNull);
expect(classRef.name, 'MyTestClass<dynamic>');
expect(inspector.isDisplayableObject(instance), isTrue);
});

test('for a list', () async {
final remote = await libraryPublic(compilationMode);
final instance = await inspector.instanceFor(remote);
expect(instance!.kind, InstanceKind.kList);
final classRef = instance.classRef!;
expect(classRef, isNotNull);
expect(classRef.name, 'List<String>');
final first = instance.elements![0];
expect(first.valueAsString, 'library');
expect(inspector.isDisplayableObject(instance), isTrue);
});

test('for a map', () async {
final remote = await inspector
.jsEvaluate(libraryVariableExpression('map', compilationMode));
final instance = await inspector.instanceFor(remote);
expect(instance!.kind, InstanceKind.kMap);
final classRef = instance.classRef!;
expect(classRef.name, 'LinkedMap<Object, Object>');
final first = instance.associations![0].value as InstanceRef;
expect(first.kind, InstanceKind.kList);
expect(first.length, 3);
final second = instance.associations![1].value as InstanceRef;
expect(second.kind, InstanceKind.kString);
expect(second.valueAsString, 'something');
expect(inspector.isDisplayableObject(instance), isTrue);
});

test('for an identityMap', () async {
final remote = await inspector.jsEvaluate(
libraryVariableExpression('identityMap', compilationMode),
);
final instance = await inspector.instanceFor(remote);
expect(instance!.kind, InstanceKind.kMap);
final classRef = instance.classRef!;
expect(classRef.name, 'IdentityMap<String, int>');
final first = instance.associations![0].value;
expect(first.valueAsString, '1');
expect(inspector.isDisplayableObject(instance), isTrue);
});

test('for a class that implements List', () async {
// The VM only uses kind List for SDK lists, and we follow that.
final remote = await inspector
.jsEvaluate(libraryVariableExpression('notAList', compilationMode));
final instance = await inspector.instanceFor(remote);
expect(instance!.kind, InstanceKind.kPlainInstance);
final classRef = instance.classRef!;
expect(classRef.name, 'NotReallyAList');
expect(instance.elements, isNull);
final field = instance.fields!.first;
expect(field.decl!.name, '_internal');
expect(inspector.isDisplayableObject(instance), isTrue);
});

test('for a native JavaScript error', () async {
final remoteObject = await inspector
.jsEvaluate(interceptorsNewExpression('NativeError'));
final instance = await inspector.instanceFor(remoteObject);
expect(instance!.kind, InstanceKind.kPlainInstance);
expect(instance.classRef!.name, 'NativeError');
expect(inspector.isDisplayableObject(instance), isFalse);
expect(inspector.isNativeJsError(instance), isTrue);
expect(inspector.isNativeJsObject(instance), isFalse);
});

test('for a native JavaScript type error', () async {
final remoteObject = await inspector
.jsEvaluate(interceptorsNewExpression('JSNoSuchMethodError'));
final instance = await inspector.instanceFor(remoteObject);
expect(instance!.kind, InstanceKind.kPlainInstance);
expect(instance.classRef!.name, 'JSNoSuchMethodError');
expect(inspector.isDisplayableObject(instance), isFalse);
expect(inspector.isNativeJsError(instance), isTrue);
expect(inspector.isNativeJsObject(instance), isFalse);
});

test('for a native JavaScript object', () async {
final remoteObject = await inspector
.jsEvaluate(interceptorsNewExpression('LegacyJavaScriptObject'));
final instance = await inspector.instanceFor(remoteObject);
expect(instance!.kind, InstanceKind.kPlainInstance);
expect(instance.classRef!.name, 'LegacyJavaScriptObject');
expect(inspector.isDisplayableObject(instance), isFalse);
expect(inspector.isNativeJsError(instance), isFalse);
expect(inspector.isNativeJsObject(instance), isTrue);
});
});
});
}
Loading