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