blob: 17cb6512da09a67dce43b032155d3eb5f052f821 [file] [log] [blame]
Ian Hickson449f4a62019-11-27 15:04:02 -08001// Copyright 2014 The Flutter Authors. All rights reserved.
Jason Simmonscfa0a2d2017-06-19 09:37:39 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ralph Bergmannc65e9d12018-05-30 16:51:25 +02005import 'dart:async';
6
Ralph Bergmann779eea62018-07-09 19:36:17 +02007import 'package:file/memory.dart';
xster46406902019-08-27 23:46:33 -07008import 'package:flutter_tools/src/android/android_sdk.dart';
9import 'package:flutter_tools/src/android/android_studio.dart';
Jason Simmonscfa0a2d2017-06-19 09:37:39 -070010import 'package:flutter_tools/src/android/gradle.dart';
Emmanuel Garcia175b3722019-10-31 13:19:15 -070011import 'package:flutter_tools/src/android/gradle_utils.dart';
12import 'package:flutter_tools/src/android/gradle_errors.dart';
Ralph Bergmann779eea62018-07-09 19:36:17 +020013import 'package:flutter_tools/src/artifacts.dart';
Emmanuel Garcia175b3722019-10-31 13:19:15 -070014import 'package:flutter_tools/src/base/context.dart';
Emmanuel Garcia242a4222019-07-23 09:27:42 -070015import 'package:flutter_tools/src/base/common.dart';
Mikkel Nygaard Ravn9496e6d2017-08-23 10:55:35 +020016import 'package:flutter_tools/src/base/file_system.dart';
Emmanuel Garcia2c857b92019-09-13 19:06:40 -070017import 'package:flutter_tools/src/base/io.dart';
Emmanuel Garcia175b3722019-10-31 13:19:15 -070018import 'package:flutter_tools/src/base/logger.dart';
Emmanuel Garcia175b3722019-10-31 13:19:15 -070019import 'package:flutter_tools/src/base/terminal.dart';
Mikkel Nygaard Ravn9496e6d2017-08-23 10:55:35 +020020import 'package:flutter_tools/src/build_info.dart';
Mikkel Nygaard Ravnb2800742018-08-02 14:12:25 +020021import 'package:flutter_tools/src/cache.dart';
Ralph Bergmann779eea62018-07-09 19:36:17 +020022import 'package:flutter_tools/src/ios/xcodeproj.dart';
Sigurd Meldgaard2d3a5c72018-07-20 08:00:30 +020023import 'package:flutter_tools/src/project.dart';
Emmanuel Garcia175b3722019-10-31 13:19:15 -070024import 'package:flutter_tools/src/reporting/reporting.dart';
Jonah Williamsee7a37f2020-01-06 11:04:20 -080025import 'package:flutter_tools/src/globals.dart' as globals;
Ralph Bergmann779eea62018-07-09 19:36:17 +020026import 'package:mockito/mockito.dart';
27import 'package:platform/platform.dart';
28import 'package:process/process.dart';
Jason Simmonscfa0a2d2017-06-19 09:37:39 -070029
Ian Hicksond919e692019-07-13 11:51:44 -070030import '../../src/common.dart';
31import '../../src/context.dart';
Emmanuel Garcia175b3722019-10-31 13:19:15 -070032import '../../src/mocks.dart';
Ian Hicksond919e692019-07-13 11:51:44 -070033import '../../src/pubspec_schema.dart';
liyuqianae8586c2018-05-15 14:42:12 -070034
Jason Simmonscfa0a2d2017-06-19 09:37:39 -070035void main() {
Mikkel Nygaard Ravnb2800742018-08-02 14:12:25 +020036 Cache.flutterRoot = getFlutterRoot();
Emmanuel Garcia175b3722019-10-31 13:19:15 -070037
38 group('build artifacts', () {
39 test('getApkDirectory in app projects', () {
40 final FlutterProject project = MockFlutterProject();
41 final AndroidProject androidProject = MockAndroidProject();
42 when(project.android).thenReturn(androidProject);
43 when(project.isModule).thenReturn(false);
Jonah Williamsee7a37f2020-01-06 11:04:20 -080044 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -070045
46 expect(
47 getApkDirectory(project).path,
Jonah Williamsee7a37f2020-01-06 11:04:20 -080048 equals(globals.fs.path.join('foo', 'app', 'outputs', 'apk')),
Emmanuel Garcia175b3722019-10-31 13:19:15 -070049 );
liyuqianae8586c2018-05-15 14:42:12 -070050 });
51
Emmanuel Garcia175b3722019-10-31 13:19:15 -070052 test('getApkDirectory in module projects', () {
53 final FlutterProject project = MockFlutterProject();
54 final AndroidProject androidProject = MockAndroidProject();
55 when(project.android).thenReturn(androidProject);
56 when(project.isModule).thenReturn(true);
Jonah Williamsee7a37f2020-01-06 11:04:20 -080057 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo'));
Jonah Williams7851a752019-06-19 11:43:06 -070058
Emmanuel Garcia175b3722019-10-31 13:19:15 -070059 expect(
60 getApkDirectory(project).path,
Jonah Williamsee7a37f2020-01-06 11:04:20 -080061 equals(globals.fs.path.join('foo', 'host', 'outputs', 'apk')),
Emmanuel Garcia175b3722019-10-31 13:19:15 -070062 );
63 });
64
65 test('getBundleDirectory in app projects', () {
66 final FlutterProject project = MockFlutterProject();
67 final AndroidProject androidProject = MockAndroidProject();
68 when(project.android).thenReturn(androidProject);
69 when(project.isModule).thenReturn(false);
Jonah Williamsee7a37f2020-01-06 11:04:20 -080070 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -070071
72 expect(
73 getBundleDirectory(project).path,
Jonah Williamsee7a37f2020-01-06 11:04:20 -080074 equals(globals.fs.path.join('foo', 'app', 'outputs', 'bundle')),
Emmanuel Garcia175b3722019-10-31 13:19:15 -070075 );
76 });
77
78 test('getBundleDirectory in module projects', () {
79 final FlutterProject project = MockFlutterProject();
80 final AndroidProject androidProject = MockAndroidProject();
81 when(project.android).thenReturn(androidProject);
82 when(project.isModule).thenReturn(true);
Jonah Williamsee7a37f2020-01-06 11:04:20 -080083 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('foo'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -070084
85 expect(
86 getBundleDirectory(project).path,
Jonah Williamsee7a37f2020-01-06 11:04:20 -080087 equals(globals.fs.path.join('foo', 'host', 'outputs', 'bundle')),
Emmanuel Garcia175b3722019-10-31 13:19:15 -070088 );
89 });
90
91 test('getRepoDirectory', () {
92 expect(
Jonah Williamsee7a37f2020-01-06 11:04:20 -080093 getRepoDirectory(globals.fs.directory('foo')).path,
94 equals(globals.fs.path.join('foo','outputs', 'repo')),
Emmanuel Garcia175b3722019-10-31 13:19:15 -070095 );
96 });
97 });
98
99 group('gradle tasks', () {
100 test('assemble release', () {
101 expect(
102 getAssembleTaskFor(const BuildInfo(BuildMode.release, null)),
103 equals('assembleRelease'),
104 );
105 expect(
106 getAssembleTaskFor(const BuildInfo(BuildMode.release, 'flavorFoo')),
107 equals('assembleFlavorFooRelease'),
108 );
109 });
110
111 test('assemble debug', () {
112 expect(
113 getAssembleTaskFor(const BuildInfo(BuildMode.debug, null)),
114 equals('assembleDebug'),
115 );
116 expect(
117 getAssembleTaskFor(const BuildInfo(BuildMode.debug, 'flavorFoo')),
118 equals('assembleFlavorFooDebug'),
119 );
120 });
121
122 test('assemble profile', () {
123 expect(
124 getAssembleTaskFor(const BuildInfo(BuildMode.profile, null)),
125 equals('assembleProfile'),
126 );
127 expect(
128 getAssembleTaskFor(const BuildInfo(BuildMode.profile, 'flavorFoo')),
129 equals('assembleFlavorFooProfile'),
130 );
131 });
132 });
133
134 group('findBundleFile', () {
Emmanuel Garciadea35da2019-11-01 10:06:23 -0700135 final Usage mockUsage = MockUsage();
136
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700137 testUsingContext('Finds app bundle when flavor contains underscores in release mode', () {
138 final FlutterProject project = generateFakeAppBundle('foo_barRelease', 'app.aab');
139 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo_bar'));
140 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800141 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barRelease', 'app.aab'));
Jonah Williams7851a752019-06-19 11:43:06 -0700142 }, overrides: <Type, Generator>{
143 FileSystem: () => MemoryFileSystem(),
Jonah Williams4c677e42019-10-22 22:05:00 -0700144 ProcessManager: () => FakeProcessManager.any(),
Jonah Williams7851a752019-06-19 11:43:06 -0700145 });
146
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700147 testUsingContext('Finds app bundle when flavor doesn\'t contain underscores in release mode', () {
148 final FlutterProject project = generateFakeAppBundle('fooRelease', 'app.aab');
149 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo'));
150 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800151 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooRelease', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700152 }, overrides: <Type, Generator>{
153 FileSystem: () => MemoryFileSystem(),
154 ProcessManager: () => FakeProcessManager.any(),
155 });
156
157 testUsingContext('Finds app bundle when no flavor is used in release mode', () {
158 final FlutterProject project = generateFakeAppBundle('release', 'app.aab');
159 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, null));
160 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800161 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'release', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700162 }, overrides: <Type, Generator>{
163 FileSystem: () => MemoryFileSystem(),
164 ProcessManager: () => FakeProcessManager.any(),
165 });
166
167 testUsingContext('Finds app bundle when flavor contains underscores in debug mode', () {
168 final FlutterProject project = generateFakeAppBundle('foo_barDebug', 'app.aab');
169 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar'));
170 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800171 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barDebug', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700172 }, overrides: <Type, Generator>{
173 FileSystem: () => MemoryFileSystem(),
174 ProcessManager: () => FakeProcessManager.any(),
175 });
176
177 testUsingContext('Finds app bundle when flavor doesn\'t contain underscores in debug mode', () {
178 final FlutterProject project = generateFakeAppBundle('fooDebug', 'app.aab');
179 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo'));
180 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800181 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooDebug', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700182 }, overrides: <Type, Generator>{
183 FileSystem: () => MemoryFileSystem(),
184 ProcessManager: () => FakeProcessManager.any(),
185 });
186
187 testUsingContext('Finds app bundle when no flavor is used in debug mode', () {
188 final FlutterProject project = generateFakeAppBundle('debug', 'app.aab');
189 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, null));
190 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800191 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'debug', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700192 }, overrides: <Type, Generator>{
193 FileSystem: () => MemoryFileSystem(),
194 ProcessManager: () => FakeProcessManager.any(),
195 });
196
197 testUsingContext('Finds app bundle when flavor contains underscores in profile mode', () {
198 final FlutterProject project = generateFakeAppBundle('foo_barProfile', 'app.aab');
199 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo_bar'));
200 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800201 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barProfile', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700202 }, overrides: <Type, Generator>{
203 FileSystem: () => MemoryFileSystem(),
204 ProcessManager: () => FakeProcessManager.any(),
205 });
206
207 testUsingContext('Finds app bundle when flavor doesn\'t contain underscores in profile mode', () {
208 final FlutterProject project = generateFakeAppBundle('fooProfile', 'app.aab');
209 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo'));
210 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800211 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'fooProfile', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700212 }, overrides: <Type, Generator>{
213 FileSystem: () => MemoryFileSystem(),
214 ProcessManager: () => FakeProcessManager.any(),
215 });
216
217 testUsingContext('Finds app bundle when no flavor is used in profile mode', () {
218 final FlutterProject project = generateFakeAppBundle('profile', 'app.aab');
219 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, null));
220 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800221 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'profile', 'app.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700222 }, overrides: <Type, Generator>{
223 FileSystem: () => MemoryFileSystem(),
224 ProcessManager: () => FakeProcessManager.any(),
225 });
226
227 testUsingContext('Finds app bundle in release mode - Gradle 3.5', () {
228 final FlutterProject project = generateFakeAppBundle('release', 'app-release.aab');
229 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, null));
230 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800231 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'release', 'app-release.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700232 }, overrides: <Type, Generator>{
233 FileSystem: () => MemoryFileSystem(),
234 ProcessManager: () => FakeProcessManager.any(),
235 });
236
237 testUsingContext('Finds app bundle in profile mode - Gradle 3.5', () {
238 final FlutterProject project = generateFakeAppBundle('profile', 'app-profile.aab');
239 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, null));
240 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800241 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'profile', 'app-profile.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700242 }, overrides: <Type, Generator>{
243 FileSystem: () => MemoryFileSystem(),
244 ProcessManager: () => FakeProcessManager.any(),
245 });
246
247 testUsingContext('Finds app bundle in debug mode - Gradle 3.5', () {
248 final FlutterProject project = generateFakeAppBundle('debug', 'app-debug.aab');
249 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, null));
250 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800251 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'debug', 'app-debug.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700252 }, overrides: <Type, Generator>{
253 FileSystem: () => MemoryFileSystem(),
254 ProcessManager: () => FakeProcessManager.any(),
255 });
256
257 testUsingContext('Finds app bundle when flavor contains underscores in release mode - Gradle 3.5', () {
258 final FlutterProject project = generateFakeAppBundle('foo_barRelease', 'app-foo_bar-release.aab');
259 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.release, 'foo_bar'));
260 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800261 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barRelease', 'app-foo_bar-release.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700262 }, overrides: <Type, Generator>{
263 FileSystem: () => MemoryFileSystem(),
264 ProcessManager: () => FakeProcessManager.any(),
265 });
266
267 testUsingContext('Finds app bundle when flavor contains underscores in profile mode - Gradle 3.5', () {
268 final FlutterProject project = generateFakeAppBundle('foo_barProfile', 'app-foo_bar-profile.aab');
269 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.profile, 'foo_bar'));
270 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800271 expect(bundle.path, globals.fs.path.join('irrelevant', 'app', 'outputs', 'bundle', 'foo_barProfile', 'app-foo_bar-profile.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700272 }, overrides: <Type, Generator>{
273 FileSystem: () => MemoryFileSystem(),
274 ProcessManager: () => FakeProcessManager.any(),
275 });
276
277 testUsingContext('Finds app bundle when flavor contains underscores in debug mode - Gradle 3.5', () {
278 final FlutterProject project = generateFakeAppBundle('foo_barDebug', 'app-foo_bar-debug.aab');
279 final File bundle = findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar'));
280 expect(bundle, isNotNull);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800281 expect(bundle.path, globals.fs.path.join('irrelevant','app', 'outputs', 'bundle', 'foo_barDebug', 'app-foo_bar-debug.aab'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700282 }, overrides: <Type, Generator>{
283 FileSystem: () => MemoryFileSystem(),
284 ProcessManager: () => FakeProcessManager.any(),
285 });
Emmanuel Garciadea35da2019-11-01 10:06:23 -0700286
287 testUsingContext('aab not found', () {
288 final FlutterProject project = FlutterProject.current();
289 expect(
290 () {
291 findBundleFile(project, const BuildInfo(BuildMode.debug, 'foo_bar'));
292 },
293 throwsToolExit(
294 message:
295 'Gradle build failed to produce an .aab file. It\'s likely that this file '
296 'was generated under ${project.android.buildDirectory.path}, but the tool couldn\'t find it.'
297 )
298 );
299 verify(
300 mockUsage.sendEvent(
301 any,
302 any,
303 label: 'gradle-expected-file-not-found',
304 parameters: const <String, String> {
305 'cd37': 'androidGradlePluginVersion: 5.6.2, fileExtension: .aab',
306 },
307 ),
308 ).called(1);
309 }, overrides: <Type, Generator>{
310 FileSystem: () => MemoryFileSystem(),
311 ProcessManager: () => FakeProcessManager.any(),
312 Usage: () => mockUsage,
313 });
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700314 });
315
316 group('findApkFiles', () {
Emmanuel Garciadea35da2019-11-01 10:06:23 -0700317 final Usage mockUsage = MockUsage();
318
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700319 testUsingContext('Finds APK without flavor in release', () {
320 final FlutterProject project = MockFlutterProject();
321 final AndroidProject androidProject = MockAndroidProject();
322
323 when(project.android).thenReturn(androidProject);
324 when(project.isModule).thenReturn(false);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800325 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700326
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800327 final Directory apkDirectory = globals.fs.directory(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700328 apkDirectory.createSync(recursive: true);
329 apkDirectory.childFile('app-release.apk').createSync();
330
331 final Iterable<File> apks = findApkFiles(
332 project,
333 const AndroidBuildInfo(BuildInfo(BuildMode.release, '')),
334 );
335 expect(apks.isNotEmpty, isTrue);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800336 expect(apks.first.path, equals(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release', 'app-release.apk')));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700337 }, overrides: <Type, Generator>{
338 FileSystem: () => MemoryFileSystem(),
339 ProcessManager: () => FakeProcessManager.any(),
340 });
341
342 testUsingContext('Finds APK with flavor in release mode', () {
343 final FlutterProject project = MockFlutterProject();
344 final AndroidProject androidProject = MockAndroidProject();
345
346 when(project.android).thenReturn(androidProject);
347 when(project.isModule).thenReturn(false);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800348 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700349
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800350 final Directory apkDirectory = globals.fs.directory(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700351 apkDirectory.createSync(recursive: true);
352 apkDirectory.childFile('app-flavor1-release.apk').createSync();
353
354 final Iterable<File> apks = findApkFiles(
355 project,
356 const AndroidBuildInfo(BuildInfo(BuildMode.release, 'flavor1')),
357 );
358 expect(apks.isNotEmpty, isTrue);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800359 expect(apks.first.path, equals(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'release', 'app-flavor1-release.apk')));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700360 }, overrides: <Type, Generator>{
361 FileSystem: () => MemoryFileSystem(),
362 ProcessManager: () => FakeProcessManager.any(),
363 });
364
365 testUsingContext('Finds APK with flavor in release mode - AGP v3', () {
366 final FlutterProject project = MockFlutterProject();
367 final AndroidProject androidProject = MockAndroidProject();
368
369 when(project.android).thenReturn(androidProject);
370 when(project.isModule).thenReturn(false);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800371 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700372
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800373 final Directory apkDirectory = globals.fs.directory(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'flavor1', 'release'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700374 apkDirectory.createSync(recursive: true);
375 apkDirectory.childFile('app-flavor1-release.apk').createSync();
376
377 final Iterable<File> apks = findApkFiles(
378 project,
379 const AndroidBuildInfo(BuildInfo(BuildMode.release, 'flavor1')),
380 );
381 expect(apks.isNotEmpty, isTrue);
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800382 expect(apks.first.path, equals(globals.fs.path.join('irrelevant', 'app', 'outputs', 'apk', 'flavor1', 'release', 'app-flavor1-release.apk')));
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700383 }, overrides: <Type, Generator>{
384 FileSystem: () => MemoryFileSystem(),
385 ProcessManager: () => FakeProcessManager.any(),
386 });
Emmanuel Garciadea35da2019-11-01 10:06:23 -0700387
388 testUsingContext('apk not found', () {
389 final FlutterProject project = FlutterProject.current();
390 expect(
391 () {
392 findApkFiles(
393 project,
394 const AndroidBuildInfo(BuildInfo(BuildMode.debug, 'foo_bar')),
395 );
396 },
397 throwsToolExit(
398 message:
399 'Gradle build failed to produce an .apk file. It\'s likely that this file '
400 'was generated under ${project.android.buildDirectory.path}, but the tool couldn\'t find it.'
401 )
402 );
403 verify(
404 mockUsage.sendEvent(
405 any,
406 any,
407 label: 'gradle-expected-file-not-found',
408 parameters: const <String, String> {
409 'cd37': 'androidGradlePluginVersion: 5.6.2, fileExtension: .apk',
410 },
411 ),
412 ).called(1);
413 }, overrides: <Type, Generator>{
414 FileSystem: () => MemoryFileSystem(),
415 ProcessManager: () => FakeProcessManager.any(),
416 Usage: () => mockUsage,
417 });
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700418 });
419
420 group('gradle build', () {
421 testUsingContext('do not crash if there is no Android SDK', () async {
422 expect(() {
423 updateLocalProperties(project: FlutterProject.current());
424 }, throwsToolExit(
425 message: '$warningMark No Android SDK found. Try setting the ANDROID_HOME environment variable.',
426 ));
427 }, overrides: <Type, Generator>{
428 AndroidSdk: () => null,
429 });
430
Michael Klimushyn4a9e5bc2019-02-07 17:23:35 -0800431 test('androidXPluginWarningRegex should match lines with the AndroidX plugin warnings', () {
432 final List<String> nonMatchingLines = <String>[
433 ':app:preBuild UP-TO-DATE',
434 'BUILD SUCCESSFUL in 0s',
435 'Generic plugin AndroidX text',
436 '',
437 ];
438 final List<String> matchingLines = <String>[
439 '*********************************************************************************************************************************',
440 "WARNING: This version of image_picker will break your Android build if it or its dependencies aren't compatible with AndroidX.",
441 'See https://goo.gl/CP92wY for more information on the problem and how to fix it.',
442 'This warning prints for all Android build failures. The real root cause of the error may be unrelated.',
443 ];
444 for (String m in nonMatchingLines) {
445 expect(androidXPluginWarningRegex.hasMatch(m), isFalse);
446 }
447 for (String m in matchingLines) {
448 expect(androidXPluginWarningRegex.hasMatch(m), isTrue);
449 }
450 });
Jason Simmonscfa0a2d2017-06-19 09:37:39 -0700451 });
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200452
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700453 group('Config files', () {
454 BufferLogger mockLogger;
455 Directory tempDir;
456
457 setUp(() {
458 mockLogger = BufferLogger();
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800459 tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_settings_aar_test.');
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700460 });
461
462 testUsingContext('create settings_aar.gradle when current settings.gradle loads plugins', () {
463 const String currentSettingsGradle = '''
464include ':app'
465
466def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
467
468def plugins = new Properties()
469def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
470if (pluginsFile.exists()) {
471 pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
472}
473
474plugins.each { name, path ->
475 def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
476 include ":\$name"
477 project(":\$name").projectDir = pluginDirectory
478}
479''';
480
481 const String settingsAarFile = '''
482include ':app'
483''';
484
485 tempDir.childFile('settings.gradle').writeAsStringSync(currentSettingsGradle);
486
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800487 final String toolGradlePath = globals.fs.path.join(
488 globals.fs.path.absolute(Cache.flutterRoot),
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700489 'packages',
490 'flutter_tools',
491 'gradle');
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800492 globals.fs.directory(toolGradlePath).createSync(recursive: true);
493 globals.fs.file(globals.fs.path.join(toolGradlePath, 'deprecated_settings.gradle'))
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700494 .writeAsStringSync(currentSettingsGradle);
495
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800496 globals.fs.file(globals.fs.path.join(toolGradlePath, 'settings_aar.gradle.tmpl'))
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700497 .writeAsStringSync(settingsAarFile);
498
499 createSettingsAarGradle(tempDir);
500
501 expect(mockLogger.statusText, contains('created successfully'));
502 expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
503
504 }, overrides: <Type, Generator>{
505 FileSystem: () => MemoryFileSystem(),
Jonah Williams4c677e42019-10-22 22:05:00 -0700506 ProcessManager: () => FakeProcessManager.any(),
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700507 Logger: () => mockLogger,
508 });
509
510 testUsingContext('create settings_aar.gradle when current settings.gradle doesn\'t load plugins', () {
511 const String currentSettingsGradle = '''
512include ':app'
513''';
514
515 const String settingsAarFile = '''
516include ':app'
517''';
518
519 tempDir.childFile('settings.gradle').writeAsStringSync(currentSettingsGradle);
520
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800521 final String toolGradlePath = globals.fs.path.join(
522 globals.fs.path.absolute(Cache.flutterRoot),
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700523 'packages',
524 'flutter_tools',
525 'gradle');
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800526 globals.fs.directory(toolGradlePath).createSync(recursive: true);
527 globals.fs.file(globals.fs.path.join(toolGradlePath, 'deprecated_settings.gradle'))
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700528 .writeAsStringSync(currentSettingsGradle);
529
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800530 globals.fs.file(globals.fs.path.join(toolGradlePath, 'settings_aar.gradle.tmpl'))
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700531 .writeAsStringSync(settingsAarFile);
532
533 createSettingsAarGradle(tempDir);
534
535 expect(mockLogger.statusText, contains('created successfully'));
536 expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
537
538 }, overrides: <Type, Generator>{
539 FileSystem: () => MemoryFileSystem(),
Jonah Williams4c677e42019-10-22 22:05:00 -0700540 ProcessManager: () => FakeProcessManager.any(),
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700541 Logger: () => mockLogger,
542 });
543 });
544
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200545 group('Gradle local.properties', () {
Ralph Bergmann779eea62018-07-09 19:36:17 +0200546 MockLocalEngineArtifacts mockArtifacts;
547 MockProcessManager mockProcessManager;
548 FakePlatform android;
549 FileSystem fs;
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200550
551 setUp(() {
Alexandre Ardhuind927c932018-09-12 08:29:29 +0200552 fs = MemoryFileSystem();
553 mockArtifacts = MockLocalEngineArtifacts();
554 mockProcessManager = MockProcessManager();
Ralph Bergmann779eea62018-07-09 19:36:17 +0200555 android = fakePlatform('android');
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200556 });
557
Ralph Bergmann779eea62018-07-09 19:36:17 +0200558 void testUsingAndroidContext(String description, dynamic testMethod()) {
559 testUsingContext(description, testMethod, overrides: <Type, Generator>{
560 Artifacts: () => mockArtifacts,
Ralph Bergmann779eea62018-07-09 19:36:17 +0200561 Platform: () => android,
562 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700563 ProcessManager: () => mockProcessManager,
Ralph Bergmann779eea62018-07-09 19:36:17 +0200564 });
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200565 }
566
567 String propertyFor(String key, File file) {
Stanislav Baranovf6c14762018-12-18 10:39:27 -0800568 final Iterable<String> result = file.readAsLinesSync()
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200569 .where((String line) => line.startsWith('$key='))
Stanislav Baranovf6c14762018-12-18 10:39:27 -0800570 .map((String line) => line.split('=')[1]);
571 return result.isEmpty ? null : result.first;
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200572 }
573
574 Future<void> checkBuildVersion({
575 String manifest,
576 BuildInfo buildInfo,
577 String expectedBuildName,
578 String expectedBuildNumber,
579 }) async {
Alexander Aprelevd7759082019-04-15 21:02:20 -0700580 when(mockArtifacts.getArtifactPath(Artifact.flutterFramework,
581 platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine');
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800582 when(mockArtifacts.engineOutPath).thenReturn(globals.fs.path.join('out', 'android_arm'));
Ralph Bergmann779eea62018-07-09 19:36:17 +0200583
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800584 final File manifestFile = globals.fs.file('path/to/project/pubspec.yaml');
Ralph Bergmann779eea62018-07-09 19:36:17 +0200585 manifestFile.createSync(recursive: true);
586 manifestFile.writeAsStringSync(manifest);
587
588 // write schemaData otherwise pubspec.yaml file can't be loaded
Danny Tuppenya74f5912018-09-04 17:12:24 +0100589 writeEmptySchemaFile(fs);
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200590
Stanislav Baranovf6c14762018-12-18 10:39:27 -0800591 updateLocalProperties(
Jonah Williams4ff46712019-04-29 08:21:32 -0700592 project: FlutterProject.fromPath('path/to/project'),
Stanislav Baranovf6c14762018-12-18 10:39:27 -0800593 buildInfo: buildInfo,
594 requireAndroidSdk: false,
595 );
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200596
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800597 final File localPropertiesFile = globals.fs.file('path/to/project/android/local.properties');
Stanislav Baranovf6c14762018-12-18 10:39:27 -0800598 expect(propertyFor('flutter.versionName', localPropertiesFile), expectedBuildName);
599 expect(propertyFor('flutter.versionCode', localPropertiesFile), expectedBuildNumber);
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200600 }
601
Ralph Bergmann779eea62018-07-09 19:36:17 +0200602 testUsingAndroidContext('extract build name and number from pubspec.yaml', () async {
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200603 const String manifest = '''
604name: test
605version: 1.0.0+1
606dependencies:
607 flutter:
608 sdk: flutter
609flutter:
610''';
611
Alexandre Ardhuineda03e22018-08-02 12:02:32 +0200612 const BuildInfo buildInfo = BuildInfo(BuildMode.release, null);
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200613 await checkBuildVersion(
614 manifest: manifest,
615 buildInfo: buildInfo,
616 expectedBuildName: '1.0.0',
617 expectedBuildNumber: '1',
618 );
619 });
620
Ralph Bergmann779eea62018-07-09 19:36:17 +0200621 testUsingAndroidContext('extract build name from pubspec.yaml', () async {
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200622 const String manifest = '''
623name: test
624version: 1.0.0
625dependencies:
626 flutter:
627 sdk: flutter
628flutter:
629''';
Alexandre Ardhuineda03e22018-08-02 12:02:32 +0200630 const BuildInfo buildInfo = BuildInfo(BuildMode.release, null);
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200631 await checkBuildVersion(
632 manifest: manifest,
633 buildInfo: buildInfo,
634 expectedBuildName: '1.0.0',
635 expectedBuildNumber: null,
636 );
637 });
638
Ralph Bergmann779eea62018-07-09 19:36:17 +0200639 testUsingAndroidContext('allow build info to override build name', () async {
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200640 const String manifest = '''
641name: test
642version: 1.0.0+1
643dependencies:
644 flutter:
645 sdk: flutter
646flutter:
647''';
Alexandre Ardhuineda03e22018-08-02 12:02:32 +0200648 const BuildInfo buildInfo = BuildInfo(BuildMode.release, null, buildName: '1.0.2');
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200649 await checkBuildVersion(
650 manifest: manifest,
651 buildInfo: buildInfo,
652 expectedBuildName: '1.0.2',
653 expectedBuildNumber: '1',
654 );
655 });
656
Ralph Bergmann779eea62018-07-09 19:36:17 +0200657 testUsingAndroidContext('allow build info to override build number', () async {
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200658 const String manifest = '''
659name: test
660version: 1.0.0+1
661dependencies:
662 flutter:
663 sdk: flutter
664flutter:
665''';
KyleWong4b4a94002019-02-13 23:48:03 +0800666 const BuildInfo buildInfo = BuildInfo(BuildMode.release, null, buildNumber: '3');
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200667 await checkBuildVersion(
668 manifest: manifest,
669 buildInfo: buildInfo,
670 expectedBuildName: '1.0.0',
671 expectedBuildNumber: '3',
672 );
673 });
674
Ralph Bergmann779eea62018-07-09 19:36:17 +0200675 testUsingAndroidContext('allow build info to override build name and number', () async {
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200676 const String manifest = '''
677name: test
678version: 1.0.0+1
679dependencies:
680 flutter:
681 sdk: flutter
682flutter:
683''';
KyleWong4b4a94002019-02-13 23:48:03 +0800684 const BuildInfo buildInfo = BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: '3');
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200685 await checkBuildVersion(
686 manifest: manifest,
687 buildInfo: buildInfo,
688 expectedBuildName: '1.0.2',
689 expectedBuildNumber: '3',
690 );
691 });
692
Ralph Bergmann779eea62018-07-09 19:36:17 +0200693 testUsingAndroidContext('allow build info to override build name and set number', () async {
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200694 const String manifest = '''
695name: test
696version: 1.0.0
697dependencies:
698 flutter:
699 sdk: flutter
700flutter:
701''';
KyleWong4b4a94002019-02-13 23:48:03 +0800702 const BuildInfo buildInfo = BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: '3');
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200703 await checkBuildVersion(
704 manifest: manifest,
705 buildInfo: buildInfo,
706 expectedBuildName: '1.0.2',
707 expectedBuildNumber: '3',
708 );
709 });
710
Ralph Bergmann779eea62018-07-09 19:36:17 +0200711 testUsingAndroidContext('allow build info to set build name and number', () async {
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200712 const String manifest = '''
713name: test
714dependencies:
715 flutter:
716 sdk: flutter
717flutter:
718''';
KyleWong4b4a94002019-02-13 23:48:03 +0800719 const BuildInfo buildInfo = BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: '3');
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200720 await checkBuildVersion(
721 manifest: manifest,
722 buildInfo: buildInfo,
723 expectedBuildName: '1.0.2',
724 expectedBuildNumber: '3',
725 );
726 });
Stanislav Baranov693fb652018-12-18 12:54:26 -0800727
728 testUsingAndroidContext('allow build info to unset build name and number', () async {
729 const String manifest = '''
730name: test
731dependencies:
732 flutter:
733 sdk: flutter
734flutter:
735''';
736 await checkBuildVersion(
737 manifest: manifest,
738 buildInfo: const BuildInfo(BuildMode.release, null, buildName: null, buildNumber: null),
739 expectedBuildName: null,
740 expectedBuildNumber: null,
741 );
742 await checkBuildVersion(
743 manifest: manifest,
KyleWong4b4a94002019-02-13 23:48:03 +0800744 buildInfo: const BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: '3'),
Stanislav Baranov693fb652018-12-18 12:54:26 -0800745 expectedBuildName: '1.0.2',
746 expectedBuildNumber: '3',
747 );
748 await checkBuildVersion(
749 manifest: manifest,
KyleWong4b4a94002019-02-13 23:48:03 +0800750 buildInfo: const BuildInfo(BuildMode.release, null, buildName: '1.0.3', buildNumber: '4'),
Stanislav Baranov693fb652018-12-18 12:54:26 -0800751 expectedBuildName: '1.0.3',
752 expectedBuildNumber: '4',
753 );
754 // Values don't get unset.
755 await checkBuildVersion(
756 manifest: manifest,
757 buildInfo: null,
758 expectedBuildName: '1.0.3',
759 expectedBuildNumber: '4',
760 );
761 // Values get unset.
762 await checkBuildVersion(
763 manifest: manifest,
764 buildInfo: const BuildInfo(BuildMode.release, null, buildName: null, buildNumber: null),
765 expectedBuildName: null,
766 expectedBuildNumber: null,
767 );
768 });
Ralph Bergmannc65e9d12018-05-30 16:51:25 +0200769 });
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700770
771 group('gradle version', () {
772 test('should be compatible with the Android plugin version', () {
773 // Granular versions.
774 expect(getGradleVersionFor('1.0.0'), '2.3');
775 expect(getGradleVersionFor('1.0.1'), '2.3');
776 expect(getGradleVersionFor('1.0.2'), '2.3');
777 expect(getGradleVersionFor('1.0.4'), '2.3');
778 expect(getGradleVersionFor('1.0.8'), '2.3');
779 expect(getGradleVersionFor('1.1.0'), '2.3');
780 expect(getGradleVersionFor('1.1.2'), '2.3');
781 expect(getGradleVersionFor('1.1.2'), '2.3');
782 expect(getGradleVersionFor('1.1.3'), '2.3');
783 // Version Ranges.
784 expect(getGradleVersionFor('1.2.0'), '2.9');
785 expect(getGradleVersionFor('1.3.1'), '2.9');
786
787 expect(getGradleVersionFor('1.5.0'), '2.2.1');
788
789 expect(getGradleVersionFor('2.0.0'), '2.13');
790 expect(getGradleVersionFor('2.1.2'), '2.13');
791
792 expect(getGradleVersionFor('2.1.3'), '2.14.1');
793 expect(getGradleVersionFor('2.2.3'), '2.14.1');
794
795 expect(getGradleVersionFor('2.3.0'), '3.3');
796
797 expect(getGradleVersionFor('3.0.0'), '4.1');
798
799 expect(getGradleVersionFor('3.1.0'), '4.4');
800
801 expect(getGradleVersionFor('3.2.0'), '4.6');
802 expect(getGradleVersionFor('3.2.1'), '4.6');
803
804 expect(getGradleVersionFor('3.3.0'), '4.10.2');
805 expect(getGradleVersionFor('3.3.2'), '4.10.2');
806
Emmanuel Garcia4d404da2019-09-13 19:37:07 -0700807 expect(getGradleVersionFor('3.4.0'), '5.6.2');
808 expect(getGradleVersionFor('3.5.0'), '5.6.2');
Emmanuel Garcia242a4222019-07-23 09:27:42 -0700809 });
810
811 test('throws on unsupported versions', () {
812 expect(() => getGradleVersionFor('3.6.0'),
813 throwsA(predicate<Exception>((Exception e) => e is ToolExit)));
814 });
815 });
xster46406902019-08-27 23:46:33 -0700816
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700817 group('isAppUsingAndroidX', () {
818 FileSystem fs;
819
820 setUp(() {
821 fs = MemoryFileSystem();
822 });
823
824 testUsingContext('returns true when the project is using AndroidX', () async {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800825 final Directory androidDirectory = globals.fs.systemTempDirectory.createTempSync('flutter_android.');
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700826
827 androidDirectory
828 .childFile('gradle.properties')
829 .writeAsStringSync('android.useAndroidX=true');
830
831 expect(isAppUsingAndroidX(androidDirectory), isTrue);
832
833 }, overrides: <Type, Generator>{
834 FileSystem: () => fs,
Jonah Williams4c677e42019-10-22 22:05:00 -0700835 ProcessManager: () => FakeProcessManager.any(),
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700836 });
837
838 testUsingContext('returns false when the project is not using AndroidX', () async {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800839 final Directory androidDirectory = globals.fs.systemTempDirectory.createTempSync('flutter_android.');
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700840
841 androidDirectory
842 .childFile('gradle.properties')
843 .writeAsStringSync('android.useAndroidX=false');
844
845 expect(isAppUsingAndroidX(androidDirectory), isFalse);
846
847 }, overrides: <Type, Generator>{
848 FileSystem: () => fs,
Jonah Williams4c677e42019-10-22 22:05:00 -0700849 ProcessManager: () => FakeProcessManager.any(),
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700850 });
851
852 testUsingContext('returns false when gradle.properties does not exist', () async {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800853 final Directory androidDirectory = globals.fs.systemTempDirectory.createTempSync('flutter_android.');
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700854
855 expect(isAppUsingAndroidX(androidDirectory), isFalse);
856
857 }, overrides: <Type, Generator>{
858 FileSystem: () => fs,
Jonah Williams4c677e42019-10-22 22:05:00 -0700859 ProcessManager: () => FakeProcessManager.any(),
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700860 });
861 });
862
863 group('buildPluginsAsAar', () {
864 FileSystem fs;
865 MockProcessManager mockProcessManager;
866 MockAndroidSdk mockAndroidSdk;
867
868 setUp(() {
869 fs = MemoryFileSystem();
870
871 mockProcessManager = MockProcessManager();
872 when(mockProcessManager.run(
873 any,
874 workingDirectory: anyNamed('workingDirectory'),
875 environment: anyNamed('environment'),
876 )).thenAnswer((_) async => ProcessResult(1, 0, '', ''));
877
878 mockAndroidSdk = MockAndroidSdk();
879 when(mockAndroidSdk.directory).thenReturn('irrelevant');
880 });
881
882 testUsingContext('calls gradle', () async {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800883 final Directory androidDirectory = globals.fs.directory('android.');
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700884 androidDirectory.createSync();
885 androidDirectory
886 .childFile('pubspec.yaml')
887 .writeAsStringSync('name: irrelevant');
888
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800889 final Directory plugin1 = globals.fs.directory('plugin1.');
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700890 plugin1
891 ..createSync()
892 ..childFile('pubspec.yaml')
893 .writeAsStringSync('''
894name: irrelevant
895flutter:
896 plugin:
897 androidPackage: irrelevant
898''');
Emmanuel Garciaa4679322019-12-12 12:38:09 -0800899
Emmanuel Garcia9884f992019-12-17 19:48:01 -0800900 plugin1
901 .childDirectory('android')
902 .childFile('build.gradle')
903 .createSync(recursive: true);
Emmanuel Garciaa4679322019-12-12 12:38:09 -0800904
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800905 final Directory plugin2 = globals.fs.directory('plugin2.');
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700906 plugin2
907 ..createSync()
908 ..childFile('pubspec.yaml')
909 .writeAsStringSync('''
910name: irrelevant
911flutter:
912 plugin:
913 androidPackage: irrelevant
914''');
915
Emmanuel Garcia9884f992019-12-17 19:48:01 -0800916 plugin2
917 .childDirectory('android')
918 .childFile('build.gradle')
919 .createSync(recursive: true);
Emmanuel Garciaa4679322019-12-12 12:38:09 -0800920
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700921 androidDirectory
922 .childFile('.flutter-plugins')
923 .writeAsStringSync('''
924plugin1=${plugin1.path}
925plugin2=${plugin2.path}
926''');
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700927 final Directory buildDirectory = androidDirectory
928 .childDirectory('build');
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700929 buildDirectory
930 .childDirectory('outputs')
931 .childDirectory('repo')
932 .createSync(recursive: true);
933
934 await buildPluginsAsAar(
935 FlutterProject.fromPath(androidDirectory.path),
936 const AndroidBuildInfo(BuildInfo.release),
Emmanuel Garcia175b3722019-10-31 13:19:15 -0700937 buildDirectory: buildDirectory,
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700938 );
939
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800940 final String flutterRoot = globals.fs.path.absolute(Cache.flutterRoot);
941 final String initScript = globals.fs.path.join(
Emmanuel Garciaa4679322019-12-12 12:38:09 -0800942 flutterRoot,
943 'packages',
944 'flutter_tools',
945 'gradle',
946 'aar_init_script.gradle',
947 );
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700948 verify(mockProcessManager.run(
949 <String>[
950 'gradlew',
951 '-I=$initScript',
952 '-Pflutter-root=$flutterRoot',
953 '-Poutput-dir=${buildDirectory.path}',
954 '-Pis-plugin=true',
Sean Freiburg418b8142019-12-12 10:28:01 -0800955 '-PbuildNumber=1.0',
Jonah Williams03a45dc2019-10-10 16:34:16 -0700956 '-Ptarget-platform=android-arm,android-arm64,android-x64',
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700957 'assembleAarRelease',
958 ],
959 environment: anyNamed('environment'),
960 workingDirectory: plugin1.childDirectory('android').path),
961 ).called(1);
962
963 verify(mockProcessManager.run(
964 <String>[
965 'gradlew',
966 '-I=$initScript',
967 '-Pflutter-root=$flutterRoot',
968 '-Poutput-dir=${buildDirectory.path}',
969 '-Pis-plugin=true',
Sean Freiburg418b8142019-12-12 10:28:01 -0800970 '-PbuildNumber=1.0',
Jonah Williams03a45dc2019-10-10 16:34:16 -0700971 '-Ptarget-platform=android-arm,android-arm64,android-x64',
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700972 'assembleAarRelease',
973 ],
974 environment: anyNamed('environment'),
975 workingDirectory: plugin2.childDirectory('android').path),
976 ).called(1);
977
978 }, overrides: <Type, Generator>{
979 AndroidSdk: () => mockAndroidSdk,
980 FileSystem: () => fs,
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700981 ProcessManager: () => mockProcessManager,
Ian Hickson08643c42019-10-11 11:23:12 -0700982 GradleUtils: () => FakeGradleUtils(),
Emmanuel Garcia16d408a2019-09-24 16:16:22 -0700983 });
Emmanuel Garciaa4679322019-12-12 12:38:09 -0800984
Emmanuel Garcia9884f992019-12-17 19:48:01 -0800985 testUsingContext('skips plugin without a android/build.gradle file', () async {
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800986 final Directory androidDirectory = globals.fs.directory('android.');
Emmanuel Garciaa4679322019-12-12 12:38:09 -0800987 androidDirectory.createSync();
988 androidDirectory
989 .childFile('pubspec.yaml')
990 .writeAsStringSync('name: irrelevant');
991
Jonah Williamsee7a37f2020-01-06 11:04:20 -0800992 final Directory plugin1 = globals.fs.directory('plugin1.');
Emmanuel Garciaa4679322019-12-12 12:38:09 -0800993 plugin1
994 ..createSync()
995 ..childFile('pubspec.yaml')
996 .writeAsStringSync('''
997name: irrelevant
998flutter:
999 plugin:
1000 androidPackage: irrelevant
1001''');
1002
1003 androidDirectory
1004 .childFile('.flutter-plugins')
1005 .writeAsStringSync('''
1006plugin1=${plugin1.path}
1007''');
Emmanuel Garcia9884f992019-12-17 19:48:01 -08001008 // Create an empty android directory.
1009 // https://github.com/flutter/flutter/issues/46898
1010 plugin1.childDirectory('android').createSync();
1011
Emmanuel Garciaa4679322019-12-12 12:38:09 -08001012 final Directory buildDirectory = androidDirectory.childDirectory('build');
1013
1014 buildDirectory
1015 .childDirectory('outputs')
1016 .childDirectory('repo')
1017 .createSync(recursive: true);
1018
1019 await buildPluginsAsAar(
1020 FlutterProject.fromPath(androidDirectory.path),
1021 const AndroidBuildInfo(BuildInfo.release),
1022 buildDirectory: buildDirectory,
1023 );
1024
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001025 final String flutterRoot = globals.fs.path.absolute(Cache.flutterRoot);
1026 final String initScript = globals.fs.path.join(
Emmanuel Garciaa4679322019-12-12 12:38:09 -08001027 flutterRoot,
1028 'packages',
1029 'flutter_tools',
1030 'gradle',
1031 'aar_init_script.gradle',
1032 );
1033 verifyNever(mockProcessManager.run(
1034 <String>[
1035 'gradlew',
1036 '-I=$initScript',
1037 '-Pflutter-root=$flutterRoot',
1038 '-Poutput-dir=${buildDirectory.path}',
1039 '-Pis-plugin=true',
1040 '-Ptarget-platform=android-arm,android-arm64,android-x64',
1041 'assembleAarRelease',
1042 ],
1043 environment: anyNamed('environment'),
1044 workingDirectory: plugin1.childDirectory('android').path),
1045 );
1046 }, overrides: <Type, Generator>{
1047 AndroidSdk: () => mockAndroidSdk,
1048 FileSystem: () => fs,
1049 ProcessManager: () => mockProcessManager,
1050 GradleUtils: () => FakeGradleUtils(),
1051 });
Emmanuel Garcia16d408a2019-09-24 16:16:22 -07001052 });
1053
xster46406902019-08-27 23:46:33 -07001054 group('gradle build', () {
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001055 final Usage mockUsage = MockUsage();
1056
xster46406902019-08-27 23:46:33 -07001057 MockAndroidSdk mockAndroidSdk;
1058 MockAndroidStudio mockAndroidStudio;
1059 MockLocalEngineArtifacts mockArtifacts;
1060 MockProcessManager mockProcessManager;
1061 FakePlatform android;
1062 FileSystem fs;
Emmanuel Garcia4a1c62c2019-08-28 14:52:08 -07001063 Cache cache;
xster46406902019-08-27 23:46:33 -07001064
1065 setUp(() {
1066 fs = MemoryFileSystem();
1067 mockAndroidSdk = MockAndroidSdk();
1068 mockAndroidStudio = MockAndroidStudio();
1069 mockArtifacts = MockLocalEngineArtifacts();
1070 mockProcessManager = MockProcessManager();
1071 android = fakePlatform('android');
Emmanuel Garcia4a1c62c2019-08-28 14:52:08 -07001072
Ian Hicksoncdc2d992019-10-07 16:43:04 -07001073 final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_artifacts_test.');
Emmanuel Garcia4a1c62c2019-08-28 14:52:08 -07001074 cache = Cache(rootOverride: tempDir);
1075
1076 final Directory gradleWrapperDirectory = tempDir
1077 .childDirectory('bin')
1078 .childDirectory('cache')
1079 .childDirectory('artifacts')
1080 .childDirectory('gradle_wrapper');
1081 gradleWrapperDirectory.createSync(recursive: true);
1082 gradleWrapperDirectory
1083 .childFile('gradlew')
1084 .writeAsStringSync('irrelevant');
1085 gradleWrapperDirectory
1086 .childDirectory('gradle')
1087 .childDirectory('wrapper')
1088 .createSync(recursive: true);
1089 gradleWrapperDirectory
1090 .childDirectory('gradle')
1091 .childDirectory('wrapper')
1092 .childFile('gradle-wrapper.jar')
1093 .writeAsStringSync('irrelevant');
xster46406902019-08-27 23:46:33 -07001094 });
1095
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001096 testUsingContext('recognizes common errors - tool exit', () async {
1097 final Process process = createMockProcess(
1098 exitCode: 1,
1099 stdout: 'irrelevant\nSome gradle message\nirrelevant',
1100 );
1101 when(mockProcessManager.start(any,
1102 workingDirectory: anyNamed('workingDirectory'),
1103 environment: anyNamed('environment')))
1104 .thenAnswer((_) => Future<Process>.value(process));
1105
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001106 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001107 .childFile('build.gradle')
1108 .createSync(recursive: true);
1109
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001110 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001111 .childFile('gradle.properties')
1112 .createSync(recursive: true);
1113
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001114 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001115 .childDirectory('app')
1116 .childFile('build.gradle')
1117 ..createSync(recursive: true)
1118 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1119
1120 bool handlerCalled = false;
1121 await expectLater(() async {
1122 await buildGradleApp(
1123 project: FlutterProject.current(),
1124 androidBuildInfo: const AndroidBuildInfo(
1125 BuildInfo(
1126 BuildMode.release,
1127 null,
1128 ),
1129 ),
1130 target: 'lib/main.dart',
1131 isBuildingBundle: false,
1132 localGradleErrors: <GradleHandledError>[
1133 GradleHandledError(
1134 test: (String line) {
1135 return line.contains('Some gradle message');
1136 },
1137 handler: ({
1138 String line,
1139 FlutterProject project,
1140 bool usesAndroidX,
1141 bool shouldBuildPluginAsAar,
1142 }) async {
1143 handlerCalled = true;
1144 return GradleBuildStatus.exit;
1145 },
1146 eventLabel: 'random-event-label',
1147 ),
1148 ],
1149 );
1150 },
1151 throwsToolExit(
1152 message: 'Gradle task assembleRelease failed with exit code 1'
1153 ));
1154
1155 expect(handlerCalled, isTrue);
1156
1157 verify(mockUsage.sendEvent(
1158 any,
1159 any,
Emmanuel Garciadea35da2019-11-01 10:06:23 -07001160 label: 'gradle-random-event-label-failure',
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001161 parameters: anyNamed('parameters'),
1162 )).called(1);
1163
1164 }, overrides: <Type, Generator>{
1165 AndroidSdk: () => mockAndroidSdk,
1166 Cache: () => cache,
1167 Platform: () => android,
1168 FileSystem: () => fs,
1169 ProcessManager: () => mockProcessManager,
1170 Usage: () => mockUsage,
1171 });
1172
1173 testUsingContext('recognizes common errors - retry build', () async {
1174 when(mockProcessManager.start(any,
1175 workingDirectory: anyNamed('workingDirectory'),
1176 environment: anyNamed('environment')))
1177 .thenAnswer((_) {
1178 final Process process = createMockProcess(
1179 exitCode: 1,
1180 stdout: 'irrelevant\nSome gradle message\nirrelevant',
1181 );
1182 return Future<Process>.value(process);
1183 });
1184
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001185 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001186 .childFile('build.gradle')
1187 .createSync(recursive: true);
1188
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001189 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001190 .childFile('gradle.properties')
1191 .createSync(recursive: true);
1192
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001193 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001194 .childDirectory('app')
1195 .childFile('build.gradle')
1196 ..createSync(recursive: true)
1197 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1198
1199 int testFnCalled = 0;
1200 await expectLater(() async {
1201 await buildGradleApp(
1202 project: FlutterProject.current(),
1203 androidBuildInfo: const AndroidBuildInfo(
1204 BuildInfo(
1205 BuildMode.release,
1206 null,
1207 ),
1208 ),
1209 target: 'lib/main.dart',
1210 isBuildingBundle: false,
1211 localGradleErrors: <GradleHandledError>[
1212 GradleHandledError(
1213 test: (String line) {
1214 if (line.contains('Some gradle message')) {
1215 testFnCalled++;
1216 return true;
1217 }
1218 return false;
1219 },
1220 handler: ({
1221 String line,
1222 FlutterProject project,
1223 bool usesAndroidX,
1224 bool shouldBuildPluginAsAar,
1225 }) async {
1226 return GradleBuildStatus.retry;
1227 },
1228 eventLabel: 'random-event-label',
1229 ),
1230 ],
1231 );
1232 }, throwsToolExit(
1233 message: 'Gradle task assembleRelease failed with exit code 1'
1234 ));
1235
1236 expect(testFnCalled, equals(2));
1237
1238 verify(mockUsage.sendEvent(
1239 any,
1240 any,
Emmanuel Garciadea35da2019-11-01 10:06:23 -07001241 label: 'gradle-random-event-label-failure',
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001242 parameters: anyNamed('parameters'),
1243 )).called(1);
1244
1245 }, overrides: <Type, Generator>{
1246 AndroidSdk: () => mockAndroidSdk,
1247 Cache: () => cache,
1248 Platform: () => android,
1249 FileSystem: () => fs,
1250 ProcessManager: () => mockProcessManager,
1251 Usage: () => mockUsage,
1252 });
1253
Emmanuel Garciaf7668712019-11-13 10:02:46 -08001254 testUsingContext('recognizes process exceptions - tool exit', () async {
1255 when(mockProcessManager.start(any,
1256 workingDirectory: anyNamed('workingDirectory'),
1257 environment: anyNamed('environment')))
1258 .thenThrow(const ProcessException('', <String>[], 'Some gradle message'));
1259
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001260 globals.fs.directory('android')
Emmanuel Garciaf7668712019-11-13 10:02:46 -08001261 .childFile('build.gradle')
1262 .createSync(recursive: true);
1263
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001264 globals.fs.directory('android')
Emmanuel Garciaf7668712019-11-13 10:02:46 -08001265 .childFile('gradle.properties')
1266 .createSync(recursive: true);
1267
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001268 globals.fs.directory('android')
Emmanuel Garciaf7668712019-11-13 10:02:46 -08001269 .childDirectory('app')
1270 .childFile('build.gradle')
1271 ..createSync(recursive: true)
1272 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1273
1274 bool handlerCalled = false;
1275 await expectLater(() async {
1276 await buildGradleApp(
1277 project: FlutterProject.current(),
1278 androidBuildInfo: const AndroidBuildInfo(
1279 BuildInfo(
1280 BuildMode.release,
1281 null,
1282 ),
1283 ),
1284 target: 'lib/main.dart',
1285 isBuildingBundle: false,
1286 localGradleErrors: <GradleHandledError>[
1287 GradleHandledError(
1288 test: (String line) {
1289 return line.contains('Some gradle message');
1290 },
1291 handler: ({
1292 String line,
1293 FlutterProject project,
1294 bool usesAndroidX,
1295 bool shouldBuildPluginAsAar,
1296 }) async {
1297 handlerCalled = true;
1298 return GradleBuildStatus.exit;
1299 },
1300 eventLabel: 'random-event-label',
1301 ),
1302 ],
1303 );
1304 },
1305 throwsToolExit(
1306 message: 'Gradle task assembleRelease failed with exit code 1'
1307 ));
1308
1309 expect(handlerCalled, isTrue);
1310
1311 verify(mockUsage.sendEvent(
1312 any,
1313 any,
1314 label: 'gradle-random-event-label-failure',
1315 parameters: anyNamed('parameters'),
1316 )).called(1);
1317
1318 }, overrides: <Type, Generator>{
1319 AndroidSdk: () => mockAndroidSdk,
1320 Cache: () => cache,
1321 Platform: () => android,
1322 FileSystem: () => fs,
1323 ProcessManager: () => mockProcessManager,
1324 Usage: () => mockUsage,
1325 });
1326
1327 testUsingContext('rethrows unrecognized ProcessException', () async {
1328 when(mockProcessManager.start(any,
1329 workingDirectory: anyNamed('workingDirectory'),
1330 environment: anyNamed('environment')))
1331 .thenThrow(const ProcessException('', <String>[], 'Unrecognized'));
1332
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001333 globals.fs.directory('android')
Emmanuel Garciaf7668712019-11-13 10:02:46 -08001334 .childFile('build.gradle')
1335 .createSync(recursive: true);
1336
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001337 globals.fs.directory('android')
Emmanuel Garciaf7668712019-11-13 10:02:46 -08001338 .childFile('gradle.properties')
1339 .createSync(recursive: true);
1340
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001341 globals.fs.directory('android')
Emmanuel Garciaf7668712019-11-13 10:02:46 -08001342 .childDirectory('app')
1343 .childFile('build.gradle')
1344 ..createSync(recursive: true)
1345 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1346
1347 await expectLater(() async {
1348 await buildGradleApp(
1349 project: FlutterProject.current(),
1350 androidBuildInfo: const AndroidBuildInfo(
1351 BuildInfo(
1352 BuildMode.release,
1353 null,
1354 ),
1355 ),
1356 target: 'lib/main.dart',
1357 isBuildingBundle: false,
1358 localGradleErrors: const <GradleHandledError>[],
1359 );
1360 },
1361 throwsA(isInstanceOf<ProcessException>()));
1362
1363 }, overrides: <Type, Generator>{
1364 AndroidSdk: () => mockAndroidSdk,
1365 Cache: () => cache,
1366 Platform: () => android,
1367 FileSystem: () => fs,
1368 ProcessManager: () => mockProcessManager,
1369 });
1370
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001371 testUsingContext('logs success event after a sucessful retry', () async {
1372 int testFnCalled = 0;
1373 when(mockProcessManager.start(any,
1374 workingDirectory: anyNamed('workingDirectory'),
1375 environment: anyNamed('environment')))
1376 .thenAnswer((_) {
1377 Process process;
1378 if (testFnCalled == 0) {
1379 process = createMockProcess(
1380 exitCode: 1,
1381 stdout: 'irrelevant\nSome gradle message\nirrelevant',
1382 );
1383 } else {
1384 process = createMockProcess(
1385 exitCode: 0,
1386 stdout: 'irrelevant',
1387 );
1388 }
1389 testFnCalled++;
1390 return Future<Process>.value(process);
1391 });
1392
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001393 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001394 .childFile('build.gradle')
1395 .createSync(recursive: true);
1396
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001397 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001398 .childFile('gradle.properties')
1399 .createSync(recursive: true);
1400
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001401 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001402 .childDirectory('app')
1403 .childFile('build.gradle')
1404 ..createSync(recursive: true)
1405 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1406
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001407 globals.fs.directory('build')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001408 .childDirectory('app')
1409 .childDirectory('outputs')
1410 .childDirectory('apk')
1411 .childDirectory('release')
1412 .childFile('app-release.apk')
1413 ..createSync(recursive: true);
1414
1415 await buildGradleApp(
1416 project: FlutterProject.current(),
1417 androidBuildInfo: const AndroidBuildInfo(
1418 BuildInfo(
1419 BuildMode.release,
1420 null,
1421 ),
1422 ),
1423 target: 'lib/main.dart',
1424 isBuildingBundle: false,
1425 localGradleErrors: <GradleHandledError>[
1426 GradleHandledError(
1427 test: (String line) {
1428 return line.contains('Some gradle message');
1429 },
1430 handler: ({
1431 String line,
1432 FlutterProject project,
1433 bool usesAndroidX,
1434 bool shouldBuildPluginAsAar,
1435 }) async {
1436 return GradleBuildStatus.retry;
1437 },
1438 eventLabel: 'random-event-label',
1439 ),
1440 ],
1441 );
1442
1443 verify(mockUsage.sendEvent(
1444 any,
1445 any,
Emmanuel Garciadea35da2019-11-01 10:06:23 -07001446 label: 'gradle-random-event-label-success',
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001447 parameters: anyNamed('parameters'),
1448 )).called(1);
1449
1450 }, overrides: <Type, Generator>{
1451 AndroidSdk: () => mockAndroidSdk,
1452 Cache: () => cache,
1453 FileSystem: () => fs,
1454 Platform: () => android,
1455 ProcessManager: () => mockProcessManager,
1456 Usage: () => mockUsage,
1457 });
1458
1459 testUsingContext('recognizes common errors - retry build with AAR plugins', () async {
1460 when(mockProcessManager.start(any,
1461 workingDirectory: anyNamed('workingDirectory'),
1462 environment: anyNamed('environment')))
1463 .thenAnswer((_) {
1464 final Process process = createMockProcess(
1465 exitCode: 1,
1466 stdout: 'irrelevant\nSome gradle message\nirrelevant',
1467 );
1468 return Future<Process>.value(process);
1469 });
1470
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001471 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001472 .childFile('build.gradle')
1473 .createSync(recursive: true);
1474
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001475 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001476 .childFile('gradle.properties')
1477 .createSync(recursive: true);
1478
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001479 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001480 .childDirectory('app')
1481 .childFile('build.gradle')
1482 ..createSync(recursive: true)
1483 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1484
1485 int testFnCalled = 0;
1486 bool builtPluginAsAar = false;
1487 await expectLater(() async {
1488 await buildGradleApp(
1489 project: FlutterProject.current(),
1490 androidBuildInfo: const AndroidBuildInfo(
1491 BuildInfo(
1492 BuildMode.release,
1493 null,
1494 ),
1495 ),
1496 target: 'lib/main.dart',
1497 isBuildingBundle: false,
1498 localGradleErrors: <GradleHandledError>[
1499 GradleHandledError(
1500 test: (String line) {
1501 if (line.contains('Some gradle message')) {
1502 testFnCalled++;
1503 return true;
1504 }
1505 return false;
1506 },
1507 handler: ({
1508 String line,
1509 FlutterProject project,
1510 bool usesAndroidX,
1511 bool shouldBuildPluginAsAar,
1512 }) async {
1513 if (testFnCalled == 2) {
1514 builtPluginAsAar = shouldBuildPluginAsAar;
1515 }
1516 return GradleBuildStatus.retryWithAarPlugins;
1517 },
1518 eventLabel: 'random-event-label',
1519 ),
1520 ],
1521 );
1522 }, throwsToolExit(
1523 message: 'Gradle task assembleRelease failed with exit code 1'
1524 ));
1525
1526 expect(testFnCalled, equals(2));
1527 expect(builtPluginAsAar, isTrue);
1528
1529 verify(mockUsage.sendEvent(
1530 any,
1531 any,
Emmanuel Garciadea35da2019-11-01 10:06:23 -07001532 label: 'gradle-random-event-label-failure',
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001533 parameters: anyNamed('parameters'),
1534 )).called(1);
1535
1536 }, overrides: <Type, Generator>{
1537 AndroidSdk: () => mockAndroidSdk,
1538 Cache: () => cache,
1539 Platform: () => android,
1540 FileSystem: () => fs,
1541 ProcessManager: () => mockProcessManager,
1542 Usage: () => mockUsage,
1543 });
1544
1545 testUsingContext('indicates that an APK has been built successfully', () async {
1546 when(mockProcessManager.start(any,
1547 workingDirectory: anyNamed('workingDirectory'),
1548 environment: anyNamed('environment')))
1549 .thenAnswer((_) {
1550 return Future<Process>.value(
1551 createMockProcess(
1552 exitCode: 0,
1553 stdout: '',
1554 ));
1555 });
1556
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001557 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001558 .childFile('build.gradle')
1559 .createSync(recursive: true);
1560
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001561 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001562 .childFile('gradle.properties')
1563 .createSync(recursive: true);
1564
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001565 globals.fs.directory('android')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001566 .childDirectory('app')
1567 .childFile('build.gradle')
1568 ..createSync(recursive: true)
1569 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1570
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001571 globals.fs.directory('build')
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001572 .childDirectory('app')
1573 .childDirectory('outputs')
1574 .childDirectory('apk')
1575 .childDirectory('release')
1576 .childFile('app-release.apk')
1577 ..createSync(recursive: true);
1578
1579 await buildGradleApp(
1580 project: FlutterProject.current(),
1581 androidBuildInfo: const AndroidBuildInfo(
1582 BuildInfo(
1583 BuildMode.release,
1584 null,
1585 ),
1586 ),
1587 target: 'lib/main.dart',
1588 isBuildingBundle: false,
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001589 localGradleErrors: const <GradleHandledError>[],
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001590 );
1591
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001592 expect(
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001593 testLogger.statusText,
Emmanuel Garcia175b3722019-10-31 13:19:15 -07001594 contains('Built build/app/outputs/apk/release/app-release.apk (0.0MB)'),
1595 );
1596
1597 }, overrides: <Type, Generator>{
1598 AndroidSdk: () => mockAndroidSdk,
1599 Cache: () => cache,
1600 FileSystem: () => fs,
1601 Platform: () => android,
1602 ProcessManager: () => mockProcessManager,
1603 });
1604
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001605 testUsingContext('doesn\'t indicate how to consume an AAR when printHowToConsumeAaar is false', () async {
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001606 final File manifestFile = globals.fs.file('pubspec.yaml');
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001607 manifestFile.createSync(recursive: true);
1608 manifestFile.writeAsStringSync('''
1609 flutter:
1610 module:
1611 androidPackage: com.example.test
1612 '''
1613 );
1614
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001615 globals.fs.file('.android/gradlew').createSync(recursive: true);
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001616
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001617 globals.fs.file('.android/gradle.properties')
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001618 .writeAsStringSync('irrelevant');
1619
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001620 globals.fs.file('.android/build.gradle')
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001621 .createSync(recursive: true);
1622
1623 // Let any process start. Assert after.
1624 when(mockProcessManager.run(
1625 any,
1626 environment: anyNamed('environment'),
1627 workingDirectory: anyNamed('workingDirectory'),
1628 )).thenAnswer((_) async => ProcessResult(1, 0, '', ''));
1629
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001630 globals.fs.directory('build/outputs/repo').createSync(recursive: true);
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001631
1632 await buildGradleAar(
1633 androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)),
1634 project: FlutterProject.current(),
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001635 outputDirectory: globals.fs.directory('build/'),
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001636 target: '',
Sean Freiburg418b8142019-12-12 10:28:01 -08001637 buildNumber: '1.0',
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001638 );
1639
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001640 expect(
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001641 testLogger.statusText,
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001642 contains('Built build/outputs/repo'),
1643 );
1644 expect(
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001645 testLogger.statusText.contains('Consuming the Module'),
Emmanuel Garciab0ad6072019-11-07 10:18:55 -08001646 isFalse,
1647 );
1648
1649 }, overrides: <Type, Generator>{
1650 AndroidSdk: () => mockAndroidSdk,
1651 AndroidStudio: () => mockAndroidStudio,
1652 Cache: () => cache,
1653 Platform: () => android,
1654 FileSystem: () => fs,
1655 ProcessManager: () => mockProcessManager,
1656 });
1657
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001658 testUsingContext('build apk uses selected local engine', () async {
1659 when(mockArtifacts.getArtifactPath(Artifact.flutterFramework,
1660 platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine');
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001661 when(mockArtifacts.engineOutPath).thenReturn(globals.fs.path.join('out', 'android_arm'));
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001662
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001663 globals.fs.file('out/android_arm/flutter_embedding_release.pom')
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001664 ..createSync(recursive: true)
1665 ..writeAsStringSync(
1666'''<?xml version="1.0" encoding="UTF-8"?>
1667<project>
1668 <version>1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b</version>
1669 <dependencies>
1670 </dependencies>
1671</project>
1672''');
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001673 globals.fs.file('out/android_arm/armeabi_v7a_release.pom').createSync(recursive: true);
1674 globals.fs.file('out/android_arm/armeabi_v7a_release.jar').createSync(recursive: true);
1675 globals.fs.file('out/android_arm/flutter_embedding_release.jar').createSync(recursive: true);
1676 globals.fs.file('out/android_arm/flutter_embedding_release.pom').createSync(recursive: true);
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001677
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001678 globals.fs.file('android/gradlew').createSync(recursive: true);
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001679
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001680 globals.fs.directory('android')
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001681 .childFile('gradle.properties')
1682 .createSync(recursive: true);
1683
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001684 globals.fs.file('android/build.gradle')
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001685 .createSync(recursive: true);
1686
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001687 globals.fs.directory('android')
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001688 .childDirectory('app')
1689 .childFile('build.gradle')
1690 ..createSync(recursive: true)
1691 ..writeAsStringSync('apply from: irrelevant/flutter.gradle');
1692
1693 // Let any process start. Assert after.
1694 when(mockProcessManager.run(
1695 any,
1696 environment: anyNamed('environment'),
1697 workingDirectory: anyNamed('workingDirectory'),
1698 )).thenAnswer((_) async => ProcessResult(1, 0, '', ''));
1699
1700 when(mockProcessManager.start(any,
1701 workingDirectory: anyNamed('workingDirectory'),
1702 environment: anyNamed('environment')))
1703 .thenAnswer((_) {
1704 return Future<Process>.value(
1705 createMockProcess(
1706 exitCode: 1,
1707 )
1708 );
1709 });
1710
1711 await expectLater(() async {
1712 await buildGradleApp(
1713 project: FlutterProject.current(),
1714 androidBuildInfo: const AndroidBuildInfo(
1715 BuildInfo(
1716 BuildMode.release,
1717 null,
1718 ),
1719 ),
1720 target: 'lib/main.dart',
1721 isBuildingBundle: false,
1722 localGradleErrors: const <GradleHandledError>[],
1723 );
1724 }, throwsToolExit());
1725
1726 final List<String> actualGradlewCall = verify(
1727 mockProcessManager.start(
1728 captureAny,
1729 environment: anyNamed('environment'),
1730 workingDirectory: anyNamed('workingDirectory')
1731 ),
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001732 ).captured.last as List<String>;
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001733
1734 expect(actualGradlewCall, contains('/android/gradlew'));
1735 expect(actualGradlewCall, contains('-Plocal-engine-out=out/android_arm'));
1736 expect(actualGradlewCall, contains('-Plocal-engine-repo=/.tmp_rand0/flutter_tool_local_engine_repo.rand0'));
1737 expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
1738
1739 }, overrides: <Type, Generator>{
1740 AndroidSdk: () => mockAndroidSdk,
1741 AndroidStudio: () => mockAndroidStudio,
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001742 Artifacts: () => mockArtifacts,
1743 Cache: () => cache,
1744 Platform: () => android,
1745 FileSystem: () => fs,
1746 ProcessManager: () => mockProcessManager,
1747 });
1748
1749 testUsingContext('build aar uses selected local engine', () async {
1750 when(mockArtifacts.getArtifactPath(Artifact.flutterFramework,
1751 platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine');
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001752 when(mockArtifacts.engineOutPath).thenReturn(globals.fs.path.join('out', 'android_arm'));
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001753
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001754 globals.fs.file('out/android_arm/flutter_embedding_release.pom')
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001755 ..createSync(recursive: true)
1756 ..writeAsStringSync(
1757'''<?xml version="1.0" encoding="UTF-8"?>
1758<project>
1759 <version>1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b</version>
1760 <dependencies>
1761 </dependencies>
1762</project>
1763''');
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001764 globals.fs.file('out/android_arm/armeabi_v7a_release.pom').createSync(recursive: true);
1765 globals.fs.file('out/android_arm/armeabi_v7a_release.jar').createSync(recursive: true);
1766 globals.fs.file('out/android_arm/flutter_embedding_release.jar').createSync(recursive: true);
1767 globals.fs.file('out/android_arm/flutter_embedding_release.pom').createSync(recursive: true);
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001768
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001769 final File manifestFile = globals.fs.file('pubspec.yaml');
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001770 manifestFile.createSync(recursive: true);
1771 manifestFile.writeAsStringSync('''
1772 flutter:
1773 module:
1774 androidPackage: com.example.test
1775 '''
1776 );
1777
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001778 globals.fs.file('.android/gradlew').createSync(recursive: true);
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001779
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001780 globals.fs.file('.android/gradle.properties')
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001781 .writeAsStringSync('irrelevant');
1782
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001783 globals.fs.file('.android/build.gradle')
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001784 .createSync(recursive: true);
1785
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001786 // Let any process start. Assert after.
1787 when(mockProcessManager.run(
1788 any,
1789 environment: anyNamed('environment'),
1790 workingDirectory: anyNamed('workingDirectory'),
1791 )).thenAnswer((_) async => ProcessResult(1, 0, '', ''));
1792
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001793 globals.fs.directory('build/outputs/repo').createSync(recursive: true);
xster46406902019-08-27 23:46:33 -07001794
1795 await buildGradleAar(
1796 androidBuildInfo: const AndroidBuildInfo(BuildInfo(BuildMode.release, null)),
1797 project: FlutterProject.current(),
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001798 outputDirectory: globals.fs.directory('build/'),
Alexandre Ardhuin89427d62019-09-24 08:06:09 +02001799 target: '',
Sean Freiburg418b8142019-12-12 10:28:01 -08001800 buildNumber: '2.0',
xster46406902019-08-27 23:46:33 -07001801 );
1802
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001803 final List<String> actualGradlewCall = verify(
1804 mockProcessManager.run(
1805 captureAny,
1806 environment: anyNamed('environment'),
1807 workingDirectory: anyNamed('workingDirectory'),
1808 ),
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001809 ).captured.last as List<String>;
xster46406902019-08-27 23:46:33 -07001810
Emmanuel Garcia76957c92019-11-04 16:28:27 -08001811 expect(actualGradlewCall, contains('/.android/gradlew'));
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001812 expect(actualGradlewCall, contains('-Plocal-engine-out=out/android_arm'));
1813 expect(actualGradlewCall, contains('-Plocal-engine-repo=/.tmp_rand0/flutter_tool_local_engine_repo.rand0'));
1814 expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
Sean Freiburg418b8142019-12-12 10:28:01 -08001815 expect(actualGradlewCall, contains('-PbuildNumber=2.0'));
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08001816
xster46406902019-08-27 23:46:33 -07001817 }, overrides: <Type, Generator>{
Alexandre Ardhuinf11c3412019-09-27 10:46:45 +02001818 AndroidSdk: () => mockAndroidSdk,
1819 AndroidStudio: () => mockAndroidStudio,
1820 Artifacts: () => mockArtifacts,
1821 Cache: () => cache,
Alexandre Ardhuinf11c3412019-09-27 10:46:45 +02001822 Platform: () => android,
1823 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -07001824 ProcessManager: () => mockProcessManager,
Alexandre Ardhuinf11c3412019-09-27 10:46:45 +02001825 });
xster46406902019-08-27 23:46:33 -07001826 });
Emmanuel Garcia00195992019-11-13 13:27:10 -08001827
1828 group('printHowToConsumeAar', () {
1829 testUsingContext('stdout contains release, debug and profile', () async {
1830 printHowToConsumeAar(
1831 buildModes: const <String>{'release', 'debug', 'profile'},
1832 androidPackage: 'com.mycompany',
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001833 repoDirectory: globals.fs.directory('build/'),
Sean Freiburg418b8142019-12-12 10:28:01 -08001834 buildNumber: '2.2',
Emmanuel Garcia00195992019-11-13 13:27:10 -08001835 );
1836
Emmanuel Garcia00195992019-11-13 13:27:10 -08001837 expect(
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001838 testLogger.statusText,
Emmanuel Garcia00195992019-11-13 13:27:10 -08001839 contains(
1840 '\n'
1841 'Consuming the Module\n'
1842 ' 1. Open <host>/app/build.gradle\n'
1843 ' 2. Ensure you have the repositories configured, otherwise add them:\n'
1844 '\n'
1845 ' repositories {\n'
1846 ' maven {\n'
1847 ' url \'build/\'\n'
1848 ' }\n'
1849 ' maven {\n'
1850 ' url \'http://download.flutter.io\'\n'
1851 ' }\n'
1852 ' }\n'
1853 '\n'
1854 ' 3. Make the host app depend on the Flutter module:\n'
1855 '\n'
1856 ' dependencies {\n'
Sean Freiburg418b8142019-12-12 10:28:01 -08001857 ' releaseImplementation \'com.mycompany:flutter_release:2.2\n'
1858 ' debugImplementation \'com.mycompany:flutter_debug:2.2\n'
1859 ' profileImplementation \'com.mycompany:flutter_profile:2.2\n'
Emmanuel Garcia00195992019-11-13 13:27:10 -08001860 ' }\n'
1861 '\n'
1862 '\n'
1863 ' 4. Add the `profile` build type:\n'
1864 '\n'
1865 ' android {\n'
1866 ' buildTypes {\n'
1867 ' profile {\n'
1868 ' initWith debug\n'
1869 ' }\n'
1870 ' }\n'
1871 ' }\n'
1872 '\n'
1873 'To learn more, visit https://flutter.dev/go/build-aar\n'
1874 )
1875 );
1876 }, overrides: <Type, Generator>{
1877 FileSystem: () => MemoryFileSystem(),
1878 Platform: () => fakePlatform('android'),
1879 ProcessManager: () => FakeProcessManager.any(),
1880 });
1881
1882 testUsingContext('stdout contains release', () async {
1883 printHowToConsumeAar(
1884 buildModes: const <String>{'release'},
1885 androidPackage: 'com.mycompany',
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001886 repoDirectory: globals.fs.directory('build/'),
Emmanuel Garcia00195992019-11-13 13:27:10 -08001887 );
1888
Emmanuel Garcia00195992019-11-13 13:27:10 -08001889 expect(
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001890 testLogger.statusText,
Emmanuel Garcia00195992019-11-13 13:27:10 -08001891 contains(
1892 '\n'
1893 'Consuming the Module\n'
1894 ' 1. Open <host>/app/build.gradle\n'
1895 ' 2. Ensure you have the repositories configured, otherwise add them:\n'
1896 '\n'
1897 ' repositories {\n'
1898 ' maven {\n'
1899 ' url \'build/\'\n'
1900 ' }\n'
1901 ' maven {\n'
1902 ' url \'http://download.flutter.io\'\n'
1903 ' }\n'
1904 ' }\n'
1905 '\n'
1906 ' 3. Make the host app depend on the Flutter module:\n'
1907 '\n'
1908 ' dependencies {\n'
1909 ' releaseImplementation \'com.mycompany:flutter_release:1.0\n'
1910 ' }\n'
1911 '\n'
1912 'To learn more, visit https://flutter.dev/go/build-aar\n'
1913 )
1914 );
1915 }, overrides: <Type, Generator>{
1916 FileSystem: () => MemoryFileSystem(),
1917 Platform: () => fakePlatform('android'),
1918 ProcessManager: () => FakeProcessManager.any(),
1919 });
1920
1921 testUsingContext('stdout contains debug', () async {
1922 printHowToConsumeAar(
1923 buildModes: const <String>{'debug'},
1924 androidPackage: 'com.mycompany',
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001925 repoDirectory: globals.fs.directory('build/'),
Emmanuel Garcia00195992019-11-13 13:27:10 -08001926 );
1927
Emmanuel Garcia00195992019-11-13 13:27:10 -08001928 expect(
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001929 testLogger.statusText,
Emmanuel Garcia00195992019-11-13 13:27:10 -08001930 contains(
1931 '\n'
1932 'Consuming the Module\n'
1933 ' 1. Open <host>/app/build.gradle\n'
1934 ' 2. Ensure you have the repositories configured, otherwise add them:\n'
1935 '\n'
1936 ' repositories {\n'
1937 ' maven {\n'
1938 ' url \'build/\'\n'
1939 ' }\n'
1940 ' maven {\n'
1941 ' url \'http://download.flutter.io\'\n'
1942 ' }\n'
1943 ' }\n'
1944 '\n'
1945 ' 3. Make the host app depend on the Flutter module:\n'
1946 '\n'
1947 ' dependencies {\n'
1948 ' debugImplementation \'com.mycompany:flutter_debug:1.0\n'
1949 ' }\n'
1950 '\n'
1951 'To learn more, visit https://flutter.dev/go/build-aar\n'
1952 )
1953 );
1954 }, overrides: <Type, Generator>{
1955 FileSystem: () => MemoryFileSystem(),
1956 Platform: () => fakePlatform('android'),
1957 ProcessManager: () => FakeProcessManager.any(),
1958 });
1959
1960 testUsingContext('stdout contains profile', () async {
1961 printHowToConsumeAar(
1962 buildModes: const <String>{'profile'},
1963 androidPackage: 'com.mycompany',
Jonah Williamsee7a37f2020-01-06 11:04:20 -08001964 repoDirectory: globals.fs.directory('build/'),
Sean Freiburg418b8142019-12-12 10:28:01 -08001965 buildNumber: '1.0',
Emmanuel Garcia00195992019-11-13 13:27:10 -08001966 );
1967
Emmanuel Garcia00195992019-11-13 13:27:10 -08001968 expect(
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01001969 testLogger.statusText,
Emmanuel Garcia00195992019-11-13 13:27:10 -08001970 contains(
1971 '\n'
1972 'Consuming the Module\n'
1973 ' 1. Open <host>/app/build.gradle\n'
1974 ' 2. Ensure you have the repositories configured, otherwise add them:\n'
1975 '\n'
1976 ' repositories {\n'
1977 ' maven {\n'
1978 ' url \'build/\'\n'
1979 ' }\n'
1980 ' maven {\n'
1981 ' url \'http://download.flutter.io\'\n'
1982 ' }\n'
1983 ' }\n'
1984 '\n'
1985 ' 3. Make the host app depend on the Flutter module:\n'
1986 '\n'
1987 ' dependencies {\n'
1988 ' profileImplementation \'com.mycompany:flutter_profile:1.0\n'
1989 ' }\n'
1990 '\n'
1991 '\n'
1992 ' 4. Add the `profile` build type:\n'
1993 '\n'
1994 ' android {\n'
1995 ' buildTypes {\n'
1996 ' profile {\n'
1997 ' initWith debug\n'
1998 ' }\n'
1999 ' }\n'
2000 ' }\n'
2001 '\n'
2002 'To learn more, visit https://flutter.dev/go/build-aar\n'
2003 )
2004 );
2005 }, overrides: <Type, Generator>{
2006 FileSystem: () => MemoryFileSystem(),
2007 Platform: () => fakePlatform('android'),
2008 ProcessManager: () => FakeProcessManager.any(),
2009 });
2010 });
Jason Simmonscfa0a2d2017-06-19 09:37:39 -07002011}
Ralph Bergmann779eea62018-07-09 19:36:17 +02002012
Emmanuel Garcia5353d5e2019-08-27 17:08:02 -07002013/// Generates a fake app bundle at the location [directoryName]/[fileName].
Emmanuel Garcia175b3722019-10-31 13:19:15 -07002014FlutterProject generateFakeAppBundle(String directoryName, String fileName) {
2015 final FlutterProject project = MockFlutterProject();
2016 final AndroidProject androidProject = MockAndroidProject();
Emmanuel Garcia5353d5e2019-08-27 17:08:02 -07002017
Emmanuel Garcia175b3722019-10-31 13:19:15 -07002018 when(project.isModule).thenReturn(false);
2019 when(project.android).thenReturn(androidProject);
Jonah Williamsee7a37f2020-01-06 11:04:20 -08002020 when(androidProject.buildDirectory).thenReturn(globals.fs.directory('irrelevant'));
Emmanuel Garcia175b3722019-10-31 13:19:15 -07002021
2022 final Directory bundleDirectory = getBundleDirectory(project);
2023 bundleDirectory
2024 .childDirectory(directoryName)
2025 ..createSync(recursive: true);
2026
2027 bundleDirectory
2028 .childDirectory(directoryName)
2029 .childFile(fileName)
2030 .createSync();
2031 return project;
Emmanuel Garcia5353d5e2019-08-27 17:08:02 -07002032}
2033
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01002034FakePlatform fakePlatform(String name) {
Emmanuel Garciabb8cf602019-11-06 12:52:37 -08002035 return FakePlatform.fromPlatform(const LocalPlatform())
2036 ..operatingSystem = name
2037 ..stdoutSupportsAnsi = false;
Ralph Bergmann779eea62018-07-09 19:36:17 +02002038}
2039
Emmanuel Garcia16d408a2019-09-24 16:16:22 -07002040class FakeGradleUtils extends GradleUtils {
2041 @override
Emmanuel Garcia175b3722019-10-31 13:19:15 -07002042 String getExecutable(FlutterProject project) {
Emmanuel Garcia16d408a2019-09-24 16:16:22 -07002043 return 'gradlew';
2044 }
2045}
2046
2047class MockAndroidSdk extends Mock implements AndroidSdk {}
Emmanuel Garcia175b3722019-10-31 13:19:15 -07002048class MockAndroidProject extends Mock implements AndroidProject {}
Emmanuel Garcia2c857b92019-09-13 19:06:40 -07002049class MockAndroidStudio extends Mock implements AndroidStudio {}
2050class MockDirectory extends Mock implements Directory {}
2051class MockFile extends Mock implements File {}
Emmanuel Garcia175b3722019-10-31 13:19:15 -07002052class MockFlutterProject extends Mock implements FlutterProject {}
Ralph Bergmann779eea62018-07-09 19:36:17 +02002053class MockLocalEngineArtifacts extends Mock implements LocalEngineArtifacts {}
2054class MockProcessManager extends Mock implements ProcessManager {}
2055class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
xster46406902019-08-27 23:46:33 -07002056class MockitoAndroidSdk extends Mock implements AndroidSdk {}
Emmanuel Garcia175b3722019-10-31 13:19:15 -07002057class MockUsage extends Mock implements Usage {}