Skip to content

Commit

Permalink
Gallery demo start-time performance test (flutter#3655)
Browse files Browse the repository at this point in the history
*  Gallery demo start-time performance test
  • Loading branch information
Hans Muller committed May 4, 2016
1 parent 76b04cd commit a9b965c
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 20 deletions.
5 changes: 2 additions & 3 deletions examples/material_gallery/lib/demo/fitness_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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),
Expand All @@ -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()
Expand Down
20 changes: 10 additions & 10 deletions examples/material_gallery/lib/gallery/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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
);
9 changes: 8 additions & 1 deletion examples/material_gallery/lib/gallery/item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
}
}
);
}
Expand Down
11 changes: 11 additions & 0 deletions examples/material_gallery/test_driver/transitions_perf.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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();
}
93 changes: 93 additions & 0 deletions examples/material_gallery/test_driver/transitions_perf_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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)));
});
}
3 changes: 2 additions & 1 deletion packages/flutter_driver/lib/flutter_driver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export 'src/driver.dart' show
find,
CommonFinders,
EvaluatorFunction,
FlutterDriver;
FlutterDriver,
TracingCategory;

export 'src/error.dart' show
DriverError,
Expand Down
45 changes: 40 additions & 5 deletions packages/flutter_driver/lib/src/driver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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._();

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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();
}
Expand Down
8 changes: 8 additions & 0 deletions packages/flutter_driver/lib/src/extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class FlutterDriverExtension {
'tap': tap,
'get_text': getText,
'scroll': scroll,
'scrollIntoView': scrollIntoView,
'waitFor': waitFor,
});

Expand All @@ -71,6 +72,7 @@ class FlutterDriverExtension {
'tap': Tap.deserialize,
'get_text': GetText.deserialize,
'scroll': Scroll.deserialize,
'scrollIntoView': ScrollIntoView.deserialize,
'waitFor': WaitFor.deserialize,
});

Expand Down Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions packages/flutter_driver/lib/src/gesture.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
44 changes: 44 additions & 0 deletions packages/flutter_driver/test/flutter_driver_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
});
}

Expand Down

0 comments on commit a9b965c

Please sign in to comment.