flutter/packages/flutter_tools/test/web.shard/expression_evaluation_web_test.dart
Michael Goderbauer 5491c8c146
Auto-format Framework (#160545)
This auto-formats all *.dart files in the repository outside of the
`engine` subdirectory and enforces that these files stay formatted with
a presubmit check.

**Reviewers:** Please carefully review all the commits except for the
one titled "formatted". The "formatted" commit was auto-generated by
running `dev/tools/format.sh -a -f`. The other commits were hand-crafted
to prepare the repo for the formatting change. I recommend reviewing the
commits one-by-one via the "Commits" tab and avoiding Github's "Files
changed" tab as it will likely slow down your browser because of the
size of this PR.

---------

Co-authored-by: Kate Lovett <katelovett@google.com>
Co-authored-by: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com>
2024-12-19 20:06:21 +00:00

277 lines
9.7 KiB
Dart

// 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.
@Tags(<String>['flutter-test-driver'])
library;
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'],
);
}
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'],
);
}
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),
),
);
}