mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
214 lines
8.4 KiB
Dart
214 lines
8.4 KiB
Dart
// Copyright 2016 The Chromium 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:convert';
|
|
|
|
import 'package:flutter_tools/src/application_package.dart';
|
|
import 'package:flutter_tools/src/base/context.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/os.dart';
|
|
import 'package:flutter_tools/src/ios/ios_workflow.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:mockito/mockito.dart';
|
|
import 'src/context.dart';
|
|
|
|
void main() {
|
|
group('ApkManifestData', () {
|
|
testUsingContext('parse sdk', () {
|
|
final ApkManifestData data =
|
|
ApkManifestData.parseFromAaptBadging(_aaptData);
|
|
expect(data, isNotNull);
|
|
expect(data.packageName, 'io.flutter.gallery');
|
|
expect(data.launchableActivityName, 'io.flutter.app.FlutterActivity');
|
|
expect(data.data['application']['label'], 'Flutter Gallery');
|
|
});
|
|
});
|
|
|
|
group('BuildableIOSApp', () {
|
|
testUsingContext('check isSwift', () {
|
|
final BuildableIOSApp buildableIOSApp = new BuildableIOSApp(
|
|
projectBundleId: 'blah',
|
|
appDirectory: 'not/important',
|
|
buildSettings: _swiftBuildSettings,
|
|
);
|
|
expect(buildableIOSApp.isSwift, true);
|
|
});
|
|
});
|
|
|
|
group('PrebuiltIOSApp', () {
|
|
final Map<Type, Generator> overrides = <Type, Generator>{
|
|
FileSystem: () => new MemoryFileSystem(),
|
|
IOSWorkflow: () => new MockIosWorkFlow()
|
|
};
|
|
testUsingContext('Error on non-existing file', () {
|
|
final PrebuiltIOSApp iosApp =
|
|
new IOSApp.fromPrebuiltApp('not_existing.ipa');
|
|
expect(iosApp, isNull);
|
|
final BufferLogger logger = context[Logger];
|
|
expect(
|
|
logger.errorText,
|
|
'File "not_existing.ipa" does not exist. Use an app bundle or an ipa.\n',
|
|
);
|
|
}, overrides: overrides);
|
|
testUsingContext('Error on non-app-bundle folder', () {
|
|
fs.directory('regular_folder').createSync();
|
|
final PrebuiltIOSApp iosApp =
|
|
new IOSApp.fromPrebuiltApp('regular_folder');
|
|
expect(iosApp, isNull);
|
|
final BufferLogger logger = context[Logger];
|
|
expect(
|
|
logger.errorText, 'Folder "regular_folder" is not an app bundle.\n');
|
|
}, overrides: overrides);
|
|
testUsingContext('Error on no info.plist', () {
|
|
fs.directory('bundle.app').createSync();
|
|
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('bundle.app');
|
|
expect(iosApp, isNull);
|
|
final BufferLogger logger = context[Logger];
|
|
expect(
|
|
logger.errorText,
|
|
'Invalid prebuilt iOS app. Does not contain Info.plist.\n',
|
|
);
|
|
}, overrides: overrides);
|
|
testUsingContext('Error on bad info.plist', () {
|
|
fs.directory('bundle.app').createSync();
|
|
fs.file('bundle.app/Info.plist').writeAsStringSync(badPlistData);
|
|
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('bundle.app');
|
|
expect(iosApp, isNull);
|
|
final BufferLogger logger = context[Logger];
|
|
expect(
|
|
logger.errorText,
|
|
contains(
|
|
'Invalid prebuilt iOS app. Info.plist does not contain bundle identifier\n'),
|
|
);
|
|
}, overrides: overrides);
|
|
testUsingContext('Success with app bundle', () {
|
|
fs.directory('bundle.app').createSync();
|
|
fs.file('bundle.app/Info.plist').writeAsStringSync(plistData);
|
|
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('bundle.app');
|
|
final BufferLogger logger = context[Logger];
|
|
expect(logger.errorText, isEmpty);
|
|
expect(iosApp.bundleDir.path, 'bundle.app');
|
|
expect(iosApp.id, 'fooBundleId');
|
|
expect(iosApp.bundleName, 'bundle.app');
|
|
}, overrides: overrides);
|
|
testUsingContext('Bad ipa zip-file, no payload dir', () {
|
|
fs.file('app.ipa').createSync();
|
|
when(os.unzip(fs.file('app.ipa'), any)).thenAnswer((Invocation _) {});
|
|
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('app.ipa');
|
|
expect(iosApp, isNull);
|
|
final BufferLogger logger = context[Logger];
|
|
expect(
|
|
logger.errorText,
|
|
'Invalid prebuilt iOS ipa. Does not contain a "Payload" directory.\n',
|
|
);
|
|
}, overrides: overrides);
|
|
testUsingContext('Bad ipa zip-file, two app bundles', () {
|
|
fs.file('app.ipa').createSync();
|
|
when(os.unzip(any, any)).thenAnswer((Invocation invocation) {
|
|
final File zipFile = invocation.positionalArguments[0];
|
|
if (zipFile.path != 'app.ipa') {
|
|
return null;
|
|
}
|
|
final Directory targetDirectory = invocation.positionalArguments[1];
|
|
final String bundlePath1 =
|
|
fs.path.join(targetDirectory.path, 'Payload', 'bundle1.app');
|
|
final String bundlePath2 =
|
|
fs.path.join(targetDirectory.path, 'Payload', 'bundle2.app');
|
|
fs.directory(bundlePath1).createSync(recursive: true);
|
|
fs.directory(bundlePath2).createSync(recursive: true);
|
|
});
|
|
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('app.ipa');
|
|
expect(iosApp, isNull);
|
|
final BufferLogger logger = context[Logger];
|
|
expect(logger.errorText,
|
|
'Invalid prebuilt iOS ipa. Does not contain a single app bundle.\n');
|
|
}, overrides: overrides);
|
|
testUsingContext('Success with ipa', () {
|
|
fs.file('app.ipa').createSync();
|
|
when(os.unzip(any, any)).thenAnswer((Invocation invocation) {
|
|
final File zipFile = invocation.positionalArguments[0];
|
|
if (zipFile.path != 'app.ipa') {
|
|
return null;
|
|
}
|
|
final Directory targetDirectory = invocation.positionalArguments[1];
|
|
final Directory bundleAppDir = fs.directory(
|
|
fs.path.join(targetDirectory.path, 'Payload', 'bundle.app'));
|
|
bundleAppDir.createSync(recursive: true);
|
|
fs
|
|
.file(fs.path.join(bundleAppDir.path, 'Info.plist'))
|
|
.writeAsStringSync(plistData);
|
|
});
|
|
final PrebuiltIOSApp iosApp = new IOSApp.fromPrebuiltApp('app.ipa');
|
|
final BufferLogger logger = context[Logger];
|
|
expect(logger.errorText, isEmpty);
|
|
expect(iosApp.bundleDir.path, endsWith('bundle.app'));
|
|
expect(iosApp.id, 'fooBundleId');
|
|
expect(iosApp.bundleName, 'bundle.app');
|
|
}, overrides: overrides);
|
|
});
|
|
}
|
|
|
|
const String _aaptData = '''
|
|
package: name='io.flutter.gallery' versionCode='1' versionName='0.0.1' platformBuildVersionName='NMR1'
|
|
sdkVersion:'14'
|
|
targetSdkVersion:'21'
|
|
uses-permission: name='android.permission.INTERNET'
|
|
application-label:'Flutter Gallery'
|
|
application-icon-160:'res/mipmap-mdpi-v4/ic_launcher.png'
|
|
application-icon-240:'res/mipmap-hdpi-v4/ic_launcher.png'
|
|
application-icon-320:'res/mipmap-xhdpi-v4/ic_launcher.png'
|
|
application-icon-480:'res/mipmap-xxhdpi-v4/ic_launcher.png'
|
|
application-icon-640:'res/mipmap-xxxhdpi-v4/ic_launcher.png'
|
|
application: label='Flutter Gallery' icon='res/mipmap-mdpi-v4/ic_launcher.png'
|
|
application-debuggable
|
|
launchable-activity: name='io.flutter.app.FlutterActivity' label='' icon=''
|
|
feature-group: label=''
|
|
uses-feature: name='android.hardware.screen.portrait'
|
|
uses-implied-feature: name='android.hardware.screen.portrait' reason='one or more activities have specified a portrait orientation'
|
|
uses-feature: name='android.hardware.touchscreen'
|
|
uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
|
|
main
|
|
supports-screens: 'small' 'normal' 'large' 'xlarge'
|
|
supports-any-density: 'true'
|
|
locales: '--_--'
|
|
densities: '160' '240' '320' '480' '640'
|
|
native-code: 'armeabi-v7a'
|
|
''';
|
|
|
|
final Map<String, String> _swiftBuildSettings = <String, String>{
|
|
'ARCHS': 'arm64',
|
|
'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon',
|
|
'CLANG_ENABLE_MODULES': 'YES',
|
|
'ENABLE_BITCODE': 'NO',
|
|
'INFOPLIST_FILE': 'Runner/Info.plist',
|
|
'PRODUCT_BUNDLE_IDENTIFIER': 'com.example.test',
|
|
'PRODUCT_NAME': 'blah',
|
|
'SWIFT_OBJC_BRIDGING_HEADER': 'Runner/Runner-Bridging-Header.h',
|
|
'SWIFT_OPTIMIZATION_LEVEL': '-Onone',
|
|
'SWIFT_VERSION': '3.0',
|
|
};
|
|
|
|
class MockIosWorkFlow extends Mock implements IOSWorkflow {
|
|
@override
|
|
String getPlistValueFromFile(String path, String key) {
|
|
final File file = fs.file(path);
|
|
if (!file.existsSync()) {
|
|
return null;
|
|
}
|
|
return json.decode(file.readAsStringSync())[key];
|
|
}
|
|
}
|
|
|
|
// Contains no bundle identifier.
|
|
const String badPlistData = '''
|
|
{}
|
|
''';
|
|
|
|
const String plistData = '''
|
|
{"CFBundleIdentifier": "fooBundleId"}
|
|
''';
|