mutest-rs relies on a set of mutation operators to produce mutations for certain patterns of code. Each mutation operator is able to match on code fragments, based on syntax and compiler information (e.g. types, lifetimes, scope), and produce corresponding mutated code fragments as output.
Following is the list of mutation operators currently implemented in mutest-rs.
Note
Replacements are illustrative and are meant to show how code behaviour effectively changes with each mutation.
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();
Swap bitwise OR for bitwise AND and vice versa.
Replaces
byte & (0x1 << 2)
with
byte | (0x1 << 2)
Swap bitwise OR for bitwise XOR and vice versa.
Replaces
bytes[i] |= 0x1 << 3
with
bytes[i] ^= 0x1 << 3
Swap the direction of bitwise shift operators.
Replaces
byte & (0x1 << i)
with
byte & (0x1 >> i)
Swap bitwise XOR for bitwise AND and vice versa.
Replaces
byte & (0x1 << 2)
with
byte ^ (0x1 << 2)
Negate boolean expressions.
Replaces
if !handle.is_active() {
drop(handle);
with
if handle.is_active() {
drop(handle);
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();
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()
};
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; }
Invert equality checks.
Replaces
if buffer.len() == 0 {
buffer.reserve(1024);
with
if buffer.len() != 0 {
buffer.reserve(1024);
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))
Swap addition for multiplication and vice versa.
Replaces
let offset = size_of::<DeclarativeEnvironment>() * index;
with
let offset = size_of::<DeclarativeEnvironment>() + index;
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));
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);
Swap multiplication for division and vice versa.
Replaces
let v = f64::sin(t * freq) * magnitude;
with
let v = f64::sin(t / freq) * magnitude;
Invert the limits (inclusivity) of range expressions.
Replaces
for i in 0..buffer.len() {
with
for i in 0..=buffer.len() {
Include or remove the boundary (equality) of relational operators.
Replaces
if self.len() <= other.len() {
with
if self.len() < other.len() {
Completely invert relation operators.
Replaces
while i < buffer.len() {
with
while i >= buffer.len() {