feisty meow concerns codebase 2.140
unit_base.cpp
Go to the documentation of this file.
1/*
2* Name : unit test tools
3* Author : Chris Koeritz
4**
5* Copyright (c) 2009-$now By Author. This program is free software; you can *
6* redistribute it and/or modify it under the terms of the GNU General Public *
7* License as published by the Free Software Foundation; either version 2 of *
8* the License or (at your option) any later version. This is online at: *
9* http://www.fsf.org/copyleft/gpl.html *
10* Please send any updates to: fred@gruntose.com *
11*/
12
13#include "unit_base.h"
14
16#include <basis/astring.h>
17#include <basis/functions.h>
21#include <filesystem/filename.h>
24
25using namespace application;
26using namespace basis;
27using namespace configuration;
28using namespace filesystem;
29using namespace loggers;
30using namespace structures;
31using namespace textual;
32
33#define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s)
34
35namespace unit_test {
36
37const int EXPECTED_MAXIMUM_TESTS = 10008;
38
39const char *name_for_bools(bool bv)
40{ if (bv) return "true"; else return "false"; }
41
43: c_lock(),
44 c_total_tests(0),
45 c_passed_tests(0),
46 c_successful(EXPECTED_MAXIMUM_TESTS),
48{
49}
50
52
53int unit_base::total_tests() const { return c_total_tests; }
54
55int unit_base::passed_tests() const { return c_passed_tests; }
56
58{ return c_total_tests - c_passed_tests; }
59
60void unit_base::count_successful_test(const basis::astring &class_name, const basis::astring &test_name)
61{
62 auto_synchronizer synch(c_lock);
63 outcome ret = c_successful.add(class_name + " -- " + test_name, "");
64 if (ret == common::IS_NEW) {
65 c_total_tests++;
66 c_passed_tests++;
67 }
68}
69
71 const astring &diag)
72{
73 auto_synchronizer synch(c_lock);
74 count_successful_test(class_name, test_name);
75//hmmm: kind of lame bailout on printing this.
76// it gets very very wordy if it's left in.
77#ifdef DEBUG
78 astring message = astring("OKAY: ") + class_name + " in test [" + test_name + "]";
79 BASE_LOG(message);
80#endif
81}
82
83void unit_base::count_failed_test(const basis::astring &class_name, const basis::astring &test_name,
84 const basis::astring &diag)
85{
86 auto_synchronizer synch(c_lock);
87 outcome ret = c_failed.add(class_name + " -- " + test_name, diag);
88 if (ret == common::IS_NEW) {
89 c_total_tests++;
90 }
91}
92
93void unit_base::record_fail(const basis::astring &class_name, const astring &test_name, const astring &diag)
94{
95 count_failed_test(class_name, test_name, diag);
96 astring message = astring("\nFAIL: ") + class_name + " in test [" + test_name + "]\n" + diag + "\n";
97 BASE_LOG(message);
98}
99
100void unit_base::record_successful_assertion(const basis::astring &class_name, const astring &test_name,
101 const astring &assertion_name)
102{
104 astring("no problem with: ") + assertion_name);
105}
106
107void unit_base::record_failed_object_compare(const hoople_standard &a, const hoople_standard &b,
108 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
109{
110 astring a_state, b_state;
111 a.text_form(a_state);
112 b.text_form(b_state);
114 astring("Error in assertion ") + assertion_name + ":\n"
115 + "==============\n"
116 + a_state + "\n"
117 + "=== versus ===\n"
118 + b_state + "\n"
119 + "==============");
120}
121
122void unit_base::record_failed_int_compare(int a, int b,
123 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
124{
126 astring("Error in assertion ") + assertion_name
127 + a_sprintf(": inappropriate values: %d & %d", a, b));
128}
129
130void unit_base::record_failed_double_compare(double a, double b,
131 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
132{
134 astring("Error in assertion ") + assertion_name
135 + a_sprintf(": inappropriate values: %f & %f", a, b));
136}
137
138void unit_base::record_failed_pointer_compare(const void *a, const void *b,
139 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
140{
142 astring("Error in assertion ") + assertion_name
143 + a_sprintf(": inappropriate values: %p & %p", a, b));
144}
145
146void unit_base::record_failed_tf_assertion(bool result,
147 bool expected_result, const basis::astring &class_name, const astring &test_name,
148 const astring &assertion_name)
149{
150 record_fail(class_name, test_name, astring("Error in assertion ") + assertion_name
151 + ": expected " + name_for_bools(expected_result)
152 + " but found " + name_for_bools(result));
153}
154
156 const basis::astring &class_name, const astring &test_name, const astring &diag)
157{
158 FUNCDEF("assert_equal");
159 bool are_equal = (a == b);
160 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
161 else record_failed_object_compare(a, b, class_name, test_name, func);
162}
163
165 const basis::astring &class_name, const astring &test_name, const astring &diag)
166{
167 FUNCDEF("assert_not_equal");
168 bool are_equal = (a == b);
169 if (!are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
170 else record_failed_object_compare(a, b, class_name, test_name, func);
171}
172
173const char *byte_array_phrase = "byte_array[%d]"; // for printing a text form of byte_array.
174
176 const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
177{
178 FUNCDEF("assert_equal");
179 bool are_equal = (a == b);
180 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
181 else {
184 record_failed_object_compare(a_s, b_s, class_name, test_name, func);
185 }
186}
187
189 const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
190{
191 FUNCDEF("assert_not_equal");
192 bool are_equal = (a == b);
193 if (!are_equal) record_successful_assertion(class_name, test_name, func);
194 else {
197 record_failed_object_compare(a_s, b_s, class_name, test_name, func);
198 }
199}
200
201void unit_base::assert_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
202{
203 FUNCDEF("assert_equal integer");
204 bool are_equal = a == b;
205 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
206 else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
207}
208
209void unit_base::assert_not_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
210{
211 FUNCDEF("assert_not_equal integer");
212 bool are_inequal = a != b;
213 if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
214 else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
215}
216
217void unit_base::assert_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
218{
219 FUNCDEF("assert_equal double");
220 bool are_equal = a == b;
221 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
222 else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
223}
224
225void unit_base::assert_not_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
226{
227 FUNCDEF("assert_not_equal double");
228 bool are_inequal = a != b;
229 if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
230 else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
231}
232
233
234void unit_base::assert_equal(const void *a, const void *b,
235 const basis::astring &class_name, const astring &test_name, const astring &diag)
236{
237 FUNCDEF("assert_equal void pointer");
238 bool are_equal = a == b;
239 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
240 else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
241}
242
243void unit_base::assert_not_equal(const void *a, const void *b,
244 const basis::astring &class_name, const astring &test_name, const astring &diag)
245{
246 FUNCDEF("assert_not_equal void pointer");
247 bool are_inequal = a != b;
248 if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
249 else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
250}
251
252void unit_base::assert_true(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
253{
254 FUNCDEF("assert_true");
255 if (result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
256 else record_failed_tf_assertion(result, true, class_name, test_name, astring(func) + ": " + diag);
257}
258
259void unit_base::assert_false(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
260{
261 FUNCDEF("assert_false");
262 if (!result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
263 else record_failed_tf_assertion(result, false, class_name, test_name, astring(func) + ": " + diag);
264}
265
267{
268 auto_synchronizer synch(c_lock);
269 int to_return = 0; // return success until we know otherwise.
270
271 astring keyword = "FAILURE"; // but be pessimistic about overall result at first..?
272
273// BASE_LOG(a_sprintf("total tests %d passed tests %d", c_total_tests, c_passed_tests));
274
275 // check whether we really did succeed or not.
276 if (c_total_tests == c_passed_tests) keyword = "SUCCESS"; // success!
277 else to_return = 12; // a failure return.
278
279 if (!c_total_tests) keyword = "LAMENESS (no tests!)"; // boring!
280
281// astring message = keyword + " for "
282// + application_configuration::application_name()
283// + a_sprintf(": %d of %d atomic tests passed.",
284// c_passed_tests, c_total_tests);
285// BASE_LOG(message);
286
287 astring message = keyword + " for "
289 + a_sprintf(": %d of %d unit tests passed.",
290 c_successful.symbols(), c_successful.symbols() + c_failed.symbols());
291 BASE_LOG(message);
292
293 // send an xml file out for the build engine to analyze.
294 write_cppunit_xml();
295
296 return to_return;
297}
298
299void unit_base::write_cppunit_xml()
300{
301 auto_synchronizer synch(c_lock);
302 astring logs_dir = environment::get("FEISTY_MEOW_LOGS");
303 if (logs_dir == astring::empty_string()) logs_dir = "logs"; // uhhh.
304 astring outfile = logs_dir + "/"
306 + ".xml";
307
308//BASE_LOG(astring("outfile is ") + outfile);
309
310 int id = 1;
311
312 xml_generator report;
313 string_table attribs;
314
315 // we are emulating a cppunit xml output.
316
317 report.open_tag("TestRun");
318
319 report.open_tag("FailedTests");
320 for (int i = 0; i < c_failed.symbols(); i++) {
321 attribs.reset();
322 attribs.add("id", a_sprintf("%d", id++));
323 report.open_tag("FailedTest", attribs);
324 attribs.reset();
325//hmmm: does open_tag eat the attribs? we could stop worrying about resetting.
326 report.open_tag("Name");
327 report.add_content(c_failed.name(i));
328 report.close_tag("Name");
329
330 report.open_tag("FailureType", attribs);
331 report.add_content("Assertion");
332 report.close_tag("FailureType");
333
334 report.open_tag("Location", attribs);
335
336 report.open_tag("File", attribs);
338 report.close_tag("File");
339
340 report.open_tag("Line", attribs);
341 report.add_content("0");
342 report.close_tag("Line");
343
344 report.close_tag("Location");
345
346 report.open_tag("Message");
347 report.add_content(c_failed[i]);
348 report.close_tag("Message");
349
350 report.close_tag("FailedTest");
351 }
352 report.close_tag("FailedTests");
353
354 report.open_tag("SuccessfulTests");
355 for (int i = 0; i < c_successful.symbols(); i++) {
356 attribs.reset();
357 attribs.add("id", a_sprintf("%d", id++));
358 attribs.reset();
359 report.open_tag("Test", attribs);
360 report.open_tag("Name");
361 report.add_content(c_successful.name(i));
362 report.close_tag("Name");
363 report.close_tag("Test");
364 }
365 report.close_tag("SuccessfulTests");
366
367 report.open_tag("Statistics");
368 report.open_tag("Tests");
369 report.add_content(a_sprintf("%d", c_failed.symbols() + c_successful.symbols()));
370 report.close_tag("Tests");
371
372 report.open_tag("FailuresTotal");
373 report.add_content(a_sprintf("%d", c_failed.symbols()));
374 report.close_tag("FailuresTotal");
375
376 report.open_tag("Errors");
377 report.add_content("0");
378 report.close_tag("Errors");
379
380 report.open_tag("Failures");
381 report.add_content(a_sprintf("%d", c_failed.symbols()));
382 report.close_tag("Failures");
383
384 report.close_tag("Statistics");
385
386 report.close_tag("TestRun");
387
388 astring text_report = report.generate();
389// BASE_LOG(astring("got report\n") + text_report);
390
391 byte_filer xml_out(outfile, "wb");
392 xml_out.write(text_report);
393 xml_out.close();
394}
395
396} //namespace.
397
#define BASE_LOG(s)
a_sprintf is a specialization of astring that provides printf style support.
Definition astring.h:440
int length() const
Returns the current reported length of the allocated C array.
Definition array.h:115
Provides a dynamically resizable ASCII character string.
Definition astring.h:35
static const astring & empty_string()
useful wherever empty strings are needed, e.g., function defaults.
Definition astring.cpp:128
auto_synchronizer simplifies concurrent code by automatically unlocking.
Definition mutex.h:113
A very common template for a dynamic array of bytes.
Definition byte_array.h:36
static astring get(const astring &variable_name)
looks up the "variable_name" in the current environment variables.
the base class of the most easily used and tested objects in the library.
Definition contracts.h:161
virtual const char * class_name() const =0
Returns the bare name of this class as a constant character pointer.
Outcomes describe the state of completion for an operation.
Definition outcome.h:31
virtual void text_form(base_string &state_fill) const =0
Provides a text view of all the important info owned by this object.
static basis::astring application_name()
returns the full name of the current application.
Provides file managment services using the standard I/O support.
Definition byte_filer.h:32
Provides operations commonly needed on file names.
Definition filename.h:64
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
Definition filename.cpp:97
filename basename() const
returns the base of the filename; no directory.
Definition filename.cpp:385
Provides a symbol_table that holds strings as the content.
const basis::astring & name(int index) const
returns the name held at the "index".
basis::outcome add(const basis::astring &name, const contents &storage)
Enters a symbol name into the table along with some contents.
int symbols() const
returns the number of symbols listed in the table.
Supports simple XML output with consistency checking.
basis::outcome add_content(const basis::astring &content)
stores content into the currently opened tag.
basis::outcome close_tag(const basis::astring &tag_name)
closes a previously added "tag_name".
basis::outcome open_tag(const basis::astring &tag_name, const structures::string_table &attributes)
adds a tag with "tag_name" and the "attributes", if any.
basis::astring generate()
writes the current state into a string and returns it.
void record_pass(const basis::astring &class_name, const basis::astring &test_name, const basis::astring &diagnostic_info)
very general recording of a successful test; better to use asserts.
Definition unit_base.cpp:70
int total_tests() const
the total count of tests that have been run.
Definition unit_base.cpp:53
void record_fail(const basis::astring &class_name, const basis::astring &test_name, const basis::astring &diagnostic_info)
very general recording of a failed test; better to use asserts.
Definition unit_base.cpp:93
void assert_false(bool result, const basis::astring &class_name, const basis::astring &test_name, const basis::astring &diagnostic_info)
tests that the "result" is a false boolean.
int passed_tests() const
count of successful tests run.
Definition unit_base.cpp:55
void assert_not_equal(const basis::hoople_standard &a, const basis::hoople_standard &b, const basis::astring &class_name, const basis::astring &test_name, const basis::astring &diagnostic_info)
tests that objects a and b are NOT equal.
void assert_equal(const basis::hoople_standard &a, const basis::hoople_standard &b, const basis::astring &class_name, const basis::astring &test_name, const basis::astring &diagnostic_info)
tests that the objects a and b are equal.
void assert_true(bool result, const basis::astring &class_name, const basis::astring &test_name, const basis::astring &diagnostic_info)
tests that the "result" is a true boolean.
int final_report()
generates a report of the total number of tests that succeeded.
int failed_tests() const
count of number of failed tests.
Definition unit_base.cpp:57
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
Implements an application lock to ensure only one is running at once.
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
A platform independent way to obtain the timestamp of a file.
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
Useful support functions for unit testing, especially within hoople.
Definition unit_base.cpp:35
const int EXPECTED_MAXIMUM_TESTS
maximum number of tests expected.
Definition unit_base.cpp:37
const char * name_for_bools(bool bv)
Definition unit_base.cpp:39
const char * byte_array_phrase
#define test_name()