diff --git a/TestFx.sln b/TestFx.sln index 53b4a4b320..ef5738fbb2 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -222,7 +222,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.Engine.UnitTests", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTest.SourceGeneration.UnitTests", "test\UnitTests\MSTest.SourceGeneration.UnitTests\MSTest.SourceGeneration.UnitTests.csproj", "{E6C0466E-BE8D-C04F-149A-FD98438F1413}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Testing.Extensions.AzureDevOps", "src\Platform\Microsoft.Testing.Extensions.AzureDevOps\Microsoft.Testing.Extensions.AzureDevOps.csproj", "{F608D3A3-125B-CD88-1D51-8714ED142029}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Testing.Extensions.AzureDevOpsReport", "src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj", "{F608D3A3-125B-CD88-1D51-8714ED142029}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/eng/TestingPlatformRunner/TestingPlatform.Runner.targets b/eng/TestingPlatformRunner/TestingPlatform.Runner.targets index 212abfe0a8..6088a84863 100644 --- a/eng/TestingPlatformRunner/TestingPlatform.Runner.targets +++ b/eng/TestingPlatformRunner/TestingPlatform.Runner.targets @@ -24,7 +24,7 @@ <_TestRunner Condition="'%(TestToRun.Architecture)'=='x86' And Exists('$(DotNetRoot)x86\dotnet.exe')">$(DotNetRoot)x86\dotnet.exe <_TestRunner Condition="'$(_TestRunner)'==''">$(DotNetTool) - <_TestRunnerArgs>exec --depsfile "$(_CoreDepsPath)" --runtimeconfig "$(_CoreRuntimeConfigPath)" $(TestRuntimeAdditionalArguments) "$(_TestAssembly)" --report-trx --report-trx-filename "$(_TestResultTrxFileName)" --results-directory "$(_TestResultDirectory)" $(_TestRunnerAdditionalArguments) + <_TestRunnerArgs>exec --depsfile "$(_CoreDepsPath)" --runtimeconfig "$(_CoreRuntimeConfigPath)" $(TestRuntimeAdditionalArguments) "$(_TestAssembly)" --report-trx --report-trx-filename "$(_TestResultTrxFileName)" --results-directory "$(_TestResultDirectory)" --report-azdo $(_TestRunnerAdditionalArguments) diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsReporter.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsReporter.cs deleted file mode 100644 index 6a919fa75d..0000000000 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsReporter.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Testing.Extensions.AzureDevOps; -using Microsoft.Testing.Extensions.AzureDevOps.Resources; -using Microsoft.Testing.Extensions.Reporting; -using Microsoft.Testing.Platform; -using Microsoft.Testing.Platform.CommandLine; -using Microsoft.Testing.Platform.Extensions.Messages; -using Microsoft.Testing.Platform.Extensions.OutputDevice; -using Microsoft.Testing.Platform.Extensions.TestHost; -using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.Platform.OutputDevice; - -namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; - -internal sealed class AzureDevOpsReporter : - IDataConsumer, - IDataProducer, - IOutputDeviceDataProducer -{ - private readonly IOutputDevice _outputDisplay; - - private static readonly char[] NewlineCharacters = new char[] { '\r', '\n' }; - private readonly ICommandLineOptions _commandLine; - private readonly IEnvironment _environment; - private string _severity = "error"; - - public AzureDevOpsReporter( - ICommandLineOptions commandLine, - IEnvironment environment, - IOutputDevice outputDisplay) - { - _commandLine = commandLine; - _environment = environment; - _outputDisplay = outputDisplay; - } - - public Type[] DataTypesConsumed { get; } = - [ - typeof(TestNodeUpdateMessage) - ]; - - public Type[] DataTypesProduced { get; } = [typeof(SessionFileArtifact)]; - - /// - public string Uid { get; } = nameof(AzureDevOpsReporter); - - /// - public string Version { get; } = AppVersion.DefaultSemVer; - - /// - public string DisplayName { get; } = AzureDevOpsResources.DisplayName; - - /// - public string Description { get; } = AzureDevOpsResources.Description; - - /// - public Task IsEnabledAsync() - { - bool isEnabled = _commandLine.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsOptionName) - && string.Equals(_environment.GetEnvironmentVariable("TF_BUILD"), "true", StringComparison.OrdinalIgnoreCase); - - if (isEnabled) - { - bool found = _commandLine.TryGetOptionArgumentList(AzureDevOpsCommandLineOptions.AzureDevOpsReportSeverity, out string[]? arguments); - if (found && arguments?.Length > 0) - { - _severity = arguments[0].ToLowerInvariant(); - } - } - - return Task.FromResult(isEnabled); - } - - public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - if (value is not TestNodeUpdateMessage nodeUpdateMessage) - { - return; - } - - TestNodeStateProperty? nodeState = nodeUpdateMessage.TestNode.Properties.SingleOrDefault(); - - switch (nodeState) - { - case FailedTestNodeStateProperty failed: - await WriteExceptionAsync(failed.Explanation, failed.Exception); - break; - case ErrorTestNodeStateProperty error: - await WriteExceptionAsync(error.Explanation, error.Exception); - break; - case CancelledTestNodeStateProperty cancelled: - await WriteExceptionAsync(cancelled.Explanation, cancelled.Exception); - break; - case TimeoutTestNodeStateProperty timeout: - await WriteExceptionAsync(timeout.Explanation, timeout.Exception); - break; - } - } - - private async Task WriteExceptionAsync(string? explanation, Exception? exception) - { - if (exception == null || exception.StackTrace == null) - { - return; - } - - string message = explanation ?? exception.Message; - - if (message == null) - { - return; - } - - string stackTrace = exception.StackTrace; - string[] lines = stackTrace.Split(NewlineCharacters, StringSplitOptions.RemoveEmptyEntries); - (string Code, string File, int LineNumber)? location = lines.Select(GetStackFrameLocation).FirstOrDefault(location => location is not null); - if (location != null) - { - string root = RootFinder.Find(); - string file = location.Value.File; - string relativePath = file.StartsWith(root, StringComparison.CurrentCultureIgnoreCase) ? file.Substring(root.Length) : file; - string relativeNormalizedPath = relativePath.Replace('\\', '/'); - - string err = AzDoEscaper.Escape(message); - - string line = $"##vso[task.logissue type={_severity};sourcepath={relativeNormalizedPath};linenumber={location.Value.LineNumber};columnnumber=1]{err}"; - await _outputDisplay.DisplayAsync(this, new FormattedTextOutputDeviceData(line)); - } - } - - internal /* for testing */ static (string Code, string File, int LineNumber)? GetStackFrameLocation(string stackTraceLine) - { - Match match = StackTraceHelper.GetFrameRegex().Match(stackTraceLine); - if (!match.Success) - { - return null; - } - - string code = match.Groups["code"].Value; - bool weHaveFilePathAndCodeLine = !RoslynString.IsNullOrWhiteSpace(code); - if (!weHaveFilePathAndCodeLine) - { - return null; - } - - string file = match.Groups["file"].Value; - if (RoslynString.IsNullOrWhiteSpace(file) || !File.Exists(file)) - { - return null; - } - - int line = int.TryParse(match.Groups["line"].Value, out int value) ? value : 0; - - return (code, file, line); - } -} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/PublicAPI/PublicAPI.Unshipped.txt deleted file mode 100644 index eabd9f205a..0000000000 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/PublicAPI/PublicAPI.Unshipped.txt +++ /dev/null @@ -1,5 +0,0 @@ -#nullable enable -Microsoft.Testing.Extensions.AzureDevOps.TestingPlatformBuilderHook -Microsoft.Testing.Extensions.AzureDevOpsExtensions -static Microsoft.Testing.Extensions.AzureDevOps.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void -static Microsoft.Testing.Extensions.AzureDevOpsExtensions.AddAzureDevOpsProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzDoEscaper.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzDoEscaper.cs similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzDoEscaper.cs rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzDoEscaper.cs diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsCommandLineOptions.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineOptions.cs similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsCommandLineOptions.cs rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineOptions.cs diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineProvider.cs similarity index 97% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsCommandLineProvider.cs rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineProvider.cs index 3a6fc76e97..53606711b9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsCommandLineProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsCommandLineProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Extensions.AzureDevOps.Resources; +using Microsoft.Testing.Extensions.AzureDevOpsReport.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs similarity index 85% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsExtensions.cs rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs index 696ee37572..728a257520 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/AzureDevOpsExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsExtensions.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.Testing.Extensions.AzureDevOpsReport; using Microsoft.Testing.Extensions.Reporting; -using Microsoft.Testing.Extensions.TrxReport.Abstractions; using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Services; @@ -25,7 +25,9 @@ public static void AddAzureDevOpsProvider(this ITestApplicationBuilder builder) new AzureDevOpsReporter( serviceProvider.GetCommandLineOptions(), serviceProvider.GetEnvironment(), - serviceProvider.GetOutputDevice())); + serviceProvider.GetFileSystem(), + serviceProvider.GetOutputDevice(), + serviceProvider.GetLoggerFactory())); builder.TestHost.AddDataConsumer(compositeTestSessionAzDoService); diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs new file mode 100644 index 0000000000..9e7093ba63 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsReporter.cs @@ -0,0 +1,335 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions.AzureDevOpsReport.Resources; +using Microsoft.Testing.Extensions.Reporting; +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.OutputDevice; + +namespace Microsoft.Testing.Extensions.AzureDevOpsReport; + +internal sealed class AzureDevOpsReporter : + IDataConsumer, + IDataProducer, + IOutputDeviceDataProducer +{ + private const string DeterministicBuildRoot = "/_/"; + + private readonly IOutputDevice _outputDisplay; + private readonly ILogger _logger; + private static readonly char[] NewlineCharacters = new char[] { '\r', '\n' }; + private readonly ICommandLineOptions _commandLine; + private readonly IEnvironment _environment; + private readonly IFileSystem _fileSystem; + private string _severity = "error"; + + public AzureDevOpsReporter( + ICommandLineOptions commandLine, + IEnvironment environment, + IFileSystem fileSystem, + IOutputDevice outputDisplay, + ILoggerFactory loggerFactory) + { + _commandLine = commandLine; + _environment = environment; + _fileSystem = fileSystem; + _outputDisplay = outputDisplay; + _logger = loggerFactory.CreateLogger(); + } + + public Type[] DataTypesConsumed { get; } = + [ + typeof(TestNodeUpdateMessage) + ]; + + public Type[] DataTypesProduced { get; } = [typeof(SessionFileArtifact)]; + + /// + public string Uid { get; } = nameof(AzureDevOpsReporter); + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } = AzureDevOpsResources.DisplayName; + + /// + public string Description { get; } = AzureDevOpsResources.Description; + + /// + public Task IsEnabledAsync() + { + bool isEnabledByParameter = _commandLine.IsOptionSet(AzureDevOpsCommandLineOptions.AzureDevOpsOptionName); + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace($"{nameof(AzureDevOpsReport)} is {(isEnabledByParameter ? "enabled" : "disabled")}."); + } + + if (!isEnabledByParameter) + { + return Task.FromResult(false); + } + + bool isEnabledByEnvVariable = string.Equals(_environment.GetEnvironmentVariable("TF_BUILD"), "true", StringComparison.OrdinalIgnoreCase); + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace($"TF_BUILD environment variable is {(isEnabledByEnvVariable ? "enabled. Will report errors to Azure DevOps, because we are running in CI." : "disabled. Will not report errors to Azure DevOps.")}."); + } + + if (!isEnabledByEnvVariable) + { + return Task.FromResult(false); + } + + bool found = _commandLine.TryGetOptionArgumentList(AzureDevOpsCommandLineOptions.AzureDevOpsReportSeverity, out string[]? arguments); + if (found && arguments?.Length > 0) + { + _severity = arguments[0].ToLowerInvariant(); + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace($"Severity is set to '{_severity}', by --report-azdo-severity parameter."); + } + } + else + { + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace($"Severity is set to '{_severity}', you can override it by using --report-azdo-severity parameter."); + } + } + + return Task.FromResult(true); + } + + public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + if (value is not TestNodeUpdateMessage nodeUpdateMessage) + { + return; + } + + TestNodeStateProperty? nodeState = nodeUpdateMessage.TestNode.Properties.SingleOrDefault(); + + switch (nodeState) + { + case FailedTestNodeStateProperty failed: + await WriteExceptionAsync(failed.Explanation, failed.Exception); + break; + case ErrorTestNodeStateProperty error: + await WriteExceptionAsync(error.Explanation, error.Exception); + break; + case CancelledTestNodeStateProperty cancelled: + await WriteExceptionAsync(cancelled.Explanation, cancelled.Exception); + break; + case TimeoutTestNodeStateProperty timeout: + await WriteExceptionAsync(timeout.Explanation, timeout.Exception); + break; + } + } + + private async Task WriteExceptionAsync(string? explanation, Exception? exception) + { + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace("Failure received."); + } + + string? line = GetErrorText(explanation, exception, _severity, _fileSystem, _logger); + if (line == null) + { + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace("Failure message is null, returning."); + } + + return; + } + + if (_logger.IsEnabled(LogLevel.Trace)) + { + _logger.LogTrace($"Showing failure message '{line}'."); + } + + await _outputDisplay.DisplayAsync(this, new FormattedTextOutputDeviceData(line)); + } + + internal static /* for testing */ string? GetErrorText(string? explanation, Exception? exception, string severity, IFileSystem fileSystem, ILogger logger) + { + if (exception == null || exception.StackTrace == null) + { + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("Exception or stack trace were null, returning."); + } + + return null; + } + + string message = explanation ?? exception.Message; + + if (message == null) + { + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("Explanation and exception message were null, returning."); + } + + return null; + } + + string repoRoot = RootFinder.Find(); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Found repo root '{repoRoot}'"); + } + + string stackTrace = exception.StackTrace; + foreach (string? stackFrame in stackTrace.Split(NewlineCharacters, StringSplitOptions.RemoveEmptyEntries)) + { + (string Code, string File, int LineNumber)? location = GetStackFrameLocation(stackFrame); + if (location == null) + { + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("StackFrame location was null, continuing to next."); + } + + continue; + } + + string file = location.Value.File; + + // TODO: We need better rule for stackframes to opt out from being interesting. + if (file.EndsWith("Assert.cs", StringComparison.Ordinal)) + { + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("StackFrame location ends with 'Assert.cs' this is a special pattern that we skip, continuing to next."); + } + + continue; + } + + // Deterministic build paths start with "/_/" + string relativePath; + if (file.StartsWith(DeterministicBuildRoot, StringComparison.OrdinalIgnoreCase)) + { + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Path '{file}' is coming from deterministic build."); + } + + relativePath = file.Substring(DeterministicBuildRoot.Length); + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Using relative path '{relativePath}'."); + } + } + else if (file.StartsWith(repoRoot, StringComparison.CurrentCultureIgnoreCase)) + { + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Path '{file}' is in current repo '{repoRoot}'."); + } + + relativePath = file.Substring(repoRoot.Length); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Using relative path '{relativePath}'."); + } + } + else + { + // Path does not belong to current repo, keep it null. + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Path '{file}' does not belong to current repo '{repoRoot}'. Continue to next."); + } + + continue; + } + + // Combine with repo root, to be able to resolve deterministic build paths. + string fullPath = Path.Combine(repoRoot, relativePath); + if (!fileSystem.Exists(fullPath)) + { + // Path does not belong to current repository or does not exist, no need to report it because it will not show up in the PR error, we will only see it details of the run, which is the same + // as not reporting it this way. Maybe there can be 2 modes, but right now we want this to be usable for GitHub + AzDo, not for pure AzDo. + // + // In case of deterministic build, all the paths will be relative, so if library carries symbols and matches our path we would see the error as coming from our file + // even though it would not. That change is slim and something we have to live with. + // + // Deterministic build will also have paths normalized to /, luckily File.Exist does not care about the slash direction (on Windows). + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Path '{fullPath}' does not exist on disk. Continue to next."); + } + + continue; + } + + // The slashes must be / for GitHub to render the error placement correctly. + string relativeNormalizedPath = relativePath.Replace('\\', '/'); + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Normalized path for GitHub '{relativeNormalizedPath}'."); + } + + string err = AzDoEscaper.Escape(message); + + string line = $"##vso[task.logissue type={severity};sourcepath={relativeNormalizedPath};linenumber={location.Value.LineNumber};columnnumber=1]{err}"; + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"Reported full message '{line}'."); + } + + // Report the error only for the first stack frame that is useful. + return line; + } + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace($"No stack trace line matched criteria, no failure line was reported."); + } + + return null; + } + + private static (string Code, string File, int LineNumber)? GetStackFrameLocation(string stackTraceLine) + { + Match match = StackTraceHelper.GetFrameRegex().Match(stackTraceLine); + if (!match.Success) + { + return null; + } + + string code = match.Groups["code"].Value; + bool weHaveFilePathAndCodeLine = !RoslynString.IsNullOrWhiteSpace(code); + if (!weHaveFilePathAndCodeLine) + { + return null; + } + + string file = match.Groups["file"].Value; + if (RoslynString.IsNullOrWhiteSpace(file)) + { + return null; + } + + int line = int.TryParse(match.Groups["line"].Value, out int value) ? value : 0; + + return (code, file, line); + } +} diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/BannedSymbols.txt b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/BannedSymbols.txt similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/BannedSymbols.txt rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/BannedSymbols.txt diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Microsoft.Testing.Extensions.AzureDevOps.csproj b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj similarity index 93% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Microsoft.Testing.Extensions.AzureDevOps.csproj rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj index 342819aa1b..c509126613 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Microsoft.Testing.Extensions.AzureDevOps.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Microsoft.Testing.Extensions.AzureDevOpsReport.csproj @@ -46,4 +46,8 @@ This package extends Microsoft Testing Platform to provide a Azure DevOps report + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/PACKAGE.md b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PACKAGE.md similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/PACKAGE.md rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PACKAGE.md diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Shipped.txt similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/PublicAPI/PublicAPI.Shipped.txt rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Shipped.txt diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..2ad77b868e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1,5 @@ +#nullable enable +Microsoft.Testing.Extensions.AzureDevOpsReport.TestingPlatformBuilderHook +Microsoft.Testing.Extensions.AzureDevOpsExtensions +static Microsoft.Testing.Extensions.AzureDevOpsReport.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void +static Microsoft.Testing.Extensions.AzureDevOpsExtensions.AddAzureDevOpsProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/AzureDevOpsResources.resx b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/AzureDevOpsResources.resx rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/AzureDevOpsResources.resx diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.cs.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.cs.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.de.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.de.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.es.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.es.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.fr.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.fr.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.it.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.it.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.ja.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ja.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.ko.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ko.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.pl.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pl.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.pt-BR.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.pt-BR.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.ru.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.ru.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.tr.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.tr.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hans.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf similarity index 100% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/Resources/xlf/AzureDevOpsResources.zh-Hant.xlf diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/RootFinder.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/RootFinder.cs similarity index 94% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/RootFinder.cs rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/RootFinder.cs index c51612f974..aa4cf86d09 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/RootFinder.cs +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/RootFinder.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Microsoft.Testing.Extensions.AzureDevOps; +namespace Microsoft.Testing.Extensions.AzureDevOpsReport; internal static class RootFinder { diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TestingPlatformBuilderHook.cs similarity index 93% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/TestingPlatformBuilderHook.cs rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TestingPlatformBuilderHook.cs index 930066693a..67cde2786d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/TestingPlatformBuilderHook.cs +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/TestingPlatformBuilderHook.cs @@ -3,7 +3,7 @@ using Microsoft.Testing.Platform.Builder; -namespace Microsoft.Testing.Extensions.AzureDevOps; +namespace Microsoft.Testing.Extensions.AzureDevOpsReport; /// /// This class is used by Microsoft.Testing.Platform.MSBuild to hook into the Testing Platform Builder to add Azure DevOps reporting support. diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/buildTransitive/Microsoft.Testing.Extensions.AzureDevOps.props b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/build/Microsoft.Testing.Extensions.AzureDevOpsReport.props similarity index 57% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/buildTransitive/Microsoft.Testing.Extensions.AzureDevOps.props rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/build/Microsoft.Testing.Extensions.AzureDevOpsReport.props index 57f9f713e2..30558c709a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/buildTransitive/Microsoft.Testing.Extensions.AzureDevOps.props +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/build/Microsoft.Testing.Extensions.AzureDevOpsReport.props @@ -1,3 +1,3 @@  - + diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/buildMultiTargeting/Microsoft.Testing.Extensions.AzureDevOps.props b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/buildMultiTargeting/Microsoft.Testing.Extensions.AzureDevOpsReport.props similarity index 72% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/buildMultiTargeting/Microsoft.Testing.Extensions.AzureDevOps.props rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/buildMultiTargeting/Microsoft.Testing.Extensions.AzureDevOpsReport.props index 9cd86af126..17d9111a29 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/buildMultiTargeting/Microsoft.Testing.Extensions.AzureDevOps.props +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/buildMultiTargeting/Microsoft.Testing.Extensions.AzureDevOpsReport.props @@ -7,8 +7,8 @@ WE HAVE CODE INSIDE THE TASK 'TestingPlatformEntryPoint' TO ENSURE THE ORDER OF THE REGISTRATION BASED ON THIS GUID --> - Microsoft.Testing.Extensions.AzureDevOps - Microsoft.Testing.Extensions.AzureDevOps.TestingPlatformBuilderHook + Microsoft.Testing.Extensions.AzureDevOpsReport + Microsoft.Testing.Extensions.AzureDevOpsReport.TestingPlatformBuilderHook diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/build/Microsoft.Testing.Extensions.AzureDevOps.props b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/buildTransitive/Microsoft.Testing.Extensions.AzureDevOpsReport.props similarity index 57% rename from src/Platform/Microsoft.Testing.Extensions.AzureDevOps/build/Microsoft.Testing.Extensions.AzureDevOps.props rename to src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/buildTransitive/Microsoft.Testing.Extensions.AzureDevOpsReport.props index 57f9f713e2..30558c709a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOps/build/Microsoft.Testing.Extensions.AzureDevOps.props +++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/buildTransitive/Microsoft.Testing.Extensions.AzureDevOpsReport.props @@ -1,3 +1,3 @@  - + diff --git a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj index bd28670a15..c77b8f1fb9 100644 --- a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj +++ b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj @@ -37,7 +37,7 @@ This package provides the core platform and the .NET implementation of the proto - + diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets index 4c84c3db2d..2980dfd99e 100644 --- a/test/Directory.Build.targets +++ b/test/Directory.Build.targets @@ -26,7 +26,7 @@ $(ArtifactsTestResultsDir)$(_ResultFileNameNoExt).trx <_TestResultTrxFileName>$([System.IO.Path]::GetFileName('$(ResultsTrxPath)')) <_TestResultDirectory>$([System.IO.Path]::GetDirectoryName('$(ResultsTrxPath)')) - $(TestingPlatformCommandLineArguments) --report-trx --report-trx-filename "$(_TestResultTrxFileName)" --results-directory "$(_TestResultDirectory)" $(TestRunnerAdditionalArguments) + $(TestingPlatformCommandLineArguments) --report-trx --report-trx-filename "$(_TestResultTrxFileName)" --results-directory "$(_TestResultDirectory)" --report-azdo $(TestRunnerAdditionalArguments) @@ -37,6 +37,7 @@ + diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs index 116b5bef8d..7c4a16519f 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs @@ -21,6 +21,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Program.cs b/test/IntegrationTests/MSTest.IntegrationTests/Program.cs index d79e30de40..ce5734f730 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Program.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Program.cs @@ -14,6 +14,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddRetryProvider(); +builder.AddAzureDevOpsProvider(); builder.AddInternalTestFramework(); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs index d79e30de40..ce5734f730 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Program.cs @@ -14,6 +14,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddRetryProvider(); +builder.AddAzureDevOpsProvider(); builder.AddInternalTestFramework(); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs index 5463488c7f..54c9e40407 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs @@ -22,6 +22,7 @@ builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); builder.AddRetryProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/Program.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/Program.cs index d79e30de40..ce5734f730 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/Program.cs +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/Program.cs @@ -14,6 +14,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddRetryProvider(); +builder.AddAzureDevOpsProvider(); builder.AddInternalTestFramework(); diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs index 6ad08378a2..92aed8206f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs @@ -16,6 +16,7 @@ builder.AddCrashDumpProvider(); builder.AddHangDumpProvider(); builder.AddTrxReportProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory = new(_ => new SlowestTestsConsumer()); diff --git a/test/UnitTests/MSTest.Engine.UnitTests/Program.cs b/test/UnitTests/MSTest.Engine.UnitTests/Program.cs index 79206b730e..cdb391dc1c 100644 --- a/test/UnitTests/MSTest.Engine.UnitTests/Program.cs +++ b/test/UnitTests/MSTest.Engine.UnitTests/Program.cs @@ -18,6 +18,7 @@ builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); builder.AddAppInsightsTelemetryProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory diff --git a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Program.cs b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Program.cs index 79206b730e..cdb391dc1c 100644 --- a/test/UnitTests/MSTest.SourceGeneration.UnitTests/Program.cs +++ b/test/UnitTests/MSTest.SourceGeneration.UnitTests/Program.cs @@ -18,6 +18,7 @@ builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); builder.AddAppInsightsTelemetryProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Program.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Program.cs index d79e30de40..ce5734f730 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Program.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Program.cs @@ -14,6 +14,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddRetryProvider(); +builder.AddAzureDevOpsProvider(); builder.AddInternalTestFramework(); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Program.cs b/test/UnitTests/MSTestAdapter.UnitTests/Program.cs index d79e30de40..ce5734f730 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Program.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Program.cs @@ -14,6 +14,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddRetryProvider(); +builder.AddAzureDevOpsProvider(); builder.AddInternalTestFramework(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsTests.cs new file mode 100644 index 0000000000..4d7c7f5314 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsTests.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !NETFRAMEWORK || NET472_OR_GREATER +using Microsoft.Testing.Extensions.AzureDevOpsReport; +using Microsoft.Testing.Platform.Helpers; +#endif +using Microsoft.Testing.Platform.Logging; + +namespace Microsoft.Testing.Extensions.UnitTests; + +[TestClass] +public sealed class AzureDevOpsTests +{ + [TestMethod] + public void ReportsTheFirstExistingFileInStackTraceWithTheRightLineNumberAndEscaping() + { +#if NETFRAMEWORK && !NET472_OR_GREATER + // We rely on code file paths that are present in pdb for this project. We use portable PDBs, which don't report the code location for + // .NET Framework <4.7.2, so we won't get the path and the test will fail. + // https://learn.microsoft.com/en-us/dotnet/core/diagnostics/symbols#support-for-portable-pdbs + return; +#else + Exception error; + try + { + throw new Exception("this is an error\nwith\rnewline"); + } + catch (Exception ex) + { + error = ex; + } + + // Trim ## so when the test fails we don't report it to AzDO, the severity is invalid, and the result is confusing. + var logger = new TextLogger(); + string? text = AzureDevOpsReporter.GetErrorText(null, error, "severity", new SystemFileSystem(), logger)?.Trim('#'); + Assert.AreEqual("vso[task.logissue type=severity;sourcepath=test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AzureDevOpsTests.cs;linenumber=27;columnnumber=1]this is an error%0Awith%0Dnewline", text, $"\nLogs:\n{string.Join("\n", logger.Logs)}"); +#endif + } + + private class TextLogger : ILogger + { + public List Logs { get; } = []; + + public bool IsEnabled(LogLevel logLevel) + => true; + + public void Log(LogLevel logLevel, TState state, Exception? exception, Func formatter) + => Logs.Add($"{logLevel.ToString().ToUpperInvariant()}: {formatter(state, exception)}"); + + public Task LogAsync(LogLevel logLevel, TState state, Exception? exception, Func formatter) + { + Logs.Add($"{logLevel.ToString().ToUpperInvariant()}: {formatter(state, exception)}"); + return Task.CompletedTask; + } + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj index 9674f0054d..35b1ea3b4a 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj @@ -2,7 +2,11 @@ $(MicrosoftTestingTargetFrameworks) - $(TargetFrameworks);net462 + + $(TargetFrameworks);net462;net472 true Exe @@ -17,6 +21,9 @@ TargetFramework=netstandard2.0 + + TargetFramework=netstandard2.0 + diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs index c731857d74..4b97c8b8e6 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs @@ -18,6 +18,7 @@ builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); builder.AddAppInsightsTelemetryProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs index 5af7b8a583..ea6f07edb4 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs @@ -29,6 +29,7 @@ builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs index c05ca317bd..9c5f7d5a7f 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs @@ -15,6 +15,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddTrxReportProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs index f8a7bf63f0..54292c954f 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs @@ -22,6 +22,7 @@ builder.AddCrashDumpProvider(); builder.AddHangDumpProvider(); builder.AddTrxReportProvider(); +builder.AddAzureDevOpsProvider(); // Custom suite tools CompositeExtensionFactory slowestTestCompositeServiceFactory = new(_ => new SlowestTestsConsumer()); diff --git a/test/UnitTests/TestFramework.UnitTests/Program.cs b/test/UnitTests/TestFramework.UnitTests/Program.cs index d79e30de40..ce5734f730 100644 --- a/test/UnitTests/TestFramework.UnitTests/Program.cs +++ b/test/UnitTests/TestFramework.UnitTests/Program.cs @@ -14,6 +14,7 @@ builder.AddHangDumpProvider(); builder.AddCrashDumpProvider(ignoreIfNotSupported: true); builder.AddRetryProvider(); +builder.AddAzureDevOpsProvider(); builder.AddInternalTestFramework();