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 | |
5 | import 'dart:io' show File, Platform; |
6 | import 'dart:typed_data'; |
7 | |
8 | import 'package:collection/collection.dart' ; |
9 | import 'package:flutter_devicelab/framework/devices.dart'; |
10 | import 'package:flutter_devicelab/framework/framework.dart'; |
11 | import 'package:flutter_devicelab/framework/task_result.dart'; |
12 | import 'package:flutter_devicelab/framework/utils.dart'; |
13 | import 'package:flutter_devicelab/tasks/integration_tests.dart'; |
14 | import 'package:path/path.dart' as path; |
15 | import 'package:standard_message_codec/standard_message_codec.dart' ; |
16 | |
17 | Future<void> main() async { |
18 | deviceOperatingSystem = DeviceOperatingSystem.android; |
19 | await task(() async { |
20 | await createFlavorsTest().call(); |
21 | await createIntegrationTestFlavorsTest().call(); |
22 | |
23 | final String projectPath = ' ${flutterDirectory.path}/dev/integration_tests/flavors' ; |
24 | final TaskResult installTestsResult = await inDirectory(projectPath, () async { |
25 | final List<TaskResult> testResults = <TaskResult>[ |
26 | await _testInstallDebugPaidFlavor(projectPath), |
27 | await _testInstallBogusFlavor(), |
28 | ]; |
29 | |
30 | final TaskResult? firstInstallFailure = testResults.firstWhereOrNull( |
31 | (TaskResult element) => element.failed, |
32 | ); |
33 | |
34 | return firstInstallFailure ?? TaskResult.success(null); |
35 | }); |
36 | |
37 | await _testFlavorsWhenBuildStartsWithGradle(projectPath); |
38 | |
39 | return installTestsResult; |
40 | }); |
41 | } |
42 | |
43 | // Ensures installation works. Also tests asset bundling while we are at it. |
44 | Future<TaskResult> _testInstallDebugPaidFlavor(String projectDir) async { |
45 | await evalFlutter('install' , options: <String>['--debug' , '--flavor' , 'paid' ]); |
46 | |
47 | final Uint8List assetManifestFileData = |
48 | File( |
49 | path.join( |
50 | projectDir, |
51 | 'build' , |
52 | 'app' , |
53 | 'intermediates' , |
54 | 'assets' , |
55 | 'paidDebug' , |
56 | 'mergePaidDebugAssets' , |
57 | 'flutter_assets' , |
58 | 'AssetManifest.bin' , |
59 | ), |
60 | ).readAsBytesSync(); |
61 | |
62 | final Map<Object?, Object?> assetManifest = |
63 | const StandardMessageCodec().decodeMessage(ByteData.sublistView(assetManifestFileData)) |
64 | as Map<Object?, Object?>; |
65 | |
66 | if (assetManifest.containsKey('assets/free/free.txt' )) { |
67 | return TaskResult.failure( |
68 | 'Expected the asset "assets/free/free.txt", which ' |
69 | ' was declared with a flavor of "free" to not be included in the asset bundle ' |
70 | ' because the --flavor was set to "paid".' , |
71 | ); |
72 | } |
73 | |
74 | if (!assetManifest.containsKey('assets/paid/paid.txt' )) { |
75 | return TaskResult.failure( |
76 | 'Expected the asset "assets/paid/paid.txt", which ' |
77 | ' was declared with a flavor of "paid" to be included in the asset bundle ' |
78 | ' because the --flavor was set to "paid".' , |
79 | ); |
80 | } |
81 | |
82 | await flutter('install' , options: <String>['--debug' , '--flavor' , 'paid' , '--uninstall-only' ]); |
83 | |
84 | return TaskResult.success(null); |
85 | } |
86 | |
87 | Future<TaskResult> _testInstallBogusFlavor() async { |
88 | final StringBuffer stderr = StringBuffer(); |
89 | await evalFlutter( |
90 | 'install' , |
91 | canFail: true, |
92 | stderr: stderr, |
93 | options: <String>['--flavor' , 'bogus' ], |
94 | ); |
95 | |
96 | final String stderrString = stderr.toString(); |
97 | final String expectedApkPath = path.join( |
98 | 'build' , |
99 | 'app' , |
100 | 'outputs' , |
101 | 'flutter-apk' , |
102 | 'app-bogus-release.apk' , |
103 | ); |
104 | if (!stderrString.contains('" $expectedApkPath" does not exist.' )) { |
105 | print(stderrString); |
106 | return TaskResult.failure('Should not succeed with bogus flavor' ); |
107 | } |
108 | |
109 | return TaskResult.success(null); |
110 | } |
111 | |
112 | Future<TaskResult> _testFlavorsWhenBuildStartsWithGradle(String projectDir) async { |
113 | final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew' ; |
114 | final String gradlewExecutable = Platform.isWindows ? '.\\ $gradlew' : './ $gradlew' ; |
115 | |
116 | final String androidDirPath = ' $projectDir/android' ; |
117 | final StringBuffer stdout = StringBuffer(); |
118 | |
119 | // Prebuild the project to generate the Android gradle wrapper files. |
120 | await inDirectory(projectDir, () async { |
121 | await flutter('build' , options: <String>['apk' , '--config-only' ]); |
122 | }); |
123 | |
124 | await inDirectory(androidDirPath, () async { |
125 | await exec(gradlewExecutable, <String>['clean' ]); |
126 | await exec(gradlewExecutable, <String>[':app:assemblePaidDebug' , '--info' ], output: stdout); |
127 | }); |
128 | |
129 | final String stdoutString = stdout.toString(); |
130 | |
131 | if (!stdoutString.contains('-dFlavor=paid' )) { |
132 | return TaskResult.failure('Expected to see -dFlavor=paid in the gradle verbose output' ); |
133 | } |
134 | |
135 | final String appPath = path.join( |
136 | projectDir, |
137 | 'build' , |
138 | 'app' , |
139 | 'outputs' , |
140 | 'flutter-apk' , |
141 | 'app-paid-debug.apk' , |
142 | ); |
143 | |
144 | return createFlavorsTest( |
145 | extraOptions: <String>['--flavor' , 'paid' , '--use-application-binary= $appPath' ], |
146 | ).call(); |
147 | } |
148 | |