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),
47 c_failed(EXPECTED_MAXIMUM_TESTS),
48 c_fail_on_errors(true)
49{
50}
51
53
54int unit_base::total_tests() const { return c_total_tests; }
55
56int unit_base::passed_tests() const { return c_passed_tests; }
57
59{ return c_total_tests - c_passed_tests; }
60
61void unit_base::count_successful_test(const basis::astring &class_name, const basis::astring &test_name)
62{
63 auto_synchronizer synch(c_lock);
64 outcome ret = c_successful.add(class_name + " -- " + test_name, "");
65 if (ret == common::IS_NEW) {
66 c_total_tests++;
67 c_passed_tests++;
68 }
69}
70
72 const astring &diag)
73{
74 auto_synchronizer synch(c_lock);
75 count_successful_test(class_name, test_name);
76//hmmm: kind of lame bailout on printing this.
77// it gets very very wordy if it's left in.
78#ifdef DEBUG
79 astring message = astring("OKAY: ") + class_name + " in test [" + test_name + "]";
80 BASE_LOG(message);
81#endif
82}
83
84void unit_base::count_failed_test(const basis::astring &class_name, const basis::astring &test_name,
85 const basis::astring &diag)
86{
87 auto_synchronizer synch(c_lock);
88 outcome ret = c_failed.add(class_name + " -- " + test_name, diag);
89 if (ret == common::IS_NEW) {
90 c_total_tests++;
91 }
92}
93
94void unit_base::record_fail(const basis::astring &class_name, const astring &test_name, const astring &diag)
95{
96 count_failed_test(class_name, test_name, diag);
97 astring message = astring("\nFAIL: ") + class_name + " in test [" + test_name + "]\n" + diag + "\n";
98 BASE_LOG(message);
99}
100
101void unit_base::record_successful_assertion(const basis::astring &class_name, const astring &test_name,
102 const astring &assertion_name)
103{
105 astring("no problem with: ") + assertion_name);
106}
107
108void unit_base::record_failed_object_compare(const hoople_standard &a, const hoople_standard &b,
109 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
110{
111 astring a_state, b_state;
112 a.text_form(a_state);
113 b.text_form(b_state);
115 astring("Error in assertion ") + assertion_name + ":\n"
116 + "==============\n"
117 + a_state + "\n"
118 + "=== versus ===\n"
119 + b_state + "\n"
120 + "==============");
121}
122
123void unit_base::record_failed_int_compare(int a, int b,
124 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
125{
127 astring("Error in assertion ") + assertion_name
128 + a_sprintf(": inappropriate values: %d & %d", a, b));
129}
130
131void unit_base::record_failed_double_compare(double a, double b,
132 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
133{
135 astring("Error in assertion ") + assertion_name
136 + a_sprintf(": inappropriate values: %f & %f", a, b));
137}
138
139void unit_base::record_failed_pointer_compare(const void *a, const void *b,
140 const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
141{
143 astring("Error in assertion ") + assertion_name
144 + a_sprintf(": inappropriate values: %p & %p", a, b));
145}
146
147void unit_base::record_failed_tf_assertion(bool result,
148 bool expected_result, const basis::astring &class_name, const astring &test_name,
149 const astring &assertion_name)
150{
151 record_fail(class_name, test_name, astring("Error in assertion ") + assertion_name
152 + ": expected " + name_for_bools(expected_result)
153 + " but found " + name_for_bools(result));
154}
155
157 const basis::astring &class_name, const astring &test_name, const astring &diag)
158{
159 FUNCDEF("assert_equal");
160 bool are_equal = (a == b);
161 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
162 else record_failed_object_compare(a, b, class_name, test_name, func);
163}
164
166 const basis::astring &class_name, const astring &test_name, const astring &diag)
167{
168 FUNCDEF("assert_not_equal");
169 bool are_equal = (a == b);
170 if (!are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
171 else record_failed_object_compare(a, b, class_name, test_name, func);
172}
173
174const char *byte_array_phrase = "byte_array[%d]"; // for printing a text form of byte_array.
175
177 const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
178{
179 FUNCDEF("assert_equal");
180 bool are_equal = (a == b);
181 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
182 else {
185 record_failed_object_compare(a_s, b_s, class_name, test_name, func);
186 }
187}
188
190 const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
191{
192 FUNCDEF("assert_not_equal");
193 bool are_equal = (a == b);
194 if (!are_equal) record_successful_assertion(class_name, test_name, func);
195 else {
198 record_failed_object_compare(a_s, b_s, class_name, test_name, func);
199 }
200}
201
202void unit_base::assert_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
203{
204 FUNCDEF("assert_equal integer");
205 bool are_equal = a == b;
206 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
207 else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
208}
209
210void unit_base::assert_not_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
211{
212 FUNCDEF("assert_not_equal integer");
213 bool are_inequal = a != b;
214 if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
215 else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
216}
217
218void unit_base::assert_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
219{
220 FUNCDEF("assert_equal double");
221 bool are_equal = a == b;
222 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
223 else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
224}
225
226void unit_base::assert_not_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
227{
228 FUNCDEF("assert_not_equal double");
229 bool are_inequal = a != b;
230 if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
231 else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
232}
233
234
235void unit_base::assert_equal(const void *a, const void *b,
236 const basis::astring &class_name, const astring &test_name, const astring &diag)
237{
238 FUNCDEF("assert_equal void pointer");
239 bool are_equal = a == b;
240 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
241 else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
242}
243
244void unit_base::assert_not_equal(const void *a, const void *b,
245 const basis::astring &class_name, const astring &test_name, const astring &diag)
246{
247 FUNCDEF("assert_not_equal void pointer");
248 bool are_inequal = a != b;
249 if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
250 else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
251}
252
253void unit_base::assert_true(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
254{
255 FUNCDEF("assert_true");
256 if (result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
257 else record_failed_tf_assertion(result, true, class_name, test_name, astring(func) + ": " + diag);
258}
259
260void unit_base::assert_false(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
261{
262 FUNCDEF("assert_false");
263 if (!result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
264 else record_failed_tf_assertion(result, false, class_name, test_name, astring(func) + ": " + diag);
265}
266
268{
269 auto_synchronizer synch(c_lock);
270 int to_return = 0; // return success until we know otherwise.
271
272 astring keyword = "FAILURE"; // but be pessimistic about overall result at first..?
273
274// BASE_LOG(a_sprintf("total tests %d passed tests %d", c_total_tests, c_passed_tests));
275
276 // check whether we really did succeed or not.
277 if (c_total_tests == c_passed_tests) keyword = "SUCCESS"; // success!
278 else to_return = 12; // a failure return.
279
280 if (!c_total_tests) keyword = "LAMENESS (no tests!)"; // boring!
281
282// astring message = keyword + " for "
283// + application_configuration::application_name()
284// + a_sprintf(": %d of %d atomic tests passed.",
285// c_passed_tests, c_total_tests);
286// BASE_LOG(message);
287
288 astring message = keyword + " for "
290 + a_sprintf(": %d of %d unit tests passed.",
291 c_successful.symbols(), c_successful.symbols() + c_failed.symbols());
292 BASE_LOG(message);
293
294 // send an xml file out for the build engine to analyze.
295 write_cppunit_xml();
296
297 if (! c_fail_on_errors) {
298 /*
299 * if we're not tracking errors as failures, due to a test that is known to
300 * fail, then we just signal a success here regardless of how many errors occurred.
301 */
302 return common::OKAY;
303 }
304
305 return to_return;
306}
307
308void unit_base::write_cppunit_xml()
309{
310 auto_synchronizer synch(c_lock);
311 astring logs_dir = environment::get("FEISTY_MEOW_LOGS");
312 if (logs_dir == astring::empty_string()) logs_dir = "logs"; // uhhh.
313 astring outfile = logs_dir + "/"
315 + ".xml";
316
317//BASE_LOG(astring("outfile is ") + outfile);
318
319 int id = 1;
320
321 xml_generator report;
322 string_table attribs;
323
324 // we are emulating a cppunit xml output.
325
326 report.open_tag("TestRun");
327
328 report.open_tag("FailedTests");
329 for (int i = 0; i < c_failed.symbols(); i++) {
330 attribs.reset();
331 attribs.add("id", a_sprintf("%d", id++));
332 report.open_tag("FailedTest", attribs);
333 attribs.reset();
334//hmmm: does open_tag eat the attribs? we could stop worrying about resetting.
335 report.open_tag("Name");
336 report.add_content(c_failed.name(i));
337 report.close_tag("Name");
338
339 report.open_tag("FailureType", attribs);
340 report.add_content("Assertion");
341 report.close_tag("FailureType");
342
343 report.open_tag("Location", attribs);
344
345 report.open_tag("File", attribs);
347 report.close_tag("File");
348
349 report.open_tag("Line", attribs);
350 report.add_content("0");
351 report.close_tag("Line");
352
353 report.close_tag("Location");
354
355 report.open_tag("Message");
356 report.add_content(c_failed[i]);
357 report.close_tag("Message");
358
359 report.close_tag("FailedTest");
360 }
361 report.close_tag("FailedTests");
362
363 report.open_tag("SuccessfulTests");
364 for (int i = 0; i < c_successful.symbols(); i++) {
365 attribs.reset();
366 attribs.add("id", a_sprintf("%d", id++));
367 attribs.reset();
368 report.open_tag("Test", attribs);
369 report.open_tag("Name");
370 report.add_content(c_successful.name(i));
371 report.close_tag("Name");
372 report.close_tag("Test");
373 }
374 report.close_tag("SuccessfulTests");
375
376 report.open_tag("Statistics");
377 report.open_tag("Tests");
378 report.add_content(a_sprintf("%d", c_failed.symbols() + c_successful.symbols()));
379 report.close_tag("Tests");
380
381 report.open_tag("FailuresTotal");
382 report.add_content(a_sprintf("%d", c_failed.symbols()));
383 report.close_tag("FailuresTotal");
384
385 report.open_tag("Errors");
386 report.add_content("0");
387 report.close_tag("Errors");
388
389 report.open_tag("Failures");
390 report.add_content(a_sprintf("%d", c_failed.symbols()));
391 report.close_tag("Failures");
392
393 report.close_tag("Statistics");
394
395 report.close_tag("TestRun");
396
397 astring text_report = report.generate();
398// BASE_LOG(astring("got report\n") + text_report);
399
400 byte_filer xml_out(outfile, "wb");
401 xml_out.write(text_report);
402 xml_out.close();
403}
404
405} //namespace.
406
#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:71
int total_tests() const
the total count of tests that have been run.
Definition unit_base.cpp:54
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:94
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:56
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:58
#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()