Commit a9b965cb authored by Hans Muller's avatar Hans Muller

Gallery demo start-time performance test (#3655)

*  Gallery demo start-time performance test
parent 76b04cdd
......@@ -4,7 +4,6 @@
import 'dart:async';
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_sprites/flutter_sprites.dart';
......@@ -288,7 +287,7 @@ class _ProgressCircle extends NodeWithSize {
Paint circlePaint = new Paint()
..color = Colors.white30
..strokeWidth = 24.0
..style = ui.PaintingStyle.stroke;
..style = PaintingStyle.stroke;
canvas.drawCircle(
new Point(size.width / 2.0, size.height / 2.0),
......@@ -299,7 +298,7 @@ class _ProgressCircle extends NodeWithSize {
Paint pathPaint = new Paint()
..color = Colors.purple[500]
..strokeWidth = 25.0
..style = ui.PaintingStyle.stroke;
..style = PaintingStyle.stroke;
double angle = value.clamp(0.0, 1.0) * _kSweep;
Path path = new Path()
......
......@@ -44,6 +44,16 @@ final Map<String, WidgetBuilder> kRoutes = <String, WidgetBuilder>{
TypographyDemo.routeName: (BuildContext context) => new TypographyDemo(),
};
final ThemeData _kGalleryLightTheme = new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.purple
);
final ThemeData _kGalleryDarkTheme = new ThemeData(
brightness: ThemeBrightness.dark,
primarySwatch: Colors.purple
);
class GalleryApp extends StatefulWidget {
GalleryApp({ Key key }) : super(key: key);
......@@ -73,13 +83,3 @@ class GalleryAppState extends State<GalleryApp> {
);
}
}
ThemeData _kGalleryLightTheme = new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: Colors.purple
);
ThemeData _kGalleryDarkTheme = new ThemeData(
brightness: ThemeBrightness.dark,
primarySwatch: Colors.purple
);
......@@ -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 'dart:developer';
import 'package:flutter/material.dart';
typedef Widget GalleryDemoBuilder();
......@@ -21,8 +23,13 @@ class GalleryItem extends StatelessWidget {
leading: leading,
title: new Text(title),
onTap: () {
if (routeName != null)
if (routeName != null) {
Timeline.instantSync('Start Transition', arguments: <String, String>{
'from': '/',
'to': routeName
});
Navigator.pushNamed(context, routeName);
}
}
);
}
......
// Copyright 2016 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:material_gallery/main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}
// Copyright 2016 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:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
// Warning: the following strings must be kept in sync with GalleryHome.
const List<String> demoCategories = const <String>['Demos', 'Components', 'Style'];
const List<String> demoNames = const <String>[
'Weather',
'Fitness',
'Fancy lines',
'Flexible space toolbar',
'Floating action button',
'Buttons',
'Cards',
'Chips',
'Date picker',
'Data tables',
'Dialog',
'Drop-down button',
'Expand/collapse list control',
'Grid',
'Icons',
'Leave-behind list items',
'List',
'Menus',
'Modal bottom sheet',
'Over-scroll',
'Page selector',
'Persistent bottom sheet',
'Progress indicators',
'Scrollable tabs',
'Selection controls',
'Sliders',
'Snackbar',
'Tabs',
'Text fields',
'Time picker',
'Tooltips',
'Colors',
'Typography'
];
void main() {
group('flutter gallery transitions', () {
FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
if (driver != null)
driver.close();
});
test('all demos', () async {
Timeline timeline = await driver.traceAction(() async {
// Expand the demo category submenus.
for (String category in demoCategories.reversed) {
await driver.tap(find.text(category));
await new Future<Null>.delayed(new Duration(milliseconds: 500));
}
// Scroll each demo menu item into view, launch the demo and
// return to the demo menu 2x.
for(String demoName in demoNames) {
SerializableFinder menuItem = find.text(demoName);
await driver.scrollIntoView(menuItem);
await new Future<Null>.delayed(new Duration(milliseconds: 500));
for(int i = 0; i < 2; i += 1) {
await driver.tap(menuItem); // Launch the demo
await new Future<Null>.delayed(new Duration(milliseconds: 500));
await driver.tap(find.byTooltip('Back'));
await new Future<Null>.delayed(new Duration(milliseconds: 1000));
}
}
},
categories: const <TracingCategory>[
TracingCategory.dart,
TracingCategory.gc,
TracingCategory.compiler
]);
new TimelineSummary.summarize(timeline)
..writeSummaryToFile('transitions_perf', pretty: true)
..writeTimelineToFile('transitions_perf', pretty: true);
}, timeout: new Timeout(new Duration(minutes: 15)));
});
}
......@@ -15,7 +15,8 @@ export 'src/driver.dart' show
find,
CommonFinders,
EvaluatorFunction,
FlutterDriver;
FlutterDriver,
TracingCategory;
export 'src/error.dart' show
DriverError,
......
......@@ -15,13 +15,39 @@ import 'health.dart';
import 'message.dart';
import 'timeline.dart';
enum TracingCategory {
all, api, compiler, dart, debugger, embedder, gc, isolate, vm
}
const List<TracingCategory> _defaultCategories = const <TracingCategory>[TracingCategory.all];
// See https://github.com/dart-lang/sdk/blob/master/runtime/vm/timeline.cc#L32
String _tracingCategoriesToString(List<TracingCategory> categories) {
final String contents = categories.map((TracingCategory category) {
switch(category) {
case TracingCategory.all: return 'all';
case TracingCategory.api: return 'API';
case TracingCategory.compiler: return 'Compiler';
case TracingCategory.dart: return 'Dart';
case TracingCategory.debugger: return 'Debugger';
case TracingCategory.embedder: return 'Embedder';
case TracingCategory.gc: return 'GC';
case TracingCategory.isolate: return 'Isolate';
case TracingCategory.vm: return 'VM';
default:
throw 'Unknown tracing category $category';
}
}).join(', ');
return '[$contents]';
}
final Logger _log = new Logger('FlutterDriver');
/// A convenient accessor to frequently used finders.
///
/// Examples:
///
/// driver.tap(find.byText('Save'));
/// driver.tap(find.text('Save'));
/// driver.scroll(find.byValueKey(42));
const CommonFinders find = const CommonFinders._();
......@@ -211,15 +237,24 @@ class FlutterDriver {
return await _sendCommand(new Scroll(finder, dx, dy, duration, frequency)).then((Map<String, dynamic> _) => null);
}
/// Scrolls the Scrollable ancestor of the widget located by [finder]
/// until the widget is completely visible.
Future<Null> scrollIntoView(SerializableFinder finder) async {
return await _sendCommand(new ScrollIntoView(finder)).then((Map<String, dynamic> _) => null);
}
/// Returns the text in the `Text` widget located by [finder].
Future<String> getText(SerializableFinder finder) async {
return GetTextResult.fromJson(await _sendCommand(new GetText(finder))).text;
}
/// Starts recording performance traces.
Future<Null> startTracing() async {
Future<Null> startTracing({List<TracingCategory> categories: _defaultCategories}) async {
assert(categories != null && categories.length > 0);
try {
await _peer.sendRequest(_kSetVMTimelineFlagsMethod, {'recordedStreams': '[all]'});
await _peer.sendRequest(_kSetVMTimelineFlagsMethod, {
'recordedStreams': _tracingCategoriesToString(categories)
});
return null;
} catch(error, stackTrace) {
throw new DriverError(
......@@ -251,8 +286,8 @@ class FlutterDriver {
///
/// This is merely a convenience wrapper on top of [startTracing] and
/// [stopTracingAndDownloadTimeline].
Future<Timeline> traceAction(Future<dynamic> action()) async {
await startTracing();
Future<Timeline> traceAction(Future<dynamic> action(), { List<TracingCategory> categories: _defaultCategories }) async {
await startTracing(categories: categories);
await action();
return stopTracingAndDownloadTimeline();
}
......
......@@ -63,6 +63,7 @@ class FlutterDriverExtension {
'tap': tap,
'get_text': getText,
'scroll': scroll,
'scrollIntoView': scrollIntoView,
'waitFor': waitFor,
});
......@@ -71,6 +72,7 @@ class FlutterDriverExtension {
'tap': Tap.deserialize,
'get_text': GetText.deserialize,
'scroll': Scroll.deserialize,
'scrollIntoView': ScrollIntoView.deserialize,
'waitFor': WaitFor.deserialize,
});
......@@ -207,6 +209,12 @@ class FlutterDriverExtension {
return new ScrollResult();
}
Future<ScrollResult> scrollIntoView(ScrollIntoView command) async {
Finder target = await _waitForElement(_createFinder(command.finder));
await Scrollable.ensureVisible(target.evaluate().single);
return new ScrollResult();
}
Future<GetTextResult> getText(GetText command) async {
Finder target = await _waitForElement(_createFinder(command.finder));
// TODO(yjbanov): support more ways to read text
......
......@@ -73,6 +73,19 @@ class Scroll extends CommandWithTarget {
});
}
/// Command the driver to ensure that the element represented by [finder]
/// has been scrolled completely into view.
class ScrollIntoView extends CommandWithTarget {
@override
final String kind = 'scrollIntoView';
ScrollIntoView(SerializableFinder finder) : super(finder);
static ScrollIntoView deserialize(Map<String, dynamic> json) {
return new ScrollIntoView(SerializableFinder.deserialize(json));
}
}
class ScrollResult extends Result {
static ScrollResult fromJson(Map<String, dynamic> json) {
return new ScrollResult();
......
......@@ -235,6 +235,50 @@ void main() {
expect(timeline.events.single.name, 'test event');
});
});
group('traceAction categories', () {
test('specify non-default categories', () async {
bool actionCalled = false;
bool startTracingCalled = false;
bool stopTracingCalled = false;
when(mockPeer.sendRequest('_setVMTimelineFlags', argThat(equals({'recordedStreams': '[Dart, GC, Compiler]'}))))
.thenAnswer((_) async {
startTracingCalled = true;
return null;
});
when(mockPeer.sendRequest('_setVMTimelineFlags', argThat(equals({'recordedStreams': '[]'}))))
.thenAnswer((_) async {
stopTracingCalled = true;
return null;
});
when(mockPeer.sendRequest('_getVMTimeline')).thenAnswer((_) async {
return <String, dynamic> {
'traceEvents': [
{
'name': 'test event'
}
],
};
});
Timeline timeline = await driver.traceAction(() {
actionCalled = true;
},
categories: const <TracingCategory>[
TracingCategory.dart,
TracingCategory.gc,
TracingCategory.compiler
]);
expect(actionCalled, isTrue);
expect(startTracingCalled, isTrue);
expect(stopTracingCalled, isTrue);
expect(timeline.events.single.name, 'test event');
});
});
});
}
......
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