mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Convert expression evaluation exceptions to errors (#54916)
This commit is contained in:
parent
bec63a54bf
commit
b69b2a8c9e
@ -77,7 +77,8 @@ class WebExpressionCompiler implements ExpressionCompiler {
|
||||
content, compilerOutput.errorCount > 0);
|
||||
}
|
||||
|
||||
throw Exception('Failed to compile $expression');
|
||||
return ExpressionCompilationResult(
|
||||
'InternalError: frontend server failed to compile \'$expression\'', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -856,7 +856,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
}
|
||||
|
||||
Future<CompilerOutput> _compileExpressionToJs(_CompileExpressionToJsRequest request) async {
|
||||
_stdoutHandler.reset(suppressCompilerMessages: true, expectSources: false);
|
||||
_stdoutHandler.reset(suppressCompilerMessages: false, expectSources: false);
|
||||
|
||||
// 'compile-expression-to-js' should be invoked after compiler has been started,
|
||||
// program was compiled.
|
||||
|
||||
@ -17,6 +17,7 @@ import 'base/file_system.dart';
|
||||
import 'base/io.dart' as io;
|
||||
import 'base/logger.dart';
|
||||
import 'base/signals.dart';
|
||||
import 'base/terminal.dart';
|
||||
import 'base/utils.dart';
|
||||
import 'build_info.dart';
|
||||
import 'codegen.dart';
|
||||
@ -92,6 +93,9 @@ class FlutterDevice {
|
||||
// Override the filesystem scheme so that the frontend_server can find
|
||||
// the generated entrypoint code.
|
||||
fileSystemScheme: 'org-dartlang-app',
|
||||
compilerMessageConsumer:
|
||||
(String message, {bool emphasis, TerminalColor color, }) =>
|
||||
globals.printTrace(message),
|
||||
initializeFromDill: globals.fs.path.join(getBuildDirectory(), 'cache.dill'),
|
||||
targetModel: TargetModel.dartdevc,
|
||||
experimentalFlags: experimentalFlags,
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/dwds.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:flutter_tools/src/build_runner/devfs_web.dart';
|
||||
import 'package:matcher/matcher.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/testbed.dart';
|
||||
|
||||
void main() {
|
||||
Testbed testbed;
|
||||
|
||||
setUp(() {
|
||||
testbed = Testbed();
|
||||
});
|
||||
|
||||
test('WebExpressionCompiler handles successful expression compilation', () => testbed.run(() async {
|
||||
globals.fs.file('compilerOutput').writeAsStringSync('a');
|
||||
|
||||
final ResidentCompiler residentCompiler = MockResidentCompiler();
|
||||
when(residentCompiler.compileExpressionToJs(
|
||||
any, any, any, any, any, any, any
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('compilerOutput', 0, <Uri>[]);
|
||||
});
|
||||
|
||||
final ExpressionCompiler expressionCompiler =
|
||||
WebExpressionCompiler(residentCompiler);
|
||||
|
||||
final ExpressionCompilationResult result =
|
||||
await expressionCompiler.compileExpressionToJs(
|
||||
null, null, 1, 1, null, null, null, null);
|
||||
|
||||
expectResult(result, false, 'a');
|
||||
}));
|
||||
|
||||
test('WebExpressionCompiler handles compilation error', () => testbed.run(() async {
|
||||
globals.fs.file('compilerOutput').writeAsStringSync('Error: a');
|
||||
|
||||
final ResidentCompiler residentCompiler = MockResidentCompiler();
|
||||
when(residentCompiler.compileExpressionToJs(
|
||||
any, any, any, any, any, any, any
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('compilerOutput', 1, <Uri>[]);
|
||||
});
|
||||
|
||||
final ExpressionCompiler expressionCompiler =
|
||||
WebExpressionCompiler(residentCompiler);
|
||||
|
||||
final ExpressionCompilationResult result =
|
||||
await expressionCompiler.compileExpressionToJs(
|
||||
null, null, 1, 1, null, null, null, null);
|
||||
|
||||
expectResult(result, true, 'Error: a');
|
||||
}));
|
||||
|
||||
test('WebExpressionCompiler handles internal error', () => testbed.run(() async {
|
||||
final ResidentCompiler residentCompiler = MockResidentCompiler();
|
||||
when(residentCompiler.compileExpressionToJs(
|
||||
any, any, any, any, any, any, any
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return null;
|
||||
});
|
||||
|
||||
final ExpressionCompiler expressionCompiler =
|
||||
WebExpressionCompiler(residentCompiler);
|
||||
|
||||
final ExpressionCompilationResult result =
|
||||
await expressionCompiler.compileExpressionToJs(
|
||||
null, null, 1, 1, null, null, null, 'a');
|
||||
|
||||
expectResult(result, true, 'InternalError: frontend server failed to compile \'a\'');
|
||||
}));
|
||||
}
|
||||
|
||||
void expectResult(ExpressionCompilationResult result, bool isError, String value) {
|
||||
expect(result,
|
||||
const TypeMatcher<ExpressionCompilationResult>()
|
||||
.having((ExpressionCompilationResult instance) => instance.isError, 'isError', isError)
|
||||
.having((ExpressionCompilationResult instance) =>instance.result, 'result', value));
|
||||
}
|
||||
|
||||
class MockResidentCompiler extends Mock implements ResidentCompiler {}
|
||||
@ -7,6 +7,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:matcher/matcher.dart';
|
||||
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
@ -146,32 +147,51 @@ void batch2() {
|
||||
}
|
||||
|
||||
Future<void> evaluateTrivialExpressions(FlutterTestDriver flutter) async {
|
||||
InstanceRef res;
|
||||
ObjRef res;
|
||||
|
||||
res = await flutter.evaluateInFrame('"test"');
|
||||
expect(res.kind == InstanceKind.kString && res.valueAsString == 'test', isTrue);
|
||||
expectValueOfType(res, InstanceKind.kString, 'test');
|
||||
|
||||
res = await flutter.evaluateInFrame('1');
|
||||
expect(res.kind == InstanceKind.kInt && res.valueAsString == 1.toString(), isTrue);
|
||||
expectValueOfType(res, InstanceKind.kInt, 1.toString());
|
||||
|
||||
res = await flutter.evaluateInFrame('true');
|
||||
expect(res.kind == InstanceKind.kBool && res.valueAsString == true.toString(), isTrue);
|
||||
expectValueOfType(res, InstanceKind.kBool, true.toString());
|
||||
}
|
||||
|
||||
Future<void> evaluateComplexExpressions(FlutterTestDriver flutter) async {
|
||||
final InstanceRef res = await flutter.evaluateInFrame('new DateTime.now().year');
|
||||
expect(res.kind == InstanceKind.kInt && res.valueAsString == DateTime.now().year.toString(), isTrue);
|
||||
final ObjRef res = await flutter.evaluateInFrame('new DateTime.now().year');
|
||||
expectValueOfType(res, InstanceKind.kInt, DateTime.now().year.toString());
|
||||
}
|
||||
|
||||
Future<void> evaluateComplexReturningExpressions(FlutterTestDriver flutter) async {
|
||||
final DateTime now = DateTime.now();
|
||||
final InstanceRef resp = await flutter.evaluateInFrame('new DateTime.now()');
|
||||
expect(resp.classRef.name, equals('DateTime'));
|
||||
final ObjRef resp = await flutter.evaluateInFrame('new DateTime.now()');
|
||||
expectInstanceOfClass(resp, 'DateTime');
|
||||
// Ensure we got a reasonable approximation. The more accurate we try to
|
||||
// make this, the more likely it'll fail due to differences in the time
|
||||
// in the remote VM and the local VM at the time the code runs.
|
||||
final InstanceRef res = await flutter.evaluate(resp.id, r'"$year-$month-$day"');
|
||||
expect(res.valueAsString, equals('${now.year}-${now.month}-${now.day}'));
|
||||
expectValue(res, '${now.year}-${now.month}-${now.day}');
|
||||
}
|
||||
|
||||
void expectInstanceOfClass(ObjRef result, String name) {
|
||||
expect(result,
|
||||
const TypeMatcher<InstanceRef>()
|
||||
.having((InstanceRef instance) => instance.classRef.name, 'resp.classRef.name', name));
|
||||
}
|
||||
|
||||
void expectValueOfType(ObjRef result, String kind, String message) {
|
||||
expect(result,
|
||||
const TypeMatcher<InstanceRef>()
|
||||
.having((InstanceRef instance) => instance.kind, 'kind', kind)
|
||||
.having((InstanceRef instance) => instance.valueAsString, 'valueAsString', message));
|
||||
}
|
||||
|
||||
void expectValue(ObjRef result, String message) {
|
||||
expect(result,
|
||||
const TypeMatcher<InstanceRef>()
|
||||
.having((InstanceRef instance) => instance.valueAsString, 'valueAsString', message));
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
@ -7,6 +7,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:matcher/matcher.dart';
|
||||
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
@ -46,6 +47,14 @@ void batch1() {
|
||||
);
|
||||
}
|
||||
|
||||
test('flutter run expression evaluation - can handle compilation errors', () async {
|
||||
await initProject();
|
||||
await _flutter.run(withDebugger: true, chrome: true);
|
||||
await breakInTopLevelFunction(_flutter);
|
||||
await evaluateErrorExpressions(_flutter);
|
||||
await cleanProject();
|
||||
}, skip: 'CI not setup for web tests'); // https://github.com/flutter/flutter/issues/53779
|
||||
|
||||
test('flutter run expression evaluation - can evaluate trivial expressions in top level function', () async {
|
||||
await initProject();
|
||||
await _flutter.run(withDebugger: true, chrome: true);
|
||||
@ -77,22 +86,6 @@ void batch1() {
|
||||
await evaluateComplexExpressions(_flutter);
|
||||
await cleanProject();
|
||||
}, skip: 'CI not setup for web tests'); // https://github.com/flutter/flutter/issues/53779
|
||||
|
||||
test('flutter run expression evaluation - can evaluate expressions returning complex objects in top level function', () async {
|
||||
await initProject();
|
||||
await _flutter.run(withDebugger: true, chrome: true);
|
||||
await breakInTopLevelFunction(_flutter);
|
||||
await evaluateComplexReturningExpressions(_flutter);
|
||||
await cleanProject();
|
||||
}, skip: 'Evaluate on objects is not supported for web yet');
|
||||
|
||||
test('flutter run expression evaluation - can evaluate expressions returning complex objects in build method', () async {
|
||||
await initProject();
|
||||
await _flutter.run(withDebugger: true, chrome: true);
|
||||
await breakInBuildMethod(_flutter);
|
||||
await evaluateComplexReturningExpressions(_flutter);
|
||||
await cleanProject();
|
||||
}, skip: 'Evaluate on objects is not supported for web yet');
|
||||
}
|
||||
|
||||
void batch2() {
|
||||
@ -133,43 +126,43 @@ void batch2() {
|
||||
await evaluateComplexExpressions(_flutter);
|
||||
await cleanProject();
|
||||
}, skip: 'CI not setup for web tests'); // https://github.com/flutter/flutter/issues/53779
|
||||
}
|
||||
|
||||
test('flutter test expression evaluation - can evaluate expressions returning complex objects in a test', () async {
|
||||
await initProject();
|
||||
await _flutter.run(withDebugger: true, chrome: true, script: _project.testFilePath);
|
||||
await breakInMethod(_flutter);
|
||||
await evaluateComplexReturningExpressions(_flutter);
|
||||
await cleanProject();
|
||||
}, skip: 'Evaluate on objects is not supported for web yet');
|
||||
|
||||
Future<void> evaluateErrorExpressions(FlutterTestDriver flutter) async {
|
||||
final ObjRef res = await flutter.evaluateInFrame('typo');
|
||||
expectError(res, 'CompilationError: Getter not found: \'typo\'.\ntypo\n^^^^');
|
||||
}
|
||||
|
||||
Future<void> evaluateTrivialExpressions(FlutterTestDriver flutter) async {
|
||||
InstanceRef res;
|
||||
ObjRef res;
|
||||
|
||||
res = await flutter.evaluateInFrame('"test"');
|
||||
expect(res.kind == InstanceKind.kString && res.valueAsString == 'test', isTrue);
|
||||
expectInstance(res, InstanceKind.kString, 'test');
|
||||
|
||||
res = await flutter.evaluateInFrame('1');
|
||||
expect(res.kind == InstanceKind.kDouble && res.valueAsString == 1.toString(), isTrue);
|
||||
expectInstance(res, InstanceKind.kDouble, 1.toString());
|
||||
|
||||
res = await flutter.evaluateInFrame('true');
|
||||
expect(res.kind == InstanceKind.kBool && res.valueAsString == true.toString(), isTrue);
|
||||
expectInstance(res, InstanceKind.kBool, true.toString());
|
||||
}
|
||||
|
||||
Future<void> evaluateComplexExpressions(FlutterTestDriver flutter) async {
|
||||
final InstanceRef res = await flutter.evaluateInFrame('new DateTime.now().year');
|
||||
expect(res.kind == InstanceKind.kDouble && res.valueAsString == DateTime.now().year.toString(), isTrue);
|
||||
final ObjRef res = await flutter.evaluateInFrame('new DateTime.now().year');
|
||||
expectInstance(res, InstanceKind.kDouble, DateTime.now().year.toString());
|
||||
}
|
||||
|
||||
Future<void> evaluateComplexReturningExpressions(FlutterTestDriver flutter) async {
|
||||
final DateTime now = DateTime.now();
|
||||
final InstanceRef resp = await flutter.evaluateInFrame('new DateTime.now()');
|
||||
expect(resp.classRef.name, equals('DateTime'));
|
||||
// Ensure we got a reasonable approximation. The more accurate we try to
|
||||
// make this, the more likely it'll fail due to differences in the time
|
||||
// in the remote VM and the local VM at the time the code runs.
|
||||
final InstanceRef res = await flutter.evaluate(resp.id, r'"$year-$month-$day"');
|
||||
expect(res.valueAsString, equals('${now.year}-${now.month}-${now.day}'));
|
||||
void expectInstance(ObjRef result, String kind, String message) {
|
||||
expect(result,
|
||||
const TypeMatcher<InstanceRef>()
|
||||
.having((InstanceRef instance) => instance.kind, 'kind', kind)
|
||||
.having((InstanceRef instance) => instance.valueAsString, 'valueAsString', message));
|
||||
}
|
||||
|
||||
void expectError(ObjRef result, String message) {
|
||||
expect(result,
|
||||
const TypeMatcher<ErrorRef>()
|
||||
.having((ErrorRef instance) => instance.message, 'message', message));
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
@ -291,9 +291,9 @@ abstract class FlutterTestDriver {
|
||||
return waitForNextPause ? waitForPause() : null;
|
||||
}
|
||||
|
||||
Future<InstanceRef> evaluateInFrame(String expression) async {
|
||||
return _timeoutWithMessages<InstanceRef>(
|
||||
() async => await _vmService.evaluateInFrame(await _getFlutterIsolateId(), 0, expression) as InstanceRef,
|
||||
Future<ObjRef> evaluateInFrame(String expression) async {
|
||||
return _timeoutWithMessages<ObjRef>(
|
||||
() async => await _vmService.evaluateInFrame(await _getFlutterIsolateId(), 0, expression) as ObjRef,
|
||||
task: 'Evaluating expression ($expression)',
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user