| Did you know ... | Search Documentation: |
| Pack logtalk -- logtalk-3.99.0/tools/mutation_testing/NOTES.md |
This file is part of Logtalk https://logtalk.org/ SPDX-FileCopyrightText: 2026 Paulo Moura <pmoura@logtalk.org> SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
mutation_testingThis tool provides mutation testing support for loaded Logtalk entities. It's still under development and breaking changes should be expected.
This tool API documentation is available at:
[../../apis/library_index.html#mutation-testing](../../apis/library_index.html#mutation-testing)
This tool can be loaded using the query:
| ?- logtalk_load(mutation_testing(loader)).
To test this tool, load the tester.lgt file:
| ?- logtalk_load(mutation_testing(tester)).
sampling(all|count(N)|rate(R)) plus seed/1).The default mutators are:
>, <, @=<, ==) with a complementary alternative.true <-> fail) in selected goals.Mutation testing requires an existing set of tests for the code being mutated. These tests are used to verify if they are able to kill the generated mutants or if they are too weak allowing the mutants to survive.
By default, the tool looks for the tests driver file (usually, tester.lgt)
in the directory of the entity, library, or directory being tested. If the
tests driver file is not found there, the tool looks into the startup
directory of the Logtalk process.
Both the name of the tests driver file and its directory can be set using the tester_file_name/1 and tester_directory/1 options if the defaults don't work in your particular case.
In this tool documentation, campaign predicates refers to the following public predicates:
Run mutation testing for one entity using defaults:
| ?- mutation_testing::entity(my_object).
List generated mutants for one entity:
| ?- mutation_testing::entity_mutants(my_object, Mutants).
Run with custom options:
entity(my_object, [
mutators([fail_insertion, body_goal_negation]),
max_mutations_per_mutator(5),
sampling(count(100)),
seed(20260303),
max_mutators(100),
threshold(60.0),
verbose(true),
print_mutation(true)
]).
Run campaigns for all loaded entities from a specific library:
| ?- mutation_testing::library(core).
Run campaigns for all loaded entities from a specific directory:
| ?- mutation_testing::directory('/path/to/sources').
Pretty-print a report term using the default text format:
| ?- mutation_testing::report_entity(my_object, Report, []),
mutation_testing::format_report(Report).
Suppress report formatting output for campaign predicates:
| ?- mutation_testing::entity(my_object, [format(none)]).
Execution uses lgtunit test sets only. When using timeout(Timeout), timeout
handling is best-effort.
When running mutation campaigns inside another lgtunit test run, per-mutant
killed versus survived classification may be affected by nested test-run
message and event handling. Campaign summary accounting remains deterministic.
head_arguments_mutation which creates one occurrence per compile-time bound head argument with supported type; internal expression-level candidate enumeration is not implemented.lgtunit runs may affect per-mutant killed vs survived status classification.[] meaning all).[]).all or positive integer; default all).all or positive integer; default 5).[] (default), mutators are auto-discovered and optionally limited by max_mutators/1.all, count(N), or rate(R) where `0.0 =< R =< 1.0`; default all). See below for full details.123456789; ignored when sampling(all)).300). Timeout handling is best-effort.0.0).false).none, text, or json; default text).
When set to text or json, a report file is generated automatically for campaign predicates.
The json format implements the Stryker Mutation Testing Framework report format:
https://stryker-mutator.io/mutation_test_report).
The extension is inferred from format/1 (text -> `.txt`, json -> `.json`).
When not absolute, the file is saved in the tests driver directory.true, prints original and mutated terms with source location for mutators. This option is only effective when verbose(true) (default false).tester.lgt).
The sampling(Sampling) option controls which generated mutants are actually
executed in a mutation campaign. It is a post-generation selection step:
sampling(...) to select a subset (or all).sampling(...) affects campaign predicates
(entity/2, predicate/3, library/2, directory/2).
The format(Format) option affects only campaign predicates that execute and
report in one step (entity/2, predicate/3, library/2, directory/2).
It does not affect the `*_mutants` predicates or the `report_*` predicates,
which only compute and return terms.
For campaign predicates, a report file is written automatically unless
format(none) is used.
For report_library/3 and report_directory/3, call format_report/2-3
explicitly if you want to persist the computed report term. A single
format_report(..., Report) call always generates a single output document
(including JSON).
The mutants/2-3 predicates are still useful for inspecting the generated
deterministic mutant list itself; they are not execution/reporting predicates.
round(R * TotalGeneratedMutants), shuffle the mutant list, and
execute that many mutants.
If the computed value is greater than the total number of generated mutants,
all mutants are executed.
The randomization used by count/1 and rate/1 is controlled by the seed/1 option, allowing reproducible selection.
sampling(all)) changing the seed has no effect on selection.
Define a new mutator as parametric object implementing the expanding protocol,
either the clause_mutator_protocol or the directive_mutator_protocol marker
protocol, and importing the mutator_common category:
:- object(my_mutator(_Entity_, _Predicate_, _TargetIndex_, _Occurrence_, _PrintMutation_),
implements((expanding, clause_mutator_protocol)),
imports(mutator_common)).
% mutate code when loading it using this object as a hook object
term_expansion(Term, Mutation) :-
...
% compute a term mutation; generate multiple mutations by backtracking
mutation(Term, Mutation) :-
...
% reset any required internal state
reset :-
...
:- end_object.
New mutators are found by dynamically looking for objects that conform to
either the clause_mutator_protocol or the directive_mutator_protocol
marker protocols.
For implementation examples, see the default mutators: