Compare commits

...

10 commits

12 changed files with 123 additions and 56 deletions

3
.gitignore vendored
View file

@ -211,3 +211,6 @@ FakesAssemblies/
# Codex
.codex
# Nix
result

View file

@ -1,10 +0,0 @@
namespace Example
module Calculator =
let addOne value = value + 1
let absoluteDifference left right =
if left >= right then left - right else right - left
let isLeapYear year =
year % 4 = 0 && (year % 100 <> 0 || year % 400 = 0)

View file

@ -1,27 +0,0 @@
namespace Example.Tests
open Example
open Mutannot
open Xunit
type CalculatorTests() =
[<Fact>]
[<MutationCase("""
diff --git a/Example.Tests/Calculator.fs b/Example.Tests/Calculator.fs
index cfcce3b..39be7f3 100644
--- a/Example.Tests/Calculator.fs
+++ b/Example.Tests/Calculator.fs
@@ -1,7 +1,7 @@
namespace Example
module Calculator =
- let addOne value = value + 1
+ let addOne value = value - 1
let absoluteDifference left right =
if left >= right then left - right else right - left
member _.AddOne_increments() =
Assert.Equal(42, Calculator.addOne 41)
""")>]
member _.AddOne_increments() = Assert.Equal(42, Calculator.addOne 41)

4
Example/Calculator.fs Normal file
View file

@ -0,0 +1,4 @@
namespace Example
module Calculator =
let addOne value = value + 1

View file

@ -0,0 +1,21 @@
namespace Example
open Example
open Mutannot
open Xunit
type CalculatorTests() =
[<Fact>]
[<MutationCase("""
diff --git a/Example/Calculator.fs b/Example/Calculator.fs
index 6f0c515..030e391 100644
--- a/Example/Calculator.fs
+++ b/Example/Calculator.fs
@@ -1,4 +1,4 @@
namespace Example
module Calculator =
- let addOne value = value + 1
+ let addOne value = value - 1
""")>]
member _.AddOne_increments() = Assert.Equal(42, Calculator.addOne 41)

View file

@ -11,6 +11,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Argu" Version="6.2.5" />
<PackageReference Include="Fli" Version="1.1000.0" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="9.0.1" />
</ItemGroup>

View file

@ -3,6 +3,7 @@ open System.IO
open System.Reflection
open System.Runtime.InteropServices
open Fli
open Argu
type MutationCase = { TestName: string; Patch: string }
@ -55,7 +56,7 @@ let runTest projectPath testName =
Output(new StreamWriter(Console.OpenStandardOutput()))
}
|> Command.execute
|> ignore
|> Output.toExitCode
let getAssemblyPath projectPath =
cli {
@ -79,7 +80,7 @@ let getMetadataLoadContext (assemblyPath: string) =
new MetadataLoadContext(pathAssemblyResolver, typeof<obj>.Assembly.GetName().Name)
let unindented (s: string) =
let unindentPatch (s: string) =
let lines = s.Split([| "\r\n"; "\n" |], StringSplitOptions.None)
let indexOfFirstNonEmptyLine =
@ -114,26 +115,81 @@ let getMutationCases projectPath =
| "Mutannot.MutationCaseAttribute" ->
Some
{ TestName = $"{m.DeclaringType.FullName}.{m.Name}"
Patch = attr.ConstructorArguments[0].Value :?> string |> unindented }
Patch = attr.ConstructorArguments[0].Value :?> string |> unindentPatch }
| _ -> None))
|> Seq.toList
type Arguments =
| [<MainCommand; ExactlyOnce>] ProjectPath of ProjectPath: string
| Filter of SearchString: string
| ValidateOnly
interface IArgParserTemplate with
member s.Usage =
match s with
| ProjectPath _ -> "path/to/project.csproj|fsproj"
| Filter _ -> "filter down to mutations that contain the given search string"
| ValidateOnly -> "check if the patches apply, but don't run the mutations"
[<EntryPoint>]
let main argv =
if argv.Length <> 1 then
eprintfn "Usage: mutannot <path/to/project.csproj|fsproj>"
exit 1
let parsedArguments =
ArgumentParser.Create<Arguments>(programName = "mutannot")
|> _.ParseCommandLine(argv)
let projectPath = parsedArguments.GetResult ProjectPath
let validateOnly = parsedArguments.Contains ValidateOnly
let maybeFilter = parsedArguments.TryGetResult Filter
ensureCleanWorkingDirectory ()
AppDomain.CurrentDomain.ProcessExit.Add(fun _ -> restore ())
let projectPath = argv[0]
let filteredMutations =
getMutationCases projectPath
|> Seq.filter _.Patch.Contains(maybeFilter |> Option.defaultValue "")
|> Seq.indexed
for index, mutationCase in filteredMutations do
Console.ForegroundColor <- ConsoleColor.Green
printf $"MUTATION {index + 1}\n"
Console.ForegroundColor <- ConsoleColor.Magenta
printf "Test:\n"
Console.ResetColor()
printf "%s\n\n" mutationCase.TestName
Console.ForegroundColor <- ConsoleColor.Magenta
printf "Patch:\n"
Console.ResetColor()
printf "%s\n" mutationCase.Patch
for mutationCase in getMutationCases projectPath do
printfn "MUTATION\n\n%s" <| mutationCase.Patch
applyPatch mutationCase.Patch
runTest projectPath mutationCase.TestName
if not validateOnly then
Console.ForegroundColor <- ConsoleColor.Magenta
printf "Output:\n"
Console.ResetColor()
match runTest projectPath mutationCase.TestName with
| 0 ->
Console.ForegroundColor <- ConsoleColor.Red
eprintf "ERROR: Expected tested to fail, but it succeeded\n"
Console.ResetColor()
exit 3
| _ ->
Console.ForegroundColor <- ConsoleColor.Green
printf "✓ Mutant killed\n\n"
restore ()
Console.ForegroundColor <- ConsoleColor.Green
if validateOnly then
printf "Success: All mutantions valid\n"
else
printf "Success: All mutants killed\n"
Console.ResetColor()
0

27
Mutannot/deps.json Normal file
View file

@ -0,0 +1,27 @@
[
{
"pname": "Argu",
"version": "6.2.5",
"hash": "sha256-5HcZcvco4e8+hgLhzlxk7ZmFVLtZL9LVr7LbmXsLmNU="
},
{
"pname": "Fli",
"version": "1.1000.0",
"hash": "sha256-LKJ2raQJuNfJKOA6Y85tECMnUFuKsmd5fBOG2Sq5OjY="
},
{
"pname": "System.Configuration.ConfigurationManager",
"version": "4.4.0",
"hash": "sha256-+8wGYllXnIxRzy9dLhZFB88GoPj8ivYXS0KUfcivT8I="
},
{
"pname": "System.Reflection.MetadataLoadContext",
"version": "9.0.1",
"hash": "sha256-kWm31a0unw/H8SjxaabVYKInR40bTAL9JnGQEVQGTsU="
},
{
"pname": "System.Security.Cryptography.ProtectedData",
"version": "4.4.0",
"hash": "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE="
}
]

View file

@ -1,8 +0,0 @@
{ fetchNuGet }:
[
(fetchNuGet {
pname = "FSharp.Core";
version = "10.1.201";
sha256 = "sha256-NzxdRJgL+5RQpUm8Y6Mc0w7sakxqThv6qHpP+u0x5x0=";
})
]

View file

@ -27,7 +27,7 @@
version = "0.1.0";
src = ./Mutannot;
projectFile = "Mutannot.fsproj";
nugetDeps = ./Mutannot/deps.nix;
nugetDeps = ./Mutannot/deps.json;
executables = [ "mutannot" ];
dotnet-sdk = pkgs.dotnet-sdk_10;
dotnet-runtime = pkgs.dotnet-sdk_10;