Poodle’s Mutators

    /''\,,,,,
   |    |    \           ___
    \   /  ^ |        __/_  `.  .-"""-.
   / \_/   0  \       \_,` | \-'  /   )`-')
  /            \       "") `"`    \  ((`"`
 /    ____      0     ___Y  ,    .'7 /|
/      /  \___ _/    (_,___/...-` (_/_/
“What do you call a labradoodle that can do Magic?”

“A Labra-cadabra-doodle”

Builtin Mutator Names

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 becomes x = 3

Then it creates a mutation for each alternate operator in the operator_level mapping

  • For example, x += 3 becomes x *= 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 becomes x = -2

  • x = -2 becomes x = +2

  • if not x: becomes if x:

  • x = ~2 becomes x = 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

is not

is not

is not

is

in

in

not in

not in

not in

in

or

or

and

and

and

or

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 becomes continue

  • continue becomes break

  • True becomes False

  • False becomes True

  • 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 - 1

  • complex values are mutated as value + 1j and value - 1j

  • float values are mutated as value / 2 and value * 2 Unless the float == 0, then it’s mutated to 1.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) becomes None

  • x = my_func(123) becomes x = 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] becomes None

  • abcd["efg"] becomes None

  • x = abcd[1] becomes x = None

  • x = abcd["efg"] becomes x = 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 becomes lambda x,y: None

  • lambda z: None becomes lambda z: ""

Return Mutator

Name: Return

This mutator replaces the value being returned from a function with "" or None.

Examples:

  • return 3 becomes return None

  • return my_variable becomes return None

  • return None becomes return ""

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):
    ...