1 % Copyright (C) 2018-2019 Alaskan Emily, Transnat Games
3 % This software is provided 'as-is', without any express or implied
4 % warranty. In no event will the authors be held liable for any damages
5 % arising from the use of this software.
7 % Permission is granted to anyone to use this software for any purpose,
8 % including commercial applications, and to alter it and redistribute it
9 % freely, subject to the following restrictions:
11 % 1. The origin of this software must not be misrepresented; you must not
12 % claim that you wrote the original software. If you use this software
13 % in a product, an acknowledgment in the product documentation would be
14 % appreciated but is not required.
15 % 2. Altered source versions must be plainly marked as such, and must not be
16 % misrepresented as being the original software.
17 % 3. This notice may not be removed or altered from any source distribution.
21 %==============================================================================%
22 % Transnat Games Unit Testing Framework
23 % Provides some utilities to run unit tests on Mercury code.
24 % This is primarily meant to test purely functional code, preds with behavior
25 % strongly dependent on typeclasses, and parser/converter style code.
27 % Feathers in our cap:
28 % - mock framework which makes it fairly easy to mock typeclasses
29 % - Result comparison with acceptable error reporting
31 % - No exception handling whatsoever
32 % - No multi-process, meaning badly behaved native code can poison the well.
34 % But there are basically no other Mercury unit test frameworks to start with,
35 % so at least this gives us a foundation.
37 %==============================================================================%
39 :- include_module transunit.compare.
40 :- include_module transunit.compare_test.
41 :- include_module transunit.mock.
44 :- import_module list.
48 :- typeclass to_string(T) where [
49 func to_string(T) = string
52 :- typeclass compare(T) where [
53 func compare(T, T) = maybe.maybe_error
56 %-----------------------------------------------------------------------------%
58 % run_test(TestPred, TestName, !IO)
59 % Runs a pred, reporting success or failure depending on if it is true of not.
60 % The input pred's parameter is a dummy parameter.
61 :- pred run_test(pred(unit.unit), string, io.io, io.io).
62 :- mode run_test(pred(in) is semidet, in, di, uo) is det.
64 %-----------------------------------------------------------------------------%
65 % run_result_test(TestPred, TestName, !IO)
66 % Runs a pred, reporting success or failure depending on the maybe_error which
68 % This is intended to be run with preds from the compare_test module as the
70 % See run_result_test/6 for using input preds that use a different state
71 % variable type than io.io.
72 :- pred run_result_test(pred(maybe.maybe_error, io.io, io.io),
75 :- mode run_result_test(pred(out, di, uo) is det,
79 %-----------------------------------------------------------------------------%
80 % run_result_test(TestPred, TestName, !State, !IO)
81 % Runs a pred, reporting success or failure depending on the maybe_error which
83 % This does not share the IO state as run_result_test/4 does.
84 :- pred run_result_test(pred(maybe.maybe_error, State, State),
88 :- mode run_result_test(pred(out, in, out) is det,
92 :- mode run_result_test(pred(out, di, uo) is det,
96 :- mode run_result_test(pred(out, mdi, muo) is det,
101 %-----------------------------------------------------------------------------%
103 :- func test_result(maybe.maybe_error, string) = string.
105 %-----------------------------------------------------------------------------%
107 :- func success_message = string.
108 :- func failure_message = string.
110 %==============================================================================%
112 %==============================================================================%
114 :- use_module string.
116 %-----------------------------------------------------------------------------%
118 run_test(Pred, Name, !IO) :-
119 ( Pred(unit.unit) -> Result = success_message ; Result = failure_message ),
120 io.write_string(Result, !IO),
121 io.write_char((' '), !IO),
122 io.write_string(Name, !IO),
125 %-----------------------------------------------------------------------------%
127 run_result_test(Pred, Name, !IO) :-
129 io.write_string(test_result(Result, Name), !IO),
132 %-----------------------------------------------------------------------------%
134 run_result_test(Pred, Name, !State, !IO) :-
135 Pred(Result, !State),
136 io.write_string(test_result(Result, Name), !IO),
139 %-----------------------------------------------------------------------------%
141 test_result(maybe.ok, Name) = string.append(success_message, Suffix) :-
142 string.first_char(Suffix, (' '), Name).
144 test_result(maybe.error(E), Name) = string.append(failure_message, Suffix) :-
145 string.first_char(Start, (' '), Name),
146 string.first_char(Rest, ('\n'), E),
147 string.append(Start, Rest, Suffix).
149 %-----------------------------------------------------------------------------%
151 success_message = "Success ...".
152 failure_message = "Failure !!!".