Unverified Commit 62dc238b authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Avoid using watchOS SDK in CI tests (#94190)

parent 7d3ca786
......@@ -2,10 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'dart:io';
import 'package:flutter_devicelab/common.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
......@@ -16,8 +14,6 @@ Future<void> main() async {
await task(() async {
section('Copy test Flutter App with watchOS Companion');
String? watchDeviceID;
String? phoneDeviceID;
final Directory tempDir = Directory.systemTemp
.createTempSync('flutter_ios_app_with_extensions_test.');
final Directory projectDir =
......@@ -32,11 +28,23 @@ Future<void> main() async {
section('Create release build');
// This only builds the iOS app, not the companion watchOS app. The watchOS app
// has been removed as a build dependency and is not embedded in the app to avoid
// requiring the watchOS being available in CI.
// Instead, validate the tool detects that there is a watch companion, and omits
// the "-sdk iphoneos" option, which fails to build the watchOS app.
// See https://github.com/flutter/flutter/pull/94190.
await inDirectory(projectDir, () async {
await flutter(
final String buildOutput = await evalFlutter(
'build',
options: <String>['ios', '--no-codesign', '--release', '--verbose'],
);
if (!buildOutput.contains('Watch companion app found')) {
throw TaskResult.failure('Did not detect watch companion');
}
if (buildOutput.contains('-sdk iphoneos -destination')) {
throw TaskResult.failure('-sdk must be omitted for app with watch companion');
}
});
final String appBundle = Directory(path.join(
......@@ -64,228 +72,17 @@ Future<void> main() async {
await _checkFlutterFrameworkArchs(appFrameworkPath);
await _checkFlutterFrameworkArchs(flutterFrameworkPath);
// Check the watch extension framework added in the Podfile
// is in place with the expected watch archs.
final String watchExtensionFrameworkPath = path.join(
appBundle,
'Watch',
'watch.app',
'PlugIns',
'watch Extension.appex',
'Frameworks',
'EFQRCode.framework',
'EFQRCode',
);
unawaited(_checkWatchExtensionFrameworkArchs(watchExtensionFrameworkPath));
section('Clean build');
await inDirectory(projectDir, () async {
await flutter('clean');
});
section('Create debug build');
await inDirectory(projectDir, () async {
await flutter(
'build',
options: <String>['ios', '--debug', '--no-codesign', '--verbose'],
);
});
checkDirectoryExists(appBundle);
await _checkFlutterFrameworkArchs(appFrameworkPath);
await _checkFlutterFrameworkArchs(flutterFrameworkPath);
unawaited(_checkWatchExtensionFrameworkArchs(watchExtensionFrameworkPath));
section('Clean build');
await inDirectory(projectDir, () async {
await flutter('clean');
});
section('Run app on simulator device');
// Xcode 11.4 simctl create makes the runtime argument optional, and defaults to latest.
// TODO(jmagman): Remove runtime parsing when devicelab upgrades to Xcode 11.4 https://github.com/flutter/flutter/issues/54889
final String availableRuntimes = await eval(
'xcrun',
<String>[
'simctl',
'list',
'runtimes',
],
workingDirectory: flutterDirectory.path,
);
// Example simctl list:
// == Runtimes ==
// iOS 10.3 (10.3.1 - 14E8301) - com.apple.CoreSimulator.SimRuntime.iOS-10-3
// iOS 13.4 (13.4 - 17E255) - com.apple.CoreSimulator.SimRuntime.iOS-13-4
// tvOS 13.4 (13.4 - 17L255) - com.apple.CoreSimulator.SimRuntime.tvOS-13-4
// watchOS 6.2 (6.2 - 17T256) - com.apple.CoreSimulator.SimRuntime.watchOS-6-2
String? iOSSimRuntime;
String? watchSimRuntime;
final RegExp iOSRuntimePattern = RegExp(r'iOS .*\) - (.*)');
final RegExp watchOSRuntimePattern = RegExp(r'watchOS .*\) - (.*)');
for (final String runtime in LineSplitter.split(availableRuntimes)) {
// These seem to be in order, so allow matching multiple lines so it grabs
// the last (hopefully latest) one.
final RegExpMatch? iOSRuntimeMatch = iOSRuntimePattern.firstMatch(runtime);
if (iOSRuntimeMatch != null) {
iOSSimRuntime = iOSRuntimeMatch.group(1)!.trim();
continue;
}
final RegExpMatch? watchOSRuntimeMatch = watchOSRuntimePattern.firstMatch(runtime);
if (watchOSRuntimeMatch != null) {
watchSimRuntime = watchOSRuntimeMatch.group(1)!.trim();
}
}
if (iOSSimRuntime == null || watchSimRuntime == null) {
String message;
if (iOSSimRuntime != null) {
message = 'Found "$iOSSimRuntime", but no watchOS simulator runtime found.';
} else if (watchSimRuntime != null) {
message = 'Found "$watchSimRuntime", but no iOS simulator runtime found.';
} else {
message = 'watchOS and iOS simulator runtimes not found.';
}
return TaskResult.failure('$message Available runtimes:\n$availableRuntimes');
}
// Create iOS simulator.
phoneDeviceID = await eval(
'xcrun',
<String>[
'simctl',
'create',
'TestFlutteriPhoneWithWatch',
'com.apple.CoreSimulator.SimDeviceType.iPhone-11',
iOSSimRuntime,
],
workingDirectory: flutterDirectory.path,
);
// Create watchOS simulator.
watchDeviceID = await eval(
'xcrun',
<String>[
'simctl',
'create',
'TestFlutterWatch',
'com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm',
watchSimRuntime,
],
workingDirectory: flutterDirectory.path,
);
// Pair watch with phone.
await eval(
'xcrun',
<String>['simctl', 'pair', watchDeviceID, phoneDeviceID],
workingDirectory: flutterDirectory.path,
);
// Boot simulator devices.
await eval(
'xcrun',
<String>['simctl', 'bootstatus', phoneDeviceID, '-b'],
workingDirectory: flutterDirectory.path,
);
await eval(
'xcrun',
<String>['simctl', 'bootstatus', watchDeviceID, '-b'],
workingDirectory: flutterDirectory.path,
);
// Start app on simulated device.
final Process process = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['run', '-d', phoneDeviceID],
workingDirectory: projectDir.path);
process.stdout
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.listen((String line) {
print('stdout: $line');
// Wait for app startup to complete and quit immediately afterwards.
if (line.startsWith('An Observatory debugger')) {
process.stdin.write('q');
}
});
process.stderr
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.listen((String line) {
print('stderr: $line');
});
final int exitCode = await process.exitCode;
if (exitCode != 0) {
return TaskResult.failure(
'Failed to start flutter iOS app with WatchOS companion on simulated device.');
}
final String simulatorAppBundle = Directory(path.join(
projectDir.path,
'build',
'ios',
'iphonesimulator',
'Runner.app',
)).path;
checkDirectoryExists(simulatorAppBundle);
checkFileExists(path.join(
simulatorAppBundle,
'Frameworks',
'App.framework',
'App',
));
checkFileExists(path.join(
simulatorAppBundle,
'Frameworks',
'Flutter.framework',
'Flutter',
));
return TaskResult.success(null);
} catch (e) {
return TaskResult.failure(e.toString());
} finally {
rmTree(tempDir);
// Delete simulator devices
if (watchDeviceID != null && watchDeviceID != '') {
await eval(
'xcrun',
<String>['simctl', 'shutdown', watchDeviceID],
canFail: true,
workingDirectory: flutterDirectory.path,
);
await eval(
'xcrun',
<String>['simctl', 'delete', watchDeviceID],
canFail: true,
workingDirectory: flutterDirectory.path,
);
}
if (phoneDeviceID != null && phoneDeviceID != '') {
await eval(
'xcrun',
<String>['simctl', 'shutdown', phoneDeviceID],
canFail: true,
workingDirectory: flutterDirectory.path,
);
await eval(
'xcrun',
<String>['simctl', 'delete', phoneDeviceID],
canFail: true,
workingDirectory: flutterDirectory.path,
);
}
}
});
}
......@@ -302,15 +99,3 @@ Future<void> _checkFlutterFrameworkArchs(String frameworkPath) async {
throw TaskResult.failure('$frameworkPath x86_64 architecture unexpectedly present');
}
}
Future<void> _checkWatchExtensionFrameworkArchs(String frameworkPath) async {
checkFileExists(frameworkPath);
final String archs = await fileType(frameworkPath);
if (!archs.contains('armv7k')) {
throw TaskResult.failure('$frameworkPath armv7k architecture missing');
}
if (!archs.contains('arm64_32')) {
throw TaskResult.failure('$frameworkPath arm64_32 architecture missing');
}
}
......@@ -18,7 +18,6 @@
49C15B60243E340E0025F804 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C15B5F243E340E0025F804 /* ExtensionDelegate.swift */; };
49C15B62243E340F0025F804 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49C15B61243E340F0025F804 /* Assets.xcassets */; };
49C15B65243E340F0025F804 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49C15B64243E340F0025F804 /* Preview Assets.xcassets */; };
49C15B69243E340F0025F804 /* watch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 49C15B4A243E340B0025F804 /* watch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
......@@ -34,13 +33,6 @@
remoteGlobalIDString = 49C15B55243E340E0025F804;
remoteInfo = "watch Extension";
};
49C15B67243E340F0025F804 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 49C15B49243E340B0025F804;
remoteInfo = watch;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
......@@ -55,17 +47,6 @@
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
49C15B73243E340F0025F804 /* Embed Watch Content */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
dstSubfolderSpec = 16;
files = (
49C15B69243E340F0025F804 /* watch.app in Embed Watch Content */,
);
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
};
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
......@@ -307,13 +288,11 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
49C15B73243E340F0025F804 /* Embed Watch Content */,
DF3DAF4426EF33A40B49B448 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
49C15B68243E340F0025F804 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
......@@ -531,11 +510,6 @@
target = 49C15B55243E340E0025F804 /* watch Extension */;
targetProxy = 49C15B58243E340E0025F804 /* PBXContainerItemProxy */;
};
49C15B68243E340F0025F804 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 49C15B49243E340B0025F804 /* watch */;
targetProxy = 49C15B67243E340F0025F804 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
......
......@@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
......@@ -38,8 +36,8 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
......@@ -61,8 +59,6 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
......
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