PlexTest - 'Equivalent mutants' and selective mutation testing

Early mutation testers made a variety of mutations to a program - operator substitution, etc. This (somewhat aimless) mutation testing often introduced a severe problem: it would create 'equivalent mutants' - mutated programs that nevertheless worked perfectly. Equivalent mutants interfere substantially with the automated testing process because each one has to be individually and manually analysed and rejected.

Later mutation testers performed selective mutation testing which alleviated the problem, but did not eliminate it.

A selective mutation tester might attempt the following:

Original code

Mutated code

bool nonzero(int *array, int size)
{
  int sum = 0;
  for (int i = 0; i < size; i++)
    sum = sum + array[i];
  return sum != 0;
}

bool nonzero(int *array, int size)
{
  int sum = 0;
  for (int i = 0; i < size; i++)
    sum = sum - array[i];
  return sum != 0;
}
 

Which, while undoubtedly different - the value of the variable 'sum' has been negated - nevertheless runs without fault. Since the variable 'sum' is merely being tested as to whether or not it has the value zero, the mutated program works as normal. It is an 'equivalent mutant'.

Plextest is a highly selective mutation tester. It only performs the mutation of 'deletion' - that is, removal of an instruction. But whereas other mutation testers only perform full statement deletion, Plextest performs sub-expression deletion. For example, it fully tests every binary operator in a program by removing, along with the operator, first the left sub-expression, then right sub-expression.

Plextest can perform full selective (and indeed, non-selective) mutation testing, if you so desire (and you can change its behavior mid-program via comment-directives). Full mutation testing (and selective mutation testing) attempt to find nearly all software faults - at the expense of sometimes creating equivalent mutants, and thereby taking the 'automated' out of automated testing. Plextest attempts to bridge the gap between coverage testing and traditional mutation testing - ensuring that each instruction in the program is not just executed, but is verifiably measured - without compromising the automated nature of the testing.

Glossary
Failure - an incorrect output, crash, hang, or other erroneous software behavior
Fault - a defect in a program that causes a fault
Testing - Regression testing
              Unit test
              Automated test
              Coverage testing
              Mutation testing
              Equivalent mutant
              Selective mutation testing
              Strong mutation testing
              Weak mutation testing