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..?
274 BASE_LOG(a_sprintf("total tests %d passed tests %d", c_total_tests, c_passed_tests));
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.
280 if (!c_total_tests) keyword = "LAMENESS (no tests!)"; // boring!
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);
288 astring message = keyword + " for "
289 + filename(application_configuration::application_name()).basename().raw()
290 + a_sprintf(": %d of %d unit tests passed.",
291 c_successful.symbols(), c_successful.symbols() + c_failed.symbols());
294 // send an xml file out for the build engine to analyze.
300 void unit_base::write_cppunit_xml()
302 auto_synchronizer synch(c_lock);
303 astring logs_dir = environment::get("LOGS_DIR");
304 if (logs_dir == astring::empty_string()) logs_dir = "logs"; // uhhh.
305 astring outfile = logs_dir + "/"
306 + filename(application_configuration::application_name()).basename().raw()
309 //BASE_LOG(astring("outfile is ") + outfile);
313 xml_generator report;
314 string_table attribs;
316 // we are emulating a cppunit xml output.
318 report.open_tag("TestRun");
320 report.open_tag("FailedTests");
321 for (int i = 0; i < c_failed.symbols(); i++) {
323 attribs.add("id", a_sprintf("%d", id++));
324 report.open_tag("FailedTest", attribs);
326 //hmmm: does open_tag eat the attribs? we could stop worrying about resetting.
327 report.open_tag("Name");
328 report.add_content(c_failed.name(i));
329 report.close_tag("Name");
331 report.open_tag("FailureType", attribs);
332 report.add_content("Assertion");
333 report.close_tag("FailureType");
335 report.open_tag("Location", attribs);
337 report.open_tag("File", attribs);
338 report.add_content(application_configuration::application_name());
339 report.close_tag("File");
341 report.open_tag("Line", attribs);
342 report.add_content("0");
343 report.close_tag("Line");
345 report.close_tag("Location");
347 report.open_tag("Message");
348 report.add_content(c_failed[i]);
349 report.close_tag("Message");
351 report.close_tag("FailedTest");
353 report.close_tag("FailedTests");
355 report.open_tag("SuccessfulTests");
356 for (int i = 0; i < c_successful.symbols(); i++) {
358 attribs.add("id", a_sprintf("%d", id++));
360 report.open_tag("Test", attribs);
361 report.open_tag("Name");
362 report.add_content(c_successful.name(i));
363 report.close_tag("Name");
364 report.close_tag("Test");
366 report.close_tag("SuccessfulTests");
368 report.open_tag("Statistics");
369 report.open_tag("Tests");
370 report.add_content(a_sprintf("%d", c_failed.symbols() + c_successful.symbols()));
371 report.close_tag("Tests");
373 report.open_tag("FailuresTotal");
374 report.add_content(a_sprintf("%d", c_failed.symbols()));
375 report.close_tag("FailuresTotal");
377 report.open_tag("Errors");
378 report.add_content("0");
379 report.close_tag("Errors");
381 report.open_tag("Failures");
382 report.add_content(a_sprintf("%d", c_failed.symbols()));
383 report.close_tag("Failures");
385 report.close_tag("Statistics");
387 report.close_tag("TestRun");
389 astring text_report = report.generate();
390 // BASE_LOG(astring("got report\n") + text_report);
392 byte_filer xml_out(outfile, "wb");
393 xml_out.write(text_report);