diff --git a/src/Microsoft.DotNet.XHarness.Android/AdbRunner.cs b/src/Microsoft.DotNet.XHarness.Android/AdbRunner.cs index 2b8803774..dbfb7a8ad 100644 --- a/src/Microsoft.DotNet.XHarness.Android/AdbRunner.cs +++ b/src/Microsoft.DotNet.XHarness.Android/AdbRunner.cs @@ -154,6 +154,9 @@ public bool TryDumpAdbLog(string outputFilePath, string filterSpec = "") Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath) ?? throw new ArgumentNullException(nameof(outputFilePath))); File.WriteAllText(outputFilePath, result.StandardOutput); _log.LogInformation($"Wrote current ADB log to {outputFilePath}"); + // The adb log is not directly accessible. + // Hence, we duplicate the log to the main console log to simplify the UX of failure investigation. + _log.LogInformation($"ADB log output:{Environment.NewLine}{result.StandardOutput}"); return true; } } diff --git a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs index 41f676fae..bbe640e9f 100644 --- a/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs +++ b/src/Microsoft.DotNet.XHarness.Apple/AppOperations/AppTester.cs @@ -190,7 +190,8 @@ public AppTester( using var crashLogs = new Logs(_logs.Directory); ICrashSnapshotReporter crashReporter = _snapshotReporterFactory.Create(_mainLog, crashLogs, isDevice: !isSimulator, device.Name); - using ITestReporter testReporter = _testReporterFactory.Create(_mainLog, + using ITestReporter testReporter = _testReporterFactory.Create( + _mainLog, _mainLog, _logs, crashReporter, @@ -361,6 +362,7 @@ 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); + var result = await RunAndWatchForAppSignal(() => _processManager.ExecuteCommandAsync( mlaunchArguments, aggregatedLog, 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); + } + } }