Option to enable the performance overlay from 'flutter run'. (#11288)
This commit is contained in:
parent
77b0c1dab0
commit
e1adc525d8
93
dev/devicelab/bin/tasks/commands_test.dart
Normal file
93
dev/devicelab/bin/tasks/commands_test.dart
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'package:flutter_devicelab/framework/adb.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
|
||||
void main() {
|
||||
task(() async {
|
||||
final Device device = await devices.workingDevice;
|
||||
await device.unlock();
|
||||
final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
|
||||
await inDirectory(appDir, () async {
|
||||
final Completer<Null> ready = new Completer<Null>();
|
||||
bool ok;
|
||||
print('run: starting...');
|
||||
final Process run = await startProcess(
|
||||
path.join(flutterDirectory.path, 'bin', 'flutter'),
|
||||
<String>['run', '--verbose', '--observatory-port=8888', '-d', device.deviceId, 'lib/commands.dart'],
|
||||
);
|
||||
run.stdout
|
||||
.transform(UTF8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.listen((String line) {
|
||||
print('run:stdout: $line');
|
||||
if (line.contains(new RegExp(r'^\[\s+\] For a more detailed help message, press "h"\. To quit, press "q"\.'))) {
|
||||
print('run: ready!');
|
||||
ready.complete();
|
||||
ok ??= true;
|
||||
}
|
||||
});
|
||||
run.stderr
|
||||
.transform(UTF8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.listen((String line) {
|
||||
stderr.writeln('run:stderr: $line');
|
||||
});
|
||||
run.exitCode.then((int exitCode) { ok = false; });
|
||||
await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]);
|
||||
if (!ok)
|
||||
throw 'Failed to run test app.';
|
||||
await drive('none');
|
||||
print('test: pressing "p" to enable debugPaintSize...');
|
||||
run.stdin.write('p');
|
||||
await drive('debug_paint');
|
||||
print('test: pressing "p" again...');
|
||||
run.stdin.write('p');
|
||||
await drive('none');
|
||||
print('test: pressing "P" to enable performance overlay...');
|
||||
run.stdin.write('P');
|
||||
await drive('performance_overlay');
|
||||
print('test: pressing "P" again...');
|
||||
run.stdin.write('P');
|
||||
await drive('none');
|
||||
run.stdin.write('q');
|
||||
final int result = await run.exitCode;
|
||||
if (result != 0)
|
||||
throw 'Received unexpected exit code $result from run process.';
|
||||
});
|
||||
return new TaskResult.success(null);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Null> drive(String name) async {
|
||||
print('drive: running commands_$name check...');
|
||||
final Process drive = await startProcess(
|
||||
path.join(flutterDirectory.path, 'bin', 'flutter'),
|
||||
<String>['drive', '--use-existing-app', 'http://127.0.0.1:8888/', '--keep-app-running', '--driver', 'test_driver/commands_${name}_test.dart'],
|
||||
);
|
||||
drive.stdout
|
||||
.transform(UTF8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.listen((String line) {
|
||||
print('drive:stdout: $line');
|
||||
});
|
||||
drive.stderr
|
||||
.transform(UTF8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.listen((String line) {
|
||||
stderr.writeln('drive:stderr: $line');
|
||||
});
|
||||
final int result = await drive.exitCode;
|
||||
if (result != 0)
|
||||
throw 'Failed to drive test app (exit code $result).';
|
||||
print('drive: finished commands_$name check successfully.');
|
||||
}
|
@ -134,6 +134,7 @@ class _TaskRunner {
|
||||
// are catching errors coming from arbitrary (and untrustworthy) task
|
||||
// code. Our goal is to convert the failure into a readable message.
|
||||
// Propagating it further is not useful.
|
||||
if (!completer.isCompleted)
|
||||
completer.complete(new TaskResult.failure(message));
|
||||
});
|
||||
return completer.future;
|
||||
|
@ -114,6 +114,13 @@ tasks:
|
||||
stage: devicelab
|
||||
required_agent_capabilities: ["has-android-device"]
|
||||
|
||||
commands_test:
|
||||
description: >
|
||||
Runs tests of flutter run commands.
|
||||
stage: devicelab
|
||||
required_agent_capabilities: ["has-android-device"]
|
||||
flaky: true
|
||||
|
||||
android_sample_catalog_generator:
|
||||
description: >
|
||||
Builds sample catalog markdown pages and Android screenshots
|
||||
@ -131,7 +138,6 @@ tasks:
|
||||
Verifies that `flutter drive --route` still works. No performance numbers.
|
||||
stage: devicelab
|
||||
required_agent_capabilities: ["linux/android"]
|
||||
flaky: true
|
||||
|
||||
flutter_gallery_instrumentation_test:
|
||||
description: >
|
||||
@ -140,7 +146,6 @@ tasks:
|
||||
test can run on off-the-shelf infrastructures, such as Firebase Test Lab.
|
||||
stage: devicelab
|
||||
required_agent_capabilities: ["linux/android"]
|
||||
flaky: true
|
||||
|
||||
# iOS on-device tests
|
||||
|
||||
@ -149,14 +154,12 @@ tasks:
|
||||
Checks that platform channels work on iOS.
|
||||
stage: devicelab_ios
|
||||
required_agent_capabilities: ["has-ios-device"]
|
||||
flaky: true
|
||||
|
||||
platform_channel_sample_test_ios:
|
||||
description: >
|
||||
Runs a driver test on the Platform Channel sample app on iOS.
|
||||
stage: devicelab_ios
|
||||
required_agent_capabilities: ["has-ios-device"]
|
||||
flaky: true
|
||||
|
||||
complex_layout_scroll_perf_ios__timeline_summary:
|
||||
description: >
|
||||
@ -164,7 +167,6 @@ tasks:
|
||||
iOS.
|
||||
stage: devicelab_ios
|
||||
required_agent_capabilities: ["has-ios-device"]
|
||||
flaky: true
|
||||
|
||||
# flutter_gallery_ios__start_up:
|
||||
# description: >
|
||||
@ -186,14 +188,12 @@ tasks:
|
||||
iOS.
|
||||
stage: devicelab_ios
|
||||
required_agent_capabilities: ["has-ios-device"]
|
||||
flaky: true
|
||||
|
||||
basic_material_app_ios__size:
|
||||
description: >
|
||||
Measures the IPA size of a basic material app.
|
||||
stage: devicelab_ios
|
||||
required_agent_capabilities: ["has-ios-device"]
|
||||
flaky: true
|
||||
|
||||
# microbenchmarks_ios:
|
||||
# description: >
|
||||
@ -215,14 +215,12 @@ tasks:
|
||||
Runs end-to-end Flutter tests on iOS.
|
||||
stage: devicelab_ios
|
||||
required_agent_capabilities: ["has-ios-device"]
|
||||
flaky: true
|
||||
|
||||
ios_sample_catalog_generator:
|
||||
description: >
|
||||
Builds sample catalog markdown pages and iOS screenshots
|
||||
stage: devicelab_ios
|
||||
required_agent_capabilities: ["has-ios-device"]
|
||||
flaky: true
|
||||
|
||||
# Tests running on Windows host
|
||||
|
||||
|
43
dev/integration_tests/ui/lib/commands.dart
Normal file
43
dev/integration_tests/ui/lib/commands.dart
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_driver/driver_extension.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
String log = '';
|
||||
|
||||
void main() {
|
||||
enableFlutterDriverExtension(handler: (String message) async {
|
||||
log = 'log:';
|
||||
await WidgetsBinding.instance.reassembleApplication();
|
||||
return log;
|
||||
});
|
||||
runApp(new MaterialApp(home: const Test()));
|
||||
}
|
||||
|
||||
class Test extends SingleChildRenderObjectWidget {
|
||||
const Test({ Key key }) : super(key: key);
|
||||
|
||||
@override
|
||||
RenderTest createRenderObject(BuildContext context) {
|
||||
return new RenderTest();
|
||||
}
|
||||
}
|
||||
|
||||
class RenderTest extends RenderProxyBox {
|
||||
RenderTest({ RenderBox child }) : super(child);
|
||||
|
||||
@override
|
||||
void debugPaintSize(PaintingContext context, Offset offset) {
|
||||
super.debugPaintSize(context, offset);
|
||||
log += ' debugPaintSize';
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
log += ' paint';
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
FlutterDriver driver;
|
||||
|
||||
setUpAll(() async {
|
||||
driver = await FlutterDriver.connect();
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
driver?.close();
|
||||
});
|
||||
|
||||
test('check that we are painting in debugPaintSize mode', () async {
|
||||
expect(await driver.requestData('status'), 'log: paint debugPaintSize');
|
||||
});
|
||||
}
|
23
dev/integration_tests/ui/test_driver/commands_none_test.dart
Normal file
23
dev/integration_tests/ui/test_driver/commands_none_test.dart
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
FlutterDriver driver;
|
||||
|
||||
setUpAll(() async {
|
||||
driver = await FlutterDriver.connect();
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
driver?.close();
|
||||
});
|
||||
|
||||
test('check that we are in normal mode', () async {
|
||||
expect(await driver.requestData('status'), 'log: paint');
|
||||
await driver.waitForAbsent(find.byType('PerformanceOverlay'), timeout: Duration.ZERO);
|
||||
});
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
FlutterDriver driver;
|
||||
|
||||
setUpAll(() async {
|
||||
driver = await FlutterDriver.connect();
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
driver?.close();
|
||||
});
|
||||
|
||||
test('check that we are showing the performance overlay', () async {
|
||||
await driver.requestData('status'); // force a reassemble
|
||||
await driver.waitFor(find.byType('PerformanceOverlay'), timeout: Duration.ZERO);
|
||||
});
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:integration_ui/keys.dart' as keys;
|
||||
|
@ -1,3 +1,7 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
@ -348,6 +348,12 @@ class FlutterDriver {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Waits until [finder] can no longer locate the target.
|
||||
Future<Null> waitForAbsent(SerializableFinder finder, {Duration timeout}) async {
|
||||
await _sendCommand(new WaitForAbsent(finder, timeout: timeout));
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Waits until there are no more transient callbacks in the queue.
|
||||
///
|
||||
/// Use this method when you need to wait for the moment when the application
|
||||
@ -597,9 +603,12 @@ class CommonFinders {
|
||||
/// Finds [Text] widgets containing string equal to [text].
|
||||
SerializableFinder text(String text) => new ByText(text);
|
||||
|
||||
/// Finds widgets by [key].
|
||||
/// Finds widgets by [key]. Only [String] and [int] values can be used.
|
||||
SerializableFinder byValueKey(dynamic key) => new ByValueKey(key);
|
||||
|
||||
/// Finds widgets with a tooltip with the given [message].
|
||||
SerializableFinder byTooltip(String message) => new ByTooltipMessage(message);
|
||||
|
||||
/// Finds widgets whose class name matches the given string.
|
||||
SerializableFinder byType(String type) => new ByType(type);
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ class FlutterDriverExtension {
|
||||
'set_semantics': _setSemantics,
|
||||
'tap': _tap,
|
||||
'waitFor': _waitFor,
|
||||
'waitForAbsent': _waitForAbsent,
|
||||
'waitUntilNoTransientCallbacks': _waitUntilNoTransientCallbacks,
|
||||
});
|
||||
|
||||
@ -100,6 +101,7 @@ class FlutterDriverExtension {
|
||||
'set_semantics': (Map<String, String> params) => new SetSemantics.deserialize(params),
|
||||
'tap': (Map<String, String> params) => new Tap.deserialize(params),
|
||||
'waitFor': (Map<String, String> params) => new WaitFor.deserialize(params),
|
||||
'waitForAbsent': (Map<String, String> params) => new WaitForAbsent.deserialize(params),
|
||||
'waitUntilNoTransientCallbacks': (Map<String, String> params) => new WaitUntilNoTransientCallbacks.deserialize(params),
|
||||
});
|
||||
|
||||
@ -107,6 +109,7 @@ class FlutterDriverExtension {
|
||||
'ByText': _createByTextFinder,
|
||||
'ByTooltipMessage': _createByTooltipMessageFinder,
|
||||
'ByValueKey': _createByValueKeyFinder,
|
||||
'ByType': _createByTypeFinder,
|
||||
});
|
||||
}
|
||||
|
||||
@ -195,6 +198,19 @@ class FlutterDriverExtension {
|
||||
return finder;
|
||||
}
|
||||
|
||||
/// Runs `finder` repeatedly until it finds zero [Element]s.
|
||||
Future<Finder> _waitForAbsentElement(Finder finder) async {
|
||||
if (_frameSync)
|
||||
await _waitUntilFrame(() => SchedulerBinding.instance.transientCallbackCount == 0);
|
||||
|
||||
await _waitUntilFrame(() => !finder.precache());
|
||||
|
||||
if (_frameSync)
|
||||
await _waitUntilFrame(() => SchedulerBinding.instance.transientCallbackCount == 0);
|
||||
|
||||
return finder;
|
||||
}
|
||||
|
||||
Finder _createByTextFinder(ByText arguments) {
|
||||
return find.text(arguments.text);
|
||||
}
|
||||
@ -219,6 +235,12 @@ class FlutterDriverExtension {
|
||||
}
|
||||
}
|
||||
|
||||
Finder _createByTypeFinder(ByType arguments) {
|
||||
return find.byElementPredicate((Element element) {
|
||||
return element.widget.runtimeType.toString() == arguments.type;
|
||||
}, description: 'widget with runtimeType "${arguments.type}"');
|
||||
}
|
||||
|
||||
Finder _createFinder(SerializableFinder finder) {
|
||||
final FinderConstructor constructor = _finders[finder.finderType];
|
||||
|
||||
@ -242,6 +264,12 @@ class FlutterDriverExtension {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<WaitForAbsentResult> _waitForAbsent(Command command) async {
|
||||
final WaitForAbsent waitForAbsentCommand = command;
|
||||
await _waitForAbsentElement(_createFinder(waitForAbsentCommand.finder));
|
||||
return new WaitForAbsentResult();
|
||||
}
|
||||
|
||||
Future<Null> _waitUntilNoTransientCallbacks(Command command) async {
|
||||
if (SchedulerBinding.instance.transientCallbackCount != 0)
|
||||
await _waitUntilFrame(() => SchedulerBinding.instance.transientCallbackCount == 0);
|
||||
|
@ -62,6 +62,22 @@ class WaitFor extends CommandWithTarget {
|
||||
WaitFor.deserialize(Map<String, String> json) : super.deserialize(json);
|
||||
}
|
||||
|
||||
/// Waits until [finder] can no longer locate the target.
|
||||
class WaitForAbsent extends CommandWithTarget {
|
||||
@override
|
||||
final String kind = 'waitForAbsent';
|
||||
|
||||
/// Creates a command that waits for the widget identified by [finder] to
|
||||
/// disappear within the [timeout] amount of time.
|
||||
///
|
||||
/// If [timeout] is not specified the command times out after 5 seconds.
|
||||
WaitForAbsent(SerializableFinder finder, {Duration timeout})
|
||||
: super(finder, timeout: timeout);
|
||||
|
||||
/// Deserializes the command from JSON generated by [serialize].
|
||||
WaitForAbsent.deserialize(Map<String, String> json) : super.deserialize(json);
|
||||
}
|
||||
|
||||
/// Waits until there are no more transient callbacks in the queue.
|
||||
class WaitUntilNoTransientCallbacks extends Command {
|
||||
@override
|
||||
@ -85,6 +101,17 @@ class WaitForResult extends Result {
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{};
|
||||
}
|
||||
|
||||
/// The result of a [WaitForAbsent] command.
|
||||
class WaitForAbsentResult extends Result {
|
||||
/// Deserializes the result from JSON.
|
||||
static WaitForAbsentResult fromJson(Map<String, dynamic> json) {
|
||||
return new WaitForAbsentResult();
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{};
|
||||
}
|
||||
|
||||
/// Describes how to the driver should search for elements.
|
||||
abstract class SerializableFinder {
|
||||
/// Identifies the type of finder to be used by the driver extension.
|
||||
@ -94,6 +121,7 @@ abstract class SerializableFinder {
|
||||
static SerializableFinder deserialize(Map<String, String> json) {
|
||||
final String finderType = json['finderType'];
|
||||
switch(finderType) {
|
||||
case 'ByType': return ByType.deserialize(json);
|
||||
case 'ByValueKey': return ByValueKey.deserialize(json);
|
||||
case 'ByTooltipMessage': return ByTooltipMessage.deserialize(json);
|
||||
case 'ByText': return ByText.deserialize(json);
|
||||
@ -200,6 +228,28 @@ class ByValueKey extends SerializableFinder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds widgets by their [runtimeType].
|
||||
class ByType extends SerializableFinder {
|
||||
@override
|
||||
final String finderType = 'ByType';
|
||||
|
||||
/// Creates a finder that given the runtime type in string form.
|
||||
ByType(this.type);
|
||||
|
||||
/// The widget's [runtimeType], in string form.
|
||||
final String type;
|
||||
|
||||
@override
|
||||
Map<String, String> serialize() => super.serialize()..addAll(<String, String>{
|
||||
'type': type,
|
||||
});
|
||||
|
||||
/// Deserializes the finder from JSON generated by [serialize].
|
||||
static ByType deserialize(Map<String, String> json) {
|
||||
return new ByType(json['type']);
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to read the text from a given element.
|
||||
class GetText extends CommandWithTarget {
|
||||
@override
|
||||
|
@ -60,6 +60,18 @@ class DriveCommand extends RunCommandBase {
|
||||
valueHelp:
|
||||
'url'
|
||||
);
|
||||
|
||||
argParser.addOption(
|
||||
'driver',
|
||||
help:
|
||||
'The test file to run on the host (as opposed to the target file to run on\n'
|
||||
'the device). By default, this file has the same base name as the target\n'
|
||||
'file, but in the "test_driver/" directory instead, and with "_test" inserted\n'
|
||||
'just before the extension, so e.g. if the target is "lib/main.dart", the\n'
|
||||
'driver will be "test_driver/main_test.dart".',
|
||||
valueHelp:
|
||||
'path'
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -139,6 +151,11 @@ class DriveCommand extends RunCommandBase {
|
||||
}
|
||||
|
||||
String _getTestFile() {
|
||||
if (argResults['driver'] != null)
|
||||
return argResults['driver'];
|
||||
|
||||
// If the --driver argument wasn't provided, then derive the value from
|
||||
// the target file.
|
||||
String appFile = fs.path.normalize(targetFile);
|
||||
|
||||
// This command extends `flutter run` and therefore CWD == package dir
|
||||
|
@ -171,6 +171,11 @@ class FlutterDevice {
|
||||
await view.uiIsolate.flutterToggleDebugPaintSizeEnabled();
|
||||
}
|
||||
|
||||
Future<Null> debugTogglePerformanceOverlayOverride() async {
|
||||
for (FlutterView view in views)
|
||||
await view.uiIsolate.flutterTogglePerformanceOverlayOverride();
|
||||
}
|
||||
|
||||
Future<String> togglePlatform({ String from }) async {
|
||||
String to;
|
||||
switch (from) {
|
||||
@ -451,6 +456,12 @@ abstract class ResidentRunner {
|
||||
await device.toggleDebugPaintSizeEnabled();
|
||||
}
|
||||
|
||||
Future<Null> _debugTogglePerformanceOverlayOverride() async {
|
||||
await refreshViews();
|
||||
for (FlutterDevice device in flutterDevices)
|
||||
await device.debugTogglePerformanceOverlayOverride();
|
||||
}
|
||||
|
||||
Future<Null> _screenshot(FlutterDevice device) async {
|
||||
final Status status = logger.startProgress('Taking screenshot for ${device.device.name}...');
|
||||
final File outputFile = getUniqueFile(fs.currentDirectory, 'flutter', 'png');
|
||||
@ -606,11 +617,16 @@ abstract class ResidentRunner {
|
||||
await _debugDumpSemanticsTree();
|
||||
return true;
|
||||
}
|
||||
} else if (lower == 'p') {
|
||||
} else if (character == 'p') {
|
||||
if (supportsServiceProtocol && isRunningDebug) {
|
||||
await _debugToggleDebugPaintSizeEnabled();
|
||||
return true;
|
||||
}
|
||||
} else if (character == 'P') {
|
||||
if (supportsServiceProtocol) {
|
||||
await _debugTogglePerformanceOverlayOverride();
|
||||
return true;
|
||||
}
|
||||
} else if (character == 's') {
|
||||
for (FlutterDevice device in flutterDevices) {
|
||||
if (device.device.supportsScreenshot)
|
||||
@ -726,11 +742,14 @@ abstract class ResidentRunner {
|
||||
if (supportsServiceProtocol) {
|
||||
printStatus('You can dump the widget hierarchy of the app (debugDumpApp) by pressing "w".');
|
||||
printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "t".');
|
||||
printStatus('For layers (debugDumpLayerTree), use "L"; accessibility (debugDumpSemantics), "S".');
|
||||
if (isRunningDebug) {
|
||||
printStatus('For layers (debugDumpLayerTree), use "L"; accessibility (debugDumpSemantics), "S".');
|
||||
printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".');
|
||||
printStatus('To simulate different operating systems, (defaultTargetPlatform), press "o".');
|
||||
} else {
|
||||
printStatus('To dump the accessibility tree (debugDumpSemantics), press "S".');
|
||||
}
|
||||
printStatus('To display the performance overlay (WidgetsApp.showPerformanceOverlay), press "P".');
|
||||
}
|
||||
if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot))
|
||||
printStatus('To save a screenshot to flutter.png, press "s".');
|
||||
|
@ -79,7 +79,10 @@ abstract class FlutterCommand extends Command<Null> {
|
||||
argParser.addOption('target',
|
||||
abbr: 't',
|
||||
defaultsTo: flx.defaultMainPath,
|
||||
help: 'Target app path / main entry-point file.');
|
||||
help: 'The main entry-point file of the application, as run on the device.\n'
|
||||
'If the --target option is omitted, but a file name is provided on\n'
|
||||
'the command line, then that is used instead.',
|
||||
valueHelp: 'path');
|
||||
_usesTargetOption = true;
|
||||
}
|
||||
|
||||
|
@ -1069,11 +1069,11 @@ class Isolate extends ServiceObjectOwner {
|
||||
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTree', timeout: kLongRequestTimeout);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() async {
|
||||
Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint');
|
||||
Future<Map<String, dynamic>> _flutterToggle(String name) async {
|
||||
Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.$name');
|
||||
if (state != null && state.containsKey('enabled') && state['enabled'] is String) {
|
||||
state = await invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.debugPaint',
|
||||
'ext.flutter.$name',
|
||||
params: <String, dynamic>{ 'enabled': state['enabled'] == 'true' ? 'false' : 'true' },
|
||||
timeout: const Duration(milliseconds: 150),
|
||||
timeoutFatal: false,
|
||||
@ -1082,6 +1082,10 @@ class Isolate extends ServiceObjectOwner {
|
||||
return state;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() => _flutterToggle('debugPaint');
|
||||
|
||||
Future<Map<String, dynamic>> flutterTogglePerformanceOverlayOverride() => _flutterToggle('showPerformanceOverlay');
|
||||
|
||||
Future<Null> flutterDebugAllowBanner(bool show) async {
|
||||
await invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.debugAllowBanner',
|
||||
|
Loading…
x
Reference in New Issue
Block a user