[tools]build ipa validate template icon files (#114841)
* [tools]build ipa validate template icon files * use the same box for both validations, and added some unit test, and some nits * add unit test for templateImageDirectory * use fs.path.join intead of raw path * use the correct filesystem * lint * use absolute path for flutter_template_images * fix rebase * update indentation
This commit is contained in:
parent
b5345ff55e
commit
dcae424783
@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
@ -130,7 +131,63 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
|
||||
return super.validateCommand();
|
||||
}
|
||||
|
||||
Future<void> _validateXcodeBuildSettingsAfterArchive() async {
|
||||
// Parses Contents.json into a map, with the key to be the combination of (idiom, size, scale), and value to be the icon image file name.
|
||||
Map<String, String> _parseIconContentsJson(String contentsJsonDirName) {
|
||||
final Directory contentsJsonDirectory = globals.fs.directory(contentsJsonDirName);
|
||||
if (!contentsJsonDirectory.existsSync()) {
|
||||
return <String, String>{};
|
||||
}
|
||||
final File contentsJsonFile = contentsJsonDirectory.childFile('Contents.json');
|
||||
final Map<String, dynamic> content = json.decode(contentsJsonFile.readAsStringSync()) as Map<String, dynamic>;
|
||||
final List<dynamic> images = content['images'] as List<dynamic>? ?? <dynamic>[];
|
||||
|
||||
final Map<String, String> iconInfo = <String, String>{};
|
||||
|
||||
for (final dynamic image in images) {
|
||||
final Map<String, dynamic> imageMap = image as Map<String, dynamic>;
|
||||
final String? idiom = imageMap['idiom'] as String?;
|
||||
final String? size = imageMap['size'] as String?;
|
||||
final String? scale = imageMap['scale'] as String?;
|
||||
final String? fileName = imageMap['filename'] as String?;
|
||||
|
||||
if (size != null && idiom != null && scale != null && fileName != null) {
|
||||
iconInfo['$idiom $size $scale'] = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
return iconInfo;
|
||||
}
|
||||
|
||||
Future<void> _validateIconsAfterArchive(StringBuffer messageBuffer) async {
|
||||
final BuildableIOSApp app = await buildableIOSApp;
|
||||
final String templateIconImageDirName = await app.templateAppIconDirNameForImages;
|
||||
|
||||
final Map<String, String> templateIconMap = _parseIconContentsJson(app.templateAppIconDirNameForContentsJson);
|
||||
final Map<String, String> projectIconMap = _parseIconContentsJson(app.projectAppIconDirName);
|
||||
|
||||
// find if any of the project icons conflict with template icons
|
||||
final bool hasConflict = projectIconMap.entries
|
||||
.where((MapEntry<String, String> entry) {
|
||||
final String projectIconFileName = entry.value;
|
||||
final String? templateIconFileName = templateIconMap[entry.key];
|
||||
if (templateIconFileName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final File projectIconFile = globals.fs.file(globals.fs.path.join(app.projectAppIconDirName, projectIconFileName));
|
||||
final File templateIconFile = globals.fs.file(globals.fs.path.join(templateIconImageDirName, templateIconFileName));
|
||||
return projectIconFile.existsSync()
|
||||
&& templateIconFile.existsSync()
|
||||
&& md5.convert(projectIconFile.readAsBytesSync()) == md5.convert(templateIconFile.readAsBytesSync());
|
||||
})
|
||||
.isNotEmpty;
|
||||
|
||||
if (hasConflict) {
|
||||
messageBuffer.writeln('\nWarning: App icon is set to the default placeholder icon. Replace with unique icons.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _validateXcodeBuildSettingsAfterArchive(StringBuffer messageBuffer) async {
|
||||
final BuildableIOSApp app = await buildableIOSApp;
|
||||
|
||||
final String plistPath = app.builtInfoPlistPathAfterArchive;
|
||||
@ -148,21 +205,13 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
|
||||
xcodeProjectSettingsMap['Deployment Target'] = globals.plistParser.getStringValueFromFile(plistPath, PlistParser.kMinimumOSVersionKey);
|
||||
xcodeProjectSettingsMap['Bundle Identifier'] = globals.plistParser.getStringValueFromFile(plistPath, PlistParser.kCFBundleIdentifierKey);
|
||||
|
||||
final StringBuffer buffer = StringBuffer();
|
||||
xcodeProjectSettingsMap.forEach((String title, String? info) {
|
||||
buffer.writeln('$title: ${info ?? "Missing"}');
|
||||
messageBuffer.writeln('$title: ${info ?? "Missing"}');
|
||||
});
|
||||
|
||||
final String message;
|
||||
if (xcodeProjectSettingsMap.values.any((String? element) => element == null)) {
|
||||
buffer.writeln('\nYou must set up the missing settings');
|
||||
buffer.write('Instructions: https://docs.flutter.dev/deployment/ios');
|
||||
message = buffer.toString();
|
||||
} else {
|
||||
// remove the new line
|
||||
message = buffer.toString().trim();
|
||||
messageBuffer.writeln('\nYou must set up the missing settings.');
|
||||
}
|
||||
globals.printBox(message, title: 'App Settings');
|
||||
}
|
||||
|
||||
@override
|
||||
@ -171,7 +220,11 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
|
||||
displayNullSafetyMode(buildInfo);
|
||||
final FlutterCommandResult xcarchiveResult = await super.runCommand();
|
||||
|
||||
await _validateXcodeBuildSettingsAfterArchive();
|
||||
final StringBuffer validationMessageBuffer = StringBuffer();
|
||||
await _validateXcodeBuildSettingsAfterArchive(validationMessageBuffer);
|
||||
await _validateIconsAfterArchive(validationMessageBuffer);
|
||||
validationMessageBuffer.write('\nTo update the settings, please refer to https://docs.flutter.dev/deployment/ios');
|
||||
globals.printBox(validationMessageBuffer.toString(), title: 'App Settings');
|
||||
|
||||
// xcarchive failed or not at expected location.
|
||||
if (xcarchiveResult.exitStatus != ExitStatus.success) {
|
||||
|
@ -5,7 +5,9 @@
|
||||
import '../application_package.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../build_info.dart';
|
||||
import '../cache.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../template.dart';
|
||||
import '../xcode_project.dart';
|
||||
import 'plist_parser.dart';
|
||||
|
||||
@ -151,12 +153,42 @@ class BuildableIOSApp extends IOSApp {
|
||||
_hostAppBundleName == null ? 'Runner.app' : _hostAppBundleName!,
|
||||
'Info.plist');
|
||||
|
||||
// Both project icon's image assets and Contents.json are in the same directory.
|
||||
String get projectAppIconDirName => globals.fs.path.join('ios', _appIconDirNameSuffix);
|
||||
|
||||
// template icon's Contents.json is in flutter_tools.
|
||||
String get templateAppIconDirNameForContentsJson => globals.fs.path.join(
|
||||
Cache.flutterRoot!,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'templates',
|
||||
'app_shared',
|
||||
'ios.tmpl',
|
||||
_appIconDirNameSuffix,
|
||||
);
|
||||
|
||||
// template icon's image assets are in flutter_template_images package.
|
||||
Future<String> get templateAppIconDirNameForImages async {
|
||||
final Directory imageTemplate = await templateImageDirectory(null, globals.fs, globals.logger);
|
||||
return globals.fs.path.join(
|
||||
imageTemplate.path,
|
||||
'app_shared',
|
||||
'ios.tmpl',
|
||||
_appIconDirNameSuffix,
|
||||
);
|
||||
}
|
||||
|
||||
String get ipaOutputPath =>
|
||||
globals.fs.path.join(getIosBuildDirectory(), 'ipa');
|
||||
|
||||
String _buildAppPath(String type) {
|
||||
return globals.fs.path.join(getIosBuildDirectory(), type, _hostAppBundleName);
|
||||
}
|
||||
|
||||
String get _appIconDirNameSuffix => globals.fs.path.join(
|
||||
'Runner',
|
||||
'Assets.xcassets',
|
||||
'AppIcon.appiconset');
|
||||
}
|
||||
|
||||
class PrebuiltIOSApp extends IOSApp implements PrebuiltApplicationPackage {
|
||||
|
@ -101,7 +101,7 @@ class Template {
|
||||
}) async {
|
||||
// All named templates are placed in the 'templates' directory
|
||||
final Directory templateDir = _templateDirectoryInPackage(name, fileSystem);
|
||||
final Directory imageDir = await _templateImageDirectory(name, fileSystem, logger);
|
||||
final Directory imageDir = await templateImageDirectory(name, fileSystem, logger);
|
||||
return Template._(
|
||||
<Directory>[templateDir],
|
||||
<Directory>[imageDir],
|
||||
@ -126,8 +126,8 @@ class Template {
|
||||
],
|
||||
<Directory>[
|
||||
for (final String name in names)
|
||||
if ((await _templateImageDirectory(name, fileSystem, logger)).existsSync())
|
||||
await _templateImageDirectory(name, fileSystem, logger),
|
||||
if ((await templateImageDirectory(name, fileSystem, logger)).existsSync())
|
||||
await templateImageDirectory(name, fileSystem, logger),
|
||||
],
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
@ -350,9 +350,10 @@ Directory _templateDirectoryInPackage(String name, FileSystem fileSystem) {
|
||||
return fileSystem.directory(fileSystem.path.join(templatesDir, name));
|
||||
}
|
||||
|
||||
// Returns the directory containing the 'name' template directory in
|
||||
// flutter_template_images, to resolve image placeholder against.
|
||||
Future<Directory> _templateImageDirectory(String name, FileSystem fileSystem, Logger logger) async {
|
||||
/// Returns the directory containing the 'name' template directory in
|
||||
/// flutter_template_images, to resolve image placeholder against.
|
||||
/// if 'name' is null, return the parent template directory.
|
||||
Future<Directory> templateImageDirectory(String? name, FileSystem fileSystem, Logger logger) async {
|
||||
final String toolPackagePath = fileSystem.path.join(
|
||||
Cache.flutterRoot!, 'packages', 'flutter_tools');
|
||||
final String packageFilePath = fileSystem.path.join(toolPackagePath, '.dart_tool', 'package_config.json');
|
||||
@ -361,10 +362,10 @@ Future<Directory> _templateImageDirectory(String name, FileSystem fileSystem, Lo
|
||||
logger: logger,
|
||||
);
|
||||
final Uri? imagePackageLibDir = packageConfig['flutter_template_images']?.packageUriRoot;
|
||||
return fileSystem.directory(imagePackageLibDir)
|
||||
final Directory templateDirectory = fileSystem.directory(imagePackageLibDir)
|
||||
.parent
|
||||
.childDirectory('templates')
|
||||
.childDirectory(name);
|
||||
.childDirectory('templates');
|
||||
return name == null ? templateDirectory : templateDirectory.childDirectory(name);
|
||||
}
|
||||
|
||||
String _escapeKotlinKeywords(String androidIdentifier) {
|
||||
|
@ -94,6 +94,22 @@ void main() {
|
||||
fileSystem.directory(fileSystem.path.join('ios', 'Runner.xcodeproj')).createSync(recursive: true);
|
||||
fileSystem.directory(fileSystem.path.join('ios', 'Runner.xcworkspace')).createSync(recursive: true);
|
||||
fileSystem.file(fileSystem.path.join('ios', 'Runner.xcodeproj', 'project.pbxproj')).createSync();
|
||||
final String packageConfigPath = '${Cache.flutterRoot!}/packages/flutter_tools/.dart_tool/package_config.json';
|
||||
fileSystem.file(packageConfigPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "flutter_template_images",
|
||||
"rootUri": "/flutter_template_images",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
createCoreMockProjectFiles();
|
||||
}
|
||||
|
||||
@ -898,20 +914,20 @@ void main() {
|
||||
<String>['build', 'ipa', '--no-pub']);
|
||||
|
||||
expect(
|
||||
testLogger.statusText,
|
||||
contains(
|
||||
'┌─ App Settings ────────────────────────────────────────┐\n'
|
||||
'│ Version Number: Missing │\n'
|
||||
'│ Build Number: Missing │\n'
|
||||
'│ Display Name: Missing │\n'
|
||||
'│ Deployment Target: Missing │\n'
|
||||
'│ Bundle Identifier: io.flutter.someProject │\n'
|
||||
'│ │\n'
|
||||
'│ You must set up the missing settings │\n'
|
||||
'│ Instructions: https://docs.flutter.dev/deployment/ios │\n'
|
||||
'└───────────────────────────────────────────────────────┘'
|
||||
)
|
||||
);
|
||||
testLogger.statusText,
|
||||
contains(
|
||||
'┌─ App Settings ──────────────────────────────────────────────────────────────────┐\n'
|
||||
'│ Version Number: Missing │\n'
|
||||
'│ Build Number: Missing │\n'
|
||||
'│ Display Name: Missing │\n'
|
||||
'│ Deployment Target: Missing │\n'
|
||||
'│ Bundle Identifier: io.flutter.someProject │\n'
|
||||
'│ │\n'
|
||||
'│ You must set up the missing settings. │\n'
|
||||
'│ │\n'
|
||||
'│ To update the settings, please refer to https://docs.flutter.dev/deployment/ios │\n'
|
||||
'└─────────────────────────────────────────────────────────────────────────────────┘\n'
|
||||
));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => fakeProcessManager,
|
||||
@ -952,16 +968,18 @@ void main() {
|
||||
<String>['build', 'ipa', '--no-pub']);
|
||||
|
||||
expect(
|
||||
testLogger.statusText,
|
||||
contains(
|
||||
'┌─ App Settings ────────────────────────────┐\n'
|
||||
'│ Version Number: 12.34.56 │\n'
|
||||
'│ Build Number: 666 │\n'
|
||||
'│ Display Name: Awesome Gallery │\n'
|
||||
'│ Deployment Target: 11.0 │\n'
|
||||
'│ Bundle Identifier: io.flutter.someProject │\n'
|
||||
'└───────────────────────────────────────────┘\n'
|
||||
)
|
||||
testLogger.statusText,
|
||||
contains(
|
||||
'┌─ App Settings ──────────────────────────────────────────────────────────────────┐\n'
|
||||
'│ Version Number: 12.34.56 │\n'
|
||||
'│ Build Number: 666 │\n'
|
||||
'│ Display Name: Awesome Gallery │\n'
|
||||
'│ Deployment Target: 11.0 │\n'
|
||||
'│ Bundle Identifier: io.flutter.someProject │\n'
|
||||
'│ │\n'
|
||||
'│ To update the settings, please refer to https://docs.flutter.dev/deployment/ios │\n'
|
||||
'└─────────────────────────────────────────────────────────────────────────────────┘\n'
|
||||
)
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
@ -971,7 +989,147 @@ void main() {
|
||||
PlistParser: () => plistUtils,
|
||||
});
|
||||
|
||||
|
||||
testUsingContext('Validate template app icons with conflicts', () async {
|
||||
const String projectIconContentsJsonPath = 'ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json';
|
||||
const String projectIconImagePath = 'ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png';
|
||||
final String templateIconContentsJsonPath = '${Cache.flutterRoot!}/packages/flutter_tools/templates/app_shared/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json';
|
||||
const String templateIconImagePath = '/flutter_template_images/templates/app_shared/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png';
|
||||
|
||||
fakeProcessManager.addCommands(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpFakeXcodeBuildHandler(onRun: () {
|
||||
fileSystem.file(templateIconContentsJsonPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename": "Icon-App-20x20@2x.png",
|
||||
"scale": "2x"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
fileSystem.file(templateIconImagePath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytes(<int>[1, 2, 3]);
|
||||
|
||||
fileSystem.file(projectIconContentsJsonPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename": "Icon-App-20x20@2x.png",
|
||||
"scale": "2x"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
fileSystem.file(projectIconImagePath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytes(<int>[1, 2, 3]);
|
||||
}),
|
||||
exportArchiveCommand(exportOptionsPlist: _exportOptionsPlist),
|
||||
]);
|
||||
|
||||
createMinimalMockProjectFiles();
|
||||
|
||||
final BuildCommand command = BuildCommand(
|
||||
androidSdk: FakeAndroidSdk(),
|
||||
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
logger: BufferLogger.test(),
|
||||
osUtils: FakeOperatingSystemUtils(),
|
||||
);
|
||||
await createTestCommandRunner(command).run(
|
||||
<String>['build', 'ipa', '--no-pub']);
|
||||
|
||||
expect(
|
||||
testLogger.statusText,
|
||||
contains('Warning: App icon is set to the default placeholder icon. Replace with unique icons.'),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => fakeProcessManager,
|
||||
Platform: () => macosPlatform,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
|
||||
testUsingContext('Validate template app icons without conflicts', () async {
|
||||
const String projectIconContentsJsonPath = 'ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json';
|
||||
const String projectIconImagePath = 'ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png';
|
||||
final String templateIconContentsJsonPath = '${Cache.flutterRoot!}/packages/flutter_tools/templates/app_shared/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json';
|
||||
const String templateIconImagePath = '/flutter_template_images/templates/app_shared/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png';
|
||||
|
||||
fakeProcessManager.addCommands(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpFakeXcodeBuildHandler(onRun: () {
|
||||
fileSystem.file(templateIconContentsJsonPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename": "Icon-App-20x20@2x.png",
|
||||
"scale": "2x"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
fileSystem.file(templateIconImagePath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytes(<int>[1, 2, 3]);
|
||||
|
||||
fileSystem.file(projectIconContentsJsonPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"size": "20x20",
|
||||
"idiom": "iphone",
|
||||
"filename": "Icon-App-20x20@2x.png",
|
||||
"scale": "2x"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
fileSystem.file(projectIconImagePath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytes(<int>[4, 5, 6]);
|
||||
}),
|
||||
exportArchiveCommand(exportOptionsPlist: _exportOptionsPlist),
|
||||
]);
|
||||
|
||||
createMinimalMockProjectFiles();
|
||||
|
||||
final BuildCommand command = BuildCommand(
|
||||
androidSdk: FakeAndroidSdk(),
|
||||
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
logger: BufferLogger.test(),
|
||||
osUtils: FakeOperatingSystemUtils(),
|
||||
);
|
||||
await createTestCommandRunner(command).run(
|
||||
<String>['build', 'ipa', '--no-pub']);
|
||||
|
||||
expect(testLogger.statusText, isNot(contains('Warning: App icon is set to the default placeholder icon. Replace with unique icons.')));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => fakeProcessManager,
|
||||
Platform: () => macosPlatform,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const String _xcBundleFilePath = '/.tmp_rand0/flutter_ios_build_temp_dirrand0/temporary_xcresult_bundle';
|
||||
const String _exportOptionsPlist = '/.tmp_rand0/flutter_build_ios.rand0/ExportOptions.plist';
|
||||
|
@ -386,10 +386,96 @@ void main() {
|
||||
final Directory project = globals.fs.directory('ios/Runner.xcodeproj')..createSync(recursive: true);
|
||||
project.childFile('project.pbxproj').createSync();
|
||||
final BuildableIOSApp? iosApp = await IOSApp.fromIosProject(
|
||||
FlutterProject.fromDirectory(globals.fs.currentDirectory).ios, null) as BuildableIOSApp?;
|
||||
FlutterProject.fromDirectory(globals.fs.currentDirectory).ios, null) as BuildableIOSApp?;
|
||||
|
||||
expect(iosApp, null);
|
||||
}, overrides: overrides);
|
||||
|
||||
testUsingContext('returns project app icon dirname', () async {
|
||||
final BuildableIOSApp iosApp = BuildableIOSApp(
|
||||
IosProject.fromFlutter(FlutterProject.fromDirectory(globals.fs.currentDirectory)),
|
||||
'com.foo.bar',
|
||||
'Runner',
|
||||
);
|
||||
final String iconDirSuffix = globals.fs.path.join(
|
||||
'Runner',
|
||||
'Assets.xcassets',
|
||||
'AppIcon.appiconset',
|
||||
);
|
||||
expect(iosApp.projectAppIconDirName, globals.fs.path.join('ios', iconDirSuffix));
|
||||
}, overrides: overrides);
|
||||
|
||||
testUsingContext('returns template app icon dirname for Contents.json', () async {
|
||||
final BuildableIOSApp iosApp = BuildableIOSApp(
|
||||
IosProject.fromFlutter(FlutterProject.fromDirectory(globals.fs.currentDirectory)),
|
||||
'com.foo.bar',
|
||||
'Runner',
|
||||
);
|
||||
final String iconDirSuffix = globals.fs.path.join(
|
||||
'Runner',
|
||||
'Assets.xcassets',
|
||||
'AppIcon.appiconset',
|
||||
);
|
||||
expect(
|
||||
iosApp.templateAppIconDirNameForContentsJson,
|
||||
globals.fs.path.join(
|
||||
Cache.flutterRoot!,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'templates',
|
||||
'app_shared',
|
||||
'ios.tmpl',
|
||||
iconDirSuffix,
|
||||
),
|
||||
);
|
||||
}, overrides: overrides);
|
||||
|
||||
testUsingContext('returns template app icon dirname for images', () async {
|
||||
final String toolsDir = globals.fs.path.join(
|
||||
Cache.flutterRoot!,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
);
|
||||
final String packageConfigPath = globals.fs.path.join(
|
||||
toolsDir,
|
||||
'.dart_tool',
|
||||
'package_config.json'
|
||||
);
|
||||
globals.fs.file(packageConfigPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "flutter_template_images",
|
||||
"rootUri": "/flutter_template_images",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
final BuildableIOSApp iosApp = BuildableIOSApp(
|
||||
IosProject.fromFlutter(FlutterProject.fromDirectory(globals.fs.currentDirectory)),
|
||||
'com.foo.bar',
|
||||
'Runner');
|
||||
final String iconDirSuffix = globals.fs.path.join(
|
||||
'Runner',
|
||||
'Assets.xcassets',
|
||||
'AppIcon.appiconset',
|
||||
);
|
||||
expect(
|
||||
await iosApp.templateAppIconDirNameForImages,
|
||||
globals.fs.path.absolute(
|
||||
'flutter_template_images',
|
||||
'templates',
|
||||
'app_shared',
|
||||
'ios.tmpl',
|
||||
iconDirSuffix,
|
||||
),
|
||||
);
|
||||
}, overrides: overrides);
|
||||
});
|
||||
|
||||
group('FuchsiaApp', () {
|
||||
|
@ -9,8 +9,11 @@ import 'package:file_testing/file_testing.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/template.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/template.dart';
|
||||
import '../src/common.dart';
|
||||
import '../src/context.dart';
|
||||
|
||||
void main() {
|
||||
testWithoutContext('Template constructor throws ToolExit when source directory is missing', () {
|
||||
@ -43,6 +46,79 @@ void main() {
|
||||
throwsToolExit());
|
||||
});
|
||||
|
||||
group('template image directory', () {
|
||||
final Map<Type, Generator> overrides = <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
};
|
||||
|
||||
testUsingContext('templateImageDirectory returns parent template directory if passed null name', () async {
|
||||
final String packageConfigPath = globals.fs.path.join(
|
||||
Cache.flutterRoot!,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'.dart_tool',
|
||||
'package_config.json',
|
||||
);
|
||||
|
||||
globals.fs.file(packageConfigPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "flutter_template_images",
|
||||
"rootUri": "/flutter_template_images",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
expect(
|
||||
(await templateImageDirectory(null, globals.fs, globals.logger)).path,
|
||||
globals.fs.path.absolute(
|
||||
'flutter_template_images',
|
||||
'templates',
|
||||
),
|
||||
);
|
||||
}, overrides: overrides);
|
||||
|
||||
testUsingContext('templateImageDirectory returns the directory containing the `name` template directory', () async {
|
||||
final String packageConfigPath = globals.fs.path.join(
|
||||
Cache.flutterRoot!,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'.dart_tool',
|
||||
'package_config.json',
|
||||
);
|
||||
globals.fs.file(packageConfigPath)
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "flutter_template_images",
|
||||
"rootUri": "/flutter_template_images",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
expect(
|
||||
(await templateImageDirectory('app_shared', globals.fs, globals.logger)).path,
|
||||
globals.fs.path.absolute(
|
||||
'flutter_template_images',
|
||||
'templates',
|
||||
'app_shared',
|
||||
),
|
||||
);
|
||||
}, overrides: overrides);
|
||||
});
|
||||
|
||||
group('renders template', () {
|
||||
late Directory destination;
|
||||
const String imageName = 'some_image.png';
|
||||
|
Loading…
x
Reference in New Issue
Block a user