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
|
// are catching errors coming from arbitrary (and untrustworthy) task
|
||||||
// code. Our goal is to convert the failure into a readable message.
|
// code. Our goal is to convert the failure into a readable message.
|
||||||
// Propagating it further is not useful.
|
// Propagating it further is not useful.
|
||||||
|
if (!completer.isCompleted)
|
||||||
completer.complete(new TaskResult.failure(message));
|
completer.complete(new TaskResult.failure(message));
|
||||||
});
|
});
|
||||||
return completer.future;
|
return completer.future;
|
||||||
|
@ -114,6 +114,13 @@ tasks:
|
|||||||
stage: devicelab
|
stage: devicelab
|
||||||
required_agent_capabilities: ["has-android-device"]
|
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:
|
android_sample_catalog_generator:
|
||||||
description: >
|
description: >
|
||||||
Builds sample catalog markdown pages and Android screenshots
|
Builds sample catalog markdown pages and Android screenshots
|
||||||
@ -131,7 +138,6 @@ tasks:
|
|||||||
Verifies that `flutter drive --route` still works. No performance numbers.
|
Verifies that `flutter drive --route` still works. No performance numbers.
|
||||||
stage: devicelab
|
stage: devicelab
|
||||||
required_agent_capabilities: ["linux/android"]
|
required_agent_capabilities: ["linux/android"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
flutter_gallery_instrumentation_test:
|
flutter_gallery_instrumentation_test:
|
||||||
description: >
|
description: >
|
||||||
@ -140,7 +146,6 @@ tasks:
|
|||||||
test can run on off-the-shelf infrastructures, such as Firebase Test Lab.
|
test can run on off-the-shelf infrastructures, such as Firebase Test Lab.
|
||||||
stage: devicelab
|
stage: devicelab
|
||||||
required_agent_capabilities: ["linux/android"]
|
required_agent_capabilities: ["linux/android"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
# iOS on-device tests
|
# iOS on-device tests
|
||||||
|
|
||||||
@ -149,14 +154,12 @@ tasks:
|
|||||||
Checks that platform channels work on iOS.
|
Checks that platform channels work on iOS.
|
||||||
stage: devicelab_ios
|
stage: devicelab_ios
|
||||||
required_agent_capabilities: ["has-ios-device"]
|
required_agent_capabilities: ["has-ios-device"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
platform_channel_sample_test_ios:
|
platform_channel_sample_test_ios:
|
||||||
description: >
|
description: >
|
||||||
Runs a driver test on the Platform Channel sample app on iOS.
|
Runs a driver test on the Platform Channel sample app on iOS.
|
||||||
stage: devicelab_ios
|
stage: devicelab_ios
|
||||||
required_agent_capabilities: ["has-ios-device"]
|
required_agent_capabilities: ["has-ios-device"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
complex_layout_scroll_perf_ios__timeline_summary:
|
complex_layout_scroll_perf_ios__timeline_summary:
|
||||||
description: >
|
description: >
|
||||||
@ -164,7 +167,6 @@ tasks:
|
|||||||
iOS.
|
iOS.
|
||||||
stage: devicelab_ios
|
stage: devicelab_ios
|
||||||
required_agent_capabilities: ["has-ios-device"]
|
required_agent_capabilities: ["has-ios-device"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
# flutter_gallery_ios__start_up:
|
# flutter_gallery_ios__start_up:
|
||||||
# description: >
|
# description: >
|
||||||
@ -186,14 +188,12 @@ tasks:
|
|||||||
iOS.
|
iOS.
|
||||||
stage: devicelab_ios
|
stage: devicelab_ios
|
||||||
required_agent_capabilities: ["has-ios-device"]
|
required_agent_capabilities: ["has-ios-device"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
basic_material_app_ios__size:
|
basic_material_app_ios__size:
|
||||||
description: >
|
description: >
|
||||||
Measures the IPA size of a basic material app.
|
Measures the IPA size of a basic material app.
|
||||||
stage: devicelab_ios
|
stage: devicelab_ios
|
||||||
required_agent_capabilities: ["has-ios-device"]
|
required_agent_capabilities: ["has-ios-device"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
# microbenchmarks_ios:
|
# microbenchmarks_ios:
|
||||||
# description: >
|
# description: >
|
||||||
@ -215,14 +215,12 @@ tasks:
|
|||||||
Runs end-to-end Flutter tests on iOS.
|
Runs end-to-end Flutter tests on iOS.
|
||||||
stage: devicelab_ios
|
stage: devicelab_ios
|
||||||
required_agent_capabilities: ["has-ios-device"]
|
required_agent_capabilities: ["has-ios-device"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
ios_sample_catalog_generator:
|
ios_sample_catalog_generator:
|
||||||
description: >
|
description: >
|
||||||
Builds sample catalog markdown pages and iOS screenshots
|
Builds sample catalog markdown pages and iOS screenshots
|
||||||
stage: devicelab_ios
|
stage: devicelab_ios
|
||||||
required_agent_capabilities: ["has-ios-device"]
|
required_agent_capabilities: ["has-ios-device"]
|
||||||
flaky: true
|
|
||||||
|
|
||||||
# Tests running on Windows host
|
# 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 'dart:async';
|
||||||
|
|
||||||
import 'package:integration_ui/keys.dart' as keys;
|
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:flutter_driver/flutter_driver.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
@ -348,6 +348,12 @@ class FlutterDriver {
|
|||||||
return null;
|
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.
|
/// 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
|
/// 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].
|
/// Finds [Text] widgets containing string equal to [text].
|
||||||
SerializableFinder text(String text) => new ByText(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);
|
SerializableFinder byValueKey(dynamic key) => new ByValueKey(key);
|
||||||
|
|
||||||
/// Finds widgets with a tooltip with the given [message].
|
/// Finds widgets with a tooltip with the given [message].
|
||||||
SerializableFinder byTooltip(String message) => new ByTooltipMessage(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,
|
'set_semantics': _setSemantics,
|
||||||
'tap': _tap,
|
'tap': _tap,
|
||||||
'waitFor': _waitFor,
|
'waitFor': _waitFor,
|
||||||
|
'waitForAbsent': _waitForAbsent,
|
||||||
'waitUntilNoTransientCallbacks': _waitUntilNoTransientCallbacks,
|
'waitUntilNoTransientCallbacks': _waitUntilNoTransientCallbacks,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,6 +101,7 @@ class FlutterDriverExtension {
|
|||||||
'set_semantics': (Map<String, String> params) => new SetSemantics.deserialize(params),
|
'set_semantics': (Map<String, String> params) => new SetSemantics.deserialize(params),
|
||||||
'tap': (Map<String, String> params) => new Tap.deserialize(params),
|
'tap': (Map<String, String> params) => new Tap.deserialize(params),
|
||||||
'waitFor': (Map<String, String> params) => new WaitFor.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),
|
'waitUntilNoTransientCallbacks': (Map<String, String> params) => new WaitUntilNoTransientCallbacks.deserialize(params),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,6 +109,7 @@ class FlutterDriverExtension {
|
|||||||
'ByText': _createByTextFinder,
|
'ByText': _createByTextFinder,
|
||||||
'ByTooltipMessage': _createByTooltipMessageFinder,
|
'ByTooltipMessage': _createByTooltipMessageFinder,
|
||||||
'ByValueKey': _createByValueKeyFinder,
|
'ByValueKey': _createByValueKeyFinder,
|
||||||
|
'ByType': _createByTypeFinder,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +198,19 @@ class FlutterDriverExtension {
|
|||||||
return finder;
|
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) {
|
Finder _createByTextFinder(ByText arguments) {
|
||||||
return find.text(arguments.text);
|
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) {
|
Finder _createFinder(SerializableFinder finder) {
|
||||||
final FinderConstructor constructor = _finders[finder.finderType];
|
final FinderConstructor constructor = _finders[finder.finderType];
|
||||||
|
|
||||||
@ -242,6 +264,12 @@ class FlutterDriverExtension {
|
|||||||
return null;
|
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 {
|
Future<Null> _waitUntilNoTransientCallbacks(Command command) async {
|
||||||
if (SchedulerBinding.instance.transientCallbackCount != 0)
|
if (SchedulerBinding.instance.transientCallbackCount != 0)
|
||||||
await _waitUntilFrame(() => 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);
|
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.
|
/// Waits until there are no more transient callbacks in the queue.
|
||||||
class WaitUntilNoTransientCallbacks extends Command {
|
class WaitUntilNoTransientCallbacks extends Command {
|
||||||
@override
|
@override
|
||||||
@ -85,6 +101,17 @@ class WaitForResult extends Result {
|
|||||||
Map<String, dynamic> toJson() => <String, dynamic>{};
|
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.
|
/// Describes how to the driver should search for elements.
|
||||||
abstract class SerializableFinder {
|
abstract class SerializableFinder {
|
||||||
/// Identifies the type of finder to be used by the driver extension.
|
/// 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) {
|
static SerializableFinder deserialize(Map<String, String> json) {
|
||||||
final String finderType = json['finderType'];
|
final String finderType = json['finderType'];
|
||||||
switch(finderType) {
|
switch(finderType) {
|
||||||
|
case 'ByType': return ByType.deserialize(json);
|
||||||
case 'ByValueKey': return ByValueKey.deserialize(json);
|
case 'ByValueKey': return ByValueKey.deserialize(json);
|
||||||
case 'ByTooltipMessage': return ByTooltipMessage.deserialize(json);
|
case 'ByTooltipMessage': return ByTooltipMessage.deserialize(json);
|
||||||
case 'ByText': return ByText.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.
|
/// Command to read the text from a given element.
|
||||||
class GetText extends CommandWithTarget {
|
class GetText extends CommandWithTarget {
|
||||||
@override
|
@override
|
||||||
|
@ -60,6 +60,18 @@ class DriveCommand extends RunCommandBase {
|
|||||||
valueHelp:
|
valueHelp:
|
||||||
'url'
|
'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
|
@override
|
||||||
@ -139,6 +151,11 @@ class DriveCommand extends RunCommandBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _getTestFile() {
|
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);
|
String appFile = fs.path.normalize(targetFile);
|
||||||
|
|
||||||
// This command extends `flutter run` and therefore CWD == package dir
|
// This command extends `flutter run` and therefore CWD == package dir
|
||||||
|
@ -171,6 +171,11 @@ class FlutterDevice {
|
|||||||
await view.uiIsolate.flutterToggleDebugPaintSizeEnabled();
|
await view.uiIsolate.flutterToggleDebugPaintSizeEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Null> debugTogglePerformanceOverlayOverride() async {
|
||||||
|
for (FlutterView view in views)
|
||||||
|
await view.uiIsolate.flutterTogglePerformanceOverlayOverride();
|
||||||
|
}
|
||||||
|
|
||||||
Future<String> togglePlatform({ String from }) async {
|
Future<String> togglePlatform({ String from }) async {
|
||||||
String to;
|
String to;
|
||||||
switch (from) {
|
switch (from) {
|
||||||
@ -451,6 +456,12 @@ abstract class ResidentRunner {
|
|||||||
await device.toggleDebugPaintSizeEnabled();
|
await device.toggleDebugPaintSizeEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Null> _debugTogglePerformanceOverlayOverride() async {
|
||||||
|
await refreshViews();
|
||||||
|
for (FlutterDevice device in flutterDevices)
|
||||||
|
await device.debugTogglePerformanceOverlayOverride();
|
||||||
|
}
|
||||||
|
|
||||||
Future<Null> _screenshot(FlutterDevice device) async {
|
Future<Null> _screenshot(FlutterDevice device) async {
|
||||||
final Status status = logger.startProgress('Taking screenshot for ${device.device.name}...');
|
final Status status = logger.startProgress('Taking screenshot for ${device.device.name}...');
|
||||||
final File outputFile = getUniqueFile(fs.currentDirectory, 'flutter', 'png');
|
final File outputFile = getUniqueFile(fs.currentDirectory, 'flutter', 'png');
|
||||||
@ -606,11 +617,16 @@ abstract class ResidentRunner {
|
|||||||
await _debugDumpSemanticsTree();
|
await _debugDumpSemanticsTree();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (lower == 'p') {
|
} else if (character == 'p') {
|
||||||
if (supportsServiceProtocol && isRunningDebug) {
|
if (supportsServiceProtocol && isRunningDebug) {
|
||||||
await _debugToggleDebugPaintSizeEnabled();
|
await _debugToggleDebugPaintSizeEnabled();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if (character == 'P') {
|
||||||
|
if (supportsServiceProtocol) {
|
||||||
|
await _debugTogglePerformanceOverlayOverride();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} else if (character == 's') {
|
} else if (character == 's') {
|
||||||
for (FlutterDevice device in flutterDevices) {
|
for (FlutterDevice device in flutterDevices) {
|
||||||
if (device.device.supportsScreenshot)
|
if (device.device.supportsScreenshot)
|
||||||
@ -726,11 +742,14 @@ abstract class ResidentRunner {
|
|||||||
if (supportsServiceProtocol) {
|
if (supportsServiceProtocol) {
|
||||||
printStatus('You can dump the widget hierarchy of the app (debugDumpApp) by pressing "w".');
|
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('To dump the rendering tree of the app (debugDumpRenderTree), press "t".');
|
||||||
printStatus('For layers (debugDumpLayerTree), use "L"; accessibility (debugDumpSemantics), "S".');
|
|
||||||
if (isRunningDebug) {
|
if (isRunningDebug) {
|
||||||
|
printStatus('For layers (debugDumpLayerTree), use "L"; accessibility (debugDumpSemantics), "S".');
|
||||||
printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".');
|
printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".');
|
||||||
printStatus('To simulate different operating systems, (defaultTargetPlatform), press "o".');
|
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))
|
if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot))
|
||||||
printStatus('To save a screenshot to flutter.png, press "s".');
|
printStatus('To save a screenshot to flutter.png, press "s".');
|
||||||
|
@ -79,7 +79,10 @@ abstract class FlutterCommand extends Command<Null> {
|
|||||||
argParser.addOption('target',
|
argParser.addOption('target',
|
||||||
abbr: 't',
|
abbr: 't',
|
||||||
defaultsTo: flx.defaultMainPath,
|
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;
|
_usesTargetOption = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1069,11 +1069,11 @@ class Isolate extends ServiceObjectOwner {
|
|||||||
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTree', timeout: kLongRequestTimeout);
|
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTree', timeout: kLongRequestTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() async {
|
Future<Map<String, dynamic>> _flutterToggle(String name) async {
|
||||||
Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint');
|
Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.$name');
|
||||||
if (state != null && state.containsKey('enabled') && state['enabled'] is String) {
|
if (state != null && state.containsKey('enabled') && state['enabled'] is String) {
|
||||||
state = await invokeFlutterExtensionRpcRaw(
|
state = await invokeFlutterExtensionRpcRaw(
|
||||||
'ext.flutter.debugPaint',
|
'ext.flutter.$name',
|
||||||
params: <String, dynamic>{ 'enabled': state['enabled'] == 'true' ? 'false' : 'true' },
|
params: <String, dynamic>{ 'enabled': state['enabled'] == 'true' ? 'false' : 'true' },
|
||||||
timeout: const Duration(milliseconds: 150),
|
timeout: const Duration(milliseconds: 150),
|
||||||
timeoutFatal: false,
|
timeoutFatal: false,
|
||||||
@ -1082,6 +1082,10 @@ class Isolate extends ServiceObjectOwner {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() => _flutterToggle('debugPaint');
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> flutterTogglePerformanceOverlayOverride() => _flutterToggle('showPerformanceOverlay');
|
||||||
|
|
||||||
Future<Null> flutterDebugAllowBanner(bool show) async {
|
Future<Null> flutterDebugAllowBanner(bool show) async {
|
||||||
await invokeFlutterExtensionRpcRaw(
|
await invokeFlutterExtensionRpcRaw(
|
||||||
'ext.flutter.debugAllowBanner',
|
'ext.flutter.debugAllowBanner',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user