| // Copyright 2014 The Flutter 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:io'; |
| |
| import 'package:path/path.dart' as path; |
| |
| import '../framework/task_result.dart'; |
| import '../framework/utils.dart'; |
| |
| /// Run each benchmark this many times and compute average, min, max. |
| const int _kRunsPerBenchmark = 10; |
| |
| Future<TaskResult> flutterToolStartupBenchmarkTask() async { |
| final Directory projectParentDirectory = |
| Directory.systemTemp.createTempSync('flutter_tool_startup_benchmark'); |
| final Directory projectDirectory = |
| dir(path.join(projectParentDirectory.path, 'benchmark')); |
| await inDirectory<void>(flutterDirectory, () async { |
| await flutter('update-packages'); |
| await flutter('create', options: <String>[projectDirectory.path]); |
| // Remove 'test' directory so we don't time the actual testing, but only the launching of the flutter tool |
| rmTree(dir(path.join(projectDirectory.path, 'test'))); |
| }); |
| |
| final Map<String, dynamic> data = <String, dynamic>{ |
| // `flutter test` in dir with no `test` folder. |
| ...(await _Benchmark( |
| projectDirectory, |
| 'test startup', |
| 'test', |
| ).run()) |
| .asMap('flutter_tool_startup_test'), |
| |
| // `flutter test -d foo_device` in dir woth no `test` folder. |
| ...(await _Benchmark( |
| projectDirectory, |
| 'test startup with specified device', |
| 'test', |
| options: <String>['-d', 'foo_device'], |
| ).run()) |
| .asMap('flutter_tool_startup_test_with_specified_device'), |
| |
| // `flutter test -v` where no android sdk will be found (at least currently). |
| ...(await _Benchmark( |
| projectDirectory, |
| 'test startup no android sdk', |
| 'test', |
| options: <String>['-v'], |
| environment: <String, String>{ |
| 'ANDROID_HOME': 'dummy value', |
| 'ANDROID_SDK_ROOT': 'dummy value', |
| 'PATH': pathWithoutWhereHits(<String>['adb', 'aapt']), |
| }, |
| ).run()) |
| .asMap('flutter_tool_startup_test_no_android_sdk'), |
| |
| // `flutter -h`. |
| ...(await _Benchmark( |
| projectDirectory, |
| 'help startup', |
| '-h', |
| ).run()) |
| .asMap('flutter_tool_startup_help'), |
| }; |
| |
| // Cleanup. |
| rmTree(projectParentDirectory); |
| |
| return TaskResult.success(data, benchmarkScoreKeys: data.keys.toList()); |
| } |
| |
| String pathWithoutWhereHits(List<String> whats) { |
| final String pathEnvironment = Platform.environment['PATH'] ?? ''; |
| List<String> paths; |
| if (Platform.isWindows) { |
| paths = pathEnvironment.split(';'); |
| } else { |
| paths = pathEnvironment.split(':'); |
| } |
| // This isn't great but will probably work for our purposes. |
| final List<String> extensions = <String>['', '.exe', '.bat', '.com']; |
| |
| final List<String> notFound = <String>[]; |
| for (final String path in paths) { |
| bool found = false; |
| for (final String extension in extensions) { |
| for (final String what in whats) { |
| final File f = File('$path${Platform.pathSeparator}$what$extension'); |
| if (f.existsSync()) { |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| break; |
| } |
| } |
| if (!found) { |
| notFound.add(path); |
| } |
| } |
| |
| if (Platform.isWindows) { |
| return notFound.join(';'); |
| } else { |
| return notFound.join(':'); |
| } |
| } |
| |
| class _BenchmarkResult { |
| const _BenchmarkResult(this.mean, this.min, this.max); |
| |
| final int mean; // Milliseconds |
| |
| final int min; // Milliseconds |
| |
| final int max; // Milliseconds |
| |
| Map<String, dynamic> asMap(String name) { |
| return <String, dynamic>{ |
| name: mean, |
| '${name}_minimum': min, |
| '${name}_maximum': max, |
| }; |
| } |
| } |
| |
| class _Benchmark { |
| _Benchmark(this.directory, this.title, this.command, |
| {this.options = const <String>[], this.environment}); |
| |
| final Directory directory; |
| |
| final String title; |
| |
| final String command; |
| |
| final List<String> options; |
| |
| final Map<String, String>? environment; |
| |
| Future<int> execute(int iteration, int targetIterations) async { |
| section('Benchmark $title - ${iteration + 1} / $targetIterations'); |
| final Stopwatch stopwatch = Stopwatch(); |
| await inDirectory<void>(directory, () async { |
| stopwatch.start(); |
| // canFail is set to true, as e.g. `flutter test` in a dir with no `test` |
| // directory sets a non-zero return value. |
| await flutter(command, |
| options: options, canFail: true, environment: environment); |
| stopwatch.stop(); |
| }); |
| return stopwatch.elapsedMilliseconds; |
| } |
| |
| /// Runs `benchmark` several times and reports the results. |
| Future<_BenchmarkResult> run() async { |
| final List<int> results = <int>[]; |
| int sum = 0; |
| for (int i = 0; i < _kRunsPerBenchmark; i++) { |
| final int thisRuntime = await execute(i, _kRunsPerBenchmark); |
| results.add(thisRuntime); |
| sum += thisRuntime; |
| } |
| results.sort(); |
| return _BenchmarkResult(sum ~/ results.length, results.first, results.last); |
| } |
| } |