monotone

monotone Mtn Source Tree

Root/unit_tests.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <map>
12#include "vector.hh"
13#include <iostream>
14#include <fstream>
15#include <iomanip>
16#include <cstdlib>
17#include <cstring>
18#include <cerrno>
19
20#include <boost/function.hpp>
21
22#include "botan/botan.h"
23#include "option.hh"
24#include "unit_tests.hh"
25#include "sanity.hh"
26#include "ui.hh"
27#include "current_exception.hh"
28
29using std::map;
30using std::pair;
31using std::make_pair;
32using std::vector;
33using std::string;
34using std::cout;
35using std::cerr;
36using std::clog;
37using std::exit;
38
39typedef unit_test::unit_test_case test_t;
40typedef map<string const, test_t> test_list_t;
41typedef map<string const, test_list_t> group_list_t;
42
43// This is used by other global constructors, so initialize on demand.
44static group_list_t & unit_tests()
45{
46 static group_list_t tests;
47 return tests;
48}
49
50unit_test::unit_test_case::unit_test_case(char const * group,
51 char const * name,
52 void (*func)(),
53 bool fis)
54 : group(group), name(name), func(func), failure_is_success(fis)
55{
56 unit_tests()[group][name] = *this;
57}
58
59unit_test::unit_test_case::unit_test_case()
60{}
61
62// Test state.
63static bool this_test_failed = false;
64
65namespace { struct require_failed {}; }
66
67static void log_state(char const * file, int line,
68 char const * kind, char const * msg)
69{
70 L(FL("%s:%s: %s: %s") % file % line % kind % msg);
71}
72
73// Report what we can about a fatal exception (caught in the outermost catch
74// handlers) which is from the std::exception hierarchy. In this case we
75// can access the exception object.
76static void log_exception(std::exception const & ex)
77{
78 using std::strcmp;
79 using std::strncmp;
80 char const * ex_name = typeid(ex).name();
81 char const * ex_dem = demangle_typename(ex_name);
82 char const * ex_what = ex.what();
83
84 if (ex_dem == 0)
85 ex_dem = ex_name;
86
87 // some demanglers stick "class" at the beginning of their output,
88 // which looks dumb in this context
89 if (!strncmp(ex_dem, "class ", 6))
90 ex_dem += 6;
91
92 // only print what() if it's interesting, i.e. nonempty and different
93 // from the name (mangled or otherwise) of the exception type.
94 if (ex_what == 0 || ex_what[0] == 0
95 || !strcmp(ex_what, ex_name)
96 || !strcmp(ex_what, ex_dem))
97 L(FL("UNCAUGHT EXCEPTION: %s") % ex_dem);
98 else
99 L(FL("UNCAUGHT EXCEPTION: %s: %s") % ex_dem % ex_what);
100}
101
102// Report what we can about a fatal exception (caught in the outermost catch
103// handlers) which is of unknown type. If we have the <cxxabi.h> interfaces,
104// we can at least get the type_info object.
105static void
106log_exception()
107{
108 std::type_info *ex_type = get_current_exception_type();
109 if (ex_type)
110 {
111 char const * ex_name = ex_type->name();
112 char const * ex_dem = demangle_typename(ex_name);
113 if (ex_dem == 0)
114 ex_dem = ex_name;
115 L(FL("UNCAUGHT EXCEPTION: %s") % ex_dem);
116 }
117 else
118 L(FL("UNCAUGHT EXCEPTION: unknown type"));
119}
120
121void unit_test::do_check(bool checkval, char const * file,
122 int line, char const * message)
123{
124 if (!checkval)
125 {
126 this_test_failed = true;
127 log_state(file, line, "CHECK FAILED", message);
128 }
129 else
130 log_state(file, line, "CHECK OK", message);
131}
132
133void unit_test::do_require(bool checkval, char const * file,
134 int line, char const * message)
135{
136 if (!checkval)
137 {
138 this_test_failed = true;
139 log_state(file, line, "REQUIRE FAILED", message);
140 throw require_failed();
141 }
142 else
143 log_state(file, line, "REQUIRE OK", message);
144}
145
146void unit_test::do_checkpoint(char const * file, int line,
147 char const * message)
148{
149 log_state(file, line, "CHECKPOINT", message);
150}
151
152static int run_test(test_t test)
153{
154}
155
156int main(int argc, char * argv[])
157{
158 bool help(false);
159 string test_to_run;
160
161 ui.initialize();
162 ui.prog_name = argv[0];
163 global_sanity.initialize(argc, argv, "C"); // we didn't call setlocale
164
165 try
166 {
167 option::concrete_option_set os;
168 os("help,h", "display help message", option::setter(help))
169 ("--", "", option::setter(test_to_run));
170
171 os.from_command_line(argc, argv);
172
173 if (help)
174 {
175 cout << (FL("Usage: %s [-h|--help] [test]\n"
176 " With no arguments, lists all test cases.\n"
177 " With the name of a test case, runs that test.\n"
178 " -h or --help prints this message.\n") % argv[0]);
179 return 0;
180 }
181 }
182 catch (option::option_error const & e)
183 {
184 cerr << argv[0] << ": " << e.what() << '\n';
185 return 2;
186 }
187
188 if (test_to_run == "")
189 {
190 for (group_list_t::const_iterator i = unit_tests().begin();
191 i != unit_tests().end(); i++)
192 for (test_list_t::const_iterator j = i->second.begin();
193 j != i->second.end(); ++j)
194 cout << i->first << ":" << j->first << "\n";
195
196 return 0;
197 }
198
199
200 // set up some global state before running the tests
201 Botan::LibraryInitializer::initialize();
202
203 // Make clog and cout use the same streambuf as cerr; this ensures
204 // that all messages will appear in the order written, no matter what
205 // stream each one is written to.
206 clog.rdbuf(cerr.rdbuf());
207 cout.rdbuf(cerr.rdbuf());
208
209 global_sanity.set_debug();
210
211 string::size_type sep = test_to_run.find(":");
212
213 if (sep == string::npos) // it's a group name
214 {
215 cerr << argv[0] << ": must specify a test, not a group, to run\n";
216 return 2;
217 }
218
219 string group, test;
220 group = test_to_run.substr(0, sep);
221 test = test_to_run.substr(sep+1, string::npos);
222
223 group_list_t::const_iterator g = unit_tests().find(group);
224
225 if (g == unit_tests().end())
226 {
227 cerr << argv[0] << ": unrecognized test group: "
228 << group << '\n';
229 return 2;
230 }
231
232 test_list_t::const_iterator t = g->second.find(test);
233 if (t == g->second.end())
234 {
235 cerr << argv[0] << ": unrecognized test: "
236 << group << ':' << test << '\n';
237 return 2;
238 }
239
240 L(FL("Beginning test %s:%s") % group % test);
241
242 try
243 {
244 t->second.func();
245 }
246 catch(require_failed &)
247 {
248 // no action required
249 }
250 catch(std::exception & e)
251 {
252 log_exception(e);
253 this_test_failed = true;
254 }
255 catch(...)
256 {
257 log_exception();
258 this_test_failed = true;
259 }
260
261 if (this_test_failed && !t->second.failure_is_success)
262 {
263 L(FL("Test %s:%s failed.\n") % group % test);
264 return 1;
265 }
266 else
267 {
268 L(FL("Test %s:%s succeeded.\n") % group % test);
269 return 0;
270 }
271}
272
273// Stub for options.cc's sake.
274void
275localize_monotone()
276{
277}
278
279// These are tests of the unit testing mechanism itself. They would all
280// fail, but we make use of a special mechanism to convert that failure
281// into a success. Since we don't want that mechanism used elsewhere,
282// the necessary definition macro is defined here and not in unit_test.hh.
283
284#define NEGATIVE_UNIT_TEST(GROUP, TEST) \
285 namespace unit_test { \
286 static void t_##GROUP##_##TEST(); \
287 static unit_test_case r_##GROUP##_##TEST \
288 (#GROUP, #TEST, t_##GROUP##_##TEST, true); \
289 } \
290 static void unit_test::t_##GROUP##_##TEST()
291
292#include <stdexcept>
293
294NEGATIVE_UNIT_TEST(_unit_tester, fail_check)
295{
296 UNIT_TEST_CHECKPOINT("checkpoint");
297 UNIT_TEST_CHECK(false);
298 UNIT_TEST_CHECK(false);
299}
300
301NEGATIVE_UNIT_TEST(_unit_tester, fail_require)
302{
303 UNIT_TEST_CHECKPOINT("checkpoint");
304 UNIT_TEST_REQUIRE(false);
305 UNIT_TEST_CHECK(false);
306}
307
308NEGATIVE_UNIT_TEST(_unit_tester, fail_throw)
309{
310 UNIT_TEST_CHECK_THROW(string().size(), int);
311}
312
313NEGATIVE_UNIT_TEST(_unit_tester, fail_nothrow)
314{
315 UNIT_TEST_CHECK_NOT_THROW(throw int(), int);
316}
317
318NEGATIVE_UNIT_TEST(_unit_tester, uncaught)
319{
320 throw int();
321}
322
323NEGATIVE_UNIT_TEST(_unit_tester, uncaught_std)
324{
325 throw std::bad_exception();
326}
327
328NEGATIVE_UNIT_TEST(_unit_tester, uncaught_std_what)
329{
330 throw std::runtime_error("There is no spoon.");
331}
332
333// Local Variables:
334// mode: C++
335// fill-column: 76
336// c-file-style: "gnu"
337// indent-tabs-mode: nil
338// End:
339// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status