Mutation Operators

Mutation operators are the rules that match patterns of program code, and produce corresponding mutations.

The active set of mutation operators can be controlled using the --mutation-operators argument (default: all), by specifying a comma-seperated list of mutation operator names.

cargo mutest --mutation-operators call_delete,call_value_default_shadow run

The following list of mutation operators is currently implemented in mutest-rs, with detailed descriptions and examples below:

Mutation OperatorShort Description
arg_default_shadowIgnore argument by shadowing it with Default::default().
bit_op_or_and_swapSwap bitwise OR for bitwise AND and vice versa.
bit_op_or_xor_swapSwap bitwise OR for bitwise XOR and vice versa.
bit_op_shift_dir_swapSwap the direction of bitwise shift operator.
bit_op_xor_and_swapSwap bitwise XOR for bitwise AND and vice versa.
bool_expr_negateNegate boolean expression.
call_deleteDelete call and replace it with Default::default().
call_value_default_shadowIgnore return value of call by shadowing it with Default::default().
continue_break_swapSwap continue for break and vice versa.
eq_op_invertInvert equality check.
logical_op_and_or_swapSwap logical and for logical or and vice versa.
math_op_add_mul_swapSwap addition for multiplication and vice versa.
math_op_add_sub_swapSwap addition for subtraction and vice versa.
math_op_div_rem_swapSwap division for modulus and vice versa.
math_op_mul_div_swapSwap multiplication for division and vice versa.
range_limit_swapSwap limit (inclusivity) of range expression.
relational_op_eq_swapInclude or remove the boundary (equality) of relational operator.
relational_op_invertInvert relation operator.

NOTE: The following replacements are illustrative and are meant to show how code behaviour effectively changes with each mutation.

arg_default_shadow

Replace the provided arguments of functions with Default::default() to check if each parameter is tested with meaningful values.

This is done by rebinding parameters at the beginning of the function.

Replaces

fn foo(hash: u64) {

with

fn foo(hash: u64) {
    let hash: u64 = Default::default();

bit_op_or_and_swap

Swap bitwise OR for bitwise AND and vice versa.

Replaces

byte & (0x1 << 2)

with

byte | (0x1 << 2)

bit_op_or_xor_swap

Swap bitwise OR for bitwise XOR and vice versa.

Replaces

bytes[i] |= 0x1 << 3

with

bytes[i] ^= 0x1 << 3

bit_op_shift_dir_swap

Swap the direction of bitwise shift operators.

Replaces

byte & (0x1 << i)

with

byte & (0x1 >> i)

bit_op_xor_and_swap

Swap bitwise XOR for bitwise AND and vice versa.

Replaces

byte & (0x1 << 2)

with

byte ^ (0x1 << 2)

bool_expr_negate

Negate boolean expressions.

Replaces

if !handle.is_active() {
    drop(handle);

with

if handle.is_active() {
    drop(handle);

call_delete

Delete function calls and replace them with Default::default() to test whether inner calls are meaningfully tested, without retaining any side-effects of the callees.

Replaces

let existing = map.insert(Id(123), 0);

with

let existing: Option<usize> = Default::default();

call_value_default_shadow

Replace the return value of function calls with Default::default() to test whether the return values of inner calls are meaningfully tested, while retaining expected side-effects of the callees.

Replaces

let existing = map.insert(Id(123), 0);

with

let existing: Option<usize> = {
    let _existing = map.insert(Id(123), 0);
    Default::default()
};

continue_break_swap

Swap continue expressions for break expressions and vice versa.

Replaces

for other in mutations {
    if conflicts.contains(&(mutation, other)) { continue; }

with

for other in mutations {
    if conflicts.contains(&(mutation, other)) { break; }

eq_op_invert

Invert equality checks.

Replaces

if buffer.len() == 0 {
    buffer.reserve(1024);

with

if buffer.len() != 0 {
    buffer.reserve(1024);

logical_op_and_or_swap

Swap logical && for logical || and vice versa.

Replaces

self.len() <= other.len() && self.iter().all(|v| other.contains(v))

with

self.len() <= other.len() || self.iter().all(|v| other.contains(v))

math_op_add_mul_swap

Swap addition for multiplication and vice versa.

Replaces

let offset = size_of::<DeclarativeEnvironment>() * index;

with

let offset = size_of::<DeclarativeEnvironment>() + index;

math_op_add_sub_swap

Swap addition for subtraction and vice versa.

Replaces

let center = Point::new(x + (width / 2), y + (height / 2));

with

let center = Point::new(x - (width / 2), y + (height / 2));

math_op_div_rem_swap

Swap division for modulus and vice versa.

Replaces

let evens = 0..100.filter(|v| v % 2 == 0);

with

let evens = 0..100.filter(|v| v / 2 == 0);

math_op_mul_div_swap

Swap multiplication for division and vice versa.

Replaces

let v = f64::sin(t * freq) * magnitude;

with

let v = f64::sin(t / freq) * magnitude;

range_limit_swap

Invert the limits (inclusivity) of range expressions.

Replaces

for i in 0..buffer.len() {

with

for i in 0..=buffer.len() {

relational_op_eq_swap

Include or remove the boundary (equality) of relational operators.

Replaces

if self.len() <= other.len() {

with

if self.len() < other.len() {

relational_op_invert

Completely invert relation operators.

Replaces

while i < buffer.len() {

with

while i >= buffer.len() {