| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:io'; |
| |
| import 'package:flutter_devicelab/framework/apk_utils.dart'; |
| import 'package:flutter_devicelab/framework/framework.dart'; |
| import 'package:flutter_devicelab/framework/task_result.dart'; |
| import 'package:flutter_devicelab/framework/utils.dart'; |
| import 'package:path/path.dart' as path; |
| |
| Future<void> main() async { |
| await task(() async { |
| try { |
| await runProjectTest((FlutterProject flutterProject) async { |
| section('Archive'); |
| |
| await inDirectory(flutterProject.rootPath, () async { |
| final File appIconFile = File(path.join( |
| flutterProject.rootPath, |
| 'ios', |
| 'Runner', |
| 'Assets.xcassets', |
| 'AppIcon.appiconset', |
| 'Icon-App-20x20@1x.png', |
| )); |
| // Resizes app icon to 123x456 (it is supposed to be 20x20). |
| appIconFile.writeAsBytesSync(appIconFile.readAsBytesSync() |
| ..buffer.asByteData().setInt32(16, 123) |
| ..buffer.asByteData().setInt32(20, 456) |
| ); |
| |
| final String output = await evalFlutter('build', options: <String>[ |
| 'xcarchive', |
| '-v', |
| ]); |
| |
| // Note this isBot so usage won't actually be sent, |
| // this log line is printed whenever the app is archived. |
| if (!output.contains('Sending archive event if usage enabled')) { |
| throw TaskResult.failure('Usage archive event not sent'); |
| } |
| |
| // The output contains extra time related prefix, so cannot use a single string. |
| const List<String> expectedValidationMessages = <String>[ |
| '[!] App Settings Validation\n', |
| ' • Version Number: 1.0.0\n', |
| ' • Build Number: 1\n', |
| ' • Display Name: Hello\n', |
| ' • Deployment Target: 11.0\n', |
| ' • Bundle Identifier: com.example.hello\n', |
| ' ! Your application still contains the default "com.example" bundle identifier.\n', |
| '[!] App Icon and Launch Image Assets Validation\n', |
| ' ! App icon is set to the default placeholder icon. Replace with unique icons.\n', |
| ' ! App icon is using the incorrect size (e.g. Icon-App-20x20@1x.png).\n', |
| ' ! Launch image is set to the default placeholder icon. Replace with unique launch image.\n', |
| 'To update the settings, please refer to https://docs.flutter.dev/deployment/ios\n', |
| ]; |
| if (expectedValidationMessages.any((String message) => !output.contains(message))) { |
| throw TaskResult.failure('Must have the expected validation message'); |
| } |
| }); |
| |
| final String archivePath = path.join( |
| flutterProject.rootPath, |
| 'build', |
| 'ios', |
| 'archive', |
| 'Runner.xcarchive', |
| ); |
| |
| final String products = path.join(archivePath, 'Products'); |
| |
| checkDirectoryExists(products); |
| |
| checkDirectoryExists(path.join( |
| archivePath, |
| 'dSYMs', |
| 'Runner.app.dSYM', |
| )); |
| final Directory applications = Directory(path.join(products, 'Applications')); |
| |
| final Directory appBundle = applications |
| .listSync() |
| .whereType<Directory>() |
| .singleWhere((Directory directory) => path.extension(directory.path) == '.app'); |
| |
| final String flutterFramework = path.join( |
| appBundle.path, |
| 'Frameworks', |
| 'Flutter.framework', |
| 'Flutter', |
| ); |
| // Exits 0 only if codesigned. |
| final Future<String> flutterCodesign = |
| eval('xcrun', <String>['codesign', '--verify', flutterFramework]); |
| |
| final String appFramework = path.join( |
| appBundle.path, |
| 'Frameworks', |
| 'App.framework', |
| 'App', |
| ); |
| final Future<String> appCodesign = |
| eval('xcrun', <String>['codesign', '--verify', appFramework]); |
| await flutterCodesign; |
| await appCodesign; |
| }); |
| |
| return TaskResult.success(null); |
| } on TaskResult catch (taskResult) { |
| return taskResult; |
| } catch (e) { |
| return TaskResult.failure(e.toString()); |
| } |
| }); |
| } |