Testing for nondeterminism in plunit

Suppose I have the following. naive_member/2 is nondeterministic on the last member, and I want to make the plunit tests fail if that occurs (both on “single tests” and on “all” tests).

How to do that?

naive_member(X,[X|_]).
naive_member(X,[_|R]) :- naive_member(X,R).

:- begin_tests(member_determinacy).

% option "nondet" suppresses warning "Test succeeded with choicepoint"
% (it is not a test failure if the test succeeds with a choicepoint)

test(one_nondet)    :- naive_member(1,[1,2,3]).  % generates a warning
test(one,[nondet])    :- naive_member(1,[1,2,3]).
test(two,[nondet])    :- naive_member(1,[1,1,3]).
test(three,[nondet])  :- naive_member(1,[1,1,1]).
test(five,fail)       :- naive_member(3,[1,1,2]).

% I want four_a to fail - How?

test(four_a,[nondet]) :- naive_member(2,[1,1,2]).  % nondeterministic on last member
test(four_b)          :- member(2,[1,1,2]).        % deterministic on last member, needs no option

% Always work, indistinguishable from member/2, but what if I want four_all_a to fail if it leaves a 
% choicepoint open?

test(one_all_a  ,all(M=[1]))     :- naive_member(M,[1,2,3]),M=1.
test(two_all_a  ,all(M=[1,1]))   :- naive_member(M,[1,1,3]),M=1.
test(three_all_a,all(M=[1,1,1])) :- naive_member(M,[1,1,1]),M=1.
test(four_all_a ,all(M=[2]))     :- naive_member(M,[1,1,2]),M=2.

% Always work

test(one_all_b  ,all(M=[1]))     :- member(M,[1,2,3]),M=1.
test(two_all_b  ,all(M=[1,1]))   :- member(M,[1,1,3]),M=1.
test(three_all_b,all(M=[1,1,1])) :- member(M,[1,1,1]),M=1.
test(four_all_b ,all(M=[2]))     :- member(M,[1,1,2]),M=2.

:- end_tests(member_determinacy).

rt :- run_tests(member_determinacy).

The first set is what this is all intended to do.

I see no reason why would want to verify that a predicate succeeds and leaves a choice point (but no second answer). If you want to know whether it can produce multiple answers use the all recipe.

In the very unlikely case you want to test success is nondet, do

    call_cleanup(test_supposed_to_be_nondet, Det=true),
    assertion(var(Det)).

The all tests are supposed to run findall/3, so no non-determinism will be left.

2 Likes