Mutare.Mutators.IntegerLiteral (mutare v0.1.0)

Copy Markdown View Source

Integer-literal mutations: nn + 1, n - 1, and 0 (the "off-by-one" boundary plus the zero sentinel), deduplicated and never equal to n.

Only literals in runtime positions are mutated, never pattern literals. Mutations that would reproduce the original value are dropped (0 is not re-emitted for the literal 0; n - 1 and 0 collapse for n = 1).

An integer literal in a known timeout/duration position (Process.sleep/1, the third argument of Process.send_after/3, the GenServer.call/3 timeout, Task.async_stream's :timeout option, …) is left unmutated — a near-unkillable equivalent mutant that also risks minting false :timeout kills. This family owns that knowledge: Mutare.Mutator.argument_marks/1 asks the transform to mark those positions with the :timeout label, and mutate/2 declines when the mark is present. A computed duration (base * 2) is not a literal at the marked node, so it still mutates. (Mutare.Mutators.AtomLiteral reuses the same table to leave :infinity alone there.)

Configuring extra positions. Add project-specific timeout positions with the argument_marks: option in .mutare.exs — entries have the declaration shape this table is written in ({module, function, arity, positions, label}; positions lists effective argument indices and {:keyword, key} option keys), and the :timeout label gives them exactly this family's reaction:

[argument_marks: [
  {MyApp.Cache, :put, 3, [2], :timeout},                       # a TTL argument
  {MyApp.Http, :get, 2, [{:keyword, :recv_timeout}], :timeout}
]]

Mutare.Mutators.AtomLiteral reacts to the same label, so a configured position also keeps its :infinity alone. To leave a position alone for every family regardless of value, route it :raw in call_routes: instead (see Mutare.CallRouting).

Integer literals are pervasive, so this is the highest-volume built-in — the cost is paid in the denominator, the benefit is catching constants the suite never pins down. The integer sibling of Mutare.Mutators.FloatLiteral (step 1, zero 0); the boolean flip that used to share this family now lives in Mutare.Mutators.BooleanLiteral.

Filterable variants — qualify a # mutare:ignore filter with :label to suppress just one kind (Mutare.Mutator.variants/0): zero, succ, pred.

Summary

Functions

The built-in timeout/duration argument marks (the :timeout label), config-independent. Public so Mutare.Mutators.AtomLiteral can declare the identical positions — keeping the two value families' view of "an opaque timeout literal" in one place.

Functions

timeout_marks()

@spec timeout_marks() :: [
  {module(), atom(), arity(), [non_neg_integer() | {:keyword, atom()}], atom()}
]

The built-in timeout/duration argument marks (the :timeout label), config-independent. Public so Mutare.Mutators.AtomLiteral can declare the identical positions — keeping the two value families' view of "an opaque timeout literal" in one place.