// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file // for details. 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:io'; import 'package:benchmark_harness/benchmark_harness.dart'; import '../bin/collect_coverage.dart' as collect_coverage; import '../bin/format_coverage.dart' as format_coverage; // Runs a test script with various different coverage configurations. class CoverageBenchmark extends AsyncBenchmarkBase { CoverageBenchmark( ScoreEmitter emitter, super.name, this.script, { this.gatherCoverage = false, this.functionCoverage = false, this.branchCoverage = false, }) : super(emitter: emitter); final String script; final bool gatherCoverage; final bool functionCoverage; final bool branchCoverage; int iteration = 0; @override Future run() async { print('Running $name...'); final covFile = 'data/$name $iteration coverage.json'; final lcovFile = 'data/$name $iteration lcov.info'; ++iteration; await Process.start( Platform.executable, [ if (branchCoverage) '--branch-coverage', 'run', if (gatherCoverage) ...[ '--pause-isolates-on-exit', '--disable-service-auth-codes', '--enable-vm-service=1234', ], script, ], mode: ProcessStartMode.detached, ); if (gatherCoverage) { await collect_coverage.main([ '--wait-paused', '--resume-isolates', '--uri=http://127.0.0.1:1234/', if (branchCoverage) '--branch-coverage', if (functionCoverage) '--function-coverage', '-o', covFile, ]); await format_coverage.main([ '--lcov', '--check-ignore', '-i', covFile, '-o', lcovFile, ]); } } } // Emitter that just captures the value. class CaptureEmitter implements ScoreEmitter { late double capturedValue; @override void emit(String testName, double value) { capturedValue = value; } } // Prints a JSON representation of the benchmark results, in a format compatible // with the github benchmark action. class JsonEmitter implements ScoreEmitter { JsonEmitter(this._baseline); final double _baseline; final _results = {}; @override void emit(String testName, double value) { _results[testName] = value; } String write() => '[${_results.entries.map((entry) => """{ "name": "${entry.key}", "unit": "times slower", "value": ${(entry.value / _baseline).toStringAsFixed(2)} }""").join(',\n')}]'; } Future runBenchmark(CoverageBenchmark benchmark) async { for (var i = 0; i < 3; ++i) { try { await benchmark.report().timeout(const Duration(minutes: 2)); return; } on TimeoutException { print('Timed out'); } } print('Timed out too many times. Giving up.'); exit(127); } Future runBenchmarkSet(String name, String script) async { final captureEmitter = CaptureEmitter(); await runBenchmark( CoverageBenchmark(captureEmitter, '$name - no coverage', script)); final benchmarkBaseline = captureEmitter.capturedValue; final emitter = JsonEmitter(benchmarkBaseline); await runBenchmark(CoverageBenchmark( emitter, '$name - basic coverage', script, gatherCoverage: true)); await runBenchmark(CoverageBenchmark( emitter, '$name - function coverage', script, gatherCoverage: true, functionCoverage: true)); await runBenchmark(CoverageBenchmark( emitter, '$name - branch coverage', script, gatherCoverage: true, branchCoverage: true)); return emitter.write(); } Future main() async { // Assume this script was started from the root coverage directory. Change to // the benchmark directory. Directory.current = 'benchmark'; final result = await runBenchmarkSet('Many isolates', 'many_isolates.dart'); await File('data/benchmark_result.json').writeAsString(result); exit(0); }