diff --git a/docs/mdsource/naming.source.md b/docs/mdsource/naming.source.md index b8b46135a4..43cd3fc85d 100644 --- a/docs/mdsource/naming.source.md +++ b/docs/mdsource/naming.source.md @@ -249,6 +249,14 @@ For example to place all `.verified.` files in a `{ProjectDirectory}\Snapshots` Verifier.UseProjectRelativeDirectory("Snapshots"); ``` +To nest the `.verified.` files in sub-directories that mirror the directory structure of the test source files (relative to the project directory), enable `mirrorSourceStructure`: + +``` +Verifier.UseProjectRelativeDirectory("Snapshots", mirrorSourceStructure: true); +``` + +For example a test in `{ProjectDirectory}\Foo\FooTests.cs` would then have its `.verified.` files placed in `{ProjectDirectory}\Snapshots\Foo`. This can make orphaned snapshots easier to identify when test source files are moved, renamed, or removed. + ### UseSourceFileRelativeDirectory diff --git a/docs/naming.md b/docs/naming.md index b62e2f443d..92313bfc1d 100644 --- a/docs/naming.md +++ b/docs/naming.md @@ -805,6 +805,14 @@ For example to place all `.verified.` files in a `{ProjectDirectory}\Snapshots` Verifier.UseProjectRelativeDirectory("Snapshots"); ``` +To nest the `.verified.` files in sub-directories that mirror the directory structure of the test source files (relative to the project directory), enable `mirrorSourceStructure`: + +``` +Verifier.UseProjectRelativeDirectory("Snapshots", mirrorSourceStructure: true); +``` + +For example a test in `{ProjectDirectory}\Foo\FooTests.cs` would then have its `.verified.` files placed in `{ProjectDirectory}\Snapshots\Foo`. This can make orphaned snapshots easier to identify when test source files are moved, renamed, or removed. + ### UseSourceFileRelativeDirectory diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 9339a0e4c4..ce2860ddef 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ CA1822;CS1591;CS0649;xUnit1026;xUnit1013;CS1573;VerifyTestsProjectDir;VerifySetParameters;PolyFillTargetsForNuget;xUnit1051;NU1608;NU1109 - 31.24.3 + 31.25.0 enable preview 1.0.0 diff --git a/src/Verify.Expecto/DerivePaths/Verifier.cs b/src/Verify.Expecto/DerivePaths/Verifier.cs index ad531f804c..937c3478aa 100644 --- a/src/Verify.Expecto/DerivePaths/Verifier.cs +++ b/src/Verify.Expecto/DerivePaths/Verifier.cs @@ -27,12 +27,22 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) /// /// Use a directory relative to the project directory for storing for `.verified.` files. /// + [Obsolete("Use the overload that accepts mirrorSourceStructure.")] public static void UseProjectRelativeDirectory(string directory) => + UseProjectRelativeDirectory(directory, false); + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + /// The project relative directory to store `.verified.` files in. + /// + /// If true, nests `.verified.` files in sub-directories that mirror the directory structure of the test source files relative to the project directory. + /// + [OverloadResolutionPriority(1)] + public static void UseProjectRelativeDirectory(string directory, bool mirrorSourceStructure = false) => DerivePathInfo( - (sourceFile, projectDirectory, typeName, methodName) => new( - directory: Path.Combine(projectDirectory, directory), - typeName: typeName, - methodName: methodName)); + (sourceFile, projectDirectory, typeName, methodName) => + PathInfo.DeriveProjectRelative(directory, mirrorSourceStructure, sourceFile, projectDirectory, typeName, methodName)); /// /// Use a directory relative to the source file directory for storing for `.verified.` files. diff --git a/src/Verify.Fixie/DerivePaths/Verifier.cs b/src/Verify.Fixie/DerivePaths/Verifier.cs index ccef8cd7ae..e0cd1d0df6 100644 --- a/src/Verify.Fixie/DerivePaths/Verifier.cs +++ b/src/Verify.Fixie/DerivePaths/Verifier.cs @@ -27,12 +27,22 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) /// /// Use a directory relative to the project directory for storing for `.verified.` files. /// + [Obsolete("Use the overload that accepts mirrorSourceStructure.")] public static void UseProjectRelativeDirectory(string directory) => + UseProjectRelativeDirectory(directory, false); + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + /// The project relative directory to store `.verified.` files in. + /// + /// If true, nests `.verified.` files in sub-directories that mirror the directory structure of the test source files relative to the project directory. + /// + [OverloadResolutionPriority(1)] + public static void UseProjectRelativeDirectory(string directory, bool mirrorSourceStructure = false) => DerivePathInfo( - (sourceFile, projectDirectory, type, method) => new( - directory: Path.Combine(projectDirectory, directory), - typeName: type.NameWithParent(), - methodName: method.Name)); + (sourceFile, projectDirectory, type, method) => + PathInfo.DeriveProjectRelative(directory, mirrorSourceStructure, sourceFile, projectDirectory, type.NameWithParent(), method.Name)); /// /// Use a directory relative to the project directory for storing for `.verified.` files. diff --git a/src/Verify.MSTest/DerivePaths/Verifier.cs b/src/Verify.MSTest/DerivePaths/Verifier.cs index 03b82adc2b..ccc13a0dbd 100644 --- a/src/Verify.MSTest/DerivePaths/Verifier.cs +++ b/src/Verify.MSTest/DerivePaths/Verifier.cs @@ -27,12 +27,22 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) /// /// Use a directory relative to the project directory for storing for `.verified.` files. /// + [Obsolete("Use the overload that accepts mirrorSourceStructure.")] public static void UseProjectRelativeDirectory(string directory) => + UseProjectRelativeDirectory(directory, false); + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + /// The project relative directory to store `.verified.` files in. + /// + /// If true, nests `.verified.` files in sub-directories that mirror the directory structure of the test source files relative to the project directory. + /// + [OverloadResolutionPriority(1)] + public static void UseProjectRelativeDirectory(string directory, bool mirrorSourceStructure = false) => DerivePathInfo( - (sourceFile, projectDirectory, type, method) => new( - directory: Path.Combine(projectDirectory, directory), - typeName: type.NameWithParent(), - methodName: method.Name)); + (sourceFile, projectDirectory, type, method) => + PathInfo.DeriveProjectRelative(directory, mirrorSourceStructure, sourceFile, projectDirectory, type.NameWithParent(), method.Name)); /// /// Use a directory relative to the source file directory for storing for `.verified.` files. diff --git a/src/Verify.MSTest/DerivePaths/VerifyBase.cs b/src/Verify.MSTest/DerivePaths/VerifyBase.cs index efb16c6bbb..9bbe0c0559 100644 --- a/src/Verify.MSTest/DerivePaths/VerifyBase.cs +++ b/src/Verify.MSTest/DerivePaths/VerifyBase.cs @@ -19,8 +19,20 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) => /// /// Use a directory relative to the project directory for storing for `.verified.` files. /// + [Obsolete("Use the overload that accepts mirrorSourceStructure.")] public static void UseProjectRelativeDirectory(string directory) => - Verifier.UseProjectRelativeDirectory(directory); + Verifier.UseProjectRelativeDirectory(directory, false); + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + /// The project relative directory to store `.verified.` files in. + /// + /// If true, nests `.verified.` files in sub-directories that mirror the directory structure of the test source files relative to the project directory. + /// + [OverloadResolutionPriority(1)] + public static void UseProjectRelativeDirectory(string directory, bool mirrorSourceStructure = false) => + Verifier.UseProjectRelativeDirectory(directory, mirrorSourceStructure); /// /// Use a directory relative to the source file directory for storing for `.verified.` files. diff --git a/src/Verify.NUnit/DerivePaths/Verifier.cs b/src/Verify.NUnit/DerivePaths/Verifier.cs index 10ce1ee329..288ab70a48 100644 --- a/src/Verify.NUnit/DerivePaths/Verifier.cs +++ b/src/Verify.NUnit/DerivePaths/Verifier.cs @@ -27,12 +27,22 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) /// /// Use a directory relative to the project directory for storing for `.verified.` files. /// + [Obsolete("Use the overload that accepts mirrorSourceStructure.")] public static void UseProjectRelativeDirectory(string directory) => + UseProjectRelativeDirectory(directory, false); + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + /// The project relative directory to store `.verified.` files in. + /// + /// If true, nests `.verified.` files in sub-directories that mirror the directory structure of the test source files relative to the project directory. + /// + [OverloadResolutionPriority(1)] + public static void UseProjectRelativeDirectory(string directory, bool mirrorSourceStructure = false) => DerivePathInfo( - (sourceFile, projectDirectory, type, method) => new( - directory: Path.Combine(projectDirectory, directory), - typeName: type.NameWithParent(), - methodName: method.Name)); + (sourceFile, projectDirectory, type, method) => + PathInfo.DeriveProjectRelative(directory, mirrorSourceStructure, sourceFile, projectDirectory, type.NameWithParent(), method.Name)); /// /// Use a directory relative to the source file directory for storing for `.verified.` files. diff --git a/src/Verify.TUnit/DerivePaths/Verifier.cs b/src/Verify.TUnit/DerivePaths/Verifier.cs index 70ac68e90a..1b002cce00 100644 --- a/src/Verify.TUnit/DerivePaths/Verifier.cs +++ b/src/Verify.TUnit/DerivePaths/Verifier.cs @@ -27,12 +27,22 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) /// /// Use a directory relative to the project directory for storing for `.verified.` files. /// + [Obsolete("Use the overload that accepts mirrorSourceStructure.")] public static void UseProjectRelativeDirectory(string directory) => + UseProjectRelativeDirectory(directory, false); + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + /// The project relative directory to store `.verified.` files in. + /// + /// If true, nests `.verified.` files in sub-directories that mirror the directory structure of the test source files relative to the project directory. + /// + [OverloadResolutionPriority(1)] + public static void UseProjectRelativeDirectory(string directory, bool mirrorSourceStructure = false) => DerivePathInfo( - (sourceFile, projectDirectory, type, method) => new( - directory: Path.Combine(projectDirectory, directory), - typeName: type.NameWithParent(), - methodName: method.Name)); + (sourceFile, projectDirectory, type, method) => + PathInfo.DeriveProjectRelative(directory, mirrorSourceStructure, sourceFile, projectDirectory, type.NameWithParent(), method.Name)); /// /// Use a directory relative to the source file directory for storing for `.verified.` files. diff --git a/src/Verify.Tests/DeriveProjectRelativeDirectoryTests.cs b/src/Verify.Tests/DeriveProjectRelativeDirectoryTests.cs new file mode 100644 index 0000000000..26d794cb9f --- /dev/null +++ b/src/Verify.Tests/DeriveProjectRelativeDirectoryTests.cs @@ -0,0 +1,42 @@ +public class DeriveProjectRelativeDirectoryTests +{ + static readonly string projectDirectory = Path.Combine(Path.GetTempPath(), "project"); + + static string? Derive(string sourceFile, bool mirrorSourceStructure) => + PathInfo.DeriveProjectRelative( + "Snapshots", + mirrorSourceStructure, + sourceFile, + projectDirectory, + "TheType", + "TheMethod") + .Directory; + + [Fact] + public void NotMirroredUsesRoot() + { + var source = Path.Combine(projectDirectory, "Foo", "MyTests.cs"); + Assert.Equal(Path.Combine(projectDirectory, "Snapshots"), Derive(source, false)); + } + + [Fact] + public void MirrorsNestedSourceStructure() + { + var source = Path.Combine(projectDirectory, "Foo", "Bar", "MyTests.cs"); + Assert.Equal(Path.Combine(projectDirectory, "Snapshots", "Foo", "Bar"), Derive(source, true)); + } + + [Fact] + public void MirrorAtProjectRootUsesRoot() + { + var source = Path.Combine(projectDirectory, "MyTests.cs"); + Assert.Equal(Path.Combine(projectDirectory, "Snapshots"), Derive(source, true)); + } + + [Fact] + public void MirrorSourceOutsideProjectUsesRoot() + { + var source = Path.Combine(Path.GetTempPath(), "other", "MyTests.cs"); + Assert.Equal(Path.Combine(projectDirectory, "Snapshots"), Derive(source, true)); + } +} diff --git a/src/Verify.XunitV3.DerivePaths.Tests/Relative/SubDir/Tests.ProjectRelativeDirectoryMirrored.verified.txt b/src/Verify.XunitV3.DerivePaths.Tests/Relative/SubDir/Tests.ProjectRelativeDirectoryMirrored.verified.txt new file mode 100644 index 0000000000..3ee291f7bd --- /dev/null +++ b/src/Verify.XunitV3.DerivePaths.Tests/Relative/SubDir/Tests.ProjectRelativeDirectoryMirrored.verified.txt @@ -0,0 +1 @@ +Value \ No newline at end of file diff --git a/src/Verify.XunitV3.DerivePaths.Tests/SubDir/Tests.Mirror.cs b/src/Verify.XunitV3.DerivePaths.Tests/SubDir/Tests.Mirror.cs new file mode 100644 index 0000000000..b74528b1fa --- /dev/null +++ b/src/Verify.XunitV3.DerivePaths.Tests/SubDir/Tests.Mirror.cs @@ -0,0 +1,13 @@ +// This partial is intentionally located in a sub-directory so that its source file path, +// relative to the project directory, is "SubDir". That exercises the mirrorSourceStructure +// option, nesting the derived `.verified.` file under "Relative/SubDir". +public partial class Tests +{ + [Fact] + public Task ProjectRelativeDirectoryMirrored() + { + VerifierSettings.Reset(); + UseProjectRelativeDirectory("Relative", mirrorSourceStructure: true); + return Verify("Value"); + } +} diff --git a/src/Verify.XunitV3.DerivePaths.Tests/Tests.cs b/src/Verify.XunitV3.DerivePaths.Tests/Tests.cs index 7305da057d..659fc65eb9 100644 --- a/src/Verify.XunitV3.DerivePaths.Tests/Tests.cs +++ b/src/Verify.XunitV3.DerivePaths.Tests/Tests.cs @@ -1,4 +1,4 @@ -public class Tests +public partial class Tests { [Fact] public Task Test() diff --git a/src/Verify.XunitV3/DerivePaths/Verifier.cs b/src/Verify.XunitV3/DerivePaths/Verifier.cs index ead9820af3..5bc1d05c63 100644 --- a/src/Verify.XunitV3/DerivePaths/Verifier.cs +++ b/src/Verify.XunitV3/DerivePaths/Verifier.cs @@ -27,12 +27,22 @@ public static void DerivePathInfo(DerivePathInfo derivePathInfo) /// /// Use a directory relative to the project directory for storing for `.verified.` files. /// + [Obsolete("Use the overload that accepts mirrorSourceStructure.")] public static void UseProjectRelativeDirectory(string directory) => + UseProjectRelativeDirectory(directory, false); + + /// + /// Use a directory relative to the project directory for storing for `.verified.` files. + /// + /// The project relative directory to store `.verified.` files in. + /// + /// If true, nests `.verified.` files in sub-directories that mirror the directory structure of the test source files relative to the project directory. + /// + [OverloadResolutionPriority(1)] + public static void UseProjectRelativeDirectory(string directory, bool mirrorSourceStructure = false) => DerivePathInfo( - (sourceFile, projectDirectory, type, method) => new( - directory: Path.Combine(projectDirectory, directory), - typeName: type.NameWithParent(), - methodName: method.Name)); + (sourceFile, projectDirectory, type, method) => + PathInfo.DeriveProjectRelative(directory, mirrorSourceStructure, sourceFile, projectDirectory, type.NameWithParent(), method.Name)); /// /// Use a directory relative to the source file directory for storing for `.verified.` files. diff --git a/src/Verify/DerivePaths/PathInfo.cs b/src/Verify/DerivePaths/PathInfo.cs index e40b366938..c78518114e 100644 --- a/src/Verify/DerivePaths/PathInfo.cs +++ b/src/Verify/DerivePaths/PathInfo.cs @@ -43,6 +43,39 @@ public static PathInfo DeriveDefault( #endregion + /// + /// Derives a that stores `.verified.` files in relative to + /// . When is true, the files are + /// nested in sub-directories that mirror the directory structure of the test source file relative to the project + /// directory. Source files at, above, or outside the project directory fall back to . + /// + public static PathInfo DeriveProjectRelative( + string directory, + bool mirrorSourceStructure, + string sourceFile, + string projectDirectory, + string? typeName, + string? methodName) + { + var resolved = Path.Combine(projectDirectory, directory); + if (mirrorSourceStructure) + { + var sourceDirectory = Path.GetDirectoryName(sourceFile)!; + var relative = Path.GetRelativePath(projectDirectory, sourceDirectory); + if (relative != "." && + !relative.StartsWith("..", StringComparison.Ordinal) && + !Path.IsPathRooted(relative)) + { + resolved = Path.Combine(resolved, relative); + } + } + + return new( + directory: resolved, + typeName: typeName, + methodName: methodName); + } + internal static PathInfo DeriveDefault( string sourceFile, string projectDirectory,