/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Testing/TestRunner.TestRunHandler.cs
217 строк
10 KB
Cyrus Najmabadi
Remove unused usings
18 июн 2025, 22:17
18 июн 2025, 22:17
481c36c
Код
Авторство
О чём код?
// 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.Collections.Immutable; using System.Collections.ObjectModel; using System.Text; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Testing; internal sealed partial class TestRunner { private sealed class TestRunHandler(BufferedProgress<RunTestsPartialResult> progress, TestProgress initialProgress, ILogger logger) : ITestRunEventsHandler { private readonly ILogger _logger = logger; private readonly BufferedProgress<RunTestsPartialResult> _progress = progress; private readonly TestProgress _initialProgress = initialProgress; private bool _isComplete = false; public void HandleLogMessage(TestMessageLevel level, string? message) { // Don't report log messages here. The log output is dependent on the test framework being used and does not consistently // report the information we desire (for example the test names that passed). Instead we report the test run information manually. // Any information here is also reported in the vs test console logs (written to the extension logs directory). return; } public void HandleRawMessage(string rawMessage) { // No need to do anything with raw messages. return; } public void HandleTestRunComplete(TestRunCompleteEventArgs testRunCompleteArgs, TestRunChangedEventArgs? lastChunkArgs, ICollection<AttachmentSet>? runContextAttachments, ICollection<string>? executorUris) { _isComplete = true; _logger.LogDebug($"Test run completed in {testRunCompleteArgs.ElapsedTimeInRunningTests}"); // Report the last set of tests. var stats = CreateReport(testRunCompleteArgs.TestRunStatistics); var message = CreateTestCaseReportMessage(lastChunkArgs); var partialResult = new RunTestsPartialResult(LanguageServerResources.Running_tests, message ?? string.Empty, stats); _progress.Report(partialResult); // Report any errors running the tests if (testRunCompleteArgs.Error != null) { _progress.Report(partialResult with { Message = string.Format(LanguageServerResources.Test_run_error, testRunCompleteArgs.Error) }); return; } var state = LanguageServerResources.Passed; if (testRunCompleteArgs.IsCanceled) { state = LanguageServerResources.Canceled; } else if (testRunCompleteArgs.IsAborted) { state = LanguageServerResources.Aborted; } else if (stats?.TestsFailed != 0) { state = LanguageServerResources.Failed; } // Report the test summary (similar to the dotnet test output). message = @$"==== {LanguageServerResources.Summary} ===={Environment.NewLine}{state} - {string.Format(LanguageServerResources.Failed_0_Passed_1_Skipped_2_Total_3_Duration_4, stats?.TestsFailed, stats?.TestsPassed, stats?.TestsSkipped, stats?.TotalTests, RunTestsHandler.GetShortTimespan(testRunCompleteArgs.ElapsedTimeInRunningTests))}{Environment.NewLine}"; _progress.Report(partialResult with { Message = message }); } public void HandleTestRunStatsChange(TestRunChangedEventArgs? testRunChangedArgs) { Contract.ThrowIfTrue(_isComplete); if (testRunChangedArgs?.TestRunStatistics != null) { var stats = CreateReport(testRunChangedArgs.TestRunStatistics); var message = CreateTestCaseReportMessage(testRunChangedArgs); _progress.Report(new RunTestsPartialResult(LanguageServerResources.Running_tests, message, stats)); } } public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo) { // TODO - implement debug tests. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1799066/ throw new NotImplementedException(); } private TestProgress? CreateReport(ITestRunStatistics? testRunStatistics) { if (testRunStatistics?.Stats == null) { return null; } long passed = 0, failed = 0, skipped = 0; foreach (var (outcome, amount) in testRunStatistics.Stats) { switch (outcome) { case TestOutcome.Passed: passed = amount; break; case TestOutcome.Failed: failed = amount; break; case TestOutcome.Skipped: skipped = amount; break; default: break; } } var stats = _initialProgress with { TestsPassed = passed, TestsFailed = failed, TestsSkipped = skipped }; return stats; } /// <summary> /// Create a nicely formatted report of the test outcome including any error message or stack trace. /// Ensures that we're not creating duplicate blank lines and have proper indentation. /// </summary> private static string CreateTestCaseReportMessage(TestRunChangedEventArgs? testRunChangedEventArgs) { if (testRunChangedEventArgs?.NewTestResults == null) { return string.Empty; } var results = testRunChangedEventArgs.NewTestResults.Select(result => { var messageBuilder = new StringBuilder(); messageBuilder.AppendLine($"[{result.Outcome}] {result.TestCase.DisplayName}"); if (!string.IsNullOrWhiteSpace(result.ErrorMessage)) { messageBuilder.AppendLine(IndentString($"{LanguageServerResources.Message}:", 4)); messageBuilder.AppendLine(IndentString(result.ErrorMessage, 8)); } if (!string.IsNullOrWhiteSpace(result.ErrorStackTrace)) { messageBuilder.AppendLine(value: IndentString($"{LanguageServerResources.Stack_Trace}:", 4)); messageBuilder.AppendLine(IndentString(result.ErrorStackTrace, 8)); } var standardOutputMessages = GetTestMessages(result.Messages, TestResultMessage.StandardOutCategory); if (standardOutputMessages.Length > 0) { messageBuilder.AppendLine(value: IndentString($"{LanguageServerResources.Standard_Output_Messages}:", 4)); messageBuilder.AppendLine(FormatMessages(standardOutputMessages, 8)); } var standardErrorMessages = GetTestMessages(result.Messages, TestResultMessage.StandardErrorCategory); if (standardErrorMessages.Length > 0) { messageBuilder.AppendLine(value: IndentString($"{LanguageServerResources.Standard_Error_Messages}:", 4)); messageBuilder.AppendLine(FormatMessages(standardErrorMessages, 8)); } var debugTraceMessages = GetTestMessages(result.Messages, TestResultMessage.DebugTraceCategory); if (debugTraceMessages.Length > 0) { messageBuilder.AppendLine(value: IndentString($"{LanguageServerResources.Debug_Trace_Messages}:", 4)); messageBuilder.AppendLine(FormatMessages(debugTraceMessages, 8)); } var additionalInfoMessages = GetTestMessages(result.Messages, TestResultMessage.AdditionalInfoCategory); if (additionalInfoMessages.Length > 0) { messageBuilder.AppendLine(value: IndentString($"{LanguageServerResources.Additional_Info_Messages}:", 4)); messageBuilder.AppendLine(FormatMessages(additionalInfoMessages, 8)); } return messageBuilder.ToString(); }); return string.Join("", results); static string IndentString(string text, int count) { var indentation = new string(' ', count); return text.Replace(Environment.NewLine, $"{Environment.NewLine}{indentation}").TrimEnd().Insert(0, indentation); } static ImmutableArray<TestResultMessage> GetTestMessages(Collection<TestResultMessage> messages, string requiredCategory) { return messages.WhereAsArray(static (msg, category) => msg.Category.Equals(category, StringComparison.OrdinalIgnoreCase), requiredCategory); } static string FormatMessages(ImmutableArray<TestResultMessage> messages, int indentation) { var builder = new StringBuilder(); foreach (var message in messages) { if (message.Text is null) continue; var indentedMessage = IndentString(message.Text, indentation); if (!string.IsNullOrWhiteSpace(indentedMessage)) builder.Append(indentedMessage); } return builder.ToString(); } } } }