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>
20 #include <filesystem/byte_filer.h>
21 #include <filesystem/filename.h>
23 #include <textual/xml_generator.h>
24 
25 using namespace application;
26 using namespace basis;
27 using namespace configuration;
28 using namespace filesystem;
29 using namespace loggers;
30 using namespace structures;
31 using namespace textual;
32 
33 #define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s)
34 
35 namespace unit_test {
36 
37 const int EXPECTED_MAXIMUM_TESTS = 10008;
38 
39 const char *name_for_bools(bool bv)
40 { if (bv) return "true"; else return "false"; }
41 
42 unit_base::unit_base()
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 {
49 }
50 
52 
53 int unit_base::total_tests() const { return c_total_tests; }
54 
55 int unit_base::passed_tests() const { return c_passed_tests; }
56 
58 { return c_total_tests - c_passed_tests; }
59 
60 void 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 
70 void unit_base::record_pass(const basis::astring &class_name, const astring &test_name,
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 
83 void 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 
93 void 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 
100 void 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 
107 void 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 
122 void 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 
130 void 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 
138 void 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 
146 void 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 
173 const 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 
201 void 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 
209 void 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 
217 void 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 
225 void 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 
234 void 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 
243 void 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 
252 void 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 
259 void 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 "
288  + filename(application_configuration::application_name()).basename().raw()
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 
299 void 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 + "/"
305  + filename(application_configuration::application_name()).basename().raw()
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);
337  report.add_content(application_configuration::application_name());
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 
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
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
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.
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.
Definition: string_table.h:32
const basis::astring & name(int index) const
returns the name held at the "index".
Definition: symbol_table.h:272
basis::outcome add(const basis::astring &name, const contents &storage)
Enters a symbol name into the table along with some contents.
Definition: symbol_table.h:383
int symbols() const
returns the number of symbols listed in the table.
Definition: symbol_table.h:241
Supports simple XML output with consistency checking.
Definition: xml_generator.h:30
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
virtual ~unit_base()
Definition: unit_base.cpp:51
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.
Definition: unit_base.cpp:259
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.
Definition: unit_base.cpp:164
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.
Definition: unit_base.cpp:155
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.
Definition: unit_base.cpp:252
int final_report()
generates a report of the total number of tests that succeeded.
Definition: unit_base.cpp:266
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:57
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.
Definition: byte_filer.cpp:37
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
Definition: unit_base.cpp:173
#define test_name()
#define BASE_LOG(s)
Definition: unit_base.cpp:33