// Copyright 2019 The Chromium 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 'dart:io'; import 'package:path/path.dart' as path; import '../framework/framework.dart'; import '../framework/running_processes.dart'; import '../framework/utils.dart'; final Directory _editedFlutterGalleryDir = dir(path.join(Directory.systemTemp.path, 'edited_flutter_gallery')); final Directory flutterGalleryDir = dir(path.join(flutterDirectory.path, 'examples/flutter_gallery')); TaskFunction createWebDevModeTest() { return () async { final List<String> options = <String>[ '--hot', '-d', 'chrome', '--verbose', '--resident', '--target=lib/main.dart', ]; int hotRestartCount = 0; String chromeProcessName; if (Platform.isMacOS) { chromeProcessName = 'Chrome'; } else if (Platform.isLinux) { chromeProcessName = 'chrome'; } else if (Platform.isWindows) { chromeProcessName = 'chrome.exe'; } final Set<String> beforeChromeProcesses = await getRunningProcesses(processName: chromeProcessName) .map((RunningProcessInfo info) => info.pid) .toSet(); try { await inDirectory<void>(flutterDirectory, () async { rmTree(_editedFlutterGalleryDir); mkdirs(_editedFlutterGalleryDir); recursiveCopy(flutterGalleryDir, _editedFlutterGalleryDir); await inDirectory<void>(_editedFlutterGalleryDir, () async { { final Process packagesGet = await startProcess( path.join(flutterDirectory.path, 'bin', 'flutter'), <String>['packages', 'get'], environment: <String, String>{ 'FLUTTER_WEB': 'true', }, ); await packagesGet.exitCode; final Process process = await startProcess( path.join(flutterDirectory.path, 'bin', 'flutter'), flutterCommandArgs('run', options), environment: <String, String>{ 'FLUTTER_WEB': 'true', }, ); final Completer<void> stdoutDone = Completer<void>(); final Completer<void> stderrDone = Completer<void>(); process.stdout .transform<String>(utf8.decoder) .transform<String>(const LineSplitter()) .listen((String line) { if (line.contains('To hot restart')) { process.stdin.write('R'); } if (line.contains('Restarted')) { if (hotRestartCount == 0) { // Update the file and reload again. final File appDartSource = file(path.join( _editedFlutterGalleryDir.path, 'lib/gallery/app.dart', )); appDartSource.writeAsStringSync( appDartSource.readAsStringSync().replaceFirst( "'Flutter Gallery'", "'Updated Flutter Gallery'", ) ); process.stdin.writeln('R'); ++hotRestartCount; } else { // Quit after second hot restart. process.stdin.writeln('q'); } } print('stdout: $line'); }, onDone: () { stdoutDone.complete(); }); process.stderr .transform<String>(utf8.decoder) .transform<String>(const LineSplitter()) .listen((String line) { print('stderr: $line'); }, onDone: () { stderrDone.complete(); }); await Future.wait<void>(<Future<void>>[ stdoutDone.future, stderrDone.future, ]); await process.exitCode; } // Start `flutter run` again to make sure it loads from the previous // state. dev compilers loads up from previously compiled JavaScript. { final Process process = await startProcess( path.join(flutterDirectory.path, 'bin', 'flutter'), flutterCommandArgs('run', options), environment: <String, String>{ 'FLUTTER_WEB': 'true', }, ); final Completer<void> stdoutDone = Completer<void>(); final Completer<void> stderrDone = Completer<void>(); process.stdout .transform<String>(utf8.decoder) .transform<String>(const LineSplitter()) .listen((String line) { if (line.contains('To hot restart')) { process.stdin.write('R'); } if (line.contains('Restarted')) { process.stdin.writeln('q'); } print('stdout: $line'); }, onDone: () { stdoutDone.complete(); }); process.stderr .transform<String>(utf8.decoder) .transform<String>(const LineSplitter()) .listen((String line) { print('stderr: $line'); }, onDone: () { stderrDone.complete(); }); await Future.wait<void>(<Future<void>>[ stdoutDone.future, stderrDone.future, ]); await process.exitCode; } }); }); } finally { final Set<String> afterChromeProcesses = await getRunningProcesses(processName: chromeProcessName) .map((RunningProcessInfo info) => info.pid) .toSet(); final Set<String> newProcesses = afterChromeProcesses.difference(beforeChromeProcesses); for (String processId in newProcesses) { await killProcess(processId); } } if (hotRestartCount != 1) { return TaskResult.failure(null); } return TaskResult.success(null); }; }