Compare commits
No commits in common. "c13b257083911b08c0c593c6f79bc73b88641361" and "627e7f76a5ab50b15ca7b75f35d34b1212fc7699" have entirely different histories.
c13b257083
...
627e7f76a5
12 changed files with 56 additions and 123 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -211,6 +211,3 @@ FakesAssemblies/
|
||||||
|
|
||||||
# Codex
|
# Codex
|
||||||
.codex
|
.codex
|
||||||
|
|
||||||
# Nix
|
|
||||||
result
|
|
||||||
|
|
|
||||||
10
Example.Tests/Calculator.fs
Normal file
10
Example.Tests/Calculator.fs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
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)
|
||||||
27
Example.Tests/CalculatorTests.fs
Normal file
27
Example.Tests/CalculatorTests.fs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
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)
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
namespace Example
|
|
||||||
|
|
||||||
module Calculator =
|
|
||||||
let addOne value = value + 1
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
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,7 +11,6 @@
|
||||||
</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,7 +3,6 @@ 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 }
|
||||||
|
|
||||||
|
|
@ -56,7 +55,7 @@ let runTest projectPath testName =
|
||||||
Output(new StreamWriter(Console.OpenStandardOutput()))
|
Output(new StreamWriter(Console.OpenStandardOutput()))
|
||||||
}
|
}
|
||||||
|> Command.execute
|
|> Command.execute
|
||||||
|> Output.toExitCode
|
|> ignore
|
||||||
|
|
||||||
let getAssemblyPath projectPath =
|
let getAssemblyPath projectPath =
|
||||||
cli {
|
cli {
|
||||||
|
|
@ -80,7 +79,7 @@ let getMetadataLoadContext (assemblyPath: string) =
|
||||||
|
|
||||||
new MetadataLoadContext(pathAssemblyResolver, typeof<obj>.Assembly.GetName().Name)
|
new MetadataLoadContext(pathAssemblyResolver, typeof<obj>.Assembly.GetName().Name)
|
||||||
|
|
||||||
let unindentPatch (s: string) =
|
let unindented (s: string) =
|
||||||
let lines = s.Split([| "\r\n"; "\n" |], StringSplitOptions.None)
|
let lines = s.Split([| "\r\n"; "\n" |], StringSplitOptions.None)
|
||||||
|
|
||||||
let indexOfFirstNonEmptyLine =
|
let indexOfFirstNonEmptyLine =
|
||||||
|
|
@ -115,81 +114,26 @@ 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 |> unindentPatch }
|
Patch = attr.ConstructorArguments[0].Value :?> string |> unindented }
|
||||||
| _ -> 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 =
|
||||||
let parsedArguments =
|
if argv.Length <> 1 then
|
||||||
ArgumentParser.Create<Arguments>(programName = "mutannot")
|
eprintfn "Usage: mutannot <path/to/project.csproj|fsproj>"
|
||||||
|> _.ParseCommandLine(argv)
|
exit 1
|
||||||
|
|
||||||
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 filteredMutations =
|
let projectPath = argv[0]
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"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="
|
|
||||||
}
|
|
||||||
]
|
|
||||||
8
Mutannot/deps.nix
Normal file
8
Mutannot/deps.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{ 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.json;
|
nugetDeps = ./Mutannot/deps.nix;
|
||||||
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