Unverified Commit c8c55b40 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Check Xcode build setting FULL_PRODUCT_NAME for bundle name (#55799)

parent 741ef919
...@@ -73,15 +73,15 @@ elif [[ "$OS" == "darwin" ]]; then ...@@ -73,15 +73,15 @@ elif [[ "$OS" == "darwin" ]]; then
exit 1 exit 1
fi fi
if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then if [[ ! -d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets" ]]; then
echo "Error: flutter_assets not assembled" echo "Error: flutter_assets not assembled"
exit 1 exit 1
fi fi
if [[ if [[
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" || -d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" ||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" || -d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" ||
-d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data" -d "build/ios/iphoneos/Flutter Gallery.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data"
]]; then ]]; then
echo "Error: compiled debug version of app with --release flag" echo "Error: compiled debug version of app with --release flag"
exit 1 exit 1
......
...@@ -451,7 +451,15 @@ class CompileTest { ...@@ -451,7 +451,15 @@ class CompileTest {
watch.start(); watch.start();
await flutter('build', options: options); await flutter('build', options: options);
watch.stop(); watch.stop();
final String appPath = '$cwd/build/ios/Release-iphoneos/Runner.app/'; final Directory appBuildDirectory = dir(path.join(cwd, 'build/ios/Release-iphoneos'));
final Directory appBundle = appBuildDirectory
.listSync()
.whereType<Directory>()
.singleWhere((Directory directory) => path.extension(directory.path) == '.app', orElse: () => null);
if (appBundle == null) {
throw 'Failed to find app bundle in ${appBuildDirectory.path}';
}
final String appPath = appBundle.path;
// IPAs are created manually, https://flutter.dev/ios-release/ // IPAs are created manually, https://flutter.dev/ios-release/
await exec('tar', <String>['-zcf', 'build/app.ipa', appPath]); await exec('tar', <String>['-zcf', 'build/app.ipa', appPath]);
releaseSizeInBytes = await file('$cwd/build/app.ipa').length(); releaseSizeInBytes = await file('$cwd/build/app.ipa').length();
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146EE1CF9000F007C117D /* Flutter Gallery.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Flutter Gallery.app"; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
97C146EF1CF9000F007C117D /* Products */ = { 97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146EE1CF9000F007C117D /* Runner.app */, 97C146EE1CF9000F007C117D /* Flutter Gallery.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -157,7 +157,7 @@ ...@@ -157,7 +157,7 @@
); );
name = Runner; name = Runner;
productName = Runner; productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productReference = 97C146EE1CF9000F007C117D /* Flutter Gallery.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
...@@ -369,7 +369,7 @@ ...@@ -369,7 +369,7 @@
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery; PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "Flutter Gallery";
}; };
name = Profile; name = Profile;
}; };
...@@ -494,7 +494,7 @@ ...@@ -494,7 +494,7 @@
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery; PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "Flutter Gallery";
}; };
name = Debug; name = Debug;
}; };
...@@ -515,7 +515,7 @@ ...@@ -515,7 +515,7 @@
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery; PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "Flutter Gallery";
}; };
name = Release; name = Release;
}; };
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Flutter Gallery.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
...@@ -27,19 +27,17 @@ ...@@ -27,19 +27,17 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Flutter Gallery.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <Testables>
</AdditionalOptions> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
...@@ -56,13 +54,11 @@ ...@@ -56,13 +54,11 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Flutter Gallery.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Profile" buildConfiguration = "Profile"
...@@ -75,7 +71,7 @@ ...@@ -75,7 +71,7 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Flutter Gallery.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Flutter Gallery</string> <string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
......
...@@ -357,18 +357,22 @@ abstract class IOSApp extends ApplicationPackage { ...@@ -357,18 +357,22 @@ abstract class IOSApp extends ApplicationPackage {
} }
class BuildableIOSApp extends IOSApp { class BuildableIOSApp extends IOSApp {
BuildableIOSApp(this.project, String projectBundleId) BuildableIOSApp(this.project, String projectBundleId, String hostAppBundleName)
: super(projectBundleId: projectBundleId); : _hostAppBundleName = hostAppBundleName,
super(projectBundleId: projectBundleId);
static Future<BuildableIOSApp> fromProject(IosProject project) async { static Future<BuildableIOSApp> fromProject(IosProject project) async {
final String projectBundleId = await project.productBundleIdentifier; final String projectBundleId = await project.productBundleIdentifier;
return BuildableIOSApp(project, projectBundleId); final String hostAppBundleName = await project.hostAppBundleName;
return BuildableIOSApp(project, projectBundleId, hostAppBundleName);
} }
final IosProject project; final IosProject project;
final String _hostAppBundleName;
@override @override
String get name => project.hostAppBundleName; String get name => _hostAppBundleName;
@override @override
String get simulatorBundlePath => _buildAppPath('iphonesimulator'); String get simulatorBundlePath => _buildAppPath('iphonesimulator');
...@@ -377,7 +381,7 @@ class BuildableIOSApp extends IOSApp { ...@@ -377,7 +381,7 @@ class BuildableIOSApp extends IOSApp {
String get deviceBundlePath => _buildAppPath('iphoneos'); String get deviceBundlePath => _buildAppPath('iphoneos');
String _buildAppPath(String type) { String _buildAppPath(String type) {
return globals.fs.path.join(getIosBuildDirectory(), type, name); return globals.fs.path.join(getIosBuildDirectory(), type, _hostAppBundleName);
} }
} }
......
...@@ -632,7 +632,7 @@ bool upgradePbxProjWithFlutterAssets(IosProject project, Logger logger) { ...@@ -632,7 +632,7 @@ bool upgradePbxProjWithFlutterAssets(IosProject project, Logger logger) {
final Match match = oldAssets.firstMatch(line); final Match match = oldAssets.firstMatch(line);
if (match != null) { if (match != null) {
if (printedStatuses.add(match.group(1))) { if (printedStatuses.add(match.group(1))) {
logger.printStatus('Removing obsolete reference to ${match.group(1)} from ${project.hostAppBundleName}'); logger.printStatus('Removing obsolete reference to ${match.group(1)} from ${project.xcodeProject?.basename}');
} }
} else { } else {
buffer.writeln(line); buffer.writeln(line);
......
...@@ -685,7 +685,7 @@ class _IOSSimulatorLogReader extends DeviceLogReader { ...@@ -685,7 +685,7 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
// Match the log prefix (in order to shorten it): // Match the log prefix (in order to shorten it):
// * Xcode 8: Sep 13 15:28:51 cbracken-macpro localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/ // * Xcode 8: Sep 13 15:28:51 cbracken-macpro localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
// * Xcode 9: 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/ // * Xcode 9: 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
static final RegExp _mapRegex = RegExp(r'\S+ +\S+ +\S+ +(\S+ +)?(\S+)\[\d+\]\)?: (\(.*?\))? *(.*)$'); static final RegExp _mapRegex = RegExp(r'\S+ +\S+ +(?:\S+) (.+?(?=\[))\[\d+\]\)?: (\(.*?\))? *(.*)$');
// Jan 31 19:23:28 --- last message repeated 1 time --- // Jan 31 19:23:28 --- last message repeated 1 time ---
static final RegExp _lastMessageSingleRegex = RegExp(r'\S+ +\S+ +\S+ --- last message repeated 1 time ---$'); static final RegExp _lastMessageSingleRegex = RegExp(r'\S+ +\S+ +\S+ --- last message repeated 1 time ---$');
...@@ -700,12 +700,16 @@ class _IOSSimulatorLogReader extends DeviceLogReader { ...@@ -700,12 +700,16 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
String _filterDeviceLine(String string) { String _filterDeviceLine(String string) {
final Match match = _mapRegex.matchAsPrefix(string); final Match match = _mapRegex.matchAsPrefix(string);
if (match != null) { if (match != null) {
final String category = match.group(2);
final String tag = match.group(3);
final String content = match.group(4);
// Filter out non-Flutter originated noise from the engine. // The category contains the text between the date and the PID. Depending on which version of iOS being run,
if (_appName != null && category != _appName) { // it can contain "hostname App Name" or just "App Name".
final String category = match.group(1);
final String tag = match.group(2);
final String content = match.group(3);
// Filter out log lines from an app other than this one (category doesn't match the app name).
// If the hostname is included in the category, check that it doesn't end with the app name.
if (_appName != null && !category.endsWith(_appName)) {
return null; return null;
} }
...@@ -725,7 +729,7 @@ class _IOSSimulatorLogReader extends DeviceLogReader { ...@@ -725,7 +729,7 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
if (_appName == null) { if (_appName == null) {
return '$category: $content'; return '$category: $content';
} else if (category == _appName) { } else if (category == _appName || category.endsWith(' $_appName')) {
return content; return content;
} }
......
...@@ -341,7 +341,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -341,7 +341,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
static final RegExp _productBundleIdPattern = RegExp(r'''^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(["']?)(.*?)\1;\s*$'''); static final RegExp _productBundleIdPattern = RegExp(r'''^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(["']?)(.*?)\1;\s*$''');
static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)'; static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)';
static const String _hostAppBundleName = 'Runner'; static const String _hostAppProjectName = 'Runner';
Directory get ephemeralDirectory => parent.directory.childDirectory('.ios'); Directory get ephemeralDirectory => parent.directory.childDirectory('.ios');
Directory get _editableDirectory => parent.directory.childDirectory('ios'); Directory get _editableDirectory => parent.directory.childDirectory('ios');
...@@ -362,9 +362,6 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -362,9 +362,6 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
/// a Flutter module with an editable host app. /// a Flutter module with an editable host app.
Directory get _flutterLibRoot => isModule ? ephemeralDirectory : _editableDirectory; Directory get _flutterLibRoot => isModule ? ephemeralDirectory : _editableDirectory;
/// The bundle name of the host app, `Runner.app`.
String get hostAppBundleName => '$_hostAppBundleName.app';
/// True, if the parent Flutter project is a module project. /// True, if the parent Flutter project is a module project.
bool get isModule => parent.isModule; bool get isModule => parent.isModule;
...@@ -387,19 +384,19 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -387,19 +384,19 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock'); File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
/// The default 'Info.plist' file of the host app. The developer can change this location in Xcode. /// The default 'Info.plist' file of the host app. The developer can change this location in Xcode.
File get defaultHostInfoPlist => hostAppRoot.childDirectory(_hostAppBundleName).childFile('Info.plist'); File get defaultHostInfoPlist => hostAppRoot.childDirectory(_hostAppProjectName).childFile('Info.plist');
@override @override
Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks'); Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks');
@override @override
Directory get xcodeProject => hostAppRoot.childDirectory('$_hostAppBundleName.xcodeproj'); Directory get xcodeProject => hostAppRoot.childDirectory('$_hostAppProjectName.xcodeproj');
@override @override
File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj'); File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj');
@override @override
Directory get xcodeWorkspace => hostAppRoot.childDirectory('$_hostAppBundleName.xcworkspace'); Directory get xcodeWorkspace => hostAppRoot.childDirectory('$_hostAppProjectName.xcworkspace');
/// Xcode workspace shared data directory for the host app. /// Xcode workspace shared data directory for the host app.
Directory get xcodeWorkspaceSharedData => xcodeWorkspace.childDirectory('xcshareddata'); Directory get xcodeWorkspaceSharedData => xcodeWorkspace.childDirectory('xcshareddata');
...@@ -414,7 +411,11 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -414,7 +411,11 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
/// The product bundle identifier of the host app, or null if not set or if /// The product bundle identifier of the host app, or null if not set or if
/// iOS tooling needed to read it is not installed. /// iOS tooling needed to read it is not installed.
Future<String> get productBundleIdentifier async { Future<String> get productBundleIdentifier async =>
_productBundleIdentifier ??= await _parseProductBundleIdentifier();
String _productBundleIdentifier;
Future<String> _parseProductBundleIdentifier() async {
String fromPlist; String fromPlist;
final File defaultInfoPlist = defaultHostInfoPlist; final File defaultInfoPlist = defaultHostInfoPlist;
// Users can change the location of the Info.plist. // Users can change the location of the Info.plist.
...@@ -456,28 +457,51 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -456,28 +457,51 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
return null; return null;
} }
/// The bundle name of the host app, `My App.app`.
Future<String> get hostAppBundleName async =>
_hostAppBundleName ??= await _parseHostAppBundleName();
String _hostAppBundleName;
Future<String> _parseHostAppBundleName() async {
// The product name and bundle name are derived from the display name, which the user
// is instructed to change in Xcode as part of deploying to the App Store.
// https://flutter.dev/docs/deployment/ios#review-xcode-project-settings
// The only source of truth for the name is Xcode's interpretation of the build settings.
String productName;
if (globals.xcodeProjectInterpreter.isInstalled) {
final Map<String, String> xcodeBuildSettings = await buildSettings;
if (xcodeBuildSettings != null) {
productName = xcodeBuildSettings['FULL_PRODUCT_NAME'];
}
}
if (productName == null) {
globals.printTrace('FULL_PRODUCT_NAME not present, defaulting to $_hostAppProjectName');
}
return productName ?? '$_hostAppProjectName.app';
}
/// The build settings for the host app of this project, as a detached map. /// The build settings for the host app of this project, as a detached map.
/// ///
/// Returns null, if iOS tooling is unavailable. /// Returns null, if iOS tooling is unavailable.
Future<Map<String, String>> get buildSettings async { Future<Map<String, String>> get buildSettings async =>
_buildSettings ??= await _xcodeProjectBuildSettings();
Map<String, String> _buildSettings;
Future<Map<String, String>> _xcodeProjectBuildSettings() async {
if (!globals.xcodeProjectInterpreter.isInstalled) { if (!globals.xcodeProjectInterpreter.isInstalled) {
return null; return null;
} }
Map<String, String> buildSettings = _buildSettings; final Map<String, String> buildSettings = await globals.xcodeProjectInterpreter.getBuildSettings(
buildSettings ??= await globals.xcodeProjectInterpreter.getBuildSettings(
xcodeProject.path, xcodeProject.path,
_hostAppBundleName, _hostAppProjectName,
); );
if (buildSettings != null && buildSettings.isNotEmpty) { if (buildSettings != null && buildSettings.isNotEmpty) {
// No timeouts, flakes, or errors. // No timeouts, flakes, or errors.
_buildSettings = buildSettings;
return buildSettings; return buildSettings;
} }
return null; return null;
} }
Map<String, String> _buildSettings;
Future<void> ensureReadyForPlatformSpecificTooling() async { Future<void> ensureReadyForPlatformSpecificTooling() async {
await _regenerateFromTemplateIfNeeded(); await _regenerateFromTemplateIfNeeded();
if (!_flutterLibRoot.existsSync()) { if (!_flutterLibRoot.existsSync()) {
...@@ -614,7 +638,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -614,7 +638,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
? _flutterLibRoot ? _flutterLibRoot
.childDirectory('Flutter') .childDirectory('Flutter')
.childDirectory('FlutterPluginRegistrant') .childDirectory('FlutterPluginRegistrant')
: hostAppRoot.childDirectory(_hostAppBundleName); : hostAppRoot.childDirectory(_hostAppProjectName);
} }
Future<void> _overwriteFromTemplate(String path, Directory target) async { Future<void> _overwriteFromTemplate(String path, Directory target) async {
...@@ -888,7 +912,7 @@ class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -888,7 +912,7 @@ class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject {
@override @override
String get pluginConfigKey => MacOSPlugin.kConfigKey; String get pluginConfigKey => MacOSPlugin.kConfigKey;
static const String _hostAppBundleName = 'Runner'; static const String _hostAppProjectName = 'Runner';
@override @override
bool existsSync() => _macOSDirectory.existsSync(); bool existsSync() => _macOSDirectory.existsSync();
...@@ -932,13 +956,13 @@ class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject { ...@@ -932,13 +956,13 @@ class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject {
File get podManifestLock => _macOSDirectory.childDirectory('Pods').childFile('Manifest.lock'); File get podManifestLock => _macOSDirectory.childDirectory('Pods').childFile('Manifest.lock');
@override @override
Directory get xcodeProject => _macOSDirectory.childDirectory('$_hostAppBundleName.xcodeproj'); Directory get xcodeProject => _macOSDirectory.childDirectory('$_hostAppProjectName.xcodeproj');
@override @override
File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj'); File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj');
@override @override
Directory get xcodeWorkspace => _macOSDirectory.childDirectory('$_hostAppBundleName.xcworkspace'); Directory get xcodeWorkspace => _macOSDirectory.childDirectory('$_hostAppProjectName.xcworkspace');
@override @override
Directory get symlinks => ephemeralDirectory.childDirectory('.symlinks'); Directory get symlinks => ephemeralDirectory.childDirectory('.symlinks');
......
...@@ -13,6 +13,8 @@ import 'package:flutter_tools/src/device.dart'; ...@@ -13,6 +13,8 @@ import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/ios_deploy.dart'; import 'package:flutter_tools/src/ios/ios_deploy.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart'; import 'package:platform/platform.dart';
...@@ -64,67 +66,158 @@ final FakePlatform macPlatform = FakePlatform( ...@@ -64,67 +66,158 @@ final FakePlatform macPlatform = FakePlatform(
); );
void main() { void main() {
FileSystem fileSystem; group('IOSDevice.startApp succeeds in release mode', () {
FakeProcessManager processManager; FileSystem fileSystem;
BufferLogger logger; FakeProcessManager processManager;
BufferLogger logger;
MockXcode mockXcode;
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
setUp(() { setUp(() {
logger = BufferLogger.test(); logger = BufferLogger.test();
fileSystem = MemoryFileSystem.test(); fileSystem = MemoryFileSystem.test();
processManager = FakeProcessManager.list(<FakeCommand>[]); processManager = FakeProcessManager.list(<FakeCommand>[]);
});
testUsingContext('IOSDevice.startApp succeeds in release mode with buildable app', () async { mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
final IOSDevice iosDevice = setUpIOSDevice( when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
fileSystem: fileSystem, when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer(
processManager: processManager, (_) {
logger: logger, return Future<XcodeProjectInfo>.value(XcodeProjectInfo(
); <String>['Runner'],
setUpIOSProject(fileSystem); <String>['Debug', 'Release'],
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory); <String>['Runner'],
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter'); ));
}
);
mockXcode = MockXcode();
when(mockXcode.isVersionSatisfactory).thenReturn(true);
});
testUsingContext('with buildable app', () async {
final IOSDevice iosDevice = setUpIOSDevice(
fileSystem: fileSystem,
processManager: processManager,
logger: logger,
);
setUpIOSProject(fileSystem);
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
processManager.addCommand(const FakeCommand(command: <String>[...kRunReleaseArgs, '-showBuildSettings']));
processManager.addCommand(FakeCommand(
command: <String>[
'ios-deploy',
'--id',
'123',
'--bundle',
'build/ios/iphoneos/My Super Awesome App.app',
'--no-wifi',
'--justlaunch',
'--args',
const <String>[
'--enable-dart-profiling',
'--enable-service-port-fallback',
'--disable-service-auth-codes',
'--observatory-port=53781',
].join(' ')
])
);
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject))); final LaunchResult launchResult = await iosDevice.startApp(
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs)); buildableIOSApp,
processManager.addCommand(const FakeCommand(command: <String>[...kRunReleaseArgs, '-showBuildSettings'])); debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
processManager.addCommand(FakeCommand( platformArgs: <String, Object>{},
command: <String>[ );
'ios-deploy',
'--id',
'123',
'--bundle',
'build/ios/iphoneos/Runner.app',
'--no-wifi',
'--justlaunch',
'--args',
const <String>[
'--enable-dart-profiling',
'--enable-service-port-fallback',
'--disable-service-auth-codes',
'--observatory-port=53781',
].join(' ')
])
);
final LaunchResult launchResult = await iosDevice.startApp( expect(launchResult.started, true);
buildableIOSApp, expect(processManager.hasRemainingExpectations, false);
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release), }, overrides: <Type, Generator>{
platformArgs: <String, Object>{}, ProcessManager: () => processManager,
); FileSystem: () => fileSystem,
Logger: () => logger,
Platform: () => macPlatform,
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
Xcode: () => mockXcode,
});
expect(launchResult.started, true); testUsingContext('with flaky buildSettings call', () async {
expect(processManager.hasRemainingExpectations, false); LaunchResult launchResult;
}, overrides: <Type, Generator>{ FakeAsync().run((FakeAsync time) {
ProcessManager: () => processManager, final IOSDevice iosDevice = setUpIOSDevice(
FileSystem: () => fileSystem, fileSystem: fileSystem,
Logger: () => logger, processManager: processManager,
Platform: () => macPlatform, logger: logger,
}); );
setUpIOSProject(fileSystem);
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
// The first showBuildSettings call should timeout.
processManager.addCommand(
const FakeCommand(
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
duration: Duration(minutes: 5), // this is longer than the timeout of 1 minute.
));
// The second call succeedes and is made after the first times out.
processManager.addCommand(
const FakeCommand(
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
exitCode: 0,
));
processManager.addCommand(FakeCommand(
command: <String>[
'ios-deploy',
'--id',
'123',
'--bundle',
'build/ios/iphoneos/My Super Awesome App.app',
'--no-wifi',
'--justlaunch',
'--args',
const <String>[
'--enable-dart-profiling',
'--enable-service-port-fallback',
'--disable-service-auth-codes',
'--observatory-port=53781',
].join(' ')
])
);
iosDevice.startApp(
buildableIOSApp,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
platformArgs: <String, Object>{},
).then((LaunchResult result) {
launchResult = result;
});
// Elapse duration for process timeout.
time.flushMicrotasks();
time.elapse(const Duration(minutes: 1));
// Elapse duration for overall process timer.
time.flushMicrotasks();
time.elapse(const Duration(minutes: 5));
time.flushTimers();
});
expect(launchResult?.started, true);
expect(processManager.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => fileSystem,
Logger: () => logger,
Platform: () => macPlatform,
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
Xcode: () => mockXcode,
});
testUsingContext('IOSDevice.startApp succeeds in release mode with buildable ' testUsingContext('with concurrent build failures', () async {
'app with flaky buildSettings call', () async {
LaunchResult launchResult;
FakeAsync().run((FakeAsync time) {
final IOSDevice iosDevice = setUpIOSDevice( final IOSDevice iosDevice = setUpIOSDevice(
fileSystem: fileSystem, fileSystem: fileSystem,
processManager: processManager, processManager: processManager,
...@@ -132,29 +225,30 @@ void main() { ...@@ -132,29 +225,30 @@ void main() {
); );
setUpIOSProject(fileSystem); setUpIOSProject(fileSystem);
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory); final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter'); final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject))); processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs)); // The first xcrun call should fail with a
// The first showBuildSettings call should timeout. // concurrent build exception.
processManager.addCommand( processManager.addCommand(
const FakeCommand( const FakeCommand(
command: <String>[...kRunReleaseArgs, '-showBuildSettings'], command: kRunReleaseArgs,
duration: Duration(minutes: 5), // this is longer than the timeout of 1 minute. exitCode: 1,
)); stdout: kConcurrentBuildErrorMessage,
// The second call succeedes and is made after the first times out. ));
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
processManager.addCommand( processManager.addCommand(
const FakeCommand( const FakeCommand(
command: <String>[...kRunReleaseArgs, '-showBuildSettings'], command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
exitCode: 0, exitCode: 0,
)); ));
processManager.addCommand(FakeCommand( processManager.addCommand(FakeCommand(
command: <String>[ command: <String>[
'ios-deploy', 'ios-deploy',
'--id', '--id',
'123', '123',
'--bundle', '--bundle',
'build/ios/iphoneos/Runner.app', 'build/ios/iphoneos/My Super Awesome App.app',
'--no-wifi', '--no-wifi',
'--justlaunch', '--justlaunch',
'--args', '--args',
...@@ -167,94 +261,24 @@ void main() { ...@@ -167,94 +261,24 @@ void main() {
]) ])
); );
iosDevice.startApp( final LaunchResult launchResult = await iosDevice.startApp(
buildableIOSApp, buildableIOSApp,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release), debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
platformArgs: <String, Object>{}, platformArgs: <String, Object>{},
).then((LaunchResult result) { );
launchResult = result;
});
// Elapse duration for process timeout.
time.flushMicrotasks();
time.elapse(const Duration(minutes: 1));
// Elapse duration for overall process timer.
time.flushMicrotasks();
time.elapse(const Duration(minutes: 5));
time.flushTimers(); expect(logger.statusText,
contains('Xcode build failed due to concurrent builds, will retry in 2 seconds'));
expect(launchResult.started, true);
expect(processManager.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => fileSystem,
Logger: () => logger,
Platform: () => macPlatform,
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
Xcode: () => mockXcode,
}); });
expect(launchResult?.started, true);
expect(processManager.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => fileSystem,
Logger: () => logger,
Platform: () => macPlatform,
});
testUsingContext('IOSDevice.startApp succeeds in release mode with buildable '
'app with concurrent build failure', () async {
final IOSDevice iosDevice = setUpIOSDevice(
fileSystem: fileSystem,
processManager: processManager,
logger: logger,
);
setUpIOSProject(fileSystem);
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter');
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
// The first xcrun call should fail with a
// concurrent build exception.
processManager.addCommand(
const FakeCommand(
command: kRunReleaseArgs,
exitCode: 1,
stdout: kConcurrentBuildErrorMessage,
));
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
processManager.addCommand(
const FakeCommand(
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
exitCode: 0,
));
processManager.addCommand(FakeCommand(
command: <String>[
'ios-deploy',
'--id',
'123',
'--bundle',
'build/ios/iphoneos/Runner.app',
'--no-wifi',
'--justlaunch',
'--args',
const <String>[
'--enable-dart-profiling',
'--enable-service-port-fallback',
'--disable-service-auth-codes',
'--observatory-port=53781',
].join(' ')
])
);
final LaunchResult launchResult = await iosDevice.startApp(
buildableIOSApp,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
platformArgs: <String, Object>{},
);
expect(logger.statusText,
contains('Xcode build failed due to concurrent builds, will retry in 2 seconds'));
expect(launchResult.started, true);
expect(processManager.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
FileSystem: () => fileSystem,
Logger: () => logger,
Platform: () => macPlatform,
}); });
} }
...@@ -263,10 +287,9 @@ void setUpIOSProject(FileSystem fileSystem) { ...@@ -263,10 +287,9 @@ void setUpIOSProject(FileSystem fileSystem) {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.packages').writeAsStringSync('\n');
fileSystem.directory('ios').createSync(); fileSystem.directory('ios').createSync();
fileSystem.directory('ios/Runner.xcworkspace').createSync(); fileSystem.directory('ios/Runner.xcworkspace').createSync();
fileSystem.directory('ios/Runner.xcodeproj').createSync(); fileSystem.file('ios/Runner.xcodeproj/project.pbxproj').createSync(recursive: true);
fileSystem.file('ios/Runner.xcodeproj/project.pbxproj').createSync();
// This is the expected output directory. // This is the expected output directory.
fileSystem.directory('build/ios/iphoneos/Runner.app').createSync(recursive: true); fileSystem.directory('build/ios/iphoneos/My Super Awesome App.app').createSync(recursive: true);
} }
IOSDevice setUpIOSDevice({ IOSDevice setUpIOSDevice({
...@@ -311,3 +334,5 @@ IOSDevice setUpIOSDevice({ ...@@ -311,3 +334,5 @@ IOSDevice setUpIOSDevice({
class MockArtifacts extends Mock implements Artifacts {} class MockArtifacts extends Mock implements Artifacts {}
class MockCache extends Mock implements Cache {} class MockCache extends Mock implements Cache {}
class MockXcode extends Mock implements Xcode {}
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
...@@ -396,7 +396,7 @@ Exited (sigterm)''', ...@@ -396,7 +396,7 @@ Exited (sigterm)''',
final MockFile pbxprojFile = MockFile(); final MockFile pbxprojFile = MockFile();
when(project.xcodeProjectInfoFile).thenReturn(pbxprojFile); when(project.xcodeProjectInfoFile).thenReturn(pbxprojFile);
when(project.hostAppBundleName).thenReturn('UnitTestRunner.app'); when(project.hostAppBundleName).thenAnswer((_) => Future<String>.value('UnitTestRunner.app'));
when(pbxprojFile.readAsLinesSync()) when(pbxprojFile.readAsLinesSync())
.thenAnswer((_) => flutterAssetPbxProjLines); .thenAnswer((_) => flutterAssetPbxProjLines);
when(pbxprojFile.existsSync()) when(pbxprojFile.existsSync())
......
...@@ -452,10 +452,10 @@ void main() { ...@@ -452,10 +452,10 @@ void main() {
simControl: mockSimControl, simControl: mockSimControl,
xcode: mockXcode, xcode: mockXcode,
); );
await launchDeviceUnifiedLogging(device, 'Runner'); await launchDeviceUnifiedLogging(device, 'My Super Awesome App');
const String expectedPredicate = 'eventType = logEvent AND ' const String expectedPredicate = 'eventType = logEvent AND '
'processImagePath ENDSWITH "Runner" AND ' 'processImagePath ENDSWITH "My Super Awesome App" AND '
'(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND ' '(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND '
'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND ' 'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND '
'NOT(eventMessage BEGINSWITH "assertion failed: ") AND ' 'NOT(eventMessage BEGINSWITH "assertion failed: ") AND '
...@@ -539,7 +539,9 @@ void main() { ...@@ -539,7 +539,9 @@ void main() {
fakeProcessManager fakeProcessManager
..addCommand(const FakeCommand( ..addCommand(const FakeCommand(
command: <String>['tail', '-n', '0', '-F', 'system.log'], command: <String>['tail', '-n', '0', '-F', 'system.log'],
stdout: 'Dec 20 17:04:32 md32-11-vm1 Runner[88374]: flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/', stdout: '''
Dec 20 17:04:32 md32-11-vm1 My Super Awesome App[88374]: flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/
Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
)) ))
..addCommand(const FakeCommand( ..addCommand(const FakeCommand(
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log'] command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
...@@ -571,9 +573,9 @@ void main() { ...@@ -571,9 +573,9 @@ void main() {
..addCommand(const FakeCommand( ..addCommand(const FakeCommand(
command: <String>['tail', '-n', '0', '-F', 'system.log'], command: <String>['tail', '-n', '0', '-F', 'system.log'],
stdout: ''' stdout: '''
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/ 2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) )))))))))) 2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) ))))))))))
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) #0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)''' 2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) #0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)'''
)) ))
..addCommand(const FakeCommand( ..addCommand(const FakeCommand(
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log'] command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
...@@ -607,19 +609,19 @@ void main() { ...@@ -607,19 +609,19 @@ void main() {
..addCommand(const FakeCommand( ..addCommand(const FakeCommand(
command: <String>['tail', '-n', '0', '-F', 'system.log'], command: <String>['tail', '-n', '0', '-F', 'system.log'],
stdout: ''' stdout: '''
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Single line message 2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Single line message
2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Multi line message 2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Multi line message
continues... continues...
continues... continues...
2020-03-11 15:58:28.207175-0700 localhost Runner[72166]: (libnetwork.dylib) [com.apple.network:] [28 www.googleapis.com:443 stream, pid: 72166, tls] cancelled 2020-03-11 15:58:28.207175-0700 localhost My Super Awesome App[72166]: (libnetwork.dylib) [com.apple.network:] [28 www.googleapis.com:443 stream, pid: 72166, tls] cancelled
[28.1 64A98447-EABF-4983-A387-7DB9D0C1785F 10.0.1.200.57912<->172.217.6.74:443] [28.1 64A98447-EABF-4983-A387-7DB9D0C1785F 10.0.1.200.57912<->172.217.6.74:443]
Connected Path: satisfied (Path is satisfied), interface: en18 Connected Path: satisfied (Path is satisfied), interface: en18
Duration: 0.271s, DNS @0.000s took 0.001s, TCP @0.002s took 0.019s, TLS took 0.046s Duration: 0.271s, DNS @0.000s took 0.001s, TCP @0.002s took 0.019s, TLS took 0.046s
bytes in/out: 4468/1933, packets in/out: 11/10, rtt: 0.016s, retransmitted packets: 0, out-of-order packets: 0 bytes in/out: 4468/1933, packets in/out: 11/10, rtt: 0.016s, retransmitted packets: 0, out-of-order packets: 0
2017-09-13 15:36:57.228948-0700 localhost Runner[37195]: (Flutter) Multi line message again 2017-09-13 15:36:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Multi line message again
and it goes... and it goes...
and goes... and goes...
2017-09-13 15:36:57.228948-0700 localhost Runner[37195]: (Flutter) Single line message, not the part of the above 2017-09-13 15:36:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Single line message, not the part of the above
''' '''
)) ))
..addCommand(const FakeCommand( ..addCommand(const FakeCommand(
...@@ -657,7 +659,7 @@ void main() { ...@@ -657,7 +659,7 @@ void main() {
group('unified logging', () { group('unified logging', () {
testUsingContext('log reader handles escaped multiline messages', () async { testUsingContext('log reader handles escaped multiline messages', () async {
const String logPredicate = 'eventType = logEvent AND processImagePath ENDSWITH "Runner" ' const String logPredicate = 'eventType = logEvent AND processImagePath ENDSWITH "My Super Awesome App" '
'AND (senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" ' 'AND (senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" '
'OR processImageUUID == senderImageUUID) AND NOT(eventMessage CONTAINS ": could not find icon ' 'OR processImageUUID == senderImageUUID) AND NOT(eventMessage CONTAINS ": could not find icon '
'for representation -> com.apple.") AND NOT(eventMessage BEGINSWITH "assertion failed: ") ' 'for representation -> com.apple.") AND NOT(eventMessage BEGINSWITH "assertion failed: ") '
......
...@@ -466,6 +466,39 @@ apply plugin: 'kotlin-android' ...@@ -466,6 +466,39 @@ apply plugin: 'kotlin-android'
}); });
}); });
group('application bundle name', () {
MemoryFileSystem fs;
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
setUp(() {
fs = MemoryFileSystem();
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
});
testUsingContext('app product name defaults to Runner.app', () async {
final FlutterProject project = await someProject();
expect(await project.ios.hostAppBundleName, 'Runner.app');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter
});
testUsingContext('app product name xcodebuild settings', () async {
final FlutterProject project = await someProject();
when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenAnswer((_) {
return Future<Map<String,String>>.value(<String, String>{
'FULL_PRODUCT_NAME': 'My App.app'
});
});
expect(await project.ios.hostAppBundleName, 'My App.app');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter
});
});
group('organization names set', () { group('organization names set', () {
testInMemory('is empty, if project not created', () async { testInMemory('is empty, if project not created', () async {
final FlutterProject project = await someProject(); final FlutterProject project = await someProject();
......
...@@ -45,7 +45,7 @@ class MockApplicationPackageStore extends ApplicationPackageStore { ...@@ -45,7 +45,7 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
versionCode: 1, versionCode: 1,
launchActivity: 'io.flutter.android.mock.MockActivity', launchActivity: 'io.flutter.android.mock.MockActivity',
), ),
iOS: BuildableIOSApp(MockIosProject(), MockIosProject.bundleId), iOS: BuildableIOSApp(MockIosProject(), MockIosProject.bundleId, MockIosProject.appBundleName),
); );
} }
...@@ -529,12 +529,13 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { ...@@ -529,12 +529,13 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
class MockIosProject extends Mock implements IosProject { class MockIosProject extends Mock implements IosProject {
static const String bundleId = 'com.example.test'; static const String bundleId = 'com.example.test';
static const String appBundleName = 'My Super Awesome App.app';
@override @override
Future<String> get productBundleIdentifier async => bundleId; Future<String> get productBundleIdentifier async => bundleId;
@override @override
String get hostAppBundleName => 'Runner.app'; Future<String> get hostAppBundleName async => appBundleName;
} }
class MockAndroidDevice extends Mock implements AndroidDevice { class MockAndroidDevice extends Mock implements AndroidDevice {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment