File | Mutants | Sequence points |
---|
ArithmeticOperatorTurtle.cs | | |
BitwiseOperatorTurtle.cs | | |
BranchConditionTurtle.cs | | |
ConditionalBoundaryTurtle.cs | | |
ConsoleProcessFactory.cs | | |
InstructionExtensions.cs | | |
MethodDefinitionResolver.cs | | |
MethodTurtleBase.cs | | |
Module.cs | | |
MutationTest.cs | | |
MutationTestBuilder.cs | | |
OpCodeRotationTurtle.cs | | |
SequencePointDeletionTurtle.cs | | |
TestDirectory.cs | | |
TypeResolver.cs | | |
VariableReadTurtle.cs | | |
VariableWriteTurtle.cs | | |
ArithmeticOperatorTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System.Collections.Generic;
23: :
24: : using Mono.Cecil.Cil;
25: :
26: : namespace NinjaTurtles.Turtles
27: : {
28: : /// <summary>
29: : /// An implementation of <see cref="IMethodTurtle"/> that replaces each of
30: : /// the arithmetic operators <see cref="OpCodes.Add" />,
31: : /// <see cref="OpCodes.Sub" />, <see cref="OpCodes.Mul" />,
32: : /// <see cref="OpCodes.Div" /> and <see cref="OpCodes.Rem" /> with each
33: : /// of the others in turn.
34: : /// </summary>
35: : public class ArithmeticOperatorTurtle : OpCodeRotationTurtle
36: : {
37: : /// <summary>
38: : /// Initializes a new instance of
39: : /// <see cref="ArithmeticOperatorTurtle" />.
40: : /// </summary>
41: : public ArithmeticOperatorTurtle()
42: : {
43: 1/1: _opCodes = new Dictionary<OpCode, IEnumerable<OpCode>>
44: 1/1: {
45: 1/1: {OpCodes.Add, new[] {OpCodes.Sub, OpCodes.Mul, OpCodes.Div, OpCodes.Rem}},
46: 1/1: {OpCodes.Sub, new[] {OpCodes.Add, OpCodes.Mul, OpCodes.Div, OpCodes.Rem}},
47: 1/1: {OpCodes.Mul, new[] {OpCodes.Add, OpCodes.Sub, OpCodes.Div, OpCodes.Rem}},
48: 1/1: {OpCodes.Div, new[] {OpCodes.Add, OpCodes.Sub, OpCodes.Mul, OpCodes.Rem}},
49: 1/1: {OpCodes.Rem, new[] {OpCodes.Add, OpCodes.Sub, OpCodes.Mul, OpCodes.Div}}
50: 1/1: };
51: : }
52: : }
53: : }
54: :
BitwiseOperatorTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System.Collections.Generic;
23: :
24: : using Mono.Cecil.Cil;
25: :
26: : namespace NinjaTurtles.Turtles
27: : {
28: : /// <summary>
29: : /// An implementation of <see cref="IMethodTurtle"/> that replaces each of
30: : /// the bitwise operators <see cref="OpCodes.Or" />,
31: : /// <see cref="OpCodes.And" /> and <see cref="OpCodes.Xor" /> with each
32: : /// of the others in turn.
33: : /// </summary>
34: : public class BitwiseOperatorTurtle : OpCodeRotationTurtle
35: : {
36: : /// <summary>
37: : /// Initializes a new instance of <see cref="BitwiseOperatorTurtle" />.
38: : /// </summary>
39: : public BitwiseOperatorTurtle()
40: : {
41: 1/1: _opCodes = new Dictionary<OpCode, IEnumerable<OpCode>>
42: 1/1: {
43: 1/1: {OpCodes.Or, new[] {OpCodes.And, OpCodes.Xor}},
44: 1/1: {OpCodes.And, new[] {OpCodes.Or, OpCodes.Xor}},
45: 1/1: {OpCodes.Xor, new[] {OpCodes.Or, OpCodes.And}}
46: 1/1: };
47: : }
48: : }
49: : }
BranchConditionTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System.Collections.Generic;
23: :
24: : using Mono.Cecil.Cil;
25: :
26: : namespace NinjaTurtles.Turtles
27: : {
28: : /// <summary>
29: : /// An implementation of <see cref="IMethodTurtle"/> that replaces the
30: : /// conditional branch operators
31: : /// <see cref="OpCodes.Brtrue" /> and <see cref="OpCodes.Brfalse"/> with
32: : /// their converse and with the operators <see cref="OpCodes.Br"/> (always
33: : /// branch) and <see cref="OpCodes.Nop" /> (never branch) in turn.
34: : /// </summary>
35: : public class BranchConditionTurtle : OpCodeRotationTurtle
36: : {
37: : /// <summary>
38: : /// Initializes a new instance of <see cref="BranchConditionTurtle" />.
39: : /// </summary>
40: : public BranchConditionTurtle()
41: : {
42: 1/1: _opCodes = new Dictionary<OpCode, IEnumerable<OpCode>>
43: 1/1: {
44: 1/1: {OpCodes.Brtrue, new[] {OpCodes.Nop, OpCodes.Brfalse, OpCodes.Br}},
45: 1/1: {OpCodes.Brfalse, new[] {OpCodes.Nop, OpCodes.Brtrue, OpCodes.Br}}
46: 1/1: };
47: : }
48: : }
49: : }
ConditionalBoundaryTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System.Collections.Generic;
23: :
24: : using Mono.Cecil;
25: : using Mono.Cecil.Cil;
26: :
27: : namespace NinjaTurtles.Turtles
28: : {
29: : /// <summary>
30: : /// An implementation of <see cref="IMethodTurtle"/> that changes
31: : /// whether or not equality is included in comparison operators, so
32: : /// for example <see cref="OpCodes.Clt" /> is replaced by a combination
33: : /// of <see cref="OpCodes.Cgt" /> and a comparison with zero to give the
34: : /// effect of a <= operator.
35: : /// </summary>
36: : public class ConditionalBoundaryTurtle : MethodTurtleBase
37: : {
38: : /// <summary>
39: : /// Performs the actual code mutations, returning each with
40: : /// <c>yield</c> for the calling code to use.
41: : /// </summary>
42: : /// <remarks>
43: : /// Implementing classes should yield the result obtained by calling
44: : /// the <see mref="DoYield" /> method.
45: : /// </remarks>
46: : /// <param name="method">
47: : /// A <see cref="MethodDefinition" /> for the method on which mutation
48: : /// testing is to be carried out.
49: : /// </param>
50: : /// <param name="module">
51: : /// A <see cref="Module" /> representing the main module of the
52: : /// containing assembly.
53: : /// </param>
54: : /// <returns>
55: : /// An <see cref="IEnumerable{T}" /> of
56: : /// <see cref="MutantMetaData" /> structures.
57: : /// </returns>
58: : protected override IEnumerable<MutantMetaData> DoMutate(MethodDefinition method, Module module)
59: : {
60: 16/16: for (int index = 0; index < method.Body.Instructions.Count; index++)
61: : {
62: 4/4: var instruction = method.Body.Instructions[index];
63: 13/13: if (instruction.OpCode == OpCodes.Clt
64: 13/13: || instruction.OpCode == OpCodes.Cgt)
65: : {
66: 3/3: var originalCode = instruction.OpCode.Code;
67: :
68: 3/3: var loadZero = method.Body.GetILProcessor().Create(OpCodes.Ldc_I4_0);
69: 3/3: var compareEqual = method.Body.GetILProcessor().Create(OpCodes.Ceq);
70: :
71: 8/8: method.Body.Instructions.Insert(index + 1, compareEqual);
72: 8/8: method.Body.Instructions.Insert(index + 1, loadZero);
73: :
74: 8/8: instruction.OpCode = instruction.OpCode == OpCodes.Clt ? OpCodes.Cgt : OpCodes.Clt;
75: :
76: 4/4: var description = string.Format("{0:x4}: {1} => not {2}", GetOriginalOffset(index), originalCode, instruction.OpCode.Code);
77: 4/4: yield return DoYield(method, module, description, index);
78: :
79: 8/8: instruction.OpCode = instruction.OpCode == OpCodes.Clt ? OpCodes.Cgt : OpCodes.Clt;
80: :
81: 3/3: method.Body.Instructions.Remove(compareEqual);
82: 3/3: method.Body.Instructions.Remove(loadZero);
83: : }
84: : }
85: : }
86: : }
87: : }
ConsoleProcessFactory.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Collections.Generic;
24: : using System.Diagnostics;
25: : using System.IO;
26: :
27: : namespace NinjaTurtles
28: : {
29: : /// <summary>
30: : /// A factory class used to instatiate a <see cref="Process" /> instance,
31: : /// taking into account the operating system and runtime.
32: : /// </summary>
33: : public static class ConsoleProcessFactory
34: : {
35: : internal static bool IsMono { get; set; }
36: : internal static bool IsWindows { get; set; }
37: :
38: : static ConsoleProcessFactory()
39: : {
40: : IsMono = Type.GetType("Mono.Runtime") != null;
41: : IsWindows = Environment.OSVersion.Platform.ToString().StartsWith("Win")
42: : || Environment.OSVersion.Platform == PlatformID.Xbox;
43: : }
44: :
45: : /// <summary>
46: : /// Creates a <see cref="Process" /> instance used to execute the
47: : /// executable identifier by the <paramref name="exeName"/>
48: : /// parameter, with the <paramref name="arguments"/> specified.
49: : /// </summary>
50: : /// <param name="exeName">
51: : /// The name (and path) of the executable to run.
52: : /// </param>
53: : /// <param name="arguments">
54: : /// The command line arguments to pass to the executable.
55: : /// </param>
56: : /// <param name="additionalSearchLocations">
57: : /// An optional list of additional search paths.
58: : /// </param>
59: : /// <returns>
60: : /// An instance of <see cref="Process" />.
61: : /// </returns>
62: : public static Process CreateProcess(string exeName, string arguments, params string[] additionalSearchLocations)
63: : {
64: 2/2: exeName = FindExecutable(exeName, additionalSearchLocations);
65: :
66: 5/5: if (IsMono)
67: : {
68: 3/3: arguments = string.Format("--runtime=v4.0 \"{0}\" {1}", exeName, arguments);
69: 1/1: exeName = "mono";
70: : }
71: :
72: 5/5: arguments = string.Format(arguments, IsWindows ? "/" : "-");
73: :
74: 3/3: var processStartInfo = new ProcessStartInfo(exeName, arguments);
75: 1/1: processStartInfo.UseShellExecute = false;
76: 1/1: processStartInfo.CreateNoWindow = true;
77: 1/1: processStartInfo.RedirectStandardOutput = true;
78: :
79: 4/4: return new Process
80: 4/4: {
81: 4/4: StartInfo = processStartInfo
82: 4/4: };
83: : }
84: :
85: : private static string FindExecutable(string exeName, string[] additionalSearchLocations)
86: : {
87: 1/1: var searchPath = new List<string>();
88: 1/1: searchPath.AddRange(additionalSearchLocations);
89: 7/7: string environmentSearchPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
90: 8/8: searchPath.AddRange(environmentSearchPath.Split(IsWindows ? ';' : ':'));
91: :
92: 12/12: foreach (string folder in searchPath)
93: : {
94: 12/12: string fullExePath = Path.Combine(folder, exeName);
95: 9/9: if (File.Exists(fullExePath))
96: : {
97: 8/8: return fullExePath;
98: : }
99: : }
100: 9/9: return exeName;
101: : }
102: : }
103: : }
InstructionExtensions.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Collections.Generic;
24: : using System.Linq;
25: :
26: : using Mono.Cecil;
27: : using Mono.Cecil.Cil;
28: :
29: : namespace NinjaTurtles
30: : {
31: : internal static class InstructionExtensions
32: : {
33: : internal static bool IsMeaninglessUnconditionalBranch(this Instruction instruction)
34: : {
35: 3/3: return instruction.OpCode == OpCodes.Br
36: 3/3: && ((Instruction)instruction.Operand).Offset == instruction.Next.Offset;
37: : }
38: :
39: : internal static bool FollowsSequence(this Instruction instruction, params OpCode[] sequence)
40: : {
41: : if (instruction.OpCode != sequence[0]) return false;
42: : if (sequence.Length == 1) return true;
43: : var newSequence = new OpCode[sequence.Length - 1];
44: : Array.Copy(sequence, 1, newSequence, 0, newSequence.Length);
45: : return instruction.Next.FollowsSequence(newSequence);
46: : }
47: :
48: : internal static bool IsPartOfSequence(this Instruction instruction, params OpCode[] sequence)
49: : {
50: : if (!sequence.Distinct().Contains(instruction.OpCode)) return false;
51: : var startInstruction = instruction;
52: : for (int i = 0; i < sequence.Length; i++)
53: : {
54: : if (startInstruction == null) break;
55: : if (startInstruction.FollowsSequence(sequence)) return true;
56: : startInstruction = startInstruction.Previous;
57: : }
58: : return false;
59: : }
60: :
61: : internal static bool IsPartOfCompilerGeneratedDispose(this Instruction instruction)
62: : {
63: : if (instruction.IsPartOfSequence(OpCodes.Leave,
64: : OpCodes.Ldloc, OpCodes.Ldnull, OpCodes.Ceq,
65: : OpCodes.Stloc, OpCodes.Ldloc, OpCodes.Brtrue,
66: : OpCodes.Ldloc, OpCodes.Callvirt))
67: : {
68: : while (instruction.OpCode != OpCodes.Callvirt)
69: : {
70: : instruction = instruction.Next;
71: : }
72: : var method = ((MethodReference)instruction.Operand);
73: : return method.Name == "Dispose";
74: : }
75: : if (instruction.IsPartOfSequence(OpCodes.Leave,
76: : OpCodes.Ldloc, OpCodes.Ldnull, OpCodes.Ceq,
77: : OpCodes.Brtrue,
78: : OpCodes.Ldloc, OpCodes.Callvirt))
79: : {
80: : while (instruction.OpCode != OpCodes.Callvirt)
81: : {
82: : instruction = instruction.Next;
83: : }
84: : var method = ((MethodReference)instruction.Operand);
85: : return method.Name == "Dispose";
86: : }
87: : if (instruction.IsPartOfSequence(OpCodes.Nop, OpCodes.Ldc_I4, OpCodes.Stloc,
88: : OpCodes.Leave, OpCodes.Ldarg, OpCodes.Call,
89: : OpCodes.Nop, OpCodes.Endfinally))
90: : {
91: : if (instruction.OpCode == OpCodes.Endfinally)
92: : {
93: : instruction = instruction.Previous;
94: : }
95: : if (instruction.Next.OpCode == OpCodes.Endfinally)
96: : {
97: : instruction = instruction.Previous;
98: : }
99: : while (instruction.OpCode != OpCodes.Call)
100: : {
101: : instruction = instruction.Next;
102: : }
103: : var method = ((MethodReference)instruction.Operand);
104: : return method.Name == "Dispose" || method.Name == "System.IDisposable.Dispose";
105: : }
106: : return false;
107: : }
108: :
109: : internal static bool ShouldReportSequencePoint(this Instruction instruction)
110: : {
111: 1/1: var instructions = new List<Instruction>();
112: : do
113: : {
114: 9/9: if (instruction.OpCode != OpCodes.Nop)
115: : {
116: 3/3: instructions.Add(instruction);
117: : }
118: 3/3: instruction = instruction.Next;
119: 13/13: } while (instruction != null && instruction.SequencePoint == null);
120: 10/10: if (instructions.All(i => i.OpCode == OpCodes.Ret))
121: : {
122: 1/1: return false;
123: : }
124: 7/7: if (instructions.Count == 2)
125: : {
126: 2/2: Instruction first = instructions[0];
127: 2/2: Instruction second = instructions[1];
128: 29/29: if (((first.OpCode == OpCodes.Ldarg
129: 29/29: && ((ParameterDefinition)first.Operand).Sequence == 0 )
130: 29/29: || first.OpCode == OpCodes.Ldarg_0)
131: 29/29: && second.OpCode == OpCodes.Call
132: 29/29: && ((MethodReference)second.Operand).Name == Methods.CONSTRUCTOR)
133: : {
134: 1/1: return false;
135: : }
136: : }
137: 1/1: return true;
138: : }
139: : }
140: : }
MethodDefinitionResolver.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Linq;
24: :
25: : using Mono.Cecil;
26: :
27: : using NLog;
28: :
29: : namespace NinjaTurtles
30: : {
31: : internal class MethodDefinitionResolver
32: : {
33: : #region Logging
34: :
35: : private static Logger _log = LogManager.GetCurrentClassLogger();
36: :
37: : #endregion
38: :
39: : public static MethodDefinition ResolveMethod(TypeDefinition typeDefinition, string methodName)
40: : {
41: 1/1: _log.Debug("Resolving method \"{0}\" in \"{1}\".", methodName, typeDefinition.FullName);
42: : try
43: : {
44: 5/5: MethodDefinition methodDefinition = typeDefinition.Methods.Single(m => m.Name == methodName);
45: 1/1: _log.Debug("Method \"{0}\" successfully resolved in \"{1}\".", methodName, typeDefinition.FullName);
46: 3/3: return methodDefinition;
47: : }
48: 1/1: catch (InvalidOperationException ex)
49: : {
50: 5/5: if (ex.Message == "Sequence contains no matching element")
51: : {
52: 1/1: _log.Error("Method \"{0}\" is unrecognised.", methodName);
53: 1/1: throw new ArgumentException(string.Format("Method \"{0}\" is unrecognised.", methodName), "methodName");
54: : }
55: 1/1: _log.Error("Method \"{0}\" is overloaded.", methodName);
56: 2/2: throw new ArgumentException(string.Format("Method \"{0}\" is overloaded.", methodName), "methodName");
57: : }
58: : }
59: :
60: : public static MethodDefinition ResolveMethod(TypeDefinition typeDefinition, string methodName, Type[] parameterTypes)
61: : {
62: 5/5: if (parameterTypes == null)
63: : {
64: 1/1: _log.Warn("\"ResolveMethod\" overload with parameter types called unnecessarily.");
65: 2/2: return ResolveMethod(typeDefinition, methodName);
66: : }
67: : try
68: : {
69: 5/5: MethodDefinition methodDefinition =
70: 5/5: typeDefinition.Methods.Single(
71: 5/5: m => m.Name == methodName
72: 5/5: && Enumerable.SequenceEqual(
73: 5/5: m.Parameters.Select(p => p.ParameterType.Name.Replace("TypeDefinition", "Type")),
74: 5/5: parameterTypes.Select(p => p.Name)));
75: 1/1: _log.Debug("Method \"{0}\" successfully resolved in \"{1}\".", methodName, typeDefinition.FullName);
76: 3/3: return methodDefinition;
77: : }
78: : catch (InvalidOperationException)
79: : {
80: 1/1: _log.Error("Method \"{0}\" with specified parameter types is unrecognised.", methodName);
81: 2/2: throw new ArgumentException(string.Format("Method \"{0}\" with specified parameter types is unrecognised.", methodName), "methodName");
82: : }
83: : }
84: : }
85: : }
MethodTurtleBase.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Collections.Generic;
24: : using System.Globalization;
25: : using System.IO;
26: : using System.Linq;
27: :
28: : using Mono.Cecil;
29: : using Mono.Cecil.Cil;
30: : using Mono.Cecil.Rocks;
31: :
32: : namespace NinjaTurtles.Turtles
33: : {
34: : /// <summary>
35: : /// An abstract base class for implementations of
36: : /// <see cref="IMethodTurtle" />.
37: : /// </summary>
38: : public abstract class MethodTurtleBase : IMethodTurtle
39: : {
40: : private int[] _originalOffsets;
41: : private Module _module;
42: : private MethodDefinition _method;
43: :
44: : internal void MutantComplete(MutantMetaData metaData)
45: : {
46: 1/1: metaData.TestDirectory.Dispose();
47: : }
48: :
49: : /// <summary>
50: : /// Returns an <see cref="IEnumerable{T}" /> of detailed descriptions
51: : /// of mutations, having first carried out the mutation in question and
52: : /// saved the modified assembly under test to disk.
53: : /// </summary>
54: : /// <param name="method">
55: : /// A <see cref="MethodDefinition" /> for the method on which mutation
56: : /// testing is to be carried out.
57: : /// </param>
58: : /// <param name="module">
59: : /// A <see cref="Module" /> representing the main module of the
60: : /// containing assembly.
61: : /// </param>
62: : /// <param name="originalOffsets">
63: : /// An array of the original IL offsets before macros were expanded.
64: : /// </param>
65: : /// <returns>
66: : /// An <see cref="IEnumerable{T}" /> of
67: : /// <see cref="MutantMetaData" /> structures.
68: : /// </returns>
69: : public IEnumerable<MutantMetaData> Mutate(MethodDefinition method, Module module, int[] originalOffsets)
70: : {
71: : _module = module;
72: : _method = method;
73: : _originalOffsets = originalOffsets;
74: : method.Body.SimplifyMacros();
75: : foreach (var mutation in DoMutate(method, module))
76: : {
77: : yield return mutation;
78: : }
79: : method.Body.OptimizeMacros();
80: : var nestedType =
81: : method.DeclaringType.NestedTypes.FirstOrDefault(
82: : t => t.Name.StartsWith(string.Format("<{0}>", method.Name))
83: : && t.Interfaces.Any(i => i.Name == "IEnumerable`1"));
84: : if (nestedType != null)
85: : {
86: : var nestedMethod = nestedType.Methods.First(m => m.Name == "MoveNext");
87: : _originalOffsets = nestedMethod.Body.Instructions.Select(i => i.Offset).ToArray();
88: : _method = nestedMethod;
89: : nestedMethod.Body.SimplifyMacros();
90: : foreach (var mutation in DoMutate(nestedMethod, module))
91: : {
92: : yield return mutation;
93: : }
94: : nestedMethod.Body.OptimizeMacros();
95: : }
96: : }
97: :
98: : /// <summary>
99: : /// Performs the actual code mutations, returning each with
100: : /// <c>yield</c> for the calling code to use.
101: : /// </summary>
102: : /// <remarks>
103: : /// Implementing classes should yield the result obtained by calling
104: : /// the <see mref="DoYield" /> method.
105: : /// </remarks>
106: : /// <param name="method">
107: : /// A <see cref="MethodDefinition" /> for the method on which mutation
108: : /// testing is to be carried out.
109: : /// </param>
110: : /// <param name="module">
111: : /// A <see cref="Module" /> representing the main module of the
112: : /// containing assembly.
113: : /// </param>
114: : /// <returns>
115: : /// An <see cref="IEnumerable{T}" /> of
116: : /// <see cref="MutantMetaData" /> structures.
117: : /// </returns>
118: : protected abstract IEnumerable<MutantMetaData> DoMutate(MethodDefinition method, Module module);
119: :
120: : /// <summary>
121: : /// A helper method that copies the test folder, and saves the mutated
122: : /// assembly under test into it before returning an instance of
123: : /// <see cref="MutantMetaData" />.
124: : /// </summary>
125: : /// <param name="method">
126: : /// A <see cref="MethodDefinition" /> for the method on which mutation
127: : /// testing is to be carried out.
128: : /// </param>
129: : /// <param name="module">
130: : /// A <see cref="Module" /> representing the main module of the
131: : /// containing assembly.
132: : /// </param>
133: : /// <param name="description">
134: : /// A description of the mutation that has been applied.
135: : /// </param>
136: : /// <param name="index">
137: : /// The index of the (first) IL instruction at which the mutation was
138: : /// applied.
139: : /// </param>
140: : /// <returns></returns>
141: : protected MutantMetaData DoYield(MethodDefinition method, Module module, string description, int index)
142: : {
143: 1/1: var testDirectory = new TestDirectory(Path.GetDirectoryName(module.AssemblyLocation));
144: 1/1: testDirectory.SaveAssembly(module);
145: 7/7: return new MutantMetaData
146: 7/7: {
147: 7/7: Description = description,
148: 7/7: MethodDefinition = method,
149: 7/7: TestDirectory = testDirectory,
150: 7/7: ILIndex = index
151: 7/7: };
152: : }
153: :
154: : internal int GetOriginalOffset(int index)
155: : {
156: : return _originalOffsets[index];
157: : }
158: :
159: : internal string GetOriginalSourceFileName(int index)
160: : {
161: : var sequencePoint = GetCurrentSequencePoint(index);
162: : return Path.GetFileName(sequencePoint.Document.Url);
163: : }
164: :
165: : internal SequencePoint GetCurrentSequencePoint(int index)
166: : {
167: : var instruction = _method.Body.Instructions[index];
168: : while ((instruction.SequencePoint == null
169: : || instruction.SequencePoint.StartLine == 0xfeefee) && index > 0)
170: : {
171: : index--;
172: : instruction = _method.Body.Instructions[index];
173: : }
174: : var sequencePoint = instruction.SequencePoint;
175: : return sequencePoint;
176: : }
177: :
178: : internal string GetOriginalSourceCode(int index)
179: : {
180: : var sequencePoint = GetCurrentSequencePoint(index);
181: : string result = "";
182: : if (!_module.SourceFiles.ContainsKey(sequencePoint.Document.Url))
183: : {
184: : return "";
185: : }
186: : string[] sourceCode = _module.SourceFiles[sequencePoint.Document.Url];
187: : int upperBound = Math.Min(sequencePoint.EndLine + 2, sourceCode.Length);
188: : for (int line = Math.Max(sequencePoint.StartLine - 2, 1); line <= upperBound; line++)
189: : {
190: : string sourceLine = sourceCode[line - 1].Replace("\t", " ");
191: : result += line.ToString(CultureInfo.InvariantCulture)
192: : .PadLeft(4, ' ') + ": " + sourceLine.TrimEnd(' ', '\t');
193: : if (line < upperBound) result += Environment.NewLine;
194: : }
195: : return result;
196: : }
197: : }
198: : }
Module.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System.Collections.Generic;
23: : using System.IO;
24: : using System.Linq;
25: :
26: : using Mono.Cecil;
27: : using Mono.Cecil.Cil;
28: : using Mono.Cecil.Mdb;
29: : using Mono.Cecil.Pdb;
30: : using Mono.Collections.Generic;
31: :
32: : namespace NinjaTurtles
33: : {
34: : /// <summary>
35: : /// Class representing the main module of a .NET assembly.
36: : /// </summary>
37: : public class Module
38: : {
39: : /// <summary>
40: : /// Initializes a new instance of the <see cref="Module" /> class.
41: : /// </summary>
42: : /// <param name="assemblyLocation">
43: : /// The location on disk of the assembly whose main module is to be
44: : /// loaded.
45: : /// </param>
46: : public Module(string assemblyLocation)
47: : {
48: : AssemblyLocation = assemblyLocation;
49: : AssemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyLocation);
50: : Definition = AssemblyDefinition.MainModule;
51: : SourceFiles = new Dictionary<string, string[]>();
52: : }
53: :
54: : /// <summary>
55: : /// Gets the location on disk of the assembly.
56: : /// </summary>
57: : public string AssemblyLocation { get; private set; }
58: :
59: : /// <summary>
60: : /// Gets the <see cref="AssemblyDefinition" />.
61: : /// </summary>
62: : public AssemblyDefinition AssemblyDefinition { get; private set; }
63: :
64: : /// <summary>
65: : /// Gets the <see cref="ModuleDefinition" />.
66: : /// </summary>
67: : public ModuleDefinition Definition { get; private set; }
68: :
69: : /// <summary>
70: : /// Gets a dictionary of source code files with their contained lines
71: : /// of code.
72: : /// </summary>
73: : public IDictionary<string, string[]> SourceFiles { get; private set; }
74: :
75: : internal void LoadDebugInformation()
76: : {
77: 1/1: var reader = ResolveSymbolReader();
78: 6/6: if (reader == null) return;
79: :
80: 1/1: Definition.ReadSymbols(reader);
81: :
82: 1/1: LoadSourceCodeForTypes(Definition.Types, reader);
83: :
84: 19/19: foreach (var method in Definition.Types
85: 8/8: .SelectMany(t => t.Methods)
86: 8/8: .Where(m => m.HasBody))
87: : {
88: 1/1: MethodDefinition capturedMethod = method;
89: 1/1: reader.Read(capturedMethod.Body,
90: 1/1: o => capturedMethod.Body.Instructions.FirstOrDefault(i => i.Offset >= o));
91: :
92: 7/7: var sourceFiles = method.Body.Instructions.Where(i => i.SequencePoint != null)
93: 7/7: .Select(i => i.SequencePoint.Document.Url)
94: 7/7: .Distinct();
95: 12/12: foreach (var sourceFile in sourceFiles)
96: : {
97: 8/8: if (!SourceFiles.ContainsKey(sourceFile) && File.Exists(sourceFile))
98: : {
99: 1/1: SourceFiles.Add(sourceFile, File.ReadAllLines(sourceFile));
100: : }
101: : }
102: : }
103: : }
104: :
105: : private void LoadSourceCodeForTypes(Collection<TypeDefinition> types, ISymbolReader reader)
106: : {
107: : foreach (var typeDefinition in types)
108: : {
109: : foreach (var method in typeDefinition.Methods.Where(m => m.HasBody))
110: : {
111: : MethodDefinition capturedMethod = method;
112: : reader.Read(capturedMethod.Body,
113: : o => capturedMethod.Body.Instructions.FirstOrDefault(i => i.Offset >= o));
114: :
115: : var sourceFiles = method.Body.Instructions.Where(i => i.SequencePoint != null)
116: : .Select(i => i.SequencePoint.Document.Url)
117: : .Distinct();
118: : foreach (var sourceFile in sourceFiles)
119: : {
120: : if (!SourceFiles.ContainsKey(sourceFile) && File.Exists(sourceFile))
121: : {
122: : SourceFiles.Add(sourceFile, File.ReadAllLines(sourceFile));
123: : }
124: : }
125: : }
126: : if (typeDefinition.NestedTypes != null)
127: : {
128: : LoadSourceCodeForTypes(typeDefinition.NestedTypes, reader);
129: : }
130: : }
131: : }
132: :
133: : private ISymbolReader ResolveSymbolReader()
134: : {
135: : string symbolLocation = null;
136: : string pdbLocation = Path.ChangeExtension(AssemblyLocation, "pdb");
137: : string mdbLocation = AssemblyLocation + ".mdb";
138: : ISymbolReaderProvider provider = null;
139: : if (File.Exists(pdbLocation))
140: : {
141: : symbolLocation = pdbLocation;
142: : provider = new PdbReaderProvider();
143: : }
144: : else if (File.Exists(mdbLocation))
145: : {
146: : symbolLocation = AssemblyLocation;
147: : provider = new MdbReaderProvider();
148: : }
149: : if (provider == null) return null;
150: : var reader = provider.GetSymbolReader(Definition, symbolLocation);
151: : return reader;
152: : }
153: : }
154: : }
MutationTest.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Collections.Generic;
24: : using System.Diagnostics;
25: : using System.IO;
26: : using System.Linq;
27: : using System.Management;
28: : using System.Text.RegularExpressions;
29: : using System.Threading;
30: : using System.Threading.Tasks;
31: : using System.Xml.Serialization;
32: :
33: : using Microsoft.Win32;
34: :
35: : using Mono.Cecil;
36: : using Mono.Cecil.Cil;
37: : using Mono.Collections.Generic;
38: :
39: : using NinjaTurtles.Reporting;
40: : using NinjaTurtles.TestRunners;
41: : using NinjaTurtles.Turtles;
42: :
43: : namespace NinjaTurtles
44: : {
45: : internal class MutationTest : IMutationTest
46: : {
47: : private const string ERROR_REPORTING_KEY = @"SOFTWARE\Microsoft\Windows\Windows Error Reporting";
48: : private const string ERROR_REPORTING_VALUE = "DontShowUI";
49: :
50: : private readonly IList<Type> _mutationsToApply = new List<Type>();
51: : private readonly string _testAssemblyLocation;
52: : private readonly Type[] _parameterTypes;
53: : private readonly AssemblyDefinition _testAssembly;
54: : private readonly TypeReference _targetTypeReference;
55: : private Module _module;
56: : private IEnumerable<string> _testsToRun;
57: : private ITestRunner _runner;
58: : private MutationTestingReport _report;
59: : private ReportingStrategy _reportingStrategy = new NullReportingStrategy();
60: : private string _reportFileName;
61: : private MethodReferenceComparer _comparer;
62: : private static Regex _automaticallyGeneratedNestedClassMatcher = new Regex("^\\<([A-Za-z0-9@_]+)\\>");
63: : //private TimeSpan _benchmark;
64: :
65: : internal MutationTest(string testAssemblyLocation, Type targetType, string targetMethod, Type[] parameterTypes)
66: : {
67: : TargetType = targetType;
68: : TargetMethod = targetMethod;
69: : _testAssemblyLocation = testAssemblyLocation;
70: : _parameterTypes = parameterTypes;
71: : _testAssembly = AssemblyDefinition.ReadAssembly(testAssemblyLocation);
72: : _targetTypeReference = _testAssembly.MainModule.Import(targetType);
73: : }
74: :
75: : public Type TargetType { get; private set; }
76: :
77: : public string TargetMethod { get; private set; }
78: :
79: : public void Run()
80: : {
81: 1/1: var key = Registry.LocalMachine.OpenSubKey(ERROR_REPORTING_KEY, RegistryKeyPermissionCheck.ReadWriteSubTree);
82: 1/1: var errorReportingValue = key.GetValue(ERROR_REPORTING_VALUE, null);
83: 1/1: key.SetValue(ERROR_REPORTING_VALUE, 1, RegistryValueKind.DWord);
84: 1/1: key.Close();
85: :
86: 1/1: MethodDefinition method = ValidateMethod();
87: 1/1: _module.LoadDebugInformation();
88: :
89: 1/1: _comparer = new MethodReferenceComparer();
90: 1/1: var matchingMethods = new List<MethodReference>();
91: 1/1: AddMethod(method, matchingMethods);
92: :
93: 4/4: int[] originalOffsets = method.Body.Instructions.Select(i => i.Offset).ToArray();
94: 1/1: _report = new MutationTestingReport();
95: 1/1: _testsToRun = GetMatchingTestsFromTree(method, matchingMethods);
96: :
97: : //_benchmark = BenchmarkTestSuite();
98: :
99: 1/1: int count = 0;
100: 1/1: int failures = 0;
101: 6/6: if (_mutationsToApply.Count == 0) PopulateDefaultTurtles();
102: 13/13: foreach (var turtleType in _mutationsToApply)
103: : {
104: 1/1: var turtle = (MethodTurtleBase)Activator.CreateInstance(turtleType);
105: 1/1: Parallel.ForEach(turtle.Mutate(method, _module, originalOffsets),
106: 1/1: mutation => RunMutation(turtle, mutation, ref failures, ref count));
107: : }
108: :
109: 1/1: _report.RegisterMethod(method);
110: 1/1: _reportingStrategy.WriteReport(_report, _reportFileName);
111: :
112: 1/1: key = Registry.LocalMachine.OpenSubKey(ERROR_REPORTING_KEY, RegistryKeyPermissionCheck.ReadWriteSubTree);
113: 5/5: if (errorReportingValue == null)
114: : {
115: 1/1: key.DeleteValue(ERROR_REPORTING_VALUE);
116: 1/1: }
117: : else
118: : {
119: 1/1: key.SetValue(ERROR_REPORTING_VALUE, errorReportingValue, RegistryValueKind.DWord);
120: : }
121: 1/1: key.Close();
122: :
123: 5/5: if (count == 0)
124: : {
125: 1/1: Console.WriteLine("No valid mutations found (this is fine).");
126: 1/1: return;
127: : }
128: 6/6: if (failures > 0)
129: : {
130: 1/1: throw new MutationTestFailureException();
131: : }
132: : }
133: :
134: : private void AddMethod(MethodDefinition targetMethod, List<MethodReference> matchingMethods)
135: : {
136: : if (matchingMethods.Contains(targetMethod, _comparer)) return;
137: : matchingMethods.Add(targetMethod);
138: : AddCallingMethods(targetMethod, matchingMethods);
139: : var declaringType = targetMethod.DeclaringType;
140: : TypeDefinition type = declaringType;
141: : while (type != null && type.BaseType != null)
142: : {
143: : var thisModule = type.BaseType.Module;
144: : if (type.BaseType.Scope.Name != type.BaseType.Module.Assembly.Name.Name
145: : && type.BaseType.Scope is AssemblyNameReference)
146: : {
147: : thisModule =
148: : type.BaseType.Module.AssemblyResolver.Resolve((AssemblyNameReference)type.BaseType.Scope).
149: : MainModule;
150: : }
151: : type = thisModule.Types.SingleOrDefault(t => t.FullName == type.BaseType.FullName);
152: : if (type != null)
153: : {
154: : var baseMethod = type.Methods
155: : .SingleOrDefault(m => MethodsMatch(m, targetMethod));
156: : if (baseMethod != null)
157: : {
158: : AddMethod(baseMethod, matchingMethods);
159: : break;
160: : }
161: : AddMethodsForInterfaces(targetMethod, matchingMethods, type);
162: : }
163: : }
164: : AddMethodsForInterfaces(targetMethod, matchingMethods, declaringType);
165: : }
166: :
167: : private void AddMethodsForInterfaces(MethodDefinition targetMethod, List<MethodReference> matchingMethods, TypeDefinition type)
168: : {
169: : foreach (var interfaceReference in type.Interfaces)
170: : {
171: : var thisModule = type.Module;
172: : if (interfaceReference.Scope.Name != type.Module.Assembly.Name.Name
173: : && interfaceReference.Scope is AssemblyNameReference)
174: : {
175: : thisModule =
176: : interfaceReference.Module.AssemblyResolver.Resolve((AssemblyNameReference)interfaceReference.Scope).
177: : MainModule;
178: : }
179: : var interfaceDefinition =
180: : thisModule.Types.SingleOrDefault(t => t.FullName == interfaceReference.FullName);
181: : if (interfaceDefinition != null)
182: : {
183: : var interfaceMethod = interfaceDefinition.Methods
184: : .SingleOrDefault(m => MethodsMatch(m, targetMethod));
185: : if (interfaceMethod != null)
186: : {
187: : AddMethod(interfaceMethod, matchingMethods);
188: : }
189: : }
190: : }
191: : }
192: :
193: : private bool MethodsMatch(MethodDefinition first, MethodDefinition second)
194: : {
195: 18/18: return first.Name == second.Name
196: 18/18: && Enumerable.SequenceEqual(first.Parameters.Select(p => p.ParameterType.Name),
197: 18/18: second.Parameters.Select(p => p.ParameterType.Name))
198: 18/18: && first.GenericParameters.Count == second.GenericParameters.Count;
199: : }
200: :
201: : private ISet<string> GetMatchingTestsFromTree(MethodDefinition targetmethod, IList<MethodReference> matchingMethods, bool force = false)
202: : {
203: 1/1: ISet<string> result = new HashSet<string>();
204: 10/10: foreach (var type in _testAssembly.MainModule.Types)
205: 2/2: AddTestsForType(targetmethod, matchingMethods, force, type, result);
206: 10/10: if (!force && result.Count == 0)
207: : {
208: 1/1: result = GetMatchingTestsFromTree(targetmethod, matchingMethods, true);
209: : }
210: 6/6: if (result.Count == 0)
211: : {
212: 1/1: throw new MutationTestFailureException(
213: 1/1: "No matching tests were found to run.");
214: : }
215: : return result;
216: : }
217: :
218: : private void AddTestsForType(MethodDefinition targetmethod, IList<MethodReference> matchingMethods, bool force, TypeDefinition type,
219: : ISet<string> result)
220: : {
221: : foreach (var method in type.Methods.Where(m => m.HasBody))
222: : {
223: : var targetType = targetmethod.DeclaringType.FullName;
224: : if (!force && !DoesMethodReferenceType(method, targetType)) continue;
225: : foreach (var instruction in method.Body.Instructions)
226: : {
227: : if (instruction.OpCode == OpCodes.Call
228: : || instruction.OpCode == OpCodes.Callvirt
229: : || instruction.OpCode == OpCodes.Newobj
230: : || instruction.OpCode == OpCodes.Ldftn)
231: : {
232: : var reference = (MethodReference)instruction.Operand;
233: : if (matchingMethods.Any(m => _comparer.Equals(m, reference))
234: : && !method.CustomAttributes.Any(a => a.AttributeType.Name == "MutationTestAttribute"))
235: : {
236: : result.Add(string.Format("{0}.{1}", type.FullName.Replace("/", "+"), method.Name));
237: : break;
238: : }
239: : }
240: : }
241: : }
242: : if (type.NestedTypes != null)
243: : {
244: : foreach (var typeDefinition in type.NestedTypes)
245: : {
246: : AddTestsForType(targetmethod, matchingMethods, force, typeDefinition, result);
247: : }
248: : }
249: : }
250: :
251: : private static bool DoesMethodReferenceType(MethodDefinition method, string targetType)
252: : {
253: 1/1: bool typeUsed = false;
254: 13/13: foreach (var instruction in method.Body.Instructions)
255: : {
256: 16/16: if (instruction.OpCode == OpCodes.Call
257: 16/16: || instruction.OpCode == OpCodes.Callvirt
258: 16/16: || instruction.OpCode == OpCodes.Newobj
259: 16/16: || instruction.OpCode == OpCodes.Ldftn)
260: : {
261: 1/1: var reference = (MethodReference)instruction.Operand;
262: 1/1: var declaringType = reference.DeclaringType;
263: 9/9: if (declaringType.FullName == targetType)
264: : {
265: 3/3: typeUsed = true;
266: 1/1: break;
267: : }
268: 1/1: var genericType = declaringType as GenericInstanceType;
269: 9/9: if (genericType != null)
270: : {
271: 12/12: if (genericType.GenericArguments.Any(a => a.FullName == targetType))
272: : {
273: 3/3: typeUsed = true;
274: 1/1: break;
275: : }
276: : }
277: 1/1: var genericMethod = reference as GenericInstanceMethod;
278: 9/9: if (genericMethod != null)
279: : {
280: 12/12: if (genericMethod.GenericArguments.Any(a => a.FullName == targetType))
281: : {
282: 3/3: typeUsed = true;
283: 1/1: break;
284: : }
285: : }
286: : }
287: 9/9: if (instruction.OpCode == OpCodes.Newobj)
288: : {
289: 1/1: var constructor = (MemberReference)instruction.Operand;
290: 1/1: var declaringType = constructor.DeclaringType;
291: 9/9: if (declaringType.FullName == targetType)
292: : {
293: 3/3: typeUsed = true;
294: 1/1: break;
295: : }
296: : }
297: : }
298: 4/4: return typeUsed;
299: : }
300: :
301: : private void AddCallingMethods(MethodReference targetMethod, List<MethodReference> matchingMethods)
302: : {
303: 9/9: foreach (var type in _module.Definition.Types)
304: 1/1: AddCallingMethodsForType(targetMethod, matchingMethods, type);
305: : }
306: :
307: : private void AddCallingMethodsForType(MethodReference targetMethod, List<MethodReference> matchingMethods, TypeDefinition type)
308: : {
309: 16/16: foreach (var method in type.Methods.Where(m => m.HasBody))
310: 9/9: foreach (var instruction in method.Body.Instructions)
311: : {
312: 14/14: if (instruction.OpCode == OpCodes.Call
313: 14/14: || instruction.OpCode == OpCodes.Callvirt
314: 14/14: || instruction.OpCode == OpCodes.Newobj
315: 14/14: || instruction.OpCode == OpCodes.Ldftn)
316: : {
317: 1/1: var reference = (MethodReference)instruction.Operand;
318: 10/10: if (_comparer.Equals(reference, targetMethod)
319: 10/10: && !matchingMethods.Contains(method, _comparer))
320: : {
321: 1/1: AddMethod(method, matchingMethods);
322: 2/2: var match = _automaticallyGeneratedNestedClassMatcher.Match(type.Name);
323: 9/9: if (match.Success && type.DeclaringType != null)
324: : {
325: 9/9: if (type.DeclaringType.Methods.Any(m => m.Name == match.Groups[1].Value))
326: : {
327: 5/5: AddMethod(type.DeclaringType.Methods.First(m => m.Name == match.Groups[1].Value), matchingMethods);
328: : }
329: : }
330: : }
331: : }
332: : }
333: 10/10: foreach (var nestedType in type.NestedTypes)
334: : {
335: 3/3: AddCallingMethodsForType(targetMethod, matchingMethods, nestedType);
336: : }
337: : }
338: :
339: : private class MethodReferenceComparer : IEqualityComparer<MethodReference>
340: : {
341: : public bool Equals(MethodReference x, MethodReference y)
342: : {
343: : if (x.Name != y.Name) return false;
344: : return x.DeclaringType.FullName == y.DeclaringType.FullName
345: : && Enumerable.SequenceEqual(x.Parameters.Select(p => p.ParameterType.Name),
346: : y.Parameters.Select(p => p.ParameterType.Name))
347: : && x.GenericParameters.Count == y.GenericParameters.Count;
348: : }
349: :
350: : public int GetHashCode(MethodReference obj)
351: : {
352: : return obj.Name.GetHashCode();
353: : }
354: : }
355: :
356: : private void RunMutation(MethodTurtleBase turtle, MutantMetaData mutation, ref int failures, ref int count)
357: : {
358: 2/2: bool testProcessFailed = CheckTestProcessFails(turtle, mutation);
359: 8/8: if (!testProcessFailed)
360: : {
361: 1/1: Interlocked.Increment(ref failures);
362: : }
363: 1/1: Interlocked.Increment(ref count);
364: : }
365: :
366: : // private TimeSpan BenchmarkTestSuite()
367: : // {
368: : // var testDirectory = new TestDirectory(Path.GetDirectoryName(_testAssemblyLocation));
369: : //
370: : // var process = GetTestRunnerProcess(testDirectory);
371: : //
372: : // var stopwatch = new Stopwatch();
373: : // stopwatch.Start();
374: : // process.Start();
375: : // process.WaitForExit();
376: : // stopwatch.Stop();
377: : //
378: : // testDirectory.Dispose();
379: : //
380: : // if (process.ExitCode != 0)
381: : // {
382: : // throw new MutationTestFailureException("Test suite does not pass with unmutated code - mutation testing aborted.");
383: : // }
384: : // var timespan = stopwatch.Elapsed;
385: : // Console.WriteLine("Test suite benchmarked at {0:0.000} seconds.", timespan.TotalSeconds);
386: : //
387: : // return timespan;
388: : // }
389: :
390: : private Process GetTestRunnerProcess(TestDirectory testDirectory)
391: : {
392: : if (_runner == null) _runner = (ITestRunner)Activator.CreateInstance(MutationTestBuilder.TestRunner);
393: : return _runner.GetRunnerProcess(testDirectory, _testAssemblyLocation, _testsToRun);
394: : }
395: :
396: : private bool CheckTestProcessFails(MethodTurtleBase turtle, MutantMetaData mutation)
397: : {
398: 1/1: var process = GetTestRunnerProcess(mutation.TestDirectory);
399: :
400: 1/1: process.Start();
401: 4/4: bool exitedInTime = process.WaitForExit(30000); //Math.Min(30000, (int)(5 * _benchmark.TotalMilliseconds)));
402: 1/1: int exitCode = -1;
403: :
404: : try
405: : {
406: 14/14: if (!exitedInTime)
407: : {
408: 1/1: KillProcessAndChildren(process.Id);
409: : }
410: 1/1: exitCode = process.ExitCode;
411: 1/1: }
412: 2/2: catch {}
413: :
414: 7/7: bool testSuitePassed = exitCode == 0 && exitedInTime;
415: :
416: 7/7: string result = string.Format("Mutant: {0}. {1}",
417: 7/7: mutation.Description,
418: 7/7: testSuitePassed
419: 7/7: ? "Survived."
420: 7/7: : "Killed.");
421: 4/4: _report.AddResult(turtle.GetCurrentSequencePoint(mutation.ILIndex), mutation, !testSuitePassed);
422: :
423: 14/14: if (testSuitePassed)
424: : {
425: 1/1: mutation.TestDirectory.DoNotDelete = true;
426: 1/1: result = string.Format("{0}\nOriginal source code around surviving mutant (in {1}):\n{2}\nFiles left for inspection in: {3}",
427: 1/1: result,
428: 1/1: turtle.GetOriginalSourceFileName(mutation.ILIndex),
429: 1/1: turtle.GetOriginalSourceCode(mutation.ILIndex),
430: 1/1: mutation.TestDirectoryName);
431: : }
432: :
433: 1/1: Console.WriteLine(result);
434: :
435: 1/1: turtle.MutantComplete(mutation);
436: 6/6: return !testSuitePassed;
437: : }
438: :
439: : private void KillProcessAndChildren(int pid)
440: : {
441: : using (var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid))
442: : using (ManagementObjectCollection moc = searcher.Get())
443: : {
444: : foreach (ManagementObject mo in moc)
445: : {
446: : KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
447: : }
448: : try
449: : {
450: : Process proc = Process.GetProcessById(pid);
451: : proc.Kill();
452: : }
453: : catch (ArgumentException) {}
454: : }
455: : }
456: :
457: :
458: : private void PopulateDefaultTurtles()
459: : {
460: : foreach (var type in GetType().Assembly.GetTypes()
461: : .Where(t => t.GetInterface("IMethodTurtle") != null
462: : && !t.IsAbstract))
463: : {
464: : _mutationsToApply.Add(type);
465: : }
466: : }
467: :
468: : private MethodDefinition ValidateMethod()
469: : {
470: : _module = new Module(TargetType.Assembly.Location);
471: :
472: : var type = ResolveFromTypeCollection(_module.Definition.Types);
473: : return MethodDefinitionResolver.ResolveMethod(type, TargetMethod, _parameterTypes);
474: : }
475: :
476: : private TypeDefinition ResolveFromTypeCollection(Collection<TypeDefinition> types)
477: : {
478: : var type = types.SingleOrDefault(t => t.FullName == TargetType.FullName.Replace("+", "/"));
479: : if (type == null)
480: : {
481: : foreach (var typeDefinition in types)
482: : {
483: : if (typeDefinition.NestedTypes != null)
484: : {
485: : type = ResolveFromTypeCollection(typeDefinition.NestedTypes);
486: : if (type != null) return type;
487: : }
488: : }
489: : }
490: : return type;
491: : }
492: :
493: : public IMutationTest With<T>() where T : IMethodTurtle
494: : {
495: : _mutationsToApply.Add(typeof(T));
496: : return this;
497: : }
498: :
499: : /// <summary>
500: : /// Sets the unit test runner to be used, which is an implementation of
501: : /// <see cref="ITestRunner" />. If none is specified, then the default
502: : /// is to use <see cref="NUnitTestRunner" />.
503: : /// </summary>
504: : /// <typeparam name="T">
505: : /// A type that implements <see cref="ITestRunner" />.
506: : /// </typeparam>
507: : /// <returns>
508: : /// The original <see cref="IMutationTest" /> instance to allow fluent
509: : /// method chaining.
510: : /// </returns>
511: : public IMutationTest UsingRunner<T>() where T : ITestRunner, new()
512: : {
513: : _runner = new T();
514: : return this;
515: : }
516: :
517: : public IMutationTest WriteReportTo(string fileName)
518: : {
519: : _reportingStrategy = new OverwriteReportingStrategy();
520: : _reportFileName = fileName;
521: : return this;
522: : }
523: :
524: : public IMutationTest MergeReportTo(string fileName)
525: : {
526: : _reportingStrategy = new MergeReportingStrategy();
527: : _reportFileName = fileName;
528: : return this;
529: : }
530: :
531: : private abstract class ReportingStrategy
532: : {
533: : public abstract void WriteReport(MutationTestingReport report, string fileName);
534: : }
535: :
536: : private class NullReportingStrategy : ReportingStrategy
537: : {
538: : public override void WriteReport(MutationTestingReport report, string fileName) { }
539: : }
540: :
541: : private class OverwriteReportingStrategy : ReportingStrategy
542: : {
543: : public override void WriteReport(MutationTestingReport report, string fileName)
544: : {
545: : using (var streamWriter = File.CreateText(fileName))
546: : {
547: : new XmlSerializer(typeof(MutationTestingReport)).Serialize(streamWriter, report);
548: : }
549: : }
550: : }
551: :
552: : private class MergeReportingStrategy : ReportingStrategy
553: : {
554: : public override void WriteReport(MutationTestingReport report, string fileName)
555: : {
556: : report.MergeFromFile(fileName);
557: : using (var streamWriter = File.CreateText(fileName))
558: : {
559: : new XmlSerializer(typeof(MutationTestingReport)).Serialize(streamWriter, report);
560: : }
561: : }
562: : }
563: : }
564: : }
565: :
MutationTestBuilder.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Reflection;
24: :
25: : using NinjaTurtles.TestRunners;
26: :
27: : namespace NinjaTurtles
28: : {
29: : /// <summary>
30: : /// A static class used as the starting point for a fluent definition of
31: : /// a set of mutation tests.
32: : /// </summary>
33: : /// <typeparam name="T">
34: : /// The type to be tested.
35: : /// </typeparam>
36: : /// <example>
37: : /// <para>
38: : /// This code creates and runs mutation tests for the
39: : /// <b>ClassUnderTest</b> class's <b>MethodUnderTest</b> method:
40: : /// </para>
41: : /// <code lang="cs">
42: : /// MutationTestBuilder<ClassUnderTest>
43: : /// .For("MethodUnderTest")
44: : /// .Run();
45: : /// </code>
46: : /// <code lang="vbnet">
47: : /// Call MutationTestBuilder(Of ClassUnderTest) _
48: : /// .For("MethodUnderTest") _
49: : /// .Run
50: : /// </code>
51: : /// <code lang="cpp">
52: : /// MutationTestBuilder<ClassUnderTest^>
53: : /// ::For("MethodUnderTest")
54: : /// ->Run();
55: : /// </code>
56: : /// <para>
57: : /// When this code is included in a test, it causes the matching tests to
58: : /// be run for each mutation that is found of the code under test. The
59: : /// matching tests are determined automatically by NinjaTurtles. By
60: : /// default, NinjaTurtles assumes it is running under NUnit, and thus uses
61: : /// an NUnit runner to run the suite against the mutated code. This can be
62: : /// changed using the fluent interface:
63: : /// </para>
64: : /// <code lang="cs">
65: : /// MutationTestBuilder<ClassUnderTest>
66: : /// .For("MethodUnderTest")
67: : /// .UsingRunner<GallioTestRunner>()
68: : /// .Run();
69: : /// </code>
70: : /// <code lang="vbnet">
71: : /// Call MutationTestBuilder(Of ClassUnderTest) _
72: : /// .For("MethodUnderTest") _
73: : /// .UsingRunner(Of GallioTestRunner)() _
74: : /// .Run
75: : /// </code>
76: : /// <code lang="cpp">
77: : /// MutationTestBuilder<ClassUnderTest^>
78: : /// ::For("MethodUnderTest")
79: : /// ->UsingRunner<GallioTestRunner^>()
80: : /// ->Run();
81: : /// </code>
82: : /// <para>
83: : /// Alternatively, this option can be set across all tests in a fixture by
84: : /// including this line in the test fixture's setup method:
85: : /// </para>
86: : /// <code lang="cs">
87: : /// MutationTestBuilder.UseRunner<GallioTestRunner>();
88: : /// </code>
89: : /// <code lang="vbnet">
90: : /// Call MutationTestBuilder.UseRunner(Of GallioTestRunner)
91: : /// </code>
92: : /// <code lang="cpp">
93: : /// MutationTestBuilder::UseRunner<GallioTestRunner^>();
94: : /// </code>
95: : /// </example>
96: : public sealed class MutationTestBuilder<T>
97: : {
98: : /// <summary>
99: : /// Returns an <see cref="IMutationTest" /> instance allowing a fluent
100: : /// definition of a set of mutation tests for a particular method.
101: : /// </summary>
102: : /// <param name="targetMethod">
103: : /// The name of the method to mutate.
104: : /// </param>
105: : /// <param name="parameterTypes">
106: : /// Optional parameter specifying an array of parameter types used to
107: : /// identify a particular method overload.
108: : /// </param>
109: : /// <returns>
110: : /// An <see cref="IMutationTest" /> instance to allow fluent
111: : /// method chaining.
112: : /// </returns>
113: : public static IMutationTest For(string targetMethod, Type[] parameterTypes = null)
114: : {
115: : var callingAssembly = Assembly.GetCallingAssembly();
116: : return MutationTestBuilder.For(callingAssembly.Location, typeof(T), targetMethod, parameterTypes);
117: : }
118: : }
119: :
120: : /// <summary>
121: : /// A static class used as the starting point for a fluent definition of
122: : /// a set of mutation tests.
123: : /// </summary>
124: : /// <remarks>
125: : /// For public classes, the generic <see cref="MutationTestBuilder{T}" />
126: : /// is to be prefered. See that class for full documentation.
127: : /// </remarks>
128: : public sealed class MutationTestBuilder
129: : {
130: : internal static Type TestRunner { get; set; }
131: :
132: : static MutationTestBuilder()
133: : {
134: : TestRunner = typeof(NUnitTestRunner);
135: : }
136: :
137: : /// <summary>
138: : /// Returns an <see cref="IMutationTest" /> instance allowing a fluent
139: : /// definition of a set of mutation tests for a particular method.
140: : /// </summary>
141: : /// <param name="targetClass">
142: : /// The namespace-qualified name of the type for which mutation tests
143: : /// are being defined.
144: : /// </param>
145: : /// <param name="targetMethod">
146: : /// The name of the method to mutate.
147: : /// </param>
148: : /// <param name="parameterTypes">
149: : /// Optional parameter specifying an array of parameter types used to
150: : /// identify a particular method overload.
151: : /// </param>
152: : /// <returns>
153: : /// An <see cref="IMutationTest" /> instance to allow fluent
154: : /// method chaining.
155: : /// </returns>
156: : public static IMutationTest For(string targetClass, string targetMethod, Type[] parameterTypes = null)
157: : {
158: 1/1: var callingAssembly = Assembly.GetCallingAssembly();
159: 2/2: Type resolvedType = TypeResolver.ResolveTypeFromReferences(callingAssembly, targetClass);
160: :
161: 1/1: return For(callingAssembly.Location, resolvedType, targetMethod, parameterTypes);
162: : }
163: :
164: : internal static IMutationTest For(string callingAssemblyLocation, Type targetType, string targetMethod, Type[] parameterTypes)
165: : {
166: : return new MutationTest(callingAssemblyLocation, targetType, targetMethod, parameterTypes);
167: : }
168: :
169: : /// <summary>
170: : /// Specifies the implementation of <see cref="ITestRunner" /> to be
171: : /// used to run the test suite for each mutant. By default, this will
172: : /// be the <see cref="NUnitTestRunner" />. This can still be overridden
173: : /// on a per-test basis using the
174: : /// <see mref="IMutationTest.UsingRunner" /> method.
175: : /// </summary>
176: : /// <typeparam name="T">
177: : /// A type that implements <see cref="ITestRunner" />.
178: : /// </typeparam>
179: : public static void UseRunner<T>() where T : ITestRunner
180: : {
181: : TestRunner = typeof(T);
182: : }
183: : }
184: : }
185: :
OpCodeRotationTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System.Collections.Generic;
23: :
24: : using Mono.Cecil;
25: : using Mono.Cecil.Cil;
26: :
27: : namespace NinjaTurtles.Turtles
28: : {
29: : /// <summary>
30: : /// An abstract base class for implementations of
31: : /// <see cref="IMethodTurtle" /> that operator by replacing a number of
32: : /// IL OpCodes with a list of replacements in turn.
33: : /// </summary>
34: : /// <remarks>
35: : /// Classes extending this one only need to set the value of the
36: : /// <see fref="_opCodes" /> field to an appropriate dictionary of source
37: : /// and target OpCodes.
38: : /// </remarks>
39: : public abstract class OpCodeRotationTurtle : MethodTurtleBase
40: : {
41: : /// <summary>
42: : /// An <see cref="IDictionary{K,V}" /> containing source OpCodes as
43: : /// keys, and <see cref="IEnumerable{T}" />s of OpCodes as each key's
44: : /// possible replacements.
45: : /// </summary>
46: : protected IDictionary<OpCode, IEnumerable<OpCode>> _opCodes;
47: :
48: : /// <summary>
49: : /// Performs the actual code mutations, returning each with
50: : /// <c>yield</c> for the calling code to use.
51: : /// </summary>
52: : /// <remarks>
53: : /// Implementing classes should yield the result obtained by calling
54: : /// the <see mref="DoYield" /> method.
55: : /// </remarks>
56: : /// <param name="method">
57: : /// A <see cref="MethodDefinition" /> for the method on which mutation
58: : /// testing is to be carried out.
59: : /// </param>
60: : /// <param name="module">
61: : /// A <see cref="Module" /> representing the main module of the
62: : /// containing assembly.
63: : /// </param>
64: : /// <returns>
65: : /// An <see cref="IEnumerable{T}" /> of
66: : /// <see cref="MutantMetaData" /> structures.
67: : /// </returns>
68: : protected override IEnumerable<MutantMetaData> DoMutate(MethodDefinition method, Module module)
69: : {
70: 16/16: for (int index = 0; index < method.Body.Instructions.Count; index++)
71: : {
72: 2/2: var instruction = method.Body.Instructions[index];
73: 7/7: if (_opCodes.ContainsKey(instruction.OpCode))
74: : {
75: 8/8: if (instruction.IsMeaninglessUnconditionalBranch()) continue;
76: :
77: 2/2: var originalOpCode = instruction.OpCode;
78: :
79: 12/12: foreach (var opCode in _opCodes[originalOpCode])
80: : {
81: 2/2: instruction.OpCode = opCode;
82: 5/5: var description = string.Format("{0:x4}: {1} => {2}", GetOriginalOffset(index), originalOpCode.Code, opCode.Code);
83: 2/2: MutantMetaData mutation = DoYield(method, module, description, index);
84: 4/4: yield return mutation;
85: : }
86: :
87: 2/2: instruction.OpCode = originalOpCode;
88: : }
89: : }
90: : }
91: : }
92: : }
SequencePointDeletionTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System.Collections.Generic;
23: : using System.Linq;
24: :
25: : using Mono.Cecil;
26: : using Mono.Cecil.Cil;
27: :
28: : namespace NinjaTurtles.Turtles
29: : {
30: : /// <summary>
31: : /// An implementation of <see cref="IMethodTurtle" /> that removes from the
32: : /// compiled IL each sequence point in turn (with the exception of
33: : /// structurally vital ones and compiler generated ones).
34: : /// </summary>
35: : public class SequencePointDeletionTurtle : MethodTurtleBase
36: : {
37: : /// <summary>
38: : /// Performs the actual code mutations, returning each with
39: : /// <c>yield</c> for the calling code to use.
40: : /// </summary>
41: : /// <remarks>
42: : /// Implementing classes should yield the result obtained by calling
43: : /// the <see mref="DoYield" /> method.
44: : /// </remarks>
45: : /// <param name="method">
46: : /// A <see cref="MethodDefinition" /> for the method on which mutation
47: : /// testing is to be carried out.
48: : /// </param>
49: : /// <param name="module">
50: : /// A <see cref="Module" /> representing the main module of the
51: : /// containing assembly.
52: : /// </param>
53: : /// <returns>
54: : /// An <see cref="IEnumerable{T}" /> of
55: : /// <see cref="MutantMetaData" /> structures.
56: : /// </returns>
57: : protected override IEnumerable<MutantMetaData> DoMutate(MethodDefinition method, Module module)
58: : {
59: 1/1: var sequence = new Dictionary<int, OpCode>();
60: 3/3: int startIndex = -1;
61: 19/19: for (int index = 0; index < method.Body.Instructions.Count; index++)
62: : {
63: 3/3: var instruction = method.Body.Instructions[index];
64: 10/10: if (instruction.SequencePoint != null
65: 10/10: && instruction.SequencePoint.StartLine != 0xfeefee)
66: : {
67: 5/5: startIndex = index;
68: 1/1: sequence.Clear();
69: : }
70: 10/10: if (startIndex >= 0)
71: : {
72: 3/3: sequence.Add(index, instruction.OpCode);
73: : }
74: 12/12: if (index == method.Body.Instructions.Count - 1 || instruction.Next.SequencePoint != null)
75: : {
76: 8/8: if (!ShouldDeleteSequence(method.Body, sequence)) continue;
77: :
78: 3/3: OpCode originalOpCode = method.Body.Instructions[startIndex].OpCode;
79: 3/3: object originalOperand = method.Body.Instructions[startIndex].Operand;
80: 3/3: method.Body.Instructions[startIndex].OpCode = OpCodes.Br;
81: 3/3: method.Body.Instructions[startIndex].Operand = instruction.Next;
82: :
83: 5/5: var codes = string.Join(", ", sequence.Values.Select(o => o.Code));
84: 5/5: var description = string.Format("{0:x4}: deleting {1}", GetOriginalOffset(startIndex), codes);
85: 4/4: MutantMetaData mutation = DoYield(method, module, description, startIndex);
86: 4/4: yield return mutation;
87: :
88: 3/3: method.Body.Instructions[startIndex].OpCode = originalOpCode;
89: 3/3: method.Body.Instructions[startIndex].Operand = originalOperand;
90: : }
91: : }
92: : }
93: :
94: : private bool ShouldDeleteSequence(MethodBody method, IDictionary<int, OpCode> opCodes)
95: : {
96: 11/11: if (opCodes.Values.All(o => o == OpCodes.Nop)) return false;
97: 11/11: if (opCodes.Values.All(o => o == OpCodes.Pop)) return false;
98: 11/11: if (opCodes.Values.All(o => o == OpCodes.Leave)) return false;
99: 11/11: if (opCodes.Values.Any(o => o == OpCodes.Ret)) return false;
100: :
101: : // If just setting compiler-generated return variable in Debug mode, don't delete.
102: 7/7: if (opCodes.Values.Last().Code == Code.Br)
103: : {
104: 11/11: if (((Instruction)method.Instructions[opCodes.Keys.Last()].Operand).Offset
105: 11/11: == method.Instructions[opCodes.Keys.Last() + 1].Offset)
106: : {
107: 11/11: if (method.Instructions[opCodes.Keys.Last() + 2].OpCode == OpCodes.Ret)
108: : {
109: 1/1: return false;
110: : }
111: : }
112: : }
113: :
114: : // If calling base constructor, don't delete.
115: 10/10: if (opCodes.Any(kv => kv.Value == OpCodes.Call))
116: : {
117: 10/10: if (((MethodReference)method.Instructions[opCodes.First(kv => kv.Value == OpCodes.Call).Key].Operand).Name == Methods.CONSTRUCTOR)
118: : {
119: 1/1: return false;
120: : }
121: : }
122: :
123: : // If compiler-generated dispose, don't delete.
124: 7/7: if (method.Instructions[opCodes.Keys.First()].IsPartOfCompilerGeneratedDispose())
125: : {
126: 1/1: return false;
127: : }
128: :
129: : // If setting default value to a field, don't delete.
130: 14/14: if (method.Instructions[opCodes.Keys.First()]
131: 14/14: .FollowsSequence(OpCodes.Ldarg, OpCodes.Ldc_I4, OpCodes.Stfld)
132: 14/14: && (int)method.Instructions[opCodes.Keys.First() + 1].Operand == 0)
133: : {
134: 1/1: return false;
135: : }
136: :
137: 1/1: return true;
138: : }
139: : }
140: : }
TestDirectory.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.IO;
24: :
25: : using NLog;
26: :
27: : namespace NinjaTurtles
28: : {
29: : /// <summary>
30: : /// Represents a temporary directory used to contain a mutated assembly
31: : /// to be tested. The directory cleans up after itself when its
32: : /// <see mref="Dispose" /> method is called, unless its
33: : /// <see pref="DoNotDelete" /> property is set to <b>true</b>.
34: : /// </summary>
35: : public class TestDirectory : IDisposable
36: : {
37: : #region Logging
38: :
39: : private static Logger _log = LogManager.GetCurrentClassLogger();
40: :
41: : #endregion
42: :
43: : private readonly string _folder;
44: :
45: : /// <summary>
46: : /// Initializes a new instance of the <see cref="TestDirectory" />
47: : /// class.
48: : /// </summary>
49: : public TestDirectory()
50: : {
51: 1/1: _folder = Path.Combine(Path.GetTempPath(),
52: 1/1: "NinjaTurtles",
53: 1/1: Guid.NewGuid().ToString("N"));
54: 1/1: _log.Debug("Creating folder \"{0}\".", _folder);
55: 1/1: Directory.CreateDirectory(_folder);
56: : }
57: :
58: : /// <summary>
59: : /// Initializes a new instance of the <see cref="TestDirectory" />
60: : /// class.
61: : /// </summary>
62: : /// <param name="sourceFolder">
63: : /// The name of a folder whose contents should be recursively
64: : /// copied to the temporary folder.
65: : /// </param>
66: : public TestDirectory(string sourceFolder)
67: : : this()
68: : {
69: 2/2: _log.Debug("Copying contents from folder \"{0}\".", sourceFolder);
70: 3/3: CopyDirectoryContents(sourceFolder, _folder);
71: : }
72: :
73: : /// <summary>
74: : /// Saves an image of a mutated assembly into the root of the test
75: : /// directory.
76: : /// </summary>
77: : /// <param name="module"></param>
78: : public void SaveAssembly(Module module)
79: : {
80: 3/3: string fileName = Path.GetFileName(module.AssemblyLocation);
81: 7/7: string path = Path.Combine(_folder, fileName);
82: 5/5: _log.Debug("Writing assembly \"{0}\" to \"{1}\".", fileName, _folder);
83: 3/3: module.AssemblyDefinition.Write(path);
84: : }
85: :
86: : private static void CopyDirectoryContents
87: : (string directory, string targetDirectory)
88: : {
89: 19/19: foreach (var file in Directory.GetFiles(directory))
90: : {
91: 11/11: string fileName = Path.GetFileName(file);
92: 7/7: _log.Trace("Copying file \"{0}\".", fileName);
93: 17/17: string target = Path.Combine(targetDirectory, fileName);
94: 13/13: File.Copy(file, target);
95: 5/5: }
96: 19/19: foreach (var subDirectory in Directory.GetDirectories(directory))
97: : {
98: 11/11: string subDirectoryName = Path.GetFileName(subDirectory);
99: 7/7: _log.Trace("Creating subdirectory \"{0}\".", subDirectoryName);
100: 17/17: string target = Path.Combine(targetDirectory, subDirectoryName);
101: 7/7: Directory.CreateDirectory(target);
102: 13/13: CopyDirectoryContents(subDirectory, target);
103: 5/5: }
104: : }
105: :
106: : /// <summary>
107: : /// Gets the full path of the test directory.
108: : /// </summary>
109: : public string FullName
110: : {
111: : get { return _folder; }
112: : }
113: :
114: : /// <summary>
115: : /// Performs application-defined tasks associated with freeing,
116: : /// releasing, or resetting unmanaged resources.
117: : /// </summary>
118: : public void Dispose()
119: : {
120: 5/5: if (DoNotDelete)
121: : {
122: 1/1: return;
123: : }
124: : try
125: : {
126: 2/2: _log.Debug("Deleting folder \"{0}\".", _folder);
127: 2/2: Directory.Delete(_folder, true);
128: 1/1: }
129: 1/1: catch (Exception ex)
130: : {
131: 3/3: string message = string.Format("Failed to delete folder \"{0}\".", _folder);
132: 2/2: _log.ErrorException(message, ex);
133: 2/2: }
134: : }
135: :
136: : /// <summary>
137: : /// Gets or sets a flag indicating whether or not the contents of the
138: : /// test directory should be allowed to remain on disk when the
139: : /// instance is disposed.
140: : /// </summary>
141: : public bool DoNotDelete { get; set; }
142: : }
143: : }
144: :
TypeResolver.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Collections.Generic;
24: : using System.Linq;
25: : using System.Reflection;
26: :
27: : using NLog;
28: :
29: : namespace NinjaTurtles
30: : {
31: : internal class TypeResolver
32: : {
33: : #region Logging
34: :
35: : private static Logger _log = LogManager.GetCurrentClassLogger();
36: :
37: : #endregion
38: :
39: : internal static Type ResolveTypeFromReferences(Assembly callingAssembly, string className)
40: : {
41: 1/1: _log.Debug("Resolving type \"{0}\" in \"{1}\".", className, callingAssembly.GetName().Name);
42: 2/2: Type type = ResolveTypeFromReferences(callingAssembly, className, new List<string>());
43: 6/6: if (type == null)
44: : {
45: 1/1: _log.Error("Could not find type \"{0}\".", className);
46: : }
47: 2/2: return type;
48: : }
49: :
50: : private static Type ResolveTypeFromReferences(Assembly assembly, string className, IList<string> consideredAssemblies)
51: : {
52: 2/2: _log.Trace("Searching for type \"{0}\" in \"{1}\".", className, assembly.GetName().Name);
53: 3/3: var type = assembly.GetTypes().SingleOrDefault(t => t.FullName == className);
54: 6/6: if (type != null)
55: : {
56: 2/2: _log.Trace("Found type \"{0}\" in \"{1}\".", className, assembly.GetName().Name);
57: 3/3: return type;
58: : }
59: 10/10: foreach (var reference in assembly.GetReferencedAssemblies())
60: : {
61: 6/6: if (consideredAssemblies.Contains(reference.Name)) continue;
62: 1/1: consideredAssemblies.Add(reference.Name);
63: 1/1: Assembly referencedAssembly = Assembly.Load(reference);
64: 3/3: type = ResolveTypeFromReferences(referencedAssembly, className, consideredAssemblies);
65: 6/6: if (type != null)
66: : {
67: 3/3: return type;
68: : }
69: 5/5: }
70: 3/3: return null;
71: : }
72: : }
73: : }
74: :
VariableReadTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Collections.Generic;
24: : using System.Linq;
25: :
26: : using Mono.Cecil;
27: : using Mono.Cecil.Cil;
28: :
29: : namespace NinjaTurtles.Turtles
30: : {
31: : /// <summary>
32: : /// An implementation of <see cref="IMethodTurtle" /> that identifies local
33: : /// variables, method parameters and fields of the same type, and permutes
34: : /// any reads from them. For example, if two <see cref="Int32" />
35: : /// parameters <c>a</c> and <c>b</c> exist, along with a local variable
36: : /// <c>c</c> of the same type, then a read from <c>a</c> will be replaced
37: : /// by one from <c>b</c> and <c>c</c> in turn, and so on.
38: : /// </summary>
39: : public class VariableReadTurtle : MethodTurtleBase
40: : {
41: : /// <summary>
42: : /// Performs the actual code mutations, returning each with
43: : /// <c>yield</c> for the calling code to use.
44: : /// </summary>
45: : /// <remarks>
46: : /// Implementing classes should yield the result obtained by calling
47: : /// the <see mref="DoYield" /> method.
48: : /// </remarks>
49: : /// <param name="method">
50: : /// A <see cref="MethodDefinition" /> for the method on which mutation
51: : /// testing is to be carried out.
52: : /// </param>
53: : /// <param name="module">
54: : /// A <see cref="Module" /> representing the main module of the
55: : /// containing assembly.
56: : /// </param>
57: : /// <returns>
58: : /// An <see cref="IEnumerable{T}" /> of
59: : /// <see cref="MutantMetaData" /> structures.
60: : /// </returns>
61: : protected override IEnumerable<MutantMetaData> DoMutate(MethodDefinition method, Module module)
62: : {
63: 1/1: var variablesByType = GroupVariablesByType(method);
64: 1/1: PopulateOperandsInVariables(method, variablesByType);
65: :
66: 7/7: if (!variablesByType.Any(kv => variablesByType[kv.Key].Count > 1))
67: : {
68: 1/1: yield break;
69: : }
70: :
71: 13/13: foreach (var keyValuePair in variablesByType.Where(kv => kv.Value.Count > 1))
72: : {
73: 1/1: var variables = keyValuePair.Value.ToList();
74: 22/22: for (int index = 0; index < method.Body.Instructions.Count; index++)
75: : {
76: 4/4: var instruction = method.Body.Instructions[index];
77: 11/11: if (instruction.OpCode == OpCodes.Ldloc && instruction.Next.OpCode == OpCodes.Ret) continue;
78: :
79: 4/4: int oldIndex = -1;
80: 7/7: if (instruction.OpCode == OpCodes.Ldarg)
81: : {
82: 1/1: int parameterIndex = ((ParameterDefinition)instruction.Operand).Sequence;
83: 4/4: oldIndex = variables.FindIndex(v => v.Type == VariableType.Parameter && v.Index == parameterIndex);
84: : }
85: 7/7: if (instruction.OpCode == OpCodes.Ldloc)
86: : {
87: 1/1: int variableIndex = ((VariableDefinition)instruction.Operand).Index;
88: 4/4: oldIndex = variables.FindIndex(v => v.Type == VariableType.Local && v.Index == variableIndex);
89: : }
90: 7/7: if (instruction.OpCode == OpCodes.Ldfld)
91: : {
92: 1/1: string fieldName = ((FieldDefinition)instruction.Operand).Name;
93: 4/4: oldIndex = variables.FindIndex(v => v.Type == VariableType.Field && v.Name == fieldName);
94: : }
95: :
96: 12/12: if (oldIndex < 0) continue;
97: :
98: 1/1: OpCode originalOpCode = instruction.OpCode;
99: 1/1: object originalOperand = instruction.Operand;
100: 5/5: var originalVariable = variables[oldIndex];
101: :
102: 22/22: for (int newIndex = 0; newIndex < variables.Count; newIndex++)
103: : {
104: 14/14: if (newIndex == oldIndex) continue;
105: 5/5: var variable = variables[newIndex];
106: 9/9: if (variable.Operand == null) continue;
107: :
108: 18/18: if (variable.Type == VariableType.Parameter
109: 18/18: && instruction.OpCode == OpCodes.Ldloc
110: 18/18: && instruction.Previous.OpCode == OpCodes.Stloc
111: 18/18: && ((VariableDefinition)instruction.Operand).Index == ((VariableDefinition)instruction.Previous.Operand).Index
112: 18/18: && instruction.Previous.Previous.OpCode == OpCodes.Ldarg
113: 18/18: && ((ParameterDefinition)instruction.Previous.Previous.Operand).Index == variable.Index)
114: : {
115: : // The .NET compiler sometimes adds a pointless
116: : // cache of a parameter into a local variable
117: : // (oddly, Mono doesn't seem to). We need to not
118: : // mutate in this scenario.
119: 1/1: continue;
120: : }
121: :
122: 7/7: if (instruction.IsPartOfCompilerGeneratedDispose())
123: : {
124: 1/1: continue;
125: : }
126: :
127: 2/2: instruction.OpCode = variable.GetReadOpCode();
128: 2/2: instruction.Operand = variable.Operand;
129: :
130: 6/6: var description =
131: 6/6: string.Format(
132: 6/6: "{0:x4}: read substitution {1}.{2} => {1}.{3}",
133: 6/6: GetOriginalOffset(index),
134: 6/6: keyValuePair.Key.Name,
135: 6/6: originalVariable.Name,
136: 6/6: variable.Name);
137: :
138: 4/4: var mutantMetaData = DoYield(method, module, description, index);
139: 4/4: yield return mutantMetaData;
140: : }
141: 1/1: instruction.OpCode = originalOpCode;
142: 1/1: instruction.Operand = originalOperand;
143: : }
144: : }
145: : }
146: :
147: : private static IDictionary<TypeReference, IList<Variable>> GroupVariablesByType(MethodDefinition method)
148: : {
149: : IDictionary<TypeReference, IList<Variable>> variables = new Dictionary<TypeReference, IList<Variable>>();
150: : int offset = method.IsStatic ? 0 : 1;
151: : foreach (var parameter in method.Parameters)
152: : {
153: : var type = parameter.ParameterType;
154: : if (!variables.ContainsKey(type))
155: : {
156: : variables.Add(type, new List<Variable>());
157: : }
158: : variables[type].Add(new Variable(VariableType.Parameter, parameter.Index + offset, parameter.Name));
159: : }
160: : foreach (var variable in method.Body.Variables)
161: : {
162: : var type = variable.VariableType;
163: : if (!variables.ContainsKey(type))
164: : {
165: : variables.Add(type, new List<Variable>());
166: : }
167: : variables[type].Add(new Variable(VariableType.Local, variable.Index, variable.Name));
168: : }
169: : foreach (var field in method.DeclaringType.Fields)
170: : {
171: : if (field.Name == "<>1__state") continue;
172: : var type = field.FieldType;
173: : if (!variables.ContainsKey(type))
174: : {
175: : variables.Add(type, new List<Variable>());
176: : }
177: : variables[type].Add(new Variable(VariableType.Field, -1, field.Name));
178: : }
179: : return variables;
180: : }
181: :
182: : private static void PopulateOperandsInVariables(MethodDefinition method, IDictionary<TypeReference, IList<Variable>> variables)
183: : {
184: : foreach (var instruction in method.Body.Instructions)
185: : {
186: : if (instruction.OpCode == OpCodes.Ldarg)
187: : {
188: : var parameterDefinition = (ParameterDefinition)instruction.Operand;
189: : int sequence = parameterDefinition.Sequence;
190: : if (!variables.ContainsKey(parameterDefinition.ParameterType)) continue;
191: : var variable =
192: : variables[parameterDefinition.ParameterType]
193: : .SingleOrDefault(v => v.Type == VariableType.Parameter && v.Index == sequence);
194: : if (variable != null)
195: : {
196: : variable.Operand = instruction.Operand;
197: : }
198: : }
199: : if (instruction.OpCode == OpCodes.Ldloc)
200: : {
201: : var variableDefinition = (VariableDefinition)instruction.Operand;
202: : int index = variableDefinition.Index;
203: : if (!variables.ContainsKey(variableDefinition.VariableType)) continue;
204: : var variable =
205: : variables[variableDefinition.VariableType]
206: : .SingleOrDefault(v => v.Type == VariableType.Local && v.Index == index);
207: : if (variable != null)
208: : {
209: : variable.Operand = instruction.Operand;
210: : }
211: : }
212: : if (instruction.OpCode == OpCodes.Ldfld)
213: : {
214: : var fieldDefinition = (FieldDefinition)instruction.Operand;
215: : string name = fieldDefinition.Name;
216: : if (!variables.ContainsKey(fieldDefinition.FieldType)) continue;
217: : var variable =
218: : variables[fieldDefinition.FieldType]
219: : .SingleOrDefault(v => v.Type == VariableType.Field && v.Name == name);
220: : if (variable != null)
221: : {
222: : variable.Operand = instruction.Operand;
223: : }
224: : }
225: : }
226: : }
227: : }
228: : }
VariableWriteTurtle.cs
Line: Mutants: Source code
1: : #region Copyright & licence
2: :
3: : // This file is part of NinjaTurtles.
4: : //
5: : // NinjaTurtles is free software: you can redistribute it and/or modify
6: : // it under the terms of the GNU Lesser General Public License as
7: : // published by the Free Software Foundation, either version 3 of the
8: : // License, or (at your option) any later version.
9: : //
10: : // NinjaTurtles is distributed in the hope that it will be useful,
11: : // but WITHOUT ANY WARRANTY; without even the implied warranty of
12: : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: : // GNU Lesser General Public License for more details.
14: : //
15: : // You should have received a copy of the GNU Lesser General Public
16: : // License along with NinjaTurtles. If not, see <http://www.gnu.org/licenses/>.
17: : //
18: : // Copyright (C) 2012 David Musgrove and others.
19: :
20: : #endregion
21: :
22: : using System;
23: : using System.Collections.Generic;
24: : using System.Linq;
25: :
26: : using Mono.Cecil;
27: : using Mono.Cecil.Cil;
28: :
29: : namespace NinjaTurtles.Turtles
30: : {
31: : /// <summary>
32: : /// An implementation of <see cref="IMethodTurtle" /> that identifies local
33: : /// variables of the same type, and permutes any assignments to them. For
34: : /// example, if two <see cref="Int32" /> variables <c>a</c> and <c>b</c>
35: : /// exist, then an assignment to <c>a</c> will be replaced by one to
36: : /// <c>b</c>, and vice versa.
37: : /// </summary>
38: : public class VariableWriteTurtle : MethodTurtleBase
39: : {
40: : /// <summary>
41: : /// Performs the actual code mutations, returning each with
42: : /// <c>yield</c> for the calling code to use.
43: : /// </summary>
44: : /// <remarks>
45: : /// Implementing classes should yield the result obtained by calling
46: : /// the <see mref="DoYield" /> method.
47: : /// </remarks>
48: : /// <param name="method">
49: : /// A <see cref="MethodDefinition" /> for the method on which mutation
50: : /// testing is to be carried out.
51: : /// </param>
52: : /// <param name="module">
53: : /// A <see cref="Module" /> representing the main module of the
54: : /// containing assembly.
55: : /// </param>
56: : /// <returns>
57: : /// An <see cref="IEnumerable{T}" /> of
58: : /// <see cref="MutantMetaData" /> structures.
59: : /// </returns>
60: : protected override IEnumerable<MutantMetaData> DoMutate(MethodDefinition method, Module module)
61: : {
62: 1/1: var variablesByType = GroupVariablesByType(method);
63: 1/1: PopulateOperandsInVariables(method, variablesByType);
64: :
65: 13/13: foreach (var keyValuePair in variablesByType.Where(kv => kv.Value.Count > 1))
66: : {
67: 1/1: var variables = keyValuePair.Value.ToList();
68: 22/22: for (int index = 0; index < method.Body.Instructions.Count; index++)
69: : {
70: 4/4: var instruction = method.Body.Instructions[index];
71: 8/8: if (instruction.IsPartOfCompilerGeneratedDispose()) continue;
72: :
73: 4/4: int oldIndex = -1;
74: 7/7: if (instruction.OpCode == OpCodes.Stloc)
75: : {
76: 1/1: int variableIndex = ((VariableDefinition)instruction.Operand).Index;
77: 4/4: oldIndex = variables.FindIndex(v => v.Type == VariableType.Local && v.Index == variableIndex);
78: : }
79: 7/7: if (instruction.OpCode == OpCodes.Stfld)
80: : {
81: 1/1: string fieldName = ((FieldDefinition)instruction.Operand).Name;
82: 4/4: oldIndex = variables.FindIndex(v => v.Type == VariableType.Field && v.Name == fieldName);
83: : }
84: :
85: 12/12: if (oldIndex < 0) continue;
86: :
87: : // Skip if could be initialising to zero, as in reality
88: : // this has already been done by the CLR.
89: 10/10: if (instruction.Previous.OpCode == OpCodes.Ldc_I4
90: 10/10: && (int)instruction.Previous.Operand == 0)
91: : {
92: 1/1: continue;
93: : }
94: :
95: 1/1: OpCode originalOpCode = instruction.OpCode;
96: 1/1: object originalOperand = instruction.Operand;
97: 5/5: var originalVariable = variables[oldIndex];
98: :
99: 22/22: for (int newIndex = 0; newIndex < variables.Count; newIndex++)
100: : {
101: 14/14: if (newIndex == oldIndex) continue;
102: 5/5: var variable = variables[newIndex];
103: 9/9: if (variable.Operand == null) continue;
104: :
105: 2/2: instruction.OpCode = variable.GetWriteOpCode();
106: 2/2: instruction.Operand = variable.Operand;
107: :
108: 6/6: var description =
109: 6/6: string.Format(
110: 6/6: "{0:x4}: write substitution {1}.{2} => {1}.{3}",
111: 6/6: GetOriginalOffset(index),
112: 6/6: keyValuePair.Key.Name,
113: 6/6: originalVariable.Name,
114: 6/6: variable.Name);
115: :
116: 6/6: yield return DoYield(method, module, description, index);
117: :
118: : }
119: 1/1: instruction.OpCode = originalOpCode;
120: 1/1: instruction.Operand = originalOperand;
121: : }
122: : }
123: : }
124: :
125: : private static IDictionary<TypeReference, IList<Variable>> GroupVariablesByType(MethodDefinition method)
126: : {
127: : IDictionary<TypeReference, IList<Variable>> variables = new Dictionary<TypeReference, IList<Variable>>();
128: : foreach (var variable in method.Body.Variables)
129: : {
130: : var type = variable.VariableType;
131: : if (!variables.ContainsKey(type))
132: : {
133: : variables.Add(type, new List<Variable>());
134: : }
135: : variables[type].Add(new Variable(VariableType.Local, variable.Index, variable.Name));
136: : }
137: : foreach (var field in method.DeclaringType.Fields)
138: : {
139: : if (field.Name == "<>1__state") continue;
140: : var type = field.FieldType;
141: : if (!variables.ContainsKey(type))
142: : {
143: : variables.Add(type, new List<Variable>());
144: : }
145: : variables[type].Add(new Variable(VariableType.Field, -1, field.Name));
146: : }
147: : return variables;
148: : }
149: :
150: : private static void PopulateOperandsInVariables(MethodDefinition method, IDictionary<TypeReference, IList<Variable>> variables)
151: : {
152: : foreach (var instruction in method.Body.Instructions)
153: : {
154: : if (instruction.OpCode == OpCodes.Ldloc)
155: : {
156: : var variableDefinition = (VariableDefinition)instruction.Operand;
157: : int index = variableDefinition.Index;
158: : if (!variables.ContainsKey(variableDefinition.VariableType)) continue;
159: : var variable =
160: : variables[variableDefinition.VariableType]
161: : .SingleOrDefault(v => v.Type == VariableType.Local && v.Index == index);
162: : if (variable != null)
163: : {
164: : variable.Operand = instruction.Operand;
165: : }
166: : }
167: : if (instruction.OpCode == OpCodes.Ldfld)
168: : {
169: : var fieldDefinition = (FieldDefinition)instruction.Operand;
170: : string name = fieldDefinition.Name;
171: : if (!variables.ContainsKey(fieldDefinition.FieldType)) continue;
172: : var variable =
173: : variables[fieldDefinition.FieldType]
174: : .SingleOrDefault(v => v.Type == VariableType.Field && v.Name == name);
175: : if (variable != null)
176: : {
177: : variable.Operand = instruction.Operand;
178: : }
179: : }
180: : }
181: : }
182: : }
183: : }