2 * Name : unit test tools
3 * Author : Chris Koeritz
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 *
13 #include "unit_base.h"
15 #include <application/application_shell.h>
16 #include <basis/astring.h>
17 #include <basis/functions.h>
18 #include <configuration/application_configuration.h>
19 #include <loggers/program_wide_logger.h>
20 #include <filesystem/byte_filer.h>
21 #include <filesystem/filename.h>
22 #include <structures/string_table.h>
23 #include <textual/xml_generator.h>
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;
33 #define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s)
37 const int EXPECTED_MAXIMUM_TESTS = 10008; //!< maximum number of tests expected.
39 const char *name_for_bools(bool bv)
40 { if (bv) return "true"; else return "false"; }
42 unit_base::unit_base()
46 c_successful(EXPECTED_MAXIMUM_TESTS),
47 c_failed(EXPECTED_MAXIMUM_TESTS)
51 unit_base::~unit_base() {}
53 int unit_base::total_tests() const { return c_total_tests; }
55 int unit_base::passed_tests() const { return c_passed_tests; }
57 int unit_base::failed_tests() const
58 { return c_total_tests - c_passed_tests; }
60 void unit_base::count_successful_test(const basis::astring &class_name, const basis::astring &test_name)
62 auto_synchronizer synch(c_lock);
63 outcome ret = c_successful.add(class_name + " -- " + test_name, "");
64 if (ret == common::IS_NEW) {
70 void unit_base::record_pass(const basis::astring &class_name, const astring &test_name,
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.
78 astring message = astring("OKAY: ") + class_name + " in test [" + test_name + "]";
83 void unit_base::count_failed_test(const basis::astring &class_name, const basis::astring &test_name,
84 const basis::astring &diag)
86 auto_synchronizer synch(c_lock);
87 outcome ret = c_failed.add(class_name + " -- " + test_name, diag);
88 if (ret == common::IS_NEW) {
93 void unit_base::record_fail(const basis::astring &class_name, const astring &test_name, const astring &diag)
95 count_failed_test(class_name, test_name, diag);
96 astring message = astring("\nFAIL: ") + class_name + " in test [" + test_name + "]\n" + diag + "\n";
100 void unit_base::record_successful_assertion(const basis::astring &class_name, const astring &test_name,
101 const astring &assertion_name)
103 record_pass(class_name, test_name,
104 astring("no problem with: ") + assertion_name);
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)
110 astring a_state, b_state;
111 a.text_form(a_state);
112 b.text_form(b_state);
113 record_fail(class_name, test_name,
114 astring("Error in assertion ") + assertion_name + ":\n"
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)
125 record_fail(class_name, test_name,
126 astring("Error in assertion ") + assertion_name
127 + a_sprintf(": inappropriate values: %d & %d", a, b));
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)
133 record_fail(class_name, test_name,
134 astring("Error in assertion ") + assertion_name
135 + a_sprintf(": inappropriate values: %f & %f", a, b));
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)
141 record_fail(class_name, test_name,
142 astring("Error in assertion ") + assertion_name
143 + a_sprintf(": inappropriate values: %p & %p", a, b));
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)
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));
155 void unit_base::assert_equal(const hoople_standard &a, const hoople_standard &b,
156 const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
164 void unit_base::assert_not_equal(const hoople_standard &a, const hoople_standard &b,
165 const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
173 const char *byte_array_phrase = "byte_array[%d]"; // for printing a text form of byte_array.
175 void unit_base::assert_equal(const basis::byte_array &a, const basis::byte_array &b,
176 const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
178 FUNCDEF("assert_equal");
179 bool are_equal = (a == b);
180 if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
182 astring a_s = a_sprintf(byte_array_phrase, a.length());
183 astring b_s = a_sprintf(byte_array_phrase, b.length());
184 record_failed_object_compare(a_s, b_s, class_name, test_name, func);
188 void unit_base::assert_not_equal(const basis::byte_array &a, const basis::byte_array &b,
189 const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
191 FUNCDEF("assert_not_equal");
192 bool are_equal = (a == b);
193 if (!are_equal) record_successful_assertion(class_name, test_name, func);
195 astring a_s = a_sprintf(byte_array_phrase, a.length());
196 astring b_s = a_sprintf(byte_array_phrase, b.length());
197 record_failed_object_compare(a_s, b_s, class_name, test_name, func);
201 void unit_base::assert_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
209 void unit_base::assert_not_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
217 void unit_base::assert_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
225 void unit_base::assert_not_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
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)
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);
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)
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);
252 void unit_base::assert_true(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
259 void unit_base::assert_false(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
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);
266 int unit_base::final_report()
268 auto_synchronizer synch(c_lock);
269 int to_return = 0; // return success until we know otherwise.
271 astring keyword = "FAILURE"; // but be pessimistic about overall result at first..?
273 // check whether we really did succeed or not.
274 if (c_total_tests == c_passed_tests) keyword = "SUCCESS"; // success!
275 else to_return = 12; // a failure return.
277 if (!c_total_tests) keyword = "LAMENESS (no tests!)"; // boring!
279 // astring message = keyword + " for "
280 // + application_configuration::application_name()
281 // + a_sprintf(": %d of %d atomic tests passed.",
282 // c_passed_tests, c_total_tests);
283 // BASE_LOG(message);
285 astring message = keyword + " for "
286 + filename(application_configuration::application_name()).basename().raw()
287 + a_sprintf(": %d of %d unit tests passed.",
288 c_successful.symbols(), c_successful.symbols() + c_failed.symbols());
291 // send an xml file out for the build engine to analyze.
297 void unit_base::write_cppunit_xml()
299 auto_synchronizer synch(c_lock);
300 astring logs_dir = environment::get("LOGS_DIR");
301 if (logs_dir == astring::empty_string()) logs_dir = "logs"; // uhhh.
302 astring outfile = logs_dir + "/"
303 + filename(application_configuration::application_name()).basename().raw()
306 //BASE_LOG(astring("outfile is ") + outfile);
310 xml_generator report;
311 string_table attribs;
313 // we are emulating a cppunit xml output.
315 report.open_tag("TestRun");
317 report.open_tag("FailedTests");
318 for (int i = 0; i < c_failed.symbols(); i++) {
320 attribs.add("id", a_sprintf("%d", id++));
321 report.open_tag("FailedTest", attribs);
323 //hmmm: does open_tag eat the attribs? we could stop worrying about resetting.
324 report.open_tag("Name");
325 report.add_content(c_failed.name(i));
326 report.close_tag("Name");
328 report.open_tag("FailureType", attribs);
329 report.add_content("Assertion");
330 report.close_tag("FailureType");
332 report.open_tag("Location", attribs);
334 report.open_tag("File", attribs);
335 report.add_content(application_configuration::application_name());
336 report.close_tag("File");
338 report.open_tag("Line", attribs);
339 report.add_content("0");
340 report.close_tag("Line");
342 report.close_tag("Location");
344 report.open_tag("Message");
345 report.add_content(c_failed[i]);
346 report.close_tag("Message");
348 report.close_tag("FailedTest");
350 report.close_tag("FailedTests");
352 report.open_tag("SuccessfulTests");
353 for (int i = 0; i < c_successful.symbols(); i++) {
355 attribs.add("id", a_sprintf("%d", id++));
357 report.open_tag("Test", attribs);
358 report.open_tag("Name");
359 report.add_content(c_successful.name(i));
360 report.close_tag("Name");
361 report.close_tag("Test");
363 report.close_tag("SuccessfulTests");
365 report.open_tag("Statistics");
366 report.open_tag("Tests");
367 report.add_content(a_sprintf("%d", c_failed.symbols() + c_successful.symbols()));
368 report.close_tag("Tests");
370 report.open_tag("FailuresTotal");
371 report.add_content(a_sprintf("%d", c_failed.symbols()));
372 report.close_tag("FailuresTotal");
374 report.open_tag("Errors");
375 report.add_content("0");
376 report.close_tag("Errors");
378 report.open_tag("Failures");
379 report.add_content(a_sprintf("%d", c_failed.symbols()));
380 report.close_tag("Failures");
382 report.close_tag("Statistics");
384 report.close_tag("TestRun");
386 astring text_report = report.generate();
387 // BASE_LOG(astring("got report\n") + text_report);
389 byte_filer xml_out(outfile, "wb");
390 xml_out.write(text_report);