timeline_summary_test.dart 12.3 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:convert' show json;
6

7
import 'package:file/file.dart';
8 9
import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_driver/src/driver/common.dart';
10
import 'package:path/path.dart' as path;
11

12
import '../common.dart';
13 14 15 16 17

void main() {
  group('TimelineSummary', () {

    TimelineSummary summarize(List<Map<String, dynamic>> testEvents) {
18
      return TimelineSummary.summarize(Timeline.fromJson(<String, dynamic>{
19
        'traceEvents': testEvents,
20
      }));
21 22
    }

23
    Map<String, dynamic> frameBegin(int timeStamp) => <String, dynamic>{
24
      'name': 'Frame',
25 26 27 28 29 30 31
      'ph': 'B',
      'ts': timeStamp,
    };

    Map<String, dynamic> frameEnd(int timeStamp) => <String, dynamic>{
      'name': 'Frame',
      'ph': 'E',
32
      'ts': timeStamp,
33 34
    };

35
    Map<String, dynamic> begin(int timeStamp) => <String, dynamic>{
36 37 38
      'name': 'GPURasterizer::Draw',
      'ph': 'B',
      'ts': timeStamp,
39 40 41
    };

    Map<String, dynamic> end(int timeStamp) => <String, dynamic>{
42 43 44
      'name': 'GPURasterizer::Draw',
      'ph': 'E',
      'ts': timeStamp,
45 46
    };

47 48 49
    List<Map<String, dynamic>> rasterizeTimeSequenceInMillis(List<int> sequence) {
      final List<Map<String, dynamic>> result = <Map<String, dynamic>>[];
      int t = 0;
50
      for (int duration in sequence) {
51 52 53 54 55 56 57
        result.add(begin(t));
        t += duration * 1000;
        result.add(end(t));
      }
      return result;
    }

58 59 60
    group('frame_count', () {
      test('counts frames', () {
        expect(
pq's avatar
pq committed
61
          summarize(<Map<String, dynamic>>[
62 63
            frameBegin(1000), frameEnd(2000),
            frameBegin(3000), frameEnd(5000),
64
          ]).countFrames(),
65
          2,
66 67 68 69 70
        );
      });
    });

    group('average_frame_build_time_millis', () {
71 72 73
      test('throws when there is no data', () {
        expect(
          () => summarize(<Map<String, dynamic>>[]).computeAverageFrameBuildTimeMillis(),
74
          throwsA(predicate<ArgumentError>((ArgumentError e) => e.message == 'durations is empty!')),
75
        );
76 77 78 79
      });

      test('computes average frame build time in milliseconds', () {
        expect(
pq's avatar
pq committed
80
          summarize(<Map<String, dynamic>>[
81 82
            frameBegin(1000), frameEnd(2000),
            frameBegin(3000), frameEnd(5000),
83
          ]).computeAverageFrameBuildTimeMillis(),
84
          1.5,
85 86
        );
      });
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

      test('skips leading "end" events', () {
        expect(
          summarize(<Map<String, dynamic>>[
            frameEnd(1000),
            frameBegin(2000), frameEnd(4000),
          ]).computeAverageFrameBuildTimeMillis(),
          2.0,
        );
      });

      test('skips trailing "begin" events', () {
        expect(
          summarize(<Map<String, dynamic>>[
            frameBegin(2000), frameEnd(4000),
            frameBegin(5000),
          ]).computeAverageFrameBuildTimeMillis(),
          2.0,
        );
      });
107 108 109
    });

    group('worst_frame_build_time_millis', () {
110 111 112
      test('throws when there is no data', () {
        expect(
          () => summarize(<Map<String, dynamic>>[]).computeWorstFrameBuildTimeMillis(),
113
          throwsA(predicate<ArgumentError>((ArgumentError e) => e.message == 'durations is empty!')),
114
        );
115 116 117 118
      });

      test('computes worst frame build time in milliseconds', () {
        expect(
pq's avatar
pq committed
119
          summarize(<Map<String, dynamic>>[
120 121 122 123 124 125 126 127 128
            frameBegin(1000), frameEnd(2000),
            frameBegin(3000), frameEnd(5000),
          ]).computeWorstFrameBuildTimeMillis(),
          2.0,
        );
        expect(
          summarize(<Map<String, dynamic>>[
            frameBegin(3000), frameEnd(5000),
            frameBegin(1000), frameEnd(2000),
129
          ]).computeWorstFrameBuildTimeMillis(),
130
          2.0,
131
        );
132 133 134 135 136 137 138 139 140 141 142 143 144
      });

      test('skips leading "end" events', () {
        expect(
          summarize(<Map<String, dynamic>>[
            frameEnd(1000),
            frameBegin(2000), frameEnd(4000),
          ]).computeWorstFrameBuildTimeMillis(),
          2.0,
        );
      });

      test('skips trailing "begin" events', () {
145
        expect(
pq's avatar
pq committed
146
          summarize(<Map<String, dynamic>>[
147 148
            frameBegin(2000), frameEnd(4000),
            frameBegin(5000),
149
          ]).computeWorstFrameBuildTimeMillis(),
150
          2.0,
151 152 153 154 155 156
        );
      });
    });

    group('computeMissedFrameBuildBudgetCount', () {
      test('computes the number of missed build budgets', () {
157
        final TimelineSummary summary = summarize(<Map<String, dynamic>>[
158 159 160
          frameBegin(1000), frameEnd(18000),
          frameBegin(19000), frameEnd(28000),
          frameBegin(29000), frameEnd(47000),
161 162 163 164 165 166 167
        ]);

        expect(summary.countFrames(), 3);
        expect(summary.computeMissedFrameBuildBudgetCount(), 2);
      });
    });

168
    group('average_frame_rasterizer_time_millis', () {
169 170 171
      test('throws when there is no data', () {
        expect(
          () => summarize(<Map<String, dynamic>>[]).computeAverageFrameRasterizerTimeMillis(),
172
          throwsA(predicate<ArgumentError>((ArgumentError e) => e.message == 'durations is empty!')),
173
        );
174 175 176 177 178 179 180 181
      });

      test('computes average frame rasterizer time in milliseconds', () {
        expect(
            summarize(<Map<String, dynamic>>[
              begin(1000), end(2000),
              begin(3000), end(5000),
            ]).computeAverageFrameRasterizerTimeMillis(),
182
            1.5,
183 184 185 186 187 188 189 190 191
        );
      });

      test('skips leading "end" events', () {
        expect(
            summarize(<Map<String, dynamic>>[
              end(1000),
              begin(2000), end(4000),
            ]).computeAverageFrameRasterizerTimeMillis(),
192
            2.0,
193 194 195 196 197 198 199 200 201
        );
      });

      test('skips trailing "begin" events', () {
        expect(
            summarize(<Map<String, dynamic>>[
              begin(2000), end(4000),
              begin(5000),
            ]).computeAverageFrameRasterizerTimeMillis(),
202
            2.0,
203 204 205 206 207
        );
      });
    });

    group('worst_frame_rasterizer_time_millis', () {
208 209 210
      test('throws when there is no data', () {
        expect(
          () => summarize(<Map<String, dynamic>>[]).computeWorstFrameRasterizerTimeMillis(),
211
          throwsA(predicate<ArgumentError>((ArgumentError e) => e.message == 'durations is empty!')),
212
        );
213 214
      });

215

216 217 218 219 220 221
      test('computes worst frame rasterizer time in milliseconds', () {
        expect(
            summarize(<Map<String, dynamic>>[
              begin(1000), end(2000),
              begin(3000), end(5000),
            ]).computeWorstFrameRasterizerTimeMillis(),
222
            2.0,
223 224 225 226 227 228
        );
        expect(
            summarize(<Map<String, dynamic>>[
              begin(3000), end(5000),
              begin(1000), end(2000),
            ]).computeWorstFrameRasterizerTimeMillis(),
229
            2.0,
230 231 232 233 234 235 236 237 238
        );
      });

      test('skips leading "end" events', () {
        expect(
            summarize(<Map<String, dynamic>>[
              end(1000),
              begin(2000), end(4000),
            ]).computeWorstFrameRasterizerTimeMillis(),
239
            2.0,
240 241 242 243 244 245 246 247 248
        );
      });

      test('skips trailing "begin" events', () {
        expect(
            summarize(<Map<String, dynamic>>[
              begin(2000), end(4000),
              begin(5000),
            ]).computeWorstFrameRasterizerTimeMillis(),
249
            2.0,
250 251 252 253
        );
      });
    });

254
    group('percentile_frame_rasterizer_time_millis', () {
255 256 257
      test('throws when there is no data', () {
        expect(
          () => summarize(<Map<String, dynamic>>[]).computePercentileFrameRasterizerTimeMillis(90.0),
258
          throwsA(predicate<ArgumentError>((ArgumentError e) => e.message == 'durations is empty!')),
259
        );
260 261
      });

262

263 264 265
      const List<List<int>> sequences = <List<int>>[
        <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        <int>[1, 2, 3, 4, 5],
266
        <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
267 268 269 270 271
      ];

      const List<int> p90s = <int>[
        9,
        5,
272
        18,
273 274 275
      ];

      test('computes 90th frame rasterizer time in milliseconds', () {
276
        for (int i = 0; i < sequences.length; ++i) {
277 278
          expect(
            summarize(rasterizeTimeSequenceInMillis(sequences[i])).computePercentileFrameRasterizerTimeMillis(90.0),
279
            p90s[i],
280 281 282 283 284 285
          );
        }
      });

      test('compute 99th frame rasterizer time in milliseconds', () {
        final List<int> sequence = <int>[];
286
        for (int i = 1; i <= 100; ++i) {
287 288 289 290
          sequence.add(i);
        }
        expect(
          summarize(rasterizeTimeSequenceInMillis(sequence)).computePercentileFrameRasterizerTimeMillis(99.0),
291
          99,
292 293 294 295
        );
      });
    });

296 297
    group('computeMissedFrameRasterizerBudgetCount', () {
      test('computes the number of missed rasterizer budgets', () {
298
        final TimelineSummary summary = summarize(<Map<String, dynamic>>[
299 300 301
          begin(1000), end(18000),
          begin(19000), end(28000),
          begin(29000), end(47000),
302 303 304 305 306 307
        ]);

        expect(summary.computeMissedFrameRasterizerBudgetCount(), 2);
      });
    });

308 309 310
    group('summaryJson', () {
      test('computes and returns summary as JSON', () {
        expect(
pq's avatar
pq committed
311
          summarize(<Map<String, dynamic>>[
312 313 314
            begin(1000), end(19000),
            begin(19000), end(29000),
            begin(29000), end(49000),
315 316 317
            frameBegin(1000), frameEnd(18000),
            frameBegin(19000), frameEnd(28000),
            frameBegin(29000), frameEnd(48000),
318
          ]).summaryJson,
pq's avatar
pq committed
319
          <String, dynamic>{
320 321 322 323
            'average_frame_build_time_millis': 15.0,
            '90th_percentile_frame_build_time_millis': 19.0,
            '99th_percentile_frame_build_time_millis': 19.0,
            'worst_frame_build_time_millis': 19.0,
324
            'missed_frame_build_budget_count': 2,
325 326 327 328
            'average_frame_rasterizer_time_millis': 16.0,
            '90th_percentile_frame_rasterizer_time_millis': 20.0,
            '99th_percentile_frame_rasterizer_time_millis': 20.0,
            'worst_frame_rasterizer_time_millis': 20.0,
329
            'missed_frame_rasterizer_budget_count': 2,
330
            'frame_count': 3,
331 332
            'frame_build_times': <int>[17000, 9000, 19000],
            'frame_rasterizer_times': <int>[18000, 10000, 20000],
333
          },
334 335 336 337 338
        );
      });
    });

    group('writeTimelineToFile', () {
339 340 341

      Directory tempDir;

342 343
      setUp(() {
        useMemoryFileSystemForTesting();
344
        tempDir = fs.systemTempDirectory.createTempSync('flutter_driver_test.');
345 346 347
      });

      tearDown(() {
348
        tryToDelete(tempDir);
349 350 351 352
        restoreFileSystem();
      });

      test('writes timeline to JSON file', () async {
pq's avatar
pq committed
353
        await summarize(<Map<String, String>>[<String, String>{'foo': 'bar'}])
354
          .writeTimelineToFile('test', destinationDirectory: tempDir.path);
355
        final String written =
356
            await fs.file(path.join(tempDir.path, 'test.timeline.json')).readAsString();
357 358 359 360
        expect(written, '{"traceEvents":[{"foo":"bar"}]}');
      });

      test('writes summary to JSON file', () async {
pq's avatar
pq committed
361
        await summarize(<Map<String, dynamic>>[
362 363 364
          begin(1000), end(19000),
          begin(19000), end(29000),
          begin(29000), end(49000),
365 366 367
          frameBegin(1000), frameEnd(18000),
          frameBegin(19000), frameEnd(28000),
          frameBegin(29000), frameEnd(48000),
368
        ]).writeSummaryToFile('test', destinationDirectory: tempDir.path);
369
        final String written =
370
            await fs.file(path.join(tempDir.path, 'test.timeline_summary.json')).readAsString();
371
        expect(json.decode(written), <String, dynamic>{
372 373 374 375
          'average_frame_build_time_millis': 15.0,
          'worst_frame_build_time_millis': 19.0,
          '90th_percentile_frame_build_time_millis': 19.0,
          '99th_percentile_frame_build_time_millis': 19.0,
376
          'missed_frame_build_budget_count': 2,
377 378 379 380
          'average_frame_rasterizer_time_millis': 16.0,
          '90th_percentile_frame_rasterizer_time_millis': 20.0,
          '99th_percentile_frame_rasterizer_time_millis': 20.0,
          'worst_frame_rasterizer_time_millis': 20.0,
381
          'missed_frame_rasterizer_budget_count': 2,
382
          'frame_count': 3,
383 384
          'frame_build_times': <int>[17000, 9000, 19000],
          'frame_rasterizer_times': <int>[18000, 10000, 20000],
385 386 387 388 389
        });
      });
    });
  });
}