From 3f952bf8cd0eed63ac7fbd488d5de95964cd85bc Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 12 Apr 2024 16:09:04 -0700 Subject: [PATCH 01/69] Clone VerifyBase -> Verifier --- src/Verify.MSTest/DerivePaths/Verifier.cs | 33 +++++ src/Verify.MSTest/DerivePaths/VerifyBase.cs | 4 +- src/Verify.MSTest/UsesVerifyAttribute.cs | 6 + src/Verify.MSTest/Verifier.cs | 130 ++++++++++++++++++++ src/Verify.MSTest/Verifier_Archive.cs | 45 +++++++ src/Verify.MSTest/Verifier_Directory.cs | 69 +++++++++++ src/Verify.MSTest/Verifier_File.cs | 27 ++++ src/Verify.MSTest/Verifier_Json.cs | 46 +++++++ src/Verify.MSTest/Verifier_Object.cs | 45 +++++++ src/Verify.MSTest/Verifier_Stream.cs | 94 ++++++++++++++ src/Verify.MSTest/Verifier_String.cs | 34 +++++ src/Verify.MSTest/Verifier_Throws.cs | 46 +++++++ src/Verify.MSTest/Verifier_Tuple.cs | 11 ++ src/Verify.MSTest/Verifier_Xml.cs | 46 +++++++ 14 files changed, 634 insertions(+), 2 deletions(-) create mode 100644 src/Verify.MSTest/DerivePaths/Verifier.cs create mode 100644 src/Verify.MSTest/UsesVerifyAttribute.cs create mode 100644 src/Verify.MSTest/Verifier.cs create mode 100644 src/Verify.MSTest/Verifier_Archive.cs create mode 100644 src/Verify.MSTest/Verifier_Directory.cs create mode 100644 src/Verify.MSTest/Verifier_File.cs create mode 100644 src/Verify.MSTest/Verifier_Json.cs create mode 100644 src/Verify.MSTest/Verifier_Object.cs create mode 100644 src/Verify.MSTest/Verifier_Stream.cs create mode 100644 src/Verify.MSTest/Verifier_String.cs create mode 100644 src/Verify.MSTest/Verifier_Throws.cs create mode 100644 src/Verify.MSTest/Verifier_Tuple.cs create mode 100644 src/Verify.MSTest/Verifier_Xml.cs diff --git a/src/Verify.MSTest/DerivePaths/Verifier.cs b/src/Verify.MSTest/DerivePaths/Verifier.cs new file mode 100644 index 000000000..2fb910d30 --- /dev/null +++ b/src/Verify.MSTest/DerivePaths/Verifier.cs @@ -0,0 +1,33 @@ +// ReSharper disable UnusedParameter.Local + +#pragma warning disable VerifyTestsProjectDir +namespace VerifyMSTest; + +public partial class Verifier +{ + static DerivePathInfo derivePathInfo = PathInfo.DeriveDefault; + + internal static PathInfo GetPathInfo(string sourceFile, Type type, MethodInfo method) => + derivePathInfo(sourceFile, VerifierSettings.ProjectDir, type, method); + + /// + /// Use custom path information for `.verified.` files. + /// + /// + /// This is sometimes needed on CI systems that move/remove the original source. + /// To move to this approach, any existing `.verified.` files will need to be moved to the new directory + /// + /// Custom callback to control the behavior. + public static void DerivePathInfo(DerivePathInfo derivePathInfo) => + Verifier.derivePathInfo = derivePathInfo; + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + public static void UseProjectRelativeDirectory(string directory) => + DerivePathInfo( + (sourceFile, projectDirectory, type, method) => new( + directory: Path.Combine(projectDirectory, directory), + typeName: type.NameWithParent(), + methodName: method.Name)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/DerivePaths/VerifyBase.cs b/src/Verify.MSTest/DerivePaths/VerifyBase.cs index 07a368ec6..5cec86aa5 100644 --- a/src/Verify.MSTest/DerivePaths/VerifyBase.cs +++ b/src/Verify.MSTest/DerivePaths/VerifyBase.cs @@ -1,4 +1,4 @@ -// ReSharper disable UnusedParameter.Local +// ReSharper disable UnusedParameter.Local #pragma warning disable VerifyTestsProjectDir namespace VerifyMSTest; @@ -30,4 +30,4 @@ public static void UseProjectRelativeDirectory(string directory) => directory: Path.Combine(projectDirectory, directory), typeName: type.NameWithParent(), methodName: method.Name)); -} \ No newline at end of file +} diff --git a/src/Verify.MSTest/UsesVerifyAttribute.cs b/src/Verify.MSTest/UsesVerifyAttribute.cs new file mode 100644 index 000000000..3b13acf50 --- /dev/null +++ b/src/Verify.MSTest/UsesVerifyAttribute.cs @@ -0,0 +1,6 @@ +namespace VerifyMSTest; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] +public sealed class UsesVerifyAttribute : Attribute +{ +} diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs new file mode 100644 index 000000000..ae54ae85b --- /dev/null +++ b/src/Verify.MSTest/Verifier.cs @@ -0,0 +1,130 @@ +namespace VerifyMSTest; + +[TestClass] +public abstract partial class Verifier +{ + static Task AddFile(FilePair path, bool autoVerify) + { + var context = currentTestContext.Value; + if (context != null) + { + var fileName = autoVerify ? path.VerifiedPath : path.ReceivedPath; + context.AddResultFile(fileName); + } + + return Task.CompletedTask; + } + + static Verifier() + { + VerifierSettings.OnFirstVerify((pair, _, autoVerify) => AddFile(pair, autoVerify)); + VerifierSettings.OnVerifyMismatch((pair, _, autoVerify) => AddFile(pair, autoVerify)); + } + + TestContext testContext = null!; + + public TestContext TestContext + { + get => testContext; + init + { + testContext = value; + currentTestContext.Value = value; + } + } + + static AsyncLocal currentTestContext = new(); + + InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, bool useUniqueDirectory) + { + var type = GetType(); + + if (useUniqueDirectory) + { + settings.UseUniqueDirectory(); + } + + var testName = TestContext.TestName; + if (testName == null) + { + throw new("TestContext.TestName is null. Ensure being used inside a test"); + } + + var testNameSpan = testName.AsSpan(); + var indexOf = testNameSpan.IndexOf('('); + if (indexOf > 0) + { + testNameSpan = testNameSpan[..indexOf]; + } + + indexOf = testNameSpan.IndexOf('.'); + if (indexOf > 0) + { + testNameSpan = testNameSpan[(indexOf + 1)..]; + } + + VerifierSettings.AssignTargetAssembly(type.Assembly); + var method = FindMethod(type, testNameSpan); + + var pathInfo = GetPathInfo(sourceFile, type, method); + return new( + sourceFile, + settings, + type.NameWithParent(), + method.Name, + method.ParameterNames(), + pathInfo); + } + + static MethodInfo FindMethod(Type type, ReadOnlySpan testName) + { + foreach (var method in type + .GetMethods(BindingFlags.Instance | BindingFlags.Public)) + { + if (testName.SequenceEqual(method.Name)) + { + return method; + } + } + + throw new($"Could not find method `{type.Name}.{testName.ToString()}`."); + } + + [Pure] + public SettingsTask Verify( + object? target, + IEnumerable rawTargets, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(target, rawTargets)); + + [Pure] + public SettingsTask Verify( + IEnumerable targets, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(targets)); + + [Pure] + public SettingsTask Verify( + Target target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(target)); + + SettingsTask Verify( + VerifySettings? settings, + string sourceFile, + Func> verify, + bool useUniqueDirectory = false) + { + Guard.AgainstBadSourceFile(sourceFile); + return new( + settings, + async verifySettings => + { + using var verifier = BuildVerifier(verifySettings, sourceFile, useUniqueDirectory); + return await verify(verifier); + }); + } +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Archive.cs b/src/Verify.MSTest/Verifier_Archive.cs new file mode 100644 index 000000000..c40f6620a --- /dev/null +++ b/src/Verify.MSTest/Verifier_Archive.cs @@ -0,0 +1,45 @@ +using System.IO.Compression; + +namespace VerifyMSTest; + +public partial class Verifier +{ + /// + /// Verifies the contents of a + /// + [Pure] + public SettingsTask Verify( + ZipArchive archive, + Func? include = null, + VerifySettings? settings = null, + object? info = null, + FileScrubber? fileScrubber = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyZip(archive, include, info, fileScrubber), true); + + /// + /// Verifies the contents of a + /// + [Pure] + public SettingsTask VerifyZip( + string path, + Func? include = null, + VerifySettings? settings = null, + object? info = null, + FileScrubber? fileScrubber = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyZip(path, include, info, fileScrubber), true); + + /// + /// Verifies the contents of a + /// + [Pure] + public SettingsTask VerifyZip( + Stream stream, + Func? include = null, + VerifySettings? settings = null, + object? info = null, + FileScrubber? fileScrubber = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyZip(stream, include, info, fileScrubber), true); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Directory.cs b/src/Verify.MSTest/Verifier_Directory.cs new file mode 100644 index 000000000..845b5aaef --- /dev/null +++ b/src/Verify.MSTest/Verifier_Directory.cs @@ -0,0 +1,69 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ +#if NET5_0_OR_GREATER + + /// + /// Verifies the contents of . + /// + [Pure] + public SettingsTask VerifyDirectory( + string path, + Func? include = null, + string? pattern = null, + EnumerationOptions? options = null, + VerifySettings? settings = null, + object? info = null, + FileScrubber? fileScrubber = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyDirectory(path, include, pattern, options, info, fileScrubber), true); + + /// + /// Verifies the contents of . + /// Differs from passing to Verify(object? target) which will verify the full path. + /// + [Pure] + public SettingsTask VerifyDirectory( + DirectoryInfo path, + Func? include = null, + string? pattern = null, + EnumerationOptions? options = null, + VerifySettings? settings = null, + object? info = null, + FileScrubber? fileScrubber = null, + [CallerFilePath] string sourceFile = "") => + VerifyDirectory(path.FullName, include, pattern, options, settings, info, fileScrubber, sourceFile); +#else + /// + /// Verifies the contents of . + /// + [Pure] + public SettingsTask VerifyDirectory( + string path, + Func? include = null, + string? pattern = null, + SearchOption option = SearchOption.AllDirectories, + VerifySettings? settings = null, + object? info = null, + FileScrubber? fileScrubber = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyDirectory(path, include, pattern, option, info, fileScrubber), true); + + /// + /// Verifies the contents of . + /// Differs from passing to Verify(object? target) which will verify the full path. + /// + [Pure] + public SettingsTask VerifyDirectory( + DirectoryInfo path, + Func? include = null, + string? pattern = null, + SearchOption option = SearchOption.AllDirectories, + VerifySettings? settings = null, + object? info = null, + FileScrubber? fileScrubber = null, + [CallerFilePath] string sourceFile = "") => + VerifyDirectory(path.FullName, include, pattern, option, settings, info, fileScrubber, sourceFile); +#endif +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_File.cs b/src/Verify.MSTest/Verifier_File.cs new file mode 100644 index 000000000..0ef66de7b --- /dev/null +++ b/src/Verify.MSTest/Verifier_File.cs @@ -0,0 +1,27 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + /// + /// Verifies the contents of . + /// + [Pure] + public SettingsTask VerifyFile( + string path, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyFile(path, info)); + + /// + /// Verifies the contents of . + /// Differs from passing to Verify(object? target) which will verify the full path. + /// + [Pure] + public SettingsTask VerifyFile( + FileInfo path, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyFile(path, info)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Json.cs b/src/Verify.MSTest/Verifier_Json.cs new file mode 100644 index 000000000..91d7e6d9a --- /dev/null +++ b/src/Verify.MSTest/Verifier_Json.cs @@ -0,0 +1,46 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + [Pure] + public SettingsTask VerifyJson( + string? target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyJson(target)); + + [Pure] + public SettingsTask VerifyJson( + Task target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyJson(target)); + + [Pure] + public SettingsTask VerifyJson( + ValueTask target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyJson(target)); + + [Pure] + public SettingsTask VerifyJson( + Stream? target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyJson(target)); + + [Pure] + public SettingsTask VerifyJson( + Task target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyJson(target)); + + [Pure] + public SettingsTask VerifyJson( + ValueTask target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyJson(target)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Object.cs b/src/Verify.MSTest/Verifier_Object.cs new file mode 100644 index 000000000..5bc96eb03 --- /dev/null +++ b/src/Verify.MSTest/Verifier_Object.cs @@ -0,0 +1,45 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + [Pure] + public SettingsTask Verify( + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify()); + + [Pure] + public SettingsTask Verify( + Func> target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(target())); + + [Pure] + public SettingsTask Verify( + Task target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(target)); + + [Pure] + public SettingsTask Verify( + ValueTask target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(target)); + + [Pure] + public SettingsTask Verify( + IAsyncEnumerable target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(target)); + + [Pure] + public SettingsTask Verify( + object? target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Verify(target)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Stream.cs b/src/Verify.MSTest/Verifier_Stream.cs new file mode 100644 index 000000000..49d149303 --- /dev/null +++ b/src/Verify.MSTest/Verifier_Stream.cs @@ -0,0 +1,94 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + [Pure] + public SettingsTask Verify( + byte[]? target, + string extension, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + + [Pure] + public SettingsTask Verify( + byte[]? target, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); + + [Pure] + public SettingsTask Verify( + Task target, + string extension, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + + [Pure] + public SettingsTask Verify( + ValueTask target, + string extension, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + + [Pure] + public SettingsTask Verify( + FileStream? target, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); + + [Pure] + public SettingsTask Verify( + Stream? target, + string extension, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + + [Pure] + public SettingsTask Verify( + Stream? target, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); + + [Pure] + public SettingsTask Verify( + Task target, + string extension, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") + where T : Stream => + Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + + [Pure] + public SettingsTask Verify( + ValueTask target, + string extension, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") + where T : Stream => + Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + + [Pure] + public SettingsTask Verify( + IEnumerable targets, + string extension, + VerifySettings? settings = null, + object? info = null, + [CallerFilePath] string sourceFile = "") + where T : Stream => + Verify(settings, sourceFile, _ => _.VerifyStreams(targets, extension, info)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_String.cs b/src/Verify.MSTest/Verifier_String.cs new file mode 100644 index 000000000..dbf100505 --- /dev/null +++ b/src/Verify.MSTest/Verifier_String.cs @@ -0,0 +1,34 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + [Pure] + public SettingsTask Verify( + string? target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyString(target)); + + [Pure] + public SettingsTask Verify( + Task target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyString(target)); + + [Pure] + public SettingsTask Verify( + string? target, + string extension, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyString(target, extension)); + + [Pure] + public SettingsTask Verify( + Task target, + string extension, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyString(target, extension)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Throws.cs b/src/Verify.MSTest/Verifier_Throws.cs new file mode 100644 index 000000000..dcf7946c4 --- /dev/null +++ b/src/Verify.MSTest/Verifier_Throws.cs @@ -0,0 +1,46 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + [Pure] + public SettingsTask Throws( + Action target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Throws(target)); + + [Pure] + public SettingsTask Throws( + Func target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.Throws(target)); + + [Pure] + public SettingsTask ThrowsTask( + Func target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.ThrowsTask(target)); + + [Pure] + public SettingsTask ThrowsTask( + Func> target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.ThrowsTask(target)); + + [Pure] + public SettingsTask ThrowsValueTask( + Func target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.ThrowsValueTask(target)); + + [Pure] + public SettingsTask ThrowsValueTask( + Func> target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.ThrowsValueTask(target)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Tuple.cs b/src/Verify.MSTest/Verifier_Tuple.cs new file mode 100644 index 000000000..6a4a3f953 --- /dev/null +++ b/src/Verify.MSTest/Verifier_Tuple.cs @@ -0,0 +1,11 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + [Pure] + public SettingsTask VerifyTuple( + Expression> target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyTuple(target)); +} \ No newline at end of file diff --git a/src/Verify.MSTest/Verifier_Xml.cs b/src/Verify.MSTest/Verifier_Xml.cs new file mode 100644 index 000000000..3574328fb --- /dev/null +++ b/src/Verify.MSTest/Verifier_Xml.cs @@ -0,0 +1,46 @@ +namespace VerifyMSTest; + +public partial class Verifier +{ + [Pure] + public SettingsTask VerifyXml( + string? target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyXml(target)); + + [Pure] + public SettingsTask VerifyXml( + Task target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyXml(target)); + + [Pure] + public SettingsTask VerifyXml( + ValueTask target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyXml(target)); + + [Pure] + public SettingsTask VerifyXml( + Stream? target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyXml(target)); + + [Pure] + public SettingsTask VerifyXml( + Task target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyXml(target)); + + [Pure] + public SettingsTask VerifyXml( + ValueTask target, + VerifySettings? settings = null, + [CallerFilePath] string sourceFile = "") => + Verify(settings, sourceFile, _ => _.VerifyXml(target)); +} \ No newline at end of file From 0b41bfbb89b26cd447c228cbcc520da6e4a262a9 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 12 Apr 2024 17:45:07 -0700 Subject: [PATCH 02/69] Forward VerifyBase calls to Verifier --- src/Verify.MSTest.Tests/Tests.cs | 22 +++- src/Verify.MSTest/DerivePaths/Verifier.cs | 2 +- src/Verify.MSTest/Verifier.cs | 54 ++++---- src/Verify.MSTest/Verifier_Archive.cs | 8 +- src/Verify.MSTest/Verifier_Directory.cs | 10 +- src/Verify.MSTest/Verifier_File.cs | 6 +- src/Verify.MSTest/Verifier_Json.cs | 14 +- src/Verify.MSTest/Verifier_Object.cs | 14 +- src/Verify.MSTest/Verifier_Stream.cs | 22 ++-- src/Verify.MSTest/Verifier_String.cs | 10 +- src/Verify.MSTest/Verifier_Throws.cs | 14 +- src/Verify.MSTest/Verifier_Tuple.cs | 4 +- src/Verify.MSTest/Verifier_Xml.cs | 14 +- src/Verify.MSTest/VerifyBase.cs | 121 +++--------------- src/Verify.MSTest/VerifyBase_Archive.cs | 12 +- src/Verify.MSTest/VerifyBase_Directory.cs | 13 +- src/Verify.MSTest/VerifyBase_File.cs | 10 +- src/Verify.MSTest/VerifyBase_Json.cs | 18 +-- src/Verify.MSTest/VerifyBase_Object.cs | 18 +-- src/Verify.MSTest/VerifyBase_Stream.cs | 26 ++-- src/Verify.MSTest/VerifyBase_String.cs | 14 +- src/Verify.MSTest/VerifyBase_Throws.cs | 18 +-- src/Verify.MSTest/VerifyBase_Tuple.cs | 8 +- src/Verify.MSTest/VerifyBase_Xml.cs | 18 +-- .../buildTransitive/Verify.MSTest.props | 1 + 25 files changed, 213 insertions(+), 258 deletions(-) diff --git a/src/Verify.MSTest.Tests/Tests.cs b/src/Verify.MSTest.Tests/Tests.cs index 32a8076d4..9dbbac36d 100644 --- a/src/Verify.MSTest.Tests/Tests.cs +++ b/src/Verify.MSTest.Tests/Tests.cs @@ -1,14 +1,28 @@ -// ReSharper disable UnusedParameter.Local +// TODO: Add a test that uses the base class to prevent regressions until it's deleted +// TODO: Add a test for when the [TestClass] already has a TestContext property + +// TODO: Make this in generator +partial class Tests +{ + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Injected by the test framework per test.")] + public TestContext TestContext + { + get => CurrentTestContext.Value!; + set => CurrentTestContext.Value = value; + + } +} + [TestClass] -public class Tests : - VerifyBase +[UsesVerify] +public partial class Tests { // ReSharper disable once UnusedMember.Local static void DerivePathInfo() { #region DerivePathInfoMSTest - DerivePathInfo( + Verifier.DerivePathInfo( (sourceFile, projectDirectory, type, method) => new( directory: Path.Combine(projectDirectory, "Snapshots"), typeName: type.Name, diff --git a/src/Verify.MSTest/DerivePaths/Verifier.cs b/src/Verify.MSTest/DerivePaths/Verifier.cs index 2fb910d30..1056ce160 100644 --- a/src/Verify.MSTest/DerivePaths/Verifier.cs +++ b/src/Verify.MSTest/DerivePaths/Verifier.cs @@ -3,7 +3,7 @@ #pragma warning disable VerifyTestsProjectDir namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { static DerivePathInfo derivePathInfo = PathInfo.DeriveDefault; diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index ae54ae85b..c5ce613cd 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -1,11 +1,10 @@ namespace VerifyMSTest; -[TestClass] -public abstract partial class Verifier +public static partial class Verifier { static Task AddFile(FilePair path, bool autoVerify) { - var context = currentTestContext.Value; + var context = CurrentTestContext.Value; if (context != null) { var fileName = autoVerify ? path.VerifiedPath : path.ReceivedPath; @@ -21,30 +20,19 @@ static Verifier() VerifierSettings.OnVerifyMismatch((pair, _, autoVerify) => AddFile(pair, autoVerify)); } - TestContext testContext = null!; + public static readonly AsyncLocal CurrentTestContext = new(); - public TestContext TestContext + static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, bool useUniqueDirectory) { - get => testContext; - init - { - testContext = value; - currentTestContext.Value = value; - } - } - - static AsyncLocal currentTestContext = new(); - - InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, bool useUniqueDirectory) - { - var type = GetType(); + var typeName = CurrentTestContext.Value?.FullyQualifiedTestClassName; + var type = FindType(typeName.AsSpan()); if (useUniqueDirectory) { settings.UseUniqueDirectory(); } - var testName = TestContext.TestName; + var testName = CurrentTestContext.Value?.TestName; if (testName == null) { throw new("TestContext.TestName is null. Ensure being used inside a test"); @@ -76,6 +64,26 @@ InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, bool use pathInfo); } + static Type FindType(ReadOnlySpan typeName) + { + // TODO: Either + // 1. Switch to the other DerivePathInfo that uses names (e.g. from Expecto) and avoid type lookups + // 2. Add a cache here to speed up the lookup + var types = AppDomain.CurrentDomain.GetAssemblies() + .Reverse() + .SelectMany(assembly => assembly.GetTypes()); + + foreach (var type in types) + { + if (typeName.SequenceEqual(type.FullName)) + { + return type; + } + } + + throw new($"Type '{typeName}' from TestContext not found."); + } + static MethodInfo FindMethod(Type type, ReadOnlySpan testName) { foreach (var method in type @@ -91,7 +99,7 @@ static MethodInfo FindMethod(Type type, ReadOnlySpan testName) } [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( object? target, IEnumerable rawTargets, VerifySettings? settings = null, @@ -99,20 +107,20 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.Verify(target, rawTargets)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( IEnumerable targets, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Verify(targets)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Target target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Verify(target)); - SettingsTask Verify( + static SettingsTask Verify( VerifySettings? settings, string sourceFile, Func> verify, diff --git a/src/Verify.MSTest/Verifier_Archive.cs b/src/Verify.MSTest/Verifier_Archive.cs index c40f6620a..dfe517050 100644 --- a/src/Verify.MSTest/Verifier_Archive.cs +++ b/src/Verify.MSTest/Verifier_Archive.cs @@ -2,13 +2,13 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { /// /// Verifies the contents of a /// [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( ZipArchive archive, Func? include = null, VerifySettings? settings = null, @@ -21,7 +21,7 @@ public SettingsTask Verify( /// Verifies the contents of a /// [Pure] - public SettingsTask VerifyZip( + public static SettingsTask VerifyZip( string path, Func? include = null, VerifySettings? settings = null, @@ -34,7 +34,7 @@ public SettingsTask VerifyZip( /// Verifies the contents of a /// [Pure] - public SettingsTask VerifyZip( + public static SettingsTask VerifyZip( Stream stream, Func? include = null, VerifySettings? settings = null, diff --git a/src/Verify.MSTest/Verifier_Directory.cs b/src/Verify.MSTest/Verifier_Directory.cs index 845b5aaef..2dd4ff289 100644 --- a/src/Verify.MSTest/Verifier_Directory.cs +++ b/src/Verify.MSTest/Verifier_Directory.cs @@ -1,6 +1,6 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { #if NET5_0_OR_GREATER @@ -8,7 +8,7 @@ public partial class Verifier /// Verifies the contents of . /// [Pure] - public SettingsTask VerifyDirectory( + public static SettingsTask VerifyDirectory( string path, Func? include = null, string? pattern = null, @@ -24,7 +24,7 @@ public SettingsTask VerifyDirectory( /// Differs from passing to Verify(object? target) which will verify the full path. /// [Pure] - public SettingsTask VerifyDirectory( + public static SettingsTask VerifyDirectory( DirectoryInfo path, Func? include = null, string? pattern = null, @@ -39,7 +39,7 @@ public SettingsTask VerifyDirectory( /// Verifies the contents of . /// [Pure] - public SettingsTask VerifyDirectory( + public static SettingsTask VerifyDirectory( string path, Func? include = null, string? pattern = null, @@ -55,7 +55,7 @@ public SettingsTask VerifyDirectory( /// Differs from passing to Verify(object? target) which will verify the full path. /// [Pure] - public SettingsTask VerifyDirectory( + public static SettingsTask VerifyDirectory( DirectoryInfo path, Func? include = null, string? pattern = null, diff --git a/src/Verify.MSTest/Verifier_File.cs b/src/Verify.MSTest/Verifier_File.cs index 0ef66de7b..228d3d64f 100644 --- a/src/Verify.MSTest/Verifier_File.cs +++ b/src/Verify.MSTest/Verifier_File.cs @@ -1,12 +1,12 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { /// /// Verifies the contents of . /// [Pure] - public SettingsTask VerifyFile( + public static SettingsTask VerifyFile( string path, VerifySettings? settings = null, object? info = null, @@ -18,7 +18,7 @@ public SettingsTask VerifyFile( /// Differs from passing to Verify(object? target) which will verify the full path. /// [Pure] - public SettingsTask VerifyFile( + public static SettingsTask VerifyFile( FileInfo path, VerifySettings? settings = null, object? info = null, diff --git a/src/Verify.MSTest/Verifier_Json.cs b/src/Verify.MSTest/Verifier_Json.cs index 91d7e6d9a..930c4e4d7 100644 --- a/src/Verify.MSTest/Verifier_Json.cs +++ b/src/Verify.MSTest/Verifier_Json.cs @@ -1,44 +1,44 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { [Pure] - public SettingsTask VerifyJson( + public static SettingsTask VerifyJson( string? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyJson(target)); [Pure] - public SettingsTask VerifyJson( + public static SettingsTask VerifyJson( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyJson(target)); [Pure] - public SettingsTask VerifyJson( + public static SettingsTask VerifyJson( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyJson(target)); [Pure] - public SettingsTask VerifyJson( + public static SettingsTask VerifyJson( Stream? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyJson(target)); [Pure] - public SettingsTask VerifyJson( + public static SettingsTask VerifyJson( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyJson(target)); [Pure] - public SettingsTask VerifyJson( + public static SettingsTask VerifyJson( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => diff --git a/src/Verify.MSTest/Verifier_Object.cs b/src/Verify.MSTest/Verifier_Object.cs index 5bc96eb03..5b04bbf33 100644 --- a/src/Verify.MSTest/Verifier_Object.cs +++ b/src/Verify.MSTest/Verifier_Object.cs @@ -1,43 +1,43 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Verify()); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Func> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Verify(target())); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Verify(target)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Verify(target)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( IAsyncEnumerable target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Verify(target)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( object? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => diff --git a/src/Verify.MSTest/Verifier_Stream.cs b/src/Verify.MSTest/Verifier_Stream.cs index 49d149303..d93014356 100644 --- a/src/Verify.MSTest/Verifier_Stream.cs +++ b/src/Verify.MSTest/Verifier_Stream.cs @@ -1,9 +1,9 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( byte[]? target, string extension, VerifySettings? settings = null, @@ -12,7 +12,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( byte[]? target, VerifySettings? settings = null, object? info = null, @@ -20,7 +20,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Task target, string extension, VerifySettings? settings = null, @@ -29,7 +29,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( ValueTask target, string extension, VerifySettings? settings = null, @@ -38,7 +38,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( FileStream? target, VerifySettings? settings = null, object? info = null, @@ -46,7 +46,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Stream? target, string extension, VerifySettings? settings = null, @@ -55,7 +55,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Stream? target, VerifySettings? settings = null, object? info = null, @@ -63,7 +63,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Task target, string extension, VerifySettings? settings = null, @@ -73,7 +73,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( ValueTask target, string extension, VerifySettings? settings = null, @@ -83,7 +83,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( IEnumerable targets, string extension, VerifySettings? settings = null, diff --git a/src/Verify.MSTest/Verifier_String.cs b/src/Verify.MSTest/Verifier_String.cs index dbf100505..aa3f710c7 100644 --- a/src/Verify.MSTest/Verifier_String.cs +++ b/src/Verify.MSTest/Verifier_String.cs @@ -1,23 +1,23 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( string? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyString(target)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyString(target)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( string? target, string extension, VerifySettings? settings = null, @@ -25,7 +25,7 @@ public SettingsTask Verify( Verify(settings, sourceFile, _ => _.VerifyString(target, extension)); [Pure] - public SettingsTask Verify( + public static SettingsTask Verify( Task target, string extension, VerifySettings? settings = null, diff --git a/src/Verify.MSTest/Verifier_Throws.cs b/src/Verify.MSTest/Verifier_Throws.cs index dcf7946c4..b2a873119 100644 --- a/src/Verify.MSTest/Verifier_Throws.cs +++ b/src/Verify.MSTest/Verifier_Throws.cs @@ -1,44 +1,44 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { [Pure] - public SettingsTask Throws( + public static SettingsTask Throws( Action target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Throws(target)); [Pure] - public SettingsTask Throws( + public static SettingsTask Throws( Func target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.Throws(target)); [Pure] - public SettingsTask ThrowsTask( + public static SettingsTask ThrowsTask( Func target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.ThrowsTask(target)); [Pure] - public SettingsTask ThrowsTask( + public static SettingsTask ThrowsTask( Func> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.ThrowsTask(target)); [Pure] - public SettingsTask ThrowsValueTask( + public static SettingsTask ThrowsValueTask( Func target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.ThrowsValueTask(target)); [Pure] - public SettingsTask ThrowsValueTask( + public static SettingsTask ThrowsValueTask( Func> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => diff --git a/src/Verify.MSTest/Verifier_Tuple.cs b/src/Verify.MSTest/Verifier_Tuple.cs index 6a4a3f953..32abf2f4f 100644 --- a/src/Verify.MSTest/Verifier_Tuple.cs +++ b/src/Verify.MSTest/Verifier_Tuple.cs @@ -1,9 +1,9 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { [Pure] - public SettingsTask VerifyTuple( + public static SettingsTask VerifyTuple( Expression> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => diff --git a/src/Verify.MSTest/Verifier_Xml.cs b/src/Verify.MSTest/Verifier_Xml.cs index 3574328fb..c89158c4e 100644 --- a/src/Verify.MSTest/Verifier_Xml.cs +++ b/src/Verify.MSTest/Verifier_Xml.cs @@ -1,44 +1,44 @@ namespace VerifyMSTest; -public partial class Verifier +partial class Verifier { [Pure] - public SettingsTask VerifyXml( + public static SettingsTask VerifyXml( string? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyXml(target)); [Pure] - public SettingsTask VerifyXml( + public static SettingsTask VerifyXml( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyXml(target)); [Pure] - public SettingsTask VerifyXml( + public static SettingsTask VerifyXml( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyXml(target)); [Pure] - public SettingsTask VerifyXml( + public static SettingsTask VerifyXml( Stream? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyXml(target)); [Pure] - public SettingsTask VerifyXml( + public static SettingsTask VerifyXml( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => Verify(settings, sourceFile, _ => _.VerifyXml(target)); [Pure] - public SettingsTask VerifyXml( + public static SettingsTask VerifyXml( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => diff --git a/src/Verify.MSTest/VerifyBase.cs b/src/Verify.MSTest/VerifyBase.cs index 7801e33f1..07e0cb1ac 100644 --- a/src/Verify.MSTest/VerifyBase.cs +++ b/src/Verify.MSTest/VerifyBase.cs @@ -1,94 +1,23 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -[TestClass] -public abstract partial class VerifyBase +// TODO: This should be added by the generator +partial class VerifyBase { - static Task AddFile(FilePair path, bool autoVerify) - { - var context = currentTestContext.Value; - if (context != null) - { - var fileName = autoVerify ? path.VerifiedPath : path.ReceivedPath; - context.AddResultFile(fileName); - } - - return Task.CompletedTask; - } - - static VerifyBase() - { - VerifierSettings.OnFirstVerify((pair, _, autoVerify) => AddFile(pair, autoVerify)); - VerifierSettings.OnVerifyMismatch((pair, _, autoVerify) => AddFile(pair, autoVerify)); - } - - TestContext testContext = null!; - + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Set by the test framework per test.")] public TestContext TestContext { - get => testContext; - init - { - testContext = value; - currentTestContext.Value = value; - } + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } +} - static AsyncLocal currentTestContext = new(); - - InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, bool useUniqueDirectory) - { - var type = GetType(); - - if (useUniqueDirectory) - { - settings.UseUniqueDirectory(); - } - - var testName = TestContext.TestName; - if (testName == null) - { - throw new("TestContext.TestName is null. Ensure being used inside a test"); - } - - var testNameSpan = testName.AsSpan(); - var indexOf = testNameSpan.IndexOf('('); - if (indexOf > 0) - { - testNameSpan = testNameSpan[..indexOf]; - } - - indexOf = testNameSpan.IndexOf('.'); - if (indexOf > 0) - { - testNameSpan = testNameSpan[(indexOf + 1)..]; - } - - VerifierSettings.AssignTargetAssembly(type.Assembly); - var method = FindMethod(type, testNameSpan); - - var pathInfo = GetPathInfo(sourceFile, type, method); - return new( - sourceFile, - settings, - type.NameWithParent(), - method.Name, - method.ParameterNames(), - pathInfo); - } - - static MethodInfo FindMethod(Type type, ReadOnlySpan testName) - { - foreach (var method in type - .GetMethods(BindingFlags.Instance | BindingFlags.Public)) - { - if (testName.SequenceEqual(method.Name)) - { - return method; - } - } - - throw new($"Could not find method `{type.Name}.{testName.ToString()}`."); - } +// TODO: Mark as obsolete +//[Obsolete("Use [UsesVerify] instead.")] +[TestClass] +[UsesVerify] +public abstract partial class VerifyBase +{ +#pragma warning disable CA1822 // Mark members as static [Pure] public SettingsTask Verify( @@ -96,35 +25,19 @@ public SettingsTask Verify( IEnumerable rawTargets, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(target, rawTargets)); + Verifier.Verify(target, rawTargets, settings, sourceFile); [Pure] public SettingsTask Verify( IEnumerable targets, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(targets)); + Verifier.Verify(targets, settings, sourceFile); [Pure] public SettingsTask Verify( Target target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(target)); - - SettingsTask Verify( - VerifySettings? settings, - string sourceFile, - Func> verify, - bool useUniqueDirectory = false) - { - Guard.AgainstBadSourceFile(sourceFile); - return new( - settings, - async verifySettings => - { - using var verifier = BuildVerifier(verifySettings, sourceFile, useUniqueDirectory); - return await verify(verifier); - }); - } + Verifier.Verify(target, settings, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Archive.cs b/src/Verify.MSTest/VerifyBase_Archive.cs index ee0e094d7..ba3702167 100644 --- a/src/Verify.MSTest/VerifyBase_Archive.cs +++ b/src/Verify.MSTest/VerifyBase_Archive.cs @@ -1,9 +1,11 @@ -using System.IO.Compression; +using System.IO.Compression; namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + /// /// Verifies the contents of a /// @@ -15,7 +17,7 @@ public SettingsTask Verify( object? info = null, FileScrubber? fileScrubber = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyZip(archive, include, info, fileScrubber), true); + Verifier.Verify(archive, include, settings, info, fileScrubber, sourceFile); /// /// Verifies the contents of a @@ -28,7 +30,7 @@ public SettingsTask VerifyZip( object? info = null, FileScrubber? fileScrubber = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyZip(path, include, info, fileScrubber), true); + Verifier.VerifyZip(path, include, settings, info, fileScrubber, sourceFile); /// /// Verifies the contents of a @@ -41,5 +43,5 @@ public SettingsTask VerifyZip( object? info = null, FileScrubber? fileScrubber = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyZip(stream, include, info, fileScrubber), true); + Verifier.VerifyZip(stream, include, settings, info, fileScrubber, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Directory.cs b/src/Verify.MSTest/VerifyBase_Directory.cs index 98191613d..927d74152 100644 --- a/src/Verify.MSTest/VerifyBase_Directory.cs +++ b/src/Verify.MSTest/VerifyBase_Directory.cs @@ -1,7 +1,8 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static #if NET5_0_OR_GREATER /// @@ -17,7 +18,7 @@ public SettingsTask VerifyDirectory( object? info = null, FileScrubber? fileScrubber = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyDirectory(path, include, pattern, options, info, fileScrubber), true); + Verifier.VerifyDirectory(path, include, pattern, options, settings, info, fileScrubber, sourceFile); /// /// Verifies the contents of . @@ -33,7 +34,7 @@ public SettingsTask VerifyDirectory( object? info = null, FileScrubber? fileScrubber = null, [CallerFilePath] string sourceFile = "") => - VerifyDirectory(path.FullName, include, pattern, options, settings, info, fileScrubber, sourceFile); + Verifier.VerifyDirectory(path, include, pattern, options, settings, info, fileScrubber, sourceFile); #else /// /// Verifies the contents of . @@ -48,7 +49,7 @@ public SettingsTask VerifyDirectory( object? info = null, FileScrubber? fileScrubber = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyDirectory(path, include, pattern, option, info, fileScrubber), true); + Verifier.VerifyDirectory(path, include, pattern, option, settings, info, fileScrubber, sourceFile); /// /// Verifies the contents of . @@ -64,6 +65,6 @@ public SettingsTask VerifyDirectory( object? info = null, FileScrubber? fileScrubber = null, [CallerFilePath] string sourceFile = "") => - VerifyDirectory(path.FullName, include, pattern, option, settings, info, fileScrubber, sourceFile); + Verifier.VerifyDirectory(path, include, pattern, option, settings, info, fileScrubber, sourceFile); #endif } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_File.cs b/src/Verify.MSTest/VerifyBase_File.cs index 8b89365df..8fc473bc5 100644 --- a/src/Verify.MSTest/VerifyBase_File.cs +++ b/src/Verify.MSTest/VerifyBase_File.cs @@ -1,7 +1,9 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + /// /// Verifies the contents of . /// @@ -11,7 +13,7 @@ public SettingsTask VerifyFile( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyFile(path, info)); + Verifier.VerifyFile(path, settings, info, sourceFile); /// /// Verifies the contents of . @@ -23,5 +25,5 @@ public SettingsTask VerifyFile( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyFile(path, info)); + Verifier.VerifyFile(path, settings, info, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Json.cs b/src/Verify.MSTest/VerifyBase_Json.cs index e5041462e..2eb0e7df4 100644 --- a/src/Verify.MSTest/VerifyBase_Json.cs +++ b/src/Verify.MSTest/VerifyBase_Json.cs @@ -1,46 +1,48 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + [Pure] public SettingsTask VerifyJson( string? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyJson(target)); + Verifier.VerifyJson(target, settings, sourceFile); [Pure] public SettingsTask VerifyJson( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyJson(target)); + Verifier.VerifyJson(target, settings, sourceFile); [Pure] public SettingsTask VerifyJson( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyJson(target)); + Verifier.VerifyJson(target, settings, sourceFile); [Pure] public SettingsTask VerifyJson( Stream? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyJson(target)); + Verifier.VerifyJson(target, settings, sourceFile); [Pure] public SettingsTask VerifyJson( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyJson(target)); + Verifier.VerifyJson(target, settings, sourceFile); [Pure] public SettingsTask VerifyJson( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyJson(target)); + Verifier.VerifyJson(target, settings, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Object.cs b/src/Verify.MSTest/VerifyBase_Object.cs index ed82aa111..42fb5277c 100644 --- a/src/Verify.MSTest/VerifyBase_Object.cs +++ b/src/Verify.MSTest/VerifyBase_Object.cs @@ -1,45 +1,47 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + [Pure] public SettingsTask Verify( VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify()); + Verifier.Verify(settings, sourceFile); [Pure] public SettingsTask Verify( Func> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(target())); + Verifier.Verify(target, settings, sourceFile); [Pure] public SettingsTask Verify( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(target)); + Verifier.Verify(target, settings, sourceFile); [Pure] public SettingsTask Verify( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(target)); + Verifier.Verify(target, settings, sourceFile); [Pure] public SettingsTask Verify( IAsyncEnumerable target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(target)); + Verifier.Verify(target, settings, sourceFile); [Pure] public SettingsTask Verify( object? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Verify(target)); + Verifier.Verify(target, settings, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Stream.cs b/src/Verify.MSTest/VerifyBase_Stream.cs index 7bbc92b98..a95ade3b1 100644 --- a/src/Verify.MSTest/VerifyBase_Stream.cs +++ b/src/Verify.MSTest/VerifyBase_Stream.cs @@ -1,7 +1,9 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + [Pure] public SettingsTask Verify( byte[]? target, @@ -9,7 +11,7 @@ public SettingsTask Verify( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + Verifier.Verify(target, extension, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -17,7 +19,7 @@ public SettingsTask Verify( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); + Verifier.Verify(target, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -26,7 +28,7 @@ public SettingsTask Verify( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + Verifier.Verify(target, extension, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -35,7 +37,7 @@ public SettingsTask Verify( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + Verifier.Verify(target, extension, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -43,7 +45,7 @@ public SettingsTask Verify( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); + Verifier.Verify(target, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -52,7 +54,7 @@ public SettingsTask Verify( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + Verifier.Verify(target, extension, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -60,7 +62,7 @@ public SettingsTask Verify( VerifySettings? settings = null, object? info = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyStream(target, info)); + Verifier.Verify(target, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -70,7 +72,7 @@ public SettingsTask Verify( object? info = null, [CallerFilePath] string sourceFile = "") where T : Stream => - Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + Verifier.Verify(target, extension, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -80,7 +82,7 @@ public SettingsTask Verify( object? info = null, [CallerFilePath] string sourceFile = "") where T : Stream => - Verify(settings, sourceFile, _ => _.VerifyStream(target, extension, info)); + Verifier.Verify(target, extension, settings, info, sourceFile); [Pure] public SettingsTask Verify( @@ -90,5 +92,5 @@ public SettingsTask Verify( object? info = null, [CallerFilePath] string sourceFile = "") where T : Stream => - Verify(settings, sourceFile, _ => _.VerifyStreams(targets, extension, info)); + Verifier.Verify(targets, extension, settings, info, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_String.cs b/src/Verify.MSTest/VerifyBase_String.cs index 29bd37c47..fde5f057b 100644 --- a/src/Verify.MSTest/VerifyBase_String.cs +++ b/src/Verify.MSTest/VerifyBase_String.cs @@ -1,20 +1,22 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + [Pure] public SettingsTask Verify( string? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyString(target)); + Verifier.Verify(target, settings, sourceFile); [Pure] public SettingsTask Verify( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyString(target)); + Verifier.Verify(target, settings, sourceFile); [Pure] public SettingsTask Verify( @@ -22,7 +24,7 @@ public SettingsTask Verify( string extension, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyString(target, extension)); + Verifier.Verify(target, extension, settings, sourceFile); [Pure] public SettingsTask Verify( @@ -30,5 +32,5 @@ public SettingsTask Verify( string extension, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyString(target, extension)); + Verifier.Verify(target, extension, settings, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Throws.cs b/src/Verify.MSTest/VerifyBase_Throws.cs index 2166415cb..c4be5d2c9 100644 --- a/src/Verify.MSTest/VerifyBase_Throws.cs +++ b/src/Verify.MSTest/VerifyBase_Throws.cs @@ -1,46 +1,48 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + [Pure] public SettingsTask Throws( Action target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Throws(target)); + Verifier.Throws(target, settings, sourceFile); [Pure] public SettingsTask Throws( Func target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.Throws(target)); + Verifier.Throws(target, settings, sourceFile); [Pure] public SettingsTask ThrowsTask( Func target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.ThrowsTask(target)); + Verifier.ThrowsTask(target, settings, sourceFile); [Pure] public SettingsTask ThrowsTask( Func> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.ThrowsTask(target)); + Verifier.ThrowsTask(target, settings, sourceFile); [Pure] public SettingsTask ThrowsValueTask( Func target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.ThrowsValueTask(target)); + Verifier.ThrowsValueTask(target, settings, sourceFile); [Pure] public SettingsTask ThrowsValueTask( Func> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.ThrowsValueTask(target)); + Verifier.ThrowsValueTask(target, settings, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Tuple.cs b/src/Verify.MSTest/VerifyBase_Tuple.cs index 483ace6a5..c4d4820b7 100644 --- a/src/Verify.MSTest/VerifyBase_Tuple.cs +++ b/src/Verify.MSTest/VerifyBase_Tuple.cs @@ -1,11 +1,13 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + [Pure] public SettingsTask VerifyTuple( Expression> target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyTuple(target)); + Verifier.VerifyTuple(target, settings, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/VerifyBase_Xml.cs b/src/Verify.MSTest/VerifyBase_Xml.cs index 22db06519..0b2825eb7 100644 --- a/src/Verify.MSTest/VerifyBase_Xml.cs +++ b/src/Verify.MSTest/VerifyBase_Xml.cs @@ -1,46 +1,48 @@ -namespace VerifyMSTest; +namespace VerifyMSTest; -public partial class VerifyBase +partial class VerifyBase { +#pragma warning disable CA1822 // Mark members as static + [Pure] public SettingsTask VerifyXml( string? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyXml(target)); + Verifier.VerifyXml(target, settings, sourceFile); [Pure] public SettingsTask VerifyXml( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyXml(target)); + Verifier.VerifyXml(target, settings, sourceFile); [Pure] public SettingsTask VerifyXml( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyXml(target)); + Verifier.VerifyXml(target, settings, sourceFile); [Pure] public SettingsTask VerifyXml( Stream? target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyXml(target)); + Verifier.VerifyXml(target, settings, sourceFile); [Pure] public SettingsTask VerifyXml( Task target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyXml(target)); + Verifier.VerifyXml(target, settings, sourceFile); [Pure] public SettingsTask VerifyXml( ValueTask target, VerifySettings? settings = null, [CallerFilePath] string sourceFile = "") => - Verify(settings, sourceFile, _ => _.VerifyXml(target)); + Verifier.VerifyXml(target, settings, sourceFile); } \ No newline at end of file diff --git a/src/Verify.MSTest/buildTransitive/Verify.MSTest.props b/src/Verify.MSTest/buildTransitive/Verify.MSTest.props index b57a4bd11..6a7aad02c 100644 --- a/src/Verify.MSTest/buildTransitive/Verify.MSTest.props +++ b/src/Verify.MSTest/buildTransitive/Verify.MSTest.props @@ -3,5 +3,6 @@ + \ No newline at end of file From 0114f84c7afdebaf87badd35b34dd6515bbdddca Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 12 Apr 2024 17:51:50 -0700 Subject: [PATCH 03/69] Move to-be-generated code to region --- src/Verify.MSTest.Tests/Tests.cs | 3 ++- src/Verify.MSTest/VerifyBase.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Verify.MSTest.Tests/Tests.cs b/src/Verify.MSTest.Tests/Tests.cs index 9dbbac36d..d74dd3a3c 100644 --- a/src/Verify.MSTest.Tests/Tests.cs +++ b/src/Verify.MSTest.Tests/Tests.cs @@ -1,7 +1,7 @@ // TODO: Add a test that uses the base class to prevent regressions until it's deleted // TODO: Add a test for when the [TestClass] already has a TestContext property -// TODO: Make this in generator +#region WillBeSourceGenerated partial class Tests { [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Injected by the test framework per test.")] @@ -12,6 +12,7 @@ public TestContext TestContext } } +#endregion [TestClass] [UsesVerify] diff --git a/src/Verify.MSTest/VerifyBase.cs b/src/Verify.MSTest/VerifyBase.cs index 07e0cb1ac..78cad0c48 100644 --- a/src/Verify.MSTest/VerifyBase.cs +++ b/src/Verify.MSTest/VerifyBase.cs @@ -1,6 +1,6 @@ namespace VerifyMSTest; -// TODO: This should be added by the generator +#region WillBeSourceGenerated partial class VerifyBase { [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Set by the test framework per test.")] @@ -10,6 +10,7 @@ public TestContext TestContext set => Verifier.CurrentTestContext.Value = value; } } +#endregion // TODO: Mark as obsolete //[Obsolete("Use [UsesVerify] instead.")] From 5180715091e33105c5e07a1b4bb760d4db67981c Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 12 Apr 2024 17:55:37 -0700 Subject: [PATCH 04/69] Clean up deprecation comments --- src/Verify.MSTest/VerifyBase.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Verify.MSTest/VerifyBase.cs b/src/Verify.MSTest/VerifyBase.cs index 78cad0c48..510787252 100644 --- a/src/Verify.MSTest/VerifyBase.cs +++ b/src/Verify.MSTest/VerifyBase.cs @@ -12,8 +12,6 @@ public TestContext TestContext } #endregion -// TODO: Mark as obsolete -//[Obsolete("Use [UsesVerify] instead.")] [TestClass] [UsesVerify] public abstract partial class VerifyBase From e9b3aaba9af1295fdc0673390b05556adb9b4549 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Wed, 8 May 2024 08:11:04 -0700 Subject: [PATCH 05/69] Initial implementation of source generator --- .../ClassToGenerate.cs | 13 +++ .../CodeTemplates.cs | 20 +++++ .../TrackingNames.cs | 7 ++ .../UsesVerifyGenerator.cs | 89 +++++++++++++++++++ .../Verify.MSTest.SourceGenerator.csproj | 16 ++++ src/Verify.sln | 6 ++ 6 files changed, 151 insertions(+) create mode 100644 src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs create mode 100644 src/Verify.MSTest.SourceGenerator/CodeTemplates.cs create mode 100644 src/Verify.MSTest.SourceGenerator/TrackingNames.cs create mode 100644 src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs create mode 100644 src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs new file mode 100644 index 000000000..5642a1619 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -0,0 +1,13 @@ +namespace Verify.MSTest.SourceGenerator; + +readonly record struct ClassToGenerate +{ + public readonly string Namespace; + public readonly string ClassName; + + public ClassToGenerate(string @namespace, string className) + { + Namespace = @namespace; + ClassName = className; + } +} diff --git a/src/Verify.MSTest.SourceGenerator/CodeTemplates.cs b/src/Verify.MSTest.SourceGenerator/CodeTemplates.cs new file mode 100644 index 000000000..7c66af54b --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/CodeTemplates.cs @@ -0,0 +1,20 @@ +namespace Verify.MSTest.SourceGenerator; + +static class CodeTemplates +{ + public static string GenerateExtensionClass(ClassToGenerate classToGenerate) => + $$""" + namespace {{classToGenerate.Namespace}} + { + partial class {{classToGenerate.ClassName}} + { + public TestContext TestContext + { + get => CurrentTestContext.Value!; + set => CurrentTestContext.Value = value; + + } + } + } + """; +} diff --git a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs new file mode 100644 index 000000000..885c7d644 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs @@ -0,0 +1,7 @@ +namespace Verify.MSTest.SourceGenerator; + +static class TrackingNames +{ + public static string InitialTransform { get; } = nameof(InitialTransform); + public static string RemovingNulls { get; } = nameof(RemovingNulls); +} diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs new file mode 100644 index 000000000..4b2b5053e --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -0,0 +1,89 @@ +using System.Collections.Immutable; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace Verify.MSTest.SourceGenerator; + +[Generator] +public class UsesVerifyGenerator : IIncrementalGenerator +{ + private static string MarkerAttributeName { get; } = "VerifyMSTest.UsesVerifyAttribute"; + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var classesToGenerate = context.SyntaxProvider + .ForAttributeWithMetadataName( + fullyQualifiedMetadataName: MarkerAttributeName, + predicate: IsSyntaxEligibleForGeneration, + transform: GetSemanticTargetForGeneration) + .WithTrackingName(TrackingNames.InitialTransform) + .Where(static classToGenerate => classToGenerate is not null) + .WithTrackingName(TrackingNames.RemovingNulls); + + context.RegisterSourceOutput(classesToGenerate, Execute); + } + + private static bool IsSyntaxEligibleForGeneration(SyntaxNode node, CancellationToken ct) => node is ClassDeclarationSyntax; + + static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) + { + if (context.TargetSymbol is not INamedTypeSymbol classSymbol) + { + return null; + } + + ct.ThrowIfCancellationRequested(); + + return new ClassToGenerate(classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.ToDisplayString()); + //return GetClassToGenerate(context.SemanticModel, classDeclarationSyntax); + + //if (context.TargetNode is not ClassDeclarationSyntax classDeclarationSyntax) + //{ + // return null; + //} + + //// loop through all the attributes on the class + //foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists) + //{ + // foreach (var attributeSyntax in attributeListSyntax.Attributes) + // { + // // TODO: Fix IMethodSymbol + // if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) + // { + // continue; + // } + + // INamedTypeSymbol attributeContainingTypeSymbol = attributeSymbol.ContainingType; + // string fullName = attributeContainingTypeSymbol.ToDisplayString(); + + // if (fullName == MarkerAttributeName) + // { + // return GetClassToGenerate(context.SemanticModel, classDeclarationSyntax); + // } + // } + //} + + //return null; + } + + private static void Execute(SourceProductionContext context, ClassToGenerate? classToGenerate) + { + if (classToGenerate is { } value) + { + var sourceCode = CodeTemplates.GenerateExtensionClass(value); + + context.AddSource($"UsesVerify.{value.ClassName}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); + } + } + + private static ClassToGenerate? GetClassToGenerate(SemanticModel semanticModel, SyntaxNode classDeclarationSyntax) + { + if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) + { + return null; + } + + return new ClassToGenerate(classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.ToDisplayString()); + } +} diff --git a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj new file mode 100644 index 000000000..ae0f022a4 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/src/Verify.sln b/src/Verify.sln index 7c8fdbfc7..91041d47d 100644 --- a/src/Verify.sln +++ b/src/Verify.sln @@ -93,6 +93,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixieNugetUsage", "NugetUsa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{381EDDAF-76E4-4B9F-812B-8A728F5CBC1E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Verify.MSTest.SourceGenerator", "Verify.MSTest.SourceGenerator\Verify.MSTest.SourceGenerator.csproj", "{19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -247,6 +249,10 @@ Global {381EDDAF-76E4-4B9F-812B-8A728F5CBC1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {381EDDAF-76E4-4B9F-812B-8A728F5CBC1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {381EDDAF-76E4-4B9F-812B-8A728F5CBC1E}.Release|Any CPU.Build.0 = Release|Any CPU + {19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From e7c6e89cedc47d7bccff3bc22cc346b3dd516088 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Wed, 8 May 2024 11:33:34 -0700 Subject: [PATCH 06/69] Add basic tests -- namespaces incorrect --- .../TestDriver.cs | 31 ++++++++ .../Tests.cs | 74 +++++++++++++++++++ ...Verify.MSTest.SourceGenerator.Tests.csproj | 25 +++++++ .../UsesVerifyGenerator.cs | 40 ---------- .../Verify.MSTest.SourceGenerator.csproj | 1 + src/Verify.sln | 6 ++ 6 files changed, 137 insertions(+), 40 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/Tests.cs create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs new file mode 100644 index 000000000..4eee43c9a --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -0,0 +1,31 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using VerifyMSTest; + +namespace Verify.MSTest.SourceGenerator.Tests; + +static class TestDriver +{ + public static GeneratorDriverRunResult Run(string source) + { + var syntaxTree = CSharpSyntaxTree.ParseText(source); + + IReadOnlyCollection references = + [ + MetadataReference.CreateFromFile(typeof(VerifyMSTest.UsesVerifyAttribute).Assembly.Location), + MetadataReference.CreateFromFile(typeof(object).Assembly.Location) + ]; + + var compilation = CSharpCompilation.Create( + assemblyName: "Tests", + syntaxTrees: [syntaxTree], + references: references); + + var generator = new UsesVerifyGenerator(); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); + driver = driver.RunGenerators(compilation); + + return driver.GetRunResult(); + } +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs new file mode 100644 index 000000000..b7ce23b74 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -0,0 +1,74 @@ +namespace Verify.MSTest.SourceGenerator.Tests; + +// These tests don't use Verify.SourceGenerator to avoid creating a circular depedency between the repos. + +public class Tests +{ + [Fact] + public Task NoAttribute() + { + var source = """ + public class Foo + { + } + """; + + var results = TestDriver + .Run(source) + .Results + .SelectMany(grr => grr.GeneratedSources) + .Select(gs => gs.SourceText.ToString()) + .ToArray(); + + // TODO: Why is static using not working? + return Verifier.Verify(results); + } + + [Fact] + public Task HasAttributeInGlobalNamespace() + { + var source = """ + using VerifyMSTest; + + [UsesVerify] + public class Foo + { + } + """; + + var results = TestDriver + .Run(source) + .Results + .SelectMany(grr => grr.GeneratedSources) + .Select(gs => gs.SourceText.ToString()) + .ToArray(); + + // TODO: Why is static using not working? + return Verifier.Verify(results); + } + + [Fact] + public Task HasAttributeInNamespace() + { + var source = """ + using VerifyMSTest; + + namespace Foo + + [UsesVerify] + public class Bar + { + } + """; + + var results = TestDriver + .Run(source) + .Results + .SelectMany(grr => grr.GeneratedSources) + .Select(gs => gs.SourceText.ToString()) + .ToArray(); + + // TODO: Why is static using not working? + return Verifier.Verify(results); + } +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj new file mode 100644 index 000000000..1e27ea9f0 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj @@ -0,0 +1,25 @@ + + + + net9.0 + false + true + + + + + + + + + + + + + + + + + + + diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 4b2b5053e..270eb1832 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -36,35 +35,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ct.ThrowIfCancellationRequested(); return new ClassToGenerate(classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.ToDisplayString()); - //return GetClassToGenerate(context.SemanticModel, classDeclarationSyntax); - - //if (context.TargetNode is not ClassDeclarationSyntax classDeclarationSyntax) - //{ - // return null; - //} - - //// loop through all the attributes on the class - //foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists) - //{ - // foreach (var attributeSyntax in attributeListSyntax.Attributes) - // { - // // TODO: Fix IMethodSymbol - // if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) - // { - // continue; - // } - - // INamedTypeSymbol attributeContainingTypeSymbol = attributeSymbol.ContainingType; - // string fullName = attributeContainingTypeSymbol.ToDisplayString(); - - // if (fullName == MarkerAttributeName) - // { - // return GetClassToGenerate(context.SemanticModel, classDeclarationSyntax); - // } - // } - //} - - //return null; } private static void Execute(SourceProductionContext context, ClassToGenerate? classToGenerate) @@ -76,14 +46,4 @@ private static void Execute(SourceProductionContext context, ClassToGenerate? cl context.AddSource($"UsesVerify.{value.ClassName}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); } } - - private static ClassToGenerate? GetClassToGenerate(SemanticModel semanticModel, SyntaxNode classDeclarationSyntax) - { - if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) - { - return null; - } - - return new ClassToGenerate(classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.ToDisplayString()); - } } diff --git a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj index ae0f022a4..05e4f43e4 100644 --- a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj +++ b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Verify.sln b/src/Verify.sln index 91041d47d..a1ad16363 100644 --- a/src/Verify.sln +++ b/src/Verify.sln @@ -95,6 +95,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Be EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Verify.MSTest.SourceGenerator", "Verify.MSTest.SourceGenerator\Verify.MSTest.SourceGenerator.csproj", "{19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Verify.MSTest.SourceGenerator.Tests", "Verify.MSTest.SourceGenerator.Tests\Verify.MSTest.SourceGenerator.Tests.csproj", "{8CAE91EB-725B-4F4A-AA70-F825395E944A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -253,6 +255,10 @@ Global {19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}.Debug|Any CPU.Build.0 = Debug|Any CPU {19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}.Release|Any CPU.ActiveCfg = Release|Any CPU {19FF2BDC-A966-4C03-9DF8-E6978E0FACF2}.Release|Any CPU.Build.0 = Release|Any CPU + {8CAE91EB-725B-4F4A-AA70-F825395E944A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CAE91EB-725B-4F4A-AA70-F825395E944A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CAE91EB-725B-4F4A-AA70-F825395E944A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CAE91EB-725B-4F4A-AA70-F825395E944A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 833b7bcb0c18f2118747bdedcd76780964c0b881 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Wed, 8 May 2024 11:44:48 -0700 Subject: [PATCH 07/69] Fix incorrect namespacing --- ...Tests.HasAttributeInNamespace.verified.txt | 14 ++++++++ .../Tests.cs | 34 ++++++++++++++++++- .../UsesVerifyGenerator.cs | 2 +- 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt new file mode 100644 index 000000000..85e11bef2 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -0,0 +1,14 @@ +[ +namespace Foo +{ + partial class Bar + { + public TestContext TestContext + { + get => CurrentTestContext.Value!; + set => CurrentTestContext.Value = value; + + } + } +} +] \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index b7ce23b74..3a62aa533 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -53,7 +53,7 @@ public Task HasAttributeInNamespace() var source = """ using VerifyMSTest; - namespace Foo + namespace Foo; [UsesVerify] public class Bar @@ -71,4 +71,36 @@ public class Bar // TODO: Why is static using not working? return Verifier.Verify(results); } + + [Fact] + public Task HasAttributeInNestedNamespaceAndClass() + { + var source = """ + using VerifyMSTest; + + namespace Foo + { + namespace Bar + { + public partial class Baz + { + [UsesVerify] + public partial class Qux + { + } + } + } + } + """; + + var results = TestDriver + .Run(source) + .Results + .SelectMany(grr => grr.GeneratedSources) + .Select(gs => gs.SourceText.ToString()) + .ToArray(); + + // TODO: Why is static using not working? + return Verifier.Verify(results); + } } diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 270eb1832..6bb823c62 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -34,7 +34,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ct.ThrowIfCancellationRequested(); - return new ClassToGenerate(classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.ToDisplayString()); + return new ClassToGenerate(classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.Name); } private static void Execute(SourceProductionContext context, ClassToGenerate? classToGenerate) From 3e0d747cdc87e69455e0cf6500d6df2de3581c46 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Thu, 9 May 2024 16:05:24 -0400 Subject: [PATCH 08/69] Handle nested partial classes --- ...HasAttributeInGlobalNamespace.verified.txt | 11 +++ ...Tests.HasAttributeInNamespace.verified.txt | 2 +- ...NamespaceAndClassWithGenerics.verified.txt | 16 ++++ .../Tests.NoAttribute.verified.txt | 1 + .../Tests.cs | 6 +- .../ClassToGenerate.cs | 10 ++- .../CodeTemplates.cs | 20 ----- .../CodeWriter.cs | 90 +++++++++++++++++++ .../IndentedStringBuilder.cs | 25 ++++++ .../ParentClass.cs | 17 ++++ .../UsesVerifyGenerator.cs | 46 +++++++++- 11 files changed, 216 insertions(+), 28 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt delete mode 100644 src/Verify.MSTest.SourceGenerator/CodeTemplates.cs create mode 100644 src/Verify.MSTest.SourceGenerator/CodeWriter.cs create mode 100644 src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs create mode 100644 src/Verify.MSTest.SourceGenerator/ParentClass.cs diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt new file mode 100644 index 000000000..340df2c7b --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt @@ -0,0 +1,11 @@ +[ +partial class Foo +{ + public TestContext TestContext + { + get => CurrentTestContext.Value!; + set => CurrentTestContext.Value = value; + } +} + +] \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt index 85e11bef2..a10d5ca8d 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -7,8 +7,8 @@ namespace Foo { get => CurrentTestContext.Value!; set => CurrentTestContext.Value = value; - } } } + ] \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt new file mode 100644 index 000000000..40815f8a4 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -0,0 +1,16 @@ +[ +namespace Foo.Bar +{ + partial class Baz + { + partial class Qux + { + public TestContext TestContext + { + get => CurrentTestContext.Value!; + set => CurrentTestContext.Value = value; + } + } + } + +] \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt new file mode 100644 index 000000000..ad47dbb93 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 3a62aa533..4f44a76f5 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -73,7 +73,7 @@ public class Bar } [Fact] - public Task HasAttributeInNestedNamespaceAndClass() + public Task HasAttributeInNestedNamespaceAndClassWithGenerics() { var source = """ using VerifyMSTest; @@ -82,10 +82,10 @@ namespace Foo { namespace Bar { - public partial class Baz + public partial class Baz { [UsesVerify] - public partial class Qux + public partial class Qux { } } diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index 5642a1619..33aed565f 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -1,13 +1,19 @@ +using System.Collections.Immutable; + namespace Verify.MSTest.SourceGenerator; readonly record struct ClassToGenerate { - public readonly string Namespace; + public readonly string Namespace; // TODO: Consider making nullable? public readonly string ClassName; + public readonly IReadOnlyCollection TypeParameters; + public readonly ParentClass? ParentClass; - public ClassToGenerate(string @namespace, string className) + public ClassToGenerate(string @namespace, string className, IReadOnlyCollection typeParameters, ParentClass? parentClass) { Namespace = @namespace; ClassName = className; + TypeParameters = typeParameters; + ParentClass = parentClass; } } diff --git a/src/Verify.MSTest.SourceGenerator/CodeTemplates.cs b/src/Verify.MSTest.SourceGenerator/CodeTemplates.cs deleted file mode 100644 index 7c66af54b..000000000 --- a/src/Verify.MSTest.SourceGenerator/CodeTemplates.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Verify.MSTest.SourceGenerator; - -static class CodeTemplates -{ - public static string GenerateExtensionClass(ClassToGenerate classToGenerate) => - $$""" - namespace {{classToGenerate.Namespace}} - { - partial class {{classToGenerate.ClassName}} - { - public TestContext TestContext - { - get => CurrentTestContext.Value!; - set => CurrentTestContext.Value = value; - - } - } - } - """; -} diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs new file mode 100644 index 000000000..54bbec12f --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -0,0 +1,90 @@ +namespace Verify.MSTest.SourceGenerator; + +static class CodeWriter +{ + // TODO: Add generated code decorations + //private const string GeneratedTypeSummary = + // " " + + // "This API supports the logging infrastructure and is not intended to be used directly from your code. " + + // "It is subject to change in the future. " + + // ""; + //private static readonly string s_generatedCodeAttribute = + // $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + + // $"\"{typeof(CodeWriter).Assembly.GetName().Name}\", " + + // $"\"{typeof(CodeWriter).Assembly.GetName().Version}\")"; + //private const string EditorBrowsableAttribute = + // "global::System.ComponentModel.EditorBrowsableAttribute(" + + // "global::System.ComponentModel.EditorBrowsableState.Never)"; + + private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) + { + if (!string.IsNullOrEmpty(classToGenerate.Namespace)) // TODO: Consider making nullable? + { + sb.AppendLine($"namespace {classToGenerate.Namespace}"); + sb.AppendLine("{"); + + sb.IncreaseIndent(); + } + + WriteParentTypes(sb, classToGenerate); + + if (!string.IsNullOrEmpty(classToGenerate.Namespace)) // TODO: Consider making nullable? + { + sb.DecreaseIndent(); + sb.AppendLine("}"); + }; + } + + private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate classToGenerate) + { + var parentClass = classToGenerate.ParentClass; + var depth = 1; + while (parentClass is not null) + { + sb.AppendLine($"partial {parentClass.Keyword} {parentClass.Name}{parentClass.Constraints}"); + sb.AppendLine("{"); + + sb.IncreaseIndent(); + depth += 1; + parentClass = parentClass.Child; + } + + WriteClass(sb, classToGenerate); + + while (depth < 0) + { + sb.DecreaseIndent(); + sb.AppendLine("}"); + + depth -= 1; + } + } + + private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) + { + var genericConstraints = string.Empty; + + if (classToGenerate.TypeParameters.Count > 0) + { + genericConstraints = $"<{string.Join(", ", classToGenerate.TypeParameters)}>"; + } + + sb.AppendLine($"partial class {classToGenerate.ClassName}{genericConstraints}"); + sb.AppendLine("{"); + sb.AppendLine(" public TestContext TestContext"); + sb.AppendLine(" {"); + sb.AppendLine(" get => CurrentTestContext.Value!;"); + sb.AppendLine(" set => CurrentTestContext.Value = value;"); + sb.AppendLine(" }"); + sb.AppendLine("}"); + } + + public static string GenerateExtensionClass(ClassToGenerate classToGenerate) + { + var sb = new IndentedStringBuilder(); + + WriteNamespace(sb, classToGenerate); + + return sb.ToString(); + } +} diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs new file mode 100644 index 000000000..2124359cf --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -0,0 +1,25 @@ +namespace Verify.MSTest.SourceGenerator; + +class IndentedStringBuilder +{ + private readonly StringBuilder builder = new StringBuilder(); + private int indentLevel = 0; + + public void IncreaseIndent() => indentLevel += 1; + + public void DecreaseIndent() => indentLevel -= 1; + + public void AppendLine(string line) + { + builder.Append(' ', indentLevel * 4); + builder.AppendLine(line); + } + + public void Append(string text) + { + builder.Append(' ', indentLevel * 4); + builder.Append(text); + } + + public override string ToString() => builder.ToString(); +} diff --git a/src/Verify.MSTest.SourceGenerator/ParentClass.cs b/src/Verify.MSTest.SourceGenerator/ParentClass.cs new file mode 100644 index 000000000..9a291fac6 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/ParentClass.cs @@ -0,0 +1,17 @@ +namespace Verify.MSTest.SourceGenerator; + +internal class ParentClass +{ + public ParentClass(string keyword, string name, string constraints, ParentClass? child) + { + Keyword = keyword; + Name = name; + Constraints = constraints; + Child = child; + } + + public ParentClass? Child { get; } + public string Keyword { get; } + public string Name { get; } + public string Constraints { get; } +} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 6bb823c62..5377658fd 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -1,5 +1,6 @@ using System.Text; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; @@ -32,18 +33,59 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return null; } + if (context.TargetNode is not BaseTypeDeclarationSyntax declarationSyntax) + { + return null; + } + ct.ThrowIfCancellationRequested(); - return new ClassToGenerate(classSymbol.ContainingNamespace.ToDisplayString(), classSymbol.Name); + var @namespace = classSymbol.ContainingNamespace.IsGlobalNamespace ? string.Empty : classSymbol.ContainingNamespace.ToString(); + var typeParameters = classSymbol.TypeParameters.Select(tp => tp.Name).ToArray(); // TODO: May be able to use Syntax instead + var parentClass = GetParentClasses(declarationSyntax); + + return new ClassToGenerate(@namespace, classSymbol.Name, typeParameters, parentClass); } private static void Execute(SourceProductionContext context, ClassToGenerate? classToGenerate) { if (classToGenerate is { } value) { - var sourceCode = CodeTemplates.GenerateExtensionClass(value); + var sourceCode = CodeWriter.GenerateExtensionClass(value); context.AddSource($"UsesVerify.{value.ClassName}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); } } + + private static ParentClass? GetParentClasses(BaseTypeDeclarationSyntax typeSyntax) + { + // TODO: Redo as stack + + // We can only be nested in class/struct/record + static bool IsAllowedKind(SyntaxKind kind) => + kind == SyntaxKind.ClassDeclaration || + kind == SyntaxKind.StructDeclaration || + kind == SyntaxKind.RecordDeclaration; + + // Try and get the parent syntax. If it isn't a type like class/struct, this will be null + var parentSyntax = typeSyntax.Parent as TypeDeclarationSyntax; + ParentClass? parentClassInfo = null; + + // Keep looping while we're in a supported nested type + while (parentSyntax != null && IsAllowedKind(parentSyntax.Kind())) + { + // Record the parent type keyword (class/struct etc), name, and constraints + parentClassInfo = new ParentClass( + keyword: parentSyntax.Keyword.ValueText, + name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList, + constraints: parentSyntax.ConstraintClauses.ToString(), // TODO: I think I can remove this + child: parentClassInfo); + + // Move to the next outer type + parentSyntax = (parentSyntax.Parent as TypeDeclarationSyntax); + } + + return parentClassInfo; + + } } From 86b0445b2055551945acbb87127237469b85f9c2 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 10 May 2024 15:03:29 -0400 Subject: [PATCH 09/69] Add autogenerted header to top of generated source file --- ...HasAttributeInGlobalNamespace.verified.txt | 9 ++++++ ...Tests.HasAttributeInNamespace.verified.txt | 9 ++++++ ...NamespaceAndClassWithGenerics.verified.txt | 9 ++++++ .../CodeWriter.cs | 29 ++++++++++--------- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt index 340df2c7b..11dd89755 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt @@ -1,4 +1,13 @@ [ +//----------------------------------------------------- +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior +// and will be lost when the code is regenerated. +// +//----------------------------------------------------- + +[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator, "1.0.0.0")] partial class Foo { public TestContext TestContext diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt index a10d5ca8d..cea4c114a 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -1,6 +1,15 @@ [ +//----------------------------------------------------- +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior +// and will be lost when the code is regenerated. +// +//----------------------------------------------------- + namespace Foo { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator, "1.0.0.0")] partial class Bar { public TestContext TestContext diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt index 40815f8a4..05721c70e 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -1,8 +1,17 @@ [ +//----------------------------------------------------- +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior +// and will be lost when the code is regenerated. +// +//----------------------------------------------------- + namespace Foo.Bar { partial class Baz { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator, "1.0.0.0")] partial class Qux { public TestContext TestContext diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs index 54bbec12f..3cdf156c3 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -2,19 +2,19 @@ namespace Verify.MSTest.SourceGenerator; static class CodeWriter { - // TODO: Add generated code decorations - //private const string GeneratedTypeSummary = - // " " + - // "This API supports the logging infrastructure and is not intended to be used directly from your code. " + - // "It is subject to change in the future. " + - // ""; - //private static readonly string s_generatedCodeAttribute = - // $"global::System.CodeDom.Compiler.GeneratedCodeAttribute(" + - // $"\"{typeof(CodeWriter).Assembly.GetName().Name}\", " + - // $"\"{typeof(CodeWriter).Assembly.GetName().Version}\")"; - //private const string EditorBrowsableAttribute = - // "global::System.ComponentModel.EditorBrowsableAttribute(" + - // "global::System.ComponentModel.EditorBrowsableState.Never)"; + private static readonly string AutoGenerationHeader = """ + //----------------------------------------------------- + // This code was generated by a tool. + // + // Changes to this file may cause incorrect behavior + // and will be lost when the code is regenerated. + // + //----------------------------------------------------- + + """; + + private static readonly string GeneratedCodeAttribute = + $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(CodeWriter).Assembly.GetName().Name}, \"{typeof(CodeWriter).Assembly.GetName().Version}\")]"; private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { @@ -69,6 +69,7 @@ private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classTo genericConstraints = $"<{string.Join(", ", classToGenerate.TypeParameters)}>"; } + sb.AppendLine(GeneratedCodeAttribute); sb.AppendLine($"partial class {classToGenerate.ClassName}{genericConstraints}"); sb.AppendLine("{"); sb.AppendLine(" public TestContext TestContext"); @@ -83,6 +84,8 @@ public static string GenerateExtensionClass(ClassToGenerate classToGenerate) { var sb = new IndentedStringBuilder(); + sb.AppendLine(AutoGenerationHeader); + WriteNamespace(sb, classToGenerate); return sb.ToString(); From 0649dc6aba0d0ff5595044a16c2c1b983dd6f806 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 10 May 2024 15:28:06 -0400 Subject: [PATCH 10/69] Refactor IndentedStringBuilder for chaining and reduced allocations --- .../CodeWriter.cs | 38 ++++++++-------- .../IndentedStringBuilder.cs | 45 +++++++++++++++++-- 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs index 3cdf156c3..86320afae 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -20,18 +20,17 @@ private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate cla { if (!string.IsNullOrEmpty(classToGenerate.Namespace)) // TODO: Consider making nullable? { - sb.AppendLine($"namespace {classToGenerate.Namespace}"); - sb.AppendLine("{"); - - sb.IncreaseIndent(); + sb.AppendLine(["namespace ", classToGenerate.Namespace]) + .AppendLine("{") + .IncreaseIndent(); } WriteParentTypes(sb, classToGenerate); if (!string.IsNullOrEmpty(classToGenerate.Namespace)) // TODO: Consider making nullable? { - sb.DecreaseIndent(); - sb.AppendLine("}"); + sb.DecreaseIndent() + .AppendLine("}"); }; } @@ -41,8 +40,8 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c var depth = 1; while (parentClass is not null) { - sb.AppendLine($"partial {parentClass.Keyword} {parentClass.Name}{parentClass.Constraints}"); - sb.AppendLine("{"); + sb.AppendLine(["partial ", parentClass.Keyword, " ", parentClass.Name, parentClass.Constraints]) + .AppendLine("{"); sb.IncreaseIndent(); depth += 1; @@ -53,8 +52,8 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c while (depth < 0) { - sb.DecreaseIndent(); - sb.AppendLine("}"); + sb.DecreaseIndent() + .AppendLine("}"); depth -= 1; } @@ -66,18 +65,19 @@ private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classTo if (classToGenerate.TypeParameters.Count > 0) { + // TODO: Reimplement for perf genericConstraints = $"<{string.Join(", ", classToGenerate.TypeParameters)}>"; } - sb.AppendLine(GeneratedCodeAttribute); - sb.AppendLine($"partial class {classToGenerate.ClassName}{genericConstraints}"); - sb.AppendLine("{"); - sb.AppendLine(" public TestContext TestContext"); - sb.AppendLine(" {"); - sb.AppendLine(" get => CurrentTestContext.Value!;"); - sb.AppendLine(" set => CurrentTestContext.Value = value;"); - sb.AppendLine(" }"); - sb.AppendLine("}"); + sb.AppendLine(GeneratedCodeAttribute) + .AppendLine($"partial class {classToGenerate.ClassName}{genericConstraints}") // TODO: Reimplement for perf + .AppendLine("{") + .AppendLine(" public TestContext TestContext") + .AppendLine(" {") + .AppendLine(" get => CurrentTestContext.Value!;") + .AppendLine(" set => CurrentTestContext.Value = value;") + .AppendLine(" }") + .AppendLine("}"); } public static string GenerateExtensionClass(ClassToGenerate classToGenerate) diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 2124359cf..43eb0590e 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -5,20 +5,57 @@ class IndentedStringBuilder private readonly StringBuilder builder = new StringBuilder(); private int indentLevel = 0; - public void IncreaseIndent() => indentLevel += 1; + public IndentedStringBuilder IncreaseIndent() + { + indentLevel += 1; - public void DecreaseIndent() => indentLevel -= 1; + return this; + } - public void AppendLine(string line) + public IndentedStringBuilder DecreaseIndent() + { + indentLevel -= 1; + + return this; + } + + public IndentedStringBuilder AppendLine(string line) { builder.Append(' ', indentLevel * 4); builder.AppendLine(line); + + return this; + } + + public IndentedStringBuilder AppendLine(IEnumerable strings) + { + builder.Append(' ', indentLevel * 4); + foreach (var s in strings) + { + builder.Append(s); + } + builder.AppendLine(); + + return this; } - public void Append(string text) + public IndentedStringBuilder Append(string text) { builder.Append(' ', indentLevel * 4); builder.Append(text); + + return this; + } + + public IndentedStringBuilder Append(IEnumerable strings) + { + builder.Append(' ', indentLevel * 4); + foreach (var s in strings) + { + builder.Append(s); + } + + return this; } public override string ToString() => builder.ToString(); From 4126d4eb4b87e8db88717ea942fa437ad409601c Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 10 May 2024 21:10:09 -0400 Subject: [PATCH 11/69] Fix invalid generated code - Code generation attribute missing a quote - Fix reuse of generic parameter name in test - Fix incorrect nesting depth braces --- .../Tests.HasAttributeInGlobalNamespace.verified.txt | 2 +- .../Tests.HasAttributeInNamespace.verified.txt | 2 +- ...ributeInNestedNamespaceAndClassWithGenerics.verified.txt | 5 +++-- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 2 +- src/Verify.MSTest.SourceGenerator/CodeWriter.cs | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt index 11dd89755..7e00b7ada 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt @@ -7,7 +7,7 @@ // //----------------------------------------------------- -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator, "1.0.0.0")] +[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] partial class Foo { public TestContext TestContext diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt index cea4c114a..ccd17bc1e 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -9,7 +9,7 @@ namespace Foo { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator, "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] partial class Bar { public TestContext TestContext diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt index 05721c70e..5a6a4ba33 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -11,8 +11,8 @@ namespace Foo.Bar { partial class Baz { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator, "1.0.0.0")] - partial class Qux + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class Qux { public TestContext TestContext { @@ -21,5 +21,6 @@ namespace Foo.Bar } } } +} ] \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 4f44a76f5..9e7cce3cf 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -85,7 +85,7 @@ namespace Bar public partial class Baz { [UsesVerify] - public partial class Qux + public partial class Qux { } } diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs index 86320afae..4bf1f4523 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -14,7 +14,7 @@ static class CodeWriter """; private static readonly string GeneratedCodeAttribute = - $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(CodeWriter).Assembly.GetName().Name}, \"{typeof(CodeWriter).Assembly.GetName().Version}\")]"; + $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(CodeWriter).Assembly.GetName().Name}\", \"{typeof(CodeWriter).Assembly.GetName().Version}\")]"; private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { @@ -37,7 +37,7 @@ private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate cla private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { var parentClass = classToGenerate.ParentClass; - var depth = 1; + var depth = 0; while (parentClass is not null) { sb.AppendLine(["partial ", parentClass.Keyword, " ", parentClass.Name, parentClass.Constraints]) @@ -50,7 +50,7 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c WriteClass(sb, classToGenerate); - while (depth < 0) + while (depth > 0) { sb.DecreaseIndent() .AppendLine("}"); From f34fb5dc69a817fda979f875cb794da4b3e9be87 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 10 May 2024 21:15:49 -0400 Subject: [PATCH 12/69] Fix nullable namespace --- src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs | 4 ++-- src/Verify.MSTest.SourceGenerator/CodeWriter.cs | 4 ++-- src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index 33aed565f..eb3daf86a 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -4,12 +4,12 @@ namespace Verify.MSTest.SourceGenerator; readonly record struct ClassToGenerate { - public readonly string Namespace; // TODO: Consider making nullable? + public readonly string? Namespace; public readonly string ClassName; public readonly IReadOnlyCollection TypeParameters; public readonly ParentClass? ParentClass; - public ClassToGenerate(string @namespace, string className, IReadOnlyCollection typeParameters, ParentClass? parentClass) + public ClassToGenerate(string? @namespace, string className, IReadOnlyCollection typeParameters, ParentClass? parentClass) { Namespace = @namespace; ClassName = className; diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs index 4bf1f4523..3bf29688c 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -18,7 +18,7 @@ static class CodeWriter private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { - if (!string.IsNullOrEmpty(classToGenerate.Namespace)) // TODO: Consider making nullable? + if (classToGenerate.Namespace is not null) { sb.AppendLine(["namespace ", classToGenerate.Namespace]) .AppendLine("{") @@ -27,7 +27,7 @@ private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate cla WriteParentTypes(sb, classToGenerate); - if (!string.IsNullOrEmpty(classToGenerate.Namespace)) // TODO: Consider making nullable? + if (classToGenerate.Namespace is not null) { sb.DecreaseIndent() .AppendLine("}"); diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 5377658fd..40e2caf64 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -40,7 +40,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ct.ThrowIfCancellationRequested(); - var @namespace = classSymbol.ContainingNamespace.IsGlobalNamespace ? string.Empty : classSymbol.ContainingNamespace.ToString(); + var @namespace = classSymbol.ContainingNamespace.IsGlobalNamespace ? null : classSymbol.ContainingNamespace.ToString(); var typeParameters = classSymbol.TypeParameters.Select(tp => tp.Name).ToArray(); // TODO: May be able to use Syntax instead var parentClass = GetParentClasses(declarationSyntax); From b122ef0dd854531c05fc134e731948be86f62415 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 10 May 2024 21:24:22 -0400 Subject: [PATCH 13/69] Partial classes don't need to specify type constraints --- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 2 +- src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs | 2 -- src/Verify.MSTest.SourceGenerator/CodeWriter.cs | 2 +- src/Verify.MSTest.SourceGenerator/ParentClass.cs | 6 ++---- src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs | 2 -- 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 9e7cce3cf..93316ae6d 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -82,7 +82,7 @@ namespace Foo { namespace Bar { - public partial class Baz + public partial class Baz where T : new() { [UsesVerify] public partial class Qux diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index eb3daf86a..eac7eb672 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace Verify.MSTest.SourceGenerator; readonly record struct ClassToGenerate diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs index 3bf29688c..937ca2772 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -40,7 +40,7 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c var depth = 0; while (parentClass is not null) { - sb.AppendLine(["partial ", parentClass.Keyword, " ", parentClass.Name, parentClass.Constraints]) + sb.AppendLine(["partial ", parentClass.Keyword, " ", parentClass.Name]) .AppendLine("{"); sb.IncreaseIndent(); diff --git a/src/Verify.MSTest.SourceGenerator/ParentClass.cs b/src/Verify.MSTest.SourceGenerator/ParentClass.cs index 9a291fac6..19b71a3d7 100644 --- a/src/Verify.MSTest.SourceGenerator/ParentClass.cs +++ b/src/Verify.MSTest.SourceGenerator/ParentClass.cs @@ -1,17 +1,15 @@ -namespace Verify.MSTest.SourceGenerator; +namespace Verify.MSTest.SourceGenerator; internal class ParentClass { - public ParentClass(string keyword, string name, string constraints, ParentClass? child) + public ParentClass(string keyword, string name, ParentClass? child) { Keyword = keyword; Name = name; - Constraints = constraints; Child = child; } public ParentClass? Child { get; } public string Keyword { get; } public string Name { get; } - public string Constraints { get; } } \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 40e2caf64..051651edb 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -1,4 +1,3 @@ -using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -78,7 +77,6 @@ static bool IsAllowedKind(SyntaxKind kind) => parentClassInfo = new ParentClass( keyword: parentSyntax.Keyword.ValueText, name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList, - constraints: parentSyntax.ConstraintClauses.ToString(), // TODO: I think I can remove this child: parentClassInfo); // Move to the next outer type From 941331ca2b3c46b568093a85fa5cb21d42d847e5 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 10 May 2024 21:42:12 -0400 Subject: [PATCH 14/69] Simplify ParentClass to use a stack instead of a LinkedList --- ...NamespaceAndClassWithGenerics.verified.txt | 47 +++++++++++++++---- .../Tests.cs | 18 +++++-- .../ClassToGenerate.cs | 12 ++--- .../CodeWriter.cs | 12 ++--- .../ParentClass.cs | 14 +++--- .../UsesVerifyGenerator.cs | 12 ++--- 6 files changed, 73 insertions(+), 42 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt index 5a6a4ba33..14e5f581c 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -7,20 +7,51 @@ // //----------------------------------------------------- -namespace Foo.Bar +namespace A.B { - partial class Baz + partial class C { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] - partial class Qux + partial class D { - public TestContext TestContext + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class TestClass1 { - get => CurrentTestContext.Value!; - set => CurrentTestContext.Value = value; + public TestContext TestContext + { + get => CurrentTestContext.Value!; + set => CurrentTestContext.Value = value; + } } } } } -] \ No newline at end of file + +//----------------------------------------------------- +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior +// and will be lost when the code is regenerated. +// +//----------------------------------------------------- + +namespace A.B +{ + partial class C + { + partial class D + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class TestClass2 + { + public TestContext TestContext + { + get => CurrentTestContext.Value!; + set => CurrentTestContext.Value = value; + } + } + } + } +} + +] diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 93316ae6d..f5922aeed 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -78,15 +78,23 @@ public Task HasAttributeInNestedNamespaceAndClassWithGenerics() var source = """ using VerifyMSTest; - namespace Foo + namespace A { - namespace Bar + namespace B { - public partial class Baz where T : new() + public partial class C where T : new() { - [UsesVerify] - public partial class Qux + public partial class D { + [UsesVerify] + public partial class TestClass1 + { + } + + [UsesVerify] + public partial class TestClass2 + { + } } } } diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index eac7eb672..c23f4a553 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -2,16 +2,16 @@ namespace Verify.MSTest.SourceGenerator; readonly record struct ClassToGenerate { - public readonly string? Namespace; - public readonly string ClassName; - public readonly IReadOnlyCollection TypeParameters; - public readonly ParentClass? ParentClass; + public string? Namespace { get; } + public string ClassName { get; } + public IReadOnlyCollection TypeParameters { get; } + public IReadOnlyCollection ParentClasses { get; } - public ClassToGenerate(string? @namespace, string className, IReadOnlyCollection typeParameters, ParentClass? parentClass) + public ClassToGenerate(string? @namespace, string className, IReadOnlyCollection typeParameters, IReadOnlyCollection parentClasses) { Namespace = @namespace; ClassName = className; TypeParameters = typeParameters; - ParentClass = parentClass; + ParentClasses = parentClasses; } } diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs index 937ca2772..cd149eaf0 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -36,31 +36,27 @@ private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate cla private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { - var parentClass = classToGenerate.ParentClass; - var depth = 0; - while (parentClass is not null) + foreach (var parentClass in classToGenerate.ParentClasses) { sb.AppendLine(["partial ", parentClass.Keyword, " ", parentClass.Name]) .AppendLine("{"); sb.IncreaseIndent(); - depth += 1; - parentClass = parentClass.Child; } WriteClass(sb, classToGenerate); - while (depth > 0) + foreach (var parentClass in classToGenerate.ParentClasses) { sb.DecreaseIndent() .AppendLine("}"); - - depth -= 1; } } private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { + // TODO: Update to put all generated classes in the same file + var genericConstraints = string.Empty; if (classToGenerate.TypeParameters.Count > 0) diff --git a/src/Verify.MSTest.SourceGenerator/ParentClass.cs b/src/Verify.MSTest.SourceGenerator/ParentClass.cs index 19b71a3d7..0c1d5d908 100644 --- a/src/Verify.MSTest.SourceGenerator/ParentClass.cs +++ b/src/Verify.MSTest.SourceGenerator/ParentClass.cs @@ -1,15 +1,13 @@ namespace Verify.MSTest.SourceGenerator; -internal class ParentClass +readonly record struct ParentClass { - public ParentClass(string keyword, string name, ParentClass? child) + public string Keyword { get; } + public string Name { get; } + + public ParentClass(string keyword, string name) { Keyword = keyword; Name = name; - Child = child; } - - public ParentClass? Child { get; } - public string Keyword { get; } - public string Name { get; } -} \ No newline at end of file +} diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 051651edb..552c537fa 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -56,9 +56,9 @@ private static void Execute(SourceProductionContext context, ClassToGenerate? cl } } - private static ParentClass? GetParentClasses(BaseTypeDeclarationSyntax typeSyntax) + private static IReadOnlyCollection GetParentClasses(BaseTypeDeclarationSyntax typeSyntax) { - // TODO: Redo as stack + var parents = new Stack(); // We can only be nested in class/struct/record static bool IsAllowedKind(SyntaxKind kind) => @@ -68,22 +68,20 @@ static bool IsAllowedKind(SyntaxKind kind) => // Try and get the parent syntax. If it isn't a type like class/struct, this will be null var parentSyntax = typeSyntax.Parent as TypeDeclarationSyntax; - ParentClass? parentClassInfo = null; // Keep looping while we're in a supported nested type while (parentSyntax != null && IsAllowedKind(parentSyntax.Kind())) { // Record the parent type keyword (class/struct etc), name, and constraints - parentClassInfo = new ParentClass( + parents.Push(new ParentClass( keyword: parentSyntax.Keyword.ValueText, - name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList, - child: parentClassInfo); + name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList)); // Move to the next outer type parentSyntax = (parentSyntax.Parent as TypeDeclarationSyntax); } - return parentClassInfo; + return parents; } } From a275e12654cfbcbd0df217892ef06985f038fc95 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Sun, 12 May 2024 11:56:54 -0400 Subject: [PATCH 15/69] Write all generated classes to a single file to avoid ambiguous hint names --- ...HasAttributeInGlobalNamespace.verified.txt | 6 +++-- ...Tests.HasAttributeInNamespace.verified.txt | 6 +++-- ...NamespaceAndClassWithGenerics.verified.txt | 15 ++++-------- .../Tests.NoAttribute.verified.txt | 2 +- .../Tests.cs | 16 ++++++------- .../CodeWriter.cs | 9 ++++--- .../IndentedStringBuilder.cs | 7 ++++++ .../UsesVerifyGenerator.cs | 24 +++++++++++++++---- 8 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt index 7e00b7ada..cf138e4a4 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt @@ -1,4 +1,6 @@ -[ +{ + Item1: UsesVerify.g.cs, + Item2: //----------------------------------------------------- // This code was generated by a tool. // @@ -17,4 +19,4 @@ partial class Foo } } -] \ No newline at end of file +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt index ccd17bc1e..8cde62cca 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -1,4 +1,6 @@ -[ +{ + Item1: UsesVerify.g.cs, + Item2: //----------------------------------------------------- // This code was generated by a tool. // @@ -20,4 +22,4 @@ namespace Foo } } -] \ No newline at end of file +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt index 14e5f581c..691771601 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -1,4 +1,6 @@ -[ +{ + Item1: UsesVerify.g.cs, + Item2: //----------------------------------------------------- // This code was generated by a tool. // @@ -26,15 +28,6 @@ namespace A.B } } - -//----------------------------------------------------- -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior -// and will be lost when the code is regenerated. -// -//----------------------------------------------------- - namespace A.B { partial class C @@ -54,4 +47,4 @@ namespace A.B } } -] +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt index ad47dbb93..f11778892 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.NoAttribute.verified.txt @@ -1 +1 @@ -[] \ No newline at end of file +{} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index f5922aeed..cd082f7b1 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -17,8 +17,8 @@ public class Foo .Run(source) .Results .SelectMany(grr => grr.GeneratedSources) - .Select(gs => gs.SourceText.ToString()) - .ToArray(); + .Select(gs => (gs.HintName, gs.SourceText.ToString())) + .SingleOrDefault(); // TODO: Why is static using not working? return Verifier.Verify(results); @@ -40,8 +40,8 @@ public class Foo .Run(source) .Results .SelectMany(grr => grr.GeneratedSources) - .Select(gs => gs.SourceText.ToString()) - .ToArray(); + .Select(gs => (gs.HintName, gs.SourceText.ToString())) + .SingleOrDefault(); // TODO: Why is static using not working? return Verifier.Verify(results); @@ -65,8 +65,8 @@ public class Bar .Run(source) .Results .SelectMany(grr => grr.GeneratedSources) - .Select(gs => gs.SourceText.ToString()) - .ToArray(); + .Select(gs => (gs.HintName, gs.SourceText.ToString())) + .SingleOrDefault(); // TODO: Why is static using not working? return Verifier.Verify(results); @@ -105,8 +105,8 @@ public partial class TestClass2 .Run(source) .Results .SelectMany(grr => grr.GeneratedSources) - .Select(gs => gs.SourceText.ToString()) - .ToArray(); + .Select(gs => (gs.HintName, gs.SourceText.ToString())) + .SingleOrDefault(); // TODO: Why is static using not working? return Verifier.Verify(results); diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs index cd149eaf0..fd883b2b8 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/CodeWriter.cs @@ -10,7 +10,6 @@ static class CodeWriter // and will be lost when the code is regenerated. // //----------------------------------------------------- - """; private static readonly string GeneratedCodeAttribute = @@ -76,13 +75,17 @@ private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classTo .AppendLine("}"); } - public static string GenerateExtensionClass(ClassToGenerate classToGenerate) + public static string GenerateExtensionClasses(IReadOnlyCollection classesToGenerate) { var sb = new IndentedStringBuilder(); sb.AppendLine(AutoGenerationHeader); - WriteNamespace(sb, classToGenerate); + foreach (var classToGenerate in classesToGenerate) + { + sb.AppendLine(); + WriteNamespace(sb, classToGenerate); + } return sb.ToString(); } diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 43eb0590e..06356f8e5 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -19,6 +19,13 @@ public IndentedStringBuilder DecreaseIndent() return this; } + public IndentedStringBuilder AppendLine() + { + builder.AppendLine(); + + return this; + } + public IndentedStringBuilder AppendLine(string line) { builder.Append(' ', indentLevel * 4); diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 552c537fa..2747ebb66 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -20,7 +21,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Where(static classToGenerate => classToGenerate is not null) .WithTrackingName(TrackingNames.RemovingNulls); - context.RegisterSourceOutput(classesToGenerate, Execute); + // Collect the classes to generate into a collection so that we can write them + // to a single file and avoid the issues of ambiguous hint names discussed in + // https://github.com/dotnet/roslyn/discussions/60272. + var classesAsCollection = classesToGenerate + .Collect(); + + context.RegisterSourceOutput(classesAsCollection, Execute); } private static bool IsSyntaxEligibleForGeneration(SyntaxNode node, CancellationToken ct) => node is ClassDeclarationSyntax; @@ -46,14 +53,21 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return new ClassToGenerate(@namespace, classSymbol.Name, typeParameters, parentClass); } - private static void Execute(SourceProductionContext context, ClassToGenerate? classToGenerate) + private static void Execute(SourceProductionContext context, ImmutableArray classesToGenerate) { - if (classToGenerate is { } value) + if (classesToGenerate.IsDefaultOrEmpty) { - var sourceCode = CodeWriter.GenerateExtensionClass(value); + return; + } - context.AddSource($"UsesVerify.{value.ClassName}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); + var classes = classesToGenerate.Distinct().OfType().ToArray(); + if (classes.Length == 0) + { + return; } + + var sourceCode = CodeWriter.GenerateExtensionClasses(classes); + context.AddSource("UsesVerify.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); } private static IReadOnlyCollection GetParentClasses(BaseTypeDeclarationSyntax typeSyntax) From 7b5a9c309a3a989f83e2a6264bea66b1980d03a9 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Sun, 12 May 2024 18:37:41 -0400 Subject: [PATCH 16/69] Extract parsing to its own class --- .../{CodeWriter.cs => Emitter.cs} | 16 +++--- .../IndentedStringBuilder.cs | 12 ++++- src/Verify.MSTest.SourceGenerator/Parser.cs | 54 +++++++++++++++++++ .../UsesVerifyGenerator.cs | 43 ++------------- 4 files changed, 79 insertions(+), 46 deletions(-) rename src/Verify.MSTest.SourceGenerator/{CodeWriter.cs => Emitter.cs} (85%) create mode 100644 src/Verify.MSTest.SourceGenerator/Parser.cs diff --git a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs similarity index 85% rename from src/Verify.MSTest.SourceGenerator/CodeWriter.cs rename to src/Verify.MSTest.SourceGenerator/Emitter.cs index fd883b2b8..9a69daa1e 100644 --- a/src/Verify.MSTest.SourceGenerator/CodeWriter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -1,6 +1,6 @@ namespace Verify.MSTest.SourceGenerator; -static class CodeWriter +class Emitter { private static readonly string AutoGenerationHeader = """ //----------------------------------------------------- @@ -13,7 +13,9 @@ static class CodeWriter """; private static readonly string GeneratedCodeAttribute = - $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(CodeWriter).Assembly.GetName().Name}\", \"{typeof(CodeWriter).Assembly.GetName().Version}\")]"; + $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(Emitter).Assembly.GetName().Name}\", \"{typeof(Emitter).Assembly.GetName().Version}\")]"; + + private static readonly IndentedStringBuilder IndentedStringBuilder = new(1024); private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { @@ -77,16 +79,16 @@ private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classTo public static string GenerateExtensionClasses(IReadOnlyCollection classesToGenerate) { - var sb = new IndentedStringBuilder(); + IndentedStringBuilder.Clear(); - sb.AppendLine(AutoGenerationHeader); + IndentedStringBuilder.AppendLine(AutoGenerationHeader); foreach (var classToGenerate in classesToGenerate) { - sb.AppendLine(); - WriteNamespace(sb, classToGenerate); + IndentedStringBuilder.AppendLine(); + WriteNamespace(IndentedStringBuilder, classToGenerate); } - return sb.ToString(); + return IndentedStringBuilder.ToString(); } } diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 06356f8e5..16cee819f 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -2,9 +2,11 @@ namespace Verify.MSTest.SourceGenerator; class IndentedStringBuilder { - private readonly StringBuilder builder = new StringBuilder(); + private readonly StringBuilder builder; private int indentLevel = 0; + public IndentedStringBuilder(int capacity) => builder = new StringBuilder(capacity); + public IndentedStringBuilder IncreaseIndent() { indentLevel += 1; @@ -65,5 +67,13 @@ public IndentedStringBuilder Append(IEnumerable strings) return this; } + public IndentedStringBuilder Clear() + { + indentLevel = 0; + builder.Clear(); + + return this; + } + public override string ToString() => builder.ToString(); } diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs new file mode 100644 index 000000000..ca48b489a --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -0,0 +1,54 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Verify.MSTest.SourceGenerator; + +static class Parser +{ + public static ClassToGenerate Parse(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeSyntax) + { + var ns = GetNamespace(typeSymbol); + var typeParameters = GetTypeParameters(typeSyntax); + var parents = GetParentClasses(typeSyntax); + + return new ClassToGenerate(ns, typeSymbol.Name, typeParameters, parents); + } + + private static string[] GetTypeParameters(TypeDeclarationSyntax typeSyntax) => + typeSyntax + .TypeParameterList? + .Parameters + .Select(p => p.Identifier.ToString()) + .ToArray() ?? []; + private static string? GetNamespace(INamedTypeSymbol symbol) => + symbol.ContainingNamespace.IsGlobalNamespace ? null : symbol.ContainingNamespace.ToString(); + + private static IReadOnlyCollection GetParentClasses(TypeDeclarationSyntax typeSyntax) + { + // We can only be nested in class/struct/record + static bool IsAllowedKind(SyntaxKind kind) => + kind == SyntaxKind.ClassDeclaration || + kind == SyntaxKind.StructDeclaration || + kind == SyntaxKind.RecordDeclaration; + + var parents = new Stack(); + + // Try and get the parent syntax. If it isn't a type like class/struct, this will be null + var parentSyntax = typeSyntax.Parent as TypeDeclarationSyntax; + + // Keep looping while we're in a supported nested type + while (parentSyntax != null && IsAllowedKind(parentSyntax.Kind())) + { + // Record the parent type keyword (class/struct etc), name, and constraints + parents.Push(new ParentClass( + keyword: parentSyntax.Keyword.ValueText, + name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList)); + + // Move to the next outer type + parentSyntax = parentSyntax.Parent as TypeDeclarationSyntax; + } + + return parents; + } +} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 2747ebb66..93a9e4320 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -1,6 +1,5 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; @@ -10,6 +9,7 @@ namespace Verify.MSTest.SourceGenerator; public class UsesVerifyGenerator : IIncrementalGenerator { private static string MarkerAttributeName { get; } = "VerifyMSTest.UsesVerifyAttribute"; + public void Initialize(IncrementalGeneratorInitializationContext context) { var classesToGenerate = context.SyntaxProvider @@ -34,23 +34,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context) static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) { - if (context.TargetSymbol is not INamedTypeSymbol classSymbol) + if (context.TargetSymbol is not INamedTypeSymbol typeSymbol) { return null; } - if (context.TargetNode is not BaseTypeDeclarationSyntax declarationSyntax) + if (context.TargetNode is not TypeDeclarationSyntax typeSyntax) { return null; } ct.ThrowIfCancellationRequested(); - var @namespace = classSymbol.ContainingNamespace.IsGlobalNamespace ? null : classSymbol.ContainingNamespace.ToString(); - var typeParameters = classSymbol.TypeParameters.Select(tp => tp.Name).ToArray(); // TODO: May be able to use Syntax instead - var parentClass = GetParentClasses(declarationSyntax); - - return new ClassToGenerate(@namespace, classSymbol.Name, typeParameters, parentClass); + return Parser.Parse(typeSymbol, typeSyntax); } private static void Execute(SourceProductionContext context, ImmutableArray classesToGenerate) @@ -66,36 +62,7 @@ private static void Execute(SourceProductionContext context, ImmutableArray GetParentClasses(BaseTypeDeclarationSyntax typeSyntax) - { - var parents = new Stack(); - - // We can only be nested in class/struct/record - static bool IsAllowedKind(SyntaxKind kind) => - kind == SyntaxKind.ClassDeclaration || - kind == SyntaxKind.StructDeclaration || - kind == SyntaxKind.RecordDeclaration; - - // Try and get the parent syntax. If it isn't a type like class/struct, this will be null - var parentSyntax = typeSyntax.Parent as TypeDeclarationSyntax; - - // Keep looping while we're in a supported nested type - while (parentSyntax != null && IsAllowedKind(parentSyntax.Kind())) - { - // Record the parent type keyword (class/struct etc), name, and constraints - parents.Push(new ParentClass( - keyword: parentSyntax.Keyword.ValueText, - name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList)); - - // Move to the next outer type - parentSyntax = (parentSyntax.Parent as TypeDeclarationSyntax); - } - - return parents; - - } } From d64246ba4fa5f3a3002a4ef5e3e969d694c29513 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Sun, 12 May 2024 18:48:42 -0400 Subject: [PATCH 17/69] Simplify how type parameters are handled --- .../ClassToGenerate.cs | 4 +--- src/Verify.MSTest.SourceGenerator/Emitter.cs | 16 ++-------------- src/Verify.MSTest.SourceGenerator/Parser.cs | 11 +++++++---- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index c23f4a553..590c12fb9 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -4,14 +4,12 @@ readonly record struct ClassToGenerate { public string? Namespace { get; } public string ClassName { get; } - public IReadOnlyCollection TypeParameters { get; } public IReadOnlyCollection ParentClasses { get; } - public ClassToGenerate(string? @namespace, string className, IReadOnlyCollection typeParameters, IReadOnlyCollection parentClasses) + public ClassToGenerate(string? @namespace, string className, IReadOnlyCollection parentClasses) { Namespace = @namespace; ClassName = className; - TypeParameters = typeParameters; ParentClasses = parentClasses; } } diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index 9a69daa1e..a146cc4bd 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -54,20 +54,9 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c } } - private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) - { - // TODO: Update to put all generated classes in the same file - - var genericConstraints = string.Empty; - - if (classToGenerate.TypeParameters.Count > 0) - { - // TODO: Reimplement for perf - genericConstraints = $"<{string.Join(", ", classToGenerate.TypeParameters)}>"; - } - + private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) => sb.AppendLine(GeneratedCodeAttribute) - .AppendLine($"partial class {classToGenerate.ClassName}{genericConstraints}") // TODO: Reimplement for perf + .AppendLine($"partial class {classToGenerate.ClassName}") .AppendLine("{") .AppendLine(" public TestContext TestContext") .AppendLine(" {") @@ -75,7 +64,6 @@ private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classTo .AppendLine(" set => CurrentTestContext.Value = value;") .AppendLine(" }") .AppendLine("}"); - } public static string GenerateExtensionClasses(IReadOnlyCollection classesToGenerate) { diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index ca48b489a..658569880 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -9,10 +9,10 @@ static class Parser public static ClassToGenerate Parse(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeSyntax) { var ns = GetNamespace(typeSymbol); - var typeParameters = GetTypeParameters(typeSyntax); + var name = GetTypeNameWithGenericParameters(typeSyntax); var parents = GetParentClasses(typeSyntax); - return new ClassToGenerate(ns, typeSymbol.Name, typeParameters, parents); + return new ClassToGenerate(ns, name, parents); } private static string[] GetTypeParameters(TypeDeclarationSyntax typeSyntax) => @@ -43,7 +43,7 @@ static bool IsAllowedKind(SyntaxKind kind) => // Record the parent type keyword (class/struct etc), name, and constraints parents.Push(new ParentClass( keyword: parentSyntax.Keyword.ValueText, - name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList)); + name: GetTypeNameWithGenericParameters(parentSyntax))); // Move to the next outer type parentSyntax = parentSyntax.Parent as TypeDeclarationSyntax; @@ -51,4 +51,7 @@ static bool IsAllowedKind(SyntaxKind kind) => return parents; } + + private static string GetTypeNameWithGenericParameters(TypeDeclarationSyntax typeSyntax) => + typeSyntax.Identifier.ToString() + typeSyntax.TypeParameterList; } \ No newline at end of file From b981557eb89895c2863371fce99466dcfcac0bf9 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Sun, 12 May 2024 19:09:51 -0400 Subject: [PATCH 18/69] Simplify StringBuilder usage --- .../ClassToGenerate.cs | 2 + src/Verify.MSTest.SourceGenerator/Emitter.cs | 16 +++++--- .../IndentedStringBuilder.cs | 39 +++++++++---------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index 590c12fb9..f0805d50a 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -6,6 +6,8 @@ readonly record struct ClassToGenerate public string ClassName { get; } public IReadOnlyCollection ParentClasses { get; } + // TODO: Double-check if collection should be value value equatable + public ClassToGenerate(string? @namespace, string className, IReadOnlyCollection parentClasses) { Namespace = @namespace; diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index a146cc4bd..bab546404 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -1,6 +1,6 @@ namespace Verify.MSTest.SourceGenerator; -class Emitter +static class Emitter { private static readonly string AutoGenerationHeader = """ //----------------------------------------------------- @@ -15,13 +15,14 @@ class Emitter private static readonly string GeneratedCodeAttribute = $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(Emitter).Assembly.GetName().Name}\", \"{typeof(Emitter).Assembly.GetName().Version}\")]"; - private static readonly IndentedStringBuilder IndentedStringBuilder = new(1024); + private static readonly IndentedStringBuilder IndentedStringBuilder = new(); private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { if (classToGenerate.Namespace is not null) { - sb.AppendLine(["namespace ", classToGenerate.Namespace]) + sb.Append("namespace ") + .AppendLine(classToGenerate.Namespace, indent: false) .AppendLine("{") .IncreaseIndent(); } @@ -39,7 +40,10 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c { foreach (var parentClass in classToGenerate.ParentClasses) { - sb.AppendLine(["partial ", parentClass.Keyword, " ", parentClass.Name]) + sb.Append("partial ") + .Append(parentClass.Keyword, indent: false) + .Append(" ", indent: false) + .AppendLine(parentClass.Name, indent: false) .AppendLine("{"); sb.IncreaseIndent(); @@ -56,7 +60,8 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) => sb.AppendLine(GeneratedCodeAttribute) - .AppendLine($"partial class {classToGenerate.ClassName}") + .Append("partial class ") + .AppendLine(classToGenerate.ClassName, indent: false) .AppendLine("{") .AppendLine(" public TestContext TestContext") .AppendLine(" {") @@ -68,7 +73,6 @@ private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classTo public static string GenerateExtensionClasses(IReadOnlyCollection classesToGenerate) { IndentedStringBuilder.Clear(); - IndentedStringBuilder.AppendLine(AutoGenerationHeader); foreach (var classToGenerate in classesToGenerate) diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 16cee819f..503ca9935 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -2,10 +2,13 @@ namespace Verify.MSTest.SourceGenerator; class IndentedStringBuilder { + // TODO: Tweak default capacity based on real-world usage + private const int DefaultStringBuilderCapacity = 1024; private readonly StringBuilder builder; private int indentLevel = 0; - public IndentedStringBuilder(int capacity) => builder = new StringBuilder(capacity); + public IndentedStringBuilder(int capacity = DefaultStringBuilderCapacity) => + builder = new StringBuilder(capacity); public IndentedStringBuilder IncreaseIndent() { @@ -24,41 +27,27 @@ public IndentedStringBuilder DecreaseIndent() public IndentedStringBuilder AppendLine() { builder.AppendLine(); - return this; } - public IndentedStringBuilder AppendLine(string line) + public IndentedStringBuilder AppendLine(string line, bool indent = true) { - builder.Append(' ', indentLevel * 4); + WriteIndentIfNeeded(indent); builder.AppendLine(line); - return this; } - public IndentedStringBuilder AppendLine(IEnumerable strings) + public IndentedStringBuilder Append(string text, bool indent = true) { - builder.Append(' ', indentLevel * 4); - foreach (var s in strings) - { - builder.Append(s); - } - builder.AppendLine(); - - return this; - } - - public IndentedStringBuilder Append(string text) - { - builder.Append(' ', indentLevel * 4); + WriteIndentIfNeeded(indent); builder.Append(text); return this; } - public IndentedStringBuilder Append(IEnumerable strings) + public IndentedStringBuilder Append(IEnumerable strings, bool indent = true) { - builder.Append(' ', indentLevel * 4); + WriteIndentIfNeeded(indent); foreach (var s in strings) { builder.Append(s); @@ -76,4 +65,12 @@ public IndentedStringBuilder Clear() } public override string ToString() => builder.ToString(); + + private void WriteIndentIfNeeded(bool indent) + { + if (indent) + { + builder.Append(' ', indentLevel * 4); + } + } } From fd8652ecd48b661b2270588474904e42f8b32def Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Sun, 12 May 2024 20:20:14 -0400 Subject: [PATCH 19/69] Simplify StringBuilder usage --- src/Verify.MSTest.SourceGenerator/Emitter.cs | 11 +++----- .../IndentedStringBuilder.cs | 25 +++++++++++-------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index bab546404..f782d73ae 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -21,8 +21,7 @@ private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate cla { if (classToGenerate.Namespace is not null) { - sb.Append("namespace ") - .AppendLine(classToGenerate.Namespace, indent: false) + sb.Append("namespace ").AppendLine(classToGenerate.Namespace) .AppendLine("{") .IncreaseIndent(); } @@ -40,10 +39,7 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c { foreach (var parentClass in classToGenerate.ParentClasses) { - sb.Append("partial ") - .Append(parentClass.Keyword, indent: false) - .Append(" ", indent: false) - .AppendLine(parentClass.Name, indent: false) + sb.Append("partial ").Append(parentClass.Keyword).Append(" ").AppendLine(parentClass.Name) .AppendLine("{"); sb.IncreaseIndent(); @@ -60,8 +56,7 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) => sb.AppendLine(GeneratedCodeAttribute) - .Append("partial class ") - .AppendLine(classToGenerate.ClassName, indent: false) + .Append("partial class ").AppendLine(classToGenerate.ClassName) .AppendLine("{") .AppendLine(" public TestContext TestContext") .AppendLine(" {") diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 503ca9935..87523fdb8 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -6,6 +6,7 @@ class IndentedStringBuilder private const int DefaultStringBuilderCapacity = 1024; private readonly StringBuilder builder; private int indentLevel = 0; + private bool isIndented = false; public IndentedStringBuilder(int capacity = DefaultStringBuilderCapacity) => builder = new StringBuilder(capacity); @@ -27,32 +28,34 @@ public IndentedStringBuilder DecreaseIndent() public IndentedStringBuilder AppendLine() { builder.AppendLine(); + isIndented = false; return this; } - public IndentedStringBuilder AppendLine(string line, bool indent = true) + public IndentedStringBuilder AppendLine(string line) { - WriteIndentIfNeeded(indent); + WriteIndentIfNeeded(); builder.AppendLine(line); + isIndented = false; return this; } - public IndentedStringBuilder Append(string text, bool indent = true) + public IndentedStringBuilder Append(string text) { - WriteIndentIfNeeded(indent); + WriteIndentIfNeeded(); builder.Append(text); - + isIndented = true; return this; } - public IndentedStringBuilder Append(IEnumerable strings, bool indent = true) + public IndentedStringBuilder Append(IEnumerable strings) { - WriteIndentIfNeeded(indent); + WriteIndentIfNeeded(); foreach (var s in strings) { builder.Append(s); } - + isIndented = true; return this; } @@ -60,15 +63,15 @@ public IndentedStringBuilder Clear() { indentLevel = 0; builder.Clear(); - + isIndented = false; return this; } public override string ToString() => builder.ToString(); - private void WriteIndentIfNeeded(bool indent) + private void WriteIndentIfNeeded() { - if (indent) + if (!isIndented) { builder.Append(' ', indentLevel * 4); } From 9e29380c7dfcc68acca63219e088a6ad5c64f87d Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Sun, 12 May 2024 21:25:13 -0400 Subject: [PATCH 20/69] Use EquatableArray --- .../ClassToGenerate.cs | 8 +- .../EquatableArray.cs | 100 +++++ src/Verify.MSTest.SourceGenerator/HashCode.cs | 382 ++++++++++++++++++ src/Verify.MSTest.SourceGenerator/Parser.cs | 12 +- .../TrackingNames.cs | 5 +- .../UsesVerifyGenerator.cs | 15 +- 6 files changed, 499 insertions(+), 23 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator/EquatableArray.cs create mode 100644 src/Verify.MSTest.SourceGenerator/HashCode.cs diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index f0805d50a..e1ad0d91c 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -4,14 +4,12 @@ readonly record struct ClassToGenerate { public string? Namespace { get; } public string ClassName { get; } - public IReadOnlyCollection ParentClasses { get; } + public EquatableArray ParentClasses { get; } - // TODO: Double-check if collection should be value value equatable - - public ClassToGenerate(string? @namespace, string className, IReadOnlyCollection parentClasses) + public ClassToGenerate(string? @namespace, string className, ParentClass[] parentClasses) { Namespace = @namespace; ClassName = className; - ParentClasses = parentClasses; + ParentClasses = new EquatableArray(parentClasses); } } diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs new file mode 100644 index 000000000..8c64de16e --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -0,0 +1,100 @@ +using System.Collections.Immutable; + +namespace Verify.MSTest.SourceGenerator; + +/// +/// An immutable, equatable array. This is equivalent to but with value equality support. +/// +/// The type of values in the array. +public readonly struct EquatableArray : IEquatable>, IEnumerable + where T : IEquatable +{ + /// + /// The underlying array. + /// + private readonly T[]? _array; + + /// + /// Creates a new instance. + /// + /// The input to wrap. + public EquatableArray(T[] array) + { + _array = array; + } + + /// + public bool Equals(EquatableArray array) + { + return AsSpan().SequenceEqual(array.AsSpan()); + } + + /// + public override bool Equals(object? obj) + { + return obj is EquatableArray array && Equals(this, array); + } + + /// + public override int GetHashCode() + { + if (_array is not T[] array) + { + return 0; + } + + HashCode hashCode = default; + + foreach (var item in array) + { + hashCode.Add(item); + } + + return hashCode.ToHashCode(); + } + + /// + /// Returns a wrapping the current items. + /// + /// A wrapping the current items. + public ReadOnlySpan AsSpan() + { + return _array.AsSpan(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)(_array ?? Array.Empty())).GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)(_array ?? Array.Empty())).GetEnumerator(); + } + + public int Count => _array?.Length ?? 0; + + /// + /// Checks whether two values are the same. + /// + /// The first value. + /// The second value. + /// Whether and are equal. + public static bool operator ==(EquatableArray left, EquatableArray right) + { + return left.Equals(right); + } + + /// + /// Checks whether two values are not the same. + /// + /// The first value. + /// The second value. + /// Whether and are not equal. + public static bool operator !=(EquatableArray left, EquatableArray right) + { + return !left.Equals(right); + } +} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs new file mode 100644 index 000000000..c423f3295 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/HashCode.cs @@ -0,0 +1,382 @@ +using System.ComponentModel; + +namespace Verify.MSTest.SourceGenerator; + +// TODO: Consider adding this to Polyfill + +/// +/// Polyfill for .NET 6 HashCode +/// +internal struct HashCode +{ + private static readonly uint s_seed = GenerateGlobalSeed(); + + private const uint Prime1 = 2654435761U; + private const uint Prime2 = 2246822519U; + private const uint Prime3 = 3266489917U; + private const uint Prime4 = 668265263U; + private const uint Prime5 = 374761393U; + + private uint _v1, _v2, _v3, _v4; + private uint _queue1, _queue2, _queue3; + private uint _length; + + private static uint GenerateGlobalSeed() + { + var buffer = new byte[sizeof(uint)]; + new Random().NextBytes(buffer); + return BitConverter.ToUInt32(buffer, 0); + } + + public static int Combine(T1 value1) + { + // Provide a way of diffusing bits from something with a limited + // input hash space. For example, many enums only have a few + // possible hashes, only using the bottom few bits of the code. Some + // collections are built on the assumption that hashes are spread + // over a larger space, so diffusing the bits may help the + // collection work more efficiently. + + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 4; + + hash = QueueRound(hash, hc1); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 8; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + + uint hash = MixEmptyState(); + hash += 12; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = QueueRound(hash, hc3); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 16; + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 20; + + hash = QueueRound(hash, hc5); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + uint hc6 = (uint)(value6?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 24; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + uint hc6 = (uint)(value6?.GetHashCode() ?? 0); + uint hc7 = (uint)(value7?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + uint hash = MixState(v1, v2, v3, v4); + hash += 28; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = QueueRound(hash, hc7); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + uint hc1 = (uint)(value1?.GetHashCode() ?? 0); + uint hc2 = (uint)(value2?.GetHashCode() ?? 0); + uint hc3 = (uint)(value3?.GetHashCode() ?? 0); + uint hc4 = (uint)(value4?.GetHashCode() ?? 0); + uint hc5 = (uint)(value5?.GetHashCode() ?? 0); + uint hc6 = (uint)(value6?.GetHashCode() ?? 0); + uint hc7 = (uint)(value7?.GetHashCode() ?? 0); + uint hc8 = (uint)(value8?.GetHashCode() ?? 0); + + Initialize(out uint v1, out uint v2, out uint v3, out uint v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + v1 = Round(v1, hc5); + v2 = Round(v2, hc6); + v3 = Round(v3, hc7); + v4 = Round(v4, hc8); + + uint hash = MixState(v1, v2, v3, v4); + hash += 32; + + hash = MixFinal(hash); + return (int)hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = s_seed + Prime1 + Prime2; + v2 = s_seed + Prime2; + v3 = s_seed; + v4 = s_seed - Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Round(uint hash, uint input) + { + return RotateLeft(hash + input * Prime2, 13) * Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint QueueRound(uint hash, uint queuedValue) + { + return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixState(uint v1, uint v2, uint v3, uint v4) + { + return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18); + } + + private static uint MixEmptyState() + { + return s_seed + Prime5; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + public void Add(T value) + { + Add(value?.GetHashCode() ?? 0); + } + + public void Add(T value, IEqualityComparer? comparer) + { + Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode())); + } + + private void Add(int value) + { + // The original xxHash works as follows: + // 0. Initialize immediately. We can't do this in a struct (no + // default ctor). + // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. + // 2. Accumulate remaining blocks of length 4 (1 uint) into the + // hash. + // 3. Accumulate remaining blocks of length 1 into the hash. + + // There is no need for #3 as this type only accepts ints. _queue1, + // _queue2 and _queue3 are basically a buffer so that when + // ToHashCode is called we can execute #2 correctly. + + // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see + // #0) nd the last place that can be done if you look at the + // original code is just before the first block of 16 bytes is mixed + // in. The xxHash32 state is never used for streams containing fewer + // than 16 bytes. + + // To see what's really going on here, have a look at the Combine + // methods. + + uint val = (uint)value; + + // Storing the value of _length locally shaves off quite a few bytes + // in the resulting machine code. + uint previousLength = _length++; + uint position = previousLength % 4; + + // Switch can't be inlined. + + if (position == 0) + _queue1 = val; + else if (position == 1) + _queue2 = val; + else if (position == 2) + _queue3 = val; + else // position == 3 + { + if (previousLength == 3) + Initialize(out _v1, out _v2, out _v3, out _v4); + + _v1 = Round(_v1, _queue1); + _v2 = Round(_v2, _queue2); + _v3 = Round(_v3, _queue3); + _v4 = Round(_v4, val); + } + } + + public int ToHashCode() + { + // Storing the value of _length locally shaves off quite a few bytes + // in the resulting machine code. + uint length = _length; + + // position refers to the *next* queue position in this method, so + // position == 1 means that _queue1 is populated; _queue2 would have + // been populated on the next call to Add. + uint position = length % 4; + + // If the length is less than 4, _v1 to _v4 don't contain anything + // yet. xxHash32 treats this differently. + + uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); + + // _length is incremented once per Add(Int32) and is therefore 4 + // times too small (xxHash length is in bytes, not ints). + + hash += length * 4; + + // Mix what remains in the queue + + // Switch can't be inlined right now, so use as few branches as + // possible by manually excluding impossible scenarios (position > 1 + // is always false if position is not > 0). + if (position > 0) + { + hash = QueueRound(hash, _queue1); + if (position > 1) + { + hash = QueueRound(hash, _queue2); + if (position > 2) + hash = QueueRound(hash, _queue3); + } + } + + hash = MixFinal(hash); + return (int)hash; + } + +#pragma warning disable 0809 + // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. + // Disallowing GetHashCode and Equals is by design + + // * We decided to not override GetHashCode() to produce the hash code + // as this would be weird, both naming-wise as well as from a + // behavioral standpoint (GetHashCode() should return the object's + // hash code, not the one being computed). + + // * Even though ToHashCode() can be called safely multiple times on + // this implementation, it is not part of the contract. If the + // implementation has to change in the future we don't want to worry + // about people who might have incorrectly used this type. + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException("Hash code not supported"); + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) => throw new NotSupportedException("Equality not supported"); +#pragma warning restore 0809 + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint RotateLeft(uint value, int offset) + => (value << offset) | (value >> (32 - offset)); +} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index 658569880..a56958190 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -6,6 +6,8 @@ namespace Verify.MSTest.SourceGenerator; static class Parser { + public static string MarkerAttributeName { get; } = "VerifyMSTest.UsesVerifyAttribute"; + public static ClassToGenerate Parse(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeSyntax) { var ns = GetNamespace(typeSymbol); @@ -15,16 +17,10 @@ public static ClassToGenerate Parse(INamedTypeSymbol typeSymbol, TypeDeclaration return new ClassToGenerate(ns, name, parents); } - private static string[] GetTypeParameters(TypeDeclarationSyntax typeSyntax) => - typeSyntax - .TypeParameterList? - .Parameters - .Select(p => p.Identifier.ToString()) - .ToArray() ?? []; private static string? GetNamespace(INamedTypeSymbol symbol) => symbol.ContainingNamespace.IsGlobalNamespace ? null : symbol.ContainingNamespace.ToString(); - private static IReadOnlyCollection GetParentClasses(TypeDeclarationSyntax typeSyntax) + private static ParentClass[] GetParentClasses(TypeDeclarationSyntax typeSyntax) { // We can only be nested in class/struct/record static bool IsAllowedKind(SyntaxKind kind) => @@ -49,7 +45,7 @@ static bool IsAllowedKind(SyntaxKind kind) => parentSyntax = parentSyntax.Parent as TypeDeclarationSyntax; } - return parents; + return parents.ToArray(); } private static string GetTypeNameWithGenericParameters(TypeDeclarationSyntax typeSyntax) => diff --git a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs index 885c7d644..7f05c21a6 100644 --- a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs +++ b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs @@ -1,7 +1,8 @@ -namespace Verify.MSTest.SourceGenerator; +namespace Verify.MSTest.SourceGenerator; static class TrackingNames { public static string InitialTransform { get; } = nameof(InitialTransform); - public static string RemovingNulls { get; } = nameof(RemovingNulls); + public static string RemoveNulls { get; } = nameof(RemoveNulls); + public static string Collect { get; } = nameof(Collect); } diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 93a9e4320..de44da739 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -8,31 +8,30 @@ namespace Verify.MSTest.SourceGenerator; [Generator] public class UsesVerifyGenerator : IIncrementalGenerator { - private static string MarkerAttributeName { get; } = "VerifyMSTest.UsesVerifyAttribute"; - public void Initialize(IncrementalGeneratorInitializationContext context) { var classesToGenerate = context.SyntaxProvider .ForAttributeWithMetadataName( - fullyQualifiedMetadataName: MarkerAttributeName, + fullyQualifiedMetadataName: Parser.MarkerAttributeName, predicate: IsSyntaxEligibleForGeneration, transform: GetSemanticTargetForGeneration) .WithTrackingName(TrackingNames.InitialTransform) .Where(static classToGenerate => classToGenerate is not null) - .WithTrackingName(TrackingNames.RemovingNulls); + .WithTrackingName(TrackingNames.RemoveNulls); // Collect the classes to generate into a collection so that we can write them // to a single file and avoid the issues of ambiguous hint names discussed in // https://github.com/dotnet/roslyn/discussions/60272. - var classesAsCollection = classesToGenerate - .Collect(); + var classesCollection = classesToGenerate + .Collect() + .WithTrackingName(TrackingNames.Collect); - context.RegisterSourceOutput(classesAsCollection, Execute); + context.RegisterSourceOutput(classesCollection, Execute); } private static bool IsSyntaxEligibleForGeneration(SyntaxNode node, CancellationToken ct) => node is ClassDeclarationSyntax; - static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) + private static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) { if (context.TargetSymbol is not INamedTypeSymbol typeSymbol) { From c8cff10f383f7c0b093c5b03812e4ff02662701b Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Mon, 13 May 2024 14:18:35 -0400 Subject: [PATCH 21/69] Clean up namespaces and usings --- .../TestDriver.cs | 5 ++--- ...ests.HasAttributeInGlobalNamespace.verified.txt | 4 ++-- .../Tests.HasAttributeInNamespace.verified.txt | 4 ++-- ...estedNamespaceAndClassWithGenerics.verified.txt | 8 ++++---- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 14 +++++--------- .../Verify.MSTest.SourceGenerator.Tests.csproj | 1 + .../ClassToGenerate.cs | 2 +- src/Verify.MSTest.SourceGenerator/Emitter.cs | 6 +++--- .../EquatableArray.cs | 11 ++++++++++- src/Verify.MSTest.SourceGenerator/HashCode.cs | 8 +++++++- .../IndentedStringBuilder.cs | 2 +- src/Verify.MSTest.SourceGenerator/ParentClass.cs | 2 +- src/Verify.MSTest.SourceGenerator/Parser.cs | 2 +- src/Verify.MSTest.SourceGenerator/TrackingNames.cs | 2 +- .../UsesVerifyGenerator.cs | 2 +- .../Verify.MSTest.SourceGenerator.csproj | 3 ++- 16 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index 4eee43c9a..7dac65091 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -1,8 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using VerifyMSTest; -namespace Verify.MSTest.SourceGenerator.Tests; +namespace VerifyMSTest.SourceGenerator.Tests; static class TestDriver { @@ -12,7 +11,7 @@ public static GeneratorDriverRunResult Run(string source) IReadOnlyCollection references = [ - MetadataReference.CreateFromFile(typeof(VerifyMSTest.UsesVerifyAttribute).Assembly.Location), + MetadataReference.CreateFromFile(typeof(UsesVerifyAttribute).Assembly.Location), MetadataReference.CreateFromFile(typeof(object).Assembly.Location) ]; diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt index cf138e4a4..37a02b9da 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInGlobalNamespace.verified.txt @@ -14,8 +14,8 @@ partial class Foo { public TestContext TestContext { - get => CurrentTestContext.Value!; - set => CurrentTestContext.Value = value; + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } } diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt index 8cde62cca..234a5a9c5 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -16,8 +16,8 @@ namespace Foo { public TestContext TestContext { - get => CurrentTestContext.Value!; - set => CurrentTestContext.Value = value; + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } } } diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt index 691771601..cf38bb523 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -20,8 +20,8 @@ namespace A.B { public TestContext TestContext { - get => CurrentTestContext.Value!; - set => CurrentTestContext.Value = value; + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } } } @@ -39,8 +39,8 @@ namespace A.B { public TestContext TestContext { - get => CurrentTestContext.Value!; - set => CurrentTestContext.Value = value; + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } } } diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index cd082f7b1..cd7b974c5 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -1,4 +1,4 @@ -namespace Verify.MSTest.SourceGenerator.Tests; +namespace VerifyMSTest.SourceGenerator.Tests; // These tests don't use Verify.SourceGenerator to avoid creating a circular depedency between the repos. @@ -20,8 +20,7 @@ public class Foo .Select(gs => (gs.HintName, gs.SourceText.ToString())) .SingleOrDefault(); - // TODO: Why is static using not working? - return Verifier.Verify(results); + return Verify(results); } [Fact] @@ -43,8 +42,7 @@ public class Foo .Select(gs => (gs.HintName, gs.SourceText.ToString())) .SingleOrDefault(); - // TODO: Why is static using not working? - return Verifier.Verify(results); + return Verify(results); } [Fact] @@ -68,8 +66,7 @@ public class Bar .Select(gs => (gs.HintName, gs.SourceText.ToString())) .SingleOrDefault(); - // TODO: Why is static using not working? - return Verifier.Verify(results); + return Verify(results); } [Fact] @@ -108,7 +105,6 @@ public partial class TestClass2 .Select(gs => (gs.HintName, gs.SourceText.ToString())) .SingleOrDefault(); - // TODO: Why is static using not working? - return Verifier.Verify(results); + return Verify(results); } } diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj index 1e27ea9f0..09cb81cf8 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj +++ b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj @@ -4,6 +4,7 @@ net9.0 false true + VerifyMSTest.SourceGenerator.Tests diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index e1ad0d91c..b496a2fa8 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -1,4 +1,4 @@ -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; readonly record struct ClassToGenerate { diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index f782d73ae..e689a4811 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -1,4 +1,4 @@ -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; static class Emitter { @@ -60,8 +60,8 @@ private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classTo .AppendLine("{") .AppendLine(" public TestContext TestContext") .AppendLine(" {") - .AppendLine(" get => CurrentTestContext.Value!;") - .AppendLine(" set => CurrentTestContext.Value = value;") + .AppendLine(" get => Verifier.CurrentTestContext.Value!;") + .AppendLine(" set => Verifier.CurrentTestContext.Value = value;") .AppendLine(" }") .AppendLine("}"); diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs index 8c64de16e..21889c96c 100644 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -1,6 +1,15 @@ using System.Collections.Immutable; -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; + +// TODO: Is there a better place for this to live? + +// Suppressing style warnings to keep code aligned with upstream version from +// https://github.com/andrewlock/StronglyTypedId/blob/e5df78d0aa72f2232f423938c0d98d9bf4517092/src/StronglyTypedIds/EquatableArray.cs +#pragma warning disable IDE1006 // Naming rule violation Prefix is not expected +#pragma warning disable IDE0021 // Use expression body for constructor +#pragma warning disable IDE0022 // Use expression body for method +#pragma warning disable IDE0024 // Use expression body for operator /// /// An immutable, equatable array. This is equivalent to but with value equality support. diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs index c423f3295..833a77586 100644 --- a/src/Verify.MSTest.SourceGenerator/HashCode.cs +++ b/src/Verify.MSTest.SourceGenerator/HashCode.cs @@ -1,9 +1,15 @@ using System.ComponentModel; -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; // TODO: Consider adding this to Polyfill +// Suppressing style warnings to keep code aligned with upstream version from +// https://github.com/andrewlock/NetEscapades.EnumGenerators/blob/cd3c9278b0ea3827f7e085c19e998ad0671a11ab/src/NetEscapades.EnumGenerators/HashCode.cs +#pragma warning disable IDE0007 // Use var instead of explicit type +#pragma warning disable IDE1006 // Naming rule violation Prefix is not expected +#pragma warning disable IDE0022 // Use expression body for method + /// /// Polyfill for .NET 6 HashCode /// diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 87523fdb8..89ebec4df 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -1,4 +1,4 @@ -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; class IndentedStringBuilder { diff --git a/src/Verify.MSTest.SourceGenerator/ParentClass.cs b/src/Verify.MSTest.SourceGenerator/ParentClass.cs index 0c1d5d908..00e0bd8f8 100644 --- a/src/Verify.MSTest.SourceGenerator/ParentClass.cs +++ b/src/Verify.MSTest.SourceGenerator/ParentClass.cs @@ -1,4 +1,4 @@ -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; readonly record struct ParentClass { diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index a56958190..2cf42b3f8 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; static class Parser { diff --git a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs index 7f05c21a6..b8adb3d5d 100644 --- a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs +++ b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs @@ -1,4 +1,4 @@ -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; static class TrackingNames { diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index de44da739..f1713a425 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; -namespace Verify.MSTest.SourceGenerator; +namespace VerifyMSTest.SourceGenerator; [Generator] public class UsesVerifyGenerator : IIncrementalGenerator diff --git a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj index 05e4f43e4..6cfc78e50 100644 --- a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj +++ b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj @@ -1,7 +1,8 @@  - netstandard2.0 + netstandard2.0 + VerifyMSTest.SourceGenerator From 3cd57c8ed8ec68781a743f3bcb4f40324e90cffe Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Mon, 13 May 2024 15:08:06 -0400 Subject: [PATCH 22/69] Add source generator to main package --- src/Directory.Build.props | 2 ++ src/Verify.MSTest.SourceGenerator/EquatableArray.cs | 2 +- .../Verify.MSTest.SourceGenerator.csproj | 8 +++----- src/Verify.MSTest.Tests/Tests.cs | 13 ------------- src/Verify.MSTest/Verify.MSTest.csproj | 4 ++++ src/Verify.MSTest/VerifyBase.cs | 12 ------------ src/global.json | 3 +++ 7 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e53327067..f0e06eb82 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -20,4 +20,6 @@ + + \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs index 21889c96c..8f8a3f962 100644 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -12,7 +12,7 @@ namespace VerifyMSTest.SourceGenerator; #pragma warning disable IDE0024 // Use expression body for operator /// -/// An immutable, equatable array. This is equivalent to but with value equality support. +/// An immutable, equatable array. This is equivalent to but with value equality support. /// /// The type of values in the array. public readonly struct EquatableArray : IEquatable>, IEnumerable diff --git a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj index 6cfc78e50..1663b9078 100644 --- a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj +++ b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj @@ -3,6 +3,9 @@ netstandard2.0 VerifyMSTest.SourceGenerator + cs + 4.4 + false @@ -10,9 +13,4 @@ - - - - diff --git a/src/Verify.MSTest.Tests/Tests.cs b/src/Verify.MSTest.Tests/Tests.cs index d74dd3a3c..63d6f336d 100644 --- a/src/Verify.MSTest.Tests/Tests.cs +++ b/src/Verify.MSTest.Tests/Tests.cs @@ -1,19 +1,6 @@ // TODO: Add a test that uses the base class to prevent regressions until it's deleted // TODO: Add a test for when the [TestClass] already has a TestContext property -#region WillBeSourceGenerated -partial class Tests -{ - [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Injected by the test framework per test.")] - public TestContext TestContext - { - get => CurrentTestContext.Value!; - set => CurrentTestContext.Value = value; - - } -} -#endregion - [TestClass] [UsesVerify] public partial class Tests diff --git a/src/Verify.MSTest/Verify.MSTest.csproj b/src/Verify.MSTest/Verify.MSTest.csproj index 4dae361a9..4271c7768 100644 --- a/src/Verify.MSTest/Verify.MSTest.csproj +++ b/src/Verify.MSTest/Verify.MSTest.csproj @@ -7,6 +7,10 @@ + diff --git a/src/Verify.MSTest/VerifyBase.cs b/src/Verify.MSTest/VerifyBase.cs index 510787252..228ebb492 100644 --- a/src/Verify.MSTest/VerifyBase.cs +++ b/src/Verify.MSTest/VerifyBase.cs @@ -1,17 +1,5 @@ namespace VerifyMSTest; -#region WillBeSourceGenerated -partial class VerifyBase -{ - [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Set by the test framework per test.")] - public TestContext TestContext - { - get => Verifier.CurrentTestContext.Value!; - set => Verifier.CurrentTestContext.Value = value; - } -} -#endregion - [TestClass] [UsesVerify] public abstract partial class VerifyBase diff --git a/src/global.json b/src/global.json index 35d6e0074..aa4afcb72 100644 --- a/src/global.json +++ b/src/global.json @@ -3,5 +3,8 @@ "version": "9.0.100-preview.3.24204.13", "allowPrerelease": true, "rollForward": "latestFeature" + }, + "msbuild-sdks": { + "ViHo.PackAsAnalyzer" : "1.0.1" } } \ No newline at end of file From 79d5b15f95b3c4428df1eb0a0a983a51ab852565 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Mon, 13 May 2024 16:08:20 -0400 Subject: [PATCH 23/69] Existing tests pass (still need to write / convert new ones) --- .../Verify.MSTest.Tests.csproj | 3 ++ src/Verify.MSTest/DerivePaths/Verifier.cs | 2 +- src/Verify.MSTest/DerivePaths/VerifyBase.cs | 7 +--- src/Verify.MSTest/Verifier.cs | 34 ++++++------------- 4 files changed, 15 insertions(+), 31 deletions(-) diff --git a/src/Verify.MSTest.Tests/Verify.MSTest.Tests.csproj b/src/Verify.MSTest.Tests/Verify.MSTest.Tests.csproj index 37538c613..524e866c3 100644 --- a/src/Verify.MSTest.Tests/Verify.MSTest.Tests.csproj +++ b/src/Verify.MSTest.Tests/Verify.MSTest.Tests.csproj @@ -14,6 +14,9 @@ + diff --git a/src/Verify.MSTest/DerivePaths/Verifier.cs b/src/Verify.MSTest/DerivePaths/Verifier.cs index 1056ce160..2fb910d30 100644 --- a/src/Verify.MSTest/DerivePaths/Verifier.cs +++ b/src/Verify.MSTest/DerivePaths/Verifier.cs @@ -3,7 +3,7 @@ #pragma warning disable VerifyTestsProjectDir namespace VerifyMSTest; -partial class Verifier +public partial class Verifier { static DerivePathInfo derivePathInfo = PathInfo.DeriveDefault; diff --git a/src/Verify.MSTest/DerivePaths/VerifyBase.cs b/src/Verify.MSTest/DerivePaths/VerifyBase.cs index 5cec86aa5..9fceaff22 100644 --- a/src/Verify.MSTest/DerivePaths/VerifyBase.cs +++ b/src/Verify.MSTest/DerivePaths/VerifyBase.cs @@ -5,11 +5,6 @@ namespace VerifyMSTest; public partial class VerifyBase { - static DerivePathInfo derivePathInfo = PathInfo.DeriveDefault; - - internal static PathInfo GetPathInfo(string sourceFile, Type type, MethodInfo method) => - derivePathInfo(sourceFile, VerifierSettings.ProjectDir, type, method); - /// /// Use custom path information for `.verified.` files. /// @@ -19,7 +14,7 @@ internal static PathInfo GetPathInfo(string sourceFile, Type type, MethodInfo me /// /// Custom callback to control the behavior. public static void DerivePathInfo(DerivePathInfo derivePathInfo) => - VerifyBase.derivePathInfo = derivePathInfo; + Verifier.DerivePathInfo(derivePathInfo); /// /// Use a directory relative to the project directory for storing for `.verified.` files. diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index c5ce613cd..30b27e664 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -1,3 +1,5 @@ +using System.Reflection; + namespace VerifyMSTest; public static partial class Verifier @@ -24,9 +26,6 @@ static Verifier() static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, bool useUniqueDirectory) { - var typeName = CurrentTestContext.Value?.FullyQualifiedTestClassName; - var type = FindType(typeName.AsSpan()); - if (useUniqueDirectory) { settings.UseUniqueDirectory(); @@ -35,33 +34,20 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b var testName = CurrentTestContext.Value?.TestName; if (testName == null) { - throw new("TestContext.TestName is null. Ensure being used inside a test"); - } - - var testNameSpan = testName.AsSpan(); - var indexOf = testNameSpan.IndexOf('('); - if (indexOf > 0) - { - testNameSpan = testNameSpan[..indexOf]; + throw new("TestContext.TestName is null. Ensure test class has a `[UsesVerify]` attribute."); } - indexOf = testNameSpan.IndexOf('.'); - if (indexOf > 0) - { - testNameSpan = testNameSpan[(indexOf + 1)..]; - } + var typeName = CurrentTestContext.Value?.FullyQualifiedTestClassName; + var type = FindType(typeName.AsSpan()); VerifierSettings.AssignTargetAssembly(type.Assembly); - var method = FindMethod(type, testNameSpan); + var method = FindMethod(type, testName.AsSpan()); + + sourceFile = IoHelpers.GetMappedBuildPath(sourceFile); + var fileName = Path.GetFileNameWithoutExtension(sourceFile); var pathInfo = GetPathInfo(sourceFile, type, method); - return new( - sourceFile, - settings, - type.NameWithParent(), - method.Name, - method.ParameterNames(), - pathInfo); + return new(sourceFile, settings, fileName, testName, method.ParameterNames(), pathInfo); } static Type FindType(ReadOnlySpan typeName) From 212b18e44bc8c831f04b576159135c95780ad6f6 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 11:28:29 -0400 Subject: [PATCH 24/69] Use reflection + lookup cache to speed up getting types --- src/Verify.MSTest.SourceGenerator/HashCode.cs | 7 +- src/Verify.MSTest.SourceGenerator/Parser.cs | 1 + src/Verify.MSTest/Verifier.cs | 113 ++++++++++++++---- 3 files changed, 95 insertions(+), 26 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs index 833a77586..e9a76d940 100644 --- a/src/Verify.MSTest.SourceGenerator/HashCode.cs +++ b/src/Verify.MSTest.SourceGenerator/HashCode.cs @@ -2,7 +2,9 @@ namespace VerifyMSTest.SourceGenerator; -// TODO: Consider adding this to Polyfill +// .NET Framework cannot use the Microsoft.Bcl.HashCode package, so inline +// an implementation of System.HashCode. +// See https://github.com/dotnet/aspnetcore/commit/d074523ba3f477501cf2a113f1d64e9a21627e53. // Suppressing style warnings to keep code aligned with upstream version from // https://github.com/andrewlock/NetEscapades.EnumGenerators/blob/cd3c9278b0ea3827f7e085c19e998ad0671a11ab/src/NetEscapades.EnumGenerators/HashCode.cs @@ -10,9 +12,6 @@ namespace VerifyMSTest.SourceGenerator; #pragma warning disable IDE1006 // Naming rule violation Prefix is not expected #pragma warning disable IDE0022 // Use expression body for method -/// -/// Polyfill for .NET 6 HashCode -/// internal struct HashCode { private static readonly uint s_seed = GenerateGlobalSeed(); diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index 2cf42b3f8..46bf418fb 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -4,6 +4,7 @@ namespace VerifyMSTest.SourceGenerator; +// TODO: Only emit if a base type doesn't already have the property? static class Parser { public static string MarkerAttributeName { get; } = "VerifyMSTest.UsesVerifyAttribute"; diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index 30b27e664..e48a10e54 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -1,9 +1,9 @@ -using System.Reflection; - namespace VerifyMSTest; public static partial class Verifier { + private static ConcurrentDictionary typeCache = new(); + static Task AddFile(FilePair path, bool autoVerify) { var context = CurrentTestContext.Value; @@ -31,46 +31,115 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b settings.UseUniqueDirectory(); } - var testName = CurrentTestContext.Value?.TestName; - if (testName == null) + if (CurrentTestContext.Value is null) + { + throw new("TestContext is null. Ensure test class has a `[UsesVerify]` attribute."); + } + + var testName = CurrentTestContext.Value.TestName; + if (testName is null) { throw new("TestContext.TestName is null. Ensure test class has a `[UsesVerify]` attribute."); } - var typeName = CurrentTestContext.Value?.FullyQualifiedTestClassName; - var type = FindType(typeName.AsSpan()); + var typeName = CurrentTestContext.Value.FullyQualifiedTestClassName; + if (typeName is null) + { + throw new("TestContext.FullyQualifiedTestClassName is null. Ensure test class has a `[UsesVerify]` attribute."); + } - VerifierSettings.AssignTargetAssembly(type.Assembly); - var method = FindMethod(type, testName.AsSpan()); + if(!TryGetTypeFromTestContext(typeName, CurrentTestContext.Value, out var type)) + { + type = FindType(typeName); + } - sourceFile = IoHelpers.GetMappedBuildPath(sourceFile); - var fileName = Path.GetFileNameWithoutExtension(sourceFile); + var testNameSpan = testName.AsSpan(); + var indexOf = testNameSpan.IndexOf('('); + if (indexOf > 0) + { + testNameSpan = testNameSpan[..indexOf]; + } + + indexOf = testNameSpan.IndexOf('.'); + if (indexOf > 0) + { + testNameSpan = testNameSpan[(indexOf + 1)..]; + } + + VerifierSettings.AssignTargetAssembly(type.Assembly); + var method = FindMethod(type, testNameSpan); var pathInfo = GetPathInfo(sourceFile, type, method); - return new(sourceFile, settings, fileName, testName, method.ParameterNames(), pathInfo); + return new( + sourceFile, + settings, + type.NameWithParent(), + method.Name, + method.ParameterNames(), + pathInfo); } - static Type FindType(ReadOnlySpan typeName) + private static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, [NotNullWhen(true)] out Type? type) { - // TODO: Either - // 1. Switch to the other DerivePathInfo that uses names (e.g. from Expecto) and avoid type lookups - // 2. Add a cache here to speed up the lookup - var types = AppDomain.CurrentDomain.GetAssemblies() + // TODO: Should we file a bug here on testfx? + + try + { + var testMethod = testContext + .GetType() + .GetField("_testMethod", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + ?.GetValue(testContext); + var assemblyPath = testMethod + ?.GetType() + ?.GetProperty("AssemblyName", BindingFlags.Instance | BindingFlags.Public) + ?.GetValue(testMethod); + var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath as string ?? string.Empty); + + type = Type.GetType($"{typeName}, {assemblyName}"); + + if (type is not null) + { + return true; + } + } + catch + { + } + + type = null; + return false; + } + + private static Type FindType(string typeName) + { + // TODO: Do we need the cache here? + var result = typeCache.GetOrAdd(typeName, name => + { + var nameSpan = name.AsSpan(); + var types = AppDomain.CurrentDomain.GetAssemblies() .Reverse() .SelectMany(assembly => assembly.GetTypes()); - foreach (var type in types) - { - if (typeName.SequenceEqual(type.FullName)) + foreach (var type in types) { - return type; + if (nameSpan.SequenceEqual(type.FullName)) + { + return type; + } } + + return null; + }); + + if (result is null) + { + throw new($"Type '{typeName}' from TestContext not found."); } - throw new($"Type '{typeName}' from TestContext not found."); + return result; } - static MethodInfo FindMethod(Type type, ReadOnlySpan testName) + private static MethodInfo FindMethod(Type type, ReadOnlySpan testName) { foreach (var method in type .GetMethods(BindingFlags.Instance | BindingFlags.Public)) From 7878ef37f4a87cd1b1f2de3a6f213d5c96dda85c Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 17:16:46 -0400 Subject: [PATCH 25/69] Add comments and clean up exceptions --- src/Verify.MSTest/Verifier.cs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index e48a10e54..c462cf891 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -3,6 +3,7 @@ namespace VerifyMSTest; public static partial class Verifier { private static ConcurrentDictionary typeCache = new(); + private const string AttributeUsageHelp = "Ensure test class has a `[UsesVerify]` attribute."; static Task AddFile(FilePair path, bool autoVerify) { @@ -33,19 +34,19 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b if (CurrentTestContext.Value is null) { - throw new("TestContext is null. Ensure test class has a `[UsesVerify]` attribute."); + throw new($"TestContext is null. {AttributeUsageHelp}"); } var testName = CurrentTestContext.Value.TestName; if (testName is null) { - throw new("TestContext.TestName is null. Ensure test class has a `[UsesVerify]` attribute."); + throw new($"TestContext.TestName is null. {AttributeUsageHelp}"); } var typeName = CurrentTestContext.Value.FullyQualifiedTestClassName; if (typeName is null) { - throw new("TestContext.FullyQualifiedTestClassName is null. Ensure test class has a `[UsesVerify]` attribute."); + throw new($"TestContext.FullyQualifiedTestClassName is null. {AttributeUsageHelp}"); } if(!TryGetTypeFromTestContext(typeName, CurrentTestContext.Value, out var type)) @@ -79,6 +80,16 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b pathInfo); } + /// + /// As an optimization, try to retrieve the stored on the + /// , and use that to retrieve the . + /// + /// If reflection fails, return false + /// + /// The fully qualified name of the class of the currently running test. + /// The of the current test. + /// The of the currently running test. + /// true if the reflection succeeded; false otherwise. private static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, [NotNullWhen(true)] out Type? type) { // TODO: Should we file a bug here on testfx? @@ -110,6 +121,17 @@ private static bool TryGetTypeFromTestContext(string typeName, TestContext testC return false; } + /// + /// Get the of the test class from the fully qualified name. + /// + /// The fully qualified class name of the currently running test. + /// The for the currently running test class. + /// + /// Uses a to avoid repeated lookups. + /// This method should only be used as a fallback if reflection fails because: + /// 1. It's slower + /// 2. The type cache can grow large for large test suites + /// private static Type FindType(string typeName) { // TODO: Do we need the cache here? From 5abb182c6646933dfaa2369fb8d3f90fd56cb627 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 17:43:19 -0400 Subject: [PATCH 26/69] Fix Source Generator to not double-emit property in inheritence scenarios --- ...ributeOnBaseAndDerivedClasses.verified.txt | 22 +++++++ .../Tests.cs | 27 ++++++++ src/Verify.MSTest.SourceGenerator/Parser.cs | 63 ++++++++++++++++--- .../UsesVerifyGenerator.cs | 4 +- 4 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeOnBaseAndDerivedClasses.verified.txt diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeOnBaseAndDerivedClasses.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeOnBaseAndDerivedClasses.verified.txt new file mode 100644 index 000000000..12c9c7417 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeOnBaseAndDerivedClasses.verified.txt @@ -0,0 +1,22 @@ +{ + Item1: UsesVerify.g.cs, + Item2: +//----------------------------------------------------- +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior +// and will be lost when the code is regenerated. +// +//----------------------------------------------------- + +[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] +partial class Base +{ + public TestContext TestContext + { + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; + } +} + +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index cd7b974c5..1424ff2f4 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -107,4 +107,31 @@ public partial class TestClass2 return Verify(results); } + + [Fact] + public Task HasAttributeOnBaseAndDerivedClasses() + { + var source = """ + using VerifyMSTest; + + [UsesVerify] + public partial class Base + { + } + + [UsesVerify] + public partial class Derived : Base + { + } + """; + + var results = TestDriver + .Run(source) + .Results + .SelectMany(grr => grr.GeneratedSources) + .Select(gs => (gs.HintName, gs.SourceText.ToString())) + .SingleOrDefault(); + + return Verify(results); + } } diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index 46bf418fb..e341f2fad 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -4,13 +4,18 @@ namespace VerifyMSTest.SourceGenerator; -// TODO: Only emit if a base type doesn't already have the property? static class Parser { public static string MarkerAttributeName { get; } = "VerifyMSTest.UsesVerifyAttribute"; - public static ClassToGenerate Parse(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeSyntax) + public static ClassToGenerate? Parse(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeSyntax) { + // Only generate for classes that don't already have a TestContext property defined. + if (HasTestContextProperty(typeSymbol)) + { + return null; + } + var ns = GetNamespace(typeSymbol); var name = GetTypeNameWithGenericParameters(typeSyntax); var parents = GetParentClasses(typeSyntax); @@ -21,6 +26,54 @@ public static ClassToGenerate Parse(INamedTypeSymbol typeSymbol, TypeDeclaration private static string? GetNamespace(INamedTypeSymbol symbol) => symbol.ContainingNamespace.IsGlobalNamespace ? null : symbol.ContainingNamespace.ToString(); + private static bool HasTestContextProperty(INamedTypeSymbol symbol) => + HasMarkerAttributeOnBase(symbol) || HasTestContextPropertyDefinedInBase(symbol); + + private static bool HasMarkerAttributeOnBase(INamedTypeSymbol symbol) + { + static bool HasMarkerAttribute(ISymbol symbol) => + symbol + .GetAttributes() + .Any(a => a.AttributeClass?.ToDisplayString() == MarkerAttributeName); + + var parent = symbol.BaseType; + + while (parent is not null) + { + if (HasMarkerAttribute(parent)) + { + return true; + } + + parent = parent.BaseType; + } + + return false; + } + + private static bool HasTestContextPropertyDefinedInBase(INamedTypeSymbol symbol) + { + static bool HasTestContextProperty(INamedTypeSymbol symbol) => + symbol + .GetMembers() + .OfType() + .Any(p => p.Name == "TestContext" && p.Type.Name == "TestContext"); + + var parent = symbol.BaseType; + + while (parent is not null) + { + if (HasTestContextProperty(parent)) + { + return true; + } + + parent = parent.BaseType; + } + + return false; + } + private static ParentClass[] GetParentClasses(TypeDeclarationSyntax typeSyntax) { // We can only be nested in class/struct/record @@ -31,18 +84,14 @@ static bool IsAllowedKind(SyntaxKind kind) => var parents = new Stack(); - // Try and get the parent syntax. If it isn't a type like class/struct, this will be null var parentSyntax = typeSyntax.Parent as TypeDeclarationSyntax; - // Keep looping while we're in a supported nested type - while (parentSyntax != null && IsAllowedKind(parentSyntax.Kind())) + while (parentSyntax is not null && IsAllowedKind(parentSyntax.Kind())) { - // Record the parent type keyword (class/struct etc), name, and constraints parents.Push(new ParentClass( keyword: parentSyntax.Keyword.ValueText, name: GetTypeNameWithGenericParameters(parentSyntax))); - // Move to the next outer type parentSyntax = parentSyntax.Parent as TypeDeclarationSyntax; } diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index f1713a425..bd0aff74a 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -55,8 +55,8 @@ private static void Execute(SourceProductionContext context, ImmutableArray().ToArray(); - if (classes.Length == 0) + var classes = classesToGenerate.Distinct().OfType().ToList(); + if (classes.Count == 0) { return; } From 4527a6692a79d4019cd8364d7d7eda3bd86fc6a6 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 18:08:58 -0400 Subject: [PATCH 27/69] Switch tests over to attribute --- src/Verify.MSTest.DerivePaths.Tests/Tests.cs | 6 +++--- .../Verify.MSTest.DerivePaths.Tests.csproj | 3 +++ src/Verify.MSTest.Tests/Inheritance/Base.cs | 6 +++--- src/Verify.MSTest.Tests/Inheritance/Inherited.cs | 5 +++-- src/Verify.MSTest.Tests/NestedTypeTests.cs | 10 +++++----- .../Scrubbers/ScrubberLevelsSample.cs | 6 +++--- src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs | 6 +++--- src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs | 6 +++--- .../Snippets/ParametersHashSample.cs | 6 +++--- src/Verify.MSTest.Tests/Snippets/ParametersSample.cs | 6 +++--- src/Verify.MSTest.Tests/Snippets/ParametersTests.cs | 6 +++--- src/Verify.MSTest.Tests/Snippets/Sample.cs | 6 +++--- src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs | 6 +++--- .../VerifyBaseTests.Simple.verified.txt | 1 + src/Verify.MSTest.Tests/VerifyBaseTests.cs | 9 +++++++++ src/Verify.MSTest.Tests/VerifyObjectSamples.cs | 6 +++--- src/Verify.MSTest.Tests/VerifyTextSample.cs | 8 ++++---- src/Verify.MSTest/DerivePaths/VerifyBase.cs | 6 +----- 18 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 src/Verify.MSTest.Tests/VerifyBaseTests.Simple.verified.txt create mode 100644 src/Verify.MSTest.Tests/VerifyBaseTests.cs diff --git a/src/Verify.MSTest.DerivePaths.Tests/Tests.cs b/src/Verify.MSTest.DerivePaths.Tests/Tests.cs index 4f08aad86..10639caad 100644 --- a/src/Verify.MSTest.DerivePaths.Tests/Tests.cs +++ b/src/Verify.MSTest.DerivePaths.Tests/Tests.cs @@ -1,6 +1,6 @@ -[TestClass] -public class Tests : - VerifyBase +[TestClass] +[UsesVerify] +public partial class Tests { [TestMethod] public Task Test() diff --git a/src/Verify.MSTest.DerivePaths.Tests/Verify.MSTest.DerivePaths.Tests.csproj b/src/Verify.MSTest.DerivePaths.Tests/Verify.MSTest.DerivePaths.Tests.csproj index a61797867..56f4b854a 100644 --- a/src/Verify.MSTest.DerivePaths.Tests/Verify.MSTest.DerivePaths.Tests.csproj +++ b/src/Verify.MSTest.DerivePaths.Tests/Verify.MSTest.DerivePaths.Tests.csproj @@ -9,6 +9,9 @@ + diff --git a/src/Verify.MSTest.Tests/Inheritance/Base.cs b/src/Verify.MSTest.Tests/Inheritance/Base.cs index cbd1536fc..e174c2266 100644 --- a/src/Verify.MSTest.Tests/Inheritance/Base.cs +++ b/src/Verify.MSTest.Tests/Inheritance/Base.cs @@ -1,8 +1,8 @@ -namespace TheTests; +namespace TheTests; [TestClass] -public class Base : - VerifyBase +[UsesVerify] +public partial class Base { [TestMethod] public Task TestInBase() => diff --git a/src/Verify.MSTest.Tests/Inheritance/Inherited.cs b/src/Verify.MSTest.Tests/Inheritance/Inherited.cs index 4737110a6..7eb937796 100644 --- a/src/Verify.MSTest.Tests/Inheritance/Inherited.cs +++ b/src/Verify.MSTest.Tests/Inheritance/Inherited.cs @@ -1,7 +1,8 @@ -namespace TheTests; +namespace TheTests; [TestClass] -public class Inherited : Base +[UsesVerify] +public partial class Inherited : Base { [TestMethod] public override Task TestToOverride() diff --git a/src/Verify.MSTest.Tests/NestedTypeTests.cs b/src/Verify.MSTest.Tests/NestedTypeTests.cs index 19a07ed3e..3bf97e806 100644 --- a/src/Verify.MSTest.Tests/NestedTypeTests.cs +++ b/src/Verify.MSTest.Tests/NestedTypeTests.cs @@ -1,16 +1,16 @@ -namespace TheTests; +namespace TheTests; [TestClass] -public class NestedTypeTests : - VerifyBase +[UsesVerify] +public partial class NestedTypeTests { [TestMethod] public Task ShouldPass() => Verify("Foo"); [TestClass] - public class Nested : - VerifyBase + [UsesVerify] + public partial class Nested { [TestMethod] public Task ShouldPass() => diff --git a/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs b/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs index c38f4c250..63f444c0a 100644 --- a/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs +++ b/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; #region ScrubberLevelsSampleMSTest [TestClass] -public class ScrubberLevelsSample : - VerifyBase +[UsesVerify] +public partial class ScrubberLevelsSample { VerifySettings classLevelSettings; diff --git a/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs b/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs index 5755671ef..62f87ec8e 100644 --- a/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs +++ b/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; #region ScrubbersSampleMSTest [TestClass] -public class ScrubbersSample : - VerifyBase +[UsesVerify] +public partial class ScrubbersSample { [TestMethod] public Task Lines() diff --git a/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs b/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs index b0e58eafb..383bd11cb 100644 --- a/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; #region MSTestExtensionSample [TestClass] -public class ExtensionSample : - VerifyBase +[UsesVerify] +public partial class ExtensionSample { [TestMethod] public Task AtMethod() => diff --git a/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs b/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs index 9a5d119fa..ffe8f8362 100644 --- a/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; #region UseParametersHashMsTest [TestClass] -public class ParametersHashSample : - VerifyBase +[UsesVerify] +public partial class ParametersHashSample { [DataTestMethod] [DataRow("Value1")] diff --git a/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs b/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs index 9e77dd09c..2d8d7bdf4 100644 --- a/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; #region MSTestDataRow [TestClass] -public class ParametersSample : - VerifyBase +[UsesVerify] +public partial class ParametersSample { [DataTestMethod] [DataRow("Value1")] diff --git a/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs b/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs index 5116e39c6..3127f5247 100644 --- a/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs +++ b/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs @@ -1,8 +1,8 @@ -namespace TheTests; +namespace TheTests; [TestClass] -public class ParametersTests : - VerifyBase +[UsesVerify] +public partial class ParametersTests { //[DataTestMethod] //[DataRow("1.1")] diff --git a/src/Verify.MSTest.Tests/Snippets/Sample.cs b/src/Verify.MSTest.Tests/Snippets/Sample.cs index 2e112c0a3..db728842f 100644 --- a/src/Verify.MSTest.Tests/Snippets/Sample.cs +++ b/src/Verify.MSTest.Tests/Snippets/Sample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; #region SampleTestMSTest [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs b/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs index d7b3122f2..f98ea03e6 100644 --- a/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; #region UniqueForSampleMSTest [TestClass] -public class UniqueForSample : - VerifyBase +[UsesVerify] +public partial class UniqueForSample { [TestMethod] public Task Runtime() diff --git a/src/Verify.MSTest.Tests/VerifyBaseTests.Simple.verified.txt b/src/Verify.MSTest.Tests/VerifyBaseTests.Simple.verified.txt new file mode 100644 index 000000000..ad32471d9 --- /dev/null +++ b/src/Verify.MSTest.Tests/VerifyBaseTests.Simple.verified.txt @@ -0,0 +1 @@ +Foo diff --git a/src/Verify.MSTest.Tests/VerifyBaseTests.cs b/src/Verify.MSTest.Tests/VerifyBaseTests.cs new file mode 100644 index 000000000..cc8a40bfd --- /dev/null +++ b/src/Verify.MSTest.Tests/VerifyBaseTests.cs @@ -0,0 +1,9 @@ +namespace TheTests; + +[TestClass] +public class VerifyBaseTests : VerifyBase +{ + [TestMethod] + public Task Simple() => + Verify("Foo"); +} \ No newline at end of file diff --git a/src/Verify.MSTest.Tests/VerifyObjectSamples.cs b/src/Verify.MSTest.Tests/VerifyObjectSamples.cs index ef5cb1e0b..ebca5ae83 100644 --- a/src/Verify.MSTest.Tests/VerifyObjectSamples.cs +++ b/src/Verify.MSTest.Tests/VerifyObjectSamples.cs @@ -1,4 +1,4 @@ -using Argon; +using Argon; // ReSharper disable NotAccessedField.Local @@ -7,8 +7,8 @@ namespace TheTests; #pragma warning disable CS8618 [TestClass] -public class VerifyObjectSamples : - VerifyBase +[UsesVerify] +public partial class VerifyObjectSamples { [TestMethod] public Task ScopedSerializer() diff --git a/src/Verify.MSTest.Tests/VerifyTextSample.cs b/src/Verify.MSTest.Tests/VerifyTextSample.cs index 0502d3c22..8780ce3a7 100644 --- a/src/Verify.MSTest.Tests/VerifyTextSample.cs +++ b/src/Verify.MSTest.Tests/VerifyTextSample.cs @@ -1,10 +1,10 @@ -namespace TheTests; +namespace TheTests; [TestClass] -public class VerifyTextSample : - VerifyBase +[UsesVerify] +public partial class VerifyTextSample { [TestMethod] public Task Simple() => Verify("Foo"); -} \ No newline at end of file +} diff --git a/src/Verify.MSTest/DerivePaths/VerifyBase.cs b/src/Verify.MSTest/DerivePaths/VerifyBase.cs index 9fceaff22..34e30597f 100644 --- a/src/Verify.MSTest/DerivePaths/VerifyBase.cs +++ b/src/Verify.MSTest/DerivePaths/VerifyBase.cs @@ -20,9 +20,5 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) => /// Use a directory relative to the project directory for storing for `.verified.` files. /// public static void UseProjectRelativeDirectory(string directory) => - DerivePathInfo( - (sourceFile, projectDirectory, type, method) => new( - directory: Path.Combine(projectDirectory, directory), - typeName: type.NameWithParent(), - methodName: method.Name)); + Verifier.UseProjectRelativeDirectory(directory); } From 74eeef56f3cc1f46a3ce8905f5ef6ac59a0b9ffe Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 18:19:19 -0400 Subject: [PATCH 28/69] Clean up tests --- .../GeneratorDriverRunResultExtensions.cs | 13 ++++++ .../Tests.cs | 45 +++---------------- 2 files changed, 18 insertions(+), 40 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs new file mode 100644 index 000000000..b68b3859b --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.CodeAnalysis; + +namespace VerifyMSTest.SourceGenerator.Tests; + +static class GeneratorDriverRunResultExtensions +{ + public static (string HintName, string SourceText)? SelectGeneratedSources(this GeneratorDriverRunResult gdrr) => + gdrr + .Results + .SelectMany(grr => grr.GeneratedSources) + .Select(gs => (gs.HintName, gs.SourceText.ToString())) + .SingleOrDefault(); +} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 1424ff2f4..63353371e 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -13,14 +13,7 @@ public class Foo } """; - var results = TestDriver - .Run(source) - .Results - .SelectMany(grr => grr.GeneratedSources) - .Select(gs => (gs.HintName, gs.SourceText.ToString())) - .SingleOrDefault(); - - return Verify(results); + return Verify(TestDriver.Run(source).SelectGeneratedSources()); } [Fact] @@ -35,14 +28,7 @@ public class Foo } """; - var results = TestDriver - .Run(source) - .Results - .SelectMany(grr => grr.GeneratedSources) - .Select(gs => (gs.HintName, gs.SourceText.ToString())) - .SingleOrDefault(); - - return Verify(results); + return Verify(TestDriver.Run(source).SelectGeneratedSources()); } [Fact] @@ -59,14 +45,7 @@ public class Bar } """; - var results = TestDriver - .Run(source) - .Results - .SelectMany(grr => grr.GeneratedSources) - .Select(gs => (gs.HintName, gs.SourceText.ToString())) - .SingleOrDefault(); - - return Verify(results); + return Verify(TestDriver.Run(source).SelectGeneratedSources()); } [Fact] @@ -98,14 +77,7 @@ public partial class TestClass2 } """; - var results = TestDriver - .Run(source) - .Results - .SelectMany(grr => grr.GeneratedSources) - .Select(gs => (gs.HintName, gs.SourceText.ToString())) - .SingleOrDefault(); - - return Verify(results); + return Verify(TestDriver.Run(source).SelectGeneratedSources()); } [Fact] @@ -125,13 +97,6 @@ public partial class Derived : Base } """; - var results = TestDriver - .Run(source) - .Results - .SelectMany(grr => grr.GeneratedSources) - .Select(gs => (gs.HintName, gs.SourceText.ToString())) - .SingleOrDefault(); - - return Verify(results); + return Verify(TestDriver.Run(source).SelectGeneratedSources()); } } From 93ed3b2d2a7b20b253e888297049fbe4a4f7a2f5 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 16:17:08 -0700 Subject: [PATCH 29/69] Fix xmldocs --- src/Verify.MSTest/Verifier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index c462cf891..6a3cfe396 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -127,7 +127,7 @@ private static bool TryGetTypeFromTestContext(string typeName, TestContext testC /// The fully qualified class name of the currently running test. /// The for the currently running test class. /// - /// Uses a to avoid repeated lookups. + /// Uses a to avoid repeated lookups. /// This method should only be used as a fallback if reflection fails because: /// 1. It's slower /// 2. The type cache can grow large for large test suites From a71b0cd56798a62b6891a6ae515b9c85e87fbba0 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 16:29:06 -0700 Subject: [PATCH 30/69] Update package and docs --- docs/explicit-targets.md | 2 +- docs/naming.md | 8 ++++---- docs/parameterised.md | 8 ++++---- docs/scrubbers.md | 8 ++++---- docs/wiz/Linux_Other_Cli_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Linux_Other_Cli_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Linux_Other_Cli_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Linux_Other_Cli_MSTest_None.md | 4 ++-- docs/wiz/Linux_Other_Gui_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Linux_Other_Gui_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Linux_Other_Gui_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Linux_Other_Gui_MSTest_None.md | 4 ++-- docs/wiz/Linux_Rider_Cli_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Linux_Rider_Cli_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Linux_Rider_Cli_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Linux_Rider_Cli_MSTest_None.md | 4 ++-- docs/wiz/Linux_Rider_Gui_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Linux_Rider_Gui_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Linux_Rider_Gui_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Linux_Rider_Gui_MSTest_None.md | 4 ++-- docs/wiz/MacOS_Other_Cli_MSTest_AppVeyor.md | 4 ++-- docs/wiz/MacOS_Other_Cli_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/MacOS_Other_Cli_MSTest_GitHubActions.md | 4 ++-- docs/wiz/MacOS_Other_Cli_MSTest_None.md | 4 ++-- docs/wiz/MacOS_Other_Gui_MSTest_AppVeyor.md | 4 ++-- docs/wiz/MacOS_Other_Gui_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/MacOS_Other_Gui_MSTest_GitHubActions.md | 4 ++-- docs/wiz/MacOS_Other_Gui_MSTest_None.md | 4 ++-- docs/wiz/MacOS_Rider_Cli_MSTest_AppVeyor.md | 4 ++-- docs/wiz/MacOS_Rider_Cli_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/MacOS_Rider_Cli_MSTest_GitHubActions.md | 4 ++-- docs/wiz/MacOS_Rider_Cli_MSTest_None.md | 4 ++-- docs/wiz/MacOS_Rider_Gui_MSTest_AppVeyor.md | 4 ++-- docs/wiz/MacOS_Rider_Gui_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/MacOS_Rider_Gui_MSTest_GitHubActions.md | 4 ++-- docs/wiz/MacOS_Rider_Gui_MSTest_None.md | 4 ++-- docs/wiz/Windows_Other_Cli_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Windows_Other_Cli_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Windows_Other_Cli_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Windows_Other_Cli_MSTest_None.md | 4 ++-- docs/wiz/Windows_Other_Gui_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Windows_Other_Gui_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Windows_Other_Gui_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Windows_Other_Gui_MSTest_None.md | 4 ++-- docs/wiz/Windows_Rider_Cli_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Windows_Rider_Cli_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Windows_Rider_Cli_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Windows_Rider_Cli_MSTest_None.md | 4 ++-- docs/wiz/Windows_Rider_Gui_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Windows_Rider_Gui_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Windows_Rider_Gui_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Windows_Rider_Gui_MSTest_None.md | 4 ++-- ...ndows_VisualStudioWithReSharper_Cli_MSTest_AppVeyor.md | 4 ++-- ...ws_VisualStudioWithReSharper_Cli_MSTest_AzureDevOps.md | 4 ++-- ..._VisualStudioWithReSharper_Cli_MSTest_GitHubActions.md | 4 ++-- .../Windows_VisualStudioWithReSharper_Cli_MSTest_None.md | 4 ++-- ...ndows_VisualStudioWithReSharper_Gui_MSTest_AppVeyor.md | 4 ++-- ...ws_VisualStudioWithReSharper_Gui_MSTest_AzureDevOps.md | 4 ++-- ..._VisualStudioWithReSharper_Gui_MSTest_GitHubActions.md | 4 ++-- .../Windows_VisualStudioWithReSharper_Gui_MSTest_None.md | 4 ++-- docs/wiz/Windows_VisualStudio_Cli_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Windows_VisualStudio_Cli_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Windows_VisualStudio_Cli_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Windows_VisualStudio_Cli_MSTest_None.md | 4 ++-- docs/wiz/Windows_VisualStudio_Gui_MSTest_AppVeyor.md | 4 ++-- docs/wiz/Windows_VisualStudio_Gui_MSTest_AzureDevOps.md | 4 ++-- docs/wiz/Windows_VisualStudio_Gui_MSTest_GitHubActions.md | 4 ++-- docs/wiz/Windows_VisualStudio_Gui_MSTest_None.md | 4 ++-- readme.md | 4 ++-- .../Verify.MSTest.SourceGenerator.Tests.csproj | 6 +++--- .../Verify.MSTest.SourceGenerator.csproj | 2 +- 71 files changed, 147 insertions(+), 147 deletions(-) diff --git a/docs/explicit-targets.md b/docs/explicit-targets.md index f2806fda8..9932bdae3 100644 --- a/docs/explicit-targets.md +++ b/docs/explicit-targets.md @@ -103,7 +103,7 @@ public Task WithTargets() => name: "targetName") ]); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/naming.md b/docs/naming.md index b355a065a..9731ba2f5 100644 --- a/docs/naming.md +++ b/docs/naming.md @@ -384,8 +384,8 @@ public class UniqueForSample ```cs [TestClass] -public class UniqueForSample : - VerifyBase +[UsesVerify] +public partial class UniqueForSample { [TestMethod] public Task Runtime() @@ -618,13 +618,13 @@ Verifier.DerivePathInfo( ```cs -DerivePathInfo( +Verifier.DerivePathInfo( (sourceFile, projectDirectory, type, method) => new( directory: Path.Combine(projectDirectory, "Snapshots"), typeName: type.Name, methodName: method.Name)); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/parameterised.md b/docs/parameterised.md index a9cb6683a..a127a37a1 100644 --- a/docs/parameterised.md +++ b/docs/parameterised.md @@ -395,8 +395,8 @@ public Task TestCaseUsage(string arg) => ```cs [TestClass] -public class ParametersSample : - VerifyBase +[UsesVerify] +public partial class ParametersSample { [DataTestMethod] [DataRow("Value1")] @@ -516,8 +516,8 @@ Hashing parameter is achieved by using `UseParameters` in combination with `Hash ```cs [TestClass] -public class ParametersHashSample : - VerifyBase +[UsesVerify] +public partial class ParametersHashSample { [DataTestMethod] [DataRow("Value1")] diff --git a/docs/scrubbers.md b/docs/scrubbers.md index a601d0bde..3f1625b79 100644 --- a/docs/scrubbers.md +++ b/docs/scrubbers.md @@ -521,8 +521,8 @@ public class ScrubbersSample ```cs [TestClass] -public class ScrubbersSample : - VerifyBase +[UsesVerify] +public partial class ScrubbersSample { [TestMethod] public Task Lines() @@ -789,8 +789,8 @@ public class ScrubberLevelsSample ```cs [TestClass] -public class ScrubberLevelsSample : - VerifyBase +[UsesVerify] +public partial class ScrubberLevelsSample { VerifySettings classLevelSettings; diff --git a/docs/wiz/Linux_Other_Cli_MSTest_AppVeyor.md b/docs/wiz/Linux_Other_Cli_MSTest_AppVeyor.md index efd1bd251..936c8bd11 100644 --- a/docs/wiz/Linux_Other_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/Linux_Other_Cli_MSTest_AppVeyor.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Other_Cli_MSTest_AzureDevOps.md b/docs/wiz/Linux_Other_Cli_MSTest_AzureDevOps.md index 3a8ff0b8c..df0a663b5 100644 --- a/docs/wiz/Linux_Other_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/Linux_Other_Cli_MSTest_AzureDevOps.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Other_Cli_MSTest_GitHubActions.md b/docs/wiz/Linux_Other_Cli_MSTest_GitHubActions.md index 83b9424b3..c061d23d0 100644 --- a/docs/wiz/Linux_Other_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/Linux_Other_Cli_MSTest_GitHubActions.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Other_Cli_MSTest_None.md b/docs/wiz/Linux_Other_Cli_MSTest_None.md index d25c08414..e97e9e73f 100644 --- a/docs/wiz/Linux_Other_Cli_MSTest_None.md +++ b/docs/wiz/Linux_Other_Cli_MSTest_None.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Other_Gui_MSTest_AppVeyor.md b/docs/wiz/Linux_Other_Gui_MSTest_AppVeyor.md index 81debb0b1..875c9d955 100644 --- a/docs/wiz/Linux_Other_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/Linux_Other_Gui_MSTest_AppVeyor.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Other_Gui_MSTest_AzureDevOps.md b/docs/wiz/Linux_Other_Gui_MSTest_AzureDevOps.md index 08a7929c0..b16cfc35b 100644 --- a/docs/wiz/Linux_Other_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/Linux_Other_Gui_MSTest_AzureDevOps.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Other_Gui_MSTest_GitHubActions.md b/docs/wiz/Linux_Other_Gui_MSTest_GitHubActions.md index ad19a4fbc..ff78b4afb 100644 --- a/docs/wiz/Linux_Other_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/Linux_Other_Gui_MSTest_GitHubActions.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Other_Gui_MSTest_None.md b/docs/wiz/Linux_Other_Gui_MSTest_None.md index 29dba54db..a791bf28a 100644 --- a/docs/wiz/Linux_Other_Gui_MSTest_None.md +++ b/docs/wiz/Linux_Other_Gui_MSTest_None.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Cli_MSTest_AppVeyor.md b/docs/wiz/Linux_Rider_Cli_MSTest_AppVeyor.md index 85d19356b..7e4570f5a 100644 --- a/docs/wiz/Linux_Rider_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/Linux_Rider_Cli_MSTest_AppVeyor.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Cli_MSTest_AzureDevOps.md b/docs/wiz/Linux_Rider_Cli_MSTest_AzureDevOps.md index d6219ebaa..481a3013a 100644 --- a/docs/wiz/Linux_Rider_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/Linux_Rider_Cli_MSTest_AzureDevOps.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Cli_MSTest_GitHubActions.md b/docs/wiz/Linux_Rider_Cli_MSTest_GitHubActions.md index 7b67b4268..1cac4771f 100644 --- a/docs/wiz/Linux_Rider_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/Linux_Rider_Cli_MSTest_GitHubActions.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Cli_MSTest_None.md b/docs/wiz/Linux_Rider_Cli_MSTest_None.md index 003f59032..40148637a 100644 --- a/docs/wiz/Linux_Rider_Cli_MSTest_None.md +++ b/docs/wiz/Linux_Rider_Cli_MSTest_None.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Gui_MSTest_AppVeyor.md b/docs/wiz/Linux_Rider_Gui_MSTest_AppVeyor.md index f41ec5388..083b96e30 100644 --- a/docs/wiz/Linux_Rider_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/Linux_Rider_Gui_MSTest_AppVeyor.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Gui_MSTest_AzureDevOps.md b/docs/wiz/Linux_Rider_Gui_MSTest_AzureDevOps.md index 6cac8edbd..83cbdefbc 100644 --- a/docs/wiz/Linux_Rider_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/Linux_Rider_Gui_MSTest_AzureDevOps.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Gui_MSTest_GitHubActions.md b/docs/wiz/Linux_Rider_Gui_MSTest_GitHubActions.md index f7453056d..9fb920904 100644 --- a/docs/wiz/Linux_Rider_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/Linux_Rider_Gui_MSTest_GitHubActions.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Linux_Rider_Gui_MSTest_None.md b/docs/wiz/Linux_Rider_Gui_MSTest_None.md index d98245b58..4fa447b2a 100644 --- a/docs/wiz/Linux_Rider_Gui_MSTest_None.md +++ b/docs/wiz/Linux_Rider_Gui_MSTest_None.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Cli_MSTest_AppVeyor.md b/docs/wiz/MacOS_Other_Cli_MSTest_AppVeyor.md index 424cb0fba..56ab1518a 100644 --- a/docs/wiz/MacOS_Other_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/MacOS_Other_Cli_MSTest_AppVeyor.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Cli_MSTest_AzureDevOps.md b/docs/wiz/MacOS_Other_Cli_MSTest_AzureDevOps.md index 18a14369b..a0822ec6b 100644 --- a/docs/wiz/MacOS_Other_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/MacOS_Other_Cli_MSTest_AzureDevOps.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Cli_MSTest_GitHubActions.md b/docs/wiz/MacOS_Other_Cli_MSTest_GitHubActions.md index fe80b3e72..cef299d2a 100644 --- a/docs/wiz/MacOS_Other_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/MacOS_Other_Cli_MSTest_GitHubActions.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Cli_MSTest_None.md b/docs/wiz/MacOS_Other_Cli_MSTest_None.md index ddbfe1508..3526924cf 100644 --- a/docs/wiz/MacOS_Other_Cli_MSTest_None.md +++ b/docs/wiz/MacOS_Other_Cli_MSTest_None.md @@ -137,8 +137,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Gui_MSTest_AppVeyor.md b/docs/wiz/MacOS_Other_Gui_MSTest_AppVeyor.md index 7481a6d34..47d0e12cb 100644 --- a/docs/wiz/MacOS_Other_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/MacOS_Other_Gui_MSTest_AppVeyor.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Gui_MSTest_AzureDevOps.md b/docs/wiz/MacOS_Other_Gui_MSTest_AzureDevOps.md index c5c264692..4fb37c1b4 100644 --- a/docs/wiz/MacOS_Other_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/MacOS_Other_Gui_MSTest_AzureDevOps.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Gui_MSTest_GitHubActions.md b/docs/wiz/MacOS_Other_Gui_MSTest_GitHubActions.md index d884d7f0f..cd135a84e 100644 --- a/docs/wiz/MacOS_Other_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/MacOS_Other_Gui_MSTest_GitHubActions.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Other_Gui_MSTest_None.md b/docs/wiz/MacOS_Other_Gui_MSTest_None.md index 33fb5f277..aaa287051 100644 --- a/docs/wiz/MacOS_Other_Gui_MSTest_None.md +++ b/docs/wiz/MacOS_Other_Gui_MSTest_None.md @@ -131,8 +131,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Cli_MSTest_AppVeyor.md b/docs/wiz/MacOS_Rider_Cli_MSTest_AppVeyor.md index 2d0c5881f..cd4ffdb86 100644 --- a/docs/wiz/MacOS_Rider_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/MacOS_Rider_Cli_MSTest_AppVeyor.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Cli_MSTest_AzureDevOps.md b/docs/wiz/MacOS_Rider_Cli_MSTest_AzureDevOps.md index bc534d2bd..a9574a95b 100644 --- a/docs/wiz/MacOS_Rider_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/MacOS_Rider_Cli_MSTest_AzureDevOps.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Cli_MSTest_GitHubActions.md b/docs/wiz/MacOS_Rider_Cli_MSTest_GitHubActions.md index 005c9bda4..98cb79cbb 100644 --- a/docs/wiz/MacOS_Rider_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/MacOS_Rider_Cli_MSTest_GitHubActions.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Cli_MSTest_None.md b/docs/wiz/MacOS_Rider_Cli_MSTest_None.md index 24bdc4767..158aca28d 100644 --- a/docs/wiz/MacOS_Rider_Cli_MSTest_None.md +++ b/docs/wiz/MacOS_Rider_Cli_MSTest_None.md @@ -186,8 +186,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Gui_MSTest_AppVeyor.md b/docs/wiz/MacOS_Rider_Gui_MSTest_AppVeyor.md index 6fedcf91e..e36cf6c2a 100644 --- a/docs/wiz/MacOS_Rider_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/MacOS_Rider_Gui_MSTest_AppVeyor.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Gui_MSTest_AzureDevOps.md b/docs/wiz/MacOS_Rider_Gui_MSTest_AzureDevOps.md index 2493dc290..7777194ae 100644 --- a/docs/wiz/MacOS_Rider_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/MacOS_Rider_Gui_MSTest_AzureDevOps.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Gui_MSTest_GitHubActions.md b/docs/wiz/MacOS_Rider_Gui_MSTest_GitHubActions.md index 04979630c..112ad4789 100644 --- a/docs/wiz/MacOS_Rider_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/MacOS_Rider_Gui_MSTest_GitHubActions.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/MacOS_Rider_Gui_MSTest_None.md b/docs/wiz/MacOS_Rider_Gui_MSTest_None.md index 885635431..dbfe38f5e 100644 --- a/docs/wiz/MacOS_Rider_Gui_MSTest_None.md +++ b/docs/wiz/MacOS_Rider_Gui_MSTest_None.md @@ -180,8 +180,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Cli_MSTest_AppVeyor.md b/docs/wiz/Windows_Other_Cli_MSTest_AppVeyor.md index 2d4e9f003..963a8cf60 100644 --- a/docs/wiz/Windows_Other_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_Other_Cli_MSTest_AppVeyor.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Cli_MSTest_AzureDevOps.md b/docs/wiz/Windows_Other_Cli_MSTest_AzureDevOps.md index 18bf173dd..342a8f2af 100644 --- a/docs/wiz/Windows_Other_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_Other_Cli_MSTest_AzureDevOps.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Cli_MSTest_GitHubActions.md b/docs/wiz/Windows_Other_Cli_MSTest_GitHubActions.md index b957b5bf5..065409245 100644 --- a/docs/wiz/Windows_Other_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_Other_Cli_MSTest_GitHubActions.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Cli_MSTest_None.md b/docs/wiz/Windows_Other_Cli_MSTest_None.md index 750732024..8779b39dd 100644 --- a/docs/wiz/Windows_Other_Cli_MSTest_None.md +++ b/docs/wiz/Windows_Other_Cli_MSTest_None.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Gui_MSTest_AppVeyor.md b/docs/wiz/Windows_Other_Gui_MSTest_AppVeyor.md index 86bbd5675..2ac6550e1 100644 --- a/docs/wiz/Windows_Other_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_Other_Gui_MSTest_AppVeyor.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Gui_MSTest_AzureDevOps.md b/docs/wiz/Windows_Other_Gui_MSTest_AzureDevOps.md index 3c92649ff..d8c735013 100644 --- a/docs/wiz/Windows_Other_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_Other_Gui_MSTest_AzureDevOps.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Gui_MSTest_GitHubActions.md b/docs/wiz/Windows_Other_Gui_MSTest_GitHubActions.md index ba5386c7d..67dfbb010 100644 --- a/docs/wiz/Windows_Other_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_Other_Gui_MSTest_GitHubActions.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Other_Gui_MSTest_None.md b/docs/wiz/Windows_Other_Gui_MSTest_None.md index 0cfaf3015..948e489f2 100644 --- a/docs/wiz/Windows_Other_Gui_MSTest_None.md +++ b/docs/wiz/Windows_Other_Gui_MSTest_None.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Cli_MSTest_AppVeyor.md b/docs/wiz/Windows_Rider_Cli_MSTest_AppVeyor.md index 318d77c46..422228f4c 100644 --- a/docs/wiz/Windows_Rider_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_Rider_Cli_MSTest_AppVeyor.md @@ -199,8 +199,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Cli_MSTest_AzureDevOps.md b/docs/wiz/Windows_Rider_Cli_MSTest_AzureDevOps.md index 3a63ad2e4..1124b4320 100644 --- a/docs/wiz/Windows_Rider_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_Rider_Cli_MSTest_AzureDevOps.md @@ -199,8 +199,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Cli_MSTest_GitHubActions.md b/docs/wiz/Windows_Rider_Cli_MSTest_GitHubActions.md index 2154f95f1..64713aec5 100644 --- a/docs/wiz/Windows_Rider_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_Rider_Cli_MSTest_GitHubActions.md @@ -199,8 +199,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Cli_MSTest_None.md b/docs/wiz/Windows_Rider_Cli_MSTest_None.md index 2afcc0355..1a7894f13 100644 --- a/docs/wiz/Windows_Rider_Cli_MSTest_None.md +++ b/docs/wiz/Windows_Rider_Cli_MSTest_None.md @@ -199,8 +199,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Gui_MSTest_AppVeyor.md b/docs/wiz/Windows_Rider_Gui_MSTest_AppVeyor.md index 446d61337..2bc651c6b 100644 --- a/docs/wiz/Windows_Rider_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_Rider_Gui_MSTest_AppVeyor.md @@ -193,8 +193,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Gui_MSTest_AzureDevOps.md b/docs/wiz/Windows_Rider_Gui_MSTest_AzureDevOps.md index 9caed664b..bfb8ffbec 100644 --- a/docs/wiz/Windows_Rider_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_Rider_Gui_MSTest_AzureDevOps.md @@ -193,8 +193,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Gui_MSTest_GitHubActions.md b/docs/wiz/Windows_Rider_Gui_MSTest_GitHubActions.md index eadcf564d..fc261aad2 100644 --- a/docs/wiz/Windows_Rider_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_Rider_Gui_MSTest_GitHubActions.md @@ -193,8 +193,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_Rider_Gui_MSTest_None.md b/docs/wiz/Windows_Rider_Gui_MSTest_None.md index 95d1fcefb..a0cc9653b 100644 --- a/docs/wiz/Windows_Rider_Gui_MSTest_None.md +++ b/docs/wiz/Windows_Rider_Gui_MSTest_None.md @@ -193,8 +193,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AppVeyor.md b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AppVeyor.md index 21ee81665..9c930f2cc 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AppVeyor.md @@ -207,8 +207,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AzureDevOps.md b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AzureDevOps.md index 56787273d..30754c7c4 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_AzureDevOps.md @@ -207,8 +207,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_GitHubActions.md b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_GitHubActions.md index e080c7ddc..6f77f7c87 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_GitHubActions.md @@ -207,8 +207,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_None.md b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_None.md index aa4664950..84939aabb 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_None.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Cli_MSTest_None.md @@ -207,8 +207,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AppVeyor.md b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AppVeyor.md index ed1869b6a..91edb7988 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AppVeyor.md @@ -201,8 +201,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AzureDevOps.md b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AzureDevOps.md index 4dc311451..eac4c4f03 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_AzureDevOps.md @@ -201,8 +201,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_GitHubActions.md b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_GitHubActions.md index a93c140eb..c06992f78 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_GitHubActions.md @@ -201,8 +201,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_None.md b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_None.md index c23f3629e..a77d172df 100644 --- a/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_None.md +++ b/docs/wiz/Windows_VisualStudioWithReSharper_Gui_MSTest_None.md @@ -201,8 +201,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Cli_MSTest_AppVeyor.md b/docs/wiz/Windows_VisualStudio_Cli_MSTest_AppVeyor.md index 86442219c..2d30ba86e 100644 --- a/docs/wiz/Windows_VisualStudio_Cli_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_VisualStudio_Cli_MSTest_AppVeyor.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Cli_MSTest_AzureDevOps.md b/docs/wiz/Windows_VisualStudio_Cli_MSTest_AzureDevOps.md index 3d72d6450..f595fc35b 100644 --- a/docs/wiz/Windows_VisualStudio_Cli_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_VisualStudio_Cli_MSTest_AzureDevOps.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Cli_MSTest_GitHubActions.md b/docs/wiz/Windows_VisualStudio_Cli_MSTest_GitHubActions.md index d1a03c61d..d8a021725 100644 --- a/docs/wiz/Windows_VisualStudio_Cli_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_VisualStudio_Cli_MSTest_GitHubActions.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Cli_MSTest_None.md b/docs/wiz/Windows_VisualStudio_Cli_MSTest_None.md index 90e94ca90..2ceac55d2 100644 --- a/docs/wiz/Windows_VisualStudio_Cli_MSTest_None.md +++ b/docs/wiz/Windows_VisualStudio_Cli_MSTest_None.md @@ -150,8 +150,8 @@ dotnet tool install -g verify.tool ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Gui_MSTest_AppVeyor.md b/docs/wiz/Windows_VisualStudio_Gui_MSTest_AppVeyor.md index f19b97e23..1e587b30e 100644 --- a/docs/wiz/Windows_VisualStudio_Gui_MSTest_AppVeyor.md +++ b/docs/wiz/Windows_VisualStudio_Gui_MSTest_AppVeyor.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Gui_MSTest_AzureDevOps.md b/docs/wiz/Windows_VisualStudio_Gui_MSTest_AzureDevOps.md index 195fe8e2e..b71f342b2 100644 --- a/docs/wiz/Windows_VisualStudio_Gui_MSTest_AzureDevOps.md +++ b/docs/wiz/Windows_VisualStudio_Gui_MSTest_AzureDevOps.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Gui_MSTest_GitHubActions.md b/docs/wiz/Windows_VisualStudio_Gui_MSTest_GitHubActions.md index da9666b59..099f09277 100644 --- a/docs/wiz/Windows_VisualStudio_Gui_MSTest_GitHubActions.md +++ b/docs/wiz/Windows_VisualStudio_Gui_MSTest_GitHubActions.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/docs/wiz/Windows_VisualStudio_Gui_MSTest_None.md b/docs/wiz/Windows_VisualStudio_Gui_MSTest_None.md index 8c6482b20..b453a6903 100644 --- a/docs/wiz/Windows_VisualStudio_Gui_MSTest_None.md +++ b/docs/wiz/Windows_VisualStudio_Gui_MSTest_None.md @@ -144,8 +144,8 @@ public static void Initialize() => ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/readme.md b/readme.md index 47e7c1108..2042059dc 100644 --- a/readme.md +++ b/readme.md @@ -214,8 +214,8 @@ Support for [MSTest](https://github.com/Microsoft/testfx-docs) ```cs [TestClass] -public class Sample : - VerifyBase +[UsesVerify] +public partial class Sample { [TestMethod] public Task Test() diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj index 09cb81cf8..ff6aec999 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj +++ b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj index 1663b9078..b98e2f326 100644 --- a/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj +++ b/src/Verify.MSTest.SourceGenerator/Verify.MSTest.SourceGenerator.csproj @@ -10,7 +10,7 @@ - + From d0838bdd3684ecadbab67e7e65fad577fcbd693c Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 14 May 2024 17:50:39 -0700 Subject: [PATCH 31/69] Clean up code comments --- docs/explicit-targets.md | 2 +- docs/naming.md | 2 +- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 4 ++-- src/Verify.MSTest.SourceGenerator/EquatableArray.cs | 2 -- src/Verify.MSTest.SourceGenerator/HashCode.cs | 3 +-- src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs | 5 +++-- src/Verify.MSTest.Tests/Tests.cs | 3 --- src/Verify.MSTest/UsesVerifyAttribute.cs | 4 ++++ src/Verify.MSTest/Verifier.cs | 7 ++----- 9 files changed, 14 insertions(+), 18 deletions(-) diff --git a/docs/explicit-targets.md b/docs/explicit-targets.md index 9932bdae3..fd018a301 100644 --- a/docs/explicit-targets.md +++ b/docs/explicit-targets.md @@ -103,7 +103,7 @@ public Task WithTargets() => name: "targetName") ]); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/naming.md b/docs/naming.md index 9731ba2f5..38dd0f584 100644 --- a/docs/naming.md +++ b/docs/naming.md @@ -624,7 +624,7 @@ Verifier.DerivePathInfo( typeName: type.Name, methodName: method.Name)); ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 63353371e..5d5ed7ba4 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -23,7 +23,7 @@ public Task HasAttributeInGlobalNamespace() using VerifyMSTest; [UsesVerify] - public class Foo + public partial class Foo { } """; @@ -40,7 +40,7 @@ public Task HasAttributeInNamespace() namespace Foo; [UsesVerify] - public class Bar + public partial class Bar { } """; diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs index 8f8a3f962..cd00f4629 100644 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -2,8 +2,6 @@ namespace VerifyMSTest.SourceGenerator; -// TODO: Is there a better place for this to live? - // Suppressing style warnings to keep code aligned with upstream version from // https://github.com/andrewlock/StronglyTypedId/blob/e5df78d0aa72f2232f423938c0d98d9bf4517092/src/StronglyTypedIds/EquatableArray.cs #pragma warning disable IDE1006 // Naming rule violation Prefix is not expected diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs index e9a76d940..d9203adf9 100644 --- a/src/Verify.MSTest.SourceGenerator/HashCode.cs +++ b/src/Verify.MSTest.SourceGenerator/HashCode.cs @@ -2,8 +2,7 @@ namespace VerifyMSTest.SourceGenerator; -// .NET Framework cannot use the Microsoft.Bcl.HashCode package, so inline -// an implementation of System.HashCode. +// A local version of System.HashCode to avoid complications from NuGet private dependencies. // See https://github.com/dotnet/aspnetcore/commit/d074523ba3f477501cf2a113f1d64e9a21627e53. // Suppressing style warnings to keep code aligned with upstream version from diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 89ebec4df..164febb20 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -2,8 +2,9 @@ namespace VerifyMSTest.SourceGenerator; class IndentedStringBuilder { - // TODO: Tweak default capacity based on real-world usage - private const int DefaultStringBuilderCapacity = 1024; + // Default capacity based on the closest power of 2 to what's used in our own tests. + // This may need to be tweaked over time. + private const int DefaultStringBuilderCapacity = 4096; private readonly StringBuilder builder; private int indentLevel = 0; private bool isIndented = false; diff --git a/src/Verify.MSTest.Tests/Tests.cs b/src/Verify.MSTest.Tests/Tests.cs index 63d6f336d..2bc3e9595 100644 --- a/src/Verify.MSTest.Tests/Tests.cs +++ b/src/Verify.MSTest.Tests/Tests.cs @@ -1,6 +1,3 @@ -// TODO: Add a test that uses the base class to prevent regressions until it's deleted -// TODO: Add a test for when the [TestClass] already has a TestContext property - [TestClass] [UsesVerify] public partial class Tests diff --git a/src/Verify.MSTest/UsesVerifyAttribute.cs b/src/Verify.MSTest/UsesVerifyAttribute.cs index 3b13acf50..446632ea5 100644 --- a/src/Verify.MSTest/UsesVerifyAttribute.cs +++ b/src/Verify.MSTest/UsesVerifyAttribute.cs @@ -1,5 +1,9 @@ namespace VerifyMSTest; +// Define the marker attribute in the main project rather than emit it as part of the source generator to +// avoid issues where the user ends up with multiple conflicting definitions of the attribute +// (commonly when using InternalsVisibleTo). + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public sealed class UsesVerifyAttribute : Attribute { diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index 6a3cfe396..3015fd941 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -84,7 +84,7 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b /// As an optimization, try to retrieve the stored on the /// , and use that to retrieve the . /// - /// If reflection fails, return false + /// If reflection fails, return false. /// /// The fully qualified name of the class of the currently running test. /// The of the current test. @@ -92,8 +92,6 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b /// true if the reflection succeeded; false otherwise. private static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, [NotNullWhen(true)] out Type? type) { - // TODO: Should we file a bug here on testfx? - try { var testMethod = testContext @@ -106,7 +104,7 @@ private static bool TryGetTypeFromTestContext(string typeName, TestContext testC ?.GetValue(testMethod); var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath as string ?? string.Empty); - type = Type.GetType($"{typeName}, {assemblyName}"); + type = Type.GetType($"{typeName}, {assemblyName}", throwOnError: false); if (type is not null) { @@ -134,7 +132,6 @@ private static bool TryGetTypeFromTestContext(string typeName, TestContext testC /// private static Type FindType(string typeName) { - // TODO: Do we need the cache here? var result = typeCache.GetOrAdd(typeName, name => { var nameSpan = name.AsSpan(); From 7af840bbc226970059566320fadedb6acbdc0dd3 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 15 May 2024 22:26:47 +1000 Subject: [PATCH 32/69] cleanup --- src/Verify.MSTest/UsesVerifyAttribute.cs | 4 +--- src/Verify.MSTest/Verifier.cs | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Verify.MSTest/UsesVerifyAttribute.cs b/src/Verify.MSTest/UsesVerifyAttribute.cs index 446632ea5..b10946813 100644 --- a/src/Verify.MSTest/UsesVerifyAttribute.cs +++ b/src/Verify.MSTest/UsesVerifyAttribute.cs @@ -5,6 +5,4 @@ namespace VerifyMSTest; // (commonly when using InternalsVisibleTo). [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] -public sealed class UsesVerifyAttribute : Attribute -{ -} +public sealed class UsesVerifyAttribute : Attribute; diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index 3015fd941..6a488d1aa 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -2,8 +2,8 @@ namespace VerifyMSTest; public static partial class Verifier { - private static ConcurrentDictionary typeCache = new(); - private const string AttributeUsageHelp = "Ensure test class has a `[UsesVerify]` attribute."; + static ConcurrentDictionary typeCache = new(); + const string AttributeUsageHelp = "Ensure test class has a `[UsesVerify]` attribute."; static Task AddFile(FilePair path, bool autoVerify) { @@ -90,7 +90,7 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b /// The of the current test. /// The of the currently running test. /// true if the reflection succeeded; false otherwise. - private static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, [NotNullWhen(true)] out Type? type) + static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, [NotNullWhen(true)] out Type? type) { try { @@ -130,7 +130,7 @@ private static bool TryGetTypeFromTestContext(string typeName, TestContext testC /// 1. It's slower /// 2. The type cache can grow large for large test suites /// - private static Type FindType(string typeName) + static Type FindType(string typeName) { var result = typeCache.GetOrAdd(typeName, name => { @@ -158,7 +158,7 @@ private static Type FindType(string typeName) return result; } - private static MethodInfo FindMethod(Type type, ReadOnlySpan testName) + static MethodInfo FindMethod(Type type, ReadOnlySpan testName) { foreach (var method in type .GetMethods(BindingFlags.Instance | BindingFlags.Public)) From b0a2673a048f285f69c4bc22b897dfc746077f93 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Wed, 15 May 2024 15:40:59 -0700 Subject: [PATCH 33/69] Add comment for UnsafeAccessor --- src/Verify.MSTest/Verifier.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index 6a488d1aa..427bec343 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -94,6 +94,9 @@ static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, { try { + // We can't use UnsafeAccessor in this context because _testMethod is of type + // Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod, which isn't part of the MSTest.TestFramework + // package. If / when an API like https://github.com/dotnet/runtime/issues/90081 lands this generic reflection can be replaced. var testMethod = testContext .GetType() .GetField("_testMethod", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) From b2f3697d4f00b152461a3ef158af8c56f77ac4bd Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Wed, 15 May 2024 15:41:14 -0700 Subject: [PATCH 34/69] Verify generators are cachable --- .../GeneratorDriverResults.cs | 7 +++ .../GeneratorDriverRunResultExtensions.cs | 8 +++ .../TestDriver.cs | 25 ++++++++-- .../Tests.cs | 49 +++++++++++++++++-- .../UsesVerifyTestDriver.cs | 10 ++++ ...Verify.MSTest.SourceGenerator.Tests.csproj | 1 + .../TrackingNames.cs | 11 ++++- 7 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs new file mode 100644 index 000000000..398d060e4 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs @@ -0,0 +1,7 @@ +using Microsoft.CodeAnalysis; + +namespace VerifyMSTest.SourceGenerator.Tests; + +readonly record struct GeneratorDriverResults(GeneratorDriverResult FirstRun, GeneratorDriverResult CachedRun); + +readonly record struct GeneratorDriverResult(GeneratorDriverRunResult RunResult, GeneratorDriverTimingInfo TimingInfo); \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs index b68b3859b..627077156 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using Microsoft.CodeAnalysis; namespace VerifyMSTest.SourceGenerator.Tests; @@ -10,4 +11,11 @@ public static (string HintName, string SourceText)? SelectGeneratedSources(this .SelectMany(grr => grr.GeneratedSources) .Select(gs => (gs.HintName, gs.SourceText.ToString())) .SingleOrDefault(); + + public static Dictionary> GetTrackedSteps(this GeneratorDriverRunResult runResult, IReadOnlyCollection trackingNames) => + runResult + .Results + .SelectMany(result => result.TrackedSteps) + .Where(step => trackingNames.Contains(step.Key)) + .ToDictionary(x => x.Key, x => x.Value); } \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index 7dac65091..f98024b57 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -1,11 +1,16 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Xunit.Abstractions; namespace VerifyMSTest.SourceGenerator.Tests; -static class TestDriver +class TestDriver { - public static GeneratorDriverRunResult Run(string source) + private readonly IEnumerable sourceGenerators; + + public TestDriver(IEnumerable sourceGenerators) => this.sourceGenerators = sourceGenerators; + + public GeneratorDriverResults Run(string source) { var syntaxTree = CSharpSyntaxTree.ParseText(source); @@ -20,11 +25,21 @@ public static GeneratorDriverRunResult Run(string source) syntaxTrees: [syntaxTree], references: references); - var generator = new UsesVerifyGenerator(); + var driverOptions = new GeneratorDriverOptions( + disabledOutputs: IncrementalGeneratorOutputKind.None, + trackIncrementalGeneratorSteps: true); + + GeneratorDriver driver = CSharpGeneratorDriver.Create(sourceGenerators, driverOptions: driverOptions); - GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); driver = driver.RunGenerators(compilation); + var results1 = driver.GetRunResult(); + var timings1 = driver.GetTimingInfo(); + driver = driver.RunGenerators(compilation.Clone()); + var results2 = driver.GetRunResult(); + var timings2 = driver.GetTimingInfo(); - return driver.GetRunResult(); + return new GeneratorDriverResults( + new GeneratorDriverResult(results1, timings1), + new GeneratorDriverResult(results2, timings2)); } } diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 5d5ed7ba4..4eec02d55 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -1,9 +1,48 @@ +using Microsoft.CodeAnalysis; +using Shouldly; +using Xunit.Abstractions; + namespace VerifyMSTest.SourceGenerator.Tests; // These tests don't use Verify.SourceGenerator to avoid creating a circular depedency between the repos. public class Tests { + readonly UsesVerifyTestDriver testDriver = new(); + readonly ITestOutputHelper output; + + public Tests(ITestOutputHelper output) => this.output = output; + + async Task VerifyGenerator(GeneratorDriverResults results) + { + output.WriteLine($"First run of generators took: {results.FirstRun.TimingInfo.ElapsedTime}"); + output.WriteLine($"Cached re-run of generators took: {results.CachedRun.TimingInfo.ElapsedTime}"); + + await Verify(results.FirstRun.RunResult.SelectGeneratedSources()); + + // Ensure cachability + var trackingNames = TrackingNames.GetTrackingNames(); + var trackedSteps1 = results.FirstRun.RunResult.GetTrackedSteps(trackingNames); + var trackedSteps2 = results.CachedRun.RunResult.GetTrackedSteps(trackingNames); + + trackedSteps2.Keys.ShouldBe(trackedSteps1.Keys); + foreach (var kvp in trackedSteps1) + { + var steps1 = kvp.Value; + var steps2 = trackedSteps2[kvp.Key]; + + steps2.Length.ShouldBe(steps1.Length); + for (var i = 0; i < steps1.Length; i++) + { + var outputs1 = steps1[i].Outputs; + var outputs2 = steps2[i].Outputs; + + outputs1.Select(o => o.Value).ShouldBe(outputs2.Select(o => o.Value)); + outputs2.Select(o => o.Reason).ShouldAllBe(r => r == IncrementalStepRunReason.Cached || r == IncrementalStepRunReason.Unchanged); + } + } + } + [Fact] public Task NoAttribute() { @@ -13,7 +52,7 @@ public class Foo } """; - return Verify(TestDriver.Run(source).SelectGeneratedSources()); + return VerifyGenerator(testDriver.Run(source)); } [Fact] @@ -28,7 +67,7 @@ public partial class Foo } """; - return Verify(TestDriver.Run(source).SelectGeneratedSources()); + return VerifyGenerator(testDriver.Run(source)); } [Fact] @@ -45,7 +84,7 @@ public partial class Bar } """; - return Verify(TestDriver.Run(source).SelectGeneratedSources()); + return VerifyGenerator(testDriver.Run(source)); } [Fact] @@ -77,7 +116,7 @@ public partial class TestClass2 } """; - return Verify(TestDriver.Run(source).SelectGeneratedSources()); + return VerifyGenerator(testDriver.Run(source)); } [Fact] @@ -97,6 +136,6 @@ public partial class Derived : Base } """; - return Verify(TestDriver.Run(source).SelectGeneratedSources()); + return VerifyGenerator(testDriver.Run(source)); } } diff --git a/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs new file mode 100644 index 000000000..798367e02 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs @@ -0,0 +1,10 @@ +using Microsoft.CodeAnalysis; + +namespace VerifyMSTest.SourceGenerator.Tests; + +class UsesVerifyTestDriver : TestDriver +{ + public UsesVerifyTestDriver() : base([new UsesVerifyGenerator().AsSourceGenerator()]) + { + } +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj index ff6aec999..e46cdadcd 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj +++ b/src/Verify.MSTest.SourceGenerator.Tests/Verify.MSTest.SourceGenerator.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs index b8adb3d5d..55b26b4a7 100644 --- a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs +++ b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs @@ -1,8 +1,17 @@ namespace VerifyMSTest.SourceGenerator; -static class TrackingNames +public static class TrackingNames { public static string InitialTransform { get; } = nameof(InitialTransform); public static string RemoveNulls { get; } = nameof(RemoveNulls); public static string Collect { get; } = nameof(Collect); + + public static IReadOnlyCollection GetTrackingNames() => + typeof(TrackingNames) + .GetProperties() + .Where(p => p.PropertyType == typeof(string)) + .Select(p => p.GetValue(null)) + .OfType() + .Where(x => !string.IsNullOrEmpty(x)) + .ToList(); } From addedd18bbd82d950cbfcc1b04ab5fb0d8efc091 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 16:50:44 +1000 Subject: [PATCH 35/69] global usings --- src/Verify.MSTest.SourceGenerator/EquatableArray.cs | 2 -- src/Verify.MSTest.SourceGenerator/GlobalUsings.cs | 6 ++++++ src/Verify.MSTest.SourceGenerator/HashCode.cs | 2 -- src/Verify.MSTest.SourceGenerator/Parser.cs | 4 ---- src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs | 5 ----- 5 files changed, 6 insertions(+), 13 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator/GlobalUsings.cs diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs index cd00f4629..7f173c3e6 100644 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace VerifyMSTest.SourceGenerator; // Suppressing style warnings to keep code aligned with upstream version from diff --git a/src/Verify.MSTest.SourceGenerator/GlobalUsings.cs b/src/Verify.MSTest.SourceGenerator/GlobalUsings.cs new file mode 100644 index 000000000..55b7d69ce --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator/GlobalUsings.cs @@ -0,0 +1,6 @@ +global using System.Collections.Immutable; +global using System.ComponentModel; +global using Microsoft.CodeAnalysis; +global using Microsoft.CodeAnalysis.CSharp; +global using Microsoft.CodeAnalysis.CSharp.Syntax; +global using Microsoft.CodeAnalysis.Text; \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs index d9203adf9..c66110864 100644 --- a/src/Verify.MSTest.SourceGenerator/HashCode.cs +++ b/src/Verify.MSTest.SourceGenerator/HashCode.cs @@ -1,5 +1,3 @@ -using System.ComponentModel; - namespace VerifyMSTest.SourceGenerator; // A local version of System.HashCode to avoid complications from NuGet private dependencies. diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index e341f2fad..8e4a97d05 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -1,7 +1,3 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - namespace VerifyMSTest.SourceGenerator; static class Parser diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index bd0aff74a..0682cee00 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -1,8 +1,3 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - namespace VerifyMSTest.SourceGenerator; [Generator] From 5c2374fa480caf4bb13fd3523720f5e79878d6dd Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 16:55:14 +1000 Subject: [PATCH 36/69] Create VerifyMSTest.slnf --- src/VerifyMSTest.slnf | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/VerifyMSTest.slnf diff --git a/src/VerifyMSTest.slnf b/src/VerifyMSTest.slnf new file mode 100644 index 000000000..25ae219ea --- /dev/null +++ b/src/VerifyMSTest.slnf @@ -0,0 +1,13 @@ +{ + "solution": { + "path": "Verify.sln", + "projects": [ + "Verify.MSTest.DerivePaths.Tests\\Verify.MSTest.DerivePaths.Tests.csproj", + "Verify.MSTest.SourceGenerator.Tests\\Verify.MSTest.SourceGenerator.Tests.csproj", + "Verify.MSTest.SourceGenerator\\Verify.MSTest.SourceGenerator.csproj", + "Verify.MSTest.Tests\\Verify.MSTest.Tests.csproj", + "Verify.MSTest\\Verify.MSTest.csproj", + "Verify\\Verify.csproj" + ] + } +} \ No newline at end of file From 20bdab7b367946f2614b376af85ae803cdc4c8a4 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 16:56:55 +1000 Subject: [PATCH 37/69] global usings --- .../GeneratorDriverResults.cs | 2 -- .../GeneratorDriverRunResultExtensions.cs | 3 --- src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs | 5 +++++ src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs | 4 ---- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 4 ---- .../UsesVerifyTestDriver.cs | 4 +--- 6 files changed, 6 insertions(+), 16 deletions(-) create mode 100644 src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs index 398d060e4..12565c492 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs @@ -1,5 +1,3 @@ -using Microsoft.CodeAnalysis; - namespace VerifyMSTest.SourceGenerator.Tests; readonly record struct GeneratorDriverResults(GeneratorDriverResult FirstRun, GeneratorDriverResult CachedRun); diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs index 627077156..f4302534b 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs @@ -1,6 +1,3 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; - namespace VerifyMSTest.SourceGenerator.Tests; static class GeneratorDriverRunResultExtensions diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs b/src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs new file mode 100644 index 000000000..17bd95524 --- /dev/null +++ b/src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs @@ -0,0 +1,5 @@ +global using System.Collections.Immutable; +global using Microsoft.CodeAnalysis; +global using Microsoft.CodeAnalysis.CSharp; +global using Shouldly; +global using Xunit.Abstractions; \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index f98024b57..cec4c056b 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -1,7 +1,3 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Xunit.Abstractions; - namespace VerifyMSTest.SourceGenerator.Tests; class TestDriver diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 4eec02d55..b150f54ad 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -1,7 +1,3 @@ -using Microsoft.CodeAnalysis; -using Shouldly; -using Xunit.Abstractions; - namespace VerifyMSTest.SourceGenerator.Tests; // These tests don't use Verify.SourceGenerator to avoid creating a circular depedency between the repos. diff --git a/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs index 798367e02..76258ef64 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs @@ -1,6 +1,4 @@ -using Microsoft.CodeAnalysis; - -namespace VerifyMSTest.SourceGenerator.Tests; +namespace VerifyMSTest.SourceGenerator.Tests; class UsesVerifyTestDriver : TestDriver { From dbdc1eda778849dc9a6aca92b683a0743e671f8f Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 16:59:41 +1000 Subject: [PATCH 38/69] redundant private --- src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs | 2 +- .../IndentedStringBuilder.cs | 10 +++++----- .../UsesVerifyGenerator.cs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index cec4c056b..1cafd1951 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -2,7 +2,7 @@ namespace VerifyMSTest.SourceGenerator.Tests; class TestDriver { - private readonly IEnumerable sourceGenerators; + readonly IEnumerable sourceGenerators; public TestDriver(IEnumerable sourceGenerators) => this.sourceGenerators = sourceGenerators; diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 164febb20..d6fc7481e 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -4,10 +4,10 @@ class IndentedStringBuilder { // Default capacity based on the closest power of 2 to what's used in our own tests. // This may need to be tweaked over time. - private const int DefaultStringBuilderCapacity = 4096; - private readonly StringBuilder builder; - private int indentLevel = 0; - private bool isIndented = false; + const int DefaultStringBuilderCapacity = 4096; + readonly StringBuilder builder; + int indentLevel = 0; + bool isIndented = false; public IndentedStringBuilder(int capacity = DefaultStringBuilderCapacity) => builder = new StringBuilder(capacity); @@ -70,7 +70,7 @@ public IndentedStringBuilder Clear() public override string ToString() => builder.ToString(); - private void WriteIndentIfNeeded() + void WriteIndentIfNeeded() { if (!isIndented) { diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 0682cee00..fca7240c1 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -24,9 +24,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(classesCollection, Execute); } - private static bool IsSyntaxEligibleForGeneration(SyntaxNode node, CancellationToken ct) => node is ClassDeclarationSyntax; + static bool IsSyntaxEligibleForGeneration(SyntaxNode node, CancellationToken ct) => node is ClassDeclarationSyntax; - private static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) + static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) { if (context.TargetSymbol is not INamedTypeSymbol typeSymbol) { @@ -43,7 +43,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return Parser.Parse(typeSymbol, typeSyntax); } - private static void Execute(SourceProductionContext context, ImmutableArray classesToGenerate) + static void Execute(SourceProductionContext context, ImmutableArray classesToGenerate) { if (classesToGenerate.IsDefaultOrEmpty) { From cfaedbdd33d08afb7df7c8d662106be3b825daef Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:06:41 +1000 Subject: [PATCH 39/69] use computed properties --- src/Verify.MSTest.SourceGenerator/Parser.cs | 2 +- src/Verify.MSTest.SourceGenerator/TrackingNames.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index 8e4a97d05..b16d8d665 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -2,7 +2,7 @@ namespace VerifyMSTest.SourceGenerator; static class Parser { - public static string MarkerAttributeName { get; } = "VerifyMSTest.UsesVerifyAttribute"; + public static string MarkerAttributeName => "VerifyMSTest.UsesVerifyAttribute"; public static ClassToGenerate? Parse(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeSyntax) { diff --git a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs index 55b26b4a7..df8b8d6fe 100644 --- a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs +++ b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs @@ -2,9 +2,9 @@ namespace VerifyMSTest.SourceGenerator; public static class TrackingNames { - public static string InitialTransform { get; } = nameof(InitialTransform); - public static string RemoveNulls { get; } = nameof(RemoveNulls); - public static string Collect { get; } = nameof(Collect); + public static string InitialTransform => nameof(InitialTransform); + public static string RemoveNulls => nameof(RemoveNulls); + public static string Collect => nameof(Collect); public static IReadOnlyCollection GetTrackingNames() => typeof(TrackingNames) From 3200e02b15b99708a74cc86dc5532c53959119c9 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:07:59 +1000 Subject: [PATCH 40/69] use some target typed new --- src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs | 6 +++--- src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs | 2 +- src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs | 2 +- src/Verify.MSTest.SourceGenerator/Parser.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index 1cafd1951..e45ae7436 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -34,8 +34,8 @@ public GeneratorDriverResults Run(string source) var results2 = driver.GetRunResult(); var timings2 = driver.GetTimingInfo(); - return new GeneratorDriverResults( - new GeneratorDriverResult(results1, timings1), - new GeneratorDriverResult(results2, timings2)); + return new( + new(results1, timings1), + new(results2, timings2)); } } diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index b496a2fa8..263281e99 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -10,6 +10,6 @@ public ClassToGenerate(string? @namespace, string className, ParentClass[] paren { Namespace = @namespace; ClassName = className; - ParentClasses = new EquatableArray(parentClasses); + ParentClasses = new(parentClasses); } } diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index d6fc7481e..53bdfedd5 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -10,7 +10,7 @@ class IndentedStringBuilder bool isIndented = false; public IndentedStringBuilder(int capacity = DefaultStringBuilderCapacity) => - builder = new StringBuilder(capacity); + builder = new(capacity); public IndentedStringBuilder IncreaseIndent() { diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index b16d8d665..8b766114a 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -84,7 +84,7 @@ static bool IsAllowedKind(SyntaxKind kind) => while (parentSyntax is not null && IsAllowedKind(parentSyntax.Kind())) { - parents.Push(new ParentClass( + parents.Push(new( keyword: parentSyntax.Keyword.ValueText, name: GetTypeNameWithGenericParameters(parentSyntax))); From 33729b76e3023491edde506192c7076b40226576 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:09:10 +1000 Subject: [PATCH 41/69] suppress some ReSharper hints --- src/Verify.MSTest.SourceGenerator/EquatableArray.cs | 2 ++ src/Verify.MSTest.SourceGenerator/HashCode.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs index 7f173c3e6..d8e2ce80f 100644 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -1,3 +1,5 @@ +// ReSharper disable ArrangeMethodOrOperatorBody +// ReSharper disable ArrangeConstructorOrDestructorBody namespace VerifyMSTest.SourceGenerator; // Suppressing style warnings to keep code aligned with upstream version from diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs index c66110864..d148c0901 100644 --- a/src/Verify.MSTest.SourceGenerator/HashCode.cs +++ b/src/Verify.MSTest.SourceGenerator/HashCode.cs @@ -1,3 +1,6 @@ +// ReSharper disable SuggestVarOrType_BuiltInTypes +// ReSharper disable ArrangeMethodOrOperatorBody +// ReSharper disable ArrangeRedundantParentheses namespace VerifyMSTest.SourceGenerator; // A local version of System.HashCode to avoid complications from NuGet private dependencies. From 504c718d79fca46efb01839fbb25d8deaebccd4b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:09:48 +1000 Subject: [PATCH 42/69] redundant inits --- src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 53bdfedd5..752d1d86f 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -6,8 +6,8 @@ class IndentedStringBuilder // This may need to be tweaked over time. const int DefaultStringBuilderCapacity = 4096; readonly StringBuilder builder; - int indentLevel = 0; - bool isIndented = false; + int indentLevel; + bool isIndented; public IndentedStringBuilder(int capacity = DefaultStringBuilderCapacity) => builder = new(capacity); From 823caf391626087dc89b515a67cbe47a68d177c6 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:12:25 +1000 Subject: [PATCH 43/69] Update EquatableArray.cs --- src/Verify.MSTest.SourceGenerator/EquatableArray.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs index d8e2ce80f..a2b2b5955 100644 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -1,5 +1,6 @@ // ReSharper disable ArrangeMethodOrOperatorBody // ReSharper disable ArrangeConstructorOrDestructorBody +// ReSharper disable UseCollectionExpression namespace VerifyMSTest.SourceGenerator; // Suppressing style warnings to keep code aligned with upstream version from From d4eb826f94374363735330d5637c9a4b81f621f9 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:17:03 +1000 Subject: [PATCH 44/69] redundant private --- src/Verify.MSTest.SourceGenerator/Emitter.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index e689a4811..5d7e5234f 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -2,7 +2,7 @@ namespace VerifyMSTest.SourceGenerator; static class Emitter { - private static readonly string AutoGenerationHeader = """ + static readonly string AutoGenerationHeader = """ //----------------------------------------------------- // This code was generated by a tool. // @@ -12,12 +12,12 @@ static class Emitter //----------------------------------------------------- """; - private static readonly string GeneratedCodeAttribute = + static readonly string GeneratedCodeAttribute = $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(Emitter).Assembly.GetName().Name}\", \"{typeof(Emitter).Assembly.GetName().Version}\")]"; - private static readonly IndentedStringBuilder IndentedStringBuilder = new(); + static readonly IndentedStringBuilder IndentedStringBuilder = new(); - private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) + static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { if (classToGenerate.Namespace is not null) { @@ -35,7 +35,7 @@ private static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate cla }; } - private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate classToGenerate) + static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate classToGenerate) { foreach (var parentClass in classToGenerate.ParentClasses) { @@ -54,7 +54,7 @@ private static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate c } } - private static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) => + static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) => sb.AppendLine(GeneratedCodeAttribute) .Append("partial class ").AppendLine(classToGenerate.ClassName) .AppendLine("{") From c1be1380733469190bc3d7c106d5cd099f59fb02 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:17:42 +1000 Subject: [PATCH 45/69] fix cancel name --- src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index fca7240c1..212a3d8bf 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -24,9 +24,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(classesCollection, Execute); } - static bool IsSyntaxEligibleForGeneration(SyntaxNode node, CancellationToken ct) => node is ClassDeclarationSyntax; + static bool IsSyntaxEligibleForGeneration(SyntaxNode node, Cancel _) => node is ClassDeclarationSyntax; - static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken ct) + static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, Cancel cancel) { if (context.TargetSymbol is not INamedTypeSymbol typeSymbol) { @@ -38,7 +38,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return null; } - ct.ThrowIfCancellationRequested(); + cancel.ThrowIfCancellationRequested(); return Parser.Parse(typeSymbol, typeSyntax); } From 71ef509393d074d6bcb0a814ecf76d4a2cd4304b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:18:06 +1000 Subject: [PATCH 46/69] Update UsesVerifyGenerator.cs --- src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs index 212a3d8bf..e77b5181e 100644 --- a/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs +++ b/src/Verify.MSTest.SourceGenerator/UsesVerifyGenerator.cs @@ -28,19 +28,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context) static ClassToGenerate? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, Cancel cancel) { - if (context.TargetSymbol is not INamedTypeSymbol typeSymbol) + if (context.TargetSymbol is not INamedTypeSymbol symbol) { return null; } - if (context.TargetNode is not TypeDeclarationSyntax typeSyntax) + if (context.TargetNode is not TypeDeclarationSyntax syntax) { return null; } cancel.ThrowIfCancellationRequested(); - return Parser.Parse(typeSymbol, typeSyntax); + return Parser.Parse(symbol, syntax); } static void Execute(SourceProductionContext context, ImmutableArray classesToGenerate) From 3be21d6f0a19866b9706ebe88e873323318d9038 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 17:19:45 +1000 Subject: [PATCH 47/69] primary constructors --- .../TestDriver.cs | 6 +----- .../UsesVerifyTestDriver.cs | 8 ++------ .../IndentedStringBuilder.cs | 7 ++----- src/Verify.MSTest.SourceGenerator/ParentClass.cs | 12 +++--------- src/Verify.MSTest.SourceGenerator/Parser.cs | 4 ++-- 5 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index e45ae7436..9cb870f97 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -1,11 +1,7 @@ namespace VerifyMSTest.SourceGenerator.Tests; -class TestDriver +class TestDriver(IEnumerable sourceGenerators) { - readonly IEnumerable sourceGenerators; - - public TestDriver(IEnumerable sourceGenerators) => this.sourceGenerators = sourceGenerators; - public GeneratorDriverResults Run(string source) { var syntaxTree = CSharpSyntaxTree.ParseText(source); diff --git a/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs index 76258ef64..8bafdbe24 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs @@ -1,8 +1,4 @@ namespace VerifyMSTest.SourceGenerator.Tests; -class UsesVerifyTestDriver : TestDriver -{ - public UsesVerifyTestDriver() : base([new UsesVerifyGenerator().AsSourceGenerator()]) - { - } -} +class UsesVerifyTestDriver() : + TestDriver([new UsesVerifyGenerator().AsSourceGenerator()]); diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 752d1d86f..306fa20fc 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -1,17 +1,14 @@ namespace VerifyMSTest.SourceGenerator; -class IndentedStringBuilder +class IndentedStringBuilder(int capacity = IndentedStringBuilder.DefaultStringBuilderCapacity) { // Default capacity based on the closest power of 2 to what's used in our own tests. // This may need to be tweaked over time. const int DefaultStringBuilderCapacity = 4096; - readonly StringBuilder builder; + readonly StringBuilder builder = new(capacity); int indentLevel; bool isIndented; - public IndentedStringBuilder(int capacity = DefaultStringBuilderCapacity) => - builder = new(capacity); - public IndentedStringBuilder IncreaseIndent() { indentLevel += 1; diff --git a/src/Verify.MSTest.SourceGenerator/ParentClass.cs b/src/Verify.MSTest.SourceGenerator/ParentClass.cs index 00e0bd8f8..60005f7c6 100644 --- a/src/Verify.MSTest.SourceGenerator/ParentClass.cs +++ b/src/Verify.MSTest.SourceGenerator/ParentClass.cs @@ -1,13 +1,7 @@ namespace VerifyMSTest.SourceGenerator; -readonly record struct ParentClass +readonly record struct ParentClass(string Keyword, string Name) { - public string Keyword { get; } - public string Name { get; } - - public ParentClass(string keyword, string name) - { - Keyword = keyword; - Name = name; - } + public string Keyword { get; } = Keyword; + public string Name { get; } = Name; } diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index 8b766114a..86c3fd2ce 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -85,8 +85,8 @@ static bool IsAllowedKind(SyntaxKind kind) => while (parentSyntax is not null && IsAllowedKind(parentSyntax.Kind())) { parents.Push(new( - keyword: parentSyntax.Keyword.ValueText, - name: GetTypeNameWithGenericParameters(parentSyntax))); + Keyword: parentSyntax.Keyword.ValueText, + Name: GetTypeNameWithGenericParameters(parentSyntax))); parentSyntax = parentSyntax.Parent as TypeDeclarationSyntax; } From 2a684a35af3f314255b9796c1b3f392b88871b84 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 18:31:47 +1000 Subject: [PATCH 48/69] remove redundant private --- src/Verify.MSTest.SourceGenerator/Parser.cs | 25 ++++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index 86c3fd2ce..a4469dd50 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -19,13 +19,14 @@ static class Parser return new ClassToGenerate(ns, name, parents); } - private static string? GetNamespace(INamedTypeSymbol symbol) => + static string? GetNamespace(INamedTypeSymbol symbol) => symbol.ContainingNamespace.IsGlobalNamespace ? null : symbol.ContainingNamespace.ToString(); - private static bool HasTestContextProperty(INamedTypeSymbol symbol) => - HasMarkerAttributeOnBase(symbol) || HasTestContextPropertyDefinedInBase(symbol); + static bool HasTestContextProperty(INamedTypeSymbol symbol) => + HasMarkerAttributeOnBase(symbol) || + HasTestContextPropertyDefinedInBase(symbol); - private static bool HasMarkerAttributeOnBase(INamedTypeSymbol symbol) + static bool HasMarkerAttributeOnBase(INamedTypeSymbol symbol) { static bool HasMarkerAttribute(ISymbol symbol) => symbol @@ -47,13 +48,14 @@ static bool HasMarkerAttribute(ISymbol symbol) => return false; } - private static bool HasTestContextPropertyDefinedInBase(INamedTypeSymbol symbol) + static bool HasTestContextPropertyDefinedInBase(INamedTypeSymbol symbol) { static bool HasTestContextProperty(INamedTypeSymbol symbol) => symbol .GetMembers() .OfType() - .Any(p => p.Name == "TestContext" && p.Type.Name == "TestContext"); + .Any(_ => _.Name == "TestContext" && + _.Type.Name == "TestContext"); var parent = symbol.BaseType; @@ -70,13 +72,14 @@ static bool HasTestContextProperty(INamedTypeSymbol symbol) => return false; } - private static ParentClass[] GetParentClasses(TypeDeclarationSyntax typeSyntax) + static ParentClass[] GetParentClasses(TypeDeclarationSyntax typeSyntax) { // We can only be nested in class/struct/record static bool IsAllowedKind(SyntaxKind kind) => - kind == SyntaxKind.ClassDeclaration || - kind == SyntaxKind.StructDeclaration || - kind == SyntaxKind.RecordDeclaration; + kind is + SyntaxKind.ClassDeclaration or + SyntaxKind.StructDeclaration or + SyntaxKind.RecordDeclaration; var parents = new Stack(); @@ -94,6 +97,6 @@ static bool IsAllowedKind(SyntaxKind kind) => return parents.ToArray(); } - private static string GetTypeNameWithGenericParameters(TypeDeclarationSyntax typeSyntax) => + static string GetTypeNameWithGenericParameters(TypeDeclarationSyntax typeSyntax) => typeSyntax.Identifier.ToString() + typeSyntax.TypeParameterList; } \ No newline at end of file From d4748d5ae138609949f34857e5e32ffd6bebe23b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 18:40:47 +1000 Subject: [PATCH 49/69] remove redundant namespaces --- src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs | 2 -- src/Verify.MSTest.SourceGenerator/Emitter.cs | 2 -- src/Verify.MSTest.SourceGenerator/EquatableArray.cs | 2 -- src/Verify.MSTest.SourceGenerator/HashCode.cs | 3 +-- src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs | 2 -- src/Verify.MSTest.SourceGenerator/ParentClass.cs | 2 -- 6 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index 263281e99..0f887490a 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -1,5 +1,3 @@ -namespace VerifyMSTest.SourceGenerator; - readonly record struct ClassToGenerate { public string? Namespace { get; } diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index 5d7e5234f..51d9b0e99 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -1,5 +1,3 @@ -namespace VerifyMSTest.SourceGenerator; - static class Emitter { static readonly string AutoGenerationHeader = """ diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs index a2b2b5955..d204e65eb 100644 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs @@ -1,8 +1,6 @@ // ReSharper disable ArrangeMethodOrOperatorBody // ReSharper disable ArrangeConstructorOrDestructorBody // ReSharper disable UseCollectionExpression -namespace VerifyMSTest.SourceGenerator; - // Suppressing style warnings to keep code aligned with upstream version from // https://github.com/andrewlock/StronglyTypedId/blob/e5df78d0aa72f2232f423938c0d98d9bf4517092/src/StronglyTypedIds/EquatableArray.cs #pragma warning disable IDE1006 // Naming rule violation Prefix is not expected diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs index d148c0901..3840a13f8 100644 --- a/src/Verify.MSTest.SourceGenerator/HashCode.cs +++ b/src/Verify.MSTest.SourceGenerator/HashCode.cs @@ -1,7 +1,6 @@ // ReSharper disable SuggestVarOrType_BuiltInTypes // ReSharper disable ArrangeMethodOrOperatorBody // ReSharper disable ArrangeRedundantParentheses -namespace VerifyMSTest.SourceGenerator; // A local version of System.HashCode to avoid complications from NuGet private dependencies. // See https://github.com/dotnet/aspnetcore/commit/d074523ba3f477501cf2a113f1d64e9a21627e53. @@ -12,7 +11,7 @@ namespace VerifyMSTest.SourceGenerator; #pragma warning disable IDE1006 // Naming rule violation Prefix is not expected #pragma warning disable IDE0022 // Use expression body for method -internal struct HashCode +struct HashCode { private static readonly uint s_seed = GenerateGlobalSeed(); diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 306fa20fc..43453ec15 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -1,5 +1,3 @@ -namespace VerifyMSTest.SourceGenerator; - class IndentedStringBuilder(int capacity = IndentedStringBuilder.DefaultStringBuilderCapacity) { // Default capacity based on the closest power of 2 to what's used in our own tests. diff --git a/src/Verify.MSTest.SourceGenerator/ParentClass.cs b/src/Verify.MSTest.SourceGenerator/ParentClass.cs index 60005f7c6..b2ec2c9ef 100644 --- a/src/Verify.MSTest.SourceGenerator/ParentClass.cs +++ b/src/Verify.MSTest.SourceGenerator/ParentClass.cs @@ -1,5 +1,3 @@ -namespace VerifyMSTest.SourceGenerator; - readonly record struct ParentClass(string Keyword, string Name) { public string Keyword { get; } = Keyword; From db1b9809fb875a90ddd448b0ca4e6e6caf374f9d Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 19:39:46 +1000 Subject: [PATCH 50/69] cleanup --- src/Verify.MSTest.SourceGenerator/Emitter.cs | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index 51d9b0e99..0ce7d36ae 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -15,45 +15,45 @@ static class Emitter static readonly IndentedStringBuilder IndentedStringBuilder = new(); - static void WriteNamespace(IndentedStringBuilder sb, ClassToGenerate classToGenerate) + static void WriteNamespace(IndentedStringBuilder builder, ClassToGenerate classToGenerate) { if (classToGenerate.Namespace is not null) { - sb.Append("namespace ").AppendLine(classToGenerate.Namespace) + builder.Append("namespace ").AppendLine(classToGenerate.Namespace) .AppendLine("{") .IncreaseIndent(); } - WriteParentTypes(sb, classToGenerate); + WriteParentTypes(builder, classToGenerate); if (classToGenerate.Namespace is not null) { - sb.DecreaseIndent() + builder.DecreaseIndent() .AppendLine("}"); }; } - static void WriteParentTypes(IndentedStringBuilder sb, ClassToGenerate classToGenerate) + static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate classToGenerate) { foreach (var parentClass in classToGenerate.ParentClasses) { - sb.Append("partial ").Append(parentClass.Keyword).Append(" ").AppendLine(parentClass.Name) + builder.Append("partial ").Append(parentClass.Keyword).Append(" ").AppendLine(parentClass.Name) .AppendLine("{"); - sb.IncreaseIndent(); + builder.IncreaseIndent(); } - WriteClass(sb, classToGenerate); + WriteClass(builder, classToGenerate); foreach (var parentClass in classToGenerate.ParentClasses) { - sb.DecreaseIndent() + builder.DecreaseIndent() .AppendLine("}"); } } - static void WriteClass(IndentedStringBuilder sb, ClassToGenerate classToGenerate) => - sb.AppendLine(GeneratedCodeAttribute) + static void WriteClass(IndentedStringBuilder builder, ClassToGenerate classToGenerate) => + builder.AppendLine(GeneratedCodeAttribute) .Append("partial class ").AppendLine(classToGenerate.ClassName) .AppendLine("{") .AppendLine(" public TestContext TestContext") From 18a7032acf73b639c6ae5d29746194fe4ff79ead Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 19:46:24 +1000 Subject: [PATCH 51/69] cleanup --- src/Verify.MSTest.SourceGenerator/TrackingNames.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs index df8b8d6fe..d70343c63 100644 --- a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs +++ b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs @@ -9,9 +9,9 @@ public static class TrackingNames public static IReadOnlyCollection GetTrackingNames() => typeof(TrackingNames) .GetProperties() - .Where(p => p.PropertyType == typeof(string)) - .Select(p => p.GetValue(null)) + .Where(_ => _.PropertyType == typeof(string)) + .Select(_ => _.GetValue(null)) .OfType() - .Where(x => !string.IsNullOrEmpty(x)) + .Where(_ => !string.IsNullOrEmpty(_)) .ToList(); } From a75b0e68e236f63be0f846366861f96099a35946 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 19:52:23 +1000 Subject: [PATCH 52/69] Update Tests.cs --- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index b150f54ad..59366cba8 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -1,6 +1,6 @@ namespace VerifyMSTest.SourceGenerator.Tests; -// These tests don't use Verify.SourceGenerator to avoid creating a circular depedency between the repos. +// These tests don't use Verify.SourceGenerator to avoid creating a circular dependency between the repos. public class Tests { @@ -33,8 +33,8 @@ async Task VerifyGenerator(GeneratorDriverResults results) var outputs1 = steps1[i].Outputs; var outputs2 = steps2[i].Outputs; - outputs1.Select(o => o.Value).ShouldBe(outputs2.Select(o => o.Value)); - outputs2.Select(o => o.Reason).ShouldAllBe(r => r == IncrementalStepRunReason.Cached || r == IncrementalStepRunReason.Unchanged); + outputs1.Select(_ => _.Value).ShouldBe(outputs2.Select(_ => _.Value)); + outputs2.Select(_ => _.Reason).ShouldAllBe(_ => _ == IncrementalStepRunReason.Cached || _ == IncrementalStepRunReason.Unchanged); } } } From 9dad2a604c44a5d3f247d22cff14d07423087c1b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 19:55:24 +1000 Subject: [PATCH 53/69] cleanup --- .../Tests.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 59366cba8..ea66d7c89 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -2,12 +2,9 @@ namespace VerifyMSTest.SourceGenerator.Tests; // These tests don't use Verify.SourceGenerator to avoid creating a circular dependency between the repos. -public class Tests +public class Tests(ITestOutputHelper output) { readonly UsesVerifyTestDriver testDriver = new(); - readonly ITestOutputHelper output; - - public Tests(ITestOutputHelper output) => this.output = output; async Task VerifyGenerator(GeneratorDriverResults results) { @@ -22,10 +19,9 @@ async Task VerifyGenerator(GeneratorDriverResults results) var trackedSteps2 = results.CachedRun.RunResult.GetTrackedSteps(trackingNames); trackedSteps2.Keys.ShouldBe(trackedSteps1.Keys); - foreach (var kvp in trackedSteps1) + foreach (var (key, steps1) in trackedSteps1) { - var steps1 = kvp.Value; - var steps2 = trackedSteps2[kvp.Key]; + var steps2 = trackedSteps2[key]; steps2.Length.ShouldBe(steps1.Length); for (var i = 0; i < steps1.Length; i++) @@ -33,8 +29,11 @@ async Task VerifyGenerator(GeneratorDriverResults results) var outputs1 = steps1[i].Outputs; var outputs2 = steps2[i].Outputs; - outputs1.Select(_ => _.Value).ShouldBe(outputs2.Select(_ => _.Value)); - outputs2.Select(_ => _.Reason).ShouldAllBe(_ => _ == IncrementalStepRunReason.Cached || _ == IncrementalStepRunReason.Unchanged); + outputs1.Select(_ => _.Value) + .ShouldBe(outputs2.Select(_ => _.Value)); + outputs2.Select(_ => _.Reason) + .ShouldAllBe(_ => _ == IncrementalStepRunReason.Cached || + _ == IncrementalStepRunReason.Unchanged); } } } From 942f5819bc0d0169531acbe255f1650111d6622c Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 20:00:40 +1000 Subject: [PATCH 54/69] redundant namespaces --- .../GeneratorDriverResults.cs | 2 -- .../GeneratorDriverRunResultExtensions.cs | 16 +++++++--------- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 5 +++-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs index 12565c492..427dd1c50 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverResults.cs @@ -1,5 +1,3 @@ -namespace VerifyMSTest.SourceGenerator.Tests; - readonly record struct GeneratorDriverResults(GeneratorDriverResult FirstRun, GeneratorDriverResult CachedRun); readonly record struct GeneratorDriverResult(GeneratorDriverRunResult RunResult, GeneratorDriverTimingInfo TimingInfo); \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs index f4302534b..b895e1a42 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/GeneratorDriverRunResultExtensions.cs @@ -1,18 +1,16 @@ -namespace VerifyMSTest.SourceGenerator.Tests; - static class GeneratorDriverRunResultExtensions { - public static (string HintName, string SourceText)? SelectGeneratedSources(this GeneratorDriverRunResult gdrr) => - gdrr + public static (string HintName, string SourceText)? SelectGeneratedSources(this GeneratorDriverRunResult runResult) => + runResult .Results - .SelectMany(grr => grr.GeneratedSources) - .Select(gs => (gs.HintName, gs.SourceText.ToString())) + .SelectMany(_ => _.GeneratedSources) + .Select(_ => (_.HintName, _.SourceText.ToString())) .SingleOrDefault(); public static Dictionary> GetTrackedSteps(this GeneratorDriverRunResult runResult, IReadOnlyCollection trackingNames) => runResult .Results - .SelectMany(result => result.TrackedSteps) - .Where(step => trackingNames.Contains(step.Key)) - .ToDictionary(x => x.Key, x => x.Value); + .SelectMany(_ => _.TrackedSteps) + .Where(_ => trackingNames.Contains(_.Key)) + .ToDictionary(_ => _.Key, _ => _.Value); } \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index ea66d7c89..41daf88e3 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -1,7 +1,8 @@ -namespace VerifyMSTest.SourceGenerator.Tests; - // These tests don't use Verify.SourceGenerator to avoid creating a circular dependency between the repos. +using VerifyMSTest.SourceGenerator; +using VerifyMSTest.SourceGenerator.Tests; + public class Tests(ITestOutputHelper output) { readonly UsesVerifyTestDriver testDriver = new(); From 902a9c6d47c8c6c863cc1ff78e984336878afd35 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 20:04:25 +1000 Subject: [PATCH 55/69] use file scoped namespace --- ...Tests.HasAttributeInNamespace.verified.txt | 16 ++++---- ...NamespaceAndClassWithGenerics.verified.txt | 38 +++++++++---------- src/Verify.MSTest.SourceGenerator/Emitter.cs | 10 +---- 3 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt index 234a5a9c5..b7a7b028e 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -9,17 +9,15 @@ // //----------------------------------------------------- -namespace Foo +namespace Foo; +[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] +partial class Bar { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] - partial class Bar + public TestContext TestContext { - public TestContext TestContext - { - get => Verifier.CurrentTestContext.Value!; - set => Verifier.CurrentTestContext.Value = value; - } + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } } -} +} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt index cf38bb523..f83982593 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -9,42 +9,38 @@ // //----------------------------------------------------- -namespace A.B +namespace A.B; +partial class C { - partial class C + partial class D { - partial class D + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class TestClass1 { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] - partial class TestClass1 + public TestContext TestContext { - public TestContext TestContext - { - get => Verifier.CurrentTestContext.Value!; - set => Verifier.CurrentTestContext.Value = value; - } + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } } } } -namespace A.B +namespace A.B; +partial class C { - partial class C + partial class D { - partial class D + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class TestClass2 { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] - partial class TestClass2 + public TestContext TestContext { - public TestContext TestContext - { - get => Verifier.CurrentTestContext.Value!; - set => Verifier.CurrentTestContext.Value = value; - } + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; } } } } -} +} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index 0ce7d36ae..88ae551da 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -19,18 +19,10 @@ static void WriteNamespace(IndentedStringBuilder builder, ClassToGenerate classT { if (classToGenerate.Namespace is not null) { - builder.Append("namespace ").AppendLine(classToGenerate.Namespace) - .AppendLine("{") - .IncreaseIndent(); + builder.AppendLine($"namespace {classToGenerate.Namespace};"); } WriteParentTypes(builder, classToGenerate); - - if (classToGenerate.Namespace is not null) - { - builder.DecreaseIndent() - .AppendLine("}"); - }; } static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate classToGenerate) From dd8fc71602356f3a9971aec4fa7eb186ff328832 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 20:05:47 +1000 Subject: [PATCH 56/69] Revert "use file scoped namespace" This reverts commit 902a9c6d47c8c6c863cc1ff78e984336878afd35. --- ...Tests.HasAttributeInNamespace.verified.txt | 16 ++++---- ...NamespaceAndClassWithGenerics.verified.txt | 38 ++++++++++--------- src/Verify.MSTest.SourceGenerator/Emitter.cs | 10 ++++- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt index b7a7b028e..234a5a9c5 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNamespace.verified.txt @@ -9,15 +9,17 @@ // //----------------------------------------------------- -namespace Foo; -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] -partial class Bar +namespace Foo { - public TestContext TestContext + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class Bar { - get => Verifier.CurrentTestContext.Value!; - set => Verifier.CurrentTestContext.Value = value; + public TestContext TestContext + { + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; + } } } -} \ No newline at end of file +} diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt index f83982593..cf38bb523 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.HasAttributeInNestedNamespaceAndClassWithGenerics.verified.txt @@ -9,38 +9,42 @@ // //----------------------------------------------------- -namespace A.B; -partial class C +namespace A.B { - partial class D + partial class C { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] - partial class TestClass1 + partial class D { - public TestContext TestContext + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class TestClass1 { - get => Verifier.CurrentTestContext.Value!; - set => Verifier.CurrentTestContext.Value = value; + public TestContext TestContext + { + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; + } } } } } -namespace A.B; -partial class C +namespace A.B { - partial class D + partial class C { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] - partial class TestClass2 + partial class D { - public TestContext TestContext + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Verify.MSTest.SourceGenerator", "1.0.0.0")] + partial class TestClass2 { - get => Verifier.CurrentTestContext.Value!; - set => Verifier.CurrentTestContext.Value = value; + public TestContext TestContext + { + get => Verifier.CurrentTestContext.Value!; + set => Verifier.CurrentTestContext.Value = value; + } } } } } -} \ No newline at end of file +} diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index 88ae551da..0ce7d36ae 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -19,10 +19,18 @@ static void WriteNamespace(IndentedStringBuilder builder, ClassToGenerate classT { if (classToGenerate.Namespace is not null) { - builder.AppendLine($"namespace {classToGenerate.Namespace};"); + builder.Append("namespace ").AppendLine(classToGenerate.Namespace) + .AppendLine("{") + .IncreaseIndent(); } WriteParentTypes(builder, classToGenerate); + + if (classToGenerate.Namespace is not null) + { + builder.DecreaseIndent() + .AppendLine("}"); + }; } static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate classToGenerate) From 4665edadc1807ca1903bf76b296688134a389e84 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 20:37:21 +1000 Subject: [PATCH 57/69] fix merge --- src/Verify.MSTest.Tests/Inheritance/Base.cs | 2 -- src/Verify.MSTest.Tests/Inheritance/Inherited.cs | 2 -- src/Verify.MSTest.Tests/NestedTypeTests.cs | 2 -- src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs | 2 -- src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs | 2 -- src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs | 2 -- src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs | 2 -- src/Verify.MSTest.Tests/Snippets/ParametersSample.cs | 2 -- src/Verify.MSTest.Tests/Snippets/ParametersTests.cs | 2 -- src/Verify.MSTest.Tests/Snippets/Sample.cs | 2 -- src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs | 2 -- src/Verify.MSTest.Tests/VerifyBaseTests.cs | 4 +--- src/Verify.MSTest.Tests/VerifyTextSample.cs | 2 -- 13 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/Verify.MSTest.Tests/Inheritance/Base.cs b/src/Verify.MSTest.Tests/Inheritance/Base.cs index e174c2266..b87cdc493 100644 --- a/src/Verify.MSTest.Tests/Inheritance/Base.cs +++ b/src/Verify.MSTest.Tests/Inheritance/Base.cs @@ -1,5 +1,3 @@ -namespace TheTests; - [TestClass] [UsesVerify] public partial class Base diff --git a/src/Verify.MSTest.Tests/Inheritance/Inherited.cs b/src/Verify.MSTest.Tests/Inheritance/Inherited.cs index 7eb937796..62fa6885d 100644 --- a/src/Verify.MSTest.Tests/Inheritance/Inherited.cs +++ b/src/Verify.MSTest.Tests/Inheritance/Inherited.cs @@ -1,5 +1,3 @@ -namespace TheTests; - [TestClass] [UsesVerify] public partial class Inherited : Base diff --git a/src/Verify.MSTest.Tests/NestedTypeTests.cs b/src/Verify.MSTest.Tests/NestedTypeTests.cs index 3bf97e806..aed60d632 100644 --- a/src/Verify.MSTest.Tests/NestedTypeTests.cs +++ b/src/Verify.MSTest.Tests/NestedTypeTests.cs @@ -1,5 +1,3 @@ -namespace TheTests; - [TestClass] [UsesVerify] public partial class NestedTypeTests diff --git a/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs b/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs index 63f444c0a..2ce507748 100644 --- a/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs +++ b/src/Verify.MSTest.Tests/Scrubbers/ScrubberLevelsSample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - #region ScrubberLevelsSampleMSTest [TestClass] diff --git a/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs b/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs index 62f87ec8e..f991ac94e 100644 --- a/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs +++ b/src/Verify.MSTest.Tests/Scrubbers/ScrubbersSample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - #region ScrubbersSampleMSTest [TestClass] diff --git a/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs b/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs index 383bd11cb..ea3a8d4b4 100644 --- a/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/ExtensionSample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - #region MSTestExtensionSample [TestClass] diff --git a/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs b/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs index ffe8f8362..d2518d236 100644 --- a/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/ParametersHashSample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - #region UseParametersHashMsTest [TestClass] diff --git a/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs b/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs index 2d8d7bdf4..4f56665ae 100644 --- a/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/ParametersSample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - #region MSTestDataRow [TestClass] diff --git a/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs b/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs index 3127f5247..9c5983526 100644 --- a/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs +++ b/src/Verify.MSTest.Tests/Snippets/ParametersTests.cs @@ -1,5 +1,3 @@ -namespace TheTests; - [TestClass] [UsesVerify] public partial class ParametersTests diff --git a/src/Verify.MSTest.Tests/Snippets/Sample.cs b/src/Verify.MSTest.Tests/Snippets/Sample.cs index db728842f..f1953827f 100644 --- a/src/Verify.MSTest.Tests/Snippets/Sample.cs +++ b/src/Verify.MSTest.Tests/Snippets/Sample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - #region SampleTestMSTest [TestClass] diff --git a/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs b/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs index f98ea03e6..23fbb171e 100644 --- a/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs +++ b/src/Verify.MSTest.Tests/Snippets/UniqueForSample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - #region UniqueForSampleMSTest [TestClass] diff --git a/src/Verify.MSTest.Tests/VerifyBaseTests.cs b/src/Verify.MSTest.Tests/VerifyBaseTests.cs index cc8a40bfd..5821df66c 100644 --- a/src/Verify.MSTest.Tests/VerifyBaseTests.cs +++ b/src/Verify.MSTest.Tests/VerifyBaseTests.cs @@ -1,6 +1,4 @@ -namespace TheTests; - -[TestClass] +[TestClass] public class VerifyBaseTests : VerifyBase { [TestMethod] diff --git a/src/Verify.MSTest.Tests/VerifyTextSample.cs b/src/Verify.MSTest.Tests/VerifyTextSample.cs index 8780ce3a7..04787e632 100644 --- a/src/Verify.MSTest.Tests/VerifyTextSample.cs +++ b/src/Verify.MSTest.Tests/VerifyTextSample.cs @@ -1,5 +1,3 @@ -namespace TheTests; - [TestClass] [UsesVerify] public partial class VerifyTextSample From c07eaaf142fa0761f9a4d314aad678c557e65264 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 21:11:38 +1000 Subject: [PATCH 58/69] redundant namespace --- src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index 9cb870f97..60e3cd09a 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -1,5 +1,3 @@ -namespace VerifyMSTest.SourceGenerator.Tests; - class TestDriver(IEnumerable sourceGenerators) { public GeneratorDriverResults Run(string source) @@ -8,7 +6,7 @@ public GeneratorDriverResults Run(string source) IReadOnlyCollection references = [ - MetadataReference.CreateFromFile(typeof(UsesVerifyAttribute).Assembly.Location), + MetadataReference.CreateFromFile(typeof(VerifyMSTest.UsesVerifyAttribute).Assembly.Location), MetadataReference.CreateFromFile(typeof(object).Assembly.Location) ]; From be8864d2c524eab66e556ed5aa912fc6a898daf0 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 21:13:38 +1000 Subject: [PATCH 59/69] redundant namespaces --- src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs | 1 + src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 3 --- .../UsesVerifyTestDriver.cs | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs b/src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs index 17bd95524..fb55680aa 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/GlobalUsings.cs @@ -2,4 +2,5 @@ global using Microsoft.CodeAnalysis; global using Microsoft.CodeAnalysis.CSharp; global using Shouldly; +global using VerifyMSTest.SourceGenerator; global using Xunit.Abstractions; \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 41daf88e3..1879763cb 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -1,8 +1,5 @@ // These tests don't use Verify.SourceGenerator to avoid creating a circular dependency between the repos. -using VerifyMSTest.SourceGenerator; -using VerifyMSTest.SourceGenerator.Tests; - public class Tests(ITestOutputHelper output) { readonly UsesVerifyTestDriver testDriver = new(); diff --git a/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs index 8bafdbe24..5d5e37b8f 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/UsesVerifyTestDriver.cs @@ -1,4 +1,2 @@ -namespace VerifyMSTest.SourceGenerator.Tests; - -class UsesVerifyTestDriver() : +class UsesVerifyTestDriver() : TestDriver([new UsesVerifyGenerator().AsSourceGenerator()]); From c24e5b02159831f7aebd94b706eea3402b1a38fe Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 21:15:03 +1000 Subject: [PATCH 60/69] cleanup --- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 1879763cb..1093ff38f 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -6,15 +6,17 @@ public class Tests(ITestOutputHelper output) async Task VerifyGenerator(GeneratorDriverResults results) { - output.WriteLine($"First run of generators took: {results.FirstRun.TimingInfo.ElapsedTime}"); - output.WriteLine($"Cached re-run of generators took: {results.CachedRun.TimingInfo.ElapsedTime}"); + var first = results.FirstRun; + output.WriteLine($"First run of generators took: {first.TimingInfo.ElapsedTime}"); + var cached = results.CachedRun; + output.WriteLine($"Cached re-run of generators took: {cached.TimingInfo.ElapsedTime}"); - await Verify(results.FirstRun.RunResult.SelectGeneratedSources()); + await Verify(first.RunResult.SelectGeneratedSources()); // Ensure cachability var trackingNames = TrackingNames.GetTrackingNames(); - var trackedSteps1 = results.FirstRun.RunResult.GetTrackedSteps(trackingNames); - var trackedSteps2 = results.CachedRun.RunResult.GetTrackedSteps(trackingNames); + var trackedSteps1 = first.RunResult.GetTrackedSteps(trackingNames); + var trackedSteps2 = cached.RunResult.GetTrackedSteps(trackingNames); trackedSteps2.Keys.ShouldBe(trackedSteps1.Keys); foreach (var (key, steps1) in trackedSteps1) From 64f72fc06eaed3a1b10028760bd998193b4d3935 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 21:16:24 +1000 Subject: [PATCH 61/69] cleanup --- src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs index 60e3cd09a..e29ec7b24 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/TestDriver.cs @@ -4,7 +4,7 @@ public GeneratorDriverResults Run(string source) { var syntaxTree = CSharpSyntaxTree.ParseText(source); - IReadOnlyCollection references = + PortableExecutableReference[] references = [ MetadataReference.CreateFromFile(typeof(VerifyMSTest.UsesVerifyAttribute).Assembly.Location), MetadataReference.CreateFromFile(typeof(object).Assembly.Location) From 115f0c9217c2a2ba161c409ca3369ce67c28dded Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 21:22:28 +1000 Subject: [PATCH 62/69] cleanup --- src/Verify.MSTest.SourceGenerator/Parser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index a4469dd50..168923fb8 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -31,7 +31,7 @@ static bool HasMarkerAttributeOnBase(INamedTypeSymbol symbol) static bool HasMarkerAttribute(ISymbol symbol) => symbol .GetAttributes() - .Any(a => a.AttributeClass?.ToDisplayString() == MarkerAttributeName); + .Any(_ => _.AttributeClass?.ToDisplayString() == MarkerAttributeName); var parent = symbol.BaseType; From 36684cf1cefb35abbd9e27e075985e25a86aae2a Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 21:41:45 +1000 Subject: [PATCH 63/69] simplify string building --- src/Verify.MSTest.SourceGenerator/Emitter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index 0ce7d36ae..d72e18a29 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -19,7 +19,7 @@ static void WriteNamespace(IndentedStringBuilder builder, ClassToGenerate classT { if (classToGenerate.Namespace is not null) { - builder.Append("namespace ").AppendLine(classToGenerate.Namespace) + builder.AppendLine($"namespace {classToGenerate.Namespace}") .AppendLine("{") .IncreaseIndent(); } From ae272783929855267918ada7086f9b138a954c20 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 16 May 2024 21:44:14 +1000 Subject: [PATCH 64/69] simplify string building --- src/Verify.MSTest.SourceGenerator/Emitter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index d72e18a29..4b000ccc1 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -37,7 +37,7 @@ static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate clas { foreach (var parentClass in classToGenerate.ParentClasses) { - builder.Append("partial ").Append(parentClass.Keyword).Append(" ").AppendLine(parentClass.Name) + builder.AppendLine($"partial {parentClass.Keyword} {parentClass.Name}") .AppendLine("{"); builder.IncreaseIndent(); @@ -45,7 +45,7 @@ static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate clas WriteClass(builder, classToGenerate); - foreach (var parentClass in classToGenerate.ParentClasses) + foreach (var _ in classToGenerate.ParentClasses) { builder.DecreaseIndent() .AppendLine("}"); @@ -54,7 +54,7 @@ static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate clas static void WriteClass(IndentedStringBuilder builder, ClassToGenerate classToGenerate) => builder.AppendLine(GeneratedCodeAttribute) - .Append("partial class ").AppendLine(classToGenerate.ClassName) + .AppendLine($"partial class {classToGenerate.ClassName}") .AppendLine("{") .AppendLine(" public TestContext TestContext") .AppendLine(" {") From e421f5f4356930633e1cbcae8de8e03a821901e8 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Thu, 16 May 2024 09:48:30 -0700 Subject: [PATCH 65/69] Replace TrackingNames reflection with simple array --- src/Verify.MSTest.SourceGenerator.Tests/Tests.cs | 2 +- src/Verify.MSTest.SourceGenerator/TrackingNames.cs | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs index 1093ff38f..55c93bd30 100644 --- a/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs +++ b/src/Verify.MSTest.SourceGenerator.Tests/Tests.cs @@ -14,7 +14,7 @@ async Task VerifyGenerator(GeneratorDriverResults results) await Verify(first.RunResult.SelectGeneratedSources()); // Ensure cachability - var trackingNames = TrackingNames.GetTrackingNames(); + var trackingNames = TrackingNames.AllNames; var trackedSteps1 = first.RunResult.GetTrackedSteps(trackingNames); var trackedSteps2 = cached.RunResult.GetTrackedSteps(trackingNames); diff --git a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs index d70343c63..93d43916c 100644 --- a/src/Verify.MSTest.SourceGenerator/TrackingNames.cs +++ b/src/Verify.MSTest.SourceGenerator/TrackingNames.cs @@ -6,12 +6,6 @@ public static class TrackingNames public static string RemoveNulls => nameof(RemoveNulls); public static string Collect => nameof(Collect); - public static IReadOnlyCollection GetTrackingNames() => - typeof(TrackingNames) - .GetProperties() - .Where(_ => _.PropertyType == typeof(string)) - .Select(_ => _.GetValue(null)) - .OfType() - .Where(_ => !string.IsNullOrEmpty(_)) - .ToList(); + // Keep this list in-sync with the tracking name properties so that tests can verify cachability + public static IReadOnlyCollection AllNames { get; } = [InitialTransform, RemoveNulls, Collect]; } From bc274b123fec68a63879513e7052fe5d8c28b86e Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Thu, 16 May 2024 09:54:29 -0700 Subject: [PATCH 66/69] Partially revert "simplify string building" to reduce allocations --- src/Verify.MSTest.SourceGenerator/Emitter.cs | 6 +++--- .../IndentedStringBuilder.cs | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Emitter.cs b/src/Verify.MSTest.SourceGenerator/Emitter.cs index 4b000ccc1..8b32a552d 100644 --- a/src/Verify.MSTest.SourceGenerator/Emitter.cs +++ b/src/Verify.MSTest.SourceGenerator/Emitter.cs @@ -19,7 +19,7 @@ static void WriteNamespace(IndentedStringBuilder builder, ClassToGenerate classT { if (classToGenerate.Namespace is not null) { - builder.AppendLine($"namespace {classToGenerate.Namespace}") + builder.Append("namespace ").AppendLine(classToGenerate.Namespace) .AppendLine("{") .IncreaseIndent(); } @@ -37,7 +37,7 @@ static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate clas { foreach (var parentClass in classToGenerate.ParentClasses) { - builder.AppendLine($"partial {parentClass.Keyword} {parentClass.Name}") + builder.Append("partial ").Append(parentClass.Keyword).Append(" ").AppendLine(parentClass.Name) .AppendLine("{"); builder.IncreaseIndent(); @@ -54,7 +54,7 @@ static void WriteParentTypes(IndentedStringBuilder builder, ClassToGenerate clas static void WriteClass(IndentedStringBuilder builder, ClassToGenerate classToGenerate) => builder.AppendLine(GeneratedCodeAttribute) - .AppendLine($"partial class {classToGenerate.ClassName}") + .Append("partial class ").AppendLine(classToGenerate.ClassName) .AppendLine("{") .AppendLine(" public TestContext TestContext") .AppendLine(" {") diff --git a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs index 43453ec15..85a67bdde 100644 --- a/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs +++ b/src/Verify.MSTest.SourceGenerator/IndentedStringBuilder.cs @@ -1,7 +1,17 @@ +/// +/// A wrapper that automatically adds indentation to the beginning of +/// every line. +/// +/// The initial capacity of the underlying StringBuilder. +/// +/// Source generators must target netstandard2.0, thus we can't use any newer features related to +/// interpolated string handling without incurring additional allocations for the formatted strings. +/// +/// As a result, all methods are chainable to make building up lines easier. +/// class IndentedStringBuilder(int capacity = IndentedStringBuilder.DefaultStringBuilderCapacity) { - // Default capacity based on the closest power of 2 to what's used in our own tests. - // This may need to be tweaked over time. + // Default capacity based on the closest power of 2 to what's used in our own tests (this value may need to be tweaked over time). const int DefaultStringBuilderCapacity = 4096; readonly StringBuilder builder = new(capacity); int indentLevel; From 155dc19989caffdba2678fd8632ab685b845311b Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Thu, 16 May 2024 10:38:35 -0700 Subject: [PATCH 67/69] Remove namespace check when serarching for TestContext property We can't overload on return type anyways, so no point in checking the type name of the property. --- src/Verify.MSTest.SourceGenerator/Parser.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Verify.MSTest.SourceGenerator/Parser.cs b/src/Verify.MSTest.SourceGenerator/Parser.cs index 168923fb8..e27f4eec9 100644 --- a/src/Verify.MSTest.SourceGenerator/Parser.cs +++ b/src/Verify.MSTest.SourceGenerator/Parser.cs @@ -54,8 +54,7 @@ static bool HasTestContextProperty(INamedTypeSymbol symbol) => symbol .GetMembers() .OfType() - .Any(_ => _.Name == "TestContext" && - _.Type.Name == "TestContext"); + .Any(_ => _.Name == "TestContext"); var parent = symbol.BaseType; From 225f1e828ff49752489e484f8033314edc68a77a Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Thu, 16 May 2024 10:56:50 -0700 Subject: [PATCH 68/69] Move TestContext reflection to helper class --- src/Verify.MSTest/TestContextReflector.cs | 129 ++++++++++++++++++++++ src/Verify.MSTest/Verifier.cs | 125 +-------------------- 2 files changed, 133 insertions(+), 121 deletions(-) create mode 100644 src/Verify.MSTest/TestContextReflector.cs diff --git a/src/Verify.MSTest/TestContextReflector.cs b/src/Verify.MSTest/TestContextReflector.cs new file mode 100644 index 000000000..3e1b49530 --- /dev/null +++ b/src/Verify.MSTest/TestContextReflector.cs @@ -0,0 +1,129 @@ +namespace VerifyMSTest; + +static class TestContextReflector +{ + const BindingFlags ReflectionBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + static readonly ConcurrentDictionary TypeCache = new(); + + public static (Assembly Assembly, Type Type, MethodInfo Method) Get(TestContext context) + { + var testName = context.TestName; + var typeName = context.FullyQualifiedTestClassName!; // Checked for null in the Verifier + + if (!TryGetTypeFromTestContext(typeName, context, out var type)) + { + type = FindType(typeName); + } + + var testNameSpan = testName.AsSpan(); + var indexOf = testNameSpan.IndexOf('('); + if (indexOf > 0) + { + testNameSpan = testNameSpan[..indexOf]; + } + + indexOf = testNameSpan.IndexOf('.'); + if (indexOf > 0) + { + testNameSpan = testNameSpan[(indexOf + 1)..]; + } + + var method = FindMethod(type, testNameSpan); + + return (type.Assembly, type, method); + } + + /// + /// As an optimization, try to retrieve the stored on the + /// , and use that to retrieve the . + /// + /// If reflection fails, return false. + /// + /// The fully qualified name of the class of the currently running test. + /// The of the current test. + /// The of the currently running test. + /// true if the reflection succeeded; false otherwise. + static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, [NotNullWhen(true)] out Type? type) + { + try + { + // We can't use UnsafeAccessor in this context because _testMethod is of type + // Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod, which isn't part of the MSTest.TestFramework + // package. If / when an API like https://github.com/dotnet/runtime/issues/90081 lands this generic reflection can be replaced. + var testMethod = testContext + .GetType() + .GetField("_testMethod", ReflectionBindingFlags)?.GetValue(testContext); + var assemblyPath = testMethod + ?.GetType() + ?.GetProperty("AssemblyName", ReflectionBindingFlags) + ?.GetValue(testMethod); + var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath as string ?? string.Empty); + + type = Type.GetType($"{typeName}, {assemblyName}", throwOnError: false); + + if (type is not null) + { + return true; + } + } + catch + { + } + + type = null; + return false; + } + + /// + /// Get the of the test class from the fully qualified name. + /// + /// The fully qualified class name of the currently running test. + /// The for the currently running test class. + /// + /// Uses a to avoid repeated lookups. + /// This method should only be used as a fallback if reflection fails because: + /// 1. It's slower + /// 2. The type cache can grow large for large test suites + /// + static Type FindType(string typeName) + { + var result = TypeCache.GetOrAdd(typeName, name => + { + var nameSpan = name.AsSpan(); + var types = AppDomain.CurrentDomain.GetAssemblies() + .Reverse() + .SelectMany(assembly => assembly.GetTypes()); + + foreach (var type in types) + { + if (nameSpan.SequenceEqual(type.FullName)) + { + return type; + } + } + + return null; + }); + + if (result is null) + { + throw new($"Type '{typeName}' from TestContext not found."); + } + + return result; + } + + static MethodInfo FindMethod(Type type, ReadOnlySpan testName) + { + foreach (var method in type + .GetMethods(BindingFlags.Instance | BindingFlags.Public)) + { + if (testName.SequenceEqual(method.Name)) + { + return method; + } + } + + throw new($"Could not find method `{type.Name}.{testName.ToString()}`."); + } +} diff --git a/src/Verify.MSTest/Verifier.cs b/src/Verify.MSTest/Verifier.cs index 427bec343..10e1f85b9 100644 --- a/src/Verify.MSTest/Verifier.cs +++ b/src/Verify.MSTest/Verifier.cs @@ -2,7 +2,6 @@ namespace VerifyMSTest; public static partial class Verifier { - static ConcurrentDictionary typeCache = new(); const string AttributeUsageHelp = "Ensure test class has a `[UsesVerify]` attribute."; static Task AddFile(FilePair path, bool autoVerify) @@ -37,39 +36,18 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b throw new($"TestContext is null. {AttributeUsageHelp}"); } - var testName = CurrentTestContext.Value.TestName; - if (testName is null) + if (CurrentTestContext.Value.TestName is null) { throw new($"TestContext.TestName is null. {AttributeUsageHelp}"); } - var typeName = CurrentTestContext.Value.FullyQualifiedTestClassName; - if (typeName is null) + if (CurrentTestContext.Value.FullyQualifiedTestClassName is null) { throw new($"TestContext.FullyQualifiedTestClassName is null. {AttributeUsageHelp}"); } - if(!TryGetTypeFromTestContext(typeName, CurrentTestContext.Value, out var type)) - { - type = FindType(typeName); - } - - var testNameSpan = testName.AsSpan(); - var indexOf = testNameSpan.IndexOf('('); - if (indexOf > 0) - { - testNameSpan = testNameSpan[..indexOf]; - } - - indexOf = testNameSpan.IndexOf('.'); - if (indexOf > 0) - { - testNameSpan = testNameSpan[(indexOf + 1)..]; - } - - VerifierSettings.AssignTargetAssembly(type.Assembly); - var method = FindMethod(type, testNameSpan); - + (var assembly, var type, var method) = TestContextReflector.Get(CurrentTestContext.Value); + VerifierSettings.AssignTargetAssembly(assembly); var pathInfo = GetPathInfo(sourceFile, type, method); return new( sourceFile, @@ -80,101 +58,6 @@ static InnerVerifier BuildVerifier(VerifySettings settings, string sourceFile, b pathInfo); } - /// - /// As an optimization, try to retrieve the stored on the - /// , and use that to retrieve the . - /// - /// If reflection fails, return false. - /// - /// The fully qualified name of the class of the currently running test. - /// The of the current test. - /// The of the currently running test. - /// true if the reflection succeeded; false otherwise. - static bool TryGetTypeFromTestContext(string typeName, TestContext testContext, [NotNullWhen(true)] out Type? type) - { - try - { - // We can't use UnsafeAccessor in this context because _testMethod is of type - // Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod, which isn't part of the MSTest.TestFramework - // package. If / when an API like https://github.com/dotnet/runtime/issues/90081 lands this generic reflection can be replaced. - var testMethod = testContext - .GetType() - .GetField("_testMethod", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - ?.GetValue(testContext); - var assemblyPath = testMethod - ?.GetType() - ?.GetProperty("AssemblyName", BindingFlags.Instance | BindingFlags.Public) - ?.GetValue(testMethod); - var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath as string ?? string.Empty); - - type = Type.GetType($"{typeName}, {assemblyName}", throwOnError: false); - - if (type is not null) - { - return true; - } - } - catch - { - } - - type = null; - return false; - } - - /// - /// Get the of the test class from the fully qualified name. - /// - /// The fully qualified class name of the currently running test. - /// The for the currently running test class. - /// - /// Uses a to avoid repeated lookups. - /// This method should only be used as a fallback if reflection fails because: - /// 1. It's slower - /// 2. The type cache can grow large for large test suites - /// - static Type FindType(string typeName) - { - var result = typeCache.GetOrAdd(typeName, name => - { - var nameSpan = name.AsSpan(); - var types = AppDomain.CurrentDomain.GetAssemblies() - .Reverse() - .SelectMany(assembly => assembly.GetTypes()); - - foreach (var type in types) - { - if (nameSpan.SequenceEqual(type.FullName)) - { - return type; - } - } - - return null; - }); - - if (result is null) - { - throw new($"Type '{typeName}' from TestContext not found."); - } - - return result; - } - - static MethodInfo FindMethod(Type type, ReadOnlySpan testName) - { - foreach (var method in type - .GetMethods(BindingFlags.Instance | BindingFlags.Public)) - { - if (testName.SequenceEqual(method.Name)) - { - return method; - } - } - - throw new($"Could not find method `{type.Name}.{testName.ToString()}`."); - } - [Pure] public static SettingsTask Verify( object? target, From 27bca2cb85c96f8acc2b51642269f199d3af6fec Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Thu, 16 May 2024 14:53:24 -0700 Subject: [PATCH 69/69] Remove HashCode and EquatableArray classes and implement equals directly --- .../ClassToGenerate.cs | 38 +- .../EquatableArray.cs | 106 ----- src/Verify.MSTest.SourceGenerator/HashCode.cs | 386 ------------------ 3 files changed, 36 insertions(+), 494 deletions(-) delete mode 100644 src/Verify.MSTest.SourceGenerator/EquatableArray.cs delete mode 100644 src/Verify.MSTest.SourceGenerator/HashCode.cs diff --git a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs index 0f887490a..d0e7d95c6 100644 --- a/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs +++ b/src/Verify.MSTest.SourceGenerator/ClassToGenerate.cs @@ -1,13 +1,47 @@ +/// +/// The data model for the class that is annotated with [UsesVerify] and needs to be generated. +/// +/// +/// For incremental source generator caching to work properly, this class must _fully_ implement +/// value (not reference) equality. +/// +/// The built in equality and hash code implementations won't work because this type includes a +/// collection (which has reference equality semantics), so we must implement them ouselves. +/// readonly record struct ClassToGenerate { public string? Namespace { get; } public string ClassName { get; } - public EquatableArray ParentClasses { get; } + public ParentClass[] ParentClasses { get; } public ClassToGenerate(string? @namespace, string className, ParentClass[] parentClasses) { Namespace = @namespace; ClassName = className; - ParentClasses = new(parentClasses); + ParentClasses = parentClasses; + } + + public readonly bool Equals(ClassToGenerate other) => + Namespace == other.Namespace && + ClassName == other.ClassName && + ParentClasses.SequenceEqual(other.ParentClasses); + + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + var hash = 1430287; + hash = hash * 7302013 ^ (Namespace ?? string.Empty).GetHashCode(); + hash = hash * 7302013 ^ ClassName.GetHashCode(); + + // Include (up to) the last 8 elements in the hash code to balance performance and specificity. + // The runtime also does this for structural equality; see + // https://github.com/dotnet/runtime/blob/2c39e052327302cafaea652e4b29dd6855e9572a/src/libraries/System.Private.CoreLib/src/System/Array.cs#L755-L768. + for (var i = (ParentClasses.Length >= 8 ? ParentClasses.Length - 8 : 0); i < ParentClasses.Length; i++) + { + hash = hash * 7302013 ^ ParentClasses[i].GetHashCode(); + } + return hash; + } } } diff --git a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs b/src/Verify.MSTest.SourceGenerator/EquatableArray.cs deleted file mode 100644 index d204e65eb..000000000 --- a/src/Verify.MSTest.SourceGenerator/EquatableArray.cs +++ /dev/null @@ -1,106 +0,0 @@ -// ReSharper disable ArrangeMethodOrOperatorBody -// ReSharper disable ArrangeConstructorOrDestructorBody -// ReSharper disable UseCollectionExpression -// Suppressing style warnings to keep code aligned with upstream version from -// https://github.com/andrewlock/StronglyTypedId/blob/e5df78d0aa72f2232f423938c0d98d9bf4517092/src/StronglyTypedIds/EquatableArray.cs -#pragma warning disable IDE1006 // Naming rule violation Prefix is not expected -#pragma warning disable IDE0021 // Use expression body for constructor -#pragma warning disable IDE0022 // Use expression body for method -#pragma warning disable IDE0024 // Use expression body for operator - -/// -/// An immutable, equatable array. This is equivalent to but with value equality support. -/// -/// The type of values in the array. -public readonly struct EquatableArray : IEquatable>, IEnumerable - where T : IEquatable -{ - /// - /// The underlying array. - /// - private readonly T[]? _array; - - /// - /// Creates a new instance. - /// - /// The input to wrap. - public EquatableArray(T[] array) - { - _array = array; - } - - /// - public bool Equals(EquatableArray array) - { - return AsSpan().SequenceEqual(array.AsSpan()); - } - - /// - public override bool Equals(object? obj) - { - return obj is EquatableArray array && Equals(this, array); - } - - /// - public override int GetHashCode() - { - if (_array is not T[] array) - { - return 0; - } - - HashCode hashCode = default; - - foreach (var item in array) - { - hashCode.Add(item); - } - - return hashCode.ToHashCode(); - } - - /// - /// Returns a wrapping the current items. - /// - /// A wrapping the current items. - public ReadOnlySpan AsSpan() - { - return _array.AsSpan(); - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)(_array ?? Array.Empty())).GetEnumerator(); - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)(_array ?? Array.Empty())).GetEnumerator(); - } - - public int Count => _array?.Length ?? 0; - - /// - /// Checks whether two values are the same. - /// - /// The first value. - /// The second value. - /// Whether and are equal. - public static bool operator ==(EquatableArray left, EquatableArray right) - { - return left.Equals(right); - } - - /// - /// Checks whether two values are not the same. - /// - /// The first value. - /// The second value. - /// Whether and are not equal. - public static bool operator !=(EquatableArray left, EquatableArray right) - { - return !left.Equals(right); - } -} \ No newline at end of file diff --git a/src/Verify.MSTest.SourceGenerator/HashCode.cs b/src/Verify.MSTest.SourceGenerator/HashCode.cs deleted file mode 100644 index 3840a13f8..000000000 --- a/src/Verify.MSTest.SourceGenerator/HashCode.cs +++ /dev/null @@ -1,386 +0,0 @@ -// ReSharper disable SuggestVarOrType_BuiltInTypes -// ReSharper disable ArrangeMethodOrOperatorBody -// ReSharper disable ArrangeRedundantParentheses - -// A local version of System.HashCode to avoid complications from NuGet private dependencies. -// See https://github.com/dotnet/aspnetcore/commit/d074523ba3f477501cf2a113f1d64e9a21627e53. - -// Suppressing style warnings to keep code aligned with upstream version from -// https://github.com/andrewlock/NetEscapades.EnumGenerators/blob/cd3c9278b0ea3827f7e085c19e998ad0671a11ab/src/NetEscapades.EnumGenerators/HashCode.cs -#pragma warning disable IDE0007 // Use var instead of explicit type -#pragma warning disable IDE1006 // Naming rule violation Prefix is not expected -#pragma warning disable IDE0022 // Use expression body for method - -struct HashCode -{ - private static readonly uint s_seed = GenerateGlobalSeed(); - - private const uint Prime1 = 2654435761U; - private const uint Prime2 = 2246822519U; - private const uint Prime3 = 3266489917U; - private const uint Prime4 = 668265263U; - private const uint Prime5 = 374761393U; - - private uint _v1, _v2, _v3, _v4; - private uint _queue1, _queue2, _queue3; - private uint _length; - - private static uint GenerateGlobalSeed() - { - var buffer = new byte[sizeof(uint)]; - new Random().NextBytes(buffer); - return BitConverter.ToUInt32(buffer, 0); - } - - public static int Combine(T1 value1) - { - // Provide a way of diffusing bits from something with a limited - // input hash space. For example, many enums only have a few - // possible hashes, only using the bottom few bits of the code. Some - // collections are built on the assumption that hashes are spread - // over a larger space, so diffusing the bits may help the - // collection work more efficiently. - - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 4; - - hash = QueueRound(hash, hc1); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2) - { - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - uint hc2 = (uint)(value2?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 8; - - hash = QueueRound(hash, hc1); - hash = QueueRound(hash, hc2); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3) - { - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - uint hc2 = (uint)(value2?.GetHashCode() ?? 0); - uint hc3 = (uint)(value3?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 12; - - hash = QueueRound(hash, hc1); - hash = QueueRound(hash, hc2); - hash = QueueRound(hash, hc3); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) - { - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - uint hc2 = (uint)(value2?.GetHashCode() ?? 0); - uint hc3 = (uint)(value3?.GetHashCode() ?? 0); - uint hc4 = (uint)(value4?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 16; - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) - { - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - uint hc2 = (uint)(value2?.GetHashCode() ?? 0); - uint hc3 = (uint)(value3?.GetHashCode() ?? 0); - uint hc4 = (uint)(value4?.GetHashCode() ?? 0); - uint hc5 = (uint)(value5?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 20; - - hash = QueueRound(hash, hc5); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) - { - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - uint hc2 = (uint)(value2?.GetHashCode() ?? 0); - uint hc3 = (uint)(value3?.GetHashCode() ?? 0); - uint hc4 = (uint)(value4?.GetHashCode() ?? 0); - uint hc5 = (uint)(value5?.GetHashCode() ?? 0); - uint hc6 = (uint)(value6?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 24; - - hash = QueueRound(hash, hc5); - hash = QueueRound(hash, hc6); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) - { - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - uint hc2 = (uint)(value2?.GetHashCode() ?? 0); - uint hc3 = (uint)(value3?.GetHashCode() ?? 0); - uint hc4 = (uint)(value4?.GetHashCode() ?? 0); - uint hc5 = (uint)(value5?.GetHashCode() ?? 0); - uint hc6 = (uint)(value6?.GetHashCode() ?? 0); - uint hc7 = (uint)(value7?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 28; - - hash = QueueRound(hash, hc5); - hash = QueueRound(hash, hc6); - hash = QueueRound(hash, hc7); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) - { - uint hc1 = (uint)(value1?.GetHashCode() ?? 0); - uint hc2 = (uint)(value2?.GetHashCode() ?? 0); - uint hc3 = (uint)(value3?.GetHashCode() ?? 0); - uint hc4 = (uint)(value4?.GetHashCode() ?? 0); - uint hc5 = (uint)(value5?.GetHashCode() ?? 0); - uint hc6 = (uint)(value6?.GetHashCode() ?? 0); - uint hc7 = (uint)(value7?.GetHashCode() ?? 0); - uint hc8 = (uint)(value8?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - v1 = Round(v1, hc5); - v2 = Round(v2, hc6); - v3 = Round(v3, hc7); - v4 = Round(v4, hc8); - - uint hash = MixState(v1, v2, v3, v4); - hash += 32; - - hash = MixFinal(hash); - return (int)hash; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) - { - v1 = s_seed + Prime1 + Prime2; - v2 = s_seed + Prime2; - v3 = s_seed; - v4 = s_seed - Prime1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Round(uint hash, uint input) - { - return RotateLeft(hash + input * Prime2, 13) * Prime1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint QueueRound(uint hash, uint queuedValue) - { - return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint MixState(uint v1, uint v2, uint v3, uint v4) - { - return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18); - } - - private static uint MixEmptyState() - { - return s_seed + Prime5; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint MixFinal(uint hash) - { - hash ^= hash >> 15; - hash *= Prime2; - hash ^= hash >> 13; - hash *= Prime3; - hash ^= hash >> 16; - return hash; - } - - public void Add(T value) - { - Add(value?.GetHashCode() ?? 0); - } - - public void Add(T value, IEqualityComparer? comparer) - { - Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode())); - } - - private void Add(int value) - { - // The original xxHash works as follows: - // 0. Initialize immediately. We can't do this in a struct (no - // default ctor). - // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. - // 2. Accumulate remaining blocks of length 4 (1 uint) into the - // hash. - // 3. Accumulate remaining blocks of length 1 into the hash. - - // There is no need for #3 as this type only accepts ints. _queue1, - // _queue2 and _queue3 are basically a buffer so that when - // ToHashCode is called we can execute #2 correctly. - - // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see - // #0) nd the last place that can be done if you look at the - // original code is just before the first block of 16 bytes is mixed - // in. The xxHash32 state is never used for streams containing fewer - // than 16 bytes. - - // To see what's really going on here, have a look at the Combine - // methods. - - uint val = (uint)value; - - // Storing the value of _length locally shaves off quite a few bytes - // in the resulting machine code. - uint previousLength = _length++; - uint position = previousLength % 4; - - // Switch can't be inlined. - - if (position == 0) - _queue1 = val; - else if (position == 1) - _queue2 = val; - else if (position == 2) - _queue3 = val; - else // position == 3 - { - if (previousLength == 3) - Initialize(out _v1, out _v2, out _v3, out _v4); - - _v1 = Round(_v1, _queue1); - _v2 = Round(_v2, _queue2); - _v3 = Round(_v3, _queue3); - _v4 = Round(_v4, val); - } - } - - public int ToHashCode() - { - // Storing the value of _length locally shaves off quite a few bytes - // in the resulting machine code. - uint length = _length; - - // position refers to the *next* queue position in this method, so - // position == 1 means that _queue1 is populated; _queue2 would have - // been populated on the next call to Add. - uint position = length % 4; - - // If the length is less than 4, _v1 to _v4 don't contain anything - // yet. xxHash32 treats this differently. - - uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); - - // _length is incremented once per Add(Int32) and is therefore 4 - // times too small (xxHash length is in bytes, not ints). - - hash += length * 4; - - // Mix what remains in the queue - - // Switch can't be inlined right now, so use as few branches as - // possible by manually excluding impossible scenarios (position > 1 - // is always false if position is not > 0). - if (position > 0) - { - hash = QueueRound(hash, _queue1); - if (position > 1) - { - hash = QueueRound(hash, _queue2); - if (position > 2) - hash = QueueRound(hash, _queue3); - } - } - - hash = MixFinal(hash); - return (int)hash; - } - -#pragma warning disable 0809 - // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. - // Disallowing GetHashCode and Equals is by design - - // * We decided to not override GetHashCode() to produce the hash code - // as this would be weird, both naming-wise as well as from a - // behavioral standpoint (GetHashCode() should return the object's - // hash code, not the one being computed). - - // * Even though ToHashCode() can be called safely multiple times on - // this implementation, it is not part of the contract. If the - // implementation has to change in the future we don't want to worry - // about people who might have incorrectly used this type. - - [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => throw new NotSupportedException("Hash code not supported"); - - [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) => throw new NotSupportedException("Equality not supported"); -#pragma warning restore 0809 - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint RotateLeft(uint value, int offset) - => (value << offset) | (value >> (32 - offset)); -} \ No newline at end of file