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';
6
7import 'package:flutter_tools/src/artifacts.dart';
8import 'package:flutter_tools/src/base/file_system.dart';
9import 'package:flutter_tools/src/base/logger.dart';
10import 'package:flutter_tools/src/build_info.dart';
11
12import '../src/common.dart';
13import '../src/context.dart';
14import '../src/test_build_system.dart';
15
16void main() {
17 late BufferLogger logger;
18 setUp(() {
19 logger = BufferLogger.test();
20 });
21
22 group('Validate build number', () {
23 testWithoutContext('CFBundleVersion for iOS', () async {
24 String? buildName = validatedBuildNumberForPlatform(TargetPlatform.ios, 'xyz', logger);
25 expect(buildName, isNull);
26 buildName = validatedBuildNumberForPlatform(TargetPlatform.ios, '0.0.1', logger);
27 expect(buildName, '0.0.1');
28 buildName = validatedBuildNumberForPlatform(TargetPlatform.ios, '123.xyz', logger);
29 expect(buildName, '123');
30 buildName = validatedBuildNumberForPlatform(TargetPlatform.ios, '123.456.xyz', logger);
31 expect(buildName, '123.456');
32 });
33
34 testWithoutContext('versionCode for Android', () async {
35 String? buildName = validatedBuildNumberForPlatform(
36 TargetPlatform.android_arm,
37 '123.abc+-',
38 logger,
39 );
40 expect(buildName, '123');
41 buildName = validatedBuildNumberForPlatform(TargetPlatform.android_arm, 'abc', logger);
42 expect(buildName, '1');
43 });
44 });
45
46 group('Validate build name', () {
47 testWithoutContext('CFBundleShortVersionString for iOS', () async {
48 String? buildName = validatedBuildNameForPlatform(TargetPlatform.ios, 'xyz', logger);
49 expect(buildName, isNull);
50 buildName = validatedBuildNameForPlatform(TargetPlatform.ios, '0.0.1', logger);
51 expect(buildName, '0.0.1');
52
53 buildName = validatedBuildNameForPlatform(TargetPlatform.ios, '123.456.xyz', logger);
54 expect(logger.traceText, contains('Invalid build-name'));
55 expect(buildName, '123.456.0');
56
57 buildName = validatedBuildNameForPlatform(TargetPlatform.ios, '123.xyz', logger);
58 expect(buildName, '123.0.0');
59 });
60
61 testWithoutContext('versionName for Android', () async {
62 String? buildName = validatedBuildNameForPlatform(
63 TargetPlatform.android_arm,
64 '123.abc+-',
65 logger,
66 );
67 expect(buildName, '123.abc+-');
68 buildName = validatedBuildNameForPlatform(TargetPlatform.android_arm, 'abc+-', logger);
69 expect(buildName, 'abc+-');
70 });
71
72 testWithoutContext('build mode configuration is correct', () {
73 expect(BuildMode.debug.isRelease, false);
74 expect(BuildMode.debug.isPrecompiled, false);
75 expect(BuildMode.debug.isJit, true);
76
77 expect(BuildMode.profile.isRelease, false);
78 expect(BuildMode.profile.isPrecompiled, true);
79 expect(BuildMode.profile.isJit, false);
80
81 expect(BuildMode.release.isRelease, true);
82 expect(BuildMode.release.isPrecompiled, true);
83 expect(BuildMode.release.isJit, false);
84
85 expect(BuildMode.jitRelease.isRelease, true);
86 expect(BuildMode.jitRelease.isPrecompiled, false);
87 expect(BuildMode.jitRelease.isJit, true);
88
89 expect(BuildMode.fromCliName('debug'), BuildMode.debug);
90 expect(BuildMode.fromCliName('profile'), BuildMode.profile);
91 expect(BuildMode.fromCliName('jit_release'), BuildMode.jitRelease);
92 expect(BuildMode.fromCliName('release'), BuildMode.release);
93 expect(() => BuildMode.fromCliName('foo'), throwsArgumentError);
94 });
95 });
96
97 testWithoutContext('getDartNameForDarwinArch returns name used in Dart SDK', () {
98 expect(DarwinArch.armv7.dartName, 'armv7');
99 expect(DarwinArch.arm64.dartName, 'arm64');
100 expect(DarwinArch.x86_64.dartName, 'x64');
101 });
102
103 testWithoutContext('getNameForDarwinArch returns Apple names', () {
104 expect(DarwinArch.armv7.name, 'armv7');
105 expect(DarwinArch.arm64.name, 'arm64');
106 expect(DarwinArch.x86_64.name, 'x86_64');
107 });
108
109 testWithoutContext('getNameForTargetPlatform on Darwin arches', () {
110 expect(getNameForTargetPlatform(TargetPlatform.ios, darwinArch: DarwinArch.arm64), 'ios-arm64');
111 expect(getNameForTargetPlatform(TargetPlatform.ios, darwinArch: DarwinArch.armv7), 'ios-armv7');
112 expect(
113 getNameForTargetPlatform(TargetPlatform.ios, darwinArch: DarwinArch.x86_64),
114 'ios-x86_64',
115 );
116 expect(getNameForTargetPlatform(TargetPlatform.android), isNot(contains('ios')));
117 });
118
119 testUsingContext(
120 'defaultIOSArchsForEnvironment',
121 () {
122 expect(
123 defaultIOSArchsForEnvironment(
124 EnvironmentType.physical,
125 Artifacts.testLocalEngine(
126 localEngineHost: 'host_debug_unopt',
127 localEngine: 'ios_debug_unopt',
128 ),
129 ).single,
130 DarwinArch.arm64,
131 );
132
133 expect(
134 defaultIOSArchsForEnvironment(
135 EnvironmentType.simulator,
136 Artifacts.testLocalEngine(
137 localEngineHost: 'host_debug_unopt',
138 localEngine: 'ios_debug_sim_unopt',
139 ),
140 ).single,
141 DarwinArch.x86_64,
142 );
143
144 expect(
145 defaultIOSArchsForEnvironment(
146 EnvironmentType.simulator,
147 Artifacts.testLocalEngine(
148 localEngineHost: 'host_debug_unopt',
149 localEngine: 'ios_debug_sim_unopt_arm64',
150 ),
151 ).single,
152 DarwinArch.arm64,
153 );
154
155 expect(
156 defaultIOSArchsForEnvironment(EnvironmentType.physical, Artifacts.test()).single,
157 DarwinArch.arm64,
158 );
159
160 expect(
161 defaultIOSArchsForEnvironment(EnvironmentType.simulator, Artifacts.test()),
162 <DarwinArch>[DarwinArch.x86_64, DarwinArch.arm64],
163 );
164 },
165 overrides: <Type, Generator>{
166 FileSystem: () => MemoryFileSystem.test(),
167 ProcessManager: () => FakeProcessManager.any(),
168 },
169 );
170
171 testUsingContext(
172 'defaultMacOSArchsForEnvironment',
173 () {
174 expect(
175 defaultMacOSArchsForEnvironment(
176 Artifacts.testLocalEngine(
177 localEngineHost: 'host_debug_unopt',
178 localEngine: 'host_debug_unopt',
179 ),
180 ).single,
181 DarwinArch.x86_64,
182 );
183
184 expect(
185 defaultMacOSArchsForEnvironment(
186 Artifacts.testLocalEngine(
187 localEngineHost: 'host_debug_unopt',
188 localEngine: 'host_debug_unopt_arm64',
189 ),
190 ).single,
191 DarwinArch.arm64,
192 );
193
194 expect(defaultMacOSArchsForEnvironment(Artifacts.test()), <DarwinArch>[
195 DarwinArch.x86_64,
196 DarwinArch.arm64,
197 ]);
198 },
199 overrides: <Type, Generator>{
200 FileSystem: () => MemoryFileSystem.test(),
201 ProcessManager: () => FakeProcessManager.any(),
202 },
203 );
204
205 testWithoutContext('getIOSArchForName on Darwin arches', () {
206 expect(getIOSArchForName('armv7'), DarwinArch.armv7);
207 expect(getIOSArchForName('arm64'), DarwinArch.arm64);
208 expect(getIOSArchForName('arm64e'), DarwinArch.arm64);
209 expect(getIOSArchForName('x86_64'), DarwinArch.x86_64);
210 expect(() => getIOSArchForName('bogus'), throwsException);
211 });
212
213 testWithoutContext('named BuildInfo has correct defaults', () {
214 expect(BuildInfo.debug.mode, BuildMode.debug);
215 expect(BuildInfo.debug.trackWidgetCreation, true);
216
217 expect(BuildInfo.profile.mode, BuildMode.profile);
218 expect(BuildInfo.profile.trackWidgetCreation, false);
219
220 expect(BuildInfo.release.mode, BuildMode.release);
221 expect(BuildInfo.release.trackWidgetCreation, false);
222 });
223
224 testWithoutContext('toBuildSystemEnvironment encoding of standard values', () {
225 const buildInfo = BuildInfo(
226 BuildMode.debug,
227 '',
228 treeShakeIcons: true,
229 trackWidgetCreation: true,
230 dartDefines: <String>['foo=2', 'bar=2'],
231 dartObfuscation: true,
232 splitDebugInfoPath: 'foo/',
233 frontendServerStarterPath: 'foo/bar/frontend_server_starter.dart',
234 extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
235 extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
236 packageConfigPath: 'foo/.dart_tool/package_config.json',
237 codeSizeDirectory: 'foo/code-size',
238 fileSystemRoots: <String>['test5', 'test6'],
239 fileSystemScheme: 'scheme',
240 buildName: '122',
241 buildNumber: '22',
242 );
243
244 expect(buildInfo.toBuildSystemEnvironment(), <String, String>{
245 'BuildMode': 'debug',
246 'DartDefines': 'Zm9vPTI=,YmFyPTI=',
247 'DartObfuscation': 'true',
248 'FrontendServerStarterPath': 'foo/bar/frontend_server_starter.dart',
249 'ExtraFrontEndOptions': '--enable-experiment=non-nullable,bar',
250 'ExtraGenSnapshotOptions': '--enable-experiment=non-nullable,fizz',
251 'SplitDebugInfo': 'foo/',
252 'TrackWidgetCreation': 'true',
253 'TreeShakeIcons': 'true',
254 'CodeSizeDirectory': 'foo/code-size',
255 'FileSystemRoots': 'test5,test6',
256 'FileSystemScheme': 'scheme',
257 'BuildName': '122',
258 'BuildNumber': '22',
259 });
260 });
261
262 testWithoutContext('toEnvironmentConfig encoding of standard values', () {
263 const buildInfo = BuildInfo(
264 BuildMode.debug,
265 'strawberry',
266 treeShakeIcons: true,
267 trackWidgetCreation: true,
268 dartDefines: <String>['foo=2', 'bar=2'],
269 dartObfuscation: true,
270 splitDebugInfoPath: 'foo/',
271 frontendServerStarterPath: 'foo/bar/frontend_server_starter.dart',
272 extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
273 extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
274 packageConfigPath: 'foo/.dart_tool/package_config.json',
275 codeSizeDirectory: 'foo/code-size',
276 // These values are ignored by toEnvironmentConfig
277 androidProjectArgs: <String>['foo=bar', 'fizz=bazz'],
278 );
279
280 expect(buildInfo.toEnvironmentConfig(), <String, String>{
281 'TREE_SHAKE_ICONS': 'true',
282 'TRACK_WIDGET_CREATION': 'true',
283 'DART_DEFINES': 'Zm9vPTI=,YmFyPTI=',
284 'DART_OBFUSCATION': 'true',
285 'SPLIT_DEBUG_INFO': 'foo/',
286 'FRONTEND_SERVER_STARTER_PATH': 'foo/bar/frontend_server_starter.dart',
287 'EXTRA_FRONT_END_OPTIONS': '--enable-experiment=non-nullable,bar',
288 'EXTRA_GEN_SNAPSHOT_OPTIONS': '--enable-experiment=non-nullable,fizz',
289 'PACKAGE_CONFIG': 'foo/.dart_tool/package_config.json',
290 'CODE_SIZE_DIRECTORY': 'foo/code-size',
291 'FLAVOR': 'strawberry',
292 });
293 });
294
295 testWithoutContext('toGradleConfig encoding of standard values', () {
296 const buildInfo = BuildInfo(
297 BuildMode.debug,
298 '',
299 treeShakeIcons: true,
300 trackWidgetCreation: true,
301 dartDefines: <String>['foo=2', 'bar=2'],
302 dartObfuscation: true,
303 splitDebugInfoPath: 'foo/',
304 frontendServerStarterPath: 'foo/bar/frontend_server_starter.dart',
305 extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
306 extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
307 packageConfigPath: 'foo/.dart_tool/package_config.json',
308 codeSizeDirectory: 'foo/code-size',
309 androidProjectArgs: <String>['foo=bar', 'fizz=bazz'],
310 );
311
312 expect(buildInfo.toGradleConfig(), <String>[
313 '-Pdart-defines=${encodeDartDefinesMap(<String, String>{'foo': '2', 'bar': '2'})}',
314 '-Pdart-obfuscation=true',
315 '-Pfrontend-server-starter-path=foo/bar/frontend_server_starter.dart',
316 '-Pextra-front-end-options=--enable-experiment=non-nullable,bar',
317 '-Pextra-gen-snapshot-options=--enable-experiment=non-nullable,fizz',
318 '-Psplit-debug-info=foo/',
319 '-Ptrack-widget-creation=true',
320 '-Ptree-shake-icons=true',
321 '-Pcode-size-directory=foo/code-size',
322 '-Pfoo=bar',
323 '-Pfizz=bazz',
324 ]);
325 });
326
327 testWithoutContext('encodeDartDefines encodes define values with base64 encoded components', () {
328 expect(encodeDartDefines(<String>['"hello"']), 'ImhlbGxvIg==');
329 expect(
330 encodeDartDefines(<String>['https://www.google.com']),
331 'aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbQ==',
332 );
333 expect(encodeDartDefines(<String>['2,3,4', '5']), 'MiwzLDQ=,NQ==');
334 expect(encodeDartDefines(<String>['true', 'false', 'flase']), 'dHJ1ZQ==,ZmFsc2U=,Zmxhc2U=');
335 expect(encodeDartDefines(<String>['1232,456', '2']), 'MTIzMiw0NTY=,Mg==');
336 });
337
338 testWithoutContext('decodeDartDefines decodes base64 encoded dart defines', () {
339 expect(
340 decodeDartDefines(<String, String>{kDartDefines: 'ImhlbGxvIg=='}, kDartDefines),
341 <String>['"hello"'],
342 );
343 expect(
344 decodeDartDefines(<String, String>{
345 kDartDefines: 'aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbQ==',
346 }, kDartDefines),
347 <String>['https://www.google.com'],
348 );
349 expect(
350 decodeDartDefines(<String, String>{kDartDefines: 'MiwzLDQ=,NQ=='}, kDartDefines),
351 <String>['2,3,4', '5'],
352 );
353 expect(
354 decodeDartDefines(<String, String>{kDartDefines: 'dHJ1ZQ==,ZmFsc2U=,Zmxhc2U='}, kDartDefines),
355 <String>['true', 'false', 'flase'],
356 );
357 expect(
358 decodeDartDefines(<String, String>{kDartDefines: 'MTIzMiw0NTY=,Mg=='}, kDartDefines),
359 <String>['1232,456', '2'],
360 );
361 });
362
363 testWithoutContext('BuildMode names', () {
364 for (final BuildMode buildMode in BuildMode.values) {
365 switch (buildMode) {
366 case BuildMode.debug:
367 expect(buildMode.cliName, 'debug');
368 expect(buildMode.uppercaseName, 'Debug');
369 expect(buildMode.friendlyName, 'debug');
370 expect(buildMode.uppercaseFriendlyName, 'Debug');
371 case BuildMode.profile:
372 expect(buildMode.cliName, 'profile');
373 expect(buildMode.uppercaseName, 'Profile');
374 expect(buildMode.friendlyName, 'profile');
375 expect(buildMode.uppercaseFriendlyName, 'Profile');
376 case BuildMode.release:
377 expect(buildMode.cliName, 'release');
378 expect(buildMode.uppercaseName, 'Release');
379 expect(buildMode.friendlyName, 'release');
380 expect(buildMode.uppercaseFriendlyName, 'Release');
381 case BuildMode.jitRelease:
382 expect(buildMode.cliName, 'jit_release');
383 expect(buildMode.uppercaseName, 'Jit_release');
384 expect(buildMode.friendlyName, 'jit release');
385 expect(buildMode.uppercaseFriendlyName, 'Jit release');
386 }
387 }
388 });
389}
390