diff --git a/.editorconfig b/.editorconfig index e323c80086..fd9640b60c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -132,7 +132,7 @@ dotnet_diagnostic.IDE0020.severity = warning # IDE0020: IDE0038: Use dotnet_diagnostic.IDE0028.severity = warning # IDE0028: Simplify collection initialization dotnet_diagnostic.IDE0031.severity = warning # IDE0031: Use null propagation dotnet_style_null_propagation = true:warning # IDE0031: Use null propagation -dotnet_diagnostic.IDE0032.severity = warning # IDE0032: Use auto-implemented property +dotnet_diagnostic.IDE0032.severity = none # IDE0032: Use auto-implemented property dotnet_diagnostic.IDE0034.severity = warning # IDE0034: Simplify 'default' expression dotnet_diagnostic.IDE0035.severity = warning # IDE0035: Remove unreachable code dotnet_diagnostic.IDE0036.severity = warning # IDE0036: Order modifiers diff --git a/Directory.Packages.props b/Directory.Packages.props index 299e8fb283..4b903f1fca 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ true - 8.1.0 + 8.2.2 17.10.0-preview-24127-03 3.11.0-beta1.24318.1 3.11.0 @@ -13,8 +13,8 @@ 6.2.14 - 17.11.0 - 1.45.1 + 17.11.1 + 1.49.0 4.3.1 4.3.1 @@ -58,6 +58,8 @@ + + @@ -68,7 +70,7 @@ - + diff --git a/NuGet.config b/NuGet.config index b5ce5f5511..604a77d5c4 100644 --- a/NuGet.config +++ b/NuGet.config @@ -5,6 +5,11 @@ + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ec849267ca..dd4e180899 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,17 +13,17 @@ https://github.com/dotnet/arcade d7f6456959c71512dd68e03021e0aadd8e27d025 - + https://dev.azure.com/devdiv/DevDiv/_git/vs-code-coverage - 80038c362167aceeeeaed68854d8328be9bebc2a + 1406857e7a6df8189656aeb9ae2a75bfc402c65f - + https://github.com/microsoft/testanywhere - d5261d64cc99fd929f1ad7ba93de50af7b8e4608 + b6b5c94527b35464e9ffaf471aef378f76888f76 - + https://github.com/microsoft/testanywhere - d5261d64cc99fd929f1ad7ba93de50af7b8e4608 + b6b5c94527b35464e9ffaf471aef378f76888f76 diff --git a/eng/Versions.props b/eng/Versions.props index 63686d20c5..0135a7e88f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,16 +1,16 @@ - 3.6.0 + 3.6.4 - 1.4.0 + 1.4.3 preview 10.0.0-beta.24429.6 - 17.12.4 + 17.13.1 - 1.4.0 - 1.0.0-alpha.24460.4 + 1.4.3 + 1.0.0-alpha.24562.1 diff --git a/eng/verify-nupkgs.ps1 b/eng/verify-nupkgs.ps1 index 20c72088ff..d119680355 100644 --- a/eng/verify-nupkgs.ps1 +++ b/eng/verify-nupkgs.ps1 @@ -22,7 +22,7 @@ function Confirm-NugetPackages { "MSTest.Sdk" = 15; "MSTest.Internal.TestFx.Documentation" = 10; "MSTest.TestFramework" = 130; - "MSTest.TestAdapter" = 167; + "MSTest.TestAdapter" = 76; "MSTest" = 6; "MSTest.Analyzers" = 10; } diff --git a/global.json b/global.json index b49cd4628a..aa3eb167a6 100644 --- a/global.json +++ b/global.json @@ -4,15 +4,15 @@ "runtimes": { "dotnet": [ "3.1.32", - "6.0.33", + "6.0.36", "7.0.20", - "8.0.8" + "8.0.11" ], "dotnet/x86": [ - "8.0.8" + "8.0.11" ], "aspnetcore": [ - "8.0.8" + "8.0.11" ] }, "vs": { diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs index eebf71446c..7faaabe606 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs @@ -1,6 +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 System.Collections.Concurrent; + namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// @@ -10,8 +12,9 @@ public class TestRunCancellationToken { /// /// Callbacks to be invoked when canceled. + /// Needs to be a concurrent collection, see https://github.com/microsoft/testfx/issues/3953. /// - private readonly List _registeredCallbacks = new(); + private readonly ConcurrentBag _registeredCallbacks = new(); /// /// Stores whether the test run is canceled or not. @@ -63,7 +66,17 @@ private set /// /// Unregister the callback method. /// - public void Unregister() => _registeredCallbacks.Clear(); + public void Unregister() +#if NETCOREAPP || WINDOWS_UWP + => _registeredCallbacks.Clear(); +#else + { + while (!_registeredCallbacks.IsEmpty) + { + _ = _registeredCallbacks.TryTake(out _); + } + } +#endif internal void ThrowIfCancellationRequested() { diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec index b09aba1d05..b9842b3993 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec +++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec @@ -93,8 +93,8 @@ - - + + diff --git a/src/Adapter/MSTest.TestAdapter/build/common/MSTest.TestAdapter.props b/src/Adapter/MSTest.TestAdapter/build/common/MSTest.TestAdapter.props index aa75379c85..5d661becfe 100644 --- a/src/Adapter/MSTest.TestAdapter/build/common/MSTest.TestAdapter.props +++ b/src/Adapter/MSTest.TestAdapter/build/common/MSTest.TestAdapter.props @@ -12,7 +12,7 @@ DO NOT CHANGE THE GUID, IT'S A WELL KNOWN EXTENSION POINT AND THIS EXTENSION NEEDS TO BE REGISTERED AT THE END WE HAVE CODE INSIDE THE TASK 'TestingPlatformEntryPoint' TO ENSURE THE ORDER OF THE REGISTRATION BASED ON THIS GUID --> - + MSTest Microsoft.VisualStudio.TestTools.UnitTesting.TestingPlatformBuilderHook diff --git a/src/Adapter/MSTest.TestAdapter/build/net/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/build/net/MSTest.TestAdapter.targets index 05548ac62e..f3ef87d399 100644 --- a/src/Adapter/MSTest.TestAdapter/build/net/MSTest.TestAdapter.targets +++ b/src/Adapter/MSTest.TestAdapter/build/net/MSTest.TestAdapter.targets @@ -73,18 +73,18 @@ - - %(CurrentUICultureHierarchy.Identity) - + - + + %(CurrentUICultureHierarchy.Identity) + - - $(CurrentUICultureHierarchy)\%(FileName).resources.dll + + + %(MSTestV2Files.UICulture)\%(FileName).dll PreserveNewest %(FullPath) False diff --git a/src/Adapter/MSTest.TestAdapter/build/netfx-netcore-netstandard/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/build/netfx-netcore-netstandard/MSTest.TestAdapter.targets index 106ac5a5da..64fb79bf30 100644 --- a/src/Adapter/MSTest.TestAdapter/build/netfx-netcore-netstandard/MSTest.TestAdapter.targets +++ b/src/Adapter/MSTest.TestAdapter/build/netfx-netcore-netstandard/MSTest.TestAdapter.targets @@ -66,14 +66,20 @@ + + - - %(CurrentUICultureHierarchy.Identity) - + + %(CurrentUICultureHierarchy.Identity) + + - - %(MSTestV2ResourceFiles.CultureString)\%(Filename)%(Extension) + + + + %(MSTestV2Files.UICulture)\%(FileName).dll PreserveNewest + %(FullPath) False diff --git a/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets index d8baf21f26..c33b561416 100644 --- a/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets +++ b/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets @@ -29,19 +29,20 @@ - - %(CurrentUICultureHierarchy.Identity) - - + - - + + %(CurrentUICultureHierarchy.Identity) + + + %(CurrentUICultureHierarchy.Identity) + - - $(CurrentUICultureHierarchy)\%(FileName).resources.dll + + + %(MSTestV2Files.UICulture)\%(FileName).dll PreserveNewest %(FullPath) False diff --git a/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs b/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs index 06d9a359a4..53a2a392b1 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs @@ -376,7 +376,15 @@ protected virtual // the ResourceHelper's currentlyLoading stack to null if an exception occurs. if (s_currentlyLoading != null && s_currentlyLoading.Count > 0 && s_currentlyLoading.LastIndexOf(assemblyPath) != -1) { - EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Assembly '{0}' is searching for itself recursively '{1}', returning as not found.", name, assemblyPath); + SafeLog( + name, + () => + { + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("MSTest.AssemblyResolver.OnResolve: Assembly '{0}' is searching for itself recursively '{1}', returning as not found.", name, assemblyPath); + } + }); _resolvedAssemblies[name] = null; return null; } diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md index 98a82d5743..5ce49d10bb 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md @@ -1,4 +1,15 @@ -## Release 3.5.0 +## Release 3.6.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +MSTEST0018 | Usage | Warning | DynamicDataShouldBeValidAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0018) +MSTEST0034 | Usage | Info | UseClassCleanupBehaviorEndOfClassAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0034) +MSTEST0035 | Usage | Info | UseDeploymentItemWithTestMethodOrTestClassAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0035) +MSTEST0036 | Design | Warning | DoNotUseShadowingAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0036) + +## Release 3.5.0 ### New Rules diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index d028872578..6eea619003 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md @@ -1,10 +1,2 @@ ; Unshipped analyzer release ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md -### New Rules - -Rule ID | Category | Severity | Notes ---------|----------|----------|------- -MSTEST0018 | Usage | Warning | DynamicDataShouldBeValidAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0018) -MSTEST0034 | Usage | Info | UseClassCleanupBehaviorEndOfClassAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0034) -MSTEST0035 | Usage | Info | UseDeploymentItemWithTestMethodOrTestClassAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0035) -MSTEST0036 | Design | Warning | DoNotUseShadowingAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0036) diff --git a/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs index 5fba21395b..abbefd8916 100644 --- a/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DoNotUseShadowingAnalyzer.cs @@ -112,7 +112,8 @@ private static bool IsMemberShadowing(ISymbol member, ISymbol baseMember) } // Compare methods - if (member is IMethodSymbol methodSymbol && baseMember is IMethodSymbol baseMethodSymbol) + if (member is IMethodSymbol methodSymbol && baseMember is IMethodSymbol baseMethodSymbol && methodSymbol.IsGenericMethod == baseMethodSymbol.IsGenericMethod + && !(methodSymbol.DeclaredAccessibility == Accessibility.Private && baseMethodSymbol.DeclaredAccessibility == Accessibility.Private)) { return methodSymbol.Name == baseMethodSymbol.Name && methodSymbol.Parameters.Length == baseMethodSymbol.Parameters.Length && diff --git a/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs index ec3599737a..c92572ebd9 100644 --- a/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs @@ -12,6 +12,9 @@ namespace MSTest.Analyzers; +/// +/// MSTEST0018: . +/// [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class DynamicDataShouldBeValidAnalyzer : DiagnosticAnalyzer { @@ -202,8 +205,7 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa return; } - if (!member.IsStatic - || member.DeclaredAccessibility != Accessibility.Public) + if (!member.IsStatic) { context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(DataMemberSignatureRule, declaringType.Name, memberName)); return; @@ -218,23 +220,29 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa } // Validate member return type. - if (member.GetMemberType() is not INamedTypeSymbol memberType) + ITypeSymbol? memberTypeSymbol = member.GetMemberType(); + if (memberTypeSymbol is INamedTypeSymbol memberNamedType) { - return; - } + if (!SymbolEqualityComparer.Default.Equals(memberNamedType.ConstructedFrom, ienumerableTypeSymbol) + || memberNamedType.TypeArguments.Length != 1) + { + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); + return; + } - if (!SymbolEqualityComparer.Default.Equals(memberType.ConstructedFrom, ienumerableTypeSymbol) - || memberType.TypeArguments.Length != 1) - { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); - return; + ITypeSymbol collectionBoundType = memberNamedType.TypeArguments[0]; + if (!collectionBoundType.Inherits(itupleTypeSymbol) + && collectionBoundType is not IArrayTypeSymbol) + { + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); + } } - - ITypeSymbol collectionBoundType = memberType.TypeArguments[0]; - if (!collectionBoundType.Inherits(itupleTypeSymbol) - && (collectionBoundType is not IArrayTypeSymbol arrayTypeSymbol || arrayTypeSymbol.ElementType.SpecialType != SpecialType.System_Object)) + else if (memberTypeSymbol is IArrayTypeSymbol arrayType) { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); + if (arrayType.ElementType is not IArrayTypeSymbol) + { + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); + } } } diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt index 4d8fce9705..681365b958 100644 --- a/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt +++ b/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt @@ -23,16 +23,20 @@ MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer.DoNotNegateBooleanAssertionAnalyzer() -> void MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer.DoNotStoreStaticTestContextAnalyzer() -> void +MSTest.Analyzers.DoNotUseShadowingAnalyzer +MSTest.Analyzers.DoNotUseShadowingAnalyzer.DoNotUseShadowingAnalyzer() -> void MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer.DoNotUseSystemDescriptionAttributeAnalyzer() -> void +MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer +MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.DynamicDataShouldBeValidAnalyzer() -> void +MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor +MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.NonNullableReferenceNotInitializedSuppressor() -> void MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer.PreferAssertFailOverAlwaysFalseConditionsAnalyzer() -> void MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer.PreferConstructorOverTestInitializeAnalyzer() -> void MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer.PreferDisposeOverTestCleanupAnalyzer() -> void -MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer -MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.ReviewAlwaysTrueAssertConditionAnalyzer() -> void MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer.PreferTestCleanupOverDisposeAnalyzer() -> void MSTest.Analyzers.PreferTestInitializeOverConstructorAnalyzer @@ -41,6 +45,8 @@ MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer.PublicMethodShouldBeTestMethodAnalyzer() -> void MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer.PublicTypeShouldBeTestClassAnalyzer() -> void +MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer +MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.ReviewAlwaysTrueAssertConditionAnalyzer() -> void MSTest.Analyzers.TestClassShouldBeValidAnalyzer MSTest.Analyzers.TestClassShouldBeValidAnalyzer.TestClassShouldBeValidAnalyzer() -> void MSTest.Analyzers.TestClassShouldHaveTestMethodAnalyzer @@ -63,6 +69,10 @@ MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor.UseAsyncSuffixTestMethodSuppressor() -> void MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer.UseAttributeOnTestMethodAnalyzer() -> void +MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer +MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.UseClassCleanupBehaviorEndOfClassAnalyzer() -> void +MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer +MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.UseDeploymentItemWithTestMethodOrTestClassAnalyzer() -> void MSTest.Analyzers.UseParallelizeAttributeAnalyzer MSTest.Analyzers.UseParallelizeAttributeAnalyzer.UseParallelizeAttributeAnalyzer() -> void override MSTest.Analyzers.AssemblyCleanupShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void @@ -85,16 +95,20 @@ override MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer.Initialize(Microso override MSTest.Analyzers.DoNotNegateBooleanAssertionAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.DoNotStoreStaticTestContextAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.DoNotUseShadowingAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.DoNotUseShadowingAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.DoNotUseSystemDescriptionAttributeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.ReportSuppressions(Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext context) -> void +override MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.SupportedSuppressions.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.PreferAssertFailOverAlwaysFalseConditionsAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.PreferDisposeOverTestCleanupAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.PreferTestCleanupOverDisposeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.PreferTestInitializeOverConstructorAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void @@ -103,6 +117,8 @@ override MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer.Initialize(Micr override MSTest.Analyzers.PublicMethodShouldBeTestMethodAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.PublicTypeShouldBeTestClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.ReviewAlwaysTrueAssertConditionAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.TestClassShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.TestClassShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.TestClassShouldHaveTestMethodAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void @@ -125,6 +141,10 @@ override MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor.ReportSuppressions( override MSTest.Analyzers.UseAsyncSuffixTestMethodSuppressor.SupportedSuppressions.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.UseAttributeOnTestMethodAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray override MSTest.Analyzers.UseParallelizeAttributeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.UseParallelizeAttributeAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray static Analyzer.Utilities.WellKnownTypeProvider.GetOrCreate(Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.WellKnownTypeProvider! diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt index ec974803bd..ab058de62d 100644 --- a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt +++ b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt @@ -1,21 +1 @@ #nullable enable -MSTest.Analyzers.DoNotUseShadowingAnalyzer -MSTest.Analyzers.DoNotUseShadowingAnalyzer.DoNotUseShadowingAnalyzer() -> void -MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer -MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.DynamicDataShouldBeValidAnalyzer() -> void -MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor -MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.NonNullableReferenceNotInitializedSuppressor() -> void -MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer -MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.UseClassCleanupBehaviorEndOfClassAnalyzer() -> void -MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer -MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.UseDeploymentItemWithTestMethodOrTestClassAnalyzer() -> void -override MSTest.Analyzers.DoNotUseShadowingAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.DoNotUseShadowingAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.DynamicDataShouldBeValidAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.ReportSuppressions(Microsoft.CodeAnalysis.Diagnostics.SuppressionAnalysisContext context) -> void -override MSTest.Analyzers.NonNullableReferenceNotInitializedSuppressor.SupportedSuppressions.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.UseClassCleanupBehaviorEndOfClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray -override MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void -override MSTest.Analyzers.UseDeploymentItemWithTestMethodOrTestClassAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray diff --git a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs index a35b887759..7855473260 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs +++ b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs @@ -375,7 +375,7 @@ internal static string DoNotUseShadowingDescription { } /// - /// Looks up a localized string similar to Member '{0}' is already exist in the base class. + /// Looks up a localized string similar to Member '{0}' already exists in the base class. /// internal static string DoNotUseShadowingMessageFormat { get { @@ -714,7 +714,7 @@ internal static string TestClassShouldBeValidTitle { } /// - /// Looks up a localized string similar to Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'.. + /// Looks up a localized string similar to Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'.. /// internal static string TestClassShouldHaveTestMethodDescription { get { @@ -723,7 +723,7 @@ internal static string TestClassShouldHaveTestMethodDescription { } /// - /// Looks up a localized string similar to Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. + /// Looks up a localized string similar to Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. /// internal static string TestClassShouldHaveTestMethodMessageFormat { get { diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index 45f62cdbf0..ff98060e2e 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -329,10 +329,10 @@ The type declaring these methods should also respect the following rules: Test classes should have valid layout - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' Test class should have test method @@ -524,7 +524,7 @@ The type declaring these methods should also respect the following rules: Shadowing test members could cause testing issues (such as NRE). - Member '{0}' is already exist in the base class + Member '{0}' already exists in the base class Do not use shadowing diff --git a/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs index f89aa288f8..da57e4d3cc 100644 --- a/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs @@ -77,7 +77,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo bool hasTestMethod = false; INamedTypeSymbol? currentType = namedTypeSymbol; - do + while (currentType is not null && !hasTestMethod) { foreach (ISymbol classMember in currentType.GetMembers()) { @@ -94,16 +94,10 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo break; } } - - if (!hasTestMethod) - { - break; - } } currentType = currentType.BaseType; } - while (currentType is not null && !hasTestMethod); if (!hasTestMethod) { diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index 28e78c38ad..91af9e06a1 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -287,8 +287,8 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: - Member '{0}' is already exist in the base class - Člen {0} už v základní (kořenové) třídě existuje. + Member '{0}' already exists in the base class + Člen {0} už v základní (kořenové) třídě existuje. @@ -509,13 +509,13 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - Testovací třída by měla mít aspoň jednu testovací metodu nebo by měla být static s metodami s označením [AssemblyInitialization] a/nebo [AssemblyCleanup]. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + Testovací třída by měla mít aspoň jednu testovací metodu nebo by měla být static s metodami s označením [AssemblyInitialization] a/nebo [AssemblyCleanup]. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - Testovací třída {0} by měla mít aspoň jednu testovací metodu nebo by měla být static s metodami s označením [AssemblyInitialization] a/nebo [AssemblyCleanup]. + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + Testovací třída {0} by měla mít aspoň jednu testovací metodu nebo by měla být static s metodami s označením [AssemblyInitialization] a/nebo [AssemblyCleanup]. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index c48b85d8d1..202b00cd98 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -289,8 +289,8 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte - Member '{0}' is already exist in the base class - Der Member "{0}" ist bereits in der Basisklasse vorhanden. + Member '{0}' already exists in the base class + Der Member "{0}" ist bereits in der Basisklasse vorhanden. @@ -511,13 +511,13 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - Die Testklasse muss mindestens eine Testmethode aufweisen oder "statisch" sein, wenn Methoden mit "[AssemblyInitialization]" und/oder "[AssemblyCleanup]" markiert sind. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + Die Testklasse muss mindestens eine Testmethode aufweisen oder "statisch" sein, wenn Methoden mit "[AssemblyInitialization]" und/oder "[AssemblyCleanup]" markiert sind. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - Die Testklasse "{0}" muss mindestens eine Testmethode aufweisen oder "statisch" sein, wenn Methoden mit "[AssemblyInitialization]" und/oder "[AssemblyCleanup]" markiert sind. + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + Die Testklasse "{0}" muss mindestens eine Testmethode aufweisen oder "statisch" sein, wenn Methoden mit "[AssemblyInitialization]" und/oder "[AssemblyCleanup]" markiert sind. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index 37085c8208..c5af60c9d1 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -287,8 +287,8 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: - Member '{0}' is already exist in the base class - El miembro ''{0}'' ya existe en la clase base + Member '{0}' already exists in the base class + El miembro ''{0}'' ya existe en la clase base @@ -509,13 +509,13 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - La clase de prueba debe tener al menos un método de prueba o ser 'static' con métodos marcados por '[AssemblyInitialization]' o '[AssemblyCleanup]'. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + La clase de prueba debe tener al menos un método de prueba o ser 'static' con métodos marcados por '[AssemblyInitialization]' o '[AssemblyCleanup]'. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - La clase de prueba '{0}' debe tener al menos un método de prueba o ser 'static' con métodos marcados por '[AssemblyInitialization]' y/o '[AssemblyCleanup]' + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + La clase de prueba '{0}' debe tener al menos un método de prueba o ser 'static' con métodos marcados por '[AssemblyInitialization]' y/o '[AssemblyCleanup]' diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index 05eb6b1d86..2ad341b45e 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -287,8 +287,8 @@ Le type doit être une classe - Member '{0}' is already exist in the base class - Le membre '{0}' existe déjà dans la classe de base + Member '{0}' already exists in the base class + Le membre '{0}' existe déjà dans la classe de base @@ -509,13 +509,13 @@ Le type doit être une classe - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - La classe de test doit avoir au moins une méthode de test ou être « statique » avec la ou les méthodes marquées par « [AssemblyInitialization] » et/ou « [AssemblyCleanup] ». + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + La classe de test doit avoir au moins une méthode de test ou être « statique » avec la ou les méthodes marquées par « [AssemblyInitialization] » et/ou « [AssemblyCleanup] ». - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - La classe de test « {0} » doit avoir au moins une méthode de test ou être « statique » avec des méthodes marquées par « [AssemblyInitialization] » et/ou « [AssemblyCleanup] » + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + La classe de test « {0} » doit avoir au moins une méthode de test ou être « statique » avec des méthodes marquées par « [AssemblyInitialization] » et/ou « [AssemblyCleanup] » diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index deb3238a00..dc5995df7b 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -287,8 +287,8 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: - Member '{0}' is already exist in the base class - Il membro '{0}' esiste già nella classe di base + Member '{0}' already exists in the base class + Il membro '{0}' esiste già nella classe di base @@ -509,13 +509,13 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - La classe di test deve avere almeno un metodo di test o essere 'static' con metodi contrassegnati da '[AssemblyInitialization]' e/o '[AssemblyCleanup]'. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + La classe di test deve avere almeno un metodo di test o essere 'static' con metodi contrassegnati da '[AssemblyInitialization]' e/o '[AssemblyCleanup]'. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - La classe di test '{0}' deve avere almeno un metodo di test o essere 'static' con metodi contrassegnati da '[AssemblyInitialization]' e/o '[AssemblyCleanup]' + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + La classe di test '{0}' deve avere almeno un metodo di test o essere 'static' con metodi contrassegnati da '[AssemblyInitialization]' e/o '[AssemblyCleanup]' diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index 9881713a6d..d747f0efeb 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -287,8 +287,8 @@ The type declaring these methods should also respect the following rules: - Member '{0}' is already exist in the base class - メンバー '{0}' は既に基本クラスに存在します + Member '{0}' already exists in the base class + メンバー '{0}' は既に基本クラスに存在します @@ -509,13 +509,13 @@ The type declaring these methods should also respect the following rules: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - テスト クラスには、少なくとも 1 つのテスト メソッドがあるか、'[AssemblyInitialization]' または '[AssemblyCleanup]' によってマークされたメソッドを含む 'static' である必要があります。 + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + テスト クラスには、少なくとも 1 つのテスト メソッドがあるか、'[AssemblyInitialization]' または '[AssemblyCleanup]' によってマークされたメソッドを含む 'static' である必要があります。 - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - テスト クラス '{0}' には、少なくとも 1 つのテスト メソッドがあるか、'[AssemblyInitialization]' または '[AssemblyCleanup]' によってマークされたメソッドを含む 'static' である必要があります。 + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + テスト クラス '{0}' には、少なくとも 1 つのテスト メソッドがあるか、'[AssemblyInitialization]' または '[AssemblyCleanup]' によってマークされたメソッドを含む 'static' である必要があります。 diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index 9d0aecbdd7..37f0993abc 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -287,8 +287,8 @@ The type declaring these methods should also respect the following rules: - Member '{0}' is already exist in the base class - 멤버 '{0}'이(가) 기본 클래스에 이미 있습니다. + Member '{0}' already exists in the base class + 멤버 '{0}'이(가) 기본 클래스에 이미 있습니다. @@ -509,13 +509,13 @@ The type declaring these methods should also respect the following rules: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - 테스트 클래스에는 하나 이상의 테스트 메서드가 있거나 '[AssemblyInitialization]' 및/또는 '[AssemblyCleanup]'으로 표시된 메서드가 있는 'static'이어야 합니다. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + 테스트 클래스에는 하나 이상의 테스트 메서드가 있거나 '[AssemblyInitialization]' 및/또는 '[AssemblyCleanup]'으로 표시된 메서드가 있는 'static'이어야 합니다. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - 테스트 클래스 '{0}'에 하나 이상의 테스트 메서드가 있거나 '[AssemblyInitialization]' 및/또는 '[AssemblyCleanup]'으로 표시된 메서드가 있는 'static'이어야 합니다. + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + 테스트 클래스 '{0}'에 하나 이상의 테스트 메서드가 있거나 '[AssemblyInitialization]' 및/또는 '[AssemblyCleanup]'으로 표시된 메서드가 있는 'static'이어야 합니다. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index 5fe248cf5e..cb152e8552 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -287,8 +287,8 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu - Member '{0}' is already exist in the base class - Element członkowski „{0}” już istnieje w klasie bazowej + Member '{0}' already exists in the base class + Element członkowski „{0}” już istnieje w klasie bazowej @@ -509,13 +509,13 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - Klasa testowa powinna mieć co najmniej jedną metodę testową lub być „statyczna” z metodami oznaczonymi jako „[AssemblyInitialization]” i/lub „[AssemblyCleanup]”. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + Klasa testowa powinna mieć co najmniej jedną metodę testową lub być „statyczna” z metodami oznaczonymi jako „[AssemblyInitialization]” i/lub „[AssemblyCleanup]”. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - Klasa testowa „{0}” powinna mieć co najmniej jedną metodę testową lub być „statyczna” z metodami oznaczonymi jako „[AssemblyInitialization]” i/lub „[AssemblyCleanup]”. + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + Klasa testowa „{0}” powinna mieć co najmniej jedną metodę testową lub być „statyczna” z metodami oznaczonymi jako „[AssemblyInitialization]” i/lub „[AssemblyCleanup]”. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index 8080f84e6c..bc30b2c16a 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -287,8 +287,8 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: - Member '{0}' is already exist in the base class - O membro "{0}" já existe na classe base + Member '{0}' already exists in the base class + O membro "{0}" já existe na classe base @@ -509,13 +509,13 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - A classe de teste deve ter pelo menos um método de teste ou ser "static" com métodos marcados por "[AssemblyInitialization]" e/ou "[AssemblyCleanup]". + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + A classe de teste deve ter pelo menos um método de teste ou ser "static" com métodos marcados por "[AssemblyInitialization]" e/ou "[AssemblyCleanup]". - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - A classe de teste "{0}" deve ter pelo menos um método de teste ou ser "static" com métodos marcados por "[AssemblyInitialization]" e/ou "[AssemblyCleanup]" + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + A classe de teste "{0}" deve ter pelo menos um método de teste ou ser "static" com métodos marcados por "[AssemblyInitialization]" e/ou "[AssemblyCleanup]" diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index 808487d6d1..0c5c99ba71 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -291,8 +291,8 @@ The type declaring these methods should also respect the following rules: - Member '{0}' is already exist in the base class - Элемент "{0}" уже существует в базовом классе + Member '{0}' already exists in the base class + Элемент "{0}" уже существует в базовом классе @@ -513,13 +513,13 @@ The type declaring these methods should also respect the following rules: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - Тестовый класс должен содержать хотя бы один тестовый метод или быть "статическим" с методами, отмеченными "[AssemblyInitialization]" или "[AssemblyCleanup]". + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + Тестовый класс должен содержать хотя бы один тестовый метод или быть "статическим" с методами, отмеченными "[AssemblyInitialization]" или "[AssemblyCleanup]". - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - Тестовый класс "{0}" должен содержать хотя бы один тестовый метод или быть "статическим" с методами, отмеченными "[AssemblyInitialization]" или "[AssemblyCleanup]" + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + Тестовый класс "{0}" должен содержать хотя бы один тестовый метод или быть "статическим" с методами, отмеченными "[AssemblyInitialization]" или "[AssemblyCleanup]" diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index f416bdb63e..08688d9799 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -287,8 +287,8 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: - Member '{0}' is already exist in the base class - Üye '{0}' zaten temel sınıfta var + Member '{0}' already exists in the base class + Üye '{0}' zaten temel sınıfta var @@ -510,13 +510,13 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - Test sınıfı en az bir test yöntemine sahip olmalıdır veya '[AssemblyInitialization]' ve/veya '[AssemblyCleanup]' ile işaretlenen yöntemlere sahip 'static' olmalıdır. + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + Test sınıfı en az bir test yöntemine sahip olmalıdır veya '[AssemblyInitialization]' ve/veya '[AssemblyCleanup]' ile işaretlenen yöntemlere sahip 'static' olmalıdır. - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - '{0}' test sınıfı en az bir test yöntemine sahip olmalıdır veya '[AssemblyInitialization]' ve/veya '[AssemblyCleanup]' ile işaretlenen yöntemlere sahip 'static' olmalıdır + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + '{0}' test sınıfı en az bir test yöntemine sahip olmalıdır veya '[AssemblyInitialization]' ve/veya '[AssemblyCleanup]' ile işaretlenen yöntemlere sahip 'static' olmalıdır diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index d5773a770e..85fae7d2fd 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -287,8 +287,8 @@ The type declaring these methods should also respect the following rules: - Member '{0}' is already exist in the base class - 基类中已存在成员“{0}” + Member '{0}' already exists in the base class + 基类中已存在成员“{0}” @@ -509,13 +509,13 @@ The type declaring these methods should also respect the following rules: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - 测试类应至少有一个测试方法,或者应为 "static" 且方法带有 "[AssemblyInitialization]" 和/或 "[AssemblyInitialization]"。 + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + 测试类应至少有一个测试方法,或者应为 "static" 且方法带有 "[AssemblyInitialization]" 和/或 "[AssemblyInitialization]"。 - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - 测试类“{0}”应至少有一个测试方法,或者应为 "static" 且方法带有 "[AssemblyInitialization]" 和/或 "[AssemblyInitialization]"。 + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + 测试类“{0}”应至少有一个测试方法,或者应为 "static" 且方法带有 "[AssemblyInitialization]" 和/或 "[AssemblyInitialization]"。 diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index de3e6c057c..04320e91d8 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -287,8 +287,8 @@ The type declaring these methods should also respect the following rules: - Member '{0}' is already exist in the base class - 成員 '{0}' 已存在於基類中 + Member '{0}' already exists in the base class + 成員 '{0}' 已存在於基類中 @@ -509,13 +509,13 @@ The type declaring these methods should also respect the following rules: - Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]'. - 測試類別至少應有一個測試方法,或是方法以 '[AssemblyInitialization]' 和/或 '[AssemblyCleanup]' 標示的 'static'。 + Test class should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]'. + 測試類別至少應有一個測試方法,或是方法以 '[AssemblyInitialization]' 和/或 '[AssemblyCleanup]' 標示的 'static'。 - Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialization]' and/or '[AssemblyCleanup]' - 測試類別 '{0}' 至少應有一個測試方法,或是方法以 '[AssemblyInitialization]' 和/或 '[AssemblyCleanup]' 標示的 'static'。 + Test class '{0}' should have at least one test method or be 'static' with method(s) marked by '[AssemblyInitialize]' and/or '[AssemblyCleanup]' + 測試類別 '{0}' 至少應有一個測試方法,或是方法以 '[AssemblyInitialization]' 和/或 '[AssemblyCleanup]' 標示的 'static'。 diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs index c6bd66c240..aeff5caa2a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs @@ -35,7 +35,7 @@ internal sealed class HangDumpActivityIndicator : IDataConsumer, ITestSessionLif private readonly ManualResetEventSlim _signalActivity = new(false); private readonly ManualResetEventSlim _mutexCreated = new(false); private readonly bool _traceLevelEnabled; - private readonly ConcurrentDictionary _testsCurrentExecutionState = new(); + private readonly ConcurrentDictionary _testsCurrentExecutionState = new(); private Task? _signalActivityIndicatorTask; private Mutex? _activityIndicatorMutex; @@ -143,7 +143,7 @@ private async Task CallbackAsync(IRequest request) if (request is GetInProgressTestsRequest) { await _logger.LogDebugAsync($"Received '{nameof(GetInProgressTestsRequest)}'"); - return new GetInProgressTestsResponse(_testsCurrentExecutionState.Select(x => (x.Key, (int)_clock.UtcNow.Subtract(x.Value.Item2).TotalSeconds)).ToArray()); + return new GetInProgressTestsResponse(_testsCurrentExecutionState.Select(x => (x.Value.Name, (int)_clock.UtcNow.Subtract(x.Value.StartTime).TotalSeconds)).ToArray()); } else if (request is ExitSignalActivityIndicatorTaskRequest) { @@ -173,14 +173,14 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella await _logger.LogTraceAsync($"New in-progress test '{nodeChangedMessage.TestNode.DisplayName}'"); } - _testsCurrentExecutionState.TryAdd(nodeChangedMessage.TestNode.DisplayName, (typeof(InProgressTestNodeStateProperty), _clock.UtcNow)); + _testsCurrentExecutionState.TryAdd(nodeChangedMessage.TestNode.Uid, (nodeChangedMessage.TestNode.DisplayName, typeof(InProgressTestNodeStateProperty), _clock.UtcNow)); } else if (state is PassedTestNodeStateProperty or ErrorTestNodeStateProperty or CancelledTestNodeStateProperty or FailedTestNodeStateProperty or TimeoutTestNodeStateProperty or SkippedTestNodeStateProperty - && _testsCurrentExecutionState.TryRemove(nodeChangedMessage.TestNode.DisplayName, out (Type, DateTimeOffset) record) + && _testsCurrentExecutionState.TryRemove(nodeChangedMessage.TestNode.Uid, out (string Name, Type Type, DateTimeOffset StartTime) record) && _traceLevelEnabled) { - await _logger.LogTraceAsync($"Test removed from in-progress list '{nodeChangedMessage.TestNode.DisplayName}' after '{_clock.UtcNow.Subtract(record.Item2)}', total in-progress '{_testsCurrentExecutionState.Count}'"); + await _logger.LogTraceAsync($"Test removed from in-progress list '{record.Name}' after '{_clock.UtcNow.Subtract(record.StartTime)}', total in-progress '{_testsCurrentExecutionState.Count}'"); } // Optimization, we're interested in test progression and eventually in the discovery progression diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs index f1f702c90e..e09eab94e6 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs @@ -2,6 +2,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; +#if NETCOREAPP +using System.Runtime.InteropServices; +#endif using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Extensions.HangDump.Serializers; @@ -362,6 +365,13 @@ private async Task TakeDumpAsync() _ => throw ApplicationStateGuard.Unreachable(), }; + // Wrap the dump path into "" when it has space in it, this is a workaround for this runtime issue: https://github.com/dotnet/diagnostics/issues/5020 + // It only affects windows. Otherwise the dump creation fails with: [createdump] The pid argument is no longer supported + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && finalDumpFileName.Contains(' ')) + { + finalDumpFileName = $"\"{finalDumpFileName}\""; + } + diagnosticsClient.WriteDump(dumpType, finalDumpFileName, true); #else MiniDumpWriteDump.MiniDumpTypeOption miniDumpTypeOption = _dumpType.ToLowerInvariant().Trim() switch diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs index afc5755114..a1ce5ff9df 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs @@ -174,7 +174,7 @@ private static string GetSourceCode(string language, string extensionsFragments) //------------------------------------------------------------------------------ [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] -public static class SelfRegisteredExtensions +internal static class SelfRegisteredExtensions { public static void AddSelfRegisteredExtensions(this global::Microsoft.Testing.Platform.Builder.ITestApplicationBuilder builder, string[] args) { @@ -193,7 +193,7 @@ public static void AddSelfRegisteredExtensions(this global::Microsoft.Testing.Pl '------------------------------------------------------------------------------ -Public Module SelfRegisteredExtensions +Friend Module SelfRegisteredExtensions Public Sub AddSelfRegisteredExtensions(ByVal builder As Global.Microsoft.Testing.Platform.Builder.ITestApplicationBuilder, ByVal args As Global.System.String()) diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.targets b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.targets index 37c0a13b02..a8ff84d21d 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.targets +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/buildMultiTargeting/Microsoft.Testing.Platform.MSBuild.targets @@ -330,4 +330,18 @@ + + + + + + + diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IHotReloadPlatformOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IHotReloadPlatformOutputDevice.cs new file mode 100644 index 0000000000..6ffbff1af4 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IHotReloadPlatformOutputDevice.cs @@ -0,0 +1,11 @@ +// 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.Platform.OutputDevice; + +internal interface IHotReloadPlatformOutputDevice : IPlatformOutputDevice +{ + Task DisplayBeforeHotReloadSessionStartAsync(); + + Task DisplayAfterHotReloadSessionEndAsync(); +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiCodes.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiCodes.cs index 08be8b624c..79dc78c804 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiCodes.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiCodes.cs @@ -16,7 +16,7 @@ internal static class AnsiCodes /// /// The control sequence introducer. /// - public const string CSI = "\x1b["; + public const string CSI = $"{Esc}["; /// /// Select graphic rendition. @@ -24,7 +24,7 @@ internal static class AnsiCodes /// /// Print color-code to change text color. /// - public const string SetColor = ";1m"; + public const string SetColor = "m"; /// /// Select graphic rendition - set bold mode. @@ -45,17 +45,17 @@ internal static class AnsiCodes /// /// Print urltext to render a hyperlink. /// - public const string LinkPrefix = "\x1b]8;;"; + public const string LinkPrefix = $"{Esc}]8;;"; /// /// . /// - public const string LinkInfix = "\x1b\\"; + public const string LinkInfix = $"{Esc}\\"; /// /// . /// - public const string LinkSuffix = "\x1b]8;;\x1b\\"; + public const string LinkSuffix = $"{Esc}]8;;{Esc}\\"; /// /// Moves up the specified number of lines and puts cursor at the beginning of the line. @@ -100,12 +100,12 @@ internal static class AnsiCodes /// /// Hides the cursor. /// - public const string HideCursor = "\x1b[?25l"; + public const string HideCursor = $"{Esc}[?25l"; /// /// Shows/restores the cursor. /// - public const string ShowCursor = "\x1b[?25h"; + public const string ShowCursor = $"{Esc}[?25h"; /// /// Set progress state to a busy spinner.
@@ -115,7 +115,7 @@ internal static class AnsiCodes /// ConEmu specific OSC codes.
/// iTerm2 proprietary escape codes. /// - public const string SetBusySpinner = "\x1b]9;4;3;\x1b\\"; + public const string SetBusySpinner = $"{Esc}]9;4;3;{Esc}\\"; /// /// Remove progress state, restoring taskbar status to normal.
@@ -125,7 +125,7 @@ internal static class AnsiCodes /// ConEmu specific OSC codes.
/// iTerm2 proprietary escape codes. /// - public const string RemoveBusySpinner = "\x1b]9;4;0;\x1b\\"; + public const string RemoveBusySpinner = $"{Esc}]9;4;0;{Esc}\\"; public static string Colorize(string? s, TerminalColor color) => RoslynString.IsNullOrWhiteSpace(s) ? s ?? string.Empty : $"{CSI}{(int)color}{SetColor}{s}{SetDefaultColor}"; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs index a3e82d9c4d..27576e76c6 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs @@ -235,7 +235,7 @@ public void AppendLink(string? path, int? lineNumber) urlString = uri.ToString(); } - SetColor(TerminalColor.Gray); + SetColor(TerminalColor.DarkGray); Append(AnsiCodes.LinkPrefix); Append(urlString); Append(AnsiCodes.LinkInfix); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs index 1ddd457c42..c2b3d7da46 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs @@ -53,7 +53,7 @@ public void AppendTestWorkerProgress(int i, AnsiTerminal terminal) terminal.Append('['); charsTaken++; - terminal.SetColor(TerminalColor.DarkGreen); + terminal.SetColor(TerminalColor.Green); terminal.Append('✓'); charsTaken++; string passedText = passed.ToString(CultureInfo.CurrentCulture); @@ -64,7 +64,7 @@ public void AppendTestWorkerProgress(int i, AnsiTerminal terminal) terminal.Append('/'); charsTaken++; - terminal.SetColor(TerminalColor.DarkRed); + terminal.SetColor(TerminalColor.Red); terminal.Append('x'); charsTaken++; string failedText = failed.ToString(CultureInfo.CurrentCulture); @@ -75,7 +75,7 @@ public void AppendTestWorkerProgress(int i, AnsiTerminal terminal) terminal.Append('/'); charsTaken++; - terminal.SetColor(TerminalColor.DarkYellow); + terminal.SetColor(TerminalColor.Yellow); terminal.Append('↓'); charsTaken++; string skippedText = skipped.ToString(CultureInfo.CurrentCulture); @@ -177,7 +177,12 @@ public void Render(AnsiTerminalTestProgressFrame previousFrame, AnsiTerminal ter terminal.MoveCursorUp(previousFrame.ProgressCount + 2); } - terminal.AppendLine(); + // When there is nothing to render, don't write empty lines, e.g. when we start the test run, and then we kick off build + // in dotnet test, there is a long pause where we have no assemblies and no test results (yet). + if (ProgressCount > 0) + { + terminal.AppendLine(); + } int i = 0; for (; i < ProgressCount; i++) diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs index 2af7044ad9..519e8431ab 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs @@ -144,9 +144,9 @@ public void StopUpdate() TerminalColor.DarkBlue => ConsoleColor.DarkBlue, TerminalColor.DarkMagenta => ConsoleColor.DarkMagenta, TerminalColor.DarkCyan => ConsoleColor.DarkCyan, - TerminalColor.DarkWhite => ConsoleColor.White, + TerminalColor.Gray => ConsoleColor.White, TerminalColor.Default => _defaultForegroundColor, - TerminalColor.Gray => ConsoleColor.Gray, + TerminalColor.DarkGray => ConsoleColor.Gray, TerminalColor.Red => ConsoleColor.Red, TerminalColor.Green => ConsoleColor.Green, TerminalColor.Yellow => ConsoleColor.Yellow, diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalColor.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalColor.cs index bccc7196df..1120f47962 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalColor.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalColor.cs @@ -14,39 +14,39 @@ internal enum TerminalColor Black = 30, /// - /// Red. + /// DarkRed. /// DarkRed = 31, /// - /// Green. + /// DarkGreen. /// DarkGreen = 32, /// - /// Yellow. + /// DarkYellow. /// DarkYellow = 33, /// - /// Blue. + /// DarkBlue. /// DarkBlue = 34, /// - /// Magenta. + /// DarkMagenta. /// DarkMagenta = 35, /// - /// Cyan. + /// DarkCyan. /// DarkCyan = 36, /// - /// White. + /// Gray. This entry looks out of order, but in reality 37 is dark white, which is lighter than bright black = Dark Gray in Console colors. /// - DarkWhite = 37, + Gray = 37, /// /// Default. @@ -54,9 +54,9 @@ internal enum TerminalColor Default = 39, /// - /// Gray. + /// DarkGray. This entry looks out of order, but in reality 90 is bright black, which is darker than dark white = Gray in Console colors. /// - Gray = 90, + DarkGray = 90, /// /// Red. diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs index 6076758537..b695d70a00 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs @@ -50,6 +50,8 @@ internal sealed partial class TerminalTestReporter : IDisposable private bool _wasCancelled; + private bool? _shouldShowPassedTests; + #if NET7_0_OR_GREATER [GeneratedRegex(@$"^ at ((?.+) in (?.+):line (?\d+)|(?.+))$", RegexOptions.ExplicitCapture, 1000)] private static partial Regex GetFrameRegex(); @@ -254,7 +256,7 @@ private void AppendTestRunSummary(ITerminal terminal) if (!_options.ShowAssembly && _assemblies.Count == 1) { TestProgressState testProgressState = _assemblies.Values.Single(); - terminal.SetColor(TerminalColor.Gray); + terminal.SetColor(TerminalColor.DarkGray); terminal.Append(" - "); terminal.ResetColor(); AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, testProgressState.Assembly, testProgressState.TargetFramework, testProgressState.Architecture); @@ -317,7 +319,7 @@ private void AppendTestRunSummary(ITerminal terminal) if (colorizeSkipped) { - terminal.SetColor(TerminalColor.DarkYellow); + terminal.SetColor(TerminalColor.Yellow); } terminal.AppendLine(skippedText); @@ -339,7 +341,7 @@ private static void AppendAssemblyResult(ITerminal terminal, bool succeeded, int { if (!succeeded) { - terminal.SetColor(TerminalColor.DarkRed); + terminal.SetColor(TerminalColor.Red); // If the build failed, we print one of three red strings. string text = (countErrors > 0, countWarnings > 0) switch { @@ -353,13 +355,13 @@ private static void AppendAssemblyResult(ITerminal terminal, bool succeeded, int } else if (countWarnings > 0) { - terminal.SetColor(TerminalColor.DarkYellow); + terminal.SetColor(TerminalColor.Yellow); terminal.Append($"succeeded with {countWarnings} warning(s)"); terminal.ResetColor(); } else { - terminal.SetColor(TerminalColor.DarkGreen); + terminal.SetColor(TerminalColor.Green); terminal.Append(PlatformResources.PassedLowercase); terminal.ResetColor(); } @@ -373,7 +375,7 @@ internal void TestCompleted( TestOutcome outcome, TimeSpan duration, string? errorMessage, - string? errorStackTrace, + Exception? exception, string? expected, string? actual) { @@ -399,7 +401,7 @@ internal void TestCompleted( } _terminalWithProgress.UpdateWorker(asm.SlotIndex); - if (outcome != TestOutcome.Passed || _options.ShowPassedTests) + if (outcome != TestOutcome.Passed || GetShowPassedTests()) { _terminalWithProgress.WriteToTerminal(terminal => RenderTestCompleted( terminal, @@ -410,12 +412,18 @@ internal void TestCompleted( outcome, duration, errorMessage, - errorStackTrace, + exception, expected, actual)); } } + private bool GetShowPassedTests() + { + _shouldShowPassedTests ??= _options.ShowPassedTests(); + return _shouldShowPassedTests.Value; + } + internal /* for testing */ void RenderTestCompleted( ITerminal terminal, string assembly, @@ -425,20 +433,20 @@ internal void TestCompleted( TestOutcome outcome, TimeSpan duration, string? errorMessage, - string? errorStackTrace, + Exception? exception, string? expected, string? actual) { - if (outcome == TestOutcome.Passed && !_options.ShowPassedTests) + if (outcome == TestOutcome.Passed && !GetShowPassedTests()) { return; } TerminalColor color = outcome switch { - TestOutcome.Error or TestOutcome.Fail or TestOutcome.Canceled or TestOutcome.Timeout => TerminalColor.DarkRed, - TestOutcome.Skipped => TerminalColor.DarkYellow, - TestOutcome.Passed => TerminalColor.DarkGreen, + TestOutcome.Error or TestOutcome.Fail or TestOutcome.Canceled or TestOutcome.Timeout => TerminalColor.Red, + TestOutcome.Skipped => TerminalColor.Yellow, + TestOutcome.Passed => TerminalColor.Green, _ => throw new NotSupportedException(), }; string outcomeText = outcome switch @@ -455,7 +463,7 @@ internal void TestCompleted( terminal.ResetColor(); terminal.Append(' '); terminal.Append(displayName); - terminal.SetColor(TerminalColor.Gray); + terminal.SetColor(TerminalColor.DarkGray); terminal.Append(' '); AppendLongDuration(terminal, duration); if (_options.ShowAssembly) @@ -469,9 +477,10 @@ internal void TestCompleted( terminal.AppendLine(); - FormatErrorMessage(terminal, errorMessage); + FormatErrorMessage(terminal, errorMessage, exception, outcome); FormatExpectedAndActual(terminal, expected, actual); - FormatStackTrace(terminal, errorStackTrace); + FormatStackTrace(terminal, exception); + FormatInnerExceptions(terminal, exception); } private static void AppendAssemblyLinkTargetFrameworkAndArchitecture(ITerminal terminal, string assembly, string? targetFramework, string? architecture) @@ -495,25 +504,16 @@ private static void AppendAssemblyLinkTargetFrameworkAndArchitecture(ITerminal t } } - private static void FormatStackTrace(ITerminal terminal, string? errorStackTrace) + private static void FormatStackTrace(ITerminal terminal, Exception? exception) { - if (RoslynString.IsNullOrWhiteSpace(errorStackTrace)) + if (exception?.StackTrace is not { } stackTrace) { return; } - terminal.SetColor(TerminalColor.DarkRed); - terminal.Append(SingleIndentation); - terminal.Append(PlatformResources.StackTrace); - terminal.AppendLine(":"); - - if (!errorStackTrace.Contains('\n')) - { - AppendStackFrame(terminal, errorStackTrace); - return; - } + terminal.SetColor(TerminalColor.DarkGray); - string[] lines = errorStackTrace.Split(NewLineStrings, StringSplitOptions.None); + string[] lines = stackTrace.Split(NewLineStrings, StringSplitOptions.None); foreach (string line in lines) { AppendStackFrame(terminal, line); @@ -529,7 +529,7 @@ private static void FormatStackTrace(ITerminal terminal, string? errorStackTrace if (match.Success) { bool weHaveFilePathAndCodeLine = !RoslynString.IsNullOrWhiteSpace(match.Groups["code"].Value); - terminal.SetColor(TerminalColor.Gray); + terminal.SetColor(TerminalColor.DarkGray); terminal.Append(PlatformResources.StackFrameAt); terminal.Append(' '); terminal.ResetColor(); @@ -545,7 +545,7 @@ private static void FormatStackTrace(ITerminal terminal, string? errorStackTrace if (weHaveFilePathAndCodeLine) { - terminal.SetColor(TerminalColor.Gray); + terminal.SetColor(TerminalColor.DarkGray); terminal.Append(' '); terminal.Append(PlatformResources.StackFrameIn); terminal.Append(' '); @@ -581,18 +581,57 @@ private static void FormatExpectedAndActual(ITerminal terminal, string? expected terminal.ResetColor(); } - private static void FormatErrorMessage(ITerminal terminal, string? errorMessage) + private static void FormatErrorMessage(ITerminal terminal, string? errorMessage, Exception? exception, TestOutcome outcome) { - if (RoslynString.IsNullOrWhiteSpace(errorMessage)) + if (RoslynString.IsNullOrWhiteSpace(errorMessage) && exception is null) { return; } terminal.SetColor(TerminalColor.Red); - AppendIndentedLine(terminal, errorMessage, SingleIndentation); + + if (exception is null) + { + AppendIndentedLine(terminal, errorMessage, SingleIndentation); + } + else if (outcome == TestOutcome.Fail) + { + // For failed tests, we don't prefix the message with the exception type because it is most likely an assertion specific exception like AssertionFailedException, and we prefer to show that without the exception type to avoid additional noise. + AppendIndentedLine(terminal, errorMessage ?? exception.Message, SingleIndentation); + } + else + { + AppendIndentedLine(terminal, $"{exception.GetType().FullName}: {errorMessage ?? exception.Message}", SingleIndentation); + } + terminal.ResetColor(); } + private static void FormatInnerExceptions(ITerminal terminal, Exception? exception) + { + IEnumerable aggregateExceptions = exception switch + { + AggregateException aggregate => aggregate.Flatten().InnerExceptions, + _ => [exception?.InnerException], + }; + + foreach (Exception? aggregate in aggregateExceptions) + { + Exception? currentException = aggregate; + while (currentException is not null) + { + terminal.SetColor(TerminalColor.Red); + terminal.Append(SingleIndentation); + terminal.Append("--->"); + FormatErrorMessage(terminal, null, currentException, TestOutcome.Error); + + FormatStackTrace(terminal, currentException); + + currentException = currentException.InnerException; + } + } + } + private static void AppendIndentedLine(ITerminal terminal, string? message, string indent) { if (RoslynString.IsNullOrWhiteSpace(message)) @@ -653,7 +692,7 @@ private static void AppendLongDuration(ITerminal terminal, TimeSpan duration, bo { if (colorize) { - terminal.SetColor(TerminalColor.Gray); + terminal.SetColor(TerminalColor.DarkGray); } HumanReadableDurationFormatter.Append(terminal, duration, wrapInParentheses); @@ -772,8 +811,8 @@ private static TerminalColor ToTerminalColor(ConsoleColor consoleColor) ConsoleColor.DarkRed => TerminalColor.DarkRed, ConsoleColor.DarkMagenta => TerminalColor.DarkMagenta, ConsoleColor.DarkYellow => TerminalColor.DarkYellow, + ConsoleColor.DarkGray => TerminalColor.DarkGray, ConsoleColor.Gray => TerminalColor.Gray, - ConsoleColor.DarkGray => TerminalColor.Gray, ConsoleColor.Blue => TerminalColor.Blue, ConsoleColor.Green => TerminalColor.Green, ConsoleColor.Cyan => TerminalColor.Cyan, diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs index 9cdd7b4c01..d2e84bc340 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs @@ -13,7 +13,7 @@ internal sealed class TerminalTestReporterOptions /// /// Gets a value indicating whether we should show passed tests. /// - public bool ShowPassedTests { get; init; } + public Func ShowPassedTests { get; init; } = () => true; /// /// Gets a value indicating whether we should show information about which assembly is the source of the data on screen. Turn this off when running directly from an exe to reduce noise, because the path will always be the same. diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs index 5792660438..9bb19adfd8 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs @@ -25,7 +25,7 @@ namespace Microsoft.Testing.Platform.OutputDevice; /// /// Implementation of output device that writes to terminal with progress and optionally with ANSI. /// -internal partial class TerminalOutputDevice : IPlatformOutputDevice, +internal partial class TerminalOutputDevice : IHotReloadPlatformOutputDevice, IDataConsumer, IOutputDeviceDataProducer, ITestSessionLifetimeHandler, @@ -123,11 +123,16 @@ public Task InitializeAsync() bool noAnsi = _commandLineOptions.IsOptionSet(TerminalTestReporterCommandLineOptionsProvider.NoAnsiOption); bool noProgress = _commandLineOptions.IsOptionSet(TerminalTestReporterCommandLineOptionsProvider.NoProgressOption); - bool showPassed = false; + // _runtimeFeature.IsHotReloadEnabled is not set to true here, even if the session will be HotReload, + // we need to postpone that decision until the first test result. + // + // This works but is NOT USED, we prefer to have the same experience of not showing passed tests in hotReload mode as in normal mode. + // Func showPassed = () => _runtimeFeature.IsHotReloadEnabled; + Func showPassed = () => false; bool outputOption = _commandLineOptions.TryGetOptionArgumentList(TerminalTestReporterCommandLineOptionsProvider.OutputOption, out string[]? arguments); if (outputOption && arguments?.Length > 0 && TerminalTestReporterCommandLineOptionsProvider.OutputOptionDetailedArgument.Equals(arguments[0], StringComparison.OrdinalIgnoreCase)) { - showPassed = true; + showPassed = () => true; } Func shouldShowProgress = noProgress @@ -270,6 +275,9 @@ public virtual async Task DisplayBannerAsync(string? bannerMessage) } } + public async Task DisplayBeforeHotReloadSessionStartAsync() + => await DisplayBeforeSessionStartAsync(); + public async Task DisplayBeforeSessionStartAsync() { RoslynDebug.Assert(_terminalTestReporter is not null); @@ -284,15 +292,30 @@ public async Task DisplayBeforeSessionStartAsync() } } + public async Task DisplayAfterHotReloadSessionEndAsync() + => await DisplayAfterSessionEndRunInternalAsync(); + public async Task DisplayAfterSessionEndRunAsync() { - RoslynDebug.Assert(_terminalTestReporter is not null); - if (_isVSTestMode || _isListTests || _isServerMode) { return; } + // Do NOT check and store the value in the constructor + // it won't be populated yet, so you will always see false. + if (_runtimeFeature.IsHotReloadEnabled) + { + return; + } + + await DisplayAfterSessionEndRunInternalAsync(); + } + + private async Task DisplayAfterSessionEndRunInternalAsync() + { + RoslynDebug.Assert(_terminalTestReporter is not null); + using (await _asyncMonitor.LockAsync(TimeoutHelper.DefaultHangTimeSpanTimeout)) { if (!_firstCallTo_OnSessionStartingAsync) @@ -411,8 +434,8 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella testNodeStateChanged.TestNode.DisplayName, TestOutcome.Error, duration, - errorMessage: errorState.Exception?.Message ?? errorState.Explanation, - errorState.Exception?.StackTrace, + errorState.Explanation, + errorState.Exception, expected: null, actual: null); break; @@ -425,8 +448,8 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella testNodeStateChanged.TestNode.DisplayName, TestOutcome.Fail, duration, - errorMessage: failedState.Exception?.Message ?? failedState.Explanation, - failedState.Exception?.StackTrace, + failedState.Explanation, + failedState.Exception, expected: failedState.Exception?.Data["assert.expected"] as string, actual: failedState.Exception?.Data["assert.actual"] as string); break; @@ -439,8 +462,8 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella testNodeStateChanged.TestNode.DisplayName, TestOutcome.Timeout, duration, - errorMessage: timeoutState.Exception?.Message ?? timeoutState.Explanation, - timeoutState.Exception?.StackTrace, + timeoutState.Explanation, + timeoutState.Exception, expected: null, actual: null); break; @@ -453,8 +476,8 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella testNodeStateChanged.TestNode.DisplayName, TestOutcome.Canceled, duration, - errorMessage: cancelledState.Exception?.Message ?? cancelledState.Explanation, - cancelledState.Exception?.StackTrace, + cancelledState.Explanation, + cancelledState.Exception, expected: null, actual: null); break; @@ -468,7 +491,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella outcome: TestOutcome.Passed, duration: duration, errorMessage: null, - errorStackTrace: null, + exception: null, expected: null, actual: null); break; @@ -482,7 +505,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella TestOutcome.Skipped, duration, errorMessage: null, - errorStackTrace: null, + exception: null, expected: null, actual: null); break; diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt index 60531fbd53..87b7d881ce 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt @@ -1,11 +1,50 @@ #nullable enable [TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IBannerMessageOwnerCapability [TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IBannerMessageOwnerCapability.GetBannerMessageAsync() -> System.Threading.Tasks.Task! +[TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Deconstruct(out string! StandardError) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.get -> string! +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.init -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! original) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(string! StandardError) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Deconstruct(out string! StandardOutput) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.get -> string! +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.init -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! original) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(string! StandardOutput) -> void +[TPEXP]Microsoft.Testing.Platform.Requests.IExecuteRequestCompletionNotifier +[TPEXP]Microsoft.Testing.Platform.Requests.IExecuteRequestCompletionNotifier.Complete() -> void +[TPEXP]Microsoft.Testing.Platform.Services.IClientInfo +[TPEXP]Microsoft.Testing.Platform.Services.IClientInfo.Id.get -> string! +[TPEXP]Microsoft.Testing.Platform.Services.IClientInfo.Version.get -> string! [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.BuildDate.get -> System.DateTimeOffset? [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.CommitHash.get -> string? [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.Name.get -> string! [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.Version.get -> string? +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(object? obj) -> bool +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.GetHashCode() -> int +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.ToString() -> string! +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(object? obj) -> bool +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.GetHashCode() -> int +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.ToString() -> string! +[TPEXP]static Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions.GetCapability(this Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> T? +[TPEXP]static Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions.HasCapability(this Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool +[TPEXP]static Microsoft.Testing.Platform.Services.ServiceProviderExtensions.GetClientInfo(this System.IServiceProvider! serviceProvider) -> Microsoft.Testing.Platform.Services.IClientInfo! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.EqualityContract.get -> System.Type! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? other) -> bool +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.EqualityContract.get -> System.Type! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? other) -> bool +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool Microsoft.Testing.Platform.Builder.ConfigurationOptions Microsoft.Testing.Platform.Builder.ConfigurationOptions.ConfigurationOptions() -> void Microsoft.Testing.Platform.Builder.ConfigurationOptions.ConfigurationSources.get -> Microsoft.Testing.Platform.Builder.ConfigurationSourcesOptions! @@ -330,6 +369,8 @@ Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.Background Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.ForegroundColor.get -> Microsoft.Testing.Platform.OutputDevice.IColor? Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.ForegroundColor.init -> void Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.FormattedTextOutputDeviceData(string! text) -> void +Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.Padding.get -> int? +Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.Padding.init -> void Microsoft.Testing.Platform.OutputDevice.IColor Microsoft.Testing.Platform.OutputDevice.IOutputDevice Microsoft.Testing.Platform.OutputDevice.IOutputDevice.DisplayAsync(Microsoft.Testing.Platform.Extensions.OutputDevice.IOutputDeviceDataProducer! producer, Microsoft.Testing.Platform.OutputDevice.IOutputDeviceData! data) -> System.Threading.Tasks.Task! diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt index 0efc76dd10..7dc5c58110 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt @@ -1,42 +1 @@ #nullable enable -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.get -> string! -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.init -> void -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.EqualityContract.get -> System.Type! -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.ToString() -> string! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.GetHashCode() -> int -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(object? obj) -> bool -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? other) -> bool -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! original) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Deconstruct(out string! StandardOutput) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(string! StandardOutput) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.get -> string! -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.init -> void -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.EqualityContract.get -> System.Type! -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.ToString() -> string! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.GetHashCode() -> int -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(object? obj) -> bool -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? other) -> bool -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! original) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Deconstruct(out string! StandardError) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(string! StandardError) -> void -Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.Padding.get -> int? -Microsoft.Testing.Platform.OutputDevice.FormattedTextOutputDeviceData.Padding.init -> void -[TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions -[TPEXP]Microsoft.Testing.Platform.Requests.IExecuteRequestCompletionNotifier -[TPEXP]Microsoft.Testing.Platform.Requests.IExecuteRequestCompletionNotifier.Complete() -> void -[TPEXP]Microsoft.Testing.Platform.Services.IClientInfo -[TPEXP]Microsoft.Testing.Platform.Services.IClientInfo.Id.get -> string! -[TPEXP]Microsoft.Testing.Platform.Services.IClientInfo.Version.get -> string! -[TPEXP]static Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions.GetCapability(this Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> T? -[TPEXP]static Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions.HasCapability(this Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> bool -[TPEXP]static Microsoft.Testing.Platform.Services.ServiceProviderExtensions.GetClientInfo(this System.IServiceProvider! serviceProvider) -> Microsoft.Testing.Platform.Services.IClientInfo! diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/JsonRpcMethods.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/JsonRpcMethods.cs index e0a988755e..cf8b52f5df 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/JsonRpcMethods.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/JsonRpcMethods.cs @@ -60,7 +60,7 @@ internal static class JsonRpcStrings public const string Message = "message"; // Telemetry - public const string EventName = "EventName"; + public const string EventName = "eventName"; public const string Metrics = "metrics"; // Process diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs index 2cc9e319db..3ac6bb615a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryManager.cs @@ -47,8 +47,29 @@ public async Task BuildAsync(ServiceProvider serviceProvide await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT} environment variable: '{cli_telemetryOptOut}'"); isTelemetryOptedOut = (cli_telemetryOptOut is "1" or "true") || isTelemetryOptedOut; - // NO_LOGO + await logger.LogDebugAsync($"Telemetry is '{(!isTelemetryOptedOut ? "ENABLED" : "DISABLED")}'"); + + if (!isTelemetryOptedOut && _telemetryFactory is not null) + { + await ShowTelemetryBannerFirstNoticeAsync(serviceProvider, logger, environment); + } + + serviceProvider.TryAddService(new TelemetryInformation(!isTelemetryOptedOut, TelemetryProperties.VersionValue)); + ITelemetryCollector telemetryCollector = _telemetryFactory is null || isTelemetryOptedOut + ? new NopTelemetryService(!isTelemetryOptedOut) + : _telemetryFactory(serviceProvider); + + if (!isTelemetryOptedOut) + { + await logger.LogDebugAsync($"Telemetry collector provider: '{telemetryCollector.GetType()}'"); + } + + return telemetryCollector; + } + + private async Task ShowTelemetryBannerFirstNoticeAsync(ServiceProvider serviceProvider, ILogger logger, IEnvironment environment) + { // If the environment variable is not set or is set to 0, telemetry is opted in. ICommandLineOptions commandLineOptions = serviceProvider.GetCommandLineOptions(); bool doNotShowLogo = commandLineOptions.IsOptionSet(PlatformCommandLineProvider.NoBannerOptionKey); @@ -61,64 +82,53 @@ public async Task BuildAsync(ServiceProvider serviceProvide await logger.LogDebugAsync($"{EnvironmentVariableConstants.DOTNET_NOLOGO} environment variable: '{dotnetNoLogoEnvVar}'"); doNotShowLogo = (dotnetNoLogoEnvVar is "1" or "true") || doNotShowLogo; - await logger.LogDebugAsync($"Telemetry is '{(!isTelemetryOptedOut ? "ENABLED" : "DISABLED")}'"); - - if (!isTelemetryOptedOut && !doNotShowLogo) + if (doNotShowLogo) { - ITestApplicationModuleInfo testApplicationModuleInfo = serviceProvider.GetTestApplicationModuleInfo(); + return; + } + + ITestApplicationModuleInfo testApplicationModuleInfo = serviceProvider.GetTestApplicationModuleInfo(); #pragma warning disable RS0030 // Do not use banned APIs - There is no easy way to disable it for all members - string? directory = environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create); + string? directory = environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create); #pragma warning restore RS0030 // Do not use banned APIs - if (directory is not null) - { - directory = Path.Combine(directory, "Microsoft", "TestingPlatform"); - } + if (directory is not null) + { + directory = Path.Combine(directory, "Microsoft", "TestingPlatform"); + } - IFileSystem fileSystem = serviceProvider.GetFileSystem(); - string fileName = Path.ChangeExtension(Path.GetFileName(testApplicationModuleInfo.GetCurrentTestApplicationFullPath()), "testingPlatformFirstTimeUseSentinel"); - bool sentinelIsNotPresent = - RoslynString.IsNullOrWhiteSpace(directory) - || !fileSystem.Exists(Path.Combine(directory, fileName)); + IFileSystem fileSystem = serviceProvider.GetFileSystem(); + string fileName = Path.ChangeExtension(Path.GetFileName(testApplicationModuleInfo.GetCurrentTestApplicationFullPath()), "testingPlatformFirstTimeUseSentinel"); + bool sentinelIsNotPresent = + RoslynString.IsNullOrWhiteSpace(directory) + || !fileSystem.Exists(Path.Combine(directory, fileName)); - if (!doNotShowLogo && sentinelIsNotPresent) + if (!sentinelIsNotPresent) + { + return; + } + + IOutputDevice outputDevice = serviceProvider.GetOutputDevice(); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.TelemetryNotice)); + + string? path = null; + try + { + // See if we should write the file, and write it. + if (!RoslynString.IsNullOrWhiteSpace(directory)) { - IOutputDevice outputDevice = serviceProvider.GetOutputDevice(); - await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.TelemetryNotice)); + Directory.CreateDirectory(directory); - string? path = null; - try - { - // See if we should write the file, and write it. - if (!RoslynString.IsNullOrWhiteSpace(directory)) - { - Directory.CreateDirectory(directory); - - // Write empty file. - path = Path.Combine(directory, fileName); - using (fileSystem.NewFileStream(path, FileMode.Create, FileAccess.Write)) - { - } - } - } - catch (Exception exception) when (exception is IOException or SystemException) + // Write empty file. + path = Path.Combine(directory, fileName); + using (fileSystem.NewFileStream(path, FileMode.Create, FileAccess.Write)) { - await logger.LogErrorAsync($"Could not write sentinel file for telemetry to path,'{path ?? ""}'.", exception); } } } - - serviceProvider.TryAddService(new TelemetryInformation(!isTelemetryOptedOut, TelemetryProperties.VersionValue)); - - ITelemetryCollector telemetryCollector = _telemetryFactory is null || isTelemetryOptedOut - ? new NopTelemetryService(!isTelemetryOptedOut) - : _telemetryFactory(serviceProvider); - - if (!isTelemetryOptedOut) + catch (Exception exception) when (exception is IOException or SystemException) { - await logger.LogDebugAsync($"Telemetry collector provider: '{telemetryCollector.GetType()}'"); + await logger.LogErrorAsync($"Could not write sentinel file for telemetry to path,'{path ?? ""}'.", exception); } - - return telemetryCollector; } public Task IsEnabledAsync() => throw new NotImplementedException(); diff --git a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs index d3d7a1d085..43465e0d68 100644 --- a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs @@ -1641,12 +1641,15 @@ private static bool CompareIEnumerable(IEnumerable? expected, IEnumerable? actua object? curExpected = expectedEnum.Current; object? curActual = actualEnum.Current; - - if (curExpected is IEnumerable curExpectedEnum && curActual is IEnumerable curActualEnum) + if (comparer.Compare(curExpected, curActual) == 0) + { + position++; + } + else if (curExpected is IEnumerable curExpectedEnum && curActual is IEnumerable curActualEnum) { stack.Push(new(expectedEnum, actualEnum, position + 1)); stack.Push(new(curExpectedEnum.GetEnumerator(), curActualEnum.GetEnumerator(), 0)); - break; + continue; } else if (comparer.Compare(curExpected, curActual) != 0) { @@ -1656,8 +1659,6 @@ private static bool CompareIEnumerable(IEnumerable? expected, IEnumerable? actua position); return false; } - - position++; } if (actualEnum.MoveNext() && !expectedEnum.MoveNext()) diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt index ee5377dcb0..23ce9ae2e1 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt @@ -143,6 +143,11 @@ Microsoft.VisualStudio.TestTools.UnitTesting.ParallelizeAttribute.Workers.set -> Microsoft.VisualStudio.TestTools.UnitTesting.PriorityAttribute Microsoft.VisualStudio.TestTools.UnitTesting.PriorityAttribute.Priority.get -> int Microsoft.VisualStudio.TestTools.UnitTesting.PriorityAttribute.PriorityAttribute(int priority) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.STATestClassAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.STATestClassAttribute.STATestClassAttribute() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.STATestMethodAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.STATestMethodAttribute.STATestMethodAttribute() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.STATestMethodAttribute.STATestMethodAttribute(string? displayName) -> void Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute.TestCategoryAttribute(string! testCategory) -> void @@ -208,6 +213,8 @@ Microsoft.VisualStudio.TestTools.UnitTesting.TestResult.TestResult() -> void Microsoft.VisualStudio.TestTools.UnitTesting.TestTimeout Microsoft.VisualStudio.TestTools.UnitTesting.TestTimeout.Infinite = 2147483647 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestTimeout Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.CooperativeCancellation.get -> bool +Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.CooperativeCancellation.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.Timeout.get -> int Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.TimeoutAttribute(int timeout) -> void Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.TimeoutAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.TestTimeout timeout) -> void diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 7241987b6a..ab058de62d 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1,8 +1 @@ #nullable enable -Microsoft.VisualStudio.TestTools.UnitTesting.STATestClassAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.STATestClassAttribute.STATestClassAttribute() -> void -Microsoft.VisualStudio.TestTools.UnitTesting.STATestMethodAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.STATestMethodAttribute.STATestMethodAttribute() -> void -Microsoft.VisualStudio.TestTools.UnitTesting.STATestMethodAttribute.STATestMethodAttribute(string? displayName) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.CooperativeCancellation.get -> bool -Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.CooperativeCancellation.set -> void diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs new file mode 100644 index 0000000000..e5bb4b6ff3 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs @@ -0,0 +1,157 @@ +// 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.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestGroup] +public sealed class HangDumpOutputTests : AcceptanceTestBase +{ + private readonly TestAssetFixture _testAssetFixture; + + public HangDumpOutputTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) + : base(testExecutionContext) => _testAssetFixture = testAssetFixture; + + [Arguments("Mini")] + public async Task HangDump_Outputs_HangingTests_EvenWhenHangingTestsHaveTheSameDisplayName(string format) + { + // This test makes sure that when tests have the same display name (e.g. like Test1 from both Class1 and Class2) + // they will still show up in the hanging tests. This was not the case before when we were just putting them into + // a dictionary based on DisplayName. In that case both tests were started at the same time, and only 1 entry was added + // to currently executing tests. When first test with name Test1 completed we removed that entry, but Class2.Test1 was still + // running. Solution is to use a more unique identifier. + string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), format); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"--hangdump --hangdump-timeout 8s --hangdump-type {format} --results-directory {resultDirectory} --no-progress", + new Dictionary + { + { "SLEEPTIMEMS1", "100" }, + { "SLEEPTIMEMS2", "600000" }, + }); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + testHostResult.AssertOutputContains("Test1"); + } + + [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] + public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string AssetName = "TestAssetFixture"; + + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + Sources + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + + private const string Sources = """ +#file HangDump.csproj + + + + $TargetFrameworks$ + Exe + true + enable + preview + + + + + + + +#file Program.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Globalization; + +using Microsoft.Testing.Platform; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Services; + +public class Startup +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.AddHangDumpProvider(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Class1.Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new InProgressTestNodeStateProperty()), + })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Class2.Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new InProgressTestNodeStateProperty()), + })); + + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS1")!, CultureInfo.InvariantCulture)); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Class1.Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty()), + })); + + Thread.Sleep(int.Parse(Environment.GetEnvironmentVariable("SLEEPTIMEMS2")!, CultureInfo.InvariantCulture)); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Class2.Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new PassedTestNodeStateProperty()), + })); + + context.Complete(); + } +} +"""; + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs index dac37079fb..c73a9da2a0 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs @@ -47,6 +47,24 @@ public async Task HangDump_CustomFileName_CreateDump() Assert.IsTrue(dumpFile is not null, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } + public async Task HangDump_PathWithSpaces_CreateDump() + { + string resultDir = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); + string resultDirectory = Path.Combine(resultDir, "directory with spaces"); + Directory.CreateDirectory(resultDirectory); + var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + TestHostResult testHostResult = await testHost.ExecuteAsync( + $"""--hangdump --hangdump-timeout 8s --hangdump-filename myhungdumpfile_%p.dmp --results-directory "{resultDirectory}" """, + new Dictionary + { + { "SLEEPTIMEMS1", "4000" }, + { "SLEEPTIMEMS2", "20000" }, + }); + testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); + string? dumpFile = Directory.GetFiles(resultDirectory, "myhungdumpfile_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); + Assert.IsTrue(dumpFile is not null, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); + } + [Arguments("Mini")] [Arguments("Heap")] [Arguments("Triage")] diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs index e004a8ced4..7ad84c074d 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs @@ -11,28 +11,9 @@ public class MSBuildTests_Solution : AcceptanceTestBase private readonly AcceptanceFixture _acceptanceFixture; private const string AssetName = "MSTestProject"; - static MSBuildTests_Solution() - { - string dotnetMuxerSDK = Directory.GetDirectories(Path.Combine(RootFinder.Find(), ".dotnet", "sdk")).OrderByDescending(x => x).First(); - File.WriteAllText(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.CrossTargeting.targets"), MicrosoftCommonCrossTargeting); - if (!File.Exists(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.Test.targets"))) - { - File.WriteAllText(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.Test.targets"), MicrosoftCommonTesttargets); - } - } - public MSBuildTests_Solution(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - private void CheckPatch() - { - // https://github.com/dotnet/sdk/issues/37712 - if (DateTime.UtcNow.Date > new DateTime(2024, 12, 1)) - { - throw new InvalidOperationException("Check if we can remove the patch!"); - } - } - internal static IEnumerable> GetBuildMatrix() { foreach (TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm)> entry in GetBuildMatrixSingleAndMultiTfmBuildConfiguration()) @@ -52,8 +33,6 @@ private void CheckPatch() [ArgumentsProvider(nameof(GetBuildMatrix))] public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_Tests(string singleTfmOrMultiTfm, BuildConfiguration _, bool isMultiTfm, string command) { - CheckPatch(); - using TestAsset generator = await TestAsset.GenerateAssetAsync( AssetName, SourceCode @@ -155,215 +134,5 @@ public void TestMethod1() global using Microsoft.Testing.Platform.Builder; global using Microsoft.Testing.Internal.Framework; global using Microsoft.Testing.Platform.MSBuild; -"""; - - private const string MicrosoftCommonCrossTargeting = """ - - - - true - true - - - - - - - - - - <_ThisProjectBuildMetadata Include="$(MSBuildProjectFullPath)"> - @(_TargetFrameworkInfo) - @(_TargetFrameworkInfo->'%(TargetFrameworkMonikers)') - @(_TargetFrameworkInfo->'%(TargetPlatformMonikers)') - $(_AdditionalPropertiesFromProject) - false - @(_TargetFrameworkInfo->'%(IsRidAgnostic)') - - - false - $(Platform) - $(Platforms) - - - - - - <_TargetFramework Include="$(TargetFrameworks)" /> - - <_TargetFrameworkNormalized Include="@(_TargetFramework->Trim()->Distinct())" /> - <_InnerBuildProjects Include="$(MSBuildProjectFile)"> - TargetFramework=%(_TargetFrameworkNormalized.Identity) - - - - - - - - - - - - true - - - - - - - - - - - - - Build - - - - - - - - - - - - $([MSBuild]::IsRunningFromVisualStudio()) - $([MSBuild]::GetToolsDirectory32())\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets - $(MSBuildToolsPath)\NuGet.targets - - - - - - true - - - - - true - - - - true - - - - <_DirectoryBuildTargetsFile Condition="'$(_DirectoryBuildTargetsFile)' == ''">Directory.Build.targets - <_DirectoryBuildTargetsBasePath Condition="'$(_DirectoryBuildTargetsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildTargetsFile)')) - $([System.IO.Path]::Combine('$(_DirectoryBuildTargetsBasePath)', '$(_DirectoryBuildTargetsFile)')) - - - - false - - - -"""; - - private const string MicrosoftCommonTesttargets = """ - - - """; } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs index 73432d0ba4..c533062424 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs @@ -35,8 +35,6 @@ public async Task Telemetry_ByDefault_TelemetryIsEnabled(string tfm) .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: True .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'ENABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); @@ -64,8 +62,6 @@ public async Task Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_Telem .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: True .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '1' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'DISABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); @@ -93,8 +89,6 @@ public async Task Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVa .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: True .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '1' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'DISABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); @@ -116,8 +110,6 @@ public async Task Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOption .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TestApplicationOptions.EnableTelemetry: False .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_TELEMETRY_OPTOUT environment variable: '' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_CLI_TELEMETRY_OPTOUT environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG TESTINGPLATFORM_NOBANNER environment variable: '' -.+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG DOTNET_NOLOGO environment variable: '' .+ Microsoft.Testing.Platform.Telemetry.TelemetryManager DEBUG Telemetry is 'DISABLED' """; await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs index 71458475d9..9f3e51de07 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs @@ -48,6 +48,94 @@ public void Method() { } await VerifyCS.VerifyAnalyzerAsync(code); } + public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButBothArePrivate_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + public class BaseClass + { + private void Method() { } + } + + [TestClass] + public class DerivedClass : BaseClass + { + private void Method() { } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButBaseIsPrivate_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + public class BaseClass + { + private void Method() { } + } + + [TestClass] + public class DerivedClass : BaseClass + { + public void [|Method|]() { } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButDerivedClassIsPrivate_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + public class BaseClass + { + public void Method() { } + } + + [TestClass] + public class DerivedClass : BaseClass + { + private void [|Method|]() { } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButOneIsGeneric_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + [TestClass] + public class BaseTest + { + protected TObject CreateObject() + { + throw new NotImplementedException(); + } + } + + [TestClass] + public class ExtendedTest : BaseTest + { + private SomeType CreateObject() + { + throw new NotImplementedException(); + } + } + + public class SomeType + { + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_WithParameters_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs index c5967776f1..b15c8c2c9d 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs @@ -164,12 +164,112 @@ public void TestMethod214(int i, string s) { } + [DynamicData("DataJaggedArray")] + [TestMethod] + public void TestMethod301(MyTestClass[] testClasses) + { + } + + [DynamicData("SomeDataJaggedArray", typeof(SomeClass))] + [TestMethod] + public void TestMethod302(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataSourceName: "DataJaggedArray")] + [TestMethod] + public void TestMethod303(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeDataJaggedArray")] + [TestMethod] + public void TestMethod304(MyTestClass[] testClasses) + { + } + + [DynamicData("GetDataJaggedArray", DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod311(MyTestClass[] testClasses) + { + } + + [DynamicData("GetSomeDataJaggedArray", typeof(SomeClass), DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod312(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetDataJaggedArray")] + [TestMethod] + public void TestMethod313(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeDataJaggedArray")] + [TestMethod] + public void TestMethod314(MyTestClass[] testClasses) + { + } + + [DynamicData("DataNonObjectTypeArray")] + [TestMethod] + public void TestMethod401(MyTestClass[] testClasses) + { + } + + [DynamicData("SomeDataNonObjectTypeArray", typeof(SomeClass))] + [TestMethod] + public void TestMethod402(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataSourceName: "DataNonObjectTypeArray")] + [TestMethod] + public void TestMethod403(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeDataNonObjectTypeArray")] + [TestMethod] + public void TestMethod404(MyTestClass[] testClasses) + { + } + + [DynamicData("GetDataNonObjectTypeArray", DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod411(MyTestClass[] testClasses) + { + } + + [DynamicData("GetSomeDataNonObjectTypeArray", typeof(SomeClass), DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod412(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetDataNonObjectTypeArray")] + [TestMethod] + public void TestMethod413(MyTestClass[] testClasses) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeDataNonObjectTypeArray")] + [TestMethod] + public void TestMethod414(MyTestClass[] testClasses) + { + } + public static IEnumerable Data => new List(); public static IEnumerable> DataTuple => new List>(); public static IEnumerable<(int, string)> DataValueTuple => new List<(int, string)>(); + public static MyTestClass[][] DataJaggedArray => System.Array.Empty(); + public static IEnumerable DataNonObjectTypeArray => new List(); public static IEnumerable GetData() => new List(); public static IEnumerable> GetDataTuple() => new List>(); public static IEnumerable<(int, string)> GetDataValueTuple() => new List<(int, string)>(); + public static MyTestClass[][] GetDataJaggedArray() => System.Array.Empty(); + public static IEnumerable GetDataNonObjectTypeArray() => new List(); } public class SomeClass @@ -177,9 +277,13 @@ public class SomeClass public static IEnumerable SomeData => new List(); public static IEnumerable> SomeDataTuple => new List>(); public static IEnumerable<(int, string)> SomeDataValueTuple => new List<(int, string)>(); + public static MyTestClass[][] SomeDataJaggedArray => System.Array.Empty(); + public static IEnumerable SomeDataNonObjectTypeArray => new List(); public static IEnumerable GetSomeData() => new List(); public static IEnumerable> GetSomeDataTuple() => new List>(); public static IEnumerable<(int, string)> GetSomeDataValueTuple() => new List<(int, string)>(); + public static MyTestClass[][] GetSomeDataJaggedArray() => System.Array.Empty(); + public static IEnumerable GetSomeDataNonObjectTypeArray() => new List(); } """; @@ -469,36 +573,88 @@ public void TestMethod4(object[] o) [{|#4:DynamicData("GetData", DynamicDataSourceType.Method)|}] [TestMethod] - public void TestMethod11(object[] o) + public void TestMethod5(object[] o) { } [{|#5:DynamicData("GetSomeData", typeof(SomeClass), DynamicDataSourceType.Method)|}] [TestMethod] - public void TestMethod12(object[] o) + public void TestMethod6(object[] o) { } [{|#6:DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetData")|}] [TestMethod] - public void TestMethod13(object[] o) + public void TestMethod7(object[] o) { } [{|#7:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeData")|}] [TestMethod] - public void TestMethod14(object[] o) + public void TestMethod8(object[] o) + { + } + + [{|#8:DynamicData("DataArray")|}] + [TestMethod] + public void TestMethod9(MyTestClass[] o) + { + } + + [{|#9:DynamicData("SomeDataArray", typeof(SomeClass))|}] + [TestMethod] + public void TestMethod10(MyTestClass[] o) + { + } + + [{|#10:DynamicData(dynamicDataSourceName: "DataArray")|}] + [TestMethod] + public void TestMethod11(MyTestClass[] o) + { + } + + [{|#11:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeDataArray")|}] + [TestMethod] + public void TestMethod12(MyTestClass[] o) + { + } + + [{|#12:DynamicData("GetDataArray", DynamicDataSourceType.Method)|}] + [TestMethod] + public void TestMethod13(MyTestClass[] o) + { + } + + [{|#13:DynamicData("GetSomeDataArray", typeof(SomeClass), DynamicDataSourceType.Method)|}] + [TestMethod] + public void TestMethod14(MyTestClass[] o) + { + } + + [{|#14:DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetDataArray")|}] + [TestMethod] + public void TestMethod15(MyTestClass[] o) + { + } + + [{|#15:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeDataArray")|}] + [TestMethod] + public void TestMethod16(MyTestClass[] o) { } public static IEnumerable Data => new List(); + public static MyTestClass[] DataArray => System.Array.Empty(); public static IEnumerable GetData() => new List(); + public static MyTestClass[] GetDataArray() => System.Array.Empty(); } public class SomeClass { public static IEnumerable SomeData => new List(); + public static MyTestClass[] SomeDataArray => System.Array.Empty(); public static IEnumerable GetSomeData() => new List(); + public static MyTestClass[] GetSomeDataArray() => System.Array.Empty(); } """; @@ -511,7 +667,15 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(4).WithArguments("MyTestClass", "GetData"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(5).WithArguments("SomeClass", "GetSomeData"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(6).WithArguments("MyTestClass", "GetData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(7).WithArguments("SomeClass", "GetSomeData")); + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(7).WithArguments("SomeClass", "GetSomeData"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(8).WithArguments("MyTestClass", "DataArray"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(9).WithArguments("SomeClass", "SomeDataArray"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(10).WithArguments("MyTestClass", "DataArray"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(11).WithArguments("SomeClass", "SomeDataArray"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(12).WithArguments("MyTestClass", "GetDataArray"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(13).WithArguments("SomeClass", "GetSomeDataArray"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(14).WithArguments("MyTestClass", "GetDataArray"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(15).WithArguments("SomeClass", "GetSomeDataArray")); } public async Task MemberIsNotStatic_Diagnostic() @@ -547,7 +711,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(1).WithArguments("MyTestClass", "GetData")); } - public async Task MemberIsNotPublic_Diagnostic() + public async Task MemberIsNotPublic_NoDiagnostic() { string code = """ using System; @@ -557,13 +721,13 @@ public async Task MemberIsNotPublic_Diagnostic() [TestClass] public class MyTestClass { - [{|#0:DynamicData("Data")|}] + [DynamicData("Data")] [TestMethod] public void TestMethod1(object[] o) { } - [{|#1:DynamicData("GetData", DynamicDataSourceType.Method)|}] + [DynamicData("GetData", DynamicDataSourceType.Method)] [TestMethod] public void TestMethod2(object[] o) { @@ -574,10 +738,7 @@ public void TestMethod2(object[] o) } """; - await VerifyCS.VerifyAnalyzerAsync( - code, - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(0).WithArguments("MyTestClass", "Data"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(1).WithArguments("MyTestClass", "GetData")); + await VerifyCS.VerifyAnalyzerAsync(code); } public async Task MethodHasParameters_Diagnostic() diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs index cad0ffead4..a743894786 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs @@ -162,4 +162,28 @@ public void TestMethod1() await VerifyCS.VerifyAnalyzerAsync(code); } + + public async Task WhenClassHasTestInitializeAndThenTestMethod_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class [|TestClass|] + { + [TestInitialize] + public void Initialize() + { + + } + + [TestMethod] + public void TestMethod1() + { + + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj index ef4a287f2f..576e802897 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj @@ -34,6 +34,7 @@ + diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj b/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj index aa25491031..46dc781e59 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj @@ -26,6 +26,7 @@ + diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs index 778d05afda..182d955119 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs @@ -47,7 +47,7 @@ public void OutputFormattingIsCorrect() var stringBuilderConsole = new StringBuilderConsole(); var terminalReporter = new TerminalTestReporter(stringBuilderConsole, new TerminalTestReporterOptions { - ShowPassedTests = true, + ShowPassedTests = () => true, UseAnsi = true, ForceAnsi = true, @@ -70,16 +70,16 @@ public void OutputFormattingIsCorrect() terminalReporter.AssemblyRunStarted(assembly, targetFramework, architecture); terminalReporter.TestCompleted(assembly, targetFramework, architecture, "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), - errorMessage: null, errorStackTrace: null, expected: null, actual: null); + errorMessage: null, exception: null, expected: null, actual: null); terminalReporter.TestCompleted(assembly, targetFramework, architecture, "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), - errorMessage: null, errorStackTrace: null, expected: null, actual: null); + errorMessage: null, exception: null, expected: null, actual: null); // timed out + cancelled + failed should all report as failed in summary terminalReporter.TestCompleted(assembly, targetFramework, architecture, "TimedoutTest1", TestOutcome.Timeout, TimeSpan.FromSeconds(10), - errorMessage: null, errorStackTrace: null, expected: null, actual: null); + errorMessage: null, exception: null, expected: null, actual: null); terminalReporter.TestCompleted(assembly, targetFramework, architecture, "CanceledTest1", TestOutcome.Canceled, TimeSpan.FromSeconds(10), - errorMessage: null, errorStackTrace: null, expected: null, actual: null); + errorMessage: null, exception: null, expected: null, actual: null); terminalReporter.TestCompleted(assembly, targetFramework, architecture, "FailedTest1", TestOutcome.Fail, TimeSpan.FromSeconds(10), - errorMessage: "Tests failed", errorStackTrace: @$" at FailingTest() in {folder}codefile.cs:line 10", expected: "ABC", actual: "DEF"); + errorMessage: "Tests failed", exception: new StackTraceException(@$" at FailingTest() in {folder}codefile.cs:line 10"), expected: "ABC", actual: "DEF"); terminalReporter.ArtifactAdded(outOfProcess: true, assembly, targetFramework, architecture, testName: null, @$"{folder}artifact1.txt"); terminalReporter.ArtifactAdded(outOfProcess: false, assembly, targetFramework, architecture, testName: null, @$"{folder}artifact2.txt"); terminalReporter.AssemblyRunCompleted(assembly, targetFramework, architecture); @@ -88,40 +88,36 @@ public void OutputFormattingIsCorrect() string output = stringBuilderConsole.Output; string expected = $""" - ␛[32;1mpassed␛[m PassedTest1␛[90;1m ␛[90;1m(10s 000ms)␛[m - ␛[33;1mskipped␛[m SkippedTest1␛[90;1m ␛[90;1m(10s 000ms)␛[m - ␛[31;1mfailed (canceled)␛[m TimedoutTest1␛[90;1m ␛[90;1m(10s 000ms)␛[m - ␛[31;1mfailed (canceled)␛[m CanceledTest1␛[90;1m ␛[90;1m(10s 000ms)␛[m - ␛[31;1mfailed␛[m FailedTest1␛[90;1m ␛[90;1m(10s 000ms)␛[m - ␛[91;1m Tests failed - ␛[m␛[91;1m Expected + ␛[92mpassed␛[m PassedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[93mskipped␛[m SkippedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[91mfailed (canceled)␛[m TimedoutTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[91mfailed (canceled)␛[m CanceledTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[91mfailed␛[m FailedTest1␛[90m ␛[90m(10s 000ms)␛[m + ␛[91m Tests failed + ␛[m␛[91m Expected ABC Actual DEF - ␛[m␛[31;1m Stack Trace: - ␛[90;1mat ␛[m␛[91;1mFailingTest()␛[90;1m in ␛[90;1m␛]8;;file:///{folderLink}codefile.cs␛\{folder}codefile.cs:10␛]8;;␛\␛[m - + ␛[m␛[90m ␛[90mat ␛[m␛[91mFailingTest()␛[90m in ␛[90m␛]8;;file:///{folderLink}codefile.cs␛\{folder}codefile.cs:10␛]8;;␛\␛[m + ␛[m Out of process file artifacts produced: - - ␛[90;1m␛]8;;file:///{folderLink}artifact1.txt␛\{folder}artifact1.txt␛]8;;␛\␛[m + - ␛[90m␛]8;;file:///{folderLink}artifact1.txt␛\{folder}artifact1.txt␛]8;;␛\␛[m In process file artifacts produced: - - ␛[90;1m␛]8;;file:///{folderLink}artifact2.txt␛\{folder}artifact2.txt␛]8;;␛\␛[m - ␛[91;1mTest run summary: Failed!␛[90;1m - ␛[m␛[90;1m␛]8;;file:///{folderLinkNoSlash}␛\{folder}assembly.dll␛]8;;␛\␛[m (net8.0|x64) + - ␛[90m␛]8;;file:///{folderLink}artifact2.txt␛\{folder}artifact2.txt␛]8;;␛\␛[m + ␛[91mTest run summary: Failed!␛[90m - ␛[m␛[90m␛]8;;file:///{folderLinkNoSlash}␛\{folder}assembly.dll␛]8;;␛\␛[m (net8.0|x64) ␛[m total: 5 - ␛[91;1m failed: 3 + ␛[91m failed: 3 ␛[m succeeded: 1 skipped: 1 duration: 3652058d 23h 59m 59s 999ms """; - EnsureAnsiMatch(expected, output); + Assert.AreEqual(expected, ShowEscape(output)); } - private void EnsureAnsiMatch(string expected, string actual) - => Assert.AreEqual(expected, ShowEscape(actual)); - - private string? ShowEscape(string? text) + private static string? ShowEscape(string? text) { string visibleEsc = "\x241b"; return text?.Replace(AnsiCodes.Esc, visibleEsc); @@ -229,4 +225,11 @@ public void SetColor(TerminalColor color) public void StopUpdate() => throw new NotImplementedException(); } + + private class StackTraceException : Exception + { + public StackTraceException(string stackTrace) => StackTrace = stackTrace; + + public override string? StackTrace { get; } + } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs index c62ae65727..ce6d52bf0d 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs @@ -152,7 +152,7 @@ private static void AssertSerialize(Type type, string instanceSerialized) if (type == typeof(TelemetryEventArgs)) { - Assert.AreEqual("""{"EventName":"eventName","metrics":{"key":1}}""".Replace(" ", string.Empty), instanceSerialized, because); + Assert.AreEqual("""{"eventName":"eventName","metrics":{"key":1}}""".Replace(" ", string.Empty), instanceSerialized, because); return; } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs index 90fc478266..d66cd424f7 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs @@ -55,6 +55,7 @@ public async Task TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(str loggerFactoryMock.Setup(f => f.CreateLogger(It.IsAny())).Returns(new Mock().Object); TelemetryManager telemetryManager = new(); + telemetryManager.AddTelemetryCollectorProvider(_ => new NopTelemetryService(false)); // Act environmentMock.Setup(e => e.GetEnvironmentVariable(variable)).Returns(value); @@ -107,6 +108,7 @@ public async Task TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(st loggerFactoryMock.Setup(f => f.CreateLogger(It.IsAny())).Returns(new Mock().Object); TelemetryManager telemetryManager = new(); + telemetryManager.AddTelemetryCollectorProvider(_ => new NopTelemetryService(false)); // Act environmentMock.Setup(e => e.GetEnvironmentVariable(variable)).Returns(value); @@ -156,6 +158,7 @@ public async Task TelemetryManager_SentinelIsWrittenPerUserAndAvoidsShowingNotic loggerFactoryMock.Setup(f => f.CreateLogger(It.IsAny())).Returns(new Mock().Object); TelemetryManager telemetryManager = new(); + telemetryManager.AddTelemetryCollectorProvider(_ => new NopTelemetryService(false)); // Act await telemetryManager.BuildAsync(serviceProvider, loggerFactoryMock.Object, options); @@ -217,6 +220,7 @@ public async Task TelemetryManager_SentinelIsWrittenOnlyWhenUserWouldSeeTheMessa loggerFactoryMock.Setup(f => f.CreateLogger(It.IsAny())).Returns(new Mock().Object); TelemetryManager telemetryManager = new(); + telemetryManager.AddTelemetryCollectorProvider(_ => new NopTelemetryService(false)); // Act // Disable showing the telemetry message. diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs index e2dfd93e21..3be3175bce 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs @@ -198,6 +198,27 @@ public void CollectionAssertAreEqualComparerNullabilityPostConditions() comparer.ToString(); // no warning } + public void CollectionAssertAreEqual_WithIgnoreCaseComparer_DoesNotThrow() + { + List expected = ["one", "two"]; + List actual = ["ONE", "tWo"]; + CollectionAssert.AreEqual(expected, actual, StringComparer.OrdinalIgnoreCase); + } + + public void CollectionAssertAreEqual_WithNestedDiffSizedArrays_Fails() + { + int[][] expected = [[1, 2], [3, 4], [5, 6], [7, 8], [9]]; + int[][] actual = [[1, 2], [999, 999, 999, 999, 999], [5, 6], [], [9]]; + VerifyThrows(() => CollectionAssert.AreEqual(expected, actual)); + } + + public void CollectionAssertAreEqual_WithCaseSensetiveComparer_Fails() + { + List expected = ["one", "two"]; + List actual = ["ONE", "tWo"]; + VerifyThrows(() => CollectionAssert.AreEqual(expected, actual, StringComparer.Ordinal)); + } + public void CollectionAssertAreEqualComparerMessageNullabilityPostConditions() { ICollection? collection1 = GetCollection(); @@ -264,6 +285,20 @@ public void CollectionAssertAreNotEqual_NotEqualNestedLists_Passes() CollectionAssert.AreNotEqual(collection1, collection2); } + public void CollectionAssertAreNotEqual_WithIgnoreCaseComparer_Fails() + { + List expected = ["one", "two"]; + List actual = ["ONE", "tWo"]; + VerifyThrows(() => CollectionAssert.AreNotEqual(expected, actual, StringComparer.OrdinalIgnoreCase)); + } + + public void CollectionAssertAreNotEqual_WithCaseSensitiveComparer_Passes() + { + List expected = ["one", "two"]; + List actual = ["ONE", "tWo"]; + CollectionAssert.AreNotEqual(expected, actual, StringComparer.Ordinal); + } + public void CollectionAssertAreNotEqual_EqualNestedLists_Fails() { ICollection? collection1 = GetNestedLists();