| 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 'package:file/memory.dart' ; |
| 6 | |
| 7 | import 'package:flutter_tools/src/artifacts.dart'; |
| 8 | import 'package:flutter_tools/src/base/file_system.dart'; |
| 9 | import 'package:flutter_tools/src/base/logger.dart'; |
| 10 | import 'package:flutter_tools/src/build_info.dart'; |
| 11 | |
| 12 | import '../src/common.dart'; |
| 13 | import '../src/context.dart'; |
| 14 | import '../src/test_build_system.dart'; |
| 15 | |
| 16 | void 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 |
|