Commit 8bf17192 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Adding first semantics perf test (#10649)

* Adding first semantics perf test

* review commnts and analyzer fixes

* fix analyzer warning
parent 0774c519
// 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:complex_layout/main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}
// 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:convert';
import 'dart:io';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
void main() {
group('semantics performance test', () {
FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect(printCommunication: true);
});
tearDownAll(() async {
if (driver != null)
driver.close();
});
test('inital tree creation', () async {
// Let app become fully idle.
await new Future<Null>.delayed(const Duration(seconds: 1));
final Timeline timeline = await driver.traceAction(() async {
expect(await driver.setSemantics(true), isTrue);
});
final Iterable<TimelineEvent> semanticsEvents = timeline.events.where((TimelineEvent event) => event.name == "Semantics");
if (semanticsEvents.length != 1)
fail('Expected exactly one semantics event, got ${semanticsEvents.length}');
final Duration semanticsTreeCreation = semanticsEvents.first.duration;
final String json = JSON.encode(<String, dynamic>{'initialSemanticsTreeCreation': semanticsTreeCreation.inMilliseconds});
new File(p.join(testOutputsDirectory, 'complex_layout_semantics_perf.json')).writeAsStringSync(json);
});
});
}
// 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 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as p;
void main() {
task(() async {
deviceOperatingSystem = DeviceOperatingSystem.android;
final Device device = await devices.workingDevice;
await device.unlock();
final String deviceId = device.deviceId;
await flutter('packages', options: <String>['get']);
final String complexLayoutPath = p.join(flutterDirectory.path, 'dev', 'benchmarks', 'complex_layout');
await inDirectory(complexLayoutPath, () async {
await flutter('drive', options: <String>[
'-v',
'--profile',
'--trace-startup', // Enables "endless" timeline event buffering.
'-t',
p.join(complexLayoutPath, 'test_driver', 'semantics_perf.dart'),
'-d',
deviceId,
]);
});
final String dataPath = p.join(complexLayoutPath, 'build', 'complex_layout_semantics_perf.json');
return new TaskResult.successFromFile(file(dataPath), benchmarkScoreKeys: <String>[
'initialSemanticsTreeCreation',
]);
});
}
......@@ -133,6 +133,13 @@ tasks:
required_agent_capabilities: ["linux/android"]
flaky: true
complex_layout_semantics_perf:
description: >
Measures duration of building the initial semantics tree.
stage: devicelab
required_agent_capabilities: ["linux/android"]
flaky: true
# iOS on-device tests
channels_integration_test_ios:
......
......@@ -21,6 +21,7 @@ import 'gesture.dart';
import 'health.dart';
import 'message.dart';
import 'render_tree.dart';
import 'semantics.dart';
import 'timeline.dart';
/// Timeline stream identifier.
......@@ -383,6 +384,15 @@ class FlutterDriver {
return GetTextResult.fromJson(await _sendCommand(new GetText(finder, timeout: timeout))).text;
}
/// Turns semantics on or off in the Flutter app under test.
///
/// Returns `true` when the call actually changed the state from on to off or
/// vice versa.
Future<bool> setSemantics(bool enabled, { Duration timeout: _kShortTimeout }) async {
final SetSemanticsResult result = SetSemanticsResult.fromJson(await _sendCommand(new SetSemantics(enabled, timeout: timeout)));
return result.changedState;
}
/// Take a screenshot. The image will be returned as a PNG.
Future<List<int>> screenshot({ Duration timeout }) async {
timeout ??= _kLongTimeout;
......
......@@ -8,7 +8,7 @@ import 'package:meta/meta.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show RendererBinding;
import 'package:flutter/rendering.dart' show RendererBinding, SemanticsHandle;
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -20,6 +20,7 @@ import 'gesture.dart';
import 'health.dart';
import 'message.dart';
import 'render_tree.dart';
import 'semantics.dart';
const String _extensionMethodName = 'driver';
const String _extensionMethod = 'ext.flutter.$_extensionMethodName';
......@@ -70,6 +71,7 @@ class FlutterDriverExtension {
'tap': _tap,
'get_text': _getText,
'set_frame_sync': _setFrameSync,
'set_semantics': _setSemantics,
'scroll': _scroll,
'scrollIntoView': _scrollIntoView,
'waitFor': _waitFor,
......@@ -82,6 +84,7 @@ class FlutterDriverExtension {
'tap': (Map<String, String> params) => new Tap.deserialize(params),
'get_text': (Map<String, String> params) => new GetText.deserialize(params),
'set_frame_sync': (Map<String, String> params) => new SetFrameSync.deserialize(params),
'set_semantics': (Map<String, String> params) => new SetSemantics.deserialize(params),
'scroll': (Map<String, String> params) => new Scroll.deserialize(params),
'scrollIntoView': (Map<String, String> params) => new ScrollIntoView.deserialize(params),
'waitFor': (Map<String, String> params) => new WaitFor.deserialize(params),
......@@ -271,4 +274,27 @@ class FlutterDriverExtension {
_frameSync = setFrameSyncCommand.enabled;
return new SetFrameSyncResult();
}
SemanticsHandle _semantics;
bool get _semanticsIsEnabled => RendererBinding.instance.pipelineOwner.semanticsOwner != null;
Future<SetSemanticsResult> _setSemantics(Command command) async {
final SetSemantics setSemanticsCommand = command;
final bool semanticsWasEnabled = _semanticsIsEnabled;
if (setSemanticsCommand.enabled && _semantics == null) {
_semantics = RendererBinding.instance.pipelineOwner.ensureSemantics();
if (!semanticsWasEnabled) {
// wait for the first frame where semantics is enabled.
final Completer<Null> completer = new Completer<Null>();
SchedulerBinding.instance.addPostFrameCallback((Duration d) {
completer.complete();
});
await completer.future;
}
} else if (!setSemanticsCommand.enabled && _semantics != null) {
_semantics.dispose();
_semantics = null;
}
return new SetSemanticsResult(semanticsWasEnabled != _semanticsIsEnabled);
}
}
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:meta/meta.dart';
/// An object sent from the Flutter Driver to a Flutter application to instruct
/// the application to perform a task.
abstract class Command {
......@@ -20,6 +22,7 @@ abstract class Command {
String get kind;
/// Serializes this command to parameter name/value pairs.
@mustCallSuper
Map<String, String> serialize() => <String, String>{
'command': kind,
'timeout': '${timeout.inMilliseconds}',
......
// 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 'message.dart';
/// Enables or disables semantics.
class SetSemantics extends Command {
@override
final String kind = 'set_semantics';
/// Whether semantics should be enabled or disabled.
final bool enabled;
SetSemantics(this.enabled, { Duration timeout }) : super(timeout: timeout);
/// Deserializes this command from the value generated by [serialize].
SetSemantics.deserialize(Map<String, String> params)
: this.enabled = params['enabled'].toLowerCase() == 'true',
super.deserialize(params);
@override
Map<String, String> serialize() => super.serialize()..addAll(<String, String>{
'enabled': '$enabled',
});
}
/// The result of a [SetSemantics] command.
class SetSemanticsResult extends Result {
SetSemanticsResult(this.changedState);
final bool changedState;
/// Deserializes this result from JSON.
static SetSemanticsResult fromJson(Map<String, dynamic> json) {
return new SetSemanticsResult(json['changedState']);
}
@override
Map<String, dynamic> toJson() => <String, dynamic>{
'changedState': changedState,
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment