In-source test runner for Answer Set Programming (ASP) with Clingo.
It provides:
* `asp-tests`: a standalone (Python) tool to run tests in a logic program.
* `bin/runasptests.sh`: Bash script to find all .lp files and run their tests.
Both tools stop at first failure.
After installation via pip, run it using:
$ asp-tests <file.lp> ...
Alternatively you can run it as a module, given that either the working directory of the PYTHONPATH are set to 'src':
$ python -m asp_selftest <file.lp> ...
There are options to silents the in-source Python tests etc, have a look:
$ asp-tests -h
The code is equiped with in-source Python tests which always run. You can silence them with --silent.
To use the program without the tests: Not Yet Implemented. But you can use the base
program anywhere of course, since all #program
s are ignored by default.
-
Use
#program
's to identify units and their dependencies. Here we have a unit calledunitA
with a unit test for it calledtestunitA
.#program unit_A. #program test_unit_A(unit_A).
The implicit program
base
(see Clingo Guide) must be referenced explicitly if needed. -
Extend the notion of
#program
by allowing the use of functions instead of only constants. This allows#program
units with constants being tested. Here is a unitstep
that is tested with constanta
being substituted with2
:#program step(a). #program test_step(step(2)).
Note that using this feature makes the program incompatible with Clingo. The test runner has an option to transform a extended program back to compatible Clingo without running the tests.
-
Within a test program, use
assert
with@all
to ensure universal truths that must be in every model. We use@all
to communicate to the runtime that this particular assert must be checked for presence in every model. Its argument is just a name for identification.#program step(n). fact(n). #program test_step(step(3)). assert(@all("step fact")) :- fact(3).
Note that
"step fact"
is just a way of distinquishing the assert. It can be an atom, a string, a number or anything else. Pay attention to the uniqueness in case of variables in the body. Take note of point 5 below. -
To enable testing constraints and to guard tests for empty model sets, we use
@models
to check for the expected number of models. In the example above, we would add:assert(@models(1)).
-
Care must be taken if variables in the body lead to expansion and conjunctions. See
duplicate_assert.lp
. The system gives a warning for:assert(@all(id_uniq)) :- def_id(Id, _, _), { def_id(Id, _, _) } = 1.
Instead you have to write:
assert(@all(id_uniq(Id))) :- def_id(Id, _, _), { def_id(Id, _, _) } = 1.