1 % Copyright (C) 2018-2019 Alaskan Emily, Transnat Games.
3 % This software is provided 'as-is', without any express or implied warranty.
4 % In no event will the authors be held liable for any damages arising from
5 % 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.
16 % 2. Altered source versions must be plainly marked as such, and must not
17 % be misrepresented as being the original software.
19 % 3. This notice may not be removed or altered from any source distribution.
24 %==============================================================================%
25 % Transnat Games Unit Testing Framework
26 % Provides some utilities to run unit tests on Mercury code.
27 % This is primarily meant to test purely functional code, preds with behavior
28 % strongly dependent on typeclasses, and parser/converter style code.
30 % Feathers in our cap:
31 % - mock framework which makes it fairly easy to mock typeclasses
32 % - Result comparison with acceptable error reporting
34 % - No exception handling whatsoever
35 % - No multi-process, meaning badly behaved native code can poison the well.
37 % But there are basically no other Mercury unit test frameworks to start with,
38 % so at least this gives us a foundation.
40 %==============================================================================%
42 :- include_module transunit.compare.
43 :- include_module transunit.compare_test.
44 :- include_module transunit.mock.
47 :- import_module list.
51 :- typeclass to_string(T) where [
52 func to_string(T) = string
55 :- typeclass compare(T) where [
56 func compare(T, T) = maybe.maybe_error
59 %-----------------------------------------------------------------------------%
61 % run_test(TestPred, TestName, !IO)
62 % Runs a pred, reporting success or failure depending on if it is true of not.
63 % The input pred's parameter is a dummy parameter.
64 :- pred run_test(pred(unit.unit), string, io.io, io.io).
65 :- mode run_test(pred(in) is semidet, in, di, uo) is det.
67 %-----------------------------------------------------------------------------%
68 % run_result_test(TestPred, TestName, !IO)
69 % Runs a pred, reporting success or failure depending on the maybe_error which
71 % This is intended to be run with preds from the compare_test module as the
73 % See run_result_test/6 for using input preds that use a different state
74 % variable type than io.io.
75 :- pred run_result_test(pred(maybe.maybe_error, io.io, io.io),
78 :- mode run_result_test(pred(out, di, uo) is det,
82 %-----------------------------------------------------------------------------%
83 % run_result_test(TestPred, TestName, !State, !IO)
84 % Runs a pred, reporting success or failure depending on the maybe_error which
86 % This does not share the IO state as run_result_test/4 does.
87 :- pred run_result_test(pred(maybe.maybe_error, State, State),
91 :- mode run_result_test(pred(out, in, out) is det,
95 :- mode run_result_test(pred(out, di, uo) is det,
99 :- mode run_result_test(pred(out, mdi, muo) is det,
104 %-----------------------------------------------------------------------------%
106 :- func test_result(maybe.maybe_error, string) = string.
108 %-----------------------------------------------------------------------------%
110 :- func success_message = string.
111 :- func failure_message = string.
113 %==============================================================================%
115 %==============================================================================%
117 :- use_module string.
119 %-----------------------------------------------------------------------------%
121 run_test(Pred, Name, !IO) :-
122 ( Pred(unit.unit) -> Result = success_message ; Result = failure_message ),
123 io.write_string(Result, !IO),
124 io.write_char((' '), !IO),
125 io.write_string(Name, !IO),
128 %-----------------------------------------------------------------------------%
130 run_result_test(Pred, Name, !IO) :-
132 io.write_string(test_result(Result, Name), !IO),
135 %-----------------------------------------------------------------------------%
137 run_result_test(Pred, Name, !State, !IO) :-
138 Pred(Result, !State),
139 io.write_string(test_result(Result, Name), !IO),
142 %-----------------------------------------------------------------------------%
144 test_result(maybe.ok, Name) = string.append(success_message, Suffix) :-
145 string.first_char(Suffix, (' '), Name).
147 test_result(maybe.error(E), Name) = string.append(failure_message, Suffix) :-
148 string.first_char(Start, (' '), Name),
149 string.first_char(Rest, ('\n'), E),
150 string.append(Start, Rest, Suffix).
152 %-----------------------------------------------------------------------------%
154 success_message = "Success ...".
155 failure_message = "Failure !!!".