diff --git a/Directory.Packages.props b/Directory.Packages.props
index 95201b853..c07629eab 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,7 +13,7 @@
-
+
diff --git a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs
index bbe640e9f..1218b5643 100644
--- a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs
+++ b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs
@@ -204,6 +204,7 @@ public AppTester(
timeout,
null,
(level, message) => _mainLog.WriteLine(message));
+ IResultFileHandler resultFileHandler = new ResultFileHandler(_processManager, _mainLog);
deviceListener.ConnectedTask
.TimeoutAfter(testLaunchTimeout)
@@ -252,10 +253,13 @@ await RunSimulatorTests(
mlaunchArguments,
crashReporter,
testReporter,
+ resultFileHandler,
+ deviceListener,
(ISimulatorDevice)device,
companionDevice as ISimulatorDevice,
timeout,
- cancellationToken);
+ cancellationToken,
+ runMode);
}
else
{
@@ -283,15 +287,18 @@ await RunSimulatorTests(
appEndTag);
await RunDeviceTests(
+ appInformation,
mlaunchArguments,
crashReporter,
testReporter,
+ resultFileHandler,
deviceListener,
device,
appOutputLog,
timeout,
extraEnvVariables,
- cancellationToken);
+ cancellationToken,
+ runMode);
}
}
@@ -305,10 +312,13 @@ private async Task RunSimulatorTests(
MlaunchArguments mlaunchArguments,
ICrashSnapshotReporter crashReporter,
ITestReporter testReporter,
+ IResultFileHandler resultFileHandler,
+ ISimpleListener deviceListener,
ISimulatorDevice simulator,
ISimulatorDevice? companionSimulator,
TimeSpan timeout,
- CancellationToken cancellationToken)
+ CancellationToken cancellationToken,
+ RunMode runMode)
{
var result = await RunSimulatorApp(
appInformation,
@@ -321,18 +331,36 @@ private async Task RunSimulatorTests(
cancellationToken);
await testReporter.CollectSimulatorResult(result);
+
+ // On iOS 18 and later, transferring results over a TCP tunnel isn’t supported.
+ // Instead, copy the results file from the device to the host machine.
+ if (deviceListener.TestLog != null &&
+ !await resultFileHandler.CopyResultsAsync(
+ runMode,
+ true,
+ simulator.OSVersion,
+ simulator.UDID,
+ appInformation.BundleIdentifier,
+ deviceListener.TestLog.FullPath,
+ cancellationToken))
+ {
+ throw new InvalidOperationException("Failed to copy test results from simulator to host.");
+ }
}
private async Task RunDeviceTests(
+ AppBundleInformation appInformation,
MlaunchArguments mlaunchArguments,
ICrashSnapshotReporter crashReporter,
ITestReporter testReporter,
+ IResultFileHandler resultFileHandler,
ISimpleListener deviceListener,
IDevice device,
ILog appOutputLog,
TimeSpan timeout,
IEnumerable<(string, string)> extraEnvVariables,
- CancellationToken cancellationToken)
+ CancellationToken cancellationToken,
+ RunMode runMode)
{
var deviceSystemLog = _logs.Create($"device-{device.Name}-{_helpers.Timestamp}.log", LogType.SystemLog.ToString());
deviceSystemLog.Timestamp = false;
@@ -391,6 +419,21 @@ private async Task RunDeviceTests(
{
_mainLog.WriteLine("Device log captured in {0}", deviceSystemLog.FullPath);
}
+
+ // On iOS 18 and later, transferring results over a TCP tunnel isn’t supported.
+ // Instead, copy the results file from the device to the host machine.
+ if (deviceListener.TestLog != null &&
+ !await resultFileHandler.CopyResultsAsync(
+ runMode,
+ false,
+ device.OSVersion,
+ device.UDID,
+ appInformation.BundleIdentifier,
+ deviceListener.TestLog.FullPath,
+ cancellationToken))
+ {
+ throw new InvalidOperationException("Failed to copy test results from device to host.");
+ }
}
///
diff --git a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTesterFactory.cs b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTesterFactory.cs
index 46c5c33a0..111b4f846 100644
--- a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTesterFactory.cs
+++ b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTesterFactory.cs
@@ -14,7 +14,7 @@ namespace Microsoft.DotNet.XHarness.Apple;
public interface IAppTesterFactory
{
- IAppTester Create(CommunicationChannel communicationChannel, bool isSimulator, IFileBackedLog log, ILogs logs, Action? logCallback);
+ IAppTester Create(CommunicationChannel communicationChannel, bool isSimulator, IFileBackedLog log, ILogs logs, Action? logCallback, Version? osVersion);
}
public class AppTesterFactory : IAppTesterFactory
@@ -50,9 +50,11 @@ public IAppTester Create(
bool isSimulator,
IFileBackedLog log,
ILogs logs,
- Action? logCallback)
+ Action? logCallback,
+ Version? osVersion)
{
- var tunnelBore = (communicationChannel == CommunicationChannel.UsbTunnel && !isSimulator)
+ // On iOS 18 and later, transferring results over a TCP tunnel isn’t supported.
+ var tunnelBore = (communicationChannel == CommunicationChannel.UsbTunnel && !isSimulator && osVersion !=null && osVersion.Major < 18)
? new TunnelBore(_processManager)
: null;
diff --git a/src/Microsoft.DotNet.XHarness.Apple/ExitCodeDetector.cs b/src/Microsoft.DotNet.XHarness.Apple/ExitCodeDetector.cs
index 0d29deb33..750198dd9 100644
--- a/src/Microsoft.DotNet.XHarness.Apple/ExitCodeDetector.cs
+++ b/src/Microsoft.DotNet.XHarness.Apple/ExitCodeDetector.cs
@@ -85,7 +85,11 @@ public class iOSExitCodeDetector : ExitCodeDetector, IiOSExitCodeDetector
{
// Example line coming from the mlaunch log
// [07:02:21.6637600] Application 'net.dot.iOS.Simulator.PInvoke.Test' terminated (with exit code '42' and/or crashing signal ').
- private Regex DeviceExitCodeRegex { get; } = new Regex(@"terminated \(with exit code '(?-?[0-9]+)' and/or crashing signal", RegexOptions.Compiled);
+ private Regex[] DeviceExitCodeRegexes { get; } = new Regex[]
+ {
+ new Regex(@"terminated \(with exit code '(?-?[0-9]+)' and/or crashing signal", RegexOptions.Compiled),
+ new Regex(@"Failed to execute 'devicectl':.*returned the exit code (?\d+)\.", RegexOptions.Compiled)
+ };
protected override Match? IsSignalLine(AppBundleInformation appBundleInfo, string logLine)
{
@@ -96,7 +100,14 @@ public class iOSExitCodeDetector : ExitCodeDetector, IiOSExitCodeDetector
if (logLine.Contains(appBundleInfo.BundleIdentifier))
{
- return DeviceExitCodeRegex.Match(logLine);
+ foreach (var regex in DeviceExitCodeRegexes)
+ {
+ var m = regex.Match(logLine);
+ if (m.Success)
+ {
+ return m;
+ }
+ }
}
return null;
diff --git a/src/Microsoft.DotNet.XHarness.Apple/Orchestration/RunOrchestrator.cs b/src/Microsoft.DotNet.XHarness.Apple/Orchestration/RunOrchestrator.cs
index d27ab8525..79bc8499b 100644
--- a/src/Microsoft.DotNet.XHarness.Apple/Orchestration/RunOrchestrator.cs
+++ b/src/Microsoft.DotNet.XHarness.Apple/Orchestration/RunOrchestrator.cs
@@ -313,7 +313,9 @@ private ExitCode ParseResult(
return ExitCode.TIMED_OUT;
}
- if (!result.Succeeded)
+ // On iOS 18 and later, mlaunch returns exit code 1 with the following error message:
+ // "Failed to execute 'devicectl': returned the exit code ."
+ if (!result.Succeeded && result.ExitCode != 1)
{
_logger.LogError($"App run has failed. mlaunch exited with {result.ExitCode}");
return ExitCode.APP_LAUNCH_FAILURE;
diff --git a/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs b/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs
index 2d2530f0f..d2766f2fb 100644
--- a/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs
+++ b/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs
@@ -214,9 +214,10 @@ private async Task ExecuteApp(
bool signalAppEnd,
CancellationToken cancellationToken)
{
+ bool versionParsed = Version.TryParse(device.OSVersion, out var version);
// iOS 14+ devices do not allow local network access and won't work unless the user confirms a dialog on the screen
// https://developer.apple.com/forums/thread/663858
- if (Version.TryParse(device.OSVersion, out var version) && version.Major >= 14 && target.Platform.ToRunMode() == RunMode.iOS && communicationChannel == CommunicationChannel.Network)
+ if (versionParsed && version!.Major >= 14 && target.Platform.ToRunMode() == RunMode.iOS && communicationChannel == CommunicationChannel.Network)
{
_logger.LogWarning(
"Applications need user permission for communication over local network on iOS 14 and newer." + Environment.NewLine +
@@ -231,7 +232,7 @@ private async Task ExecuteApp(
_logger.LogInformation("Starting test run for " + appBundleInfo.BundleIdentifier + "..");
- var appTester = GetAppTester(communicationChannel, target.Platform.IsSimulator());
+ var appTester = GetAppTester(communicationChannel, target.Platform.IsSimulator(), version);
(TestExecutingResult testResult, string resultMessage) = await appTester.TestApp(
appBundleInfo,
@@ -255,7 +256,7 @@ private async Task ExecuteApp(
// Copy system and application logs to the main log for better failure investigation.
CopyLogsToMainLog();
}
-
+
return exitCode;
}
@@ -272,7 +273,7 @@ private async Task ExecuteMacCatalystApp(
bool signalAppEnd,
CancellationToken cancellationToken)
{
- var appTester = GetAppTester(communicationChannel, TestTarget.MacCatalyst.IsSimulator());
+ var appTester = GetAppTester(communicationChannel, TestTarget.MacCatalyst.IsSimulator(), null);
(TestExecutingResult testResult, string resultMessage) = await appTester.TestMacCatalystApp(
appBundleInfo,
@@ -294,12 +295,12 @@ private async Task ExecuteMacCatalystApp(
return exitCode;
}
- private IAppTester GetAppTester(CommunicationChannel communicationChannel, bool isSimulator)
+ private IAppTester GetAppTester(CommunicationChannel communicationChannel, bool isSimulator, Version? osVersion)
{
// Only add the extra callback if we do know that the feature was indeed enabled
Action? logCallback = IsLldbEnabled() ? (l) => NotifyUserLldbCommand(_logger, l) : null;
- return _appTesterFactory.Create(communicationChannel, isSimulator, _mainLog, _logs, logCallback);
+ return _appTesterFactory.Create(communicationChannel, isSimulator, _mainLog, _logs, logCallback, osVersion);
}
private ExitCode ParseResult(TestExecutingResult testResult, string resultMessage, bool listenerConnected)
@@ -389,7 +390,7 @@ private void CopyLogsToMainLog()
{
_mainLog.WriteLine($"==================== {log.Description} ====================");
_mainLog.WriteLine($"Log file: {log.FullPath}");
-
+
try
{
// Read and append log content to the main log
@@ -406,7 +407,7 @@ private void CopyLogsToMainLog()
{
_mainLog.WriteLine($"Failed to read {log.Description}: {ex.Message}");
}
-
+
_mainLog.WriteLine($"==================== End of {log.Description} ====================");
_mainLog.WriteLine(string.Empty);
}
diff --git a/src/Microsoft.DotNet.XHarness.TestRunners.Common/iOSApplicationEntryPointBase.cs b/src/Microsoft.DotNet.XHarness.TestRunners.Common/iOSApplicationEntryPointBase.cs
index 0c18581d1..55825b75b 100644
--- a/src/Microsoft.DotNet.XHarness.TestRunners.Common/iOSApplicationEntryPointBase.cs
+++ b/src/Microsoft.DotNet.XHarness.TestRunners.Common/iOSApplicationEntryPointBase.cs
@@ -3,35 +3,58 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
+using System.IO;
#nullable enable
namespace Microsoft.DotNet.XHarness.TestRunners.Common;
public abstract class iOSApplicationEntryPointBase : ApplicationEntryPoint
{
+ ///
+ /// Logger used for outputting logs. Defaults to Console.Out.
+ ///
+ public TextWriter? Logger = Console.Out;
+
+ ///
+ /// The final path where test results in XML format will be saved.
+ ///
+ public string TestsResultsFinalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "test-results.xml");
+
public override async Task RunAsync()
{
var options = ApplicationOptions.Current;
- TcpTextWriter? writer;
- try
+ // On iOS 18 and later, transferring results over a TCP tunnel isn’t supported.
+ // Instead, copy the results file from the device to the host machine.
+ if (!OperatingSystem.IsMacCatalyst() && Environment.OSVersion.Version.Major >= 18)
{
- writer = options.UseTunnel
- ? TcpTextWriter.InitializeWithTunnelConnection(options.HostPort)
- : TcpTextWriter.InitializeWithDirectConnection(options.HostName, options.HostPort);
+ using TextWriter? resultsFileMaybe = options.EnableXml ? System.IO.File.CreateText(TestsResultsFinalPath) : null;
+ await InternalRunAsync(options, Logger, resultsFileMaybe);
+ Console.WriteLine($"Test results saved to: {TestsResultsFinalPath}");
}
- catch (Exception ex)
+ else
{
- Console.WriteLine("Failed to initialize TCP writer. Continuing on console." + Environment.NewLine + ex);
- writer = null; // null means we will fall back to console output
- }
+ TcpTextWriter? writer;
- using (writer)
- {
- var logger = (writer == null || options.EnableXml) ? new LogWriter(Device) : new LogWriter(Device, writer);
- logger.MinimumLogLevel = MinimumLogLevel.Info;
+ try
+ {
+ writer = options.UseTunnel
+ ? TcpTextWriter.InitializeWithTunnelConnection(options.HostPort)
+ : TcpTextWriter.InitializeWithDirectConnection(options.HostName, options.HostPort);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Failed to initialize TCP writer. Continuing on console." + Environment.NewLine + ex);
+ writer = null; // null means we will fall back to console output
+ }
+
+ using (writer)
+ {
+ var logger = (writer == null || options.EnableXml) ? new LogWriter(Device) : new LogWriter(Device, writer);
+ logger.MinimumLogLevel = MinimumLogLevel.Info;
- await InternalRunAsync(options, writer, writer);
+ await InternalRunAsync(options, writer, writer);
+ }
}
}
}
diff --git a/src/Microsoft.DotNet.XHarness.iOS.Shared/IResultFileHandler.cs b/src/Microsoft.DotNet.XHarness.iOS.Shared/IResultFileHandler.cs
new file mode 100644
index 000000000..e2d88f140
--- /dev/null
+++ b/src/Microsoft.DotNet.XHarness.iOS.Shared/IResultFileHandler.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+#nullable enable
+namespace Microsoft.DotNet.XHarness.iOS.Shared;
+
+public interface IResultFileHandler
+{
+ ///
+ /// Copy the XML results file from the app container (simulator or device) to the host path.
+ ///
+ Task CopyResultsAsync(
+ RunMode runMode,
+ bool isSimulator,
+ string osVersion,
+ string udid,
+ string bundleIdentifier,
+ string hostDestinationPath,
+ CancellationToken token);
+}
diff --git a/src/Microsoft.DotNet.XHarness.iOS.Shared/ResultFileHandler.cs b/src/Microsoft.DotNet.XHarness.iOS.Shared/ResultFileHandler.cs
new file mode 100644
index 000000000..c4640ee23
--- /dev/null
+++ b/src/Microsoft.DotNet.XHarness.iOS.Shared/ResultFileHandler.cs
@@ -0,0 +1,112 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.DotNet.XHarness.Common.Logging;
+using Microsoft.DotNet.XHarness.iOS.Shared.Execution;
+
+#nullable enable
+namespace Microsoft.DotNet.XHarness.iOS.Shared;
+
+public class ResultFileHandler : IResultFileHandler
+{
+ private IMlaunchProcessManager _processManager;
+ private IFileBackedLog _mainLog;
+
+ public ResultFileHandler(IMlaunchProcessManager pm, IFileBackedLog fs)
+ {
+ _processManager = pm;
+ _mainLog = fs;
+ }
+
+ public async Task CopyResultsAsync(
+ RunMode runMode,
+ bool isSimulator,
+ string osVersion,
+ string udid,
+ string bundleIdentifier,
+ string hostDestinationPath,
+ CancellationToken token)
+ {
+ // This file path is set in iOSApplicationEntryPointBase
+ string sourcePath = runMode == RunMode.iOS
+ ? "/Documents/test-results.xml"
+ : "/Library/Caches/Documents/test-results.xml";
+
+ if (isSimulator)
+ {
+ // Version format contains string like "Simulator 18.0".
+ string [] osVersionParts = osVersion.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ if (osVersionParts.Length < 2)
+ {
+ _mainLog.WriteLine("Simulator OS version is not in the expected format, skipping result copying.");
+ return false;
+ }
+
+ if (!Version.TryParse(osVersionParts[1], out Version osVersionParsed))
+ {
+ _mainLog.WriteLine("Simulator OS version is not in the expected format, skipping result copying.");
+ return false;
+ }
+
+ if (osVersionParsed.Major >= 18)
+ {
+
+ string cmd = $"cp \"$(xcrun simctl get_app_container {udid} {bundleIdentifier} data){sourcePath}\" \"{hostDestinationPath}\"";
+
+ await _processManager.ExecuteCommandAsync(
+ "/bin/bash",
+ new[] { "-c", cmd },
+ _mainLog,
+ _mainLog,
+ _mainLog,
+ TimeSpan.FromMinutes(1),
+ null,
+ cancellationToken: token);
+
+ if (!File.Exists(hostDestinationPath))
+ {
+ _mainLog.WriteLine($"Failed to copy results file from simulator. Expected at: {hostDestinationPath}");
+ return false;
+ }
+ }
+
+ return true;
+ }
+ else
+ {
+ if (!Version.TryParse(osVersion, out Version osVersionParsed))
+ {
+ _mainLog.WriteLine($"Device OS version is not in the expected format, skipping result copying.");
+ return false;
+ }
+
+ if (osVersionParsed.Major >= 18)
+ {
+ string cmd = $"xcrun devicectl device copy from --device {udid} --source {sourcePath} --destination {hostDestinationPath} --domain-type appDataContainer --domain-identifier {bundleIdentifier}";
+ await _processManager.ExecuteCommandAsync(
+ "/bin/bash",
+ new List { "-c", cmd },
+ _mainLog,
+ _mainLog,
+ _mainLog,
+ TimeSpan.FromMinutes(1),
+ null,
+ cancellationToken: token);
+
+ if (!File.Exists(hostDestinationPath))
+ {
+ _mainLog.WriteLine($"Failed to copy results file from device. Expected at: {hostDestinationPath}");
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests.csproj b/tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests.csproj
index 01307fd06..ef11c8698 100644
--- a/tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests.csproj
+++ b/tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests.csproj
@@ -53,7 +53,7 @@
-
+
diff --git a/tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests/ResultFileHandlerTests.cs b/tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests/ResultFileHandlerTests.cs
new file mode 100644
index 000000000..7bd403e35
--- /dev/null
+++ b/tests/Microsoft.DotNet.XHarness.iOS.Shared.Tests/ResultFileHandlerTests.cs
@@ -0,0 +1,172 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.DotNet.XHarness.Common.Logging;
+using Microsoft.DotNet.XHarness.iOS.Shared.Execution;
+using Moq;
+using Xunit;
+
+namespace Microsoft.DotNet.XHarness.iOS.Shared.Tests;
+
+public class ResultFileHandlerTests : IDisposable
+{
+ private readonly string _tempFile;
+
+ public ResultFileHandlerTests()
+ {
+ _tempFile = Path.GetTempFileName();
+ }
+
+ public void Dispose()
+ {
+ if (File.Exists(_tempFile))
+ {
+ File.Delete(_tempFile);
+ }
+ }
+
+ private static ResultFileHandler CreateHandler(
+ Mock processManagerMock,
+ Mock logMock)
+ {
+ return new ResultFileHandler(processManagerMock.Object, logMock.Object);
+ }
+
+ [Fact]
+ public async Task SimulatorBadOsVersionFormatReturnsFalse()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, true, "Simulator", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.False(result);
+ log.Verify(l => l.WriteLine("Simulator OS version is not in the expected format, skipping result copying."), Times.Once);
+ }
+
+ [Fact]
+ public async Task SimulatorBadOsVersionNumberReturnsFalse()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, true, "Simulator notanumber", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.False(result);
+ log.Verify(l => l.WriteLine("Simulator OS version is not in the expected format, skipping result copying."), Times.Once);
+ }
+
+ [Fact]
+ public async Task SimulatorOsVersionLessThan18ReturnsFalse()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, true, "Simulator 17.4", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.True(result);
+ }
+
+ [Fact]
+ public async Task SimulatorOsVersion18FileExistsReturnsTrue()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ File.WriteAllText(_tempFile, "dummy");
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, true, "Simulator 18.0", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.True(result);
+ }
+
+ [Fact]
+ public async Task SimulatorOsVersion18FileMissingReturnsFalse()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ if (File.Exists(_tempFile))
+ File.Delete(_tempFile);
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, true, "Simulator 18.0", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.False(result);
+ log.Verify(l => l.WriteLine($"Failed to copy results file from simulator. Expected at: {_tempFile}"), Times.Once);
+ }
+
+ [Fact]
+ public async Task DeviceBadOsVersionFormatReturnsFalse()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, false, "notanumber", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.False(result);
+ log.Verify(l => l.WriteLine("Device OS version is not in the expected format, skipping result copying."), Times.Once);
+ }
+
+ [Fact]
+ public async Task DeviceOsVersionLessThan18ReturnsTrue()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, false, "17.4", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.True(result);
+ }
+
+ [Fact]
+ public async Task DeviceOsVersion18FileExistsReturnsTrue()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ File.WriteAllText(_tempFile, "dummy");
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, false, "18.0", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.True(result);
+ }
+
+ [Fact]
+ public async Task DeviceOsVersion18FileMissingReturnsFalse()
+ {
+ Mock pm = new Mock();
+ Mock log = new Mock();
+ ResultFileHandler handler = CreateHandler(pm, log);
+
+ if (File.Exists(_tempFile))
+ File.Delete(_tempFile);
+
+ bool result = await handler.CopyResultsAsync(
+ RunMode.iOS, false, "18.0", "udid", "bundle", _tempFile, CancellationToken.None);
+
+ Assert.False(result);
+ log.Verify(l => l.WriteLine($"Failed to copy results file from device. Expected at: {_tempFile}"), Times.Once);
+ }
+}
diff --git a/tests/integration-tests/Apple/Device.Commands.Tests.proj b/tests/integration-tests/Apple/Device.Commands.Tests.proj
index 3a0de57b8..7a79f7188 100644
--- a/tests/integration-tests/Apple/Device.Commands.Tests.proj
+++ b/tests/integration-tests/Apple/Device.Commands.Tests.proj
@@ -6,8 +6,8 @@
System.Buffers.Tests
- $(AssetsBaseUri)/ios/test-app/ios-device/$(TestAppBundleName).app.zip
- $(ArtifactsTmpDir)test-app\ios-device
+ $(AssetsBaseUri)/ios/test-app-new/ios-device/$(TestAppBundleName).app.zip
+ $(ArtifactsTmpDir)test-app-new\ios-device
diff --git a/tests/integration-tests/Apple/Simulator.Commands.Tests.proj b/tests/integration-tests/Apple/Simulator.Commands.Tests.proj
index 8fa960587..bca59f7e7 100644
--- a/tests/integration-tests/Apple/Simulator.Commands.Tests.proj
+++ b/tests/integration-tests/Apple/Simulator.Commands.Tests.proj
@@ -10,8 +10,8 @@
System.Numerics.Vectors.Tests
- $(AssetsBaseUri)/ios/test-app/ios-simulator-64/$(TestAppBundleName).app.zip
- $(ArtifactsTmpDir)test-app\ios-simulator-64
+ $(AssetsBaseUri)/ios/test-app-new/ios-simulator-64/$(TestAppBundleName).app.zip
+ $(ArtifactsTmpDir)test-app-new\ios-simulator-64
diff --git a/tests/integration-tests/Apple/Simulator.Scouting.Commands.Tests.proj b/tests/integration-tests/Apple/Simulator.Scouting.Commands.Tests.proj
index c10cb9f07..eda021197 100644
--- a/tests/integration-tests/Apple/Simulator.Scouting.Commands.Tests.proj
+++ b/tests/integration-tests/Apple/Simulator.Scouting.Commands.Tests.proj
@@ -6,7 +6,7 @@
-
@@ -16,8 +16,8 @@
System.Numerics.Vectors.Tests
- $(AssetsBaseUri)/ios/test-app/ios-simulator-64/$(TestAppBundleName).app.zip
- $(ArtifactsTmpDir)test-app\ios-simulator-64
+ $(AssetsBaseUri)/ios/test-app-new/ios-simulator-64/$(TestAppBundleName).app.zip
+ $(ArtifactsTmpDir)test-app-new\ios-simulator-64
diff --git a/tests/integration-tests/Apple/TestAppBundle.proj b/tests/integration-tests/Apple/TestAppBundle.proj
index 48b3460ec..c28668355 100644
--- a/tests/integration-tests/Apple/TestAppBundle.proj
+++ b/tests/integration-tests/Apple/TestAppBundle.proj
@@ -13,9 +13,9 @@
- $(AssetsBaseUri)/ios/test-app/$(TestTarget)
+ $(AssetsBaseUri)/ios/test-app-new/$(TestTarget)
$(AppStorageUrl)/$(TestAppBundleName).zip
- $(ArtifactsTmpDir)test-app\$(TestTarget)
+ $(ArtifactsTmpDir)test-app-new\$(TestTarget)