gallery.dart 3.31 KB
Newer Older
1 2 3 4 5 6 7
// 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 'dart:convert';
import 'dart:io';
8
import 'dart:math' as math;
9 10 11

import '../framework/adb.dart';
import '../framework/framework.dart';
12
import '../framework/ios.dart';
13 14
import '../framework/utils.dart';

15
TaskFunction createGalleryTransitionTest({ bool semanticsEnabled = false }) {
16
  return GalleryTransitionTest(semanticsEnabled: semanticsEnabled);
17 18 19 20
}

class GalleryTransitionTest {

21
  GalleryTransitionTest({ this.semanticsEnabled = false });
22 23 24

  final bool semanticsEnabled;

25
  Future<TaskResult> call() async {
26
    final Device device = await devices.workingDevice;
27
    await device.unlock();
28 29
    final String deviceId = device.deviceId;
    final Directory galleryDirectory =
30
        dir('${flutterDirectory.path}/examples/flutter_gallery');
31
    await inDirectory<void>(galleryDirectory, () async {
32
      await flutter('packages', options: <String>['get']);
33

34
      if (deviceOperatingSystem == DeviceOperatingSystem.ios)
35
        await prepareProvisioningCertificates(galleryDirectory.path);
36

37 38 39 40
      final String testDriver = semanticsEnabled
          ? 'transitions_perf_with_semantics.dart'
          : 'transitions_perf.dart';

41 42 43 44
      await flutter('drive', options: <String>[
        '--profile',
        '--trace-startup',
        '-t',
45
        'test_driver/$testDriver',
46 47 48 49 50 51 52
        '-d',
        deviceId,
      ]);
    });

    // Route paths contains slashes, which Firebase doesn't accept in keys, so we
    // remove them.
53 54 55 56
    final Map<String, dynamic> original = Map<String, dynamic>.from(
        json.decode(
            file('${galleryDirectory.path}/build/transition_durations.timeline.json').readAsStringSync()
        ));
57 58
    final Map<String, List<int>> transitions = <String, List<int>>{};
    for (String key in original.keys) {
59
      transitions[key.replaceAll('/', '')] = List<int>.from(original[key]);
60
    }
61

62
    final Map<String, dynamic> summary = json.decode(file('${galleryDirectory.path}/build/transitions.timeline_summary.json').readAsStringSync());
63

64
    final Map<String, dynamic> data = <String, dynamic>{
65
      'transitions': transitions,
66
      'missed_transition_count': _countMissedTransitions(transitions),
67 68 69
    };
    data.addAll(summary);

70
    return TaskResult.success(data, benchmarkScoreKeys: <String>[
71
      'missed_transition_count',
72 73 74
      'average_frame_build_time_millis',
      'worst_frame_build_time_millis',
      'missed_frame_build_budget_count',
75 76
      '90th_percentile_frame_build_time_millis',
      '99th_percentile_frame_build_time_millis',
77 78
      'average_frame_rasterizer_time_millis',
      'worst_frame_rasterizer_time_millis',
79
      'missed_frame_rasterizer_budget_count',
80 81
      '90th_percentile_frame_rasterizer_time_millis',
      '99th_percentile_frame_rasterizer_time_millis',
82
    ]);
83 84
  }
}
85 86 87 88 89

int _countMissedTransitions(Map<String, List<int>> transitions) {
  const int _kTransitionBudget = 100000; // µs
  int count = 0;
  transitions.forEach((String demoName, List<int> durations) {
90
    final int longestDuration = durations.reduce(math.max);
91 92 93 94 95 96 97
    if (longestDuration > _kTransitionBudget) {
      print('$demoName missed transition time budget ($longestDuration µs > $_kTransitionBudget µs)');
      count++;
    }
  });
  return count;
}