monotone

monotone Mtn Source Tree

Root/unit_tester.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#include <botan/botan.h>
22
23#include "option.hh"
24#include "unit_tests.hh"
25#include "sanity.hh"
26#include "current_exception.hh"
27#include "botan_pipe_cache.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 const *> 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
50// this is effectively basename(), but has no dependencies on
51// e.g. system_path being usable yet. The special-case for 'fis'
52// ensures that the unit tests of the unit tester itself will be
53// run first (see below).
54static string
55extract_group(char const *filename, bool fis)
56{
57 string result(filename);
58 string::size_type last_slash = result.find_last_of("/\\");
59 if (last_slash != string::npos)
60 result.erase(0, last_slash+1);
61 string::size_type first_dot = result.find_first_of('.');
62 if (first_dot != string::npos)
63 result.erase(first_dot, string::npos);
64 if (fis)
65 result.insert(0U, 1, '_');
66 return result;
67}
68
69unit_test::unit_test_case::unit_test_case(char const * g,
70 char const * n,
71 void (*f)(),
72 bool fis)
73 : group(extract_group(g, fis)), name(n), func(f), failure_is_success(fis)
74{
75 // All unit_test_case objects are statically allocated so this is safe.
76 unit_tests()[group][name] = this;
77}
78
79// Test state.
80static bool this_test_failed = false;
81
82namespace { struct require_failed {}; }
83
84static void log_state(char const * file, int line,
85 char const * kind, char const * msg)
86{
87 L(FL("%s:%s: %s: %s") % file % line % kind % msg);
88}
89
90// Report what we can about a fatal exception (caught in the outermost catch
91// handlers) which is from the std::exception hierarchy. In this case we
92// can access the exception object.
93static void log_exception(std::exception const & ex)
94{
95 using std::strcmp;
96 using std::strncmp;
97 char const * ex_name = typeid(ex).name();
98 char const * ex_dem = demangle_typename(ex_name);
99 char const * ex_what = ex.what();
100
101 if (ex_dem == 0)
102 ex_dem = ex_name;
103
104 // some demanglers stick "class" at the beginning of their output,
105 // which looks dumb in this context
106 if (!strncmp(ex_dem, "class ", 6))
107 ex_dem += 6;
108
109 // only print what() if it's interesting, i.e. nonempty and different
110 // from the name (mangled or otherwise) of the exception type.
111 if (ex_what == 0 || ex_what[0] == 0
112 || !strcmp(ex_what, ex_name)
113 || !strcmp(ex_what, ex_dem))
114 L(FL("UNCAUGHT EXCEPTION: %s") % ex_dem);
115 else
116 L(FL("UNCAUGHT EXCEPTION: %s: %s") % ex_dem % ex_what);
117}
118
119// Report what we can about a fatal exception (caught in the outermost catch
120// handlers) which is of unknown type. If we have the <cxxabi.h> interfaces,
121// we can at least get the type_info object.
122static void
123log_exception()
124{
125 std::type_info *ex_type = get_current_exception_type();
126 if (ex_type)
127 {
128 char const * ex_name = ex_type->name();
129 char const * ex_dem = demangle_typename(ex_name);
130 if (ex_dem == 0)
131 ex_dem = ex_name;
132 L(FL("UNCAUGHT EXCEPTION: %s") % ex_dem);
133 }
134 else
135 L(FL("UNCAUGHT EXCEPTION: unknown type"));
136}
137
138void unit_test::do_check(bool checkval, char const * file,
139 int line, char const * message)
140{
141 if (!checkval)
142 {
143 this_test_failed = true;
144 log_state(file, line, "CHECK FAILED", message);
145 }
146 else
147 log_state(file, line, "CHECK OK", message);
148}
149
150void unit_test::do_require(bool checkval, char const * file,
151 int line, char const * message)
152{
153 if (!checkval)
154 {
155 this_test_failed = true;
156 log_state(file, line, "REQUIRE FAILED", message);
157 throw require_failed();
158 }
159 else
160 log_state(file, line, "REQUIRE OK", message);
161}
162
163void unit_test::do_checkpoint(char const * file, int line,
164 char const * message)
165{
166 log_state(file, line, "CHECKPOINT", message);
167}
168
169// define the global objects needed by botan_pipe_cache.hh
170pipe_cache_cleanup * global_pipe_cleanup_object;
171Botan::Pipe * unfiltered_pipe;
172static unsigned char unfiltered_pipe_cleanup_mem[sizeof(cached_botan_pipe)];
173
174int main(int argc, char * argv[])
175{
176 bool help(false);
177 string test_to_run;
178
179 global_sanity.initialize(argc, argv, "C"); // we didn't call setlocale
180
181 try
182 {
183 option::concrete_option_set os;
184 os("help,h", "display help message", option::setter(help))
185 ("--", "", option::setter(test_to_run));
186
187 os.from_command_line(argc, argv);
188
189 if (help)
190 {
191 cout << (FL("Usage: %s [-h|--help] [test]\n"
192 " With no arguments, lists all test cases.\n"
193 " With the name of a test case, runs that test.\n"
194 " -h or --help prints this message.\n") % argv[0]);
195 return 0;
196 }
197 }
198 catch (option::option_error const & e)
199 {
200 cerr << argv[0] << ": " << e.what() << '\n';
201 return 2;
202 }
203
204 if (test_to_run == "")
205 {
206 for (group_list_t::const_iterator i = unit_tests().begin();
207 i != unit_tests().end(); i++)
208 for (test_list_t::const_iterator j = i->second.begin();
209 j != i->second.end(); ++j)
210 cout << i->first << ":" << j->first << "\n";
211
212 return 0;
213 }
214
215
216 // set up some global state before running the tests
217 // keep this in sync with monotone.cc, except for selftest=1 here, =0 there
218 Botan::LibraryInitializer acquire_botan("thread_safe=0 selftest=1 "
219 "seed_rng=1 use_engines=0 "
220 "secure_memory=1 fips140=0");
221 // and caching for botan pipes
222 pipe_cache_cleanup acquire_botan_pipe_caching;
223 unfiltered_pipe = new Botan::Pipe;
224 new (unfiltered_pipe_cleanup_mem) cached_botan_pipe(unfiltered_pipe);
225
226 // Make clog and cout use the same streambuf as cerr; this ensures
227 // that all messages will appear in the order written, no matter what
228 // stream each one is written to.
229 clog.rdbuf(cerr.rdbuf());
230 cout.rdbuf(cerr.rdbuf());
231
232 global_sanity.set_debug();
233
234 string::size_type sep = test_to_run.find(":");
235
236 if (sep == string::npos) // it's a group name
237 {
238 cerr << argv[0] << ": must specify a test, not a group, to run\n";
239 return 2;
240 }
241
242 string group, test;
243 group = test_to_run.substr(0, sep);
244 test = test_to_run.substr(sep+1, string::npos);
245
246 group_list_t::const_iterator g = unit_tests().find(group);
247
248 if (g == unit_tests().end())
249 {
250 cerr << argv[0] << ": unrecognized test group: "
251 << group << '\n';
252 return 2;
253 }
254
255 test_list_t::const_iterator t = g->second.find(test);
256 if (t == g->second.end())
257 {
258 cerr << argv[0] << ": unrecognized test: "
259 << group << ':' << test << '\n';
260 return 2;
261 }
262
263 L(FL("Beginning test %s:%s") % group % test);
264
265 try
266 {
267 t->second->func();
268 }
269 catch(require_failed &)
270 {
271 // no action required
272 }
273 catch(std::exception & e)
274 {
275 log_exception(e);
276 this_test_failed = true;
277 }
278 catch(...)
279 {
280 log_exception();
281 this_test_failed = true;
282 }
283
284 if (this_test_failed && !t->second->failure_is_success)
285 {
286 L(FL("Test %s:%s failed.\n") % group % test);
287 return 1;
288 }
289 else
290 {
291 L(FL("Test %s:%s succeeded.\n") % group % test);
292 return 0;
293 }
294}
295
296// Global sanity object. We don't want to depend on ui.
297struct unit_tester_sanity : public sanity
298{
299 void inform_log(std::string const &msg)
300 { cout << msg << "\n"; }
301 void inform_message(std::string const &msg)
302 { cout << msg << "\n"; }
303 void inform_warning(std::string const &msg)
304 { cerr << "warning: " << msg << "\n"; }
305 void inform_error(std::string const &msg)
306 { cerr << "error: " << msg << "\n"; }
307};
308unit_tester_sanity real_sanity;
309sanity & global_sanity = real_sanity;
310
311// These are tests of the unit testing mechanism itself. They would all
312// fail, but we make use of a special mechanism to convert that failure
313// into a success. Since we don't want that mechanism used elsewhere,
314// the necessary definition macro is defined here and not in unit_test.hh.
315
316#define NEGATIVE_UNIT_TEST(TEST) \
317 namespace unit_test { \
318 static void t_##TEST(); \
319 static const unit_test_case r_##TEST \
320 (__FILE__, #TEST, t_##TEST, true); \
321 } \
322 static void unit_test::t_##TEST()
323
324#include <stdexcept>
325
326NEGATIVE_UNIT_TEST(fail_check)
327{
328 UNIT_TEST_CHECKPOINT("checkpoint");
329 UNIT_TEST_CHECK(false);
330 UNIT_TEST_CHECK(false);
331}
332
333NEGATIVE_UNIT_TEST(fail_require)
334{
335 UNIT_TEST_CHECKPOINT("checkpoint");
336 UNIT_TEST_REQUIRE(false);
337 UNIT_TEST_CHECK(false);
338}
339
340NEGATIVE_UNIT_TEST(fail_throw)
341{
342 UNIT_TEST_CHECK_THROW(string().size(), int);
343}
344
345NEGATIVE_UNIT_TEST(fail_nothrow)
346{
347 UNIT_TEST_CHECK_NOT_THROW(throw int(), int);
348}
349
350NEGATIVE_UNIT_TEST(uncaught)
351{
352 throw int();
353}
354
355NEGATIVE_UNIT_TEST(uncaught_std)
356{
357 throw std::bad_exception();
358}
359
360NEGATIVE_UNIT_TEST(uncaught_std_what)
361{
362 throw std::runtime_error("There is no spoon.");
363}
364
365// Local Variables:
366// mode: C++
367// fill-column: 76
368// c-file-style: "gnu"
369// indent-tabs-mode: nil
370// End:
371// 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