Compare commits
10 commits
627e7f76a5
...
c13b257083
| Author | SHA1 | Date | |
|---|---|---|---|
| c13b257083 | |||
| cf8f914df0 | |||
| 9ca9bfd687 | |||
| de5843a783 | |||
| 9ff53f1803 | |||
| 500b3f0d8c | |||
| 294b2f85a9 | |||
| ae246d3f36 | |||
| 63d0219e9e | |||
| 5a521e7bfc |
12 changed files with 123 additions and 56 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -211,3 +211,6 @@ FakesAssemblies/
|
||||||
|
|
||||||
# Codex
|
# Codex
|
||||||
.codex
|
.codex
|
||||||
|
|
||||||
|
# Nix
|
||||||
|
result
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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
4
Example/Calculator.fs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
namespace Example
|
||||||
|
|
||||||
|
module Calculator =
|
||||||
|
let addOne value = value + 1
|
||||||
21
Example/CalculatorTests.fs
Normal file
21
Example/CalculatorTests.fs
Normal 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)
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Argu" Version="6.2.5" />
|
||||||
<PackageReference Include="Fli" Version="1.1000.0" />
|
<PackageReference Include="Fli" Version="1.1000.0" />
|
||||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="9.0.1" />
|
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="9.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ open System.IO
|
||||||
open System.Reflection
|
open System.Reflection
|
||||||
open System.Runtime.InteropServices
|
open System.Runtime.InteropServices
|
||||||
open Fli
|
open Fli
|
||||||
|
open Argu
|
||||||
|
|
||||||
type MutationCase = { TestName: string; Patch: string }
|
type MutationCase = { TestName: string; Patch: string }
|
||||||
|
|
||||||
|
|
@ -55,7 +56,7 @@ let runTest projectPath testName =
|
||||||
Output(new StreamWriter(Console.OpenStandardOutput()))
|
Output(new StreamWriter(Console.OpenStandardOutput()))
|
||||||
}
|
}
|
||||||
|> Command.execute
|
|> Command.execute
|
||||||
|> ignore
|
|> Output.toExitCode
|
||||||
|
|
||||||
let getAssemblyPath projectPath =
|
let getAssemblyPath projectPath =
|
||||||
cli {
|
cli {
|
||||||
|
|
@ -79,7 +80,7 @@ let getMetadataLoadContext (assemblyPath: string) =
|
||||||
|
|
||||||
new MetadataLoadContext(pathAssemblyResolver, typeof<obj>.Assembly.GetName().Name)
|
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 lines = s.Split([| "\r\n"; "\n" |], StringSplitOptions.None)
|
||||||
|
|
||||||
let indexOfFirstNonEmptyLine =
|
let indexOfFirstNonEmptyLine =
|
||||||
|
|
@ -114,26 +115,81 @@ let getMutationCases projectPath =
|
||||||
| "Mutannot.MutationCaseAttribute" ->
|
| "Mutannot.MutationCaseAttribute" ->
|
||||||
Some
|
Some
|
||||||
{ TestName = $"{m.DeclaringType.FullName}.{m.Name}"
|
{ TestName = $"{m.DeclaringType.FullName}.{m.Name}"
|
||||||
Patch = attr.ConstructorArguments[0].Value :?> string |> unindented }
|
Patch = attr.ConstructorArguments[0].Value :?> string |> unindentPatch }
|
||||||
| _ -> None))
|
| _ -> None))
|
||||||
|> Seq.toList
|
|> 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>]
|
[<EntryPoint>]
|
||||||
let main argv =
|
let main argv =
|
||||||
if argv.Length <> 1 then
|
let parsedArguments =
|
||||||
eprintfn "Usage: mutannot <path/to/project.csproj|fsproj>"
|
ArgumentParser.Create<Arguments>(programName = "mutannot")
|
||||||
exit 1
|
|> _.ParseCommandLine(argv)
|
||||||
|
|
||||||
|
let projectPath = parsedArguments.GetResult ProjectPath
|
||||||
|
let validateOnly = parsedArguments.Contains ValidateOnly
|
||||||
|
let maybeFilter = parsedArguments.TryGetResult Filter
|
||||||
|
|
||||||
ensureCleanWorkingDirectory ()
|
ensureCleanWorkingDirectory ()
|
||||||
|
|
||||||
AppDomain.CurrentDomain.ProcessExit.Add(fun _ -> restore ())
|
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
|
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 ()
|
restore ()
|
||||||
|
|
||||||
|
Console.ForegroundColor <- ConsoleColor.Green
|
||||||
|
|
||||||
|
if validateOnly then
|
||||||
|
printf "Success: All mutantions valid\n"
|
||||||
|
else
|
||||||
|
printf "Success: All mutants killed\n"
|
||||||
|
|
||||||
|
Console.ResetColor()
|
||||||
|
|
||||||
0
|
0
|
||||||
|
|
|
||||||
27
Mutannot/deps.json
Normal file
27
Mutannot/deps.json
Normal 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="
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{ fetchNuGet }:
|
|
||||||
[
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "FSharp.Core";
|
|
||||||
version = "10.1.201";
|
|
||||||
sha256 = "sha256-NzxdRJgL+5RQpUm8Y6Mc0w7sakxqThv6qHpP+u0x5x0=";
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
src = ./Mutannot;
|
src = ./Mutannot;
|
||||||
projectFile = "Mutannot.fsproj";
|
projectFile = "Mutannot.fsproj";
|
||||||
nugetDeps = ./Mutannot/deps.nix;
|
nugetDeps = ./Mutannot/deps.json;
|
||||||
executables = [ "mutannot" ];
|
executables = [ "mutannot" ];
|
||||||
dotnet-sdk = pkgs.dotnet-sdk_10;
|
dotnet-sdk = pkgs.dotnet-sdk_10;
|
||||||
dotnet-runtime = pkgs.dotnet-sdk_10;
|
dotnet-runtime = pkgs.dotnet-sdk_10;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue