diff --git a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppRunnerBase.cs b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppRunnerBase.cs index a24848d6e..e2e94d048 100644 --- a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppRunnerBase.cs +++ b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppRunnerBase.cs @@ -327,11 +327,8 @@ private CancellationTokenSource CaptureLogStream(string appName, bool useSimctl, } else { - // For MacCatalyst, the test output log does not propagate to the main log. - // Hence, we duplicate the log to the main console log to simplify the UX of failure investigation. - IFileBackedLog aggregatedAppOutputLog = Log.CreateReadableAggregatedLog(_mainLog, log); _processManager - .ExecuteCommandAsync("log", logArgs, _mainLog, aggregatedAppOutputLog, aggregatedAppOutputLog, TimeSpan.FromDays(1), cancellationToken: streamCancellation) + .ExecuteCommandAsync("log", logArgs, _mainLog, log, log, TimeSpan.FromDays(1), cancellationToken: streamCancellation) .DoNotAwait(); } diff --git a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs index 06dbb6c36..bbe640e9f 100644 --- a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs +++ b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs @@ -362,15 +362,12 @@ private async Task RunDeviceTests( // We need to check for MT1111 (which means that mlaunch won't wait for the app to exit) IFileBackedLog aggregatedLog = Log.CreateReadableAggregatedLog(_mainLog, testReporter.CallbackLog); - // The app output log is not directly accessible. - // Hence, we duplicate the log to the main console log to simplify the UX of failure investigation. - IFileBackedLog aggregatedAppOutputLog = Log.CreateReadableAggregatedLog(_mainLog, appOutputLog); var result = await RunAndWatchForAppSignal(() => _processManager.ExecuteCommandAsync( mlaunchArguments, aggregatedLog, - aggregatedAppOutputLog, - aggregatedAppOutputLog, + appOutputLog, + appOutputLog, timeout, envVars, cancellationToken: cancellationToken)); diff --git a/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs b/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs index f9f2e4cd7..2d2530f0f 100644 --- a/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs +++ b/src/Microsoft.DotNet.XHarness.Apple/Orchestration/TestOrchestrator.cs @@ -248,7 +248,15 @@ private async Task ExecuteApp( skippedTestClasses: classMethodFilters?.ToArray(), cancellationToken: cancellationToken); - return ParseResult(testResult, resultMessage, appTester.ListenerConnected); + ExitCode exitCode = ParseResult(testResult, resultMessage, appTester.ListenerConnected); + + if (!target.Platform.IsSimulator()) // Simulator app logs are already included in the main log + { + // Copy system and application logs to the main log for better failure investigation. + CopyLogsToMainLog(); + } + + return exitCode; } private async Task ExecuteMacCatalystApp( @@ -278,7 +286,12 @@ private async Task ExecuteMacCatalystApp( skippedTestClasses: classMethodFilters?.ToArray(), cancellationToken: cancellationToken); - return ParseResult(testResult, resultMessage, appTester.ListenerConnected); + ExitCode exitCode = ParseResult(testResult, resultMessage, appTester.ListenerConnected); + + // Copy system and application logs to the main log for better failure investigation. + CopyLogsToMainLog(); + + return exitCode; } private IAppTester GetAppTester(CommunicationChannel communicationChannel, bool isSimulator) @@ -364,4 +377,38 @@ ExitCode LogProblem(string message, ExitCode defaultExitCode) return ExitCode.GENERAL_FAILURE; } } + + /// + /// Copy system and application logs to the main log for better failure investigation. + /// + private void CopyLogsToMainLog() + { + var logs = _logs.Where(log => log.Description == LogType.SystemLog.ToString() || log.Description == LogType.ApplicationLog.ToString()).ToList(); + + foreach (var log in logs) + { + _mainLog.WriteLine($"==================== {log.Description} ===================="); + _mainLog.WriteLine($"Log file: {log.FullPath}"); + + try + { + // Read and append log content to the main log + using var reader = log.GetReader(); + while (!reader.EndOfStream) + { + var logContent = reader.ReadLine(); + if (logContent is null) + continue; + _mainLog.WriteLine(logContent); + } + } + catch (Exception ex) + { + _mainLog.WriteLine($"Failed to read {log.Description}: {ex.Message}"); + } + + _mainLog.WriteLine($"==================== End of {log.Description} ===================="); + _mainLog.WriteLine(string.Empty); + } + } }