diff --git a/.gitignore b/.gitignore index c166b1576..30fc9f202 100644 --- a/.gitignore +++ b/.gitignore @@ -360,3 +360,5 @@ MigrationBackup/ # some ide stuff .idea + +.vscode/ \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 3873efc7d..3fb05e9d6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,7 +15,7 @@ - + diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/InstallCommand.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/InstallCommand.cs index f28dea399..8ad7580ef 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/InstallCommand.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/InstallCommand.cs @@ -121,6 +121,30 @@ protected override async Task InvokeInternal(ILogger logger) private async Task Install(Simulator simulator) { + if (simulator.Source is null) + { + var xcodeVersion = await GetXcodeVersion(); + if (!simulator.IsCryptexDiskImage || Version.Parse(xcodeVersion).Major < 16) + { + throw new Exception($"Cannot download simulator: {simulator.Name} from source nor through Xcode: {xcodeVersion}"); + } + else + { + Logger.LogInformation($"Downloading and installing simulator: {simulator.Name} through xcodebuild with Xcode: {xcodeVersion}"); + var (succeeded, stdout) = await ExecuteCommand("xcodebuild", TimeSpan.FromMinutes(15), "-downloadPlatform", simulator.Platform, "-verbose"); + if (!succeeded) + { + Logger.LogError($"Download and installation failed through xcodebuild for simulator: {simulator.Name} with Xcode: {xcodeVersion}!" + Environment.NewLine + stdout); + return false; + } + else + { + Logger.LogDebug(stdout); + return true; + } + } + } + var filename = Path.GetFileName(simulator.Source); var downloadPath = Path.Combine(TempDirectory, filename); var download = true; diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/Simulator.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/Simulator.cs index 87be8a453..f16fcfc9e 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/Simulator.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/Simulator.cs @@ -9,21 +9,25 @@ namespace Microsoft.DotNet.XHarness.CLI.Commands.Apple.Simulators; internal class Simulator { public string Name { get; } + public string Platform { get; } public string Identifier { get; } public string Version { get; } - public string Source { get; } + public string? Source { get; } public string InstallPrefix { get; } public long FileSize { get; } public bool IsDmgFormat { get; } + public bool IsCryptexDiskImage { get; } - public Simulator(string name, string identifier, string version, string source, string installPrefix, long fileSize) + public Simulator(string name, string platform, string identifier, string version, string? source, string installPrefix, long fileSize, bool isCryptexDiskImage) { Name = name; + Platform = platform; Identifier = identifier; Version = version; Source = source; InstallPrefix = installPrefix; FileSize = fileSize; IsDmgFormat = Identifier.StartsWith("com.apple.dmg.", StringComparison.OrdinalIgnoreCase); + IsCryptexDiskImage = isCryptexDiskImage; } } diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/SimulatorsCommand.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/SimulatorsCommand.cs index ea3429563..cbfb51f32 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/SimulatorsCommand.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/Apple/Simulators/SimulatorsCommand.cs @@ -108,13 +108,6 @@ protected async Task> GetAvailableSimulators() var versionNode = downloadable.SelectSingleNode("key[text()='version']/following-sibling::string") ?? throw new Exception("Version node not found"); var identifierNode = downloadable.SelectSingleNode("key[text()='identifier']/following-sibling::string") ?? throw new Exception("Identifier node not found"); var sourceNode = downloadable.SelectSingleNode("key[text()='source']/following-sibling::string"); - if (sourceNode is null) - { - // It seems that Apple can list beta simulators in the index file, but they do not provide a source for downloading them (eg: iOS 18.0 beta Simulator Runtime). - // In such cases log a warning and skip trying to download such simulator as they are not publicly available. - Logger.LogWarning($"Simulator with name: '{nameNode.InnerText}' version: '{versionNode.InnerText}' identifier: '{identifierNode.InnerText}' has no source for download, skipping..."); - continue; - } var fileSizeNode = downloadable.SelectSingleNode("key[text()='fileSize']/following-sibling::integer|key[text()='fileSize']/following-sibling::real"); var installPrefixNode = downloadable.SelectSingleNode("key[text()='userInfo']/following-sibling::dict/key[text()='InstallPrefix']/following-sibling::string"); @@ -144,13 +137,51 @@ protected async Task> GetAvailableSimulators() installPrefix = $"/Library/Developer/CoreSimulator/Profiles/Runtimes/{simRuntimeName}"; } + var platform = name.Split(' ').FirstOrDefault(); + if (platform is null) + { + Logger.LogWarning($"Platform name could not be parsed from simulator name: '{nameNode.InnerText}' version: '{versionNode.InnerText}' identifier: '{identifierNode.InnerText}' skipping..."); + continue; + } + + var source = ReplaceStringUsingKey(sourceNode?.InnerText, dict); + var isCryptexDiskImage = false; + if (source is null) + { + // We allow source to be missing for newer simulators (e.g., iOS 18+ available from Xcode 16) that use cryptographically-sealed archives. + // Eg.: + // + // category + // simulator + // contentType + // cryptexDiskImage + // ... + // These images are downloaded and installed through xcodebuild instead. + // https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes#Install-and-manage-Simulator-runtimes-from-the-command-line + var contentTypeNode = downloadable.SelectSingleNode("key[text()='contentType']/following-sibling::string") ?? throw new Exception("ContentType node not found"); + var contentType = contentTypeNode.InnerText; + if (contentType.Equals("cryptexDiskImage", StringComparison.OrdinalIgnoreCase)) + { + isCryptexDiskImage = true; + Logger.LogInformation($"Simulator with name: '{nameNode.InnerText}' version: '{versionNode.InnerText}' identifier: '{identifierNode.InnerText}' has no source but it is a cryptex disk image which can be downloaded through xcodebuild."); + } + else + { + Logger.LogWarning($"Simulator with name: '{nameNode.InnerText}' version: '{versionNode.InnerText}' identifier: '{identifierNode.InnerText}' has no source for download nor it is a cryptex disk image, skipping..."); + continue; + } + } + simulators.Add(new Simulator( name: name, + platform: platform, identifier: ReplaceStringUsingKey(identifierNode.InnerText, dict), version: versionNode.InnerText, - source: ReplaceStringUsingKey(sourceNode.InnerText, dict), + source: source, installPrefix: installPrefix, - fileSize: (long)parsedFileSize)); + fileSize: (long)parsedFileSize, + isCryptexDiskImage: isCryptexDiskImage + )); } return simulators; @@ -182,6 +213,8 @@ protected async Task> GetAvailableSimulators() { return null; } + Logger.LogDebug($"Listing runtime disk images via returned: {json}"); + string simulatorRuntime = ""; string simulatorVersion = ""; @@ -385,7 +418,7 @@ private async Task DownloadFile(string url, string destinationPath) return false; } - private async Task GetXcodeVersion() + protected async Task GetXcodeVersion() { if (_xcodeVersion is not null) { diff --git a/tests/integration-tests/Apple/Simulator.Commands.Tests.proj b/tests/integration-tests/Apple/Simulator.Commands.Tests.proj index 147ccede8..8fa960587 100644 --- a/tests/integration-tests/Apple/Simulator.Commands.Tests.proj +++ b/tests/integration-tests/Apple/Simulator.Commands.Tests.proj @@ -4,6 +4,10 @@ + + 16.4 + + System.Numerics.Vectors.Tests $(AssetsBaseUri)/ios/test-app/ios-simulator-64/$(TestAppBundleName).app.zip @@ -21,13 +25,15 @@ - ios-simulator-64 + ios-simulator-64_$(iOSSimulatorVersionUnderTest) 00:20:00 00:07:00 00:03:30 + + + + + + + + + Xcode_16_beta_6 + 18.0 + + + + System.Numerics.Vectors.Tests + $(AssetsBaseUri)/ios/test-app/ios-simulator-64/$(TestAppBundleName).app.zip + $(ArtifactsTmpDir)test-app\ios-simulator-64 + + + + + + + + + + + + + + ios-simulator-64_$(iOSSimulatorVersionUnderTest) + 00:20:00 + 00:07:00 + 00:03:30 + + + + + + + + diff --git a/tests/integration-tests/README.md b/tests/integration-tests/README.md new file mode 100644 index 000000000..7769c853a --- /dev/null +++ b/tests/integration-tests/README.md @@ -0,0 +1,20 @@ +# Integration tests + +This folder includes integration tests projects for different support platforms (iOS, Android, WASM). +They are used in end-to-end testing scenarios and are referenced from `azure-pipelines-public.yml` E2E templates. + +In the relevant `*.proj` files one can configure various setting for execution on Helix like: +- configuring the Helix queue (e.g., `osx.13.amd64.iphone.open` via `HelixTargetQueue` item group) +- app bundle to download, send to Helix and test (e.g., `System.Buffers.Tests.app`) +- etc. + +## Testing on scouting queue + +NOTE: This is Apple-specific but can be applied to other platforms as well + +There are two test projects which can be used on scouting queues which are not used by default: + +- Apple/Simulator.Scouting.Tests.proj +- Apple/Simulator.Scouting.Commands.Tests.proj + +When desired, these can be included in the `azure-pipelines-public.yml` so that the CI runs them on a desired scouting queue (check the `HelixTargetQueue` setting) with a particular version of Xcode.