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
This commit is contained in:
parent
c12c019bcc
commit
2a54524337
@ -1 +1 @@
|
||||
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']));
|
||||
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']));
|
||||
});
|
||||
}
|
||||
|
@ -159,6 +159,9 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
///
|
||||
/// 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 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
/// 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 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
///
|
||||
/// 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 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
_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 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
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 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
@override
|
||||
Future<Null> idle() {
|
||||
final Future<Null> result = super.idle();
|
||||
_fakeAsync.flushMicrotasks();
|
||||
_fakeAsync.elapse(const Duration());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -551,7 +568,8 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
}
|
||||
|
||||
@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 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
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 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
|
||||
}
|
||||
|
||||
/// 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 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
/// 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 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
|
||||
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 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
/// 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 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
@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 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
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 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
|
||||
}
|
||||
|
||||
@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 _LiveTestPointerRecord {
|
||||
|
||||
class _LiveTestRenderView extends RenderView {
|
||||
_LiveTestRenderView({
|
||||
ViewConfiguration configuration
|
||||
ViewConfiguration configuration,
|
||||
this.onNeedPaint,
|
||||
}) : super(configuration: configuration);
|
||||
|
||||
@override
|
||||
@ -865,8 +947,28 @@ class _LiveTestRenderView extends RenderView {
|
||||
@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 @@ class _LiveTestRenderView extends RenderView {
|
||||
.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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -25,21 +25,6 @@ import '../vmservice.dart';
|
||||
// -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 @@ class FuchsiaReloadCommand extends FlutterCommand {
|
||||
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,
|
||||
@ -66,6 +51,21 @@ class FuchsiaReloadCommand extends FlutterCommand {
|
||||
'Relative to --gn-target path, e.g. lib/main.dart');
|
||||
}
|
||||
|
||||
@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 @@ class FuchsiaReloadCommand extends FlutterCommand {
|
||||
|
||||
// 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 @@ class FuchsiaReloadCommand extends FlutterCommand {
|
||||
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 @@ class FuchsiaReloadCommand extends FlutterCommand {
|
||||
|
||||
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 @@ class FuchsiaReloadCommand extends FlutterCommand {
|
||||
|
||||
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 @@ class FuchsiaReloadCommand extends FlutterCommand {
|
||||
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 @@ class FuchsiaDeviceCommandRunner {
|
||||
|
||||
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;
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user