Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ public void PublishNativeAOT(string id, string framework, string runtimeIdentifi
var projectDir = TestDirectory;
var projectFile = Path.Combine(projectDir, $"{Path.GetFileName(projectDir)}.csproj");

Assert.True(DotnetInternal.New(id, projectDir, DotNetCurrent),
Assert.True(DotnetInternal.New(id, projectDir, DotNetCurrent, output: _output),
$"Unable to create template {id}. Check test output for errors.");

var extendedBuildProps = isWindowsFramework ? PrepareNativeAotBuildPropsWindows(runtimeIdentifier) : PrepareNativeAotBuildProps();

string binLogFilePath = $"publish-{DateTime.UtcNow.ToFileTimeUtc()}.binlog";
Assert.True(DotnetInternal.Build(projectFile, "Release", framework: framework, properties: extendedBuildProps, runtimeIdentifier: runtimeIdentifier, binlogPath: binLogFilePath),
Assert.True(DotnetInternal.Build(projectFile, "Release", framework: framework, properties: extendedBuildProps, runtimeIdentifier: runtimeIdentifier, binlogPath: binLogFilePath, output: _output),
$"Project {Path.GetFileName(projectFile)} failed to build. Check test output/attachments for errors.");

var actualWarnings = BuildWarningsUtilities.ReadNativeAOTWarningsFromBinLog(binLogFilePath);
Expand Down Expand Up @@ -64,7 +64,7 @@ public void PublishNativeAOTRootAllMauiAssemblies(string id, string framework, s
var projectDir = TestDirectory;
var projectFile = Path.Combine(projectDir, $"{Path.GetFileName(projectDir)}.csproj");

Assert.True(DotnetInternal.New(id, projectDir, DotNetCurrent),
Assert.True(DotnetInternal.New(id, projectDir, DotNetCurrent, output: _output),
$"Unable to create template {id}. Check test output for errors.");

var extendedBuildProps = isWindowsFramework ? PrepareNativeAotBuildPropsWindows(runtimeIdentifier) : PrepareNativeAotBuildProps();
Expand All @@ -91,7 +91,7 @@ public void PublishNativeAOTRootAllMauiAssemblies(string id, string framework, s
""");

string binLogFilePath = $"publish-{DateTime.UtcNow.ToFileTimeUtc()}.binlog";
Assert.True(DotnetInternal.Build(projectFile, "Release", framework: framework, properties: extendedBuildProps, runtimeIdentifier: runtimeIdentifier, binlogPath: binLogFilePath),
Assert.True(DotnetInternal.Build(projectFile, "Release", framework: framework, properties: extendedBuildProps, runtimeIdentifier: runtimeIdentifier, binlogPath: binLogFilePath, output: _output),
$"Project {Path.GetFileName(projectFile)} failed to build. Check test output/attachments for errors.");

var actualWarnings = BuildWarningsUtilities.ReadNativeAOTWarningsFromBinLog(binLogFilePath);
Expand Down
27 changes: 14 additions & 13 deletions src/TestUtils/src/Microsoft.Maui.IntegrationTests/Android/Adb.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
锘縰sing System.Diagnostics;
using Xunit.Abstractions;

namespace Microsoft.Maui.IntegrationTests.Android
{
Expand All @@ -7,16 +8,16 @@ public static class Adb
static readonly string AdbTool = Path.Combine(TestEnvironment.GetAndroidSdkPath(), "platform-tools", "adb");
const int DEFAULT_TIMEOUT = 20;

public static bool WaitForEmulator(int timeout, string deviceId = "")
public static bool WaitForEmulator(int timeout, string deviceId = "", ITestOutputHelper? output = null)
{
var maxWaitTime = DateTime.UtcNow.AddSeconds(timeout);
int currentWaitTime = 0;
bool bootCompleted = false;
while (DateTime.UtcNow < maxWaitTime && !bootCompleted)
{
Console.WriteLine($"Waiting {currentWaitTime}/{timeout} seconds for the emulator to boot up...");
var adbOutput = RunForOutput(GetArgs("shell getprop sys.boot_completed", deviceId), out _);
Console.WriteLine($"sys.boot_completed: {adbOutput}");
output?.WriteLine($"Waiting {currentWaitTime}/{timeout} seconds for the emulator to boot up...");
var adbOutput = RunForOutput(GetArgs("shell getprop sys.boot_completed", deviceId), out _, output: output);
output?.WriteLine($"sys.boot_completed: {adbOutput}");
int.TryParse(adbOutput, out int bootCompletedPropValue);
bootCompleted = bootCompletedPropValue == 1;
Thread.Sleep(10000);
Expand All @@ -25,33 +26,33 @@ public static bool WaitForEmulator(int timeout, string deviceId = "")
return bootCompleted;
}

public static bool KillEmulator(string deviceId = "")
public static bool KillEmulator(string deviceId = "", ITestOutputHelper? output = null)
{
return Run(GetArgs("emu kill", deviceId));
return Run(GetArgs("emu kill", deviceId), output: output);
}

public static bool UninstallPackage(string package, string deviceId = "")
public static bool UninstallPackage(string package, string deviceId = "", ITestOutputHelper? output = null)
{
return Run(GetArgs($"uninstall {package}", deviceId));
return Run(GetArgs($"uninstall {package}", deviceId), output: output);
}

static string GetArgs(string args, string deviceId)
{
return string.IsNullOrEmpty(deviceId) ? args : $"-s {deviceId} " + args;
}

public static bool Run(string args, int timeout = DEFAULT_TIMEOUT, string deviceId = "")
public static bool Run(string args, int timeout = DEFAULT_TIMEOUT, string deviceId = "", ITestOutputHelper? output = null)
{
RunForOutput(args, out int exitCode, timeout, deviceId);
RunForOutput(args, out int exitCode, timeout, deviceId, output: output);
if (exitCode != 0)
Console.WriteLine(exitCode);
output?.WriteLine(exitCode.ToString());

return exitCode == 0;
}

public static string RunForOutput(string args, out int exitCode, int timeout = DEFAULT_TIMEOUT, string deviceId = "")
public static string RunForOutput(string args, out int exitCode, int timeout = DEFAULT_TIMEOUT, string deviceId = "", ITestOutputHelper? output = null)
{
return ToolRunner.Run(AdbTool, GetArgs(args, deviceId), out exitCode, timeoutInSeconds: timeout);
return ToolRunner.Run(AdbTool, GetArgs(args, deviceId), out exitCode, timeoutInSeconds: timeout, output: output);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
锘縰sing System.Diagnostics;
using Xunit.Abstractions;

namespace Microsoft.Maui.IntegrationTests.Android
{
Expand All @@ -18,69 +19,69 @@ public class Emulator
public string Id => $"emulator-{Port}";
public string SystemImageId => $"system-images;android-{ApiLevel};{ImageType};{Abi}";

public bool AcceptLicenses(out string acceptLicenseOutput)
public bool AcceptLicenses(out string acceptLicenseOutput, ITestOutputHelper? output = null)
{
acceptLicenseOutput = ToolRunner.Run(new ProcessStartInfo(SdkManagerTool, "--licenses"), out int exitCode, timeoutInSeconds: 30, inputAction: (p) =>
{
for (int i = 0; i < 10; i++)
{
p.StandardInput.WriteLine('y');
}
});
}, output: output);

if (exitCode != 0)
Console.WriteLine(acceptLicenseOutput);
output?.WriteLine(acceptLicenseOutput);

return exitCode == 0;
}

public bool InstallAvd(out string installOutput)
public bool InstallAvd(out string installOutput, ITestOutputHelper? output = null)
{
installOutput = ToolRunner.Run(SdkManagerTool, $"\"{SystemImageId}\"", out int exitCode, timeoutInSeconds: 180);
installOutput = ToolRunner.Run(SdkManagerTool, $"\"{SystemImageId}\"", out int exitCode, timeoutInSeconds: 180, output: output);
if (exitCode != 0)
Console.WriteLine(installOutput);
output?.WriteLine(installOutput);

return exitCode == 0;
}

public bool DeleteAvd()
public bool DeleteAvd(ITestOutputHelper? output = null)
{
var deleteOutput = ToolRunner.Run(AvdManagerTool, $"delete avd -n {Name}", out int exitCode, timeoutInSeconds: 15);
var deleteOutput = ToolRunner.Run(AvdManagerTool, $"delete avd -n {Name}", out int exitCode, timeoutInSeconds: 15, output: output);
return exitCode == 0 || deleteOutput.Contains($"There is no Android Virtual Device named '{Name}'", StringComparison.OrdinalIgnoreCase);
}

public bool CreateAvd(bool force = true)
public bool CreateAvd(bool force = true, ITestOutputHelper? output = null)
{
var createArgs = $"create avd -n {Name} -k \"{SystemImageId}\" -d {DeviceType}";
if (force)
createArgs += " -f";

var createOutput = ToolRunner.Run(AvdManagerTool, createArgs, out int exitCode, timeoutInSeconds: 15);
var createOutput = ToolRunner.Run(AvdManagerTool, createArgs, out int exitCode, timeoutInSeconds: 15, output: output);
if (exitCode != 0)
Console.WriteLine(createOutput);
output?.WriteLine(createOutput);

return exitCode == 0;
}

public bool LaunchAndWaitForAvd(int timeToWaitInSeconds, string logFile)
public bool LaunchAndWaitForAvd(int timeToWaitInSeconds, string logFile, ITestOutputHelper? output = null)
{
if (Adb.WaitForEmulator(10, Id))
if (Adb.WaitForEmulator(10, Id, output: output))
return true;

if (!DeleteAvd())
if (!DeleteAvd(output: output))
return false;

if (!CreateAvd())
if (!CreateAvd(output: output))
return false;

var launchArgs = $"-verbose -detect-image-hang -port {Port} -avd {Name}";
launchArgs += TestEnvironment.IsRunningOnCI ? " -no-window -no-boot-anim -no-audio -no-snapshot -cache-size 512" : string.Empty;

// Emulator process does not stop once the emulator is running, end it after 15 seconds and then begin polling for boot success
Console.WriteLine($"Launching AVD: {Name}...");
var emulatorOutput = ToolRunner.Run(EmulatorTool, launchArgs, out _, timeoutInSeconds: 15);
output?.WriteLine($"Launching AVD: {Name}...");
var emulatorOutput = ToolRunner.Run(EmulatorTool, launchArgs, out _, timeoutInSeconds: 15, output: output);
File.WriteAllText(logFile, emulatorOutput);
return Adb.WaitForEmulator(timeToWaitInSeconds, Id);
return Adb.WaitForEmulator(timeToWaitInSeconds, Id, output: output);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void RunOnAndroid(string id, string framework, string config, string? tri
var projectDir = TestDirectory;
var projectFile = Path.Combine(projectDir, $"{Path.GetFileName(projectDir)}.csproj");

Assert.True(DotnetInternal.New(id, projectDir, framework),
Assert.True(DotnetInternal.New(id, projectDir, framework, output: _output),
$"Unable to create template {id}. Check test output for errors.");

var buildProps = BuildProps;
Expand All @@ -101,15 +101,15 @@ public void RunOnAndroid(string id, string framework, string config, string? tri

AddInstrumentation(projectDir);

Assert.True(DotnetInternal.Build(projectFile, config, target: "Install", framework: $"{framework}-android", properties: BuildProps),
Assert.True(DotnetInternal.Build(projectFile, config, target: "Install", framework: $"{framework}-android", properties: BuildProps, output: _output),
$"Project {Path.GetFileName(projectFile)} failed to install. Check test output/attachments for errors.");

// Write xh-results to the log directory for artifact collection
var xhResultsDir = Path.Combine(TestEnvironment.GetLogDirectory(), "xh-results", Path.GetFileName(projectDir));
Directory.CreateDirectory(xhResultsDir);

testPackage = $"com.companyname.{Path.GetFileName(projectDir).ToLowerInvariant()}";
Assert.True(XHarness.RunAndroid(testPackage, xhResultsDir, -1),
Assert.True(XHarness.RunAndroid(testPackage, xhResultsDir, -1, output: _output),
$"Project {Path.GetFileName(projectFile)} failed to run. Check test output/attachments for errors.");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
锘縰sing System.Text.Json;
using Xunit.Abstractions;

namespace Microsoft.Maui.IntegrationTests.Apple
{
public class Simulator
{
readonly string XCRunTool = "xcrun";
readonly ITestOutputHelper? _output;

public Simulator(ITestOutputHelper? output = null)
{
_output = output;
}

string? _xharnessID;

Expand Down Expand Up @@ -45,12 +52,12 @@ string ResolveXHarnessID()
{
var versionSuffix = $"{latestVersion.Major}.{latestVersion.Minor}";
var resolvedTarget = $"{baseTarget}_{versionSuffix}";
Console.WriteLine($"Auto-detected iOS target: {resolvedTarget}");
_output?.WriteLine($"Auto-detected iOS target: {resolvedTarget}");
return resolvedTarget;
}

// Fallback to base target (may fail if XHarness can't find a match)
Console.WriteLine($"Warning: Could not auto-detect iOS version, using '{baseTarget}' which may fail.");
_output?.WriteLine($"Warning: Could not auto-detect iOS version, using '{baseTarget}' which may fail.");
return baseTarget;
}

Expand All @@ -62,7 +69,7 @@ string ResolveXHarnessID()
{
try
{
var output = ToolRunner.Run(XCRunTool, "simctl list runtimes --json", out int exitCode, timeoutInSeconds: 30);
var output = ToolRunner.Run(XCRunTool, "simctl list runtimes --json", out int exitCode, timeoutInSeconds: 30, output: _output);
if (exitCode != 0 || string.IsNullOrEmpty(output))
return null;

Expand Down Expand Up @@ -99,7 +106,7 @@ string ResolveXHarnessID()
}
catch (Exception ex)
{
Console.WriteLine($"Failed to detect iOS version: {ex.Message}");
_output?.WriteLine($"Failed to detect iOS version: {ex.Message}");
return null;
}
}
Expand All @@ -110,7 +117,7 @@ public string GetUDID()
if (!string.IsNullOrEmpty(_udid))
return _udid;

var xharnessOutput = XHarness.GetSimulatorUDID(XHarnessID).Trim();
var xharnessOutput = XHarness.GetSimulatorUDID(XHarnessID, output: _output).Trim();

// XHarness returns a UDID on success, or an error message on failure.
// A valid UDID is a UUID format (e.g., "DE87D078-70D4-47F6-9F21-82612D9D4F7E")
Expand All @@ -132,21 +139,21 @@ static bool IsValidUDID(string value)

public bool Launch()
{
var output = ToolRunner.Run(XCRunTool, $"simctl boot {GetUDID()}", out int exitCode, timeoutInSeconds: 30);
var output = ToolRunner.Run(XCRunTool, $"simctl boot {GetUDID()}", out int exitCode, timeoutInSeconds: 30, output: _output);
// Exit code 0 = successfully booted
// Exit code 149 with "current state: Booted" = already running (also success)
return exitCode == 0 || (exitCode == 149 && output.Contains("current state: Booted", StringComparison.Ordinal));
}

public bool Shutdown()
{
ToolRunner.Run(XCRunTool, $"simctl shutdown {GetUDID()}", out int exitCode, timeoutInSeconds: 60);
ToolRunner.Run(XCRunTool, $"simctl shutdown {GetUDID()}", out int exitCode, timeoutInSeconds: 60, output: _output);
return exitCode == 0;
}

public bool ShowWindow()
{
ToolRunner.Run("open", $"-a Simulator", out int exitCode, timeoutInSeconds: 30);
ToolRunner.Run("open", $"-a Simulator", out int exitCode, timeoutInSeconds: 30, output: _output);
return exitCode == 0;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ void RunOniOS(string id, string config, string framework, RuntimeVariant runtime
var projectDir = TestDirectory;
var projectFile = Path.Combine(projectDir, $"{Path.GetFileName(projectDir)}.csproj");

Assert.True(DotnetInternal.New(id, projectDir, framework),
Assert.True(DotnetInternal.New(id, projectDir, framework, output: _output),
$"Unable to create template {id}. Check test output for errors.");

var buildProps = BuildProps;
Expand All @@ -126,7 +126,7 @@ void RunOniOS(string id, string config, string framework, RuntimeVariant runtime
buildProps.Add("TrimmerSingleWarn=false"); // Disable trimmer warnings for iOS full trimming builds due to ObjCRuntime issues
}

Assert.True(DotnetInternal.Build(projectFile, config, framework: $"{framework}-ios", properties: buildProps, runtimeIdentifier: runtimeIdentifier),
Assert.True(DotnetInternal.Build(projectFile, config, framework: $"{framework}-ios", properties: buildProps, runtimeIdentifier: runtimeIdentifier, output: _output),
$"Project {Path.GetFileName(projectFile)} failed to build. Check test output/attachments for errors.");

// Find the .app bundle - it may be in the bin folder with or without a RID subfolder depending on build settings
Expand All @@ -142,7 +142,7 @@ void RunOniOS(string id, string config, string framework, RuntimeVariant runtime
// Let XHarness find the simulator based on target (e.g., ios-simulator-64_18.5).
// Don't pass a specific UDID - this gives XHarness full control over the simulator
// lifecycle and avoids race conditions with watchdog disabling.
Assert.True(XHarness.RunAppleForTimeout(appFile, xhResultsDir, _simulatorFixture.TestSimulator.XHarnessID),
Assert.True(XHarness.RunAppleForTimeout(appFile, xhResultsDir, _simulatorFixture.TestSimulator.XHarnessID, output: _output),
$"Project {Path.GetFileName(projectFile)} failed to run. Check test output/attachments for errors.");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void BuildMauiBlazorWebSolution(string framework, string config, string a

_output.WriteLine($"Creating project in {solutionProjectDir}");

Assert.True(DotnetInternal.New(templateShortName, outputDirectory: solutionProjectDir, framework: framework, additionalDotNetNewParams: additionalDotNetNewParams),
Assert.True(DotnetInternal.New(templateShortName, outputDirectory: solutionProjectDir, framework: framework, additionalDotNetNewParams: additionalDotNetNewParams, output: _output),
$"Unable to create template {templateShortName}. Check test output for errors.");

_output.WriteLine($"Solution directory: {solutionProjectDir} (exists? {Directory.Exists(solutionProjectDir)})");
Expand All @@ -78,11 +78,11 @@ public void BuildMauiBlazorWebSolution(string framework, string config, string a
_output.WriteLine($"MAUI app project file: {mauiAppProjectFile} (exists? {File.Exists(mauiAppProjectFile)})");

_output.WriteLine($"Building Blazor Web app: {webAppProjectFile}");
Assert.True(DotnetInternal.Build(webAppProjectFile, config, target: "", properties: BuildProps, msbuildWarningsAsErrors: true),
Assert.True(DotnetInternal.Build(webAppProjectFile, config, target: "", properties: BuildProps, msbuildWarningsAsErrors: true, output: _output),
$"Project {Path.GetFileName(webAppProjectFile)} failed to build. Check test output/attachments for errors.");

_output.WriteLine($"Building .NET MAUI app: {mauiAppProjectFile} props: {buildProps}");
Assert.True(DotnetInternal.Build(mauiAppProjectFile, config, target: "", properties: buildProps, msbuildWarningsAsErrors: true),
Assert.True(DotnetInternal.Build(mauiAppProjectFile, config, target: "", properties: buildProps, msbuildWarningsAsErrors: true, output: _output),
$"Project {Path.GetFileName(mauiAppProjectFile)} failed to build. Check test output/attachments for errors.");
}
}
Loading
Loading