| // 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:async'; |
| import 'dart:convert'; |
| |
| import 'package:file/file.dart'; |
| import 'package:flutter_tools/src/base/io.dart'; |
| |
| import '../src/common.dart'; |
| import 'test_utils.dart'; |
| |
| final String flutterRootPath = getFlutterRoot(); |
| final Directory flutterRoot = fileSystem.directory(flutterRootPath); |
| |
| Future<void> main() async { |
| // Regression test for https://github.com/flutter/flutter/issues/132592 |
| test('flutter/bin/dart updates the Dart SDK without hanging', () async { |
| // Run the Dart entrypoint once to ensure the Dart SDK is downloaded. |
| await runDartBatch(); |
| |
| expect(dartSdkStamp.existsSync(), true); |
| |
| // Remove the Dart SDK stamp and run the Dart entrypoint again to trigger |
| // the Dart SDK update. |
| dartSdkStamp.deleteSync(); |
| final Future<String> runFuture = runDartBatch(); |
| final Timer timer = Timer(const Duration(minutes: 5), () { |
| // This print is useful for people debugging this test. Normally we would |
| // avoid printing in a test but this is an exception because it's useful |
| // ambient information. |
| // ignore: avoid_print |
| print( |
| 'The Dart batch entrypoint did not complete after 5 minutes. ' |
| 'Historically this is a sign that 7-Zip zip extraction is waiting for ' |
| 'the user to confirm they would like to overwrite files. ' |
| "This likely means the test isn't a flake and will fail. " |
| 'See: https://github.com/flutter/flutter/issues/132592' |
| ); |
| }); |
| |
| final String output = await runFuture; |
| timer.cancel(); |
| |
| // Check the Dart SDK was re-downloaded and extracted. |
| // If 7-Zip is installed, unexpected overwrites causes this to hang. |
| // If 7-Zip is not installed, unexpected overwrites results in error messages. |
| // See: https://github.com/flutter/flutter/issues/132592 |
| expect(dartSdkStamp.existsSync(), true); |
| expect(output, contains('Downloading Dart SDK from Flutter engine ...')); |
| // Do not assert on the exact unzipping method, as this could change on CI |
| expect(output, contains(RegExp(r'Expanding downloaded archive with (.*)...'))); |
| expect(output, isNot(contains('Use the -Force parameter' /* Luke */))); |
| }, |
| skip: !platform.isWindows); // [intended] Only Windows uses the batch entrypoint |
| } |
| |
| Future<String> runDartBatch() async { |
| String output = ''; |
| final Process process = await processManager.start( |
| <String>[ |
| dartBatch.path |
| ], |
| ); |
| final Future<Object?> stdoutFuture = process.stdout |
| .transform<String>(utf8.decoder) |
| .forEach((String str) { |
| output += str; |
| }); |
| final Future<Object?> stderrFuture = process.stderr |
| .transform<String>(utf8.decoder) |
| .forEach((String str) { |
| output += str; |
| }); |
| |
| // Wait for the output to complete |
| await Future.wait(<Future<Object?>>[stdoutFuture, stderrFuture]); |
| // Ensure child exited successfully |
| expect( |
| await process.exitCode, |
| 0, |
| reason: 'child process exited with code ${await process.exitCode}, and ' |
| 'output:\n$output', |
| ); |
| |
| // Check the Dart tool prints the expected output. |
| expect(output, contains('A command-line utility for Dart development.')); |
| expect(output, contains('Usage: dart <command|dart-file> [arguments]')); |
| |
| return output; |
| } |
| |
| // The executable batch entrypoint for the Dart binary. |
| File get dartBatch { |
| return flutterRoot |
| .childDirectory('bin') |
| .childFile('dart.bat') |
| .absolute; |
| } |
| |
| // The Dart SDK's stamp file. |
| File get dartSdkStamp { |
| return flutterRoot |
| .childDirectory('bin') |
| .childDirectory('cache') |
| .childFile('engine-dart-sdk.stamp') |
| .absolute; |
| } |