// 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; }