Poodle’s Mutators#
“What do you call a labradoodle that can do Magic?”
“A Labra-cadabra-doodle”
Builtin Mutator Names#
“BinOp”: Binary Operation Mutator
“AugAssign”: Augmented Assignment Mutator
“UnaryOp”: Unary Operation Mutator
“Compare”: Comparison Mutator
“Keyword”: Keyword Mutator
“Number”: Number Mutator
“String”: String Mutator
“FuncCall”: Function Call Mutator
“DictArray”: Dict Array Call Mutator
“Lambda”: Lambda Return Mutator
“Return”: Return Mutator
“Decorator”: Decorator Mutator
Binary Operation Mutator#
Name: BinOp
Replaces operators used in binary expressions. For example x + y
becomes x * y
See operator_level for a list of all replacements included in this mutator.
If multiple operations are listed, the mutator will create multiple mutations for that location, one for each alternate operator.
Augmented Assignment Mutator#
Name: AugAssign
This mutator always creates multiple mutations for each location.
First it creates a mutation changing the Augmented Assignment to Assignment.
For example:
x += 3
becomesx = 3
Then it creates a mutation for each alternate operator in the operator_level mapping
For example,
x += 3
becomesx *= 3
Operation Mutator Options#
There are two mutators that share the same operator mutation settings. Both the Binary Operation Mutator and Augmented Assignment Mutator work by modifying the operator in the expression. Both share the same operator_level option to determine how many replacements to use for each operator.
operator_level#
Default: “std”
Allowed Values: “min”, “std”, “max”
Operator |
Symbol |
“min” |
“std” |
“max” |
---|---|---|---|---|
Addition |
|
|
|
|
Subtraction |
|
|
|
|
Multiplication |
|
|
|
|
Division |
|
|
|
|
Floor Division |
|
|
|
|
Modulus |
|
|
|
|
Power |
|
|
|
|
Left Shift |
|
|
|
|
Right Shift |
|
|
|
|
Bitwise OR |
|
|
|
|
Bitwise XOR |
|
|
|
|
Bitwise AND |
|
|
|
|
Matrix Mult. |
|
N/A |
N/A |
N/A |
mutator_opts = {"operator_level":"min"}
[poodle.mutator_opts]
operator_level = "min"
[tool.poodle.mutator_opts]
operator_level = "min"
Unary Operation Mutator#
Name: UnaryOp
This mutator modifies unary operators Add, Subtract, Not, and Invert.
Examples:
x = +2
becomesx = -2
x = -2
becomesx = +2
if not x:
becomesif x:
x = ~2
becomesx = 2
Comparison Mutator#
Name: Compare
This mutator modifies comparison operators including some binary comparison operators.
However changing some comparisons can cause problems for testing tools, the mutator maintains a list of comparisons that should NOT be mutated. In addition, compare_filters can be provided for additional patterns of comparisons that should not be mutated. See compare_filters
Operator |
Symbol |
Replacement(s) |
---|---|---|
Equality |
|
|
Not Equal |
|
|
Less Than |
|
|
Less Than or Equal |
|
|
Greater Than |
|
|
Greater Than or Equal |
|
|
is |
|
|
is not |
|
|
in |
|
|
not in |
|
|
or |
|
|
and |
|
|
Options#
compare_filters#
Additional regex filters to prevent mutation of specific comparisons.
Default Filters:
r"__name__ == '__main__'"
The default filters are always used, in addition to the ones specified in compare_filters.
mutator_opts = {"compare_filters":[r"sys.version_info.minor\s*>\s*10"]}
[poodle.mutator_opts]
compare_filters = ["sys.version_info.minor\s*>\s*10"]
[tool.poodle.mutator_opts]
compare_filters = ["sys.version_info.minor\s*>\s*10"]
Tip
Modules are first parsed to ast, then unparsed to text before comparing to the compare_filters. For example, __name__=="__main__"
is unparsed as __name__ == '__main__'
See how something is unparsed with a statement like:
import ast
ast.unparse(ast.parse('__name__=="__main__"',mode='eval'))
Keyword Mutator#
Name: Keyword
This mutator modifies specific keywords:
break
becomescontinue
continue
becomesbreak
True
becomesFalse
False
becomesTrue
None
becomes' '
Number Mutator#
Name: Number
This mutator modifies numbers that are hard coded in the code. This can include int, octal int, hex int, binary int, float, complex. Most numbers are mutated twice.
int
values are mutated as value + 1 and value - 1complex
values are mutated as value + 1j and value - 1jfloat
values are mutated as value / 2 and value * 2 Unless the float == 0, then it’s mutated to1.0
String Mutator#
Name: String
The string mutator adds “XX” to the beginning and end of strings. It automatically skips docstrings.
Function Call Mutator#
Name: FuncCall
This mutator replaces calls to a function with the keyword None
.
Examples:
my_func(123)
becomesNone
x = my_func(123)
becomesx = None
Dict Array Call Mutator#
Name: DictArray
This mutator replaces calls to retrieve data from a dict or array/list object with None
.
Examples:
abcd[1]
becomesNone
abcd["efg"]
becomesNone
x = abcd[1]
becomesx = None
x = abcd["efg"]
becomesx = None
Lambda Return Mutator#
Name: Lambda
This mutator changes the body of a lambda statement to return either ""
or None
.
Examples:
lambda x,y: x+y
becomeslambda x,y: None
lambda z: None
becomeslambda z: ""
Return Mutator#
Name: Return
This mutator replaces the value being returned from a function with ""
or None
.
Examples:
return 3
becomesreturn None
return my_variable
becomesreturn None
return None
becomesreturn ""
Decorator Mutator#
Name: Decorator
This mutator creates mutations where decorators are removed.
Example Input:
@log_input()
@cache
def my function(a,b):
...
Mutation 1:
@cache
def my function(a,b):
...
Mutation 2:
@log_input()
def my function(a,b):
...