Gallery demo start-time performance test (#3655)
* Gallery demo start-time performance test
This commit is contained in:
parent
76b04cddde
commit
a9b965cb9b
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'dart:ui' as ui;
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_sprites/flutter_sprites.dart';
|
import 'package:flutter_sprites/flutter_sprites.dart';
|
||||||
@ -288,7 +287,7 @@ class _ProgressCircle extends NodeWithSize {
|
|||||||
Paint circlePaint = new Paint()
|
Paint circlePaint = new Paint()
|
||||||
..color = Colors.white30
|
..color = Colors.white30
|
||||||
..strokeWidth = 24.0
|
..strokeWidth = 24.0
|
||||||
..style = ui.PaintingStyle.stroke;
|
..style = PaintingStyle.stroke;
|
||||||
|
|
||||||
canvas.drawCircle(
|
canvas.drawCircle(
|
||||||
new Point(size.width / 2.0, size.height / 2.0),
|
new Point(size.width / 2.0, size.height / 2.0),
|
||||||
@ -299,7 +298,7 @@ class _ProgressCircle extends NodeWithSize {
|
|||||||
Paint pathPaint = new Paint()
|
Paint pathPaint = new Paint()
|
||||||
..color = Colors.purple[500]
|
..color = Colors.purple[500]
|
||||||
..strokeWidth = 25.0
|
..strokeWidth = 25.0
|
||||||
..style = ui.PaintingStyle.stroke;
|
..style = PaintingStyle.stroke;
|
||||||
|
|
||||||
double angle = value.clamp(0.0, 1.0) * _kSweep;
|
double angle = value.clamp(0.0, 1.0) * _kSweep;
|
||||||
Path path = new Path()
|
Path path = new Path()
|
||||||
|
@ -44,6 +44,16 @@ final Map<String, WidgetBuilder> kRoutes = <String, WidgetBuilder>{
|
|||||||
TypographyDemo.routeName: (BuildContext context) => new TypographyDemo(),
|
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 {
|
class GalleryApp extends StatefulWidget {
|
||||||
GalleryApp({ Key key }) : super(key: key);
|
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
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
typedef Widget GalleryDemoBuilder();
|
typedef Widget GalleryDemoBuilder();
|
||||||
@ -21,8 +23,13 @@ class GalleryItem extends StatelessWidget {
|
|||||||
leading: leading,
|
leading: leading,
|
||||||
title: new Text(title),
|
title: new Text(title),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (routeName != null)
|
if (routeName != null) {
|
||||||
|
Timeline.instantSync('Start Transition', arguments: <String, String>{
|
||||||
|
'from': '/',
|
||||||
|
'to': routeName
|
||||||
|
});
|
||||||
Navigator.pushNamed(context, routeName);
|
Navigator.pushNamed(context, routeName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
11
examples/material_gallery/test_driver/transitions_perf.dart
Normal file
11
examples/material_gallery/test_driver/transitions_perf.dart
Normal file
@ -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();
|
||||||
|
}
|
@ -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)));
|
||||||
|
});
|
||||||
|
}
|
@ -15,7 +15,8 @@ export 'src/driver.dart' show
|
|||||||
find,
|
find,
|
||||||
CommonFinders,
|
CommonFinders,
|
||||||
EvaluatorFunction,
|
EvaluatorFunction,
|
||||||
FlutterDriver;
|
FlutterDriver,
|
||||||
|
TracingCategory;
|
||||||
|
|
||||||
export 'src/error.dart' show
|
export 'src/error.dart' show
|
||||||
DriverError,
|
DriverError,
|
||||||
|
@ -15,13 +15,39 @@ import 'health.dart';
|
|||||||
import 'message.dart';
|
import 'message.dart';
|
||||||
import 'timeline.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');
|
final Logger _log = new Logger('FlutterDriver');
|
||||||
|
|
||||||
/// A convenient accessor to frequently used finders.
|
/// A convenient accessor to frequently used finders.
|
||||||
///
|
///
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// driver.tap(find.byText('Save'));
|
/// driver.tap(find.text('Save'));
|
||||||
/// driver.scroll(find.byValueKey(42));
|
/// driver.scroll(find.byValueKey(42));
|
||||||
const CommonFinders find = const CommonFinders._();
|
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);
|
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].
|
/// Returns the text in the `Text` widget located by [finder].
|
||||||
Future<String> getText(SerializableFinder finder) async {
|
Future<String> getText(SerializableFinder finder) async {
|
||||||
return GetTextResult.fromJson(await _sendCommand(new GetText(finder))).text;
|
return GetTextResult.fromJson(await _sendCommand(new GetText(finder))).text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts recording performance traces.
|
/// Starts recording performance traces.
|
||||||
Future<Null> startTracing() async {
|
Future<Null> startTracing({List<TracingCategory> categories: _defaultCategories}) async {
|
||||||
|
assert(categories != null && categories.length > 0);
|
||||||
try {
|
try {
|
||||||
await _peer.sendRequest(_kSetVMTimelineFlagsMethod, {'recordedStreams': '[all]'});
|
await _peer.sendRequest(_kSetVMTimelineFlagsMethod, {
|
||||||
|
'recordedStreams': _tracingCategoriesToString(categories)
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
} catch(error, stackTrace) {
|
} catch(error, stackTrace) {
|
||||||
throw new DriverError(
|
throw new DriverError(
|
||||||
@ -251,8 +286,8 @@ class FlutterDriver {
|
|||||||
///
|
///
|
||||||
/// This is merely a convenience wrapper on top of [startTracing] and
|
/// This is merely a convenience wrapper on top of [startTracing] and
|
||||||
/// [stopTracingAndDownloadTimeline].
|
/// [stopTracingAndDownloadTimeline].
|
||||||
Future<Timeline> traceAction(Future<dynamic> action()) async {
|
Future<Timeline> traceAction(Future<dynamic> action(), { List<TracingCategory> categories: _defaultCategories }) async {
|
||||||
await startTracing();
|
await startTracing(categories: categories);
|
||||||
await action();
|
await action();
|
||||||
return stopTracingAndDownloadTimeline();
|
return stopTracingAndDownloadTimeline();
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ class FlutterDriverExtension {
|
|||||||
'tap': tap,
|
'tap': tap,
|
||||||
'get_text': getText,
|
'get_text': getText,
|
||||||
'scroll': scroll,
|
'scroll': scroll,
|
||||||
|
'scrollIntoView': scrollIntoView,
|
||||||
'waitFor': waitFor,
|
'waitFor': waitFor,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -71,6 +72,7 @@ class FlutterDriverExtension {
|
|||||||
'tap': Tap.deserialize,
|
'tap': Tap.deserialize,
|
||||||
'get_text': GetText.deserialize,
|
'get_text': GetText.deserialize,
|
||||||
'scroll': Scroll.deserialize,
|
'scroll': Scroll.deserialize,
|
||||||
|
'scrollIntoView': ScrollIntoView.deserialize,
|
||||||
'waitFor': WaitFor.deserialize,
|
'waitFor': WaitFor.deserialize,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -207,6 +209,12 @@ class FlutterDriverExtension {
|
|||||||
return new ScrollResult();
|
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 {
|
Future<GetTextResult> getText(GetText command) async {
|
||||||
Finder target = await _waitForElement(_createFinder(command.finder));
|
Finder target = await _waitForElement(_createFinder(command.finder));
|
||||||
// TODO(yjbanov): support more ways to read text
|
// 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 {
|
class ScrollResult extends Result {
|
||||||
static ScrollResult fromJson(Map<String, dynamic> json) {
|
static ScrollResult fromJson(Map<String, dynamic> json) {
|
||||||
return new ScrollResult();
|
return new ScrollResult();
|
||||||
|
@ -235,6 +235,50 @@ void main() {
|
|||||||
expect(timeline.events.single.name, 'test event');
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user