Unverified Commit 93e7d34d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] Remove globals/mocks from GradleUtils (#76020)

parent 3c77c432
...@@ -25,6 +25,7 @@ import '../globals.dart' as globals hide logger, printStatus, printTrace, printE ...@@ -25,6 +25,7 @@ import '../globals.dart' as globals hide logger, printStatus, printTrace, printE
import '../project.dart'; import '../project.dart';
import '../reporting/reporting.dart'; import '../reporting/reporting.dart';
import 'android_builder.dart'; import 'android_builder.dart';
import 'android_studio.dart';
import 'gradle_errors.dart'; import 'gradle_errors.dart';
import 'gradle_utils.dart'; import 'gradle_utils.dart';
...@@ -142,12 +143,15 @@ Future<void> checkGradleDependencies(Logger logger) async { ...@@ -142,12 +143,15 @@ Future<void> checkGradleDependencies(Logger logger) async {
); );
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
await globals.processUtils.run(<String>[ await globals.processUtils.run(<String>[
gradleUtils.getExecutable(flutterProject), globals.gradleUtils.getExecutable(flutterProject),
'dependencies', 'dependencies',
], ],
throwOnError: true, throwOnError: true,
workingDirectory: flutterProject.android.hostAppGradleRoot.path, workingDirectory: flutterProject.android.hostAppGradleRoot.path,
environment: gradleEnvironment, environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath,
},
); );
globals.androidSdk?.reinitialize(); globals.androidSdk?.reinitialize();
progress.stop(); progress.stop();
...@@ -364,7 +368,7 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -364,7 +368,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
); );
final List<String> command = <String>[ final List<String> command = <String>[
gradleUtils.getExecutable(project), globals.gradleUtils.getExecutable(project),
]; ];
if (_logger.isVerbose) { if (_logger.isVerbose) {
command.add('-Pverbose=true'); command.add('-Pverbose=true');
...@@ -454,7 +458,10 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -454,7 +458,10 @@ class AndroidGradleBuilder implements AndroidBuilder {
command, command,
workingDirectory: project.android.hostAppGradleRoot.path, workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true, allowReentrantFlutter: true,
environment: gradleEnvironment, environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath,
},
mapFunction: consumeLog, mapFunction: consumeLog,
); );
} on ProcessException catch (exception) { } on ProcessException catch (exception) {
...@@ -525,7 +532,7 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -525,7 +532,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
} }
if (isBuildingBundle) { if (isBuildingBundle) {
final File bundleFile = findBundleFile(project, buildInfo); final File bundleFile = findBundleFile(project, buildInfo, _logger);
final String appSize = (buildInfo.mode == BuildMode.debug) final String appSize = (buildInfo.mode == BuildMode.debug)
? '' // Don't display the size when building a debug variant. ? '' // Don't display the size when building a debug variant.
: ' (${getSizeAsMB(bundleFile.lengthSync())})'; : ' (${getSizeAsMB(bundleFile.lengthSync())})';
...@@ -542,7 +549,7 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -542,7 +549,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
} }
// Gradle produced an APK. // Gradle produced an APK.
final Iterable<String> apkFilesPaths = project.isModule final Iterable<String> apkFilesPaths = project.isModule
? findApkFilesModule(project, androidBuildInfo) ? findApkFilesModule(project, androidBuildInfo, _logger)
: listApkPaths(androidBuildInfo); : listApkPaths(androidBuildInfo);
final Directory apkDirectory = getApkDirectory(project); final Directory apkDirectory = getApkDirectory(project);
final File apkFile = apkDirectory.childFile(apkFilesPaths.first); final File apkFile = apkDirectory.childFile(apkFilesPaths.first);
...@@ -550,6 +557,7 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -550,6 +557,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
_exitWithExpectedFileNotFound( _exitWithExpectedFileNotFound(
project: project, project: project,
fileExtension: '.apk', fileExtension: '.apk',
logger: _logger,
); );
} }
...@@ -659,7 +667,7 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -659,7 +667,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
'aar_init_script.gradle', 'aar_init_script.gradle',
); );
final List<String> command = <String>[ final List<String> command = <String>[
gradleUtils.getExecutable(project), globals.gradleUtils.getExecutable(project),
'-I=$initScript', '-I=$initScript',
'-Pflutter-root=$flutterRoot', '-Pflutter-root=$flutterRoot',
'-Poutput-dir=${outputDirectory.path}', '-Poutput-dir=${outputDirectory.path}',
...@@ -702,14 +710,14 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -702,14 +710,14 @@ class AndroidGradleBuilder implements AndroidBuilder {
// Copy the local engine repo in the output directory. // Copy the local engine repo in the output directory.
try { try {
globals.fsUtils.copyDirectorySync( copyDirectory(
localEngineRepo, localEngineRepo,
getRepoDirectory(outputDirectory), getRepoDirectory(outputDirectory),
); );
} on FileSystemException catch (_) { } on FileSystemException catch (error, st) {
throwToolExit( throwToolExit(
'Failed to copy the local engine ${localEngineRepo.path} repo ' 'Failed to copy the local engine ${localEngineRepo.path} repo '
'in ${outputDirectory.path}' 'in ${outputDirectory.path}: $error, $st'
); );
} }
command.add('-Ptarget-platform=${_getTargetPlatformByLocalEnginePath( command.add('-Ptarget-platform=${_getTargetPlatformByLocalEnginePath(
...@@ -730,7 +738,10 @@ class AndroidGradleBuilder implements AndroidBuilder { ...@@ -730,7 +738,10 @@ class AndroidGradleBuilder implements AndroidBuilder {
command, command,
workingDirectory: project.android.hostAppGradleRoot.path, workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true, allowReentrantFlutter: true,
environment: gradleEnvironment, environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath,
},
); );
} finally { } finally {
status.stop(); status.stop();
...@@ -921,6 +932,7 @@ bool isAppUsingAndroidX(Directory androidDirectory) { ...@@ -921,6 +932,7 @@ bool isAppUsingAndroidX(Directory androidDirectory) {
Iterable<String> findApkFilesModule( Iterable<String> findApkFilesModule(
FlutterProject project, FlutterProject project,
AndroidBuildInfo androidBuildInfo, AndroidBuildInfo androidBuildInfo,
Logger logger,
) { ) {
final Iterable<String> apkFileNames = _apkFilesFor(androidBuildInfo); final Iterable<String> apkFileNames = _apkFilesFor(androidBuildInfo);
final Directory apkDirectory = getApkDirectory(project); final Directory apkDirectory = getApkDirectory(project);
...@@ -953,6 +965,7 @@ Iterable<String> findApkFilesModule( ...@@ -953,6 +965,7 @@ Iterable<String> findApkFilesModule(
_exitWithExpectedFileNotFound( _exitWithExpectedFileNotFound(
project: project, project: project,
fileExtension: '.apk', fileExtension: '.apk',
logger: logger,
); );
} }
return apks.map((File file) => file.path); return apks.map((File file) => file.path);
...@@ -991,7 +1004,7 @@ Iterable<String> listApkPaths( ...@@ -991,7 +1004,7 @@ Iterable<String> listApkPaths(
} }
@visibleForTesting @visibleForTesting
File findBundleFile(FlutterProject project, BuildInfo buildInfo) { File findBundleFile(FlutterProject project, BuildInfo buildInfo, Logger logger) {
final List<File> fileCandidates = <File>[ final List<File> fileCandidates = <File>[
getBundleDirectory(project) getBundleDirectory(project)
.childDirectory(camelCase(buildInfo.modeName)) .childDirectory(camelCase(buildInfo.modeName))
...@@ -1025,6 +1038,7 @@ File findBundleFile(FlutterProject project, BuildInfo buildInfo) { ...@@ -1025,6 +1038,7 @@ File findBundleFile(FlutterProject project, BuildInfo buildInfo) {
_exitWithExpectedFileNotFound( _exitWithExpectedFileNotFound(
project: project, project: project,
fileExtension: '.aab', fileExtension: '.aab',
logger: logger,
); );
return null; return null;
} }
...@@ -1033,12 +1047,13 @@ File findBundleFile(FlutterProject project, BuildInfo buildInfo) { ...@@ -1033,12 +1047,13 @@ File findBundleFile(FlutterProject project, BuildInfo buildInfo) {
void _exitWithExpectedFileNotFound({ void _exitWithExpectedFileNotFound({
@required FlutterProject project, @required FlutterProject project,
@required String fileExtension, @required String fileExtension,
@required Logger logger,
}) { }) {
assert(project != null); assert(project != null);
assert(fileExtension != null); assert(fileExtension != null);
final String androidGradlePluginVersion = final String androidGradlePluginVersion =
getGradleVersionForAndroidPlugin(project.android.hostAppGradleRoot); getGradleVersionForAndroidPlugin(project.android.hostAppGradleRoot, logger);
BuildEvent('gradle-expected-file-not-found', BuildEvent('gradle-expected-file-not-found',
settings: settings:
'androidGradlePluginVersion: $androidGradlePluginVersion, ' 'androidGradlePluginVersion: $androidGradlePluginVersion, '
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8 // @dart = 2.8
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import '../base/error_handling_io.dart'; import '../base/error_handling_io.dart';
...@@ -13,7 +12,7 @@ import '../base/terminal.dart'; ...@@ -13,7 +12,7 @@ import '../base/terminal.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../project.dart'; import '../project.dart';
import '../reporting/reporting.dart'; import '../reporting/reporting.dart';
import 'gradle_utils.dart'; import 'android_studio.dart';
typedef GradleErrorTest = bool Function(String); typedef GradleErrorTest = bool Function(String);
...@@ -295,14 +294,17 @@ final GradleHandledError flavorUndefinedHandler = GradleHandledError( ...@@ -295,14 +294,17 @@ final GradleHandledError flavorUndefinedHandler = GradleHandledError(
}) async { }) async {
final RunResult tasksRunResult = await globals.processUtils.run( final RunResult tasksRunResult = await globals.processUtils.run(
<String>[ <String>[
gradleUtils.getExecutable(project), globals.gradleUtils.getExecutable(project),
'app:tasks' , 'app:tasks' ,
'--all', '--all',
'--console=auto', '--console=auto',
], ],
throwOnError: true, throwOnError: true,
workingDirectory: project.android.hostAppGradleRoot.path, workingDirectory: project.android.hostAppGradleRoot.path,
environment: gradleEnvironment, environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath,
},
); );
// Extract build types and product flavors. // Extract build types and product flavors.
final Set<String> variants = <String>{}; final Set<String> variants = <String>{};
......
...@@ -7,8 +7,11 @@ ...@@ -7,8 +7,11 @@
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/os.dart';
import '../base/platform.dart';
import '../base/terminal.dart'; import '../base/terminal.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../base/version.dart'; import '../base/version.dart';
...@@ -17,41 +20,46 @@ import '../cache.dart'; ...@@ -17,41 +20,46 @@ import '../cache.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../project.dart'; import '../project.dart';
import '../reporting/reporting.dart'; import '../reporting/reporting.dart';
import 'android_studio.dart';
/// The environment variables needed to run Gradle. const String _defaultGradleVersion = '6.7';
Map<String, String> get gradleEnvironment {
final Map<String, String> environment = Map<String, String>.of(globals.platform.environment);
if (javaPath != null) {
// Use java bundled with Android Studio.
environment['JAVA_HOME'] = javaPath;
}
// Don't log analytics for downstream Flutter commands.
// e.g. `flutter build bundle`.
environment['FLUTTER_SUPPRESS_ANALYTICS'] = 'true';
return environment;
}
/// Gradle utils in the current [AppContext]. final RegExp _androidPluginRegExp = RegExp(r'com\.android\.tools\.build:gradle:(\d+\.\d+\.\d+)');
GradleUtils get gradleUtils => context.get<GradleUtils>();
/// Provides utilities to run a Gradle task, /// Provides utilities to run a Gradle task, such as finding the Gradle executable
/// such as finding the Gradle executable or constructing a Gradle project. /// or constructing a Gradle project.
class GradleUtils { class GradleUtils {
GradleUtils({
@required Platform platform,
@required Logger logger,
@required FileSystem fileSystem,
@required Cache cache,
@required OperatingSystemUtils operatingSystemUtils,
}) : _platform = platform,
_logger = logger,
_cache = cache,
_fileSystem = fileSystem,
_operatingSystemUtils = operatingSystemUtils;
final Cache _cache;
final FileSystem _fileSystem;
final Platform _platform;
final Logger _logger;
final OperatingSystemUtils _operatingSystemUtils;
/// Gets the Gradle executable path and prepares the Gradle project. /// Gets the Gradle executable path and prepares the Gradle project.
/// This is the `gradlew` or `gradlew.bat` script in the `android/` directory. /// This is the `gradlew` or `gradlew.bat` script in the `android/` directory.
String getExecutable(FlutterProject project) { String getExecutable(FlutterProject project) {
final Directory androidDir = project.android.hostAppGradleRoot; final Directory androidDir = project.android.hostAppGradleRoot;
gradleUtils.injectGradleWrapperIfNeeded(androidDir); injectGradleWrapperIfNeeded(androidDir);
final File gradle = androidDir.childFile( final File gradle = androidDir.childFile(
globals.platform.isWindows ? 'gradlew.bat' : 'gradlew', _platform.isWindows ? 'gradlew.bat' : 'gradlew',
); );
if (gradle.existsSync()) { if (gradle.existsSync()) {
globals.printTrace('Using gradle from ${gradle.absolute.path}.'); _logger.printTrace('Using gradle from ${gradle.absolute.path}.');
// If the Gradle executable doesn't have execute permission, // If the Gradle executable doesn't have execute permission,
// then attempt to set it. // then attempt to set it.
_giveExecutePermissionIfNeeded(gradle); _operatingSystemUtils.makeExecutable(gradle);
return gradle.absolute.path; return gradle.absolute.path;
} }
throwToolExit( throwToolExit(
...@@ -63,89 +71,61 @@ class GradleUtils { ...@@ -63,89 +71,61 @@ class GradleUtils {
/// Injects the Gradle wrapper files if any of these files don't exist in [directory]. /// Injects the Gradle wrapper files if any of these files don't exist in [directory].
void injectGradleWrapperIfNeeded(Directory directory) { void injectGradleWrapperIfNeeded(Directory directory) {
globals.fsUtils.copyDirectorySync( copyDirectory(
globals.cache.getArtifactDirectory('gradle_wrapper'), _cache.getArtifactDirectory('gradle_wrapper'),
directory, directory,
shouldCopyFile: (File sourceFile, File destinationFile) { shouldCopyFile: (File sourceFile, File destinationFile) {
// Don't override the existing files in the project. // Don't override the existing files in the project.
return !destinationFile.existsSync(); return !destinationFile.existsSync();
}, },
onFileCopied: (File sourceFile, File destinationFile) { onFileCopied: (File source, File dest) {
if (_hasAnyExecutableFlagSet(sourceFile)) { _operatingSystemUtils.makeExecutable(dest);
_giveExecutePermissionIfNeeded(destinationFile); }
}
},
); );
// Add the `gradle-wrapper.properties` file if it doesn't exist. // Add the `gradle-wrapper.properties` file if it doesn't exist.
final Directory propertiesDirectory = directory.childDirectory( final Directory propertiesDirectory = directory
globals.fs.path.join('gradle', 'wrapper')); .childDirectory(_fileSystem.path.join('gradle', 'wrapper'));
final File propertiesFile = propertiesDirectory.childFile('gradle-wrapper.properties'); final File propertiesFile = propertiesDirectory
if (!propertiesFile.existsSync()) { .childFile('gradle-wrapper.properties');
propertiesDirectory.createSync(recursive: true);
final String gradleVersion = getGradleVersionForAndroidPlugin(directory); if (propertiesFile.existsSync()) {
propertiesFile.writeAsStringSync(''' return;
}
propertiesDirectory.createSync(recursive: true);
final String gradleVersion = getGradleVersionForAndroidPlugin(directory, _logger);
final String propertyContents = '''
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersion-all.zip distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersion-all.zip
''', flush: true, ''';
); propertiesFile.writeAsStringSync(propertyContents);
}
} }
} }
const String _defaultGradleVersion = '6.7';
final RegExp _androidPluginRegExp = RegExp(r'com\.android\.tools\.build:gradle:(\d+\.\d+\.\d+)');
/// Returns the Gradle version that the current Android plugin depends on when found, /// Returns the Gradle version that the current Android plugin depends on when found,
/// otherwise it returns a default version. /// otherwise it returns a default version.
/// ///
/// The Android plugin version is specified in the [build.gradle] file within /// The Android plugin version is specified in the [build.gradle] file within
/// the project's Android directory. /// the project's Android directory.
String getGradleVersionForAndroidPlugin(Directory directory) { String getGradleVersionForAndroidPlugin(Directory directory, Logger logger) {
final File buildFile = directory.childFile('build.gradle'); final File buildFile = directory.childFile('build.gradle');
if (!buildFile.existsSync()) { if (!buildFile.existsSync()) {
globals.printTrace('$buildFile doesn\'t exist, assuming AGP version: $_defaultGradleVersion'); logger.printTrace('$buildFile doesn\'t exist, assuming AGP version: $_defaultGradleVersion');
return _defaultGradleVersion; return _defaultGradleVersion;
} }
final String buildFileContent = buildFile.readAsStringSync(); final String buildFileContent = buildFile.readAsStringSync();
final Iterable<Match> pluginMatches = _androidPluginRegExp.allMatches(buildFileContent); final Iterable<Match> pluginMatches = _androidPluginRegExp.allMatches(buildFileContent);
if (pluginMatches.isEmpty) { if (pluginMatches.isEmpty) {
globals.printTrace('$buildFile doesn\'t provide an AGP version, assuming AGP version: $_defaultGradleVersion'); logger.printTrace('$buildFile doesn\'t provide an AGP version, assuming AGP version: $_defaultGradleVersion');
return _defaultGradleVersion; return _defaultGradleVersion;
} }
final String androidPluginVersion = pluginMatches.first.group(1); final String androidPluginVersion = pluginMatches.first.group(1);
globals.printTrace('$buildFile provides AGP version: $androidPluginVersion'); logger.printTrace('$buildFile provides AGP version: $androidPluginVersion');
return getGradleVersionFor(androidPluginVersion); return getGradleVersionFor(androidPluginVersion);
} }
const int _kExecPermissionMask = 0x49; // a+x
/// Returns [true] if [executable] has all executable flag set.
bool _hasAllExecutableFlagSet(File executable) {
final FileStat stat = executable.statSync();
assert(stat.type != FileSystemEntityType.notFound);
globals.printTrace('${executable.path} mode: ${stat.mode} ${stat.modeString()}.');
return stat.mode & _kExecPermissionMask == _kExecPermissionMask;
}
/// Returns [true] if [executable] has any executable flag set.
bool _hasAnyExecutableFlagSet(File executable) {
final FileStat stat = executable.statSync();
assert(stat.type != FileSystemEntityType.notFound);
globals.printTrace('${executable.path} mode: ${stat.mode} ${stat.modeString()}.');
return stat.mode & _kExecPermissionMask != 0;
}
/// Gives execute permission to [executable] if it doesn't have it already.
void _giveExecutePermissionIfNeeded(File executable) {
if (!_hasAllExecutableFlagSet(executable)) {
globals.printTrace('Trying to give execute permission to ${executable.path}.');
globals.os.makeExecutable(executable);
}
}
/// Returns true if [targetVersion] is within the range [min] and [max] inclusive. /// Returns true if [targetVersion] is within the range [min] and [max] inclusive.
bool _isWithinVersionRange( bool _isWithinVersionRange(
String targetVersion, { String targetVersion, {
...@@ -162,6 +142,7 @@ bool _isWithinVersionRange( ...@@ -162,6 +142,7 @@ bool _isWithinVersionRange(
/// Returns the Gradle version that is required by the given Android Gradle plugin version /// Returns the Gradle version that is required by the given Android Gradle plugin version
/// by picking the largest compatible version from /// by picking the largest compatible version from
/// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle /// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
@visibleForTesting
String getGradleVersionFor(String androidPluginVersion) { String getGradleVersionFor(String androidPluginVersion) {
if (_isWithinVersionRange(androidPluginVersion, min: '1.0.0', max: '1.1.3')) { if (_isWithinVersionRange(androidPluginVersion, min: '1.0.0', max: '1.1.3')) {
return '2.3'; return '2.3';
......
...@@ -54,47 +54,6 @@ class FileSystemUtils { ...@@ -54,47 +54,6 @@ class FileSystemUtils {
} }
} }
/// Creates `destDir` if needed, then recursively copies `srcDir` to
/// `destDir`, invoking [onFileCopied], if specified, for each
/// source/destination file pair.
///
/// Skips files if [shouldCopyFile] returns `false`.
void copyDirectorySync(
Directory srcDir,
Directory destDir, {
bool shouldCopyFile(File srcFile, File destFile),
void onFileCopied(File srcFile, File destFile),
}) {
if (!srcDir.existsSync()) {
throw Exception('Source directory "${srcDir.path}" does not exist, nothing to copy');
}
if (!destDir.existsSync()) {
destDir.createSync(recursive: true);
}
for (final FileSystemEntity entity in srcDir.listSync()) {
final String newPath = destDir.fileSystem.path.join(destDir.path, entity.basename);
if (entity is File) {
final File newFile = destDir.fileSystem.file(newPath);
if (shouldCopyFile != null && !shouldCopyFile(entity, newFile)) {
continue;
}
newFile.writeAsBytesSync(entity.readAsBytesSync());
onFileCopied?.call(entity, newFile);
} else if (entity is Directory) {
copyDirectorySync(
entity,
destDir.fileSystem.directory(newPath),
shouldCopyFile: shouldCopyFile,
onFileCopied: onFileCopied,
);
} else {
throw Exception('${entity.path} is neither File nor Directory');
}
}
}
/// Appends a number to a filename in order to make it unique under a /// Appends a number to a filename in order to make it unique under a
/// directory. /// directory.
File getUniqueFile(Directory dir, String baseName, String ext) { File getUniqueFile(Directory dir, String baseName, String ext) {
...@@ -170,6 +129,50 @@ class FileSystemUtils { ...@@ -170,6 +129,50 @@ class FileSystemUtils {
} }
} }
/// Creates `destDir` if needed, then recursively copies `srcDir` to
/// `destDir`, invoking [onFileCopied], if specified, for each
/// source/destination file pair.
///
/// Skips files if [shouldCopyFile] returns `false`.
void copyDirectory(
Directory srcDir,
Directory destDir, {
bool shouldCopyFile(File srcFile, File destFile),
void onFileCopied(File srcFile, File destFile),
}) {
if (!srcDir.existsSync()) {
throw Exception('Source directory "${srcDir.path}" does not exist, nothing to copy');
}
if (!destDir.existsSync()) {
destDir.createSync(recursive: true);
}
for (final FileSystemEntity entity in srcDir.listSync()) {
final String newPath = destDir.fileSystem.path.join(destDir.path, entity.basename);
if (entity is Link) {
final Link newLink = destDir.fileSystem.link(newPath);
newLink.createSync(entity.targetSync());
} else if (entity is File) {
final File newFile = destDir.fileSystem.file(newPath);
if (shouldCopyFile != null && !shouldCopyFile(entity, newFile)) {
continue;
}
newFile.writeAsBytesSync(entity.readAsBytesSync());
onFileCopied?.call(entity, newFile);
} else if (entity is Directory) {
copyDirectory(
entity,
destDir.fileSystem.directory(newPath),
shouldCopyFile: shouldCopyFile,
onFileCopied: onFileCopied,
);
} else {
throw Exception('${entity.path} is neither File nor Directory, was ${entity.runtimeType}');
}
}
}
/// This class extends [local_fs.LocalFileSystem] in order to clean up /// This class extends [local_fs.LocalFileSystem] in order to clean up
/// directories and files that the tool creates under the system temporary /// directories and files that the tool creates under the system temporary
/// directory when the tool exits either normally or when killed by a signal. /// directory when the tool exits either normally or when killed by a signal.
......
...@@ -12,7 +12,7 @@ import 'package:meta/meta.dart'; ...@@ -12,7 +12,7 @@ import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart'; import 'package:package_config/package_config.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'android/gradle_utils.dart'; import 'android/android_studio.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/error_handling_io.dart'; import 'base/error_handling_io.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
...@@ -1174,7 +1174,7 @@ class AndroidMavenArtifacts extends ArtifactSet { ...@@ -1174,7 +1174,7 @@ class AndroidMavenArtifacts extends ArtifactSet {
final Directory tempDir = cache.getRoot().createTempSync( final Directory tempDir = cache.getRoot().createTempSync(
'flutter_gradle_wrapper.', 'flutter_gradle_wrapper.',
); );
gradleUtils.injectGradleWrapperIfNeeded(tempDir); globals.gradleUtils.injectGradleWrapperIfNeeded(tempDir);
final Status status = logger.startProgress('Downloading Android Maven dependencies...'); final Status status = logger.startProgress('Downloading Android Maven dependencies...');
final File gradle = tempDir.childFile( final File gradle = tempDir.childFile(
...@@ -1190,7 +1190,11 @@ class AndroidMavenArtifacts extends ArtifactSet { ...@@ -1190,7 +1190,11 @@ class AndroidMavenArtifacts extends ArtifactSet {
'--project-cache-dir', tempDir.path, '--project-cache-dir', tempDir.path,
'resolveDependencies', 'resolveDependencies',
], ],
environment: gradleEnvironment); environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath,
},
);
if (processResult.exitCode != 0) { if (processResult.exitCode != 0) {
logger.printError('Failed to download the Android dependencies'); logger.printError('Failed to download the Android dependencies');
} }
......
...@@ -322,7 +322,7 @@ end ...@@ -322,7 +322,7 @@ end
try { try {
// Copy xcframework engine cache framework to mode directory. // Copy xcframework engine cache framework to mode directory.
globals.fsUtils.copyDirectorySync( copyDirectory(
globals.fs.directory(engineCacheFlutterFrameworkDirectory), globals.fs.directory(engineCacheFlutterFrameworkDirectory),
flutterFrameworkCopy, flutterFrameworkCopy,
); );
......
...@@ -521,7 +521,7 @@ abstract class CreateBase extends FlutterCommand { ...@@ -521,7 +521,7 @@ abstract class CreateBase extends FlutterCommand {
int _injectGradleWrapper(FlutterProject project) { int _injectGradleWrapper(FlutterProject project) {
int filesCreated = 0; int filesCreated = 0;
globals.fsUtils.copyDirectorySync( copyDirectory(
globals.cache.getArtifactDirectory('gradle_wrapper'), globals.cache.getArtifactDirectory('gradle_wrapper'),
project.android.hostAppGradleRoot, project.android.hostAppGradleRoot,
onFileCopied: (File sourceFile, File destinationFile) { onFileCopied: (File sourceFile, File destinationFile) {
......
...@@ -219,7 +219,13 @@ Future<T> runInContext<T>( ...@@ -219,7 +219,13 @@ Future<T> runInContext<T>(
platform: globals.platform, platform: globals.platform,
fuchsiaArtifacts: globals.fuchsiaArtifacts, fuchsiaArtifacts: globals.fuchsiaArtifacts,
), ),
GradleUtils: () => GradleUtils(), GradleUtils: () => GradleUtils(
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
logger: globals.logger,
platform: globals.platform,
cache: globals.cache,
),
HotRunnerConfig: () => HotRunnerConfig(), HotRunnerConfig: () => HotRunnerConfig(),
IOSSimulatorUtils: () => IOSSimulatorUtils( IOSSimulatorUtils: () => IOSSimulatorUtils(
logger: globals.logger, logger: globals.logger,
......
...@@ -8,6 +8,7 @@ import 'package:process/process.dart'; ...@@ -8,6 +8,7 @@ import 'package:process/process.dart';
import 'android/android_sdk.dart'; import 'android/android_sdk.dart';
import 'android/android_studio.dart'; import 'android/android_studio.dart';
import 'android/gradle_utils.dart';
import 'artifacts.dart'; import 'artifacts.dart';
import 'base/bot_detector.dart'; import 'base/bot_detector.dart';
import 'base/config.dart'; import 'base/config.dart';
...@@ -203,3 +204,6 @@ PlistParser _plistInstance; ...@@ -203,3 +204,6 @@ PlistParser _plistInstance;
/// The global template renderer. /// The global template renderer.
TemplateRenderer get templateRenderer => context.get<TemplateRenderer>(); TemplateRenderer get templateRenderer => context.get<TemplateRenderer>();
/// Gradle utils in the current [AppContext].
GradleUtils get gradleUtils => context.get<GradleUtils>();
...@@ -435,7 +435,7 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -435,7 +435,7 @@ Future<XcodeBuildResult> buildXcodeProject({
// (for example, kernel binary files produced from previous run). // (for example, kernel binary files produced from previous run).
globals.fs.directory(outputDir).deleteSync(recursive: true); globals.fs.directory(outputDir).deleteSync(recursive: true);
} }
globals.fsUtils.copyDirectorySync( copyDirectory(
globals.fs.directory(expectedOutputDirectory), globals.fs.directory(expectedOutputDirectory),
globals.fs.directory(outputDir), globals.fs.directory(outputDir),
); );
......
...@@ -689,7 +689,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -689,7 +689,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
) )
); );
if (framework.existsSync()) { if (framework.existsSync()) {
globals.fsUtils.copyDirectorySync( copyDirectory(
framework, framework,
engineCopyDirectory.childDirectory('Flutter.xcframework'), engineCopyDirectory.childDirectory('Flutter.xcframework'),
); );
...@@ -900,7 +900,7 @@ to migrate your project. ...@@ -900,7 +900,7 @@ to migrate your project.
'library_new_embedding', 'library_new_embedding',
), ephemeralDirectory); ), ephemeralDirectory);
await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), ephemeralDirectory); await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
gradle.gradleUtils.injectGradleWrapperIfNeeded(ephemeralDirectory); globals.gradleUtils.injectGradleWrapperIfNeeded(ephemeralDirectory);
} }
Future<void> _overwriteFromTemplate(String path, Directory target) async { Future<void> _overwriteFromTemplate(String path, Directory target) async {
......
...@@ -120,24 +120,18 @@ class ChromiumLauncher { ...@@ -120,24 +120,18 @@ class ChromiumLauncher {
@required OperatingSystemUtils operatingSystemUtils, @required OperatingSystemUtils operatingSystemUtils,
@required BrowserFinder browserFinder, @required BrowserFinder browserFinder,
@required Logger logger, @required Logger logger,
@visibleForTesting FileSystemUtils fileSystemUtils,
}) : _fileSystem = fileSystem, }) : _fileSystem = fileSystem,
_platform = platform, _platform = platform,
_processManager = processManager, _processManager = processManager,
_operatingSystemUtils = operatingSystemUtils, _operatingSystemUtils = operatingSystemUtils,
_browserFinder = browserFinder, _browserFinder = browserFinder,
_logger = logger, _logger = logger;
_fileSystemUtils = fileSystemUtils ?? FileSystemUtils(
fileSystem: fileSystem,
platform: platform,
);
final FileSystem _fileSystem; final FileSystem _fileSystem;
final Platform _platform; final Platform _platform;
final ProcessManager _processManager; final ProcessManager _processManager;
final OperatingSystemUtils _operatingSystemUtils; final OperatingSystemUtils _operatingSystemUtils;
final BrowserFinder _browserFinder; final BrowserFinder _browserFinder;
final FileSystemUtils _fileSystemUtils;
final Logger _logger; final Logger _logger;
bool get hasChromeInstance => _currentCompleter.isCompleted; bool get hasChromeInstance => _currentCompleter.isCompleted;
...@@ -314,11 +308,10 @@ class ChromiumLauncher { ...@@ -314,11 +308,10 @@ class ChromiumLauncher {
void _cacheUserSessionInformation(Directory userDataDir, Directory cacheDir) { void _cacheUserSessionInformation(Directory userDataDir, Directory cacheDir) {
final Directory targetChromeDefault = _fileSystem.directory(_fileSystem.path.join(cacheDir?.path ?? '', _chromeDefaultPath)); final Directory targetChromeDefault = _fileSystem.directory(_fileSystem.path.join(cacheDir?.path ?? '', _chromeDefaultPath));
final Directory sourceChromeDefault = _fileSystem.directory(_fileSystem.path.join(userDataDir.path, _chromeDefaultPath)); final Directory sourceChromeDefault = _fileSystem.directory(_fileSystem.path.join(userDataDir.path, _chromeDefaultPath));
if (sourceChromeDefault.existsSync()) { if (sourceChromeDefault.existsSync()) {
targetChromeDefault.createSync(recursive: true); targetChromeDefault.createSync(recursive: true);
try { try {
_fileSystemUtils.copyDirectorySync(sourceChromeDefault, targetChromeDefault); copyDirectory(sourceChromeDefault, targetChromeDefault);
} on FileSystemException catch (err) { } on FileSystemException catch (err) {
// This is a best-effort update. Display the message in case the failure is relevant. // This is a best-effort update. Display the message in case the failure is relevant.
// one possible example is a file lock due to multiple running chrome instances. // one possible example is a file lock due to multiple running chrome instances.
...@@ -343,11 +336,10 @@ class ChromiumLauncher { ...@@ -343,11 +336,10 @@ class ChromiumLauncher {
void _restoreUserSessionInformation(Directory cacheDir, Directory userDataDir) { void _restoreUserSessionInformation(Directory cacheDir, Directory userDataDir) {
final Directory sourceChromeDefault = _fileSystem.directory(_fileSystem.path.join(cacheDir.path ?? '', _chromeDefaultPath)); final Directory sourceChromeDefault = _fileSystem.directory(_fileSystem.path.join(cacheDir.path ?? '', _chromeDefaultPath));
final Directory targetChromeDefault = _fileSystem.directory(_fileSystem.path.join(userDataDir.path, _chromeDefaultPath)); final Directory targetChromeDefault = _fileSystem.directory(_fileSystem.path.join(userDataDir.path, _chromeDefaultPath));
try { try {
if (sourceChromeDefault.existsSync()) { if (sourceChromeDefault.existsSync()) {
targetChromeDefault.createSync(recursive: true); targetChromeDefault.createSync(recursive: true);
_fileSystemUtils.copyDirectorySync(sourceChromeDefault, targetChromeDefault); copyDirectory(sourceChromeDefault, targetChromeDefault);
} }
} on FileSystemException catch (err) { } on FileSystemException catch (err) {
_logger.printError('Failed to restore Chrome preferences: $err'); _logger.printError('Failed to restore Chrome preferences: $err');
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import 'package:archive/archive.dart'; import 'package:archive/archive.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart'; import 'package:flutter_tools/src/android/android_studio.dart';
import 'package:flutter_tools/src/android/gradle.dart'; import 'package:flutter_tools/src/android/gradle.dart';
...@@ -38,7 +39,6 @@ void main() { ...@@ -38,7 +39,6 @@ void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
FakePlatform android; FakePlatform android;
FileSystem fileSystem; FileSystem fileSystem;
FileSystemUtils fileSystemUtils;
Cache cache; Cache cache;
AndroidGradleBuilder builder; AndroidGradleBuilder builder;
...@@ -46,7 +46,6 @@ void main() { ...@@ -46,7 +46,6 @@ void main() {
logger = BufferLogger.test(); logger = BufferLogger.test();
testUsage = TestUsage(); testUsage = TestUsage();
fileSystem = MemoryFileSystem.test(); fileSystem = MemoryFileSystem.test();
fileSystemUtils = MockFileSystemUtils();
mockAndroidSdk = MockAndroidSdk(); mockAndroidSdk = MockAndroidSdk();
mockAndroidStudio = MockAndroidStudio(); mockAndroidStudio = MockAndroidStudio();
mockArtifacts = MockLocalEngineArtifacts(); mockArtifacts = MockLocalEngineArtifacts();
...@@ -1284,8 +1283,6 @@ void main() { ...@@ -1284,8 +1283,6 @@ void main() {
fileSystem.directory('build/outputs/repo').createSync(recursive: true); fileSystem.directory('build/outputs/repo').createSync(recursive: true);
when(fileSystemUtils.copyDirectorySync(any, any)).thenReturn(null);
await builder.buildGradleAar( await builder.buildGradleAar(
androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null, treeShakeIcons: false)), androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null, treeShakeIcons: false)),
project: FlutterProject.current(), project: FlutterProject.current(),
...@@ -1308,15 +1305,11 @@ void main() { ...@@ -1308,15 +1305,11 @@ void main() {
expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release')); expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
expect(actualGradlewCall, contains('-PbuildNumber=2.0')); expect(actualGradlewCall, contains('-PbuildNumber=2.0'));
// Verify the local engine repo is copied into the generated Maven repo. expect(fileSystem.link(
final List<dynamic> copyDirectoryArguments = verify( 'build/outputs/repo/io/flutter/flutter_embedding_release/'
fileSystemUtils.copyDirectorySync(captureAny, captureAny) '1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b/'
).captured; 'flutter_embedding_release-1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b.pom'
), exists);
expect(copyDirectoryArguments.length, 2);
expect((copyDirectoryArguments.first as Directory).path, '/.tmp_rand0/flutter_tool_local_engine_repo.rand0');
expect((copyDirectoryArguments.last as Directory).path, 'build/outputs/repo');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk, AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio, AndroidStudio: () => mockAndroidStudio,
...@@ -1324,7 +1317,6 @@ void main() { ...@@ -1324,7 +1317,6 @@ void main() {
Cache: () => cache, Cache: () => cache,
Platform: () => android, Platform: () => android,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FileSystemUtils: () => fileSystemUtils,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
...@@ -1387,8 +1379,6 @@ void main() { ...@@ -1387,8 +1379,6 @@ void main() {
fileSystem.directory('build/outputs/repo').createSync(recursive: true); fileSystem.directory('build/outputs/repo').createSync(recursive: true);
when(fileSystemUtils.copyDirectorySync(any, any)).thenReturn(null);
await builder.buildGradleAar( await builder.buildGradleAar(
androidBuildInfo: const AndroidBuildInfo( androidBuildInfo: const AndroidBuildInfo(
BuildInfo(BuildMode.release, null, treeShakeIcons: false)), BuildInfo(BuildMode.release, null, treeShakeIcons: false)),
...@@ -1412,15 +1402,11 @@ void main() { ...@@ -1412,15 +1402,11 @@ void main() {
expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release')); expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
expect(actualGradlewCall, contains('-PbuildNumber=2.0')); expect(actualGradlewCall, contains('-PbuildNumber=2.0'));
// Verify the local engine repo is copied into the generated Maven repo. expect(fileSystem.link(
final List<dynamic> copyDirectoryArguments = verify( 'build/outputs/repo/io/flutter/flutter_embedding_release/'
fileSystemUtils.copyDirectorySync(captureAny, captureAny) '1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b/'
).captured; 'flutter_embedding_release-1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b.pom'
), exists);
expect(copyDirectoryArguments.length, 2);
expect((copyDirectoryArguments.first as Directory).path, '/.tmp_rand0/flutter_tool_local_engine_repo.rand0');
expect((copyDirectoryArguments.last as Directory).path, 'build/outputs/repo');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk, AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio, AndroidStudio: () => mockAndroidStudio,
...@@ -1428,7 +1414,6 @@ void main() { ...@@ -1428,7 +1414,6 @@ void main() {
Cache: () => cache, Cache: () => cache,
Platform: () => android, Platform: () => android,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FileSystemUtils: () => fileSystemUtils,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
...@@ -1491,8 +1476,6 @@ void main() { ...@@ -1491,8 +1476,6 @@ void main() {
fileSystem.directory('build/outputs/repo').createSync(recursive: true); fileSystem.directory('build/outputs/repo').createSync(recursive: true);
when(fileSystemUtils.copyDirectorySync(any, any)).thenReturn(null);
await builder.buildGradleAar( await builder.buildGradleAar(
androidBuildInfo: const AndroidBuildInfo( androidBuildInfo: const AndroidBuildInfo(
BuildInfo(BuildMode.release, null, treeShakeIcons: false)), BuildInfo(BuildMode.release, null, treeShakeIcons: false)),
...@@ -1516,15 +1499,11 @@ void main() { ...@@ -1516,15 +1499,11 @@ void main() {
expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release')); expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
expect(actualGradlewCall, contains('-PbuildNumber=2.0')); expect(actualGradlewCall, contains('-PbuildNumber=2.0'));
// Verify the local engine repo is copied into the generated Maven repo. expect(fileSystem.link(
final List<dynamic> copyDirectoryArguments = verify( 'build/outputs/repo/io/flutter/flutter_embedding_release/'
fileSystemUtils.copyDirectorySync(captureAny, captureAny) '1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b/'
).captured; 'flutter_embedding_release-1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b.pom'
), exists);
expect(copyDirectoryArguments.length, 2);
expect((copyDirectoryArguments.first as Directory).path, '/.tmp_rand0/flutter_tool_local_engine_repo.rand0');
expect((copyDirectoryArguments.last as Directory).path, 'build/outputs/repo');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk, AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio, AndroidStudio: () => mockAndroidStudio,
...@@ -1532,12 +1511,10 @@ void main() { ...@@ -1532,12 +1511,10 @@ void main() {
Cache: () => cache, Cache: () => cache,
Platform: () => android, Platform: () => android,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FileSystemUtils: () => fileSystemUtils,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
testUsingContext( testUsingContext('build aar uses selected local engine,the engine abi is x64', () async {
'build aar uses selected local engine,the engine abi is x64', () async {
when(mockArtifacts.getArtifactPath( when(mockArtifacts.getArtifactPath(
Artifact.flutterFramework, Artifact.flutterFramework,
platform: anyNamed('platform'), platform: anyNamed('platform'),
...@@ -1595,8 +1572,6 @@ void main() { ...@@ -1595,8 +1572,6 @@ void main() {
fileSystem.directory('build/outputs/repo').createSync(recursive: true); fileSystem.directory('build/outputs/repo').createSync(recursive: true);
when(fileSystemUtils.copyDirectorySync(any, any)).thenReturn(null);
await builder.buildGradleAar( await builder.buildGradleAar(
androidBuildInfo: const AndroidBuildInfo( androidBuildInfo: const AndroidBuildInfo(
BuildInfo(BuildMode.release, null, treeShakeIcons: false)), BuildInfo(BuildMode.release, null, treeShakeIcons: false)),
...@@ -1620,15 +1595,11 @@ void main() { ...@@ -1620,15 +1595,11 @@ void main() {
expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release')); expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
expect(actualGradlewCall, contains('-PbuildNumber=2.0')); expect(actualGradlewCall, contains('-PbuildNumber=2.0'));
// Verify the local engine repo is copied into the generated Maven repo. expect(fileSystem.link(
final List<dynamic> copyDirectoryArguments = verify( 'build/outputs/repo/io/flutter/flutter_embedding_release/'
fileSystemUtils.copyDirectorySync(captureAny, captureAny) '1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b/'
).captured; 'flutter_embedding_release-1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b.pom'
), exists);
expect(copyDirectoryArguments.length, 2);
expect((copyDirectoryArguments.first as Directory).path, '/.tmp_rand0/flutter_tool_local_engine_repo.rand0');
expect((copyDirectoryArguments.last as Directory).path, 'build/outputs/repo');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk, AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio, AndroidStudio: () => mockAndroidStudio,
...@@ -1636,7 +1607,6 @@ void main() { ...@@ -1636,7 +1607,6 @@ void main() {
Cache: () => cache, Cache: () => cache,
Platform: () => android, Platform: () => android,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
FileSystemUtils: () => fileSystemUtils,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
}); });
...@@ -1653,5 +1623,4 @@ FakePlatform fakePlatform(String name) { ...@@ -1653,5 +1623,4 @@ FakePlatform fakePlatform(String name) {
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockAndroidSdk extends Mock implements AndroidSdk {} class MockAndroidSdk extends Mock implements AndroidSdk {}
class MockAndroidStudio extends Mock implements AndroidStudio {} class MockAndroidStudio extends Mock implements AndroidStudio {}
class MockFileSystemUtils extends Mock implements FileSystemUtils {}
class MockLocalEngineArtifacts extends Mock implements LocalEngineArtifacts {} class MockLocalEngineArtifacts extends Mock implements LocalEngineArtifacts {}
...@@ -77,11 +77,7 @@ void main() { ...@@ -77,11 +77,7 @@ void main() {
const String targetPath = '/some/non-existent/target'; const String targetPath = '/some/non-existent/target';
final Directory targetDirectory = targetMemoryFs.directory(targetPath); final Directory targetDirectory = targetMemoryFs.directory(targetPath);
final FileSystemUtils fsUtils = FileSystemUtils( copyDirectory(sourceDirectory, targetDirectory);
fileSystem: sourceMemoryFs,
platform: FakePlatform(),
);
fsUtils.copyDirectorySync(sourceDirectory, targetDirectory);
expect(targetDirectory.existsSync(), true); expect(targetDirectory.existsSync(), true);
targetMemoryFs.currentDirectory = targetPath; targetMemoryFs.currentDirectory = targetPath;
...@@ -97,10 +93,6 @@ void main() { ...@@ -97,10 +93,6 @@ void main() {
testWithoutContext('Skip files if shouldCopyFile returns false', () { testWithoutContext('Skip files if shouldCopyFile returns false', () {
final MemoryFileSystem fileSystem = MemoryFileSystem.test(); final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final FileSystemUtils fsUtils = FileSystemUtils(
fileSystem: fileSystem,
platform: FakePlatform(),
);
final Directory origin = fileSystem.directory('/origin'); final Directory origin = fileSystem.directory('/origin');
origin.createSync(); origin.createSync();
fileSystem.file(fileSystem.path.join('origin', 'a.txt')).writeAsStringSync('irrelevant'); fileSystem.file(fileSystem.path.join('origin', 'a.txt')).writeAsStringSync('irrelevant');
...@@ -109,7 +101,7 @@ void main() { ...@@ -109,7 +101,7 @@ void main() {
fileSystem.file(fileSystem.path.join('origin', 'nested', 'b.txt')).writeAsStringSync('irrelevant'); fileSystem.file(fileSystem.path.join('origin', 'nested', 'b.txt')).writeAsStringSync('irrelevant');
final Directory destination = fileSystem.directory('/destination'); final Directory destination = fileSystem.directory('/destination');
fsUtils.copyDirectorySync(origin, destination, shouldCopyFile: (File origin, File dest) { copyDirectory(origin, destination, shouldCopyFile: (File origin, File dest) {
return origin.basename == 'b.txt'; return origin.basename == 'b.txt';
}); });
......
...@@ -104,19 +104,17 @@ void main() { ...@@ -104,19 +104,17 @@ void main() {
}); });
testWithoutContext('does not crash if saving profile information fails due to a file system exception.', () async { testWithoutContext('does not crash if saving profile information fails due to a file system exception.', () async {
final MockFileSystemUtils fileSystemUtils = MockFileSystemUtils();
final BufferLogger logger = BufferLogger.test(); final BufferLogger logger = BufferLogger.test();
final ErrorThrowingFileSystem errorFileSystem = ErrorThrowingFileSystem(fileSystem);
errorFileSystem.addErrorEntity(FakeDirectory('/.tmp_rand0/flutter_tools_chrome_device.rand0/Default', errorFileSystem));
chromeLauncher = ChromiumLauncher( chromeLauncher = ChromiumLauncher(
fileSystem: fileSystem, fileSystem: errorFileSystem,
platform: platform, platform: platform,
processManager: processManager, processManager: processManager,
operatingSystemUtils: operatingSystemUtils, operatingSystemUtils: operatingSystemUtils,
browserFinder: findChromeExecutable, browserFinder: findChromeExecutable,
logger: logger, logger: logger,
fileSystemUtils: fileSystemUtils,
); );
when(fileSystemUtils.copyDirectorySync(any, any))
.thenThrow(const FileSystemException());
processManager.addCommand(const FakeCommand( processManager.addCommand(const FakeCommand(
command: <String>[ command: <String>[
'example_chrome', 'example_chrome',
...@@ -143,19 +141,19 @@ void main() { ...@@ -143,19 +141,19 @@ void main() {
}); });
testWithoutContext('does not crash if restoring profile information fails due to a file system exception.', () async { testWithoutContext('does not crash if restoring profile information fails due to a file system exception.', () async {
final MockFileSystemUtils fileSystemUtils = MockFileSystemUtils();
final BufferLogger logger = BufferLogger.test(); final BufferLogger logger = BufferLogger.test();
final ErrorThrowingFileSystem errorFileSystem = ErrorThrowingFileSystem(fileSystem);
errorFileSystem.addErrorEntity(FakeDirectory('/Default', errorFileSystem));
chromeLauncher = ChromiumLauncher( chromeLauncher = ChromiumLauncher(
fileSystem: fileSystem, fileSystem: errorFileSystem,
platform: platform, platform: platform,
processManager: processManager, processManager: processManager,
operatingSystemUtils: operatingSystemUtils, operatingSystemUtils: operatingSystemUtils,
browserFinder: findChromeExecutable, browserFinder: findChromeExecutable,
logger: logger, logger: logger,
fileSystemUtils: fileSystemUtils,
); );
when(fileSystemUtils.copyDirectorySync(any, any))
.thenThrow(const FileSystemException());
processManager.addCommand(const FakeCommand( processManager.addCommand(const FakeCommand(
command: <String>[ command: <String>[
'example_chrome', 'example_chrome',
...@@ -171,7 +169,7 @@ void main() { ...@@ -171,7 +169,7 @@ void main() {
final Chromium chrome = await chromeLauncher.launch( final Chromium chrome = await chromeLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
cacheDir: fileSystem.currentDirectory, cacheDir: errorFileSystem.currentDirectory,
); );
// Create cache dir that the Chrome launcher will atttempt to persist. // Create cache dir that the Chrome launcher will atttempt to persist.
...@@ -233,7 +231,6 @@ void main() { ...@@ -233,7 +231,6 @@ void main() {
testWithoutContext('can seed chrome temp directory with existing session data', () async { testWithoutContext('can seed chrome temp directory with existing session data', () async {
final Completer<void> exitCompleter = Completer<void>.sync(); final Completer<void> exitCompleter = Completer<void>.sync();
final Directory dataDir = fileSystem.directory('chrome-stuff'); final Directory dataDir = fileSystem.directory('chrome-stuff');
final File preferencesFile = dataDir final File preferencesFile = dataDir
.childDirectory('Default') .childDirectory('Default')
.childFile('preferences'); .childFile('preferences');
...@@ -265,7 +262,7 @@ void main() { ...@@ -265,7 +262,7 @@ void main() {
); );
exitCompleter.complete(); exitCompleter.complete();
await Future<void>.delayed(const Duration(microseconds: 1)); await Future<void>.delayed(const Duration(milliseconds: 1));
// writes non-crash back to dart_tool // writes non-crash back to dart_tool
expect(preferencesFile.readAsStringSync(), '"exit_type":"Normal"'); expect(preferencesFile.readAsStringSync(), '"exit_type":"Normal"');
...@@ -366,3 +363,60 @@ Future<Chromium> _testLaunchChrome(String userDataDir, FakeProcessManager proces ...@@ -366,3 +363,60 @@ Future<Chromium> _testLaunchChrome(String userDataDir, FakeProcessManager proces
skipCheck: true, skipCheck: true,
); );
} }
class FakeDirectory extends Fake implements Directory {
FakeDirectory(this.path, this.fileSystem);
@override
final FileSystem fileSystem;
@override
final String path;
@override
void createSync({bool recursive = false}) {}
@override
bool existsSync() {
return true;
}
@override
List<FileSystemEntity> listSync({bool recursive = false, bool followLinks = true}) {
throw FileSystemException(path, '');
}
}
class ErrorThrowingFileSystem extends ForwardingFileSystem {
ErrorThrowingFileSystem(FileSystem delegate) : super(delegate);
final Map<String, FileSystemEntity> errorEntities = <String, FileSystemEntity>{};
void addErrorEntity(FileSystemEntity entity) {
errorEntities[entity.path] = entity;
}
@override
Directory directory(dynamic path) {
if (errorEntities.containsKey(path)) {
return errorEntities[path] as Directory;
}
return delegate.directory(path);
}
@override
File file(dynamic path) {
if (errorEntities.containsKey(path)) {
return errorEntities[path] as File;
}
return delegate.file(path);
}
@override
Link link(dynamic path) {
if (errorEntities.containsKey(path)) {
return errorEntities[path] as Link;
}
return delegate.link(path);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment