mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Bumps the Dart version to 3.8 across the repo (excluding engine/src/flutter/third_party) and applies formatting updates from Dart 3.8. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
163 lines
5.3 KiB
Dart
163 lines
5.3 KiB
Dart
// 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:convert';
|
|
import 'dart:io' as io;
|
|
|
|
import 'package:args/args.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
final ArgParser _argParser = ArgParser()
|
|
..addFlag('help', abbr: 'h', help: 'Display usage information.', negatable: false)
|
|
..addFlag('verbose', abbr: 'v', help: 'Show noisy output while running', negatable: false)
|
|
..addFlag(
|
|
'generate-initial-golden',
|
|
help:
|
|
'Whether an initial run (not part of "runs") should generate the '
|
|
'base golden file. If false, it is assumed the golden file wasl already generated.',
|
|
defaultsTo: true,
|
|
)
|
|
..addFlag(
|
|
'build-app-once',
|
|
help:
|
|
'Whether to use flutter build and --use-application-binary instead of rebuilding every iteration.',
|
|
defaultsTo: true,
|
|
)
|
|
..addOption('runs', abbr: 'n', help: 'How many times to run the test.', defaultsTo: '10');
|
|
|
|
/// Builds, establishes a baseline, and runs a golden-file test N number of times.
|
|
///
|
|
/// Example use:
|
|
/// ```sh
|
|
/// dart ./tool/deflake.dart lib/external_texture/surface_texture_smiley_face_main.dart
|
|
/// ```
|
|
///
|
|
/// By default it will:
|
|
/// - Build the app once (and reuse the APK);
|
|
/// - Generate a baseline (local) golden-file, overwriting your local file system;
|
|
/// - Run N (by default, 10) subsequent tests, asserting the generated golden exactly matches.
|
|
///
|
|
/// For advanced usage, see `dart ./tool/deflake.dart --help`.
|
|
void main(List<String> args) async {
|
|
final ArgResults argResults = _argParser.parse(args);
|
|
if (argResults.flag('help')) {
|
|
return _printUsage();
|
|
}
|
|
|
|
final List<String> testFiles = argResults.rest;
|
|
if (testFiles.length != 1) {
|
|
io.stderr.writeln('Exactly one test-file must be specified');
|
|
_printUsage();
|
|
io.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
final io.File testFile = io.File(testFiles.single);
|
|
if (!testFile.existsSync()) {
|
|
io.stderr.writeln('Not a file: ${testFile.path}');
|
|
_printUsage();
|
|
io.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
final bool generateInitialGolden = argResults.flag('generate-initial-golden');
|
|
final bool buildAppOnce = argResults.flag('build-app-once');
|
|
final bool verbose = argResults.flag('verbose');
|
|
final int runs;
|
|
{
|
|
final String rawRuns = argResults.option('runs')!;
|
|
final int? parsedRuns = int.tryParse(rawRuns);
|
|
if (parsedRuns == null || parsedRuns < 1) {
|
|
io.stderr.writeln('--runs must be a positive number: "$rawRuns".');
|
|
io.exitCode = 1;
|
|
return;
|
|
}
|
|
runs = parsedRuns;
|
|
}
|
|
|
|
final List<String> driverArgs;
|
|
if (buildAppOnce) {
|
|
io.stderr.writeln('Building initial app with "flutter build apk --debug...');
|
|
final io.Process proccess = await io.Process.start('flutter', <String>[
|
|
'build',
|
|
'apk',
|
|
'--debug',
|
|
testFile.path,
|
|
], mode: verbose ? io.ProcessStartMode.inheritStdio : io.ProcessStartMode.normal);
|
|
if (await proccess.exitCode case final int exitCode when exitCode != 0) {
|
|
io.stderr.writeln('Failed to build (exit code = $exitCode).');
|
|
io.stderr.writeln(_collectStdOut(proccess));
|
|
io.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
// Strictly speaking, it would be better to parse stdout for:
|
|
// "✓ Built build/app/outputs/flutter-apk/app-debug.apk"
|
|
//
|
|
// ... _or_ specify the expected out ourselves and rely on that.
|
|
driverArgs = <String>[
|
|
'drive',
|
|
'--use-application-binary',
|
|
p.join('build', 'app', 'outputs', 'flutter-apk', 'app-debug.apk'),
|
|
testFile.path,
|
|
];
|
|
} else {
|
|
// I can't imagine wanting to do this, but here is the option anyway!
|
|
driverArgs = <String>['drive', testFile.path];
|
|
}
|
|
|
|
Future<bool> runDriverTest({Map<String, String>? environment}) async {
|
|
final io.Process proccess = await io.Process.start(
|
|
'flutter',
|
|
driverArgs,
|
|
mode: verbose ? io.ProcessStartMode.inheritStdio : io.ProcessStartMode.normal,
|
|
environment: environment,
|
|
);
|
|
if (await proccess.exitCode case final int exitCode when exitCode != 0) {
|
|
io.stderr.writeln('Failed to build (exit code = $exitCode).');
|
|
io.stderr.writeln(_collectStdOut(proccess));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Do an initial baseline run.
|
|
if (generateInitialGolden) {
|
|
io.stderr.writeln('Generating a baseline set of golden-files...');
|
|
await runDriverTest(environment: <String, String>{'UPDATE_GOLDENS': '1'});
|
|
}
|
|
|
|
// Now run.
|
|
int totalFailed = 0;
|
|
for (int i = 0; i < runs; i++) {
|
|
io.stderr.writeln('RUN ${i + 1} of $runs');
|
|
final bool result = await runDriverTest();
|
|
if (!result) {
|
|
totalFailed++;
|
|
io.stderr.writeln('FAIL');
|
|
} else {
|
|
io.stderr.writeln('PASS');
|
|
}
|
|
}
|
|
|
|
io.stderr.writeln('PASSED: ${runs - totalFailed} / $runs');
|
|
if (totalFailed != 0) {
|
|
io.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
void _printUsage() {
|
|
io.stdout.writeln('Usage: dart tool/deflake.dart lib/<path-to-main>.dart');
|
|
io.stdout.writeln(_argParser.usage);
|
|
}
|
|
|
|
Future<String> _collectStdOut(io.Process process) async {
|
|
final StringBuffer buffer = StringBuffer();
|
|
buffer.writeln('stdout:');
|
|
buffer.writeln(await utf8.decodeStream(process.stdout));
|
|
buffer.writeln('stderr:');
|
|
buffer.writeln(await utf8.decodeStream(process.stderr));
|
|
return buffer.toString();
|
|
}
|