1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:file/memory.dart';
6import 'package:file_testing/file_testing.dart';
7import 'package:flutter_tools/src/android/gradle_errors.dart';
8import 'package:flutter_tools/src/android/gradle_utils.dart';
9import 'package:flutter_tools/src/android/java.dart';
10import 'package:flutter_tools/src/base/bot_detector.dart';
11import 'package:flutter_tools/src/base/file_system.dart';
12import 'package:flutter_tools/src/base/logger.dart';
13import 'package:flutter_tools/src/base/platform.dart';
14import 'package:flutter_tools/src/base/terminal.dart';
15import 'package:flutter_tools/src/project.dart';
16import 'package:test/fake.dart';
17
18import '../../src/common.dart';
19import '../../src/context.dart';
20import '../../src/fake_process_manager.dart';
21import '../../src/fakes.dart';
22
23void main() {
24 late FileSystem fileSystem;
25 late FakeProcessManager processManager;
26
27 setUp(() {
28 fileSystem = MemoryFileSystem.test();
29 processManager = FakeProcessManager.empty();
30 });
31
32 group('gradleErrors', () {
33 testWithoutContext('list of errors', () {
34 // If you added a new Gradle error, please update this test.
35 expect(
36 gradleErrors,
37 equals(<GradleHandledError>[
38 licenseNotAcceptedHandler,
39 networkErrorHandler,
40 permissionDeniedErrorHandler,
41 flavorUndefinedHandler,
42 r8DexingBugInAgp73Handler,
43 minSdkVersionHandler,
44 transformInputIssueHandler,
45 lockFileDepMissingHandler,
46 minCompileSdkVersionHandler,
47 incompatibleJavaAndAgpVersionsHandler,
48 outdatedGradleHandler,
49 sslExceptionHandler,
50 zipExceptionHandler,
51 incompatibleJavaAndGradleVersionsHandler,
52 remoteTerminatedHandshakeHandler,
53 couldNotOpenCacheDirectoryHandler,
54 incompatibleCompileSdk35AndAgpVersionHandler,
55 usageOfV1EmbeddingReferencesHandler,
56 jlinkErrorWithJava21AndSourceCompatibility,
57 incompatibleKotlinVersionHandler,
58 ]),
59 );
60 });
61 });
62
63 group('network errors', () {
64 testUsingContext(
65 'retries if gradle fails while downloading',
66 () async {
67 const String errorMessage = r'''
68Exception in thread "main" java.io.FileNotFoundException: https://downloads.gradle.org/distributions/gradle-4.1.1-all.zip
69at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1872)
70at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
71at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
72at org.gradle.wrapper.Download.downloadInternal(Download.java:58)
73at org.gradle.wrapper.Download.download(Download.java:44)
74at org.gradle.wrapper.Install$1.call(Install.java:61)
75at org.gradle.wrapper.Install$1.call(Install.java:48)
76at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
77at org.gradle.wrapper.Install.createDist(Install.java:48)
78at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
79at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
80
81 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
82 expect(
83 await networkErrorHandler.handler(
84 line: '',
85 project: FakeFlutterProject(),
86 usesAndroidX: true,
87 ),
88 equals(GradleBuildStatus.retry),
89 );
90
91 expect(
92 testLogger.errorText,
93 contains('Gradle threw an error while downloading artifacts from the network.'),
94 );
95 },
96 overrides: <Type, Generator>{
97 FileSystem: () => fileSystem,
98 ProcessManager: () => processManager,
99 },
100 );
101
102 testUsingContext('retries if remote host terminated ssl handshake', () async {
103 const String errorMessage = r'''
104Exception in thread "main" javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
105 at java.base/sun.security.ssl.SSLSocketImpl.handleEOF(SSLSocketImpl.java:1696)
106 at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1514)
107 at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1416)
108 at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:456)
109 at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
110 at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:572)
111 at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:197)
112 at java.base/sun.net.www.protocol.http.HttpURLConnection.followRedirect0(HttpURLConnection.java:2783)
113 at java.base/sun.net.www.protocol.http.HttpURLConnection.followRedirect(HttpURLConnection.java:2695)
114 at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1854)
115 at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1520)
116 at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:250)
117 at org.gradle.wrapper.Download.downloadInternal(Download.java:58)
118 at org.gradle.wrapper.Download.download(Download.java:44)
119 at org.gradle.wrapper.Install$1.call(Install.java:61)
120 at org.gradle.wrapper.Install$1.call(Install.java:48)
121 at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
122 at org.gradle.wrapper.Install.createDist(Install.java:48)
123 at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
124 at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)
125Caused by: java.io.EOFException: SSL peer shut down incorrectly
126 at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:483)
127 at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:472)
128 at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:160)
129 at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:111)
130 at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506)''';
131
132 expect(formatTestErrorMessage(errorMessage, remoteTerminatedHandshakeHandler), isTrue);
133 expect(
134 await remoteTerminatedHandshakeHandler.handler(
135 line: '',
136 project: FakeFlutterProject(),
137 usesAndroidX: true,
138 ),
139 equals(GradleBuildStatus.retry),
140 );
141
142 expect(
143 testLogger.errorText,
144 contains('Gradle threw an error while downloading artifacts from the network.'),
145 );
146 });
147
148 testUsingContext(
149 'retries if gradle fails downloading with proxy error',
150 () async {
151 const String errorMessage = r'''
152Exception in thread "main" java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 400 Bad Request"
153at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:2124)
154at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:183)
155at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1546)
156at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
157at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
158at org.gradle.wrapper.Download.downloadInternal(Download.java:58)
159at org.gradle.wrapper.Download.download(Download.java:44)
160at org.gradle.wrapper.Install$1.call(Install.java:61)
161at org.gradle.wrapper.Install$1.call(Install.java:48)
162at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
163at org.gradle.wrapper.Install.createDist(Install.java:48)
164at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
165at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
166
167 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
168 expect(
169 await networkErrorHandler.handler(
170 line: '',
171 project: FakeFlutterProject(),
172 usesAndroidX: true,
173 ),
174 equals(GradleBuildStatus.retry),
175 );
176
177 expect(
178 testLogger.errorText,
179 contains('Gradle threw an error while downloading artifacts from the network.'),
180 );
181 },
182 overrides: <Type, Generator>{
183 FileSystem: () => fileSystem,
184 ProcessManager: () => processManager,
185 },
186 );
187
188 testUsingContext(
189 'retries if gradle fails downloading with bad gateway error',
190 () async {
191 const String errorMessage = r'''
192Exception in thread "main" java.io.IOException: Server returned HTTP response code: 502 for URL: https://objects.githubusercontent.com/github-production-release-asset-2e65be/696192900/1e77bbfb-4cde-4376-92ea-fc4ff57b8362?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=FFFF%2F20231220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231220T160553Z&X-Amz-Expires=300&X-Amz-Signature=ffff&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=696192900&response-content-disposition=attachment%3B%20filename%3Dgradle-8.2.1-all.zip&response-content-type=application%2Foctet-stream
193at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1997)
194at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
195at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:224)
196at org.gradle.wrapper.Download.downloadInternal(Download.java:58)
197at org.gradle.wrapper.Download.download(Download.java:44)
198at org.gradle.wrapper.Install$1.call(Install.java:61)
199at org.gradle.wrapper.Install$1.call(Install.java:48)
200at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
201at org.gradle.wrapper.Install.createDist(Install.java:48)
202at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
203at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
204
205 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
206 expect(
207 await networkErrorHandler.handler(
208 line: '',
209 project: FakeFlutterProject(),
210 usesAndroidX: true,
211 ),
212 equals(GradleBuildStatus.retry),
213 );
214
215 expect(
216 testLogger.errorText,
217 contains('Gradle threw an error while downloading artifacts from the network.'),
218 );
219 },
220 overrides: <Type, Generator>{
221 FileSystem: () => fileSystem,
222 ProcessManager: () => processManager,
223 },
224 );
225
226 testUsingContext(
227 'retries if gradle times out waiting for exclusive access to zip',
228 () async {
229 const String errorMessage = '''
230Exception in thread "main" java.lang.RuntimeException: Timeout of 120000 reached waiting for exclusive access to file: /User/documents/gradle-5.6.2-all.zip
231 at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:61)
232 at org.gradle.wrapper.Install.createDist(Install.java:48)
233 at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
234 at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
235
236 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
237 expect(
238 await networkErrorHandler.handler(
239 line: '',
240 project: FakeFlutterProject(),
241 usesAndroidX: true,
242 ),
243 equals(GradleBuildStatus.retry),
244 );
245
246 expect(
247 testLogger.errorText,
248 contains('Gradle threw an error while downloading artifacts from the network.'),
249 );
250 },
251 overrides: <Type, Generator>{
252 FileSystem: () => fileSystem,
253 ProcessManager: () => processManager,
254 },
255 );
256
257 testUsingContext(
258 'retries if remote host closes connection',
259 () async {
260 const String errorMessage = r'''
261Downloading https://services.gradle.org/distributions/gradle-5.6.2-all.zip
262Exception in thread "main" javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
263 at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:994)
264 at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
265 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
266 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
267 at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
268 at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
269 at sun.net.www.protocol.http.HttpURLConnection.followRedirect0(HttpURLConnection.java:2729)
270 at sun.net.www.protocol.http.HttpURLConnection.followRedirect(HttpURLConnection.java:2641)
271 at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1824)
272 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
273 at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263)
274 at org.gradle.wrapper.Download.downloadInternal(Download.java:58)
275 at org.gradle.wrapper.Download.download(Download.java:44)
276 at org.gradle.wrapper.Install$1.call(Install.java:61)
277 at org.gradle.wrapper.Install$1.call(Install.java:48)
278 at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
279 at org.gradle.wrapper.Install.createDist(Install.java:48)
280 at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
281 at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
282
283 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
284 expect(
285 await networkErrorHandler.handler(
286 line: '',
287 project: FakeFlutterProject(),
288 usesAndroidX: true,
289 ),
290 equals(GradleBuildStatus.retry),
291 );
292
293 expect(
294 testLogger.errorText,
295 contains('Gradle threw an error while downloading artifacts from the network.'),
296 );
297 },
298 overrides: <Type, Generator>{
299 FileSystem: () => fileSystem,
300 ProcessManager: () => processManager,
301 },
302 );
303
304 testUsingContext(
305 'retries if file opening fails',
306 () async {
307 const String errorMessage = r'''
308Downloading https://services.gradle.org/distributions/gradle-3.5.0-all.zip
309Exception in thread "main" java.io.FileNotFoundException: https://downloads.gradle-dn.com/distributions/gradle-3.5.0-all.zip
310 at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1890)
311 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
312 at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263)
313 at org.gradle.wrapper.Download.downloadInternal(Download.java:58)
314 at org.gradle.wrapper.Download.download(Download.java:44)
315 at org.gradle.wrapper.Install$1.call(Install.java:61)
316 at org.gradle.wrapper.Install$1.call(Install.java:48)
317 at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
318 at org.gradle.wrapper.Install.createDist(Install.java:48)
319 at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
320 at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
321
322 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
323 expect(
324 await networkErrorHandler.handler(
325 line: '',
326 project: FakeFlutterProject(),
327 usesAndroidX: true,
328 ),
329 equals(GradleBuildStatus.retry),
330 );
331
332 expect(
333 testLogger.errorText,
334 contains('Gradle threw an error while downloading artifacts from the network.'),
335 );
336 },
337 overrides: <Type, Generator>{
338 FileSystem: () => fileSystem,
339 ProcessManager: () => processManager,
340 },
341 );
342
343 testUsingContext(
344 'retries if the connection is reset',
345 () async {
346 const String errorMessage = r'''
347Downloading https://services.gradle.org/distributions/gradle-5.6.2-all.zip
348Exception in thread "main" java.net.SocketException: Connection reset
349 at java.net.SocketInputStream.read(SocketInputStream.java:210)
350 at java.net.SocketInputStream.read(SocketInputStream.java:141)
351 at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
352 at sun.security.ssl.InputRecord.readV3Record(InputRecord.java:593)
353 at sun.security.ssl.InputRecord.read(InputRecord.java:532)
354 at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:975)
355 at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
356 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
357 at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
358 at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
359 at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
360 at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1564)
361 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
362 at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263)
363 at org.gradle.wrapper.Download.downloadInternal(Download.java:58)
364 at org.gradle.wrapper.Download.download(Download.java:44)
365 at org.gradle.wrapper.Install$1.call(Install.java:61)
366 at org.gradle.wrapper.Install$1.call(Install.java:48)
367 at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
368 at org.gradle.wrapper.Install.createDist(Install.java:48)
369 at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
370 at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
371
372 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
373 expect(
374 await networkErrorHandler.handler(
375 line: '',
376 project: FakeFlutterProject(),
377 usesAndroidX: true,
378 ),
379 equals(GradleBuildStatus.retry),
380 );
381
382 expect(
383 testLogger.errorText,
384 contains('Gradle threw an error while downloading artifacts from the network.'),
385 );
386 },
387 overrides: <Type, Generator>{
388 FileSystem: () => fileSystem,
389 ProcessManager: () => processManager,
390 },
391 );
392
393 testUsingContext(
394 'retries if Gradle could not get a resource',
395 () async {
396 const String errorMessage = '''
397A problem occurred configuring root project 'android'.
398> Could not resolve all artifacts for configuration ':classpath'.
399 > Could not resolve net.sf.proguard:proguard-gradle:6.0.3.
400 Required by:
401 project : > com.android.tools.build:gradle:3.3.0
402 > Could not resolve net.sf.proguard:proguard-gradle:6.0.3.
403 > Could not parse POM https://jcenter.bintray.com/net/sf/proguard/proguard-gradle/6.0.3/proguard-gradle-6.0.3.pom
404 > Could not resolve net.sf.proguard:proguard-parent:6.0.3.
405 > Could not resolve net.sf.proguard:proguard-parent:6.0.3.
406 > Could not get resource 'https://jcenter.bintray.com/net/sf/proguard/proguard-parent/6.0.3/proguard-parent-6.0.3.pom'.
407 > Could not GET 'https://jcenter.bintray.com/net/sf/proguard/proguard-parent/6.0.3/proguard-parent-6.0.3.pom'. Received status code 504 from server: Gateway Time-out''';
408
409 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
410 expect(
411 await networkErrorHandler.handler(
412 line: '',
413 project: FakeFlutterProject(),
414 usesAndroidX: true,
415 ),
416 equals(GradleBuildStatus.retry),
417 );
418
419 expect(
420 testLogger.errorText,
421 contains('Gradle threw an error while downloading artifacts from the network.'),
422 );
423 },
424 overrides: <Type, Generator>{
425 FileSystem: () => fileSystem,
426 ProcessManager: () => processManager,
427 },
428 );
429
430 testUsingContext(
431 'retries if Gradle could not get a resource (non-Gateway)',
432 () async {
433 const String errorMessage = '''
434* Error running Gradle:
435Exit code 1 from: /home/travis/build/flutter/flutter sdk/examples/flutter_gallery/android/gradlew app:properties:
436Starting a Gradle Daemon (subsequent builds will be faster)
437Picked up _JAVA_OPTIONS: -Xmx2048m -Xms512m
438FAILURE: Build failed with an exception.
439* What went wrong:
440A problem occurred configuring root project 'android'.
441> Could not resolve all files for configuration ':classpath'.
442 > Could not resolve com.android.tools.build:gradle:3.1.2.
443 Required by:
444 project :
445 > Could not resolve com.android.tools.build:gradle:3.1.2.
446 > Could not get resource 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.1.2/gradle-3.1.2.pom'.
447 > Could not GET 'https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/3.1.2/gradle-3.1.2.pom'.
448 > Remote host closed connection during handshake''';
449
450 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
451 expect(
452 await networkErrorHandler.handler(
453 line: '',
454 project: FakeFlutterProject(),
455 usesAndroidX: true,
456 ),
457 equals(GradleBuildStatus.retry),
458 );
459
460 expect(
461 testLogger.errorText,
462 contains('Gradle threw an error while downloading artifacts from the network.'),
463 );
464 },
465 overrides: <Type, Generator>{
466 FileSystem: () => fileSystem,
467 ProcessManager: () => processManager,
468 },
469 );
470
471 testUsingContext(
472 'retries if connection times out',
473 () async {
474 const String errorMessage = r'''
475Exception in thread "main" java.net.ConnectException: Connection timed out
476java.base/sun.nio.ch.Net.connect0(Native Method)
477 at java.base/sun.nio.ch.Net.connect(Net.java:579)
478 at java.base/sun.nio.ch.Net.connect(Net.java:568)
479 at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:588)
480 at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
481 at java.base/java.net.Socket.connect(Socket.java:633)
482 at java.base/sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:299)
483 at java.base/sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:174)
484 at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:183)
485 at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:498)
486 at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:603)
487 at java.base/sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:266)
488 at java.base/sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:380)''';
489
490 expect(formatTestErrorMessage(errorMessage, networkErrorHandler), isTrue);
491 expect(
492 await networkErrorHandler.handler(
493 line: '',
494 project: FakeFlutterProject(),
495 usesAndroidX: true,
496 ),
497 equals(GradleBuildStatus.retry),
498 );
499
500 expect(
501 testLogger.errorText,
502 contains('Gradle threw an error while downloading artifacts from the network.'),
503 );
504 },
505 overrides: <Type, Generator>{
506 FileSystem: () => fileSystem,
507 ProcessManager: () => processManager,
508 },
509 );
510 });
511
512 group('permission errors', () {
513 testUsingContext('throws toolExit if gradle is missing execute permissions', () async {
514 const String errorMessage = '''
515Permission denied
516Command: /home/android/gradlew assembleRelease
517''';
518 expect(formatTestErrorMessage(errorMessage, permissionDeniedErrorHandler), isTrue);
519 expect(
520 await permissionDeniedErrorHandler.handler(
521 usesAndroidX: true,
522 line: '',
523 project: FakeFlutterProject(),
524 ),
525 equals(GradleBuildStatus.exit),
526 );
527
528 expect(testLogger.statusText, contains('Gradle does not have execution permission.'));
529 expect(
530 testLogger.statusText,
531 contains(
532 '\n'
533 '┌─ Flutter Fix ───────────────────────────────────────────────────────────────────────────────────┐\n'
534 '│ [!] Gradle does not have execution permission. │\n'
535 '│ You should change the ownership of the project directory to your user, or move the project to a │\n'
536 '│ directory with execute permissions. │\n'
537 '└─────────────────────────────────────────────────────────────────────────────────────────────────┘\n',
538 ),
539 );
540 });
541
542 testUsingContext('pattern', () async {
543 const String errorMessage = '''
544Permission denied
545Command: /home/android/gradlew assembleRelease
546''';
547 expect(formatTestErrorMessage(errorMessage, permissionDeniedErrorHandler), isTrue);
548 });
549
550 testUsingContext('handler', () async {
551 expect(
552 await permissionDeniedErrorHandler.handler(
553 usesAndroidX: true,
554 line: '',
555 project: FakeFlutterProject(),
556 ),
557 equals(GradleBuildStatus.exit),
558 );
559
560 expect(testLogger.statusText, contains('Gradle does not have execution permission.'));
561 expect(
562 testLogger.statusText,
563 contains(
564 '\n'
565 '┌─ Flutter Fix ───────────────────────────────────────────────────────────────────────────────────┐\n'
566 '│ [!] Gradle does not have execution permission. │\n'
567 '│ You should change the ownership of the project directory to your user, or move the project to a │\n'
568 '│ directory with execute permissions. │\n'
569 '└─────────────────────────────────────────────────────────────────────────────────────────────────┘\n',
570 ),
571 );
572 });
573 });
574
575 group('license not accepted', () {
576 testWithoutContext('pattern', () {
577 expect(
578 licenseNotAcceptedHandler.test(
579 'You have not accepted the license agreements of the following SDK components',
580 ),
581 isTrue,
582 );
583 });
584
585 testUsingContext('handler', () async {
586 await licenseNotAcceptedHandler.handler(
587 line:
588 'You have not accepted the license agreements of the following SDK components: [foo, bar]',
589 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
590 usesAndroidX: true,
591 );
592
593 expect(
594 testLogger.statusText,
595 contains(
596 '\n'
597 '┌─ Flutter Fix ─────────────────────────────────────────────────────────────────────────────────┐\n'
598 '│ [!] Unable to download needed Android SDK components, as the following licenses have not been │\n'
599 '│ accepted: foo, bar │\n'
600 '│ │\n'
601 '│ To resolve this, please run the following command in a Terminal: │\n'
602 '│ flutter doctor --android-licenses │\n'
603 '└───────────────────────────────────────────────────────────────────────────────────────────────┘\n',
604 ),
605 );
606 });
607 });
608
609 group('flavor undefined', () {
610 testWithoutContext('pattern', () {
611 expect(
612 flavorUndefinedHandler.test('Task assembleFooRelease not found in root project.'),
613 isTrue,
614 );
615 expect(
616 flavorUndefinedHandler.test('Task assembleBarRelease not found in root project.'),
617 isTrue,
618 );
619 expect(flavorUndefinedHandler.test('Task assembleBar not found in root project.'), isTrue);
620 expect(
621 flavorUndefinedHandler.test('Task assembleBar_foo not found in root project.'),
622 isTrue,
623 );
624 });
625
626 testUsingContext(
627 'handler - with flavor',
628 () async {
629 processManager.addCommand(
630 const FakeCommand(
631 command: <String>['gradlew', 'app:tasks', '--all', '--console=auto'],
632 stdout: '''
633assembleRelease
634assembleFlavor1
635assembleFlavor1Release
636assembleFlavor_2
637assembleFlavor_2Release
638assembleDebug
639assembleProfile
640assembles
641assembleFooTest
642 ''',
643 ),
644 );
645
646 await flavorUndefinedHandler.handler(
647 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
648 usesAndroidX: true,
649 line: '',
650 );
651
652 expect(
653 testLogger.statusText,
654 contains(
655 'Gradle project does not define a task suitable '
656 'for the requested build.',
657 ),
658 );
659 expect(
660 testLogger.statusText,
661 contains(
662 '\n'
663 '┌─ Flutter Fix ───────────────────────────────────────────────────────────────────────────────────┐\n'
664 '│ [!] Gradle project does not define a task suitable for the requested build. │\n'
665 '│ │\n'
666 '│ The /android/app/build.gradle file defines product flavors: flavor1, flavor_2. You must specify │\n'
667 '│ a --flavor option to select one of them. │\n'
668 '└─────────────────────────────────────────────────────────────────────────────────────────────────┘\n',
669 ),
670 );
671 expect(processManager, hasNoRemainingExpectations);
672 },
673 overrides: <Type, Generator>{
674 Java: () => FakeJava(),
675 GradleUtils: () => FakeGradleUtils(),
676 Platform: () => fakePlatform('android'),
677 FileSystem: () => fileSystem,
678 ProcessManager: () => processManager,
679 },
680 );
681
682 testUsingContext(
683 'handler - without flavor',
684 () async {
685 processManager.addCommand(
686 const FakeCommand(
687 command: <String>['gradlew', 'app:tasks', '--all', '--console=auto'],
688 stdout: '''
689assembleRelease
690assembleDebug
691assembleProfile
692 ''',
693 ),
694 );
695
696 await flavorUndefinedHandler.handler(
697 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
698 usesAndroidX: true,
699 line: '',
700 );
701
702 expect(
703 testLogger.statusText,
704 contains(
705 '\n'
706 '┌─ Flutter Fix ─────────────────────────────────────────────────────────────────────────────────┐\n'
707 '│ [!] Gradle project does not define a task suitable for the requested build. │\n'
708 '│ │\n'
709 '│ The /android/app/build.gradle file does not define any custom product flavors. You cannot use │\n'
710 '│ the --flavor option. │\n'
711 '└───────────────────────────────────────────────────────────────────────────────────────────────┘\n',
712 ),
713 );
714 expect(processManager, hasNoRemainingExpectations);
715 },
716 overrides: <Type, Generator>{
717 Java: () => FakeJava(),
718 GradleUtils: () => FakeGradleUtils(),
719 Platform: () => fakePlatform('android'),
720 FileSystem: () => fileSystem,
721 ProcessManager: () => processManager,
722 },
723 );
724 });
725
726 group('higher minSdkVersion', () {
727 const String stdoutLine =
728 'uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared in library [:webview_flutter] /tmp/cirrus-ci-build/all_plugins/build/webview_flutter/intermediates/library_manifest/release/AndroidManifest.xml as the library might be using APIs not available in 21';
729
730 testWithoutContext('pattern', () {
731 expect(minSdkVersionHandler.test(stdoutLine), isTrue);
732 });
733
734 testUsingContext(
735 'suggestion',
736 () async {
737 await minSdkVersionHandler.handler(
738 line: stdoutLine,
739 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
740 usesAndroidX: true,
741 );
742
743 expect(
744 testLogger.statusText,
745 contains(
746 '\n'
747 '┌─ Flutter Fix ─────────────────────────────────────────────────────────────────────────────────┐\n'
748 '│ The plugin webview_flutter requires a higher Android SDK version. │\n'
749 '│ Fix this issue by adding the following to the file /android/app/build.gradle: │\n'
750 '│ android { │\n'
751 '│ defaultConfig { │\n'
752 '│ minSdkVersion 21 │\n'
753 '│ } │\n'
754 '│ } │\n'
755 '│ │\n'
756 '│ Following this change, your app will not be available to users running Android SDKs below 21. │\n'
757 '│ Consider searching for a version of this plugin that supports these lower versions of the │\n'
758 '│ Android SDK instead. │\n'
759 '│ For more information, see: https://flutter.dev/to/review-gradle-config │\n'
760 '└───────────────────────────────────────────────────────────────────────────────────────────────┘\n',
761 ),
762 );
763 },
764 overrides: <Type, Generator>{
765 GradleUtils: () => FakeGradleUtils(),
766 Platform: () => fakePlatform('android'),
767 FileSystem: () => fileSystem,
768 ProcessManager: () => processManager,
769 },
770 );
771 });
772
773 // https://issuetracker.google.com/issues/141126614
774 group('transform input issue', () {
775 testWithoutContext('pattern', () {
776 expect(
777 transformInputIssueHandler.test('https://issuetracker.google.com/issues/158753935'),
778 isTrue,
779 );
780 });
781
782 testUsingContext(
783 'suggestion',
784 () async {
785 await transformInputIssueHandler.handler(
786 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
787 usesAndroidX: true,
788 line: '',
789 );
790
791 expect(
792 testLogger.statusText,
793 contains(
794 '\n'
795 '┌─ Flutter Fix ─────────────────────────────────────────────────────────────────┐\n'
796 '│ This issue appears to be https://github.com/flutter/flutter/issues/58247. │\n'
797 '│ Fix this issue by adding the following to the file /android/app/build.gradle: │\n'
798 '│ android { │\n'
799 '│ lintOptions { │\n'
800 '│ checkReleaseBuilds false │\n'
801 '│ } │\n'
802 '│ } │\n'
803 '└───────────────────────────────────────────────────────────────────────────────┘\n',
804 ),
805 );
806 },
807 overrides: <Type, Generator>{
808 GradleUtils: () => FakeGradleUtils(),
809 Platform: () => fakePlatform('android'),
810 FileSystem: () => fileSystem,
811 ProcessManager: () => processManager,
812 },
813 );
814 });
815
816 group('Dependency mismatch', () {
817 testWithoutContext('pattern', () {
818 expect(
819 lockFileDepMissingHandler.test(
820 '''
821* What went wrong:
822Execution failed for task ':app:generateDebugFeatureTransitiveDeps'.
823> Could not resolve all artifacts for configuration ':app:debugRuntimeClasspath'.
824 > Resolved 'androidx.lifecycle:lifecycle-common:2.2.0' which is not part of the dependency lock state
825 > Resolved 'androidx.customview:customview:1.0.0' which is not part of the dependency lock state''',
826 ),
827 isTrue,
828 );
829 });
830
831 testUsingContext(
832 'suggestion',
833 () async {
834 await lockFileDepMissingHandler.handler(
835 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
836 usesAndroidX: true,
837 line: '',
838 );
839
840 expect(
841 testLogger.statusText,
842 contains(
843 '\n'
844 '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────┐\n'
845 '│ You need to update the lockfile, or disable Gradle dependency locking. │\n'
846 '│ To regenerate the lockfiles run: `./gradlew :generateLockfiles` in /android/build.gradle │\n'
847 '│ To remove dependency locking, remove the `dependencyLocking` from /android/build.gradle │\n'
848 '└──────────────────────────────────────────────────────────────────────────────────────────┘\n',
849 ),
850 );
851 },
852 overrides: <Type, Generator>{
853 GradleUtils: () => FakeGradleUtils(),
854 Platform: () => fakePlatform('android'),
855 FileSystem: () => fileSystem,
856 ProcessManager: () => processManager,
857 },
858 );
859 });
860
861 testUsingContext(
862 'generates correct gradle command for Unix-like environment',
863 () async {
864 await lockFileDepMissingHandler.handler(
865 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
866 usesAndroidX: true,
867 line: '',
868 );
869
870 expect(
871 testLogger.statusText,
872 contains(
873 '\n'
874 '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────┐\n'
875 '│ You need to update the lockfile, or disable Gradle dependency locking. │\n'
876 '│ To regenerate the lockfiles run: `./gradlew :generateLockfiles` in /android/build.gradle │\n'
877 '│ To remove dependency locking, remove the `dependencyLocking` from /android/build.gradle │\n'
878 '└──────────────────────────────────────────────────────────────────────────────────────────┘\n'
879 '',
880 ),
881 );
882 },
883 overrides: <Type, Generator>{
884 GradleUtils: () => FakeGradleUtils(),
885 Platform: () => fakePlatform('linux'),
886 FileSystem: () => fileSystem,
887 ProcessManager: () => processManager,
888 },
889 );
890
891 testUsingContext(
892 'generates correct gradle command for windows environment',
893 () async {
894 await lockFileDepMissingHandler.handler(
895 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
896 usesAndroidX: true,
897 line: '',
898 );
899 expect(
900 testLogger.statusText,
901 contains(
902 '\n'
903 '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────┐\n'
904 '│ You need to update the lockfile, or disable Gradle dependency locking. │\n'
905 '│ To regenerate the lockfiles run: `.\\gradlew.bat :generateLockfiles` in /android/build.gradle │\n'
906 '│ To remove dependency locking, remove the `dependencyLocking` from /android/build.gradle │\n'
907 '└──────────────────────────────────────────────────────────────────────────────────────────────┘\n'
908 '',
909 ),
910 );
911 },
912 overrides: <Type, Generator>{
913 GradleUtils: () => FakeGradleUtils(),
914 Platform: () => fakePlatform('windows'),
915 FileSystem: () => fileSystem,
916 ProcessManager: () => processManager,
917 },
918 );
919
920 group('Incompatible Kotlin version', () {
921 testWithoutContext('pattern', () {
922 expect(
923 incompatibleKotlinVersionHandler.test(
924 'Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.5.1, expected version is 1.1.15.',
925 ),
926 isTrue,
927 );
928 expect(
929 incompatibleKotlinVersionHandler.test(
930 "class 'kotlin.Unit' was compiled with an incompatible version of Kotlin.",
931 ),
932 isTrue,
933 );
934 });
935
936 testUsingContext(
937 'suggestion',
938 () async {
939 await incompatibleKotlinVersionHandler.handler(
940 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
941 usesAndroidX: true,
942 line: '',
943 );
944
945 expect(
946 testLogger.statusText,
947 contains(
948 '\n'
949 '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────┐\n'
950 '│ [!] Your project requires a newer version of the Kotlin Gradle plugin. │\n'
951 '│ Find the latest version on https://kotlinlang.org/docs/releases.html#release-details, then │\n'
952 '│ update the │\n'
953 '│ version number of the plugin with id "org.jetbrains.kotlin.android" in the plugins block of │\n'
954 '│ /android/settings.gradle. │\n'
955 '│ │\n'
956 '│ Alternatively (if your project was created before Flutter 3.19), update │\n'
957 '│ /android/build.gradle │\n'
958 "│ ext.kotlin_version = '<latest-version>' │\n"
959 '└──────────────────────────────────────────────────────────────────────────────────────────────┘\n',
960 ),
961 );
962 },
963 overrides: <Type, Generator>{
964 GradleUtils: () => FakeGradleUtils(),
965 Platform: () => fakePlatform('android'),
966 FileSystem: () => fileSystem,
967 ProcessManager: () => processManager,
968 },
969 );
970 });
971
972 group('Bump Gradle', () {
973 const String errorMessage = '''
974A problem occurred evaluating project ':app'.
975> Failed to apply plugin [id 'kotlin-android']
976 > The current Gradle version 4.10.2 is not compatible with the Kotlin Gradle plugin. Please use Gradle 6.1.1 or newer, or the previous version of the Kotlin plugin.
977''';
978
979 testWithoutContext('pattern', () {
980 expect(outdatedGradleHandler.test(errorMessage), isTrue);
981 });
982
983 testUsingContext(
984 'suggestion',
985 () async {
986 await outdatedGradleHandler.handler(
987 line: errorMessage,
988 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
989 usesAndroidX: true,
990 );
991
992 expect(
993 testLogger.statusText,
994 contains(
995 '\n'
996 '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────┐\n'
997 '│ [!] Your project needs to upgrade Gradle and the Android Gradle plugin. │\n'
998 '│ │\n'
999 '│ To fix this issue, replace the following content: │\n'
1000 '│ /android/build.gradle: │\n'
1001 "│ - classpath 'com.android.tools.build:gradle:<current-version>' │\n"
1002 "│ + classpath 'com.android.tools.build:gradle:$templateAndroidGradlePluginVersion' │\n"
1003 '│ /android/gradle/wrapper/gradle-wrapper.properties: │\n'
1004 '│ - https://services.gradle.org/distributions/gradle--all.zip │\n'
1005 '│ + https://services.gradle.org/distributions/gradle-$templateDefaultGradleVersion-all.zip │\n'
1006 '└──────────────────────────────────────────────────────────────────────────────────┘\n',
1007 ),
1008 );
1009 },
1010 overrides: <Type, Generator>{
1011 GradleUtils: () => FakeGradleUtils(),
1012 Platform: () => fakePlatform('android'),
1013 FileSystem: () => fileSystem,
1014 ProcessManager: () => processManager,
1015 },
1016 );
1017 });
1018
1019 group('Required compileSdkVersion', () {
1020 const String errorMessage = '''
1021Execution failed for task ':app:checkDebugAarMetadata'.
1022> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckAarMetadataWorkAction
1023 > One or more issues found when checking AAR metadata values:
1024
1025 The minCompileSdk (31) specified in a
1026 dependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties)
1027 is greater than this module's compileSdkVersion (android-30).
1028 Dependency: androidx.window:window-java:1.0.0-beta04.
1029 AAR metadata file: ~/.gradle/caches/transforms-3/2adc32c5b3f24bed763d33fbfb203338/transformed/jetified-window-java-1.0.0-beta04/META-INF/com/android/build/gradle/aar-metadata.properties.
1030
1031 The minCompileSdk (31) specified in a
1032 dependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties)
1033 is greater than this module's compileSdkVersion (android-30).
1034 Dependency: androidx.window:window:1.0.0-beta04.
1035 AAR metadata file: ~/.gradle/caches/transforms-3/88f7e476ef68cecca729426edff955b5/transformed/jetified-window-1.0.0-beta04/META-INF/com/android/build/gradle/aar-metadata.properties.
1036''';
1037
1038 testWithoutContext('pattern', () {
1039 expect(minCompileSdkVersionHandler.test(errorMessage), isTrue);
1040 });
1041
1042 testUsingContext(
1043 'suggestion',
1044 () async {
1045 await minCompileSdkVersionHandler.handler(
1046 line: errorMessage,
1047 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
1048 usesAndroidX: true,
1049 );
1050
1051 expect(
1052 testLogger.statusText,
1053 contains(
1054 '\n'
1055 '┌─ Flutter Fix ──────────────────────────────────────────────────────────────────┐\n'
1056 '│ [!] Your project requires a higher compileSdk version. │\n'
1057 '│ Fix this issue by bumping the compileSdk version in /android/app/build.gradle: │\n'
1058 '│ android { │\n'
1059 '│ compileSdk 31 │\n'
1060 '│ } │\n'
1061 '└────────────────────────────────────────────────────────────────────────────────┘\n',
1062 ),
1063 );
1064 },
1065 overrides: <Type, Generator>{
1066 GradleUtils: () => FakeGradleUtils(),
1067 Platform: () => fakePlatform('android'),
1068 FileSystem: () => fileSystem,
1069 ProcessManager: () => processManager,
1070 },
1071 );
1072 });
1073
1074 group('incompatible java and android gradle plugin versions error', () {
1075 const String errorMessage = '''
1076* What went wrong:
1077An exception occurred applying plugin request [id: 'com.android.application']
1078> Failed to apply plugin 'com.android.internal.application'.
1079 > Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
1080 You can try some of the following options:
1081 - changing the IDE settings.
1082 - changing the JAVA_HOME environment variable.
1083 - changing `org.gradle.java.home` in `gradle.properties`.
1084''';
1085
1086 testWithoutContext('pattern', () {
1087 expect(incompatibleJavaAndAgpVersionsHandler.test(errorMessage), isTrue);
1088 });
1089
1090 testUsingContext(
1091 'suggestion',
1092 () async {
1093 await incompatibleJavaAndAgpVersionsHandler.handler(
1094 line: errorMessage,
1095 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
1096 usesAndroidX: true,
1097 );
1098
1099 // Ensure the error notes the required Java version, the Java version currently used,
1100 // the android studio and android sdk installation link, the flutter command to set
1101 // the Java version Flutter uses, and the flutter doctor command.
1102 expect(
1103 testLogger.statusText,
1104 contains(
1105 'Android Gradle plugin requires Java 17 to run. You are currently using Java 11.',
1106 ),
1107 );
1108 expect(testLogger.statusText, contains('https://developer.android.com/studio/install'));
1109 expect(testLogger.statusText, contains('`flutter config --jdk-dir=“</path/to/jdk>“`'));
1110 expect(testLogger.statusText, contains('`flutter doctor --verbose`'));
1111 },
1112 overrides: <Type, Generator>{
1113 GradleUtils: () => FakeGradleUtils(),
1114 Platform: () => fakePlatform('android'),
1115 FileSystem: () => fileSystem,
1116 ProcessManager: () => processManager,
1117 },
1118 );
1119 });
1120
1121 group('SSLException', () {
1122 testWithoutContext('pattern', () {
1123 expect(
1124 sslExceptionHandler.test(r'''
1125Exception in thread "main" javax.net.ssl.SSLException: Tag mismatch!
1126at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:129)
1127at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
1128at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
1129at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
1130at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:129)
1131at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1155)
1132at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1125)
1133at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:823)
1134at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:290)
1135at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
1136at java.base/sun.net.www.MeteredStream.read(MeteredStream.java:134)
1137at java.base/java.io.FilterInputStream.read(FilterInputStream.java:133)
1138at java.base/sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3444)
1139at java.base/sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3437)
1140at org.gradle.wrapper.Download.downloadInternal(Download.java:62)
1141at org.gradle.wrapper.Download.download(Download.java:44)
1142at org.gradle.wrapper.Install$1.call(Install.java:61)
1143at org.gradle.wrapper.Install$1.call(Install.java:48)
1144at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
1145at org.gradle.wrapper.Install.createDist(Install.java:48)
1146at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
1147at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''),
1148 isTrue,
1149 );
1150
1151 expect(
1152 sslExceptionHandler.test(r'''
1153Caused by: javax.crypto.AEADBadTagException: Tag mismatch!
1154at java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:580)
1155at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1049)
1156at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:985)
1157at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:491)
1158at java.base/javax.crypto.CipherSpi.bufferCrypt(CipherSpi.java:779)
1159at java.base/javax.crypto.CipherSpi.engineDoFinal(CipherSpi.java:730)
1160at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2497)
1161at java.base/sun.security.ssl.SSLCipher$T12GcmReadCipherGenerator$GcmReadCipher.decrypt(SSLCipher.java:1613)
1162at java.base/sun.security.ssl.SSLSocketInputRecord.decodeInputRecord(SSLSocketInputRecord.java:262)
1163at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:190)
1164at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:108)'''),
1165 isTrue,
1166 );
1167 });
1168
1169 testUsingContext(
1170 'suggestion',
1171 () async {
1172 final GradleBuildStatus status = await sslExceptionHandler.handler(
1173 project: FakeFlutterProject(),
1174 usesAndroidX: true,
1175 line: '',
1176 );
1177
1178 expect(status, GradleBuildStatus.retry);
1179 expect(
1180 testLogger.errorText,
1181 contains('Gradle threw an error while downloading artifacts from the network.'),
1182 );
1183 },
1184 overrides: <Type, Generator>{
1185 GradleUtils: () => FakeGradleUtils(),
1186 Platform: () => fakePlatform('android'),
1187 FileSystem: () => fileSystem,
1188 ProcessManager: () => processManager,
1189 },
1190 );
1191 });
1192
1193 group('Zip exception', () {
1194 testWithoutContext('pattern', () {
1195 expect(
1196 zipExceptionHandler.test(r'''
1197Exception in thread "main" java.util.zip.ZipException: error in opening zip file
1198at java.util.zip.ZipFile.open(Native Method)
1199at java.util.zip.ZipFile.(ZipFile.java:225)
1200at java.util.zip.ZipFile.(ZipFile.java:155)
1201at java.util.zip.ZipFile.(ZipFile.java:169)
1202at org.gradle.wrapper.Install.unzip(Install.java:214)
1203at org.gradle.wrapper.Install.access$600(Install.java:27)
1204at org.gradle.wrapper.Install$1.call(Install.java:74)
1205at org.gradle.wrapper.Install$1.call(Install.java:48)
1206at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
1207at org.gradle.wrapper.Install.createDist(Install.java:48)
1208at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:128)
1209at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''),
1210 isTrue,
1211 );
1212 });
1213
1214 testUsingContext(
1215 'suggestion',
1216 () async {
1217 fileSystem.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1218
1219 final GradleBuildStatus result = await zipExceptionHandler.handler(
1220 project: FakeFlutterProject(),
1221 usesAndroidX: true,
1222 line: '',
1223 );
1224
1225 expect(result, equals(GradleBuildStatus.retry));
1226 expect(fileSystem.file('foo/.gradle/fizz.zip'), exists);
1227 expect(
1228 testLogger.errorText,
1229 contains('[!] Your .gradle directory under the home directory might be corrupted.\n'),
1230 );
1231 expect(testLogger.statusText, '');
1232 },
1233 overrides: <Type, Generator>{
1234 Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1235 FileSystem: () => fileSystem,
1236 ProcessManager: () => processManager,
1237 BotDetector: () => const FakeBotDetector(false),
1238 },
1239 );
1240
1241 testUsingContext(
1242 'suggestion if running as bot',
1243 () async {
1244 fileSystem.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1245
1246 final GradleBuildStatus result = await zipExceptionHandler.handler(
1247 project: FakeFlutterProject(),
1248 usesAndroidX: true,
1249 line: '',
1250 );
1251
1252 expect(result, equals(GradleBuildStatus.retry));
1253 expect(fileSystem.file('foo/.gradle/fizz.zip'), isNot(exists));
1254
1255 expect(
1256 testLogger.errorText,
1257 contains('[!] Your .gradle directory under the home directory might be corrupted.\n'),
1258 );
1259 expect(testLogger.statusText, contains('Deleting foo/.gradle\n'));
1260 },
1261 overrides: <Type, Generator>{
1262 Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1263 FileSystem: () => fileSystem,
1264 ProcessManager: () => processManager,
1265 BotDetector: () => const FakeBotDetector(true),
1266 },
1267 );
1268
1269 testUsingContext(
1270 'suggestion if stdin has terminal and user entered y',
1271 () async {
1272 fileSystem.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1273
1274 final GradleBuildStatus result = await zipExceptionHandler.handler(
1275 line: '',
1276 usesAndroidX: true,
1277 project: FakeFlutterProject(),
1278 );
1279
1280 expect(result, equals(GradleBuildStatus.retry));
1281 expect(fileSystem.file('foo/.gradle/fizz.zip'), isNot(exists));
1282 expect(
1283 testLogger.errorText,
1284 contains('[!] Your .gradle directory under the home directory might be corrupted.\n'),
1285 );
1286 expect(testLogger.statusText, contains('Deleting foo/.gradle\n'));
1287 },
1288 overrides: <Type, Generator>{
1289 Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1290 FileSystem: () => fileSystem,
1291 ProcessManager: () => processManager,
1292 AnsiTerminal: () => _TestPromptTerminal('y'),
1293 BotDetector: () => const FakeBotDetector(false),
1294 },
1295 );
1296
1297 testUsingContext(
1298 'suggestion if stdin has terminal and user entered n',
1299 () async {
1300 fileSystem.file('foo/.gradle/fizz.zip').createSync(recursive: true);
1301
1302 final GradleBuildStatus result = await zipExceptionHandler.handler(
1303 line: '',
1304 usesAndroidX: true,
1305 project: FakeFlutterProject(),
1306 );
1307
1308 expect(result, equals(GradleBuildStatus.retry));
1309 expect(fileSystem.file('foo/.gradle/fizz.zip'), exists);
1310 expect(
1311 testLogger.errorText,
1312 contains('[!] Your .gradle directory under the home directory might be corrupted.\n'),
1313 );
1314 expect(testLogger.statusText, '');
1315 },
1316 overrides: <Type, Generator>{
1317 Platform: () => FakePlatform(environment: <String, String>{'HOME': 'foo/'}),
1318 FileSystem: () => fileSystem,
1319 ProcessManager: () => processManager,
1320 AnsiTerminal: () => _TestPromptTerminal('n'),
1321 BotDetector: () => const FakeBotDetector(false),
1322 },
1323 );
1324 });
1325
1326 group('incompatible java and gradle versions error', () {
1327 const String errorMessage = '''
1328Could not compile build file '…/example/android/build.gradle'.
1329> startup failed:
1330 General error during conversion: Unsupported class file major version 61
1331 java.lang.IllegalArgumentException: Unsupported class file major version 61
1332''';
1333
1334 testWithoutContext('pattern', () {
1335 expect(incompatibleJavaAndGradleVersionsHandler.test(errorMessage), isTrue);
1336 });
1337
1338 testUsingContext(
1339 'suggestion',
1340 () async {
1341 await incompatibleJavaAndGradleVersionsHandler.handler(
1342 line: errorMessage,
1343 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
1344 usesAndroidX: true,
1345 );
1346
1347 // Ensure the error notes the incompatible Gradle/AGP/Java versions, links to related resources,
1348 // and a portion of the path to where to change their gradle version.
1349 expect(
1350 testLogger.statusText,
1351 contains('Gradle version is incompatible with the Java version'),
1352 );
1353 expect(testLogger.statusText, contains('flutter.dev/to/java-gradle-incompatibility'));
1354 expect(testLogger.statusText, contains('gradle-wrapper.properties'));
1355 expect(
1356 testLogger.statusText,
1357 contains('https://docs.gradle.org/current/userguide/compatibility.html#java'),
1358 );
1359 },
1360 overrides: <Type, Generator>{
1361 GradleUtils: () => FakeGradleUtils(),
1362 Platform: () => fakePlatform('android'),
1363 FileSystem: () => fileSystem,
1364 ProcessManager: () => processManager,
1365 },
1366 );
1367 });
1368
1369 testUsingContext(
1370 'couldNotOpenCacheDirectoryHandler',
1371 () async {
1372 final GradleBuildStatus status = await couldNotOpenCacheDirectoryHandler.handler(
1373 line: '''
1374FAILURE: Build failed with an exception.
1375
1376* Where:
1377Script '/Volumes/Work/s/w/ir/x/w/flutter/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy' line: 276
1378
1379* What went wrong:
1380A problem occurred evaluating script.
1381> Failed to apply plugin class 'FlutterPlugin'.
1382 > Could not open cache directory 41rl0ui7kgmsyfwn97o2jypl6 (/Volumes/Work/s/w/ir/cache/gradle/caches/6.7/gradle-kotlin-dsl/41rl0ui7kgmsyfwn97o2jypl6).
1383 > Failed to create Jar file /Volumes/Work/s/w/ir/cache/gradle/caches/6.7/generated-gradle-jars/gradle-api-6.7.jar.''',
1384 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
1385 usesAndroidX: true,
1386 );
1387 expect(testLogger.errorText, contains('Gradle threw an error while resolving dependencies'));
1388 expect(status, GradleBuildStatus.retry);
1389 },
1390 overrides: <Type, Generator>{
1391 GradleUtils: () => FakeGradleUtils(),
1392 Platform: () => fakePlatform('android'),
1393 FileSystem: () => fileSystem,
1394 ProcessManager: () => processManager,
1395 },
1396 );
1397
1398 testUsingContext(
1399 'compileSdk 35 and AGP < 8.1',
1400 () async {
1401 const String errorExample = r'''
1402Execution failed for task ':app:bundleReleaseResources'.
1403> A failure occurred while executing com.android.build.gradle.internal.res.Aapt2ProcessResourcesRunnable
1404 > Android resource linking failed
1405 aapt2 E 08-19 15:06:26 76078 5921862 LoadedArsc.cpp:94] RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data.
1406 aapt2 E 08-19 15:06:26 76078 5921862 ApkAssets.cpp:152] Failed to load resources table in APK '/Users/mackall/Library/Android/sdk/platforms/android-35/android.jar'.
1407 error: failed to load include path /Users/mackall/Library/Android/sdk/platforms/android-35/android.jar.
1408 ''';
1409
1410 await incompatibleCompileSdk35AndAgpVersionHandler.handler(
1411 line: errorExample,
1412 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
1413 usesAndroidX: true,
1414 );
1415
1416 expect(
1417 testLogger.statusText,
1418 contains(
1419 '\n'
1420 '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────────┐\n'
1421 '│ [!] Using compileSdk 35 requires Android Gradle Plugin (AGP) 8.1.0 or higher. │\n'
1422 '│ Please upgrade to a newer AGP version. The version of AGP that your project uses is likely │\n'
1423 '│ defined in: │\n'
1424 '│ /android/settings.gradle, │\n'
1425 '│ in the \'plugins\' closure (by the number following "com.android.application"). │\n'
1426 '│ Alternatively, if your project was created with an older version of the templates, it is likely │\n'
1427 '│ in the buildscript.dependencies closure of the top-level build.gradle: │\n'
1428 '│ /android/build.gradle, │\n'
1429 '│ as the number following "com.android.tools.build:gradle:". │\n'
1430 '│ │\n'
1431 '│ Finally, if you have a strong reason to avoid upgrading AGP, you can temporarily lower the │\n'
1432 '│ compileSdk version in the following file: │\n'
1433 '│ /android/app/build.gradle │\n'
1434 '└──────────────────────────────────────────────────────────────────────────────────────────────────┘\n'
1435 '',
1436 ),
1437 );
1438 },
1439 overrides: <Type, Generator>{
1440 GradleUtils: () => FakeGradleUtils(),
1441 Platform: () => fakePlatform('android'),
1442 FileSystem: () => fileSystem,
1443 ProcessManager: () => processManager,
1444 },
1445 );
1446
1447 testUsingContext(
1448 'AGP 7.3.0 R8 bug',
1449 () async {
1450 const String errorExample = r'''
1451ERROR:/Users/mackall/.gradle/caches/transforms-3/bd2c84591857c6d4c308221ffece862e/transformed/jetified-media3-exoplayer-dash-1.4.0-runtime.jar: R8: com.android.tools.r8.internal.Y10: Unused argument with users in androidx
1452 ''';
1453
1454 await r8DexingBugInAgp73Handler.handler(
1455 line: errorExample,
1456 project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
1457 usesAndroidX: true,
1458 );
1459
1460 expect(
1461 testLogger.statusText,
1462 contains(
1463 '\n'
1464 '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────────┐\n'
1465 '│ [!] Version 7.3 of the Android Gradle Plugin (AGP) uses a version of R8 that contains a bug │\n'
1466 '│ which causes this error (see more info at https://issuetracker.google.com/issues/242308990). │\n'
1467 '│ To fix this error, update to a newer version of AGP (at least 7.4.0). │\n'
1468 '│ │\n'
1469 '│ The version of AGP that your project uses is likely defined in: │\n'
1470 '│ /android/settings.gradle, │\n'
1471 '│ in the \'plugins\' closure (by the number following "com.android.application"). │\n'
1472 '│ Alternatively, if your project was created with an older version of the templates, it is likely │\n'
1473 '│ in the buildscript.dependencies closure of the top-level build.gradle: │\n'
1474 '│ /android/build.gradle, │\n'
1475 '│ as the number following "com.android.tools.build:gradle:". │\n'
1476 '└──────────────────────────────────────────────────────────────────────────────────────────────────┘\n'
1477 '',
1478 ),
1479 );
1480 },
1481 overrides: <Type, Generator>{
1482 GradleUtils: () => FakeGradleUtils(),
1483 Platform: () => fakePlatform('android'),
1484 FileSystem: () => fileSystem,
1485 ProcessManager: () => processManager,
1486 },
1487 );
1488
1489 testUsingContext(
1490 'Usage of removed v1 embedding references',
1491 () async {
1492 const String errorExample = r'''
1493/Users/jesswon/.pub-cache/hosted/pub.dev/video_player_android-2.5.0/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java:42: error: cannot find symbol
1494 private VideoPlayerPlugin(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
1495 ^
1496 symbol: class Registrar
1497 location: interface PluginRegistry
14981 error
1499
1500FAILURE: Build failed with an exception.
1501 ''';
1502
1503 final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
1504 await usageOfV1EmbeddingReferencesHandler.handler(
1505 line: errorExample,
1506 project: project,
1507 usesAndroidX: true,
1508 );
1509
1510 // Main fix text.
1511 expect(
1512 testLogger.statusText,
1513 contains(
1514 "To fix this error, please upgrade your current package's dependencies to latest versions by",
1515 ),
1516 );
1517 expect(testLogger.statusText, contains('running `flutter pub upgrade`.'));
1518 // Text and link to file an issue.
1519 expect(
1520 testLogger.statusText,
1521 contains('If that does not work, please file an issue for the problematic plugin(s) here:'),
1522 );
1523 expect(testLogger.statusText, contains('https://github.com/flutter/flutter/issues'));
1524 },
1525 overrides: <Type, Generator>{
1526 GradleUtils: () => FakeGradleUtils(),
1527 Platform: () => fakePlatform('android'),
1528 FileSystem: () => fileSystem,
1529 ProcessManager: () => processManager,
1530 },
1531 );
1532
1533 testUsingContext(
1534 'Java 21 and jlink bug',
1535 () async {
1536 const String errorExample = r'''
1537* What went wrong:
1538Execution failed for task ':shared_preferences_android:compileReleaseJavaWithJavac'.
1539> Could not resolve all files for configuration ':shared_preferences_android:androidJdkImage'.
1540 > Failed to transform core-for-system-modules.jar to match attributes {artifactType=_internal_android_jdk_image, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}.
1541 > Execution failed for JdkImageTransform: /Users/mackall/Library/Android/sdk/platforms/android-34/core-for-system-modules.jar.
1542 > Error while executing process /Users/mackall/Desktop/JDKs/21/jdk-21.0.2.jdk/Contents/Home/bin/jlink with arguments {--module-path /Users/mackall/.gradle/caches/8.9/transforms/2890fec03da42154757073d3208548e5-79660961-f91d-4df2-90bc-b9a3f2a270bd/transformed/output/temp/jmod --add-modules java.base --output /Users/mackall/.gradle/caches/8.9/transforms/2890fec03da42154757073d3208548e5-79660961-f91d-4df2-90bc-b9a3f2a270bd/transformed/output/jdkImage --disable-plugin system-modules}
1543 ''';
1544
1545 final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
1546 await jlinkErrorWithJava21AndSourceCompatibility.handler(
1547 line: errorExample,
1548 project: project,
1549 usesAndroidX: true,
1550 );
1551
1552 // Main fix text.
1553 expect(
1554 testLogger.statusText,
1555 contains('To fix this error, please upgrade your AGP version to at least 8.2.1.'),
1556 );
1557 // Paths to AGP location.
1558 expect(testLogger.statusText, contains('/android/settings.gradle'));
1559 expect(testLogger.statusText, contains('/android/build.gradle'));
1560 // Links to info.
1561 expect(testLogger.statusText, contains('https://issuetracker.google.com/issues/294137077'));
1562 expect(testLogger.statusText, contains('https://github.com/flutter/flutter/issues/156304'));
1563 },
1564 overrides: <Type, Generator>{
1565 GradleUtils: () => FakeGradleUtils(),
1566 Platform: () => fakePlatform('android'),
1567 FileSystem: () => fileSystem,
1568 ProcessManager: () => processManager,
1569 },
1570 );
1571}
1572
1573bool formatTestErrorMessage(String errorMessage, GradleHandledError error) {
1574 return errorMessage.split('\n').any((String line) => error.test(line));
1575}
1576
1577Platform fakePlatform(String name) {
1578 return FakePlatform(environment: <String, String>{'HOME': '/'}, operatingSystem: name);
1579}
1580
1581class FakeGradleUtils extends Fake implements GradleUtils {
1582 @override
1583 String getExecutable(FlutterProject project) {
1584 return 'gradlew';
1585 }
1586}
1587
1588/// Simple terminal that returns the specified string when
1589/// promptForCharInput is called.
1590class _TestPromptTerminal extends Fake implements AnsiTerminal {
1591 _TestPromptTerminal(this.promptResult);
1592
1593 final String promptResult;
1594
1595 @override
1596 bool get stdinHasTerminal => true;
1597
1598 @override
1599 Future<String> promptForCharInput(
1600 List<String> acceptedCharacters, {
1601 required Logger logger,
1602 String? prompt,
1603 int? defaultChoiceIndex,
1604 bool displayAcceptedCharacters = true,
1605 }) {
1606 return Future<String>.value(promptResult);
1607 }
1608}
1609
1610class FakeFlutterProject extends Fake implements FlutterProject {}
1611

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com