blob: 3277a2a3eed9ca9b33a550db7367e125071d5d6c [file] [log] [blame]
// 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.
@Tags(['expression-compilation-service'])
@TestOn('vm')
import 'dart:async';
import 'package:dwds/src/connections/debug_connection.dart';
import 'package:dwds/src/services/chrome_proxy_service.dart';
import 'package:matcher/src/type_matcher.dart';
import 'package:path/path.dart' as p;
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';
import 'fixtures/logging.dart';
final context = TestContext(
directory: p.join('..', 'fixtures', '_testPackage'),
entry: p.join('..', 'fixtures', '_testPackage', 'web', 'main.dart'),
path: 'index.html',
pathToServe: 'web');
ChromeProxyService get service =>
fetchChromeProxyService(context.debugConnection);
WipConnection get tabConnection => context.tabConnection;
void main() async {
Future<void> onBreakPoint(String isolate, ScriptRef script,
String breakPointId, Future<void> Function() body) async {
Breakpoint bp;
try {
var line =
await context.findBreakpointLine(breakPointId, isolate, script);
bp = await service.addBreakpointWithScriptUri(isolate, script.uri, line);
await body();
} finally {
// Remove breakpoint so it doesn't impact other tests or retries.
if (bp != null) {
await service.removeBreakpoint(isolate, bp.id);
}
}
}
group('shared context with evaluation', () {
setUpAll(() async {
// Note: change 'printOnFailure' to 'print' for debug printing
configureLogWriter(
customLogWriter: (level, message,
{loggerName, error, stackTrace, verbose}) =>
printOnFailure('[$level] $loggerName: $message'));
await context.setUp(enableExpressionEvaluation: true, verbose: false);
});
tearDownAll(() async {
await context.tearDown();
});
group('evaluateInFrame', () {
VM vm;
Isolate isolate;
ScriptList scripts;
ScriptRef mainScript;
ScriptRef libraryScript;
ScriptRef testLibraryScript;
Stream<Event> stream;
setUp(() async {
vm = await service.getVM();
isolate = await service.getIsolate(vm.isolates.first.id);
scripts = await service.getScripts(isolate.id);
await service.streamListen('Debug');
stream = service.onEvent('Debug');
mainScript = scripts.scripts
.firstWhere((each) => each.uri.contains('main.dart'));
testLibraryScript = scripts.scripts.firstWhere((each) =>
each.uri.contains('package:_testPackage/test_library.dart'));
libraryScript = scripts.scripts.firstWhere(
(each) => each.uri.contains('package:_test/library.dart'));
});
tearDown(() async {
await service.resume(isolate.id);
});
test('local', () async {
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'local');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '42'));
});
});
test('field', () async {
await onBreakPoint(isolate.id, mainScript, 'printField', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'instance.field');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '1'));
});
});
test('private field from another library', () async {
await onBreakPoint(isolate.id, mainScript, 'printField', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'instance._field');
expect(
result,
isA<ErrorRef>().having((instance) => instance.message, 'message',
contains("The getter '_field' isn't defined")));
});
});
test('private field from current library', () async {
await onBreakPoint(isolate.id, mainScript, 'printFieldMain', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'instance._field');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '1'));
});
});
test('access instance fields after evaluation', () async {
await onBreakPoint(isolate.id, mainScript, 'printField', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var instanceRef = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'instance') as InstanceRef;
var instance =
await service.getObject(isolate.id, instanceRef.id) as Instance;
var field = instance.fields
.firstWhere((BoundField element) => element.decl.name == 'field');
expect(
field.value,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '1'));
});
});
test('global', () async {
await onBreakPoint(isolate.id, mainScript, 'printGlobal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'testLibraryValue');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '3'));
});
});
test('call core function', () async {
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'print(local)');
expect(
result,
isA<InstanceRef>().having((instance) => instance.valueAsString,
'valueAsString', 'null'));
});
});
test('call library function with const param', () async {
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'testLibraryFunction(42)');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '42'));
});
});
test('call library function with local param', () async {
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'testLibraryFunction(local)');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '42'));
});
});
test('evaluate expression in _testPackage/test_library', () async {
await onBreakPoint(isolate.id, testLibraryScript, 'testLibraryFunction',
() async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'formal');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '23'));
});
});
test('evaluate expression in a class constructor in another library',
() async {
await onBreakPoint(
isolate.id, testLibraryScript, 'testLibraryClassConstructor',
() async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'this.field');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '1'));
});
});
test('evaluate expression in caller frame', () async {
await onBreakPoint(isolate.id, testLibraryScript, 'testLibraryFunction',
() async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index + 1, 'local');
expect(
result,
isA<InstanceRef>().having(
(instance) => instance.valueAsString, 'valueAsString', '23'));
});
});
test('evaluate expression in another library', () async {
await onBreakPoint(isolate.id, libraryScript, 'Concatenate', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var result = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'a');
expect(
result,
isA<InstanceRef>().having((instance) => instance.valueAsString,
'valueAsString', 'Hello'));
});
});
test('error', () async {
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
var error = await service.evaluateInFrame(
isolate.id, event.topFrame.index, 'typo');
expect(
error,
isA<ErrorRef>().having((instance) => instance.message, 'message',
matches('CompilationError: Getter not found:.*typo')));
});
});
test('cannot evaluate in unsupported isolate', () async {
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
expect(
() =>
service.evaluateInFrame('bad', event.topFrame.index, 'local'),
throwsRPCError);
});
});
});
}, timeout: const Timeout.factor(2));
group('shared context with no evaluation', () {
setUpAll(() async {
await context.setUp(enableExpressionEvaluation: false, verbose: false);
});
tearDownAll(() async {
await context.tearDown();
});
group('evaluateInFrame', () {
VM vm;
Isolate isolate;
ScriptList scripts;
ScriptRef mainScript;
Stream<Event> stream;
setUp(() async {
vm = await service.getVM();
isolate = await service.getIsolate(vm.isolates.first.id);
scripts = await service.getScripts(isolate.id);
await service.streamListen('Debug');
stream = service.onEvent('Debug');
mainScript = scripts.scripts
.firstWhere((each) => each.uri.contains('main.dart'));
});
tearDown(() async {
await service.resume(isolate.id);
});
test('cannot evaluate expression', () async {
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
var event = await stream
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
expect(
() => service.evaluateInFrame(
isolate.id, event.topFrame.index, 'local'),
throwsRPCError);
});
});
});
});
}