Commit 2a545243 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Fix tests to use Ahem, and helpful changes around that (#9332)

* Fix tests to use Ahem, and helpful changes around that

- Fix fonts that had metric-specific behaviours.

- LiveTestWidgetsFlutterBinding.allowAllFrames has been renamed
  to LiveTestWidgetsFlutterBinding.framePolicy.

- LiveTestWidgetsFlutterBinding now defaults to using a frame policy
  that pumps slightly more frames, to animate the pointer crosshairs.

- Added "flutter run --use-test-fonts" to enable Ahem on devices.

- Changed how idle() works to be more effective in live mode.

- Display the test name in live mode (unless ahem fonts are enabled).

- Added a toString to TextSelectionPoint.

- Style nit fixes.

* Roll engine to get Ahem changes.

* Update tests for dartdoc changes.

* Fix flutter_tools tests
parent c12c019b
5d9a6422577d95c242f45f48c47b431f7cf3c548
1fed16fb25f3f7afc8303116d6ef707c4043c127
......@@ -15,7 +15,8 @@ When the exception was thrown, this was the stack:
<<skip until matching line>>
\(elided .+\)
The test description was:
TestAsyncUtils - custom guarded sections
════════════════════════════════════════════════════════════════════════════════════════════════════
.*(this line has more of the test framework's output)?
Test failed\. See exception logs above\.
......
......@@ -14,7 +14,8 @@ When the exception was thrown, this was the stack:
<<skip until matching line>>
(elided [0-9]+ frames from .+)
The test description was:
TestAsyncUtils - handling unguarded async helper functions
════════════════════════════════════════════════════════════════════════════════════════════════════
.*..:.. \+0 -1: - TestAsyncUtils - handling unguarded async helper functions *
Test failed. See exception logs above.
......
......@@ -22,7 +22,7 @@ Future<Null> main() async {
// This allows us to call onBeginFrame even when the engine didn't request it,
// and have it actually do something:
final LiveTestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
binding.allowAllFrames = true;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
final Stopwatch watch = new Stopwatch();
int iterations = 0;
......
......@@ -21,7 +21,7 @@ Future<Null> main() async {
// This allows us to call onBeginFrame even when the engine didn't request it,
// and have it actually do something:
final LiveTestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
binding.allowAllFrames = true;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
final Stopwatch watch = new Stopwatch();
int iterations = 0;
......
......@@ -108,9 +108,9 @@ void createFooter(String footerPath) {
void sanityCheckDocs() {
final List<String> canaries = <String>[
'$kDocRoot/api/dart-io/File-class.html',
'$kDocRoot/api/dart-ui/Canvas-class.html',
'$kDocRoot/api/dart-ui/Canvas/drawRect.html',
'$kDocRoot/api/dart.io/File-class.html',
'$kDocRoot/api/dart_ui/Canvas-class.html',
'$kDocRoot/api/dart_ui/Canvas/drawRect.html',
'$kDocRoot/api/flutter_test/WidgetTester/pumpWidget.html',
'$kDocRoot/api/material/Material-class.html',
'$kDocRoot/api/material/Tooltip-class.html',
......
......@@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding)
binding.allowAllFrames = true;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
// We press the "1" and the "2" buttons and check that the display
// reads "12".
......
......@@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding)
binding.allowAllFrames = true;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
testWidgets('Flutter gallery button example code displays', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/6147
......
......@@ -9,7 +9,7 @@ import 'package:flutter_gallery/gallery/app.dart';
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding)
binding.allowAllFrames = true;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
// Regression test for https://github.com/flutter/flutter/pull/5168
testWidgets('Pesto appbar heroics', (WidgetTester tester) async {
......
......@@ -9,7 +9,7 @@ import 'package:flutter_gallery/main.dart' as flutter_gallery_main;
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding)
binding.allowAllFrames = true;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
testWidgets('Flutter Gallery app simple smoke test', (WidgetTester tester) async {
flutter_gallery_main.main(); // builds the app and schedules a frame but doesn't trigger one
......
......@@ -13,7 +13,7 @@ Future<String> mockUpdateUrlFetcher() {
void main() {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
if (binding is LiveTestWidgetsFlutterBinding)
binding.allowAllFrames = true;
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
// Regression test for https://github.com/flutter/flutter/pull/5168
testWidgets('update dialog', (WidgetTester tester) async {
......
......@@ -40,6 +40,17 @@ class TextSelectionPoint {
/// Direction of the text at this edge of the selection.
final TextDirection direction;
@override
String toString() {
switch (direction) {
case TextDirection.ltr:
return '$point-ltr';
case TextDirection.rtl:
return '$point-rtl';
}
return '$point';
}
}
/// A single line of editable text.
......
......@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
......@@ -41,12 +40,12 @@ void main() {
SystemChannels.platform.setMockMethodCallHandler(mockClipboard.handleMethodCall);
const String kThreeLines =
'First line of text is here abcdef ghijkl mnopqrst. ' +
'Second line of text goes until abcdef ghijkl mnopq. ' +
'Third line of stuff keeps going until abcdef ghijk. ';
'First line of text is ' +
'Second line goes until ' +
'Third line of stuff ';
const String kFourLines =
kThreeLines +
'Fourth line won\'t display and ends at abcdef ghi. ';
'Fourth line won\'t display and ends at';
// Returns the first RenderEditable.
RenderEditable findRenderEditable(WidgetTester tester) {
......@@ -69,7 +68,8 @@ void main() {
Point textOffsetToPosition(WidgetTester tester, int offset) {
final RenderEditable renderEditable = findRenderEditable(tester);
final List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
new TextSelection.collapsed(offset: offset));
new TextSelection.collapsed(offset: offset),
);
expect(endpoints.length, 1);
return endpoints[0].point + const Offset(0.0, -2.0);
}
......@@ -102,15 +102,18 @@ void main() {
final Size emptyInputSize = inputBox.size;
Future<Null> checkText(String testValue) async {
await tester.enterText(find.byType(EditableText), testValue);
return TestAsyncUtils.guard(() async {
await tester.enterText(find.byType(EditableText), testValue);
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
return await tester.pumpWidget(builder());
await tester.pumpWidget(builder());
});
}
await checkText(' ');
expect(findTextFieldBox(), equals(inputBox));
expect(inputBox.size, equals(emptyInputSize));
......@@ -492,7 +495,7 @@ void main() {
await tester.pumpWidget(builder());
final String testValue = kThreeLines;
final String cutValue = 'First line of stuff keeps going until abcdef ghijk. ';
final String cutValue = 'First line of stuff ';
await tester.enterText(find.byType(EditableText), testValue);
await tester.pumpWidget(builder());
......@@ -513,8 +516,8 @@ void main() {
await gesture.up();
await tester.pump();
expect(controller.selection.baseOffset, 76);
expect(controller.selection.extentOffset, 81);
expect(controller.selection.baseOffset, 39);
expect(controller.selection.extentOffset, 44);
final RenderEditable renderEditable = findRenderEditable(tester);
final List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
......@@ -531,8 +534,8 @@ void main() {
await gesture.up();
await tester.pumpWidget(builder());
expect(controller.selection.baseOffset, 76);
expect(controller.selection.extentOffset, 108);
expect(controller.selection.baseOffset, 39);
expect(controller.selection.extentOffset, 50);
// Drag the left handle to the first line, just after 'First'.
handlePos = endpoints[0].point + const Offset(-1.0, 1.0);
......@@ -545,13 +548,13 @@ void main() {
await tester.pumpWidget(builder());
expect(controller.selection.baseOffset, 5);
expect(controller.selection.extentOffset, 108);
expect(controller.selection.extentOffset, 50);
await tester.tap(find.text('CUT'));
await tester.pumpWidget(builder());
expect(controller.selection.isCollapsed, true);
expect(controller.text, cutValue);
}, skip: Platform.isMacOS); // Skip due to https://github.com/flutter/flutter/issues/6961
});
testWidgets('Can scroll multiline input', (WidgetTester tester) async {
final Key textFieldKey = new UniqueKey();
......@@ -571,10 +574,12 @@ void main() {
}
await tester.pumpWidget(builder());
await tester.pump(const Duration(seconds: 1));
await tester.enterText(find.byType(EditableText), kFourLines);
await tester.pumpWidget(builder());
await tester.pump(const Duration(seconds: 1));
RenderBox findInputBox() => tester.renderObject(find.byKey(textFieldKey));
final RenderBox inputBox = findInputBox();
......@@ -590,11 +595,11 @@ void main() {
TestGesture gesture = await tester.startGesture(firstPos, pointer: 7);
await tester.pump();
await gesture.moveBy(const Offset(0.0, -1000.0));
await tester.pump(const Duration(seconds: 2));
await tester.pump(const Duration(seconds: 1));
// Wait and drag again to trigger https://github.com/flutter/flutter/issues/6329
// (No idea why this is necessary, but the bug wouldn't repro without it.)
await gesture.moveBy(const Offset(0.0, -1000.0));
await tester.pump(const Duration(seconds: 2));
await tester.pump(const Duration(seconds: 1));
await gesture.up();
await tester.pump();
......@@ -609,27 +614,26 @@ void main() {
// Now try scrolling by dragging the selection handle.
// Long press the 'i' in 'Fourth line' to select the word.
await tester.pump(const Duration(seconds: 2));
await tester.pump(const Duration(seconds: 1));
final Point untilPos = textOffsetToPosition(tester, kFourLines.indexOf('Fourth line')+8);
gesture = await tester.startGesture(untilPos, pointer: 7);
await tester.pump(const Duration(seconds: 2));
await tester.pump(const Duration(seconds: 1));
await gesture.up();
await tester.pump();
await tester.pump(const Duration(seconds: 1));
final RenderEditable renderEditable = findRenderEditable(tester);
final List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
controller.selection);
final List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(controller.selection);
expect(endpoints.length, 2);
// Drag the left handle to the first line, just after 'First'.
final Point handlePos = endpoints[0].point + const Offset(-1.0, 1.0);
final Point newHandlePos = textOffsetToPosition(tester, kFourLines.indexOf('First') + 5);
gesture = await tester.startGesture(handlePos, pointer: 7);
await tester.pump();
await tester.pump(const Duration(seconds: 1));
await gesture.moveTo(newHandlePos + const Offset(0.0, -10.0));
await tester.pump();
await tester.pump(const Duration(seconds: 1));
await gesture.up();
await tester.pump();
await tester.pump(const Duration(seconds: 1));
// The text should have scrolled up with the handle to keep the active
// cursor visible, back to its original position.
......@@ -638,7 +642,7 @@ void main() {
expect(newFirstPos.y, firstPos.y);
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFirstPos)), isTrue);
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFourthPos)), isFalse);
}, skip: Platform.isMacOS); // Skip due to https://github.com/flutter/flutter/issues/6961
});
testWidgets('InputField smoke test', (WidgetTester tester) async {
String textFieldValue;
......@@ -658,16 +662,18 @@ void main() {
await tester.pumpWidget(builder());
Future<Null> checkText(String testValue) async {
await tester.enterText(find.byType(EditableText), testValue);
Future<Null> checkText(String testValue) {
return TestAsyncUtils.guard(() async {
await tester.enterText(find.byType(EditableText), testValue);
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
return await tester.pumpWidget(builder());
await tester.pumpWidget(builder());
});
}
checkText('Hello World');
await checkText('Hello World');
});
testWidgets('InputField with global key', (WidgetTester tester) async {
......@@ -691,15 +697,17 @@ void main() {
await tester.pumpWidget(builder());
Future<Null> checkText(String testValue) async {
await tester.enterText(find.byType(EditableText), testValue);
return TestAsyncUtils.guard(() async {
await tester.enterText(find.byType(EditableText), testValue);
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
return await tester.pumpWidget(builder());
await tester.pumpWidget(builder());
});
}
checkText('Hello World');
await checkText('Hello World');
});
testWidgets('TextField with default hintStyle', (WidgetTester tester) async {
......@@ -929,35 +937,28 @@ void main() {
),
),
));
expect(tester.testTextInput.editingState['text'], isEmpty);
await tester.tap(find.byType(TextField));
await tester.pump();
expect(tester.testTextInput.editingState['text'], equals('Initial Text'));
controller.text = 'Updated Text';
await tester.idle();
expect(tester.testTextInput.editingState['text'], equals('Updated Text'));
setState(() {
currentController = controller2;
});
await tester.pump();
expect(tester.testTextInput.editingState['text'], equals('More Text'));
controller.text = 'Ignored Text';
await tester.idle();
expect(tester.testTextInput.editingState['text'], equals('More Text'));
controller2.text = 'Final Text';
await tester.idle();
expect(tester.testTextInput.editingState['text'], equals('Final Text'));
});
}
......@@ -76,7 +76,10 @@ void main() {
test('overflow test', () {
final RenderParagraph paragraph = new RenderParagraph(
const TextSpan(text: 'This is\na wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.'),
const TextSpan(
text: 'This\n' // 4 characters * 10px font size = 40px width on the first line
'is a wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.',
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0)),
maxLines: 1,
softWrap: true,
);
......@@ -90,7 +93,7 @@ void main() {
}
// Lay out in a narrow box to force wrapping.
layout(paragraph, constraints: const BoxConstraints(maxWidth: 50.0));
layout(paragraph, constraints: const BoxConstraints(maxWidth: 50.0)); // enough to fit "This" but not "This is"
final double lineHeight = paragraph.size.height;
relayoutWith(maxLines: 3, softWrap: true, overflow: TextOverflow.clip);
......
......@@ -2,21 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
const TextStyle testFont = const TextStyle(
color: const Color(0xFF00FF00),
fontFamily: 'Ahem',
);
Future<Null> pumpTest(WidgetTester tester, TargetPlatform platform) async {
await tester.pumpWidget(new Container());
await tester.pumpWidget(new MaterialApp(
theme: new ThemeData(
platform: platform
platform: platform,
),
home: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Text('$index');
},
home: new Container(
color: const Color(0xFF111111),
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Text('$index', style: testFont);
},
),
),
));
return null;
......@@ -53,44 +59,44 @@ void main() {
final List<String> log = <String>[];
final List<Widget> textWidgets = <Widget>[];
for (int i = 0; i < 250; i++)
textWidgets.add(new GestureDetector(onTap: () { log.add('tap $i'); }, child: new Text('$i')));
for (int i = 0; i < 250; i += 1)
textWidgets.add(new GestureDetector(onTap: () { log.add('tap $i'); }, child: new Text('$i', style: testFont)));
await tester.pumpWidget(new ListView(children: textWidgets));
expect(log, equals(<String>[]));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
expect(log, equals(<String>['tap 21']));
await tester.fling(find.byType(Scrollable), const Offset(0.0, -200.0), 1000.0);
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.tap(find.byType(Scrollable));
expect(log, equals(<String>['tap 21']));
await tester.tap(find.byType(Scrollable)); // should stop the fling but not tap anything
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
expect(log, equals(<String>['tap 21']));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18', 'tap 31']));
}, skip: Platform.isMacOS); // Skip due to https://github.com/flutter/flutter/issues/6961
expect(log, equals(<String>['tap 21', 'tap 35']));
});
testWidgets('fling and wait and tap', (WidgetTester tester) async {
final List<String> log = <String>[];
final List<Widget> textWidgets = <Widget>[];
for (int i = 0; i < 250; i++)
textWidgets.add(new GestureDetector(onTap: () { log.add('tap $i'); }, child: new Text('$i')));
for (int i = 0; i < 250; i += 1)
textWidgets.add(new GestureDetector(onTap: () { log.add('tap $i'); }, child: new Text('$i', style: testFont)));
await tester.pumpWidget(new ListView(children: textWidgets));
expect(log, equals(<String>[]));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
expect(log, equals(<String>['tap 21']));
await tester.fling(find.byType(Scrollable), const Offset(0.0, -200.0), 1000.0);
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18']));
await tester.pump(const Duration(seconds: 50));
expect(log, equals(<String>['tap 18']));
expect(log, equals(<String>['tap 21']));
await tester.pump(const Duration(seconds: 50)); // long wait, so the fling will have ended at the end of it
expect(log, equals(<String>['tap 21']));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
expect(log, equals(<String>['tap 18', 'tap 42']));
}, skip: Platform.isMacOS); // Skip due to https://github.com/flutter/flutter/issues/6961
expect(log, equals(<String>['tap 21', 'tap 48']));
});
}
This diff is collapsed.
......@@ -282,9 +282,13 @@ class TestAsyncUtils {
}
}
static bool _stripAsynchronousSuspensions(String line) {
return line != '<asynchronous suspension>';
}
static _StackEntry _findResponsibleMethod(StackTrace rawStack, String method, StringBuffer errors) {
assert(method == 'guard' || method == 'guardSync');
final List<String> stack = rawStack.toString().split('\n');
final List<String> stack = rawStack.toString().split('\n').where(_stripAsynchronousSuspensions).toList();
assert(stack.last == '');
stack.removeLast();
final RegExp getClassPattern = new RegExp(r'^#[0-9]+ +([^. ]+)');
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/services.dart';
......@@ -48,7 +49,7 @@ class TestTextInput {
<dynamic>[_client, value.toJSON()],
),
),
(_) {},
(ByteData data) { /* response from framework is discarded */ },
);
}
......
......@@ -51,7 +51,17 @@ void testWidgets(String description, WidgetTesterCallback callback, {
final WidgetTester tester = new WidgetTester._(binding);
timeout ??= binding.defaultTestTimeout;
test_package.group('-', () {
test_package.test(description, () => binding.runTest(() => callback(tester), tester._endOfTestVerifications), skip: skip);
test_package.test(
description,
() {
return binding.runTest(
() => callback(tester),
tester._endOfTestVerifications,
description: description ?? '',
);
},
skip: skip,
);
test_package.tearDown(binding.postTest);
}, timeout: timeout);
}
......@@ -109,7 +119,10 @@ Future<Null> benchmarkWidgets(WidgetTesterCallback callback) {
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
assert(binding is! AutomatedTestWidgetsFlutterBinding);
final WidgetTester tester = new WidgetTester._(binding);
return binding.runTest(() => callback(tester), tester._endOfTestVerifications) ?? new Future<Null>.value();
return binding.runTest(
() => callback(tester),
tester._endOfTestVerifications,
) ?? new Future<Null>.value();
}
/// Assert that `actual` matches `matcher`.
......@@ -163,6 +176,9 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
/// Subsequent calls to this is different from [pump] in that it forces a full
/// rebuild of the tree, even if [widget] is the same as the previous call.
/// [pump] will only rebuild the widgets that have changed.
///
/// See also [LiveTestWidgetsFlutterBindingFramePolicy], which affects how
/// this method works when the test is run with `flutter run`.
Future<Null> pumpWidget(Widget widget, [
Duration duration,
EnginePhase phase = EnginePhase.sendSemanticsTree
......@@ -182,6 +198,9 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
///
/// This is a convenience function that just calls
/// [TestWidgetsFlutterBinding.pump].
///
/// See also [LiveTestWidgetsFlutterBindingFramePolicy], which affects how
/// this method works when the test is run with `flutter run`.
@override
Future<Null> pump([
Duration duration,
......@@ -426,23 +445,25 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
/// Tests that just need to add text to widgets like [Input] or [TextField]
/// only need to call [enterText].
Future<Null> showKeyboard(Finder finder) async {
// TODO(hansmuller): Once find.descendant (#7789) lands replace the following
// RHS with state(find.descendant(finder), find.byType(EditableText)).
final EditableTextState editable = state(finder);
if (editable != binding.focusedEditable) {
binding.focusedEditable = editable;
await pump();
}
return null;
return TestAsyncUtils.guard(() async {
// TODO(hansmuller): Once find.descendant (#7789) lands replace the following
// RHS with state(find.descendant(finder), find.byType(EditableText)).
final EditableTextState editable = state(finder);
if (editable != binding.focusedEditable) {
binding.focusedEditable = editable;
await pump();
}
});
}
/// Give the EditableText widget specified by [finder] the focus and
/// enter [text] as if it been provided by the onscreen keyboard.
Future<Null> enterText(Finder finder, String text) async {
await showKeyboard(finder);
testTextInput.enterText(text);
await idle();
return null;
return TestAsyncUtils.guard(() async {
await showKeyboard(finder);
testTextInput.enterText(text);
await idle();
});
}
}
......
......@@ -337,7 +337,7 @@ class AndroidDevice extends Device {
if (debuggingOptions.debuggingEnabled) {
// TODO(devoncarew): Remember the forwarding information (so we can later remove the
// port forwarding).
// port forwarding or set it up again when adb fails on us).
observatoryDiscovery = new ProtocolDiscovery.observatory(
getLogReader(), portForwarder: portForwarder, hostPort: debuggingOptions.observatoryPort);
diagnosticDiscovery = new ProtocolDiscovery.diagnosticService(
......@@ -363,6 +363,8 @@ class AndroidDevice extends Device {
cmd.addAll(<String>['--ez', 'enable-checked-mode', 'true']);
if (debuggingOptions.startPaused)
cmd.addAll(<String>['--ez', 'start-paused', 'true']);
if (debuggingOptions.useTestFonts)
cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']);
}
cmd.add(apk.launchActivity);
final String result = runCheckedSync(cmd);
......@@ -372,9 +374,8 @@ class AndroidDevice extends Device {
return new LaunchResult.failed();
}
if (!debuggingOptions.debuggingEnabled) {
if (!debuggingOptions.debuggingEnabled)
return new LaunchResult.succeeded();
}
// Wait for the service protocol port here. This will complete once the
// device has printed "Observatory is listening on...".
......
......@@ -296,6 +296,7 @@ class AppDomain extends Domain {
final String deviceId = _getStringArg(args, 'deviceId', required: true);
final String projectDirectory = _getStringArg(args, 'projectDirectory', required: true);
final bool startPaused = _getBoolArg(args, 'startPaused') ?? false;
final bool useTestFonts = _getBoolArg(args, 'useTestFonts') ?? false;
final String route = _getStringArg(args, 'route');
final String mode = _getStringArg(args, 'mode');
final String target = _getStringArg(args, 'target');
......@@ -309,10 +310,25 @@ class AppDomain extends Domain {
throw "'$projectDirectory' does not exist";
final BuildMode buildMode = getBuildModeForName(mode) ?? BuildMode.debug;
DebuggingOptions options;
if (buildMode == BuildMode.release) {
options = new DebuggingOptions.disabled(buildMode);
} else {
options = new DebuggingOptions.enabled(
buildMode,
startPaused: startPaused,
useTestFonts: useTestFonts,
);
}
final AppInstance app = await startApp(
device, projectDirectory, target, route,
buildMode, startPaused, enableHotReload);
device,
projectDirectory,
target,
route,
options,
enableHotReload,
);
return <String, dynamic>{
'appId': app.id,
......@@ -324,28 +340,14 @@ class AppDomain extends Domain {
Future<AppInstance> startApp(
Device device, String projectDirectory, String target, String route,
BuildMode buildMode, bool startPaused, bool enableHotReload, {
DebuggingOptions options, bool enableHotReload, {
String applicationBinary,
String projectRootPath,
String packagesFilePath,
String projectAssets,
}) async {
DebuggingOptions options;
switch (buildMode) {
case BuildMode.debug:
case BuildMode.profile:
options = new DebuggingOptions.enabled(buildMode, startPaused: startPaused);
break;
case BuildMode.release:
options = new DebuggingOptions.disabled(buildMode);
break;
default:
throw 'unhandle build mode: $buildMode';
}
if (device.isLocalEmulator && !isEmulatorBuildMode(buildMode))
throw '${toTitleCase(getModeName(buildMode))} mode is not supported for emulators.';
if (device.isLocalEmulator && !isEmulatorBuildMode(options.buildMode))
throw '${toTitleCase(getModeName(options.buildMode))} mode is not supported for emulators.';
// We change the current working directory for the duration of the `start` command.
final Directory cwd = fs.currentDirectory;
......
......@@ -88,6 +88,14 @@ class RunCommand extends RunCommandBase {
defaultsTo: false,
negatable: false,
help: 'Start in a paused mode and wait for a debugger to connect.');
argParser.addFlag('use-test-fonts',
negatable: true,
defaultsTo: false,
help: 'Enable (and default to) the "Ahem" font. This is a special font\n'
'used in tests to remove any dependencies on the font metrics. It\n'
'is enabled when you use "flutter test". Set this flag when running\n'
'a test using "flutter run" for debugging purposes. This flag is\n'
'only available when running in debug mode.');
argParser.addFlag('build',
defaultsTo: true,
help: 'If necessary, build the app before running.');
......@@ -126,18 +134,19 @@ class RunCommand extends RunCommandBase {
hide: !verboseHelp,
help: 'Stay resident after launching the application.');
// Hidden option to enable a benchmarking mode. This will run the given
// application, measure the startup time and the app restart time, write the
// results out to 'refresh_benchmark.json', and exit. This flag is intended
// for use in generating automated flutter benchmarks.
argParser.addFlag('benchmark', negatable: false, hide: !verboseHelp);
argParser.addFlag('benchmark',
negatable: false,
hide: !verboseHelp,
help: 'Enable a benchmarking mode. This will run the given application,\n'
'measure the startup time and the app restart time, write the\n'
'results out to "refresh_benchmark.json", and exit. This flag is\n'
'intended for use in generating automated flutter benchmarks.');
commandValidator = () {
if (!runningWithPrebuiltApplication)
commonCommandValidator();
// When running with a prebuilt application, no command validation is
// necessary.
if (!runningWithPrebuiltApplication)
commonCommandValidator();
};
}
......@@ -196,6 +205,20 @@ class RunCommand extends RunCommandBase {
return super.verifyThenRunCommand();
}
DebuggingOptions _createDebuggingOptions() {
if (getBuildMode() == BuildMode.release) {
return new DebuggingOptions.disabled(getBuildMode());
} else {
return new DebuggingOptions.enabled(
getBuildMode(),
startPaused: argResults['start-paused'],
useTestFonts: argResults['use-test-fonts'],
observatoryPort: observatoryPort,
diagnosticPort: diagnosticPort,
);
}
}
@override
Future<Null> runCommand() async {
......@@ -212,7 +235,7 @@ class RunCommand extends RunCommandBase {
try {
app = await daemon.appDomain.startApp(
device, fs.currentDirectory.path, targetFile, route,
getBuildMode(), argResults['start-paused'], hotMode,
_createDebuggingOptions(), hotMode,
applicationBinary: argResults['use-application-binary'],
projectRootPath: argResults['project-root'],
packagesFilePath: argResults['packages'],
......@@ -229,19 +252,6 @@ class RunCommand extends RunCommandBase {
if (device.isLocalEmulator && !isEmulatorBuildMode(getBuildMode()))
throwToolExit('${toTitleCase(getModeName(getBuildMode()))} mode is not supported for emulators.');
DebuggingOptions options;
if (getBuildMode() == BuildMode.release) {
options = new DebuggingOptions.disabled(getBuildMode());
} else {
options = new DebuggingOptions.enabled(
getBuildMode(),
startPaused: argResults['start-paused'],
observatoryPort: observatoryPort,
diagnosticPort: diagnosticPort,
);
}
if (hotMode) {
if (!device.supportsHotMode)
throwToolExit('Hot mode is not supported by this device. Run with --no-hot.');
......@@ -258,7 +268,7 @@ class RunCommand extends RunCommandBase {
runner = new HotRunner(
device,
target: targetFile,
debuggingOptions: options,
debuggingOptions: _createDebuggingOptions(),
benchmarkMode: argResults['benchmark'],
applicationBinary: argResults['use-application-binary'],
kernelFilePath: argResults['kernel'],
......@@ -271,7 +281,7 @@ class RunCommand extends RunCommandBase {
runner = new ColdRunner(
device,
target: targetFile,
debuggingOptions: options,
debuggingOptions: _createDebuggingOptions(),
traceStartup: traceStartup,
applicationBinary: argResults['use-application-binary'],
stayResident: stayResident,
......
......@@ -279,12 +279,14 @@ abstract class Device {
class DebuggingOptions {
DebuggingOptions.enabled(this.buildMode, {
this.startPaused: false,
this.useTestFonts: false,
this.observatoryPort,
this.diagnosticPort
}) : debuggingEnabled = true;
DebuggingOptions.disabled(this.buildMode) :
debuggingEnabled = false,
useTestFonts = false,
startPaused = false,
observatoryPort = null,
diagnosticPort = null;
......@@ -293,6 +295,7 @@ class DebuggingOptions {
final BuildMode buildMode;
final bool startPaused;
final bool useTestFonts;
final int observatoryPort;
final int diagnosticPort;
......
......@@ -228,6 +228,9 @@ class IOSDevice extends Device {
if (debuggingOptions.startPaused)
launchArguments.add("--start-paused");
if (debuggingOptions.useTestFonts)
launchArguments.add("--use-test-fonts");
if (debuggingOptions.debuggingEnabled) {
launchArguments.add("--enable-checked-mode");
......
......@@ -455,6 +455,8 @@ class IOSSimulator extends Device {
args.add('--enable-checked-mode');
if (debuggingOptions.startPaused)
args.add('--start-paused');
if (debuggingOptions.useTestFonts)
args.add('--use-test-fonts');
final int observatoryPort = await debuggingOptions.findBestObservatoryPort();
args.add('--observatory-port=$observatoryPort');
......
......@@ -210,9 +210,8 @@ abstract class ResidentRunner {
}
Future<Null> connectToServiceProtocol(Uri uri, {String isolateFilter}) async {
if (!debuggingOptions.debuggingEnabled) {
if (!debuggingOptions.debuggingEnabled)
return new Future<Null>.error('Error the service protocol is not enabled.');
}
vmService = VMService.connect(uri);
printTrace('Connected to service protocol: $uri');
await vmService.getVM();
......
......@@ -100,9 +100,8 @@ class ColdRunner extends ResidentRunner {
startTime.stop();
// Connect to observatory.
if (debuggingOptions.debuggingEnabled) {
if (debuggingOptions.debuggingEnabled)
await connectToServiceProtocol(_result.observatoryUri);
}
if (_result.hasObservatory) {
connectionInfoCompleter?.complete(new DebugConnectionInfo(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment