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
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index c84d3fc..a56ed9c 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-5d9a6422577d95c242f45f48c47b431f7cf3c548
+1fed16fb25f3f7afc8303116d6ef707c4043c127
diff --git a/dev/automated_tests/flutter_test/test_async_utils_guarded_expectation.txt b/dev/automated_tests/flutter_test/test_async_utils_guarded_expectation.txt
index 4601a54..e61ce99 100644
--- a/dev/automated_tests/flutter_test/test_async_utils_guarded_expectation.txt
+++ b/dev/automated_tests/flutter_test/test_async_utils_guarded_expectation.txt
@@ -15,7 +15,8 @@
<<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\.
diff --git a/dev/automated_tests/flutter_test/test_async_utils_unguarded_expectation.txt b/dev/automated_tests/flutter_test/test_async_utils_unguarded_expectation.txt
index de23568..1705102 100644
--- a/dev/automated_tests/flutter_test/test_async_utils_unguarded_expectation.txt
+++ b/dev/automated_tests/flutter_test/test_async_utils_unguarded_expectation.txt
@@ -14,7 +14,8 @@
<<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.
diff --git a/dev/benchmarks/microbenchmarks/lib/stocks/build_bench.dart b/dev/benchmarks/microbenchmarks/lib/stocks/build_bench.dart
index f3ca937..ed81233 100644
--- a/dev/benchmarks/microbenchmarks/lib/stocks/build_bench.dart
+++ b/dev/benchmarks/microbenchmarks/lib/stocks/build_bench.dart
@@ -22,7 +22,7 @@
// 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;
diff --git a/dev/benchmarks/microbenchmarks/lib/stocks/layout_bench.dart b/dev/benchmarks/microbenchmarks/lib/stocks/layout_bench.dart
index b317210..663adc8 100644
--- a/dev/benchmarks/microbenchmarks/lib/stocks/layout_bench.dart
+++ b/dev/benchmarks/microbenchmarks/lib/stocks/layout_bench.dart
@@ -21,7 +21,7 @@
// 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;
diff --git a/dev/tools/dartdoc.dart b/dev/tools/dartdoc.dart
index 3ae633a..cc02754 100644
--- a/dev/tools/dartdoc.dart
+++ b/dev/tools/dartdoc.dart
@@ -108,9 +108,9 @@
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',
diff --git a/examples/flutter_gallery/test/calculator/smoke_test.dart b/examples/flutter_gallery/test/calculator/smoke_test.dart
index 93ed624..1292617 100644
--- a/examples/flutter_gallery/test/calculator/smoke_test.dart
+++ b/examples/flutter_gallery/test/calculator/smoke_test.dart
@@ -9,7 +9,7 @@
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".
diff --git a/examples/flutter_gallery/test/example_code_display_test.dart b/examples/flutter_gallery/test/example_code_display_test.dart
index fcc9142..32e1922 100644
--- a/examples/flutter_gallery/test/example_code_display_test.dart
+++ b/examples/flutter_gallery/test/example_code_display_test.dart
@@ -9,7 +9,7 @@
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
diff --git a/examples/flutter_gallery/test/pesto_test.dart b/examples/flutter_gallery/test/pesto_test.dart
index a1c57a2..13257ec 100644
--- a/examples/flutter_gallery/test/pesto_test.dart
+++ b/examples/flutter_gallery/test/pesto_test.dart
@@ -9,7 +9,7 @@
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 {
diff --git a/examples/flutter_gallery/test/simple_smoke_test.dart b/examples/flutter_gallery/test/simple_smoke_test.dart
index 7fcbea3..776c5a7 100644
--- a/examples/flutter_gallery/test/simple_smoke_test.dart
+++ b/examples/flutter_gallery/test/simple_smoke_test.dart
@@ -9,7 +9,7 @@
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
diff --git a/examples/flutter_gallery/test/update_test.dart b/examples/flutter_gallery/test/update_test.dart
index 4cd742b..24cbf11 100644
--- a/examples/flutter_gallery/test/update_test.dart
+++ b/examples/flutter_gallery/test/update_test.dart
@@ -13,7 +13,7 @@
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 {
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
index 75e4b22..306dadb 100644
--- a/packages/flutter/lib/src/rendering/editable.dart
+++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -40,6 +40,17 @@
/// 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.
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index 412aafc..7ae0e72 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
),
),
));
-
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'));
});
}
diff --git a/packages/flutter/test/rendering/paragraph_test.dart b/packages/flutter/test/rendering/paragraph_test.dart
index 9c514c1..99f19dc 100644
--- a/packages/flutter/test/rendering/paragraph_test.dart
+++ b/packages/flutter/test/rendering/paragraph_test.dart
@@ -76,7 +76,10 @@
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 @@
}
// 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);
diff --git a/packages/flutter/test/widgets/scrollable_fling_test.dart b/packages/flutter/test/widgets/scrollable_fling_test.dart
index 656aee2..fb788cf 100644
--- a/packages/flutter/test/widgets/scrollable_fling_test.dart
+++ b/packages/flutter/test/widgets/scrollable_fling_test.dart
@@ -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 @@
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']));
+ 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 21']));
await tester.tap(find.byType(Scrollable));
await tester.pump(const Duration(milliseconds: 50));
- expect(log, equals(<String>['tap 18']));
- 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']));
+ });
}
diff --git a/packages/flutter_test/lib/src/binding.dart b/packages/flutter_test/lib/src/binding.dart
index 7bb8ae7..3223f94 100644
--- a/packages/flutter_test/lib/src/binding.dart
+++ b/packages/flutter_test/lib/src/binding.dart
@@ -159,6 +159,9 @@
///
/// The supplied EnginePhase is the final phase reached during the pump pass;
/// if not supplied, the whole pass is executed.
+ ///
+ /// See also [LiveTestWidgetsFlutterBindingFramePolicy], which affects how
+ /// this method works when the test is run with `flutter run`.
Future<Null> pump([ Duration duration, EnginePhase newPhase = EnginePhase.sendSemanticsTree ]);
/// Artificially calls dispatchLocaleChanged on the Widget binding,
@@ -175,13 +178,20 @@
/// Acts as if the application went idle.
///
/// Runs all remaining microtasks, including those scheduled as a result of
- /// running them, until there are no more microtasks scheduled.
+ /// running them, until there are no more microtasks scheduled. Then, runs any
+ /// previously scheduled timers with zero time, and completes the returned future.
///
- /// Does not run timers. May result in an infinite loop or run out of memory
- /// if microtasks continue to recursively schedule new microtasks.
+ /// May result in an infinite loop or run out of memory if microtasks continue
+ /// to recursively schedule new microtasks. Will not run any timers scheduled
+ /// after this method was invoked, even if they are zero-time timers.
Future<Null> idle() {
- TestAsyncUtils.guardSync();
- return new Future<Null>.value();
+ return TestAsyncUtils.guard(() {
+ final Completer<Null> completer = new Completer<Null>();
+ Timer.run(() {
+ completer.complete(null);
+ });
+ return completer.future;
+ });
}
/// Convert the given point from the global coodinate system (as used by
@@ -272,7 +282,11 @@
///
/// The `invariantTester` argument is called after the `testBody`'s [Future]
/// completes. If it throws, then the test is marked as failed.
- Future<Null> runTest(Future<Null> testBody(), VoidCallback invariantTester);
+ ///
+ /// The `description` is used by the [LiveTestWidgetsFlutterBinding] to
+ /// show a label on the screen during the test. The description comes from
+ /// the value passed to [testWidgets]. It must not be null.
+ Future<Null> runTest(Future<Null> testBody(), VoidCallback invariantTester, { String description: '' });
/// This is called during test execution before and after the body has been
/// executed.
@@ -305,7 +319,8 @@
_currentTestCompleter.complete(null);
}
- Future<Null> _runTest(Future<Null> testBody(), VoidCallback invariantTester) {
+ Future<Null> _runTest(Future<Null> testBody(), VoidCallback invariantTester, String description) {
+ assert(description != null);
assert(inTest);
_oldExceptionHandler = FlutterError.onError;
int _exceptionCount = 0; // number of un-taken exceptions
@@ -392,6 +407,8 @@
information.writeln('At the time of the failure, the widget tree looked as follows:');
information.writeln('# ${treeDump.split("\n").takeWhile((String s) => s != "").join("\n# ")}');
}
+ if (description.isNotEmpty)
+ information.writeln('The test description was:\n$description');
}
));
assert(_parentZone != null);
@@ -514,7 +531,7 @@
@override
Future<Null> idle() {
final Future<Null> result = super.idle();
- _fakeAsync.flushMicrotasks();
+ _fakeAsync.elapse(const Duration());
return result;
}
@@ -551,7 +568,8 @@
}
@override
- Future<Null> runTest(Future<Null> testBody(), VoidCallback invariantTester) {
+ Future<Null> runTest(Future<Null> testBody(), VoidCallback invariantTester, { String description: '' }) {
+ assert(description != null);
assert(!inTest);
assert(_fakeAsync == null);
assert(_clock == null);
@@ -560,7 +578,7 @@
Future<Null> testBodyResult;
_fakeAsync.run((FakeAsync fakeAsync) {
assert(fakeAsync == _fakeAsync);
- testBodyResult = _runTest(testBody, invariantTester);
+ testBodyResult = _runTest(testBody, invariantTester, description);
assert(inTest);
});
// testBodyResult is a Future that was created in the Zone of the fakeAsync.
@@ -603,6 +621,35 @@
}
+/// Available policies for how a [LiveTestWidgetsFlutterBinding] should paint
+/// frames.
+///
+/// These values are set on the binding's
+/// [LiveTestWidgetsFlutterBinding.framePolicy] property. The default is
+/// [fadePointers].
+enum LiveTestWidgetsFlutterBindingFramePolicy {
+ /// Strictly show only frames that are explicitly pumped. This most closely
+ /// matches the behavior of tests when run under `flutter test`.
+ onlyPumps,
+
+ /// Show pumped frames, and additionally schedule and run frames to fade
+ /// out the pointer crosshairs and other debugging information shown by
+ /// the binding.
+ ///
+ /// This can result in additional frames being pumped beyond those that
+ /// the test itself requests, which can cause differences in behavior.
+ fadePointers,
+
+ /// Show every frame that the framework requests, even if the frames are not
+ /// explicitly pumped.
+ ///
+ /// This can help with orienting the developer when looking at
+ /// heavily-animated situations, and will almost certainly result in
+ /// additional frames being pumped beyond those that the test itself requests,
+ /// which can cause differences in behavior.
+ fullyLive,
+}
+
/// A variant of [TestWidgetsFlutterBinding] for executing tests in
/// the `flutter run` environment, on a device. This is intended to
/// allow interactive test development.
@@ -611,13 +658,21 @@
/// a device from a development computer, see the [flutter_driver]
/// package and the `flutter drive` command.
///
-/// This binding overrides the default [SchedulerBinding] behavior to
-/// ensure that tests work in the same way in this environment as they
-/// would under the [AutomatedTestWidgetsFlutterBinding]. To override
-/// this (and see intermediate frames that the test does not
-/// explicitly trigger), set [allowAllFrames] to true. (This is likely
-/// to make tests fail, though, especially if e.g. they test how many
-/// times a particular widget was built.)
+/// When running tests using `flutter run`, consider adding the
+/// `--use-test-fonts` argument so that the fonts used match those used under
+/// `flutter test`. (This forces all text to use the "Ahem" font, which is a
+/// font that covers ASCII characters and gives them all the appearance of a
+/// square whose size equals the font size.)
+///
+/// This binding overrides the default [SchedulerBinding] behavior to ensure
+/// that tests work in the same way in this environment as they would under the
+/// [AutomatedTestWidgetsFlutterBinding]. To override this (and see intermediate
+/// frames that the test does not explicitly trigger), set [framePolicy] to
+/// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive]. (This is likely to
+/// make tests fail, though, especially if e.g. they test how many times a
+/// particular widget was built.) The default behavior is to show pumped frames
+/// and a few additional frames when pointers are triggered (to animate the
+/// pointer crosshairs).
///
/// This binding does not support the [EnginePhase] argument to
/// [pump]. (There would be no point setting it to a value that
@@ -644,6 +699,7 @@
Completer<Null> _pendingFrame;
bool _expectingFrame = false;
+ bool _viewNeedsPaint = false;
/// Whether to have [pump] with a duration only pump a single frame
/// (as would happen in a normal test environment using
@@ -652,31 +708,46 @@
/// asynchronous pause in the test (as would normally happen when
/// running an application with [WidgetsFlutterBinding]).
///
- /// `false` is the default behavior, which is to only pump once.
+ /// * [LiveTestWidgetsFlutterBindingFramePolicy.fadePointers] is the default
+ /// behavior, which is to only pump once, except when there has been some
+ /// activity with [TestPointer]s, in which case those are shown and may pump
+ /// additional frames.
///
- /// `true` allows all frame requests from the engine to be serviced.
+ /// * [LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps] is the strictest
+ /// behavior, which is to only pump once. This most closely matches the
+ /// [AutomatedTestWidgetsFlutterBinding] (`flutter test`) behavior.
///
- /// Setting this to `true` means pumping extra frames, which might
- /// involve calling builders more, or calling paint callbacks more,
- /// etc, which might interfere with the test. If you know your test
- /// file wouldn't be affected by this, you can set it to true
- /// persistently in that particular test file. To set this to `true`
- /// while still allowing the test file to work as a normal test, add
- /// the following code to your test file at the top of your `void
- /// main() { }` function, before calls to `testWidgets`:
+ /// * [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive] allows all frame
+ /// requests from the engine to be serviced, even those the test did not
+ /// explicitly pump.
+ ///
+ /// Setting this to anything other than
+ /// [LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps] means pumping extra
+ /// frames, which might involve calling builders more, or calling paint
+ /// callbacks more, etc, which might interfere with the test. If you know your
+ /// test file wouldn't be affected by this, you can set it to
+ /// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive] persistently in that
+ /// particular test file. To set this to
+ /// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive] while still allowing
+ /// the test file to work as a normal test, add the following code to your
+ /// test file at the top of your `void main() { }` function, before calls to
+ /// [testWidgets]:
///
/// ```dart
/// TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
/// if (binding is LiveTestWidgetsFlutterBinding)
- /// binding.allowAllFrames = true;
+ /// binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
/// ```
- bool allowAllFrames = false;
+ LiveTestWidgetsFlutterBindingFramePolicy framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fadePointers;
@override
void handleBeginFrame(Duration rawTimeStamp) {
- if (_expectingFrame || allowAllFrames)
+ if (_expectingFrame ||
+ (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.fullyLive) ||
+ (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.fadePointers && _viewNeedsPaint))
super.handleBeginFrame(rawTimeStamp);
- if (_expectingFrame) {
+ _viewNeedsPaint = false;
+ if (_expectingFrame) { // set during pump
assert(_pendingFrame != null);
_pendingFrame.complete(); // unlocks the test API
_pendingFrame = null;
@@ -689,13 +760,21 @@
@override
void initRenderView() {
assert(renderView == null);
- renderView = new _LiveTestRenderView(configuration: createViewConfiguration());
+ renderView = new _LiveTestRenderView(
+ configuration: createViewConfiguration(),
+ onNeedPaint: _handleViewNeedsPaint,
+ );
renderView.scheduleInitialFrame();
}
@override
_LiveTestRenderView get renderView => super.renderView;
+ void _handleViewNeedsPaint() {
+ _viewNeedsPaint = true;
+ renderView.markNeedsPaint();
+ }
+
/// An object to which real device events should be routed.
///
/// Normally, device events are silently dropped. However, if this property is
@@ -719,7 +798,7 @@
if (!event.down)
renderView._pointers[event.pointer].decay = _kPointerDecay;
}
- renderView.markNeedsPaint();
+ _handleViewNeedsPaint();
super.dispatchEvent(event, result, source: source);
break;
case TestBindingEventSource.device:
@@ -751,10 +830,12 @@
}
@override
- Future<Null> runTest(Future<Null> testBody(), VoidCallback invariantTester) async {
+ Future<Null> runTest(Future<Null> testBody(), VoidCallback invariantTester, { String description: '' }) async {
+ assert(description != null);
assert(!inTest);
_inTest = true;
- return _runTest(testBody, invariantTester);
+ renderView._setDescription(description);
+ return _runTest(testBody, invariantTester, description);
}
@override
@@ -857,7 +938,8 @@
class _LiveTestRenderView extends RenderView {
_LiveTestRenderView({
- ViewConfiguration configuration
+ ViewConfiguration configuration,
+ this.onNeedPaint,
}) : super(configuration: configuration);
@override
@@ -865,8 +947,28 @@
@override
set configuration(covariant TestViewConfiguration value) { super.configuration = value; }
+ final VoidCallback onNeedPaint;
+
final Map<int, _LiveTestPointerRecord> _pointers = <int, _LiveTestPointerRecord>{};
+ TextPainter _label;
+ static const TextStyle _labelStyle = const TextStyle(
+ fontFamily: 'sans-serif',
+ fontSize: 10.0,
+ );
+ void _setDescription(String value) {
+ assert(value != null);
+ if (value.isEmpty) {
+ _label = null;
+ return;
+ }
+ _label ??= new TextPainter(textAlign: TextAlign.left);
+ _label.text = new TextSpan(text: value, style: _labelStyle);
+ _label.layout();
+ if (onNeedPaint != null)
+ onNeedPaint();
+ }
+
@override
bool hitTest(HitTestResult result, { Point position }) {
final Matrix4 transform = configuration.toHitTestMatrix();
@@ -906,9 +1008,10 @@
.where((int pointer) => _pointers[pointer].decay == 0)
.toList()
.forEach(_pointers.remove);
- if (dirty)
- scheduleMicrotask(markNeedsPaint);
+ if (dirty && onNeedPaint != null)
+ scheduleMicrotask(onNeedPaint);
}
+ _label?.paint(context.canvas, offset - const Offset(0.0, 10.0));
}
}
diff --git a/packages/flutter_test/lib/src/test_async_utils.dart b/packages/flutter_test/lib/src/test_async_utils.dart
index 100089c..bba63a4 100644
--- a/packages/flutter_test/lib/src/test_async_utils.dart
+++ b/packages/flutter_test/lib/src/test_async_utils.dart
@@ -282,9 +282,13 @@
}
}
+ 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]+ +([^. ]+)');
diff --git a/packages/flutter_test/lib/src/test_text_input.dart b/packages/flutter_test/lib/src/test_text_input.dart
index e4c3629..f7631f8 100644
--- a/packages/flutter_test/lib/src/test_text_input.dart
+++ b/packages/flutter_test/lib/src/test_text_input.dart
@@ -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 @@
<dynamic>[_client, value.toJSON()],
),
),
- (_) {},
+ (ByteData data) { /* response from framework is discarded */ },
);
}
diff --git a/packages/flutter_test/lib/src/widget_tester.dart b/packages/flutter_test/lib/src/widget_tester.dart
index 8fa900c..88e0afe 100644
--- a/packages/flutter_test/lib/src/widget_tester.dart
+++ b/packages/flutter_test/lib/src/widget_tester.dart
@@ -51,7 +51,17 @@
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 @@
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 @@
/// 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 @@
///
/// 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 @@
/// 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();
+ });
}
}
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 2e1bf5e..9bc7f22 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -337,7 +337,7 @@
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 @@
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 @@
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...".
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index 16da917..50483fe 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -296,6 +296,7 @@
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 @@
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 @@
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;
diff --git a/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart b/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
index dcce29e..e399fc6 100644
--- a/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
+++ b/packages/flutter_tools/lib/src/commands/fuchsia_reload.dart
@@ -25,21 +25,6 @@
// -g //lib/flutter/examples/flutter_gallery:flutter_gallery
class FuchsiaReloadCommand extends FlutterCommand {
- String _fuchsiaRoot;
- String _projectRoot;
- String _projectName;
- String _binaryName;
- String _fuchsiaProjectPath;
- String _target;
- String _address;
- String _dotPackagesPath;
-
- @override
- final String name = 'fuchsia_reload';
-
- @override
- final String description = 'Hot reload on Fuchsia.';
-
FuchsiaReloadCommand() {
addBuildModeFlags(defaultToRelease: false);
argParser.addOption('address',
@@ -58,7 +43,7 @@
help: 'GN target of the application, e.g //path/to/app:app');
argParser.addOption('name-override',
abbr: 'n',
- help: 'On-device name of the application binary');
+ help: 'On-device name of the application binary.');
argParser.addOption('target',
abbr: 't',
defaultsTo: flx.defaultMainPath,
@@ -67,6 +52,21 @@
}
@override
+ final String name = 'fuchsia_reload';
+
+ @override
+ final String description = 'Hot reload on Fuchsia.';
+
+ String _fuchsiaRoot;
+ String _projectRoot;
+ String _projectName;
+ String _binaryName;
+ String _fuchsiaProjectPath;
+ String _target;
+ String _address;
+ String _dotPackagesPath;
+
+ @override
Future<Null> runCommand() async {
Cache.releaseLockEarly();
@@ -74,37 +74,34 @@
// Find the network ports used on the device by VM service instances.
final List<int> servicePorts = await _getServicePorts();
- if (servicePorts.isEmpty) {
- throwToolExit("Couldn't find any running Observatory instances.");
- }
- for (int port in servicePorts) {
- printTrace("Fuchsia service port: $port");
- }
+ if (servicePorts.isEmpty)
+ throwToolExit('Couldn\'t find any running Observatory instances.');
+ for (int port in servicePorts)
+ printTrace('Fuchsia service port: $port');
// Check that there are running VM services on the returned
// ports, and find the Isolates that are running the target app.
- final String isolateName = "$_binaryName\$main";
+ final String isolateName = '$_binaryName\$main';
final List<int> targetPorts = await _filterPorts(servicePorts, isolateName);
- if (targetPorts.isEmpty) {
- throwToolExit("No VMs found running $_binaryName");
- }
- for (int port in targetPorts) {
- printTrace("Found $_binaryName at $port");
- }
+ if (targetPorts.isEmpty)
+ throwToolExit('No VMs found running $_binaryName.');
+ for (int port in targetPorts)
+ printTrace('Found $_binaryName at $port');
// Set up a device and hot runner and attach the hot runner to the first
// vm service we found.
final int firstPort = targetPorts[0];
- final String fullAddress = "$_address:$firstPort";
+ final String fullAddress = '$_address:$firstPort';
final FuchsiaDevice device = new FuchsiaDevice(fullAddress);
final HotRunner hotRunner = new HotRunner(
- device,
- debuggingOptions: new DebuggingOptions.enabled(getBuildMode()),
- target: _target,
- projectRootPath: _fuchsiaProjectPath,
- packagesFilePath: _dotPackagesPath);
- final Uri observatoryUri = Uri.parse("http://$fullAddress");
- printStatus("Connecting to $_binaryName at $observatoryUri");
+ device,
+ debuggingOptions: new DebuggingOptions.enabled(getBuildMode()),
+ target: _target,
+ projectRootPath: _fuchsiaProjectPath,
+ packagesFilePath: _dotPackagesPath,
+ );
+ final Uri observatoryUri = Uri.parse('http://$fullAddress');
+ printStatus('Connecting to $_binaryName at $observatoryUri');
await hotRunner.attach(observatoryUri, isolateFilter: isolateName);
}
@@ -112,20 +109,19 @@
Future<List<int>> _filterPorts(List<int> ports, String isolateFilter) async {
final List<int> result = <int>[];
for (int port in ports) {
- final String addr = "http://$_address:$port";
+ final String addr = 'http://$_address:$port';
final Uri uri = Uri.parse(addr);
final VMService vmService = VMService.connect(uri);
await vmService.getVM();
await vmService.waitForViews();
if (vmService.vm.firstView == null) {
- printTrace("Found no views at $addr");
+ printTrace('Found no views at $addr');
continue;
}
for (FlutterView v in vmService.vm.views) {
- printTrace("At $addr, found view: ${v.uiIsolate.name}");
- if (v.uiIsolate.name.indexOf(isolateFilter) == 0) {
+ printTrace('At $addr, found view: ${v.uiIsolate.name}');
+ if (v.uiIsolate.name.indexOf(isolateFilter) == 0)
result.add(port);
- }
}
}
return result;
@@ -133,48 +129,36 @@
void _validateArguments() {
_fuchsiaRoot = argResults['fuchsia-root'];
- if (_fuchsiaRoot == null) {
- throwToolExit(
- "Please give the location of the Fuchsia tree with --fuchsia-root");
- }
- if (!_directoryExists(_fuchsiaRoot)) {
- throwToolExit("Specified --fuchsia-root '$_fuchsiaRoot' does not exist");
- }
+ if (_fuchsiaRoot == null)
+ throwToolExit('Please give the location of the Fuchsia tree with --fuchsia-root.');
+ if (!_directoryExists(_fuchsiaRoot))
+ throwToolExit('Specified --fuchsia-root "$_fuchsiaRoot" does not exist.');
_address = argResults['address'];
- if (_address == null) {
- throwToolExit(
- "Give the address of the device running Fuchsia with --address");
- }
+ if (_address == null)
+ throwToolExit('Give the address of the device running Fuchsia with --address.');
final List<String> gnTarget = _extractPathAndName(argResults['gn-target']);
_projectRoot = gnTarget[0];
_projectName = gnTarget[1];
- _fuchsiaProjectPath = "$_fuchsiaRoot/$_projectRoot";
- if (!_directoryExists(_fuchsiaProjectPath)) {
- throwToolExit(
- "Target does not exist in the Fuchsia tree: $_fuchsiaProjectPath");
- }
+ _fuchsiaProjectPath = '$_fuchsiaRoot/$_projectRoot';
+ if (!_directoryExists(_fuchsiaProjectPath))
+ throwToolExit('Target does not exist in the Fuchsia tree: $_fuchsiaProjectPath.');
final String relativeTarget = argResults['target'];
- if (relativeTarget == null) {
- throwToolExit('Give the application entry point with --target');
- }
- _target = "$_fuchsiaProjectPath/$relativeTarget";
- if (!_fileExists(_target)) {
- throwToolExit("Couldn't find application entry point at $_target");
- }
+ if (relativeTarget == null)
+ throwToolExit('Give the application entry point with --target.');
+ _target = '$_fuchsiaProjectPath/$relativeTarget';
+ if (!_fileExists(_target))
+ throwToolExit('Couldn\'t find application entry point at $_target.');
final String buildType = argResults['build-type'];
- if (buildType == null) {
- throwToolExit("Give the build type with --build-type");
- }
- final String packagesFileName = "${_projectName}_dart_package.packages";
- _dotPackagesPath =
- "$_fuchsiaRoot/out/$buildType/gen/$_projectRoot/$packagesFileName";
- if (!_fileExists(_dotPackagesPath)) {
- throwToolExit("Couldn't find .packages file at $_dotPackagesPath");
- }
+ if (buildType == null)
+ throwToolExit('Give the build type with --build-type.');
+ final String packagesFileName = '${_projectName}_dart_package.packages';
+ _dotPackagesPath = '$_fuchsiaRoot/out/$buildType/gen/$_projectRoot/$packagesFileName';
+ if (!_fileExists(_dotPackagesPath))
+ throwToolExit('Couldn\'t find .packages file at $_dotPackagesPath.');
final String nameOverride = argResults['name-override'];
if (nameOverride == null) {
@@ -186,26 +170,23 @@
List<String> _extractPathAndName(String gnTarget) {
final String errorMessage =
- "fuchsia_reload --target '$gnTarget' should have the form: "
- "'//path/to/app:name'";
+ 'fuchsia_reload --target "$gnTarget" should have the form: '
+ '"//path/to/app:name"';
// Separate strings like //path/to/target:app into [path/to/target, app]
final int lastColon = gnTarget.lastIndexOf(':');
- if (lastColon < 0) {
+ if (lastColon < 0)
throwToolExit(errorMessage);
- }
final String name = gnTarget.substring(lastColon + 1);
// Skip '//' and chop off after :
- if ((gnTarget.length < 3) || (gnTarget[0] != '/') || (gnTarget[1] != '/')) {
+ if ((gnTarget.length < 3) || (gnTarget[0] != '/') || (gnTarget[1] != '/'))
throwToolExit(errorMessage);
- }
final String path = gnTarget.substring(2, lastColon);
return <String>[path, name];
}
Future<List<int>> _getServicePorts() async {
- final FuchsiaDeviceCommandRunner runner =
- new FuchsiaDeviceCommandRunner(_fuchsiaRoot);
- final List<String> lsOutput = await runner.run("ls /tmp/dart.services");
+ final FuchsiaDeviceCommandRunner runner = new FuchsiaDeviceCommandRunner(_fuchsiaRoot);
+ final List<String> lsOutput = await runner.run('ls /tmp/dart.services');
final List<int> ports = <int>[];
for (String s in lsOutput) {
final String trimmed = s.trim();
@@ -213,9 +194,8 @@
final String lastWord = trimmed.substring(lastSpace + 1);
if ((lastWord != '.') && (lastWord != '..')) {
final int value = int.parse(lastWord, onError: (_) => null);
- if (value != null) {
+ if (value != null)
ports.add(value);
- }
}
}
return ports;
@@ -242,26 +222,24 @@
Future<List<String>> run(String command) async {
final int tag = _rng.nextInt(999999);
- const String kNetRunCommand = "out/build-magenta/tools/netruncmd";
+ const String kNetRunCommand = 'out/build-magenta/tools/netruncmd';
final String netruncmd = fs.path.join(_fuchsiaRoot, kNetRunCommand);
- const String kNetCP = "out/build-magenta/tools/netcp";
+ const String kNetCP = 'out/build-magenta/tools/netcp';
final String netcp = fs.path.join(_fuchsiaRoot, kNetCP);
- final String remoteStdout = "/tmp/netruncmd.$tag";
- final String localStdout = "${fs.systemTempDirectory.path}/netruncmd.$tag";
- final String redirectedCommand = "$command > $remoteStdout";
+ final String remoteStdout = '/tmp/netruncmd.$tag';
+ final String localStdout = '${fs.systemTempDirectory.path}/netruncmd.$tag';
+ final String redirectedCommand = '$command > $remoteStdout';
// Run the command with output directed to a tmp file.
ProcessResult result =
- await Process.run(netruncmd, <String>[":", redirectedCommand]);
- if (result.exitCode != 0) {
+ await Process.run(netruncmd, <String>[':', redirectedCommand]);
+ if (result.exitCode != 0)
return null;
- }
// Copy that file to the local filesystem.
- result = await Process.run(netcp, <String>[":$remoteStdout", localStdout]);
+ result = await Process.run(netcp, <String>[':$remoteStdout', localStdout]);
// Try to delete the remote file. Don't care about the result;
- Process.run(netruncmd, <String>[":", "rm $remoteStdout"]);
- if (result.exitCode != 0) {
+ Process.run(netruncmd, <String>[':', 'rm $remoteStdout']);
+ if (result.exitCode != 0)
return null;
- }
// Read the local file.
final File f = fs.file(localStdout);
List<String> lines;
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index e159780..50fe3ed 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -88,6 +88,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
runner = new ColdRunner(
device,
target: targetFile,
- debuggingOptions: options,
+ debuggingOptions: _createDebuggingOptions(),
traceStartup: traceStartup,
applicationBinary: argResults['use-application-binary'],
stayResident: stayResident,
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index a69951e..7489153 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -279,12 +279,14 @@
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 @@
final BuildMode buildMode;
final bool startPaused;
+ final bool useTestFonts;
final int observatoryPort;
final int diagnosticPort;
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 8f642db..f9cd43b 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -228,6 +228,9 @@
if (debuggingOptions.startPaused)
launchArguments.add("--start-paused");
+ if (debuggingOptions.useTestFonts)
+ launchArguments.add("--use-test-fonts");
+
if (debuggingOptions.debuggingEnabled) {
launchArguments.add("--enable-checked-mode");
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 3ecd761..fbd99dd 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -455,6 +455,8 @@
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');
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 94ab4bc3..d843fee 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -210,9 +210,8 @@
}
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();
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart
index 7bd6ced..a31a326 100644
--- a/packages/flutter_tools/lib/src/run_cold.dart
+++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -100,9 +100,8 @@
startTime.stop();
// Connect to observatory.
- if (debuggingOptions.debuggingEnabled) {
+ if (debuggingOptions.debuggingEnabled)
await connectToServiceProtocol(_result.observatoryUri);
- }
if (_result.hasObservatory) {
connectionInfoCompleter?.complete(new DebugConnectionInfo(