From 7e9fecbddde8aa8672e072bfd6a58d3d5e5abe5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Sun, 31 May 2026 20:30:32 +0200 Subject: [PATCH] coverage: kill FileVersionInfo Is*/IsNot* and WhoseParent mutation survivors Add tests targeting surviving string mutants reported on the Stryker dashboard: - FileVersionInfo Is*/IsNot* (IsDebug, IsPatched, IsPreRelease, IsPrivateBuild, IsSpecialBuild): the opposite-direction expectation strings were never rendered. Exercise them via DoesNotComplyWith, which flips the grammar so both literals appear in a failure message. - FileInfo.WhoseParent / DirectoryInfo.WhoseParent: assert the ' whose parent ' connector by chaining HasName().And.WhoseParent. - FileInfo.WhoseParent: cover the GetDirectoryOrThrow message for a root file whose Directory is null. --- .../DirectoryInfo.WhoseParent.Tests.cs | 25 +++++++++++ .../FileInfo.WhoseParent.Tests.cs | 41 +++++++++++++++++ .../FileVersionInfo.IsDebug.Tests.cs | 44 +++++++++++++++++++ ...FileVersionInfo.IsNotPrivateBuild.Tests.cs | 22 ++++++++++ ...FileVersionInfo.IsNotSpecialBuild.Tests.cs | 22 ++++++++++ .../FileVersionInfo.IsPatched.Tests.cs | 44 +++++++++++++++++++ .../FileVersionInfo.IsPreRelease.Tests.cs | 44 +++++++++++++++++++ .../FileVersionInfo.IsPrivateBuild.Tests.cs | 22 ++++++++++ .../FileVersionInfo.IsSpecialBuild.Tests.cs | 22 ++++++++++ 9 files changed, 286 insertions(+) diff --git a/Tests/aweXpect.Testably.Tests/DirectoryInfo.WhoseParent.Tests.cs b/Tests/aweXpect.Testably.Tests/DirectoryInfo.WhoseParent.Tests.cs index c0ff290..0afc6f7 100644 --- a/Tests/aweXpect.Testably.Tests/DirectoryInfo.WhoseParent.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/DirectoryInfo.WhoseParent.Tests.cs @@ -56,6 +56,31 @@ async Task Act() await That(Act).Throws() .WithMessage("Cannot assert on the parent of a root directory because it has no parent."); } + + [Fact] + public async Task HasName_AndWhoseParentHasName_WhenParentNameDiffers_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.Initialize().WithSubdirectory("project").Initialized(p => p + .WithSubdirectory("src")); + IDirectoryInfo dirInfo = fileSystem.DirectoryInfo.New("project/src"); + + async Task Act() + { + await That(dirInfo).HasName("src").And.WhoseParent.HasName("wrong"); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that dirInfo + has name equal to "src" whose parent has name equal to "wrong", + but it was "project" which differs at index 0: + ↓ (actual) + "project" + "wrong" + ↑ (expected) + """); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileInfo.WhoseParent.Tests.cs b/Tests/aweXpect.Testably.Tests/FileInfo.WhoseParent.Tests.cs index 8958948..662ac19 100644 --- a/Tests/aweXpect.Testably.Tests/FileInfo.WhoseParent.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileInfo.WhoseParent.Tests.cs @@ -57,6 +57,47 @@ async Task Act() await That(Act).DoesNotThrow(); } + + [Fact] + public async Task HasName_AndWhoseParentHasName_WhenParentNameDiffers_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.Initialize().WithSubdirectory("logs").Initialized(d => d + .WithFile("today.log")); + IFileInfo fileInfo = fileSystem.FileInfo.New("logs/today.log"); + + async Task Act() + { + await That(fileInfo).HasName("today.log").And.WhoseParent.HasName("wrong"); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that fileInfo + has name equal to "today.log" whose parent has name equal to "wrong", + but it was "logs" which differs at index 0: + ↓ (actual) + "logs" + "wrong" + ↑ (expected) + """); + } + + [Fact] + public async Task OnRootFile_ShouldThrow() + { + MockFileSystem fileSystem = new(); + IFileInfo fileInfo = fileSystem.FileInfo.New( + fileSystem.Path.GetPathRoot(fileSystem.Directory.GetCurrentDirectory())!); + + async Task Act() + { + await That(fileInfo).WhoseParent.IsNotEmpty(); + } + + await That(Act).Throws() + .WithMessage("Cannot assert on the parent directory of the file because it has none."); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsDebug.Tests.cs b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsDebug.Tests.cs index cc733d2..dc67504 100644 --- a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsDebug.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsDebug.Tests.cs @@ -66,6 +66,28 @@ Expected that info but it was not """); } + + [Fact] + public async Task WhenNegated_AndInfoIsDebug_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsDebug(true)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsDebug()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is not debug, + but it was + """); + } } } @@ -130,6 +152,28 @@ async Task Act() await That(Act).DoesNotThrow(); } + + [Fact] + public async Task WhenNegated_AndInfoIsNotDebug_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsDebug(false)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsNotDebug()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is debug, + but it was not + """); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotPrivateBuild.Tests.cs b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotPrivateBuild.Tests.cs index 415dddd..70d7f08 100644 --- a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotPrivateBuild.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotPrivateBuild.Tests.cs @@ -66,6 +66,28 @@ Expected that info but it was """); } + + [Fact] + public async Task WhenNegated_AndInfoIsNotPrivateBuild_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsPrivateBuild(false)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsNotPrivateBuild()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is private build, + but it was not + """); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotSpecialBuild.Tests.cs b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotSpecialBuild.Tests.cs index 97778c9..55c09ad 100644 --- a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotSpecialBuild.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsNotSpecialBuild.Tests.cs @@ -66,6 +66,28 @@ Expected that info but it was """); } + + [Fact] + public async Task WhenNegated_AndInfoIsNotSpecialBuild_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsSpecialBuild(false)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsNotSpecialBuild()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is special build, + but it was not + """); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPatched.Tests.cs b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPatched.Tests.cs index 43c0810..430f393 100644 --- a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPatched.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPatched.Tests.cs @@ -66,6 +66,28 @@ async Task Act() await That(Act).DoesNotThrow(); } + + [Fact] + public async Task WhenNegated_AndInfoIsPatched_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsPatched(true)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsPatched()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is not patched, + but it was + """); + } } } @@ -130,6 +152,28 @@ Expected that info but it was """); } + + [Fact] + public async Task WhenNegated_AndInfoIsNotPatched_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsPatched(false)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsNotPatched()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is patched, + but it was not + """); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPreRelease.Tests.cs b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPreRelease.Tests.cs index 21e2017..baf5e96 100644 --- a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPreRelease.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPreRelease.Tests.cs @@ -66,6 +66,28 @@ async Task Act() await That(Act).DoesNotThrow(); } + + [Fact] + public async Task WhenNegated_AndInfoIsPreRelease_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsPreRelease(true)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsPreRelease()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is not pre-release, + but it was + """); + } } } @@ -130,6 +152,28 @@ Expected that info but it was """); } + + [Fact] + public async Task WhenNegated_AndInfoIsNotPreRelease_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsPreRelease(false)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsNotPreRelease()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is pre-release, + but it was not + """); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPrivateBuild.Tests.cs b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPrivateBuild.Tests.cs index 8930f7e..12f9dbf 100644 --- a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPrivateBuild.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsPrivateBuild.Tests.cs @@ -66,6 +66,28 @@ async Task Act() await That(Act).DoesNotThrow(); } + + [Fact] + public async Task WhenNegated_AndInfoIsPrivateBuild_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsPrivateBuild(true)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsPrivateBuild()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is not private build, + but it was + """); + } } } } diff --git a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsSpecialBuild.Tests.cs b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsSpecialBuild.Tests.cs index 4dcc806..536796d 100644 --- a/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsSpecialBuild.Tests.cs +++ b/Tests/aweXpect.Testably.Tests/FileVersionInfo.IsSpecialBuild.Tests.cs @@ -66,6 +66,28 @@ async Task Act() await That(Act).DoesNotThrow(); } + + [Fact] + public async Task WhenNegated_AndInfoIsSpecialBuild_ShouldFail() + { + MockFileSystem fileSystem = new(); + fileSystem.WithFileVersionInfo("*.dll", v => v.SetIsSpecialBuild(true)); + // ReSharper disable once MethodHasAsyncOverload + fileSystem.File.WriteAllText("Acme.dll", ""); + IFileVersionInfo info = fileSystem.FileVersionInfo.GetVersionInfo("Acme.dll"); + + async Task Act() + { + await That(info).DoesNotComplyWith(it => it.IsSpecialBuild()); + } + + await That(Act).ThrowsException() + .WithMessage(""" + Expected that info + is not special build, + but it was + """); + } } } }