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