blob: 737f5e7d4d883ade220505fa7e93810691865c4e [file] [log] [blame]
// 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:file/file.dart';
import 'package:vm_service/vm_service.dart';
import '../integration.shard/test_data/basic_project.dart';
import '../integration.shard/test_data/tests_project.dart';
import '../integration.shard/test_driver.dart';
import '../integration.shard/test_utils.dart';
import '../src/common.dart';
void main() {
group('Flutter run for web', () {
final BasicProject project = BasicProject();
late Directory tempDir;
late FlutterRunTestDriver flutter;
setUp(() async {
tempDir = createResolvedTempDirectorySync('run_expression_eval_test.');
await project.setUpIn(tempDir);
flutter = FlutterRunTestDriver(tempDir);
flutter.stdout.listen((String line) {
expect(line, isNot(contains('Unresolved uri:')));
expect(line, isNot(contains('No module for')));
});
});
tearDown(() async {
await flutter.stop();
tryToDelete(tempDir);
});
Future<void> start({required bool expressionEvaluation}) async {
// The non-test project has a loop around its breakpoints.
// No need to start paused as all breakpoint would be eventually reached.
await flutter.run(
withDebugger: true, chrome: true,
expressionEvaluation: expressionEvaluation,
additionalCommandArgs: <String>['--verbose', '--web-renderer=html']);
}
Future<void> breakInBuildMethod(FlutterTestDriver flutter) async {
await flutter.breakAt(
project.buildMethodBreakpointUri,
project.buildMethodBreakpointLine,
);
}
Future<void> breakInTopLevelFunction(FlutterTestDriver flutter) async {
await flutter.breakAt(
project.topLevelFunctionBreakpointUri,
project.topLevelFunctionBreakpointLine,
);
}
testWithoutContext('cannot evaluate expression if feature is disabled', () async {
await start(expressionEvaluation: false);
await breakInTopLevelFunction(flutter);
await failToEvaluateExpression(flutter);
});
testWithoutContext('shows no native javascript objects in static scope', () async {
await start(expressionEvaluation: true);
await breakInTopLevelFunction(flutter);
await checkStaticScope(flutter);
});
testWithoutContext('can handle compilation errors', () async {
await start(expressionEvaluation: true);
await breakInTopLevelFunction(flutter);
await evaluateErrorExpressions(flutter);
});
testWithoutContext('can evaluate trivial expressions in top level function', () async {
await start(expressionEvaluation: true);
await breakInTopLevelFunction(flutter);
await evaluateTrivialExpressions(flutter);
});
testWithoutContext('can evaluate trivial expressions in build method', () async {
await start(expressionEvaluation: true);
await breakInBuildMethod(flutter);
await evaluateTrivialExpressions(flutter);
});
testWithoutContext('can evaluate complex expressions in top level function', () async {
await start(expressionEvaluation: true);
await breakInTopLevelFunction(flutter);
await evaluateComplexExpressions(flutter);
});
testWithoutContext('can evaluate complex expressions in build method', () async {
await start(expressionEvaluation: true);
await breakInBuildMethod(flutter);
await evaluateComplexExpressions(flutter);
});
testWithoutContext('can evaluate trivial expressions in library without pause', () async {
await start(expressionEvaluation: true);
await evaluateTrivialExpressionsInLibrary(flutter);
});
testWithoutContext('can evaluate complex expressions in library without pause', () async {
await start(expressionEvaluation: true);
await evaluateComplexExpressionsInLibrary(flutter);
});
testWithoutContext('evaluated expression includes web library environment defines', () async {
await start(expressionEvaluation: true);
await evaluateWebLibraryBooleanFromEnvironmentInLibrary(flutter);
});
});
group('Flutter test for web', () {
final TestsProject project = TestsProject();
late Directory tempDir;
late FlutterRunTestDriver flutter;
setUp(() async {
tempDir = createResolvedTempDirectorySync('run_expression_eval_test.');
await project.setUpIn(tempDir);
flutter = FlutterRunTestDriver(tempDir);
});
tearDown(() async {
await flutter.stop();
tryToDelete(tempDir);
});
Future<Isolate?> breakInMethod(FlutterTestDriver flutter) async {
await flutter.addBreakpoint(
project.breakpointAppUri,
project.breakpointLine,
);
return flutter.resume(waitForNextPause: true);
}
Future<void> startPaused({required bool expressionEvaluation}) {
// The test project does not have a loop around its breakpoints.
// Start paused so we can set a breakpoint before passing it
// in the execution.
return flutter.run(
withDebugger: true, chrome: true,
expressionEvaluation: expressionEvaluation,
startPaused: true, script: project.testFilePath,
additionalCommandArgs: <String>['--verbose', '--web-renderer=html']);
}
testWithoutContext('cannot evaluate expressions if feature is disabled', () async {
await startPaused(expressionEvaluation: false);
await breakInMethod(flutter);
await failToEvaluateExpression(flutter);
});
testWithoutContext('can evaluate trivial expressions in a test', () async {
await startPaused(expressionEvaluation: true);
await breakInMethod(flutter);
await evaluateTrivialExpressions(flutter);
});
testWithoutContext('can evaluate complex expressions in a test', () async {
await startPaused(expressionEvaluation: true);
await breakInMethod(flutter);
await evaluateComplexExpressions(flutter);
});
testWithoutContext('can evaluate trivial expressions in library without pause', () async {
await startPaused(expressionEvaluation: true);
await evaluateTrivialExpressionsInLibrary(flutter);
});
testWithoutContext('can evaluate complex expressions in library without pause', () async {
await startPaused(expressionEvaluation: true);
await evaluateComplexExpressionsInLibrary(flutter);
});
testWithoutContext('evaluated expression includes web library environment defines', () async {
await startPaused(expressionEvaluation: true);
await evaluateWebLibraryBooleanFromEnvironmentInLibrary(flutter);
});
});
}
Future<void> failToEvaluateExpression(FlutterTestDriver flutter) async {
await expectLater(
flutter.evaluateInFrame('"test"'),
throwsA(isA<RPCError>().having(
(RPCError error) => error.message,
'message',
contains('Expression evaluation is not supported for this configuration'),
)),
);
}
Future<void> checkStaticScope(FlutterTestDriver flutter) async {
final Frame res = await flutter.getTopStackFrame();
expect(res.vars, equals(<BoundVariable>[]));
}
Future<void> evaluateErrorExpressions(FlutterTestDriver flutter) async {
final ObjRef res = await flutter.evaluateInFrame('typo');
expectError(res, 'CompilationError:');
}
Future<void> evaluateTrivialExpressions(FlutterTestDriver flutter) async {
ObjRef res;
res = await flutter.evaluateInFrame('"test"');
expectInstance(res, InstanceKind.kString, 'test');
res = await flutter.evaluateInFrame('1');
expectInstance(res, InstanceKind.kDouble, 1.toString());
res = await flutter.evaluateInFrame('true');
expectInstance(res, InstanceKind.kBool, true.toString());
}
Future<void> evaluateComplexExpressions(FlutterTestDriver flutter) async {
final ObjRef res = await flutter.evaluateInFrame('new DateTime.now().year');
expectInstance(res, InstanceKind.kDouble, DateTime.now().year.toString());
}
Future<void> evaluateTrivialExpressionsInLibrary(FlutterTestDriver flutter) async {
final LibraryRef library = await getRootLibrary(flutter);
final ObjRef res = await flutter.evaluate(library.id!, '"test"');
expectInstance(res, InstanceKind.kString, 'test');
}
Future<void> evaluateComplexExpressionsInLibrary(FlutterTestDriver flutter) async {
final LibraryRef library = await getRootLibrary(flutter);
final ObjRef res = await flutter.evaluate(library.id!, 'new DateTime.now().year');
expectInstance(res, InstanceKind.kDouble, DateTime.now().year.toString());
}
Future<void> evaluateWebLibraryBooleanFromEnvironmentInLibrary(FlutterTestDriver flutter) async {
final LibraryRef library = await getRootLibrary(flutter);
final ObjRef res = await flutter.evaluate(library.id!, 'const bool.fromEnvironment("dart.library.html")');
expectInstance(res, InstanceKind.kBool, true.toString());
}
Future<LibraryRef> getRootLibrary(FlutterTestDriver flutter) async {
// `isolate.rootLib` returns incorrect library, so find the
// entrypoint manually here instead.
//
// Issue: https://github.com/dart-lang/sdk/issues/44760
final Isolate isolate = await flutter.getFlutterIsolate();
return isolate.libraries!
.firstWhere((LibraryRef l) => l.uri!.contains('org-dartlang-app'));
}
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', contains(message)));
}