blob: 2880576018d043c46c0ecdf3a30af3cf86a469b3 [file] [log] [blame] [edit]
// 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.
@TestOn('vm')
@Timeout(Duration(minutes: 2))
import 'dart:async';
import 'package:dwds/src/debugging/debugger.dart';
import 'package:dwds/src/debugging/location.dart';
import 'package:dwds/src/debugging/skip_list.dart';
import 'package:dwds/src/services/batched_expression_evaluator.dart';
import 'package:dwds/src/services/expression_evaluator.dart';
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart' hide LogRecord;
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
import 'fixtures/context.dart';
import 'fixtures/fakes.dart';
import 'fixtures/utilities.dart';
late ExpressionEvaluator? _evaluator;
late ExpressionEvaluator? _batchedEvaluator;
void main() async {
group('expression compiler service with fake asset server', () {
Future<void> stop() async {
_evaluator?.close();
_evaluator = null;
_batchedEvaluator?.close();
_batchedEvaluator = null;
}
late StreamController<DebuggerPausedEvent> pausedController;
late StreamController<Event> debugEventController;
setUp(() async {
final assetReader = FakeAssetReader(sourceMap: '');
final toolConfiguration = TestToolConfiguration.forTests(
loadStrategy: FakeStrategy(assetReader),
);
setGlobalsForTesting(
toolConfiguration: toolConfiguration,
);
final modules = FakeModules();
final webkitDebugger = FakeWebkitDebugger();
pausedController = StreamController<DebuggerPausedEvent>();
debugEventController = StreamController<Event>();
webkitDebugger.onPaused = pausedController.stream;
final root = 'fakeRoot';
final entry = 'fake_entrypoint';
final locations = Locations(assetReader, modules, root);
locations.initialize(entry);
final skipLists = SkipLists();
final debugger = await Debugger.create(
webkitDebugger,
(_, e) => debugEventController.sink.add(e),
locations,
skipLists,
root,
);
final inspector =
FakeInspector(webkitDebugger, fakeIsolate: simpleIsolate);
debugger.updateInspector(inspector);
_evaluator = ExpressionEvaluator(
entry,
inspector,
debugger,
locations,
modules,
FakeExpressionCompiler(),
);
_batchedEvaluator = BatchedExpressionEvaluator(
entry,
inspector,
debugger,
locations,
modules,
FakeExpressionCompiler(),
);
});
tearDown(() async {
await stop();
});
group('evaluator', () {
late ExpressionEvaluator evaluator;
setUp(() async {
evaluator = _evaluator!;
});
test('can evaluate expression', () async {
final result =
await evaluator.evaluateExpression('1', 'main.dart', 'true', {});
expect(
result,
const TypeMatcher<RemoteObject>()
.having((o) => o.value, 'value', 'true'),
);
});
test('can evaluate expression in frame with null scope', () async {
// Verify that we don't get the internal error.
// More extensive testing of 'evaluateExpressionInFrame' is done in
// evaluation tests for frontend server and build daemon.
await expectLater(
evaluator.evaluateExpressionInFrame('1', 0, 'true', null),
throwsRPCErrorWithMessage(
'Cannot evaluate on a call frame when the program is not paused',
),
);
});
test('cannot evaluate expression in async frame ', () async {
// Add a DebuggerPausedEvent with no frames provoke an error.
pausedController.sink.add(
DebuggerPausedEvent({
'method': '',
'params': {
'reason': 'other',
'callFrames': [],
},
}),
);
await debugEventController.stream
.firstWhere((e) => e.kind == EventKind.kPauseInterrupted);
// Verify that we get the internal error.
final result =
await evaluator.evaluateExpressionInFrame('20', 0, 'true', null);
expect(
result,
isA<RemoteObject>()
.having((o) => o.json['type'], 'type', 'AsyncFrameError')
.having(
(o) => o.json['value'],
'value',
'Expression evaluation in async frames is not supported. '
'No frame with index 0.',
),
);
});
test('can evaluate expression in frame with empty scope', () async {
// Verify that we don't get the internal error.
// More extensive testing of 'evaluateExpressionInFrame' is done in
// evaluation tests for frontend server and build daemon.
await expectLater(
evaluator.evaluateExpressionInFrame('1', 0, 'true', {}),
throwsRPCErrorWithMessage(
'Cannot evaluate on a call frame when the program is not paused',
),
);
});
test('returns error if closed', () async {
evaluator.close();
final result =
await evaluator.evaluateExpression('1', 'main.dart', 'true', {});
expect(
result,
const TypeMatcher<RemoteObject>()
.having((o) => o.type, 'type', 'InternalError')
.having((o) => o.value, 'value', contains('evaluator closed')),
);
});
});
group('batched evaluator', () {
late ExpressionEvaluator evaluator;
setUp(() async {
evaluator = _batchedEvaluator!;
});
test('can evaluate expression', () async {
final result =
await evaluator.evaluateExpression('1', 'main.dart', 'true', {});
expect(
result,
const TypeMatcher<RemoteObject>()
.having((o) => o.value, 'value', 'true'),
);
});
test('returns error if closed', () async {
evaluator.close();
final result =
await evaluator.evaluateExpression('1', 'main.dart', 'true', {});
expect(
result,
const TypeMatcher<RemoteObject>()
.having((o) => o.type, 'type', 'InternalError')
.having((o) => o.value, 'value', contains('evaluator closed')),
);
});
});
});
}