Borderline case: in a plunit test case specify the "Formal" in the body to be used in the head "error" option

Another little problem: The last test in the series here succeeds, but should fail (I think):

:- begin_tests(correct_catch).

   % This test will succeed as it catches any exception that threw a term that
   % looks like "error(_,_)", which are all ISO standard exception terms.

   test("catch anything",[error(_)]) :-
      must_be(integer,foo).

   % This test will succeed as it expects "type_error" to be thrown,
   % which is what happens.
   
   test("correctly catch type error",error(type_error(_,_))) :-
      must_be(integer,foo).

   % This test will fail because it expects "domain_error" to be thrown,
   % but "type_error" is thrown instead
   
   test("wrongly catch domain error",[error(domain_error(_,_))]) :-
      must_be(integer,foo).

   % Unexpectedly, this test succeeds: it should not, unless the relationship
   % between the "Formal" appearing in the head and the "Formal" appearing in the
   % body is somehow broken when this code is preprocessed/rewritten. A hypotheses for why
   % this test succeeds would be the "Formal" of the head staying fresh in spite
   % of the body saying "Formal = domain_error(_,_)"
   
   test(one,[error(Formal)]) :-        % this catches the exception term thrown by must_be/2
      Formal = domain_error(_,_),  % this is not thorwn by the next instruction
      must_be(integer,foo).
            
:- end_tests(correct_catch).

The exception unwinds back to the catch/3 somewhere in the plunit code, udoing the Formal = domain_error(_,_) assignment. Typically, Prolog code contains few explicit calls to =/2 :slight_smile:

1 Like

Soooo… the solution is “avoid this case”?

Well, further instantiating the test options only works if the body succeeds. If it fails or throws an exception the instantiation is lost.

1 Like

Oh trivial. Sometimes reflexes of imperative programming are hard to to get rid of.