transitions_perf_test.dart 4.61 KB
Newer Older
1 2 3 4 5
// 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';
6
import 'dart:convert' show JsonEncoder;
7

8 9
import 'package:file/file.dart';
import 'package:file/io.dart';
10
import 'package:flutter_driver/flutter_driver.dart';
11
import 'package:path/path.dart' as path;
12 13
import 'package:test/test.dart';

14 15 16 17 18 19 20
// Warning: this list must be kept in sync with the value of
// kAllGalleryItems.map((GalleryItem item) => item.category)).toList();
final List<String> demoCategories = <String>[
  'Demos',
  'Components',
  'Style'
];
21

22 23 24 25
// Warning: this list must be kept in sync with the value of
// kAllGalleryItems.map((GalleryItem item) => item.title).toList();
final List<String> demoTitles = <String>[
  // Demos
26
  'Pesto',
27
  'Shrine',
28
  'Contact profile',
29
  // Components
30
  'Bottom navigation',
31 32 33 34 35 36
  'Buttons',
  'Cards',
  'Chips',
  'Date picker',
  'Dialog',
  'Expand/collapse list control',
37
  'Expansion panels',
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
  'Floating action button',
  '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',
  // Style
  'Colors',
  'Typography'
];
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

Future<Null> saveDurationsHistogram(List<Map<String, dynamic>> events) async {
  final Map<String, List<int>> durations = new Map<String, List<int>>();
  Map<String, dynamic> startEvent;

  // Save the duration of the first frame after each 'Start Transition' event.
  for (Map<String, dynamic> event in events) {
    final String eventName = event['name'];
    if (eventName == 'Start Transition') {
      assert(startEvent == null);
      startEvent = event;
    } else if (startEvent != null && eventName == 'Frame') {
      final String routeName = startEvent['args']['to'];
      durations[routeName] ??= new List<int>();
      durations[routeName].add(event['dur']);
      startEvent = null;
    }
  }

  // Verify that the durations data is valid.
  if (durations.keys.isEmpty)
    throw 'no "Start Transition" timeline events found';
  for(String routeName in durations.keys) {
    if (durations[routeName] == null || durations[routeName].length != 2)
      throw 'invalid timeline data for $routeName transition';
  }

  // Save the durations Map to a file.
  final String destinationDirectory = 'build';
  final FileSystem fs = new LocalFileSystem();
  await fs.directory(destinationDirectory).create(recursive: true);
  final File file = fs.file(path.join(destinationDirectory, 'transition_durations.timeline.json'));
  await file.writeAsString(new JsonEncoder.withIndent('  ').convert(durations));
}

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
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.
117
        for(String demoTitle in demoTitles) {
118
          print('Testing "$demoTitle" demo');
119
          SerializableFinder menuItem = find.text(demoTitle);
120 121 122 123 124 125 126 127 128
          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));
          }
129
          print('Success');
130 131
        }
      },
132
      streams: const <TimelineStream>[
133
        TimelineStream.dart
134
      ]);
135 136 137 138 139 140

      // Save the duration (in microseconds) of the first timeline Frame event
      // that follows a 'Start Transition' event. The Gallery app adds a
      // 'Start Transition' event when a demo is launched (see GalleryItem).
      saveDurationsHistogram(timeline.json['traceEvents']);

141 142 143
      TimelineSummary summary = new TimelineSummary.summarize(timeline);
      summary.writeSummaryToFile('transitions');

144
    }, timeout: new Timeout(new Duration(minutes: 5)));
145 146
  });
}