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 <map>
11#include <vector>
12#include <string>
13#include <iostream>
14#include <fstream>
15#include <cstdlib>
16#include <cstring>
17#include <cerrno>
18
19#include <boost/version.hpp>
20#include <boost/function.hpp>
21#include <boost/test/unit_test_suite.hpp>
22#if BOOST_VERSION >= 103300
23#include <boost/test/parameterized_test.hpp>
24#endif
25
26#include "botan/botan.h"
27#include "option.hh"
28#include "unit_tests.hh"
29#include "sanity.hh"
30#include "ui.hh"
31
32using std::multimap;
33using std::pair;
34using std::make_pair;
35using std::vector;
36using std::string;
37using std::cout;
38using std::cerr;
39using std::clog;
40using std::exit;
41using std::atexit;
42using boost::unit_test::test_suite;
43typedef boost::unit_test::test_case boost_unit_test_case;
44
45// This must be a pointer. It is used by the constructor below, which is
46// used to construct file-scope objects in different files; if it were the
47// actual object there would be no way to guarantee that this gets
48// constructed first. Instead, we initialize it ourselves the first time we
49// use it.
50typedef multimap<string const,
51 boost_unit_test_case *> unit_test_list_t;
52
53static unit_test_list_t * unit_tests;
54
55unit_test::unit_test_case::unit_test_case(char const * group,
56 char const * test,
57 void (*func)())
58{
59 if (unit_tests == NULL)
60 unit_tests = new unit_test_list_t;
61
62 boost_unit_test_case * tcase = BOOST_TEST_CASE(func);
63#if BOOST_VERSION >= 103300
64 tcase->p_name.set(string(test));
65#endif
66 unit_tests->insert(make_pair(string(group), tcase));
67}
68
69// This appears to be the sanest way to get progress notifications on a
70// per-test-group level.
71static void notifier(string const & group)
72{
73 cerr << group << "...\n";
74}
75
76// A teebuf implements the basic_streambuf interface and forwards all
77// operations to two 'targets'. This is used to get progress messages sent
78// to both the log file and the terminal. Note that it cannot be used for
79// reading (and does not implement the read-side interfaces) nor is it
80// seekable.
81namespace {
82 template <class C, class T = std::char_traits<C> >
83 class basic_teebuf : public std::basic_streambuf<C, T>
84 {
85 public:
86 // grmbl grmbl typedefs not inherited grmbl.
87 typedef C char_type;
88 typedef T traits_type;
89 typedef typename T::int_type int_type;
90 typedef typename T::pos_type pos_type;
91 typedef typename T::off_type off_type;
92
93 virtual ~basic_teebuf() {}
94 basic_teebuf(std::basic_streambuf<C,T> * t1,
95 std::basic_streambuf<C,T> * t2)
96 : std::basic_streambuf<C,T>(), target1(t1), target2(t2) {}
97
98 protected:
99 std::basic_streambuf<C, T> * target1;
100 std::basic_streambuf<C, T> * target2;
101
102 virtual void imbue(std::locale const & loc)
103 {
104 target1->pubimbue(loc);
105 target2->pubimbue(loc);
106 }
107 virtual basic_teebuf * setbuf(C * p, std::streamsize n)
108 {
109 target1->pubsetbuf(p,n);
110 target2->pubsetbuf(p,n);
111 return this;
112 }
113 virtual int sync()
114 {
115 int r1 = target1->pubsync();
116 int r2 = target2->pubsync();
117 return (r1 == 0 && r2 == 0) ? 0 : -1;
118 }
119
120 // Not overriding the seek, get, or putback functions produces a
121 // streambuf which always fails those operations, thanks to the defaults
122 // in basic_streambuf.
123
124 // As we do no buffering in this object, it would be correct to forward
125 // xsputn to the targets. However, this could cause a headache in the
126 // case that one target consumed fewer characters than the other. As
127 // this streambuf is not used for much data (most of the chatter goes to
128 // clog) it is okay to fall back on the dumb-but-reliable default xsputn
129 // in basic_streambuf.
130
131 // You might think that overflow in this object should forward to
132 // overflow in the targets, but that would defeat buffering in those
133 // objects.
134 virtual int_type overflow(int_type c = traits_type::eof())
135 {
136 if (!traits_type::eq_int_type(c,traits_type::eof()))
137 {
138 int_type r1 = target1->sputc(c);
139 int_type r2 = target2->sputc(c);
140 if (r1 == traits_type::eof() || r2 == traits_type::eof())
141 return traits_type::eof();
142 return traits_type::not_eof(c);
143 }
144 else
145 {
146 return sync() ? traits_type::eof() : traits_type::not_eof(c);
147 }
148 }
149 };
150 typedef basic_teebuf<char> teebuf;
151}
152
153test_suite * init_unit_test_suite(int argc, char * argv[])
154{
155 bool help(false);
156 bool list_groups(false);
157 bool list_tests(false);
158 bool debug(false);
159 string log;
160 vector<string> tests;
161
162 try
163 {
164 option::concrete_option_set os;
165 os("help,h", "display help message", option::setter(help))
166 ("list-groups,l", "list all test groups", option::setter(list_groups))
167 ("list-tests,L", "list all test cases", option::setter(list_tests))
168 ("debug", "write verbose debug log to stderr", option::setter(debug))
169 ("log", "write verbose debug log to this file"
170 " (default is unit_tests.log)", option::setter(log))
171 ("--", "", option::setter(tests));
172
173 os.from_command_line(argc, argv);
174
175 if (help)
176 {
177 cout << (FL("Usage: %s [options] [tests]\nOptions") % argv[0])
178 << os.get_usage_str() << '\n';
179 exit(0);
180 }
181 }
182 catch (option::option_error const & e)
183 {
184 cerr << argv[0] << ": " << e.what() << '\n';
185 exit(2);
186 }
187
188 if (list_groups && list_tests)
189 {
190 cerr << argv[0]
191 << ": only one of --list-groups and --list-tests at a time\n";
192 exit(2);
193 }
194
195 if (list_groups)
196 {
197 string last;
198 for (unit_test_list_t::const_iterator i = unit_tests->begin();
199 i != unit_tests->end();
200 i++)
201 if (last != (*i).first)
202 {
203 cout << (*i).first << '\n';
204 last = (*i).first;
205 }
206 exit(0);
207 }
208
209 if (list_tests)
210 {
211 for (unit_test_list_t::const_iterator i = unit_tests->begin();
212 i != unit_tests->end();
213 i++)
214 cout << (*i).first << ':' << (*i).second->p_name.get() << '\n';
215 exit(0);
216 }
217
218 // If we get here, we are really running the test suite.
219 test_suite * suite = BOOST_TEST_SUITE("monotone unit tests");
220
221 if (tests.size() == 0) // run all tests
222 {
223 string last;
224 for (unit_test_list_t::const_iterator i = unit_tests->begin();
225 i != unit_tests->end();
226 i++)
227 {
228 if (last != (*i).first)
229 {
230 suite->add(BOOST_PARAM_TEST_CASE(notifier,
231 &((*i).first),
232 &((*i).first)+1));
233 last = (*i).first;
234 }
235 suite->add((*i).second);
236 }
237 }
238 else
239 {
240 bool unrecognized = false;
241
242 for(vector<string>::const_iterator i = tests.begin();
243 i != tests.end();
244 i++)
245 {
246 string group, test;
247 string::size_type sep = (*i).find(":");
248
249 if (sep >= (*i).length()) // it's a group name
250 group = *i;
251 else
252 {
253 group = (*i).substr(0, sep);
254 test = (*i).substr(sep+1, string::npos);
255 }
256
257 pair<unit_test_list_t::const_iterator,
258 unit_test_list_t::const_iterator>
259 range = unit_tests->equal_range(group);
260
261 if (range.first == range.second)
262 {
263 unrecognized = true;
264 cerr << argv[0] << ": unrecognized test group: "
265 << group << '\n';
266 continue;
267 }
268
269 suite->add(BOOST_PARAM_TEST_CASE(notifier,
270 &(*i), &(*i)+1));
271
272 bool found = false;
273 for (unit_test_list_t::const_iterator j = range.first;
274 j != range.second;
275 j++)
276 if (test == "" || test == (*j).second->p_name.get())
277 {
278 suite->add((*j).second);
279 found = true;
280 }
281 if (!found)
282 {
283 unrecognized = true;
284 cerr << argv[0] << ": unrecognized test: "
285 << group << ':' << test << '\n';
286 }
287 }
288
289 if (unrecognized)
290 exit(1);
291 }
292
293 // set up some global state before running the tests
294 ui.initialize();
295 ui.prog_name = argv[0];
296 global_sanity.initialize(argc, argv, "C"); // we didn't call setlocale
297 Botan::LibraryInitializer::initialize();
298
299 if (!debug)
300 {
301 // We would _like_ to use ui.redirect_log_to() but it takes a
302 // system_path and we're not set up to use that here.
303 char const * logname;
304 if (log.empty())
305 logname = "unit_tests.log";
306 else
307 logname = log.c_str();
308
309 std::filebuf * logbuf = new std::filebuf;
310 if (!logbuf->open(logname, std::ios_base::out|std::ios_base::trunc))
311 {
312 char const * syserr = std::strerror(errno);
313 cerr << argv[0] << ": failed to open " << logname
314 << ": " << syserr << '\n';
315 exit(1);
316 }
317 clog.rdbuf(logbuf);
318
319 // Redirect both cerr and cout to a teebuf which will send their data
320 // to both the logfile and wherever cerr currently goes.
321 teebuf * progress_output = new teebuf(logbuf, cerr.rdbuf());
322 cerr.rdbuf(progress_output);
323 cout.rdbuf(progress_output);
324 }
325 else
326 {
327 if (!log.empty())
328 {
329 cerr << argv[0]
330 << ": only one of --debug and --log at a time\n";
331 exit(2);
332 }
333
334 // Make clog and cout use the same streambuf as cerr; this ensures
335 // that all messages will appear in the order written, no matter what
336 // stream each one is written to.
337 clog.rdbuf(cerr.rdbuf());
338 cout.rdbuf(cerr.rdbuf());
339 }
340
341 global_sanity.set_debug();
342 return suite;
343}
344
345// Stub for options.cc's sake.
346void
347localize_monotone()
348{
349}
350
351// Local Variables:
352// mode: C++
353// fill-column: 76
354// c-file-style: "gnu"
355// indent-tabs-mode: nil
356// End:
357// 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