Muex.MutantOptimizer (Muex v0.3.3)

View Source

Sophisticated heuristics to minimize the number of mutants while maintaining mutation testing effectiveness.

This module implements several strategies:

  1. Equivalent Mutant Detection: Identifies mutations that are semantically equivalent to the original code (e.g., x + 0x - 0)

  2. Impact Analysis: Prioritizes mutations in complex, frequently-tested code over simple getters or trivial functions

  3. Mutation Clustering: Groups similar mutations and tests only representative samples from each cluster

  4. Code Complexity Scoring: Focuses on mutations in code with higher cyclomatic complexity, where bugs are more likely

  5. Pattern-Based Filtering: Removes mutations known to be low-value based on AST patterns (e.g., mutating literal 0 in arithmetic identity operations)

  6. Boundary Value Focus: Prioritizes mutations at decision boundaries (>=, <=, ==) over less critical operators

  7. Guard Clause Deprioritization: Reduces mutation testing on simple validation guards that are typically well-covered by tests

Summary

Functions

Groups similar mutations and samples representatives from each cluster.

Filters out mutations in trivially simple code.

Filters out mutations that are likely to be equivalent to the original code.

Limits the number of mutations per function to prevent explosion.

Generates a summary report of the optimization results.

Filters and prioritizes mutations based on sophisticated heuristics.

Ensures boundary condition mutations are always included.

Assigns impact scores to mutations based on code characteristics.

Types

filter_options()

@type filter_options() :: [
  enabled: boolean(),
  min_complexity: non_neg_integer(),
  max_mutations_per_function: non_neg_integer(),
  cluster_similarity_threshold: float(),
  keep_boundary_mutations: boolean()
]

mutation()

@type mutation() :: %{
  ast: tuple(),
  mutator: module(),
  description: String.t(),
  location: map()
}

Functions

cluster_and_sample(mutations, similarity_threshold)

Groups similar mutations and samples representatives from each cluster.

This reduces redundant mutations that test the same code path. For example, if a function has 10 arithmetic operations, we don't need to test all possible +- mutations.

filter_by_complexity(mutations, min_complexity)

Filters out mutations in trivially simple code.

Removes mutations from:

  • Simple getters/setters
  • Trivial boolean guards
  • Single-operation functions

filter_equivalent_mutants(mutations)

Filters out mutations that are likely to be equivalent to the original code.

Detects patterns like:

  • x + 0x - 0 (arithmetic identity)
  • x * 1x / 1 (multiplicative identity)
  • true and xtrue or x (boolean short-circuit)
  • Empty list mutations [][]

limit_per_function(mutations, max_per_function)

Limits the number of mutations per function to prevent explosion.

Keeps the highest-impact mutations per function.

optimization_report(original_mutations, optimized_mutations)

Generates a summary report of the optimization results.

optimize(mutations, opts \\ [])

@spec optimize([mutation()], filter_options()) :: [mutation()]

Filters and prioritizes mutations based on sophisticated heuristics.

Options

  • :enabled - Enable optimization (default: false)
  • :min_complexity - Minimum complexity score to mutate (default: 2)
  • :max_mutations_per_function - Maximum mutations per function (default: 20)
  • :cluster_similarity_threshold - Similarity threshold for clustering (default: 0.8)
  • :keep_boundary_mutations - Always keep boundary condition mutations (default: true)

Returns

A filtered and prioritized list of mutations.

prioritize_boundary_mutations(mutations, keep_boundary)

Ensures boundary condition mutations are always included.

Boundary mutations (>=, <=, ==, !=) are critical for finding off-by-one errors and are always kept regardless of other filters.

score_by_impact(mutations)

Assigns impact scores to mutations based on code characteristics.

Higher scores indicate mutations more likely to reveal test weaknesses:

  • Complex conditional logic: +5
  • Arithmetic in loops or recursion: +4
  • Boundary conditions (>=, <=, ==): +3
  • Function calls: +2
  • Simple assignments: +1