| // 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 'package:file/memory.dart'; |
| import 'package:flutter_tools/src/android/gradle_utils.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/base/os.dart'; |
| import 'package:flutter_tools/src/cache.dart'; |
| import 'package:flutter_tools/src/project.dart'; |
| import 'package:flutter_tools/src/globals.dart' as globals; |
| import 'package:mockito/mockito.dart'; |
| import 'package:process/process.dart'; |
| |
| import '../../src/common.dart'; |
| import '../../src/context.dart'; |
| |
| void main() { |
| group('injectGradleWrapperIfNeeded', () { |
| MemoryFileSystem memoryFileSystem; |
| Directory tempDir; |
| Directory gradleWrapperDirectory; |
| |
| setUp(() { |
| memoryFileSystem = MemoryFileSystem.test(); |
| tempDir = memoryFileSystem.systemTempDirectory.createTempSync('flutter_artifacts_test.'); |
| gradleWrapperDirectory = memoryFileSystem.directory( |
| memoryFileSystem.path.join(tempDir.path, 'bin', 'cache', 'artifacts', 'gradle_wrapper')); |
| gradleWrapperDirectory.createSync(recursive: true); |
| gradleWrapperDirectory |
| .childFile('gradlew') |
| .writeAsStringSync('irrelevant'); |
| gradleWrapperDirectory |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .createSync(recursive: true); |
| gradleWrapperDirectory |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.jar') |
| .writeAsStringSync('irrelevant'); |
| }); |
| |
| testUsingContext('injects the wrapper when all files are missing', () { |
| final Directory sampleAppAndroid = globals.fs.directory('/sample-app/android'); |
| sampleAppAndroid.createSync(recursive: true); |
| |
| gradleUtils.injectGradleWrapperIfNeeded(sampleAppAndroid); |
| |
| expect(sampleAppAndroid.childFile('gradlew').existsSync(), isTrue); |
| |
| expect(sampleAppAndroid |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.jar') |
| .existsSync(), isTrue); |
| |
| expect(sampleAppAndroid |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties') |
| .existsSync(), isTrue); |
| |
| expect(sampleAppAndroid |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties') |
| .readAsStringSync(), |
| 'distributionBase=GRADLE_USER_HOME\n' |
| 'distributionPath=wrapper/dists\n' |
| 'zipStoreBase=GRADLE_USER_HOME\n' |
| 'zipStorePath=wrapper/dists\n' |
| 'distributionUrl=https\\://services.gradle.org/distributions/gradle-5.6.2-all.zip\n'); |
| }, overrides: <Type, Generator>{ |
| Cache: () => Cache(rootOverride: tempDir), |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| }); |
| |
| testUsingContext('injects the wrapper when some files are missing', () { |
| final Directory sampleAppAndroid = globals.fs.directory('/sample-app/android'); |
| sampleAppAndroid.createSync(recursive: true); |
| |
| // There's an existing gradlew |
| sampleAppAndroid.childFile('gradlew').writeAsStringSync('existing gradlew'); |
| |
| gradleUtils.injectGradleWrapperIfNeeded(sampleAppAndroid); |
| |
| expect(sampleAppAndroid.childFile('gradlew').existsSync(), isTrue); |
| expect(sampleAppAndroid.childFile('gradlew').readAsStringSync(), |
| equals('existing gradlew')); |
| |
| expect(sampleAppAndroid |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.jar') |
| .existsSync(), isTrue); |
| |
| expect(sampleAppAndroid |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties') |
| .existsSync(), isTrue); |
| |
| expect(sampleAppAndroid |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties') |
| .readAsStringSync(), |
| 'distributionBase=GRADLE_USER_HOME\n' |
| 'distributionPath=wrapper/dists\n' |
| 'zipStoreBase=GRADLE_USER_HOME\n' |
| 'zipStorePath=wrapper/dists\n' |
| 'distributionUrl=https\\://services.gradle.org/distributions/gradle-5.6.2-all.zip\n'); |
| }, overrides: <Type, Generator>{ |
| Cache: () => Cache(rootOverride: tempDir), |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| }); |
| }); |
| |
| |
| group('migrateToR8', () { |
| MemoryFileSystem memoryFileSystem; |
| |
| setUp(() { |
| memoryFileSystem = MemoryFileSystem.test(); |
| }); |
| |
| testUsingContext("throws ToolExit if gradle.properties doesn't exist", () { |
| final Directory sampleAppAndroid = globals.fs.directory('/sample-app/android'); |
| sampleAppAndroid.createSync(recursive: true); |
| |
| expect(() { |
| gradleUtils.migrateToR8(sampleAppAndroid); |
| }, throwsToolExit(message: 'Expected file ${sampleAppAndroid.path}')); |
| |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| }); |
| |
| testUsingContext('throws ToolExit if it cannot write gradle.properties', () { |
| final MockDirectory sampleAppAndroid = MockDirectory(); |
| final MockFile gradleProperties = MockFile(); |
| |
| when(gradleProperties.path).thenReturn('foo/gradle.properties'); |
| when(gradleProperties.existsSync()).thenReturn(true); |
| when(gradleProperties.readAsStringSync()).thenReturn(''); |
| when(gradleProperties.writeAsStringSync('android.enableR8=true\n', mode: FileMode.append)) |
| .thenThrow(const FileSystemException()); |
| |
| when(sampleAppAndroid.childFile('gradle.properties')) |
| .thenReturn(gradleProperties); |
| |
| expect(() { |
| gradleUtils.migrateToR8(sampleAppAndroid); |
| }, |
| throwsToolExit(message: |
| 'The tool failed to add `android.enableR8=true` to foo/gradle.properties. ' |
| 'Please update the file manually and try this command again.')); |
| }); |
| |
| testUsingContext('does not update gradle.properties if it already uses R8', () { |
| final Directory sampleAppAndroid = globals.fs.directory('/sample-app/android'); |
| sampleAppAndroid.createSync(recursive: true); |
| sampleAppAndroid.childFile('gradle.properties') |
| .writeAsStringSync('android.enableR8=true'); |
| |
| gradleUtils.migrateToR8(sampleAppAndroid); |
| |
| expect(testLogger.traceText, |
| contains('gradle.properties already sets `android.enableR8`')); |
| expect(sampleAppAndroid.childFile('gradle.properties').readAsStringSync(), |
| equals('android.enableR8=true')); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| }); |
| |
| testUsingContext('sets android.enableR8=true', () { |
| final Directory sampleAppAndroid = globals.fs.directory('/sample-app/android'); |
| sampleAppAndroid.createSync(recursive: true); |
| sampleAppAndroid.childFile('gradle.properties') |
| .writeAsStringSync('org.gradle.jvmargs=-Xmx1536M\n'); |
| |
| gradleUtils.migrateToR8(sampleAppAndroid); |
| |
| expect(testLogger.traceText, contains('set `android.enableR8=true` in gradle.properties')); |
| expect( |
| sampleAppAndroid.childFile('gradle.properties').readAsStringSync(), |
| equals( |
| 'org.gradle.jvmargs=-Xmx1536M\n' |
| 'android.enableR8=true\n' |
| ), |
| ); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| }); |
| |
| testUsingContext('appends android.enableR8=true to the new line', () { |
| final Directory sampleAppAndroid = globals.fs.directory('/sample-app/android'); |
| sampleAppAndroid.createSync(recursive: true); |
| sampleAppAndroid.childFile('gradle.properties') |
| .writeAsStringSync('org.gradle.jvmargs=-Xmx1536M'); |
| |
| gradleUtils.migrateToR8(sampleAppAndroid); |
| |
| expect(testLogger.traceText, contains('set `android.enableR8=true` in gradle.properties')); |
| expect( |
| sampleAppAndroid.childFile('gradle.properties').readAsStringSync(), |
| equals( |
| 'org.gradle.jvmargs=-Xmx1536M\n' |
| 'android.enableR8=true\n' |
| ), |
| ); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any() |
| }); |
| }); |
| |
| group('GradleUtils.getExecutable', () { |
| final String gradlewFilename = globals.platform.isWindows ? 'gradlew.bat' : 'gradlew'; |
| |
| MemoryFileSystem memoryFileSystem; |
| OperatingSystemUtils operatingSystemUtils; |
| MockGradleUtils gradleUtils; |
| |
| setUp(() { |
| memoryFileSystem = MemoryFileSystem.test(); |
| operatingSystemUtils = MockOperatingSystemUtils(); |
| gradleUtils = MockGradleUtils(); |
| }); |
| |
| testUsingContext('returns the gradlew path', () { |
| final Directory androidDirectory = globals.fs.directory('/android')..createSync(); |
| androidDirectory.childFile('gradlew').createSync(); |
| androidDirectory.childFile('gradlew.bat').createSync(); |
| androidDirectory.childFile('gradle.properties').createSync(); |
| |
| when(gradleUtils.injectGradleWrapperIfNeeded(any)).thenReturn(null); |
| when(gradleUtils.migrateToR8(any)).thenReturn(null); |
| |
| expect( |
| GradleUtils().getExecutable(FlutterProject.current()), |
| androidDirectory.childFile(gradlewFilename).path, |
| ); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| OperatingSystemUtils: () => operatingSystemUtils, |
| GradleUtils: () => gradleUtils, |
| }); |
| |
| testUsingContext('gives execute permission to gradle', () { |
| final FlutterProject flutterProject = MockFlutterProject(); |
| final AndroidProject androidProject = MockAndroidProject(); |
| when(flutterProject.android).thenReturn(androidProject); |
| |
| final FileStat gradleStat = MockFileStat(); |
| when(gradleStat.mode).thenReturn(444); |
| |
| final File gradlew = MockFile(); |
| when(gradlew.path).thenReturn('gradlew'); |
| when(gradlew.absolute).thenReturn(gradlew); |
| when(gradlew.statSync()).thenReturn(gradleStat); |
| when(gradlew.existsSync()).thenReturn(true); |
| |
| final Directory androidDirectory = MockDirectory(); |
| when(androidDirectory.childFile(gradlewFilename)).thenReturn(gradlew); |
| when(androidProject.hostAppGradleRoot).thenReturn(androidDirectory); |
| |
| when(gradleUtils.injectGradleWrapperIfNeeded(any)).thenReturn(null); |
| when(gradleUtils.migrateToR8(any)).thenReturn(null); |
| |
| GradleUtils().getExecutable(flutterProject); |
| |
| verify(operatingSystemUtils.makeExecutable(gradlew)).called(1); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| OperatingSystemUtils: () => operatingSystemUtils, |
| GradleUtils: () => gradleUtils, |
| }); |
| |
| testUsingContext('gives execute permission to gradle even when not all permission flags are set', () { |
| final FlutterProject flutterProject = MockFlutterProject(); |
| final AndroidProject androidProject = MockAndroidProject(); |
| when(flutterProject.android).thenReturn(androidProject); |
| |
| final FileStat gradleStat = MockFileStat(); |
| when(gradleStat.mode).thenReturn(400); |
| |
| final File gradlew = MockFile(); |
| when(gradlew.path).thenReturn('gradlew'); |
| when(gradlew.absolute).thenReturn(gradlew); |
| when(gradlew.statSync()).thenReturn(gradleStat); |
| when(gradlew.existsSync()).thenReturn(true); |
| |
| final Directory androidDirectory = MockDirectory(); |
| when(androidDirectory.childFile(gradlewFilename)).thenReturn(gradlew); |
| when(androidProject.hostAppGradleRoot).thenReturn(androidDirectory); |
| |
| when(gradleUtils.injectGradleWrapperIfNeeded(any)).thenReturn(null); |
| when(gradleUtils.migrateToR8(any)).thenReturn(null); |
| |
| GradleUtils().getExecutable(flutterProject); |
| |
| verify(operatingSystemUtils.makeExecutable(gradlew)).called(1); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| OperatingSystemUtils: () => operatingSystemUtils, |
| GradleUtils: () => gradleUtils, |
| }); |
| |
| testUsingContext("doesn't give execute permission to gradle if not needed", () { |
| final FlutterProject flutterProject = MockFlutterProject(); |
| final AndroidProject androidProject = MockAndroidProject(); |
| when(flutterProject.android).thenReturn(androidProject); |
| |
| final FileStat gradleStat = MockFileStat(); |
| when(gradleStat.mode).thenReturn(0x49 /* a+x */); |
| |
| final File gradlew = MockFile(); |
| when(gradlew.path).thenReturn('gradlew'); |
| when(gradlew.absolute).thenReturn(gradlew); |
| when(gradlew.statSync()).thenReturn(gradleStat); |
| when(gradlew.existsSync()).thenReturn(true); |
| |
| final Directory androidDirectory = MockDirectory(); |
| when(androidDirectory.childFile(gradlewFilename)).thenReturn(gradlew); |
| when(androidProject.hostAppGradleRoot).thenReturn(androidDirectory); |
| |
| when(gradleUtils.injectGradleWrapperIfNeeded(any)).thenReturn(null); |
| when(gradleUtils.migrateToR8(any)).thenReturn(null); |
| |
| GradleUtils().getExecutable(flutterProject); |
| |
| verifyNever(operatingSystemUtils.makeExecutable(gradlew)); |
| }, overrides: <Type, Generator>{ |
| FileSystem: () => memoryFileSystem, |
| ProcessManager: () => FakeProcessManager.any(), |
| OperatingSystemUtils: () => operatingSystemUtils, |
| GradleUtils: () => gradleUtils, |
| }); |
| }); |
| } |
| |
| class MockAndroidProject extends Mock implements AndroidProject {} |
| class MockDirectory extends Mock implements Directory {} |
| class MockFile extends Mock implements File {} |
| class MockFileStat extends Mock implements FileStat {} |
| class MockFlutterProject extends Mock implements FlutterProject {} |
| class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} |
| class MockGradleUtils extends Mock implements GradleUtils {} |