unit_base.cpp

Go to the documentation of this file.
00001 /*
00002 *  Name   : unit test tools
00003 *  Author : Chris Koeritz
00004 **
00005 * Copyright (c) 2009-$now By Author.  This program is free software; you can  *
00006 * redistribute it and/or modify it under the terms of the GNU General Public  *
00007 * License as published by the Free Software Foundation; either version 2 of   *
00008 * the License or (at your option) any later version.  This is online at:      *
00009 *     http://www.fsf.org/copyleft/gpl.html                                    *
00010 * Please send any updates to: fred@gruntose.com                               *
00011 */
00012 
00013 #include "unit_base.h"
00014 
00015 #include <application/application_shell.h>
00016 #include <basis/astring.h>
00017 #include <basis/functions.h>
00018 #include <configuration/application_configuration.h>
00019 #include <loggers/program_wide_logger.h>
00020 #include <filesystem/byte_filer.h>
00021 #include <filesystem/filename.h>
00022 #include <structures/string_table.h>
00023 #include <textual/xml_generator.h>
00024 
00025 using namespace application;
00026 using namespace basis;
00027 using namespace configuration;
00028 using namespace filesystem;
00029 using namespace loggers;
00030 using namespace structures;
00031 using namespace textual;
00032 
00033 #define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s)
00034 
00035 namespace unit_test {
00036 
00037 const int EXPECTED_MAXIMUM_TESTS = 10008;  
00038 
00039 const char *name_for_bools(bool bv)
00040 { if (bv) return "true"; else return "false"; }
00041 
00042 unit_base::unit_base()
00043 : c_lock(),
00044   c_total_tests(0),
00045   c_passed_tests(0),
00046   c_successful(EXPECTED_MAXIMUM_TESTS),
00047   c_failed(EXPECTED_MAXIMUM_TESTS)
00048 {
00049 }
00050 
00051 unit_base::~unit_base() {}
00052 
00053 int unit_base::total_tests() const { return c_total_tests; }
00054 
00055 int unit_base::passed_tests() const { return c_passed_tests; }
00056 
00057 int unit_base::failed_tests() const
00058 { return c_total_tests - c_passed_tests; }
00059 
00060 void unit_base::count_successful_test(const basis::astring &class_name, const basis::astring &test_name)
00061 {
00062   auto_synchronizer synch(c_lock);
00063   outcome ret = c_successful.add(class_name + " -- " + test_name, "");
00064   if (ret == common::IS_NEW) {
00065     c_total_tests++;
00066     c_passed_tests++;
00067   }
00068 }
00069 
00070 void unit_base::record_pass(const basis::astring &class_name, const astring &test_name,
00071     const astring &diag)
00072 {
00073   auto_synchronizer synch(c_lock);
00074   count_successful_test(class_name, test_name);
00075 //hmmm: kind of lame bailout on printing this.
00076 //      it gets very very wordy if it's left in.
00077 #ifdef DEBUG
00078   astring message = astring("OKAY: ") + class_name + " in test [" + test_name + "]";
00079   BASE_LOG(message);
00080 #endif
00081 }
00082 
00083 void unit_base::count_failed_test(const basis::astring &class_name, const basis::astring &test_name,
00084     const basis::astring &diag)
00085 {
00086   auto_synchronizer synch(c_lock);
00087   outcome ret = c_failed.add(class_name + " -- " + test_name, diag);
00088   if (ret == common::IS_NEW) {
00089     c_total_tests++;
00090   }
00091 }
00092 
00093 void unit_base::record_fail(const basis::astring &class_name, const astring &test_name, const astring &diag)
00094 {
00095   count_failed_test(class_name, test_name, diag);
00096   astring message = astring("\nFAIL: ") + class_name + " in test [" + test_name + "]\n" + diag + "\n";
00097   BASE_LOG(message);
00098 }
00099 
00100 void unit_base::record_successful_assertion(const basis::astring &class_name, const astring &test_name,
00101     const astring &assertion_name)
00102 {
00103   record_pass(class_name, test_name,
00104       astring("no problem with: ") + assertion_name);
00105 }
00106 
00107 void unit_base::record_failed_object_compare(const hoople_standard &a, const hoople_standard &b,
00108     const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
00109 {
00110   astring a_state, b_state;
00111   a.text_form(a_state);
00112   b.text_form(b_state);
00113   record_fail(class_name, test_name,
00114       astring("Error in assertion ") + assertion_name + ":\n"
00115       + "==============\n"
00116       + a_state + "\n"
00117       + "=== versus ===\n"
00118       + b_state + "\n"
00119       + "==============");
00120 }
00121 
00122 void unit_base::record_failed_int_compare(int a, int b,
00123     const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
00124 {
00125   record_fail(class_name, test_name,
00126       astring("Error in assertion ") + assertion_name
00127       + a_sprintf(": inappropriate values: %d & %d", a, b));
00128 }
00129 
00130 void unit_base::record_failed_double_compare(double a, double b,
00131     const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
00132 {
00133   record_fail(class_name, test_name,
00134       astring("Error in assertion ") + assertion_name
00135       + a_sprintf(": inappropriate values: %f & %f", a, b));
00136 }
00137 
00138 void unit_base::record_failed_pointer_compare(const void *a, const void *b,
00139     const basis::astring &class_name, const astring &test_name, const astring &assertion_name)
00140 {
00141   record_fail(class_name, test_name,
00142       astring("Error in assertion ") + assertion_name
00143       + a_sprintf(": inappropriate values: %p & %p", a, b));
00144 }
00145 
00146 void unit_base::record_failed_tf_assertion(bool result,
00147     bool expected_result, const basis::astring &class_name, const astring &test_name,
00148     const astring &assertion_name)
00149 {
00150   record_fail(class_name, test_name, astring("Error in assertion ") + assertion_name
00151       + ": expected " + name_for_bools(expected_result)
00152       + " but found " + name_for_bools(result));
00153 }
00154 
00155 void unit_base::assert_equal(const hoople_standard &a, const hoople_standard &b,
00156     const basis::astring &class_name, const astring &test_name, const astring &diag)
00157 {
00158   FUNCDEF("assert_equal");
00159   bool are_equal = (a == b);
00160   if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00161   else record_failed_object_compare(a, b, class_name, test_name, func);
00162 }
00163 
00164 void unit_base::assert_not_equal(const hoople_standard &a, const hoople_standard &b,
00165     const basis::astring &class_name, const astring &test_name, const astring &diag)
00166 {  
00167   FUNCDEF("assert_not_equal");
00168   bool are_equal = (a == b);
00169   if (!are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00170   else record_failed_object_compare(a, b, class_name, test_name, func);
00171 }
00172 
00173 const char *byte_array_phrase = "byte_array[%d]";  // for printing a text form of byte_array.
00174 
00175 void unit_base::assert_equal(const basis::byte_array &a, const basis::byte_array &b,
00176     const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
00177 {
00178   FUNCDEF("assert_equal");
00179   bool are_equal = (a == b);
00180   if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00181   else {
00182     astring a_s = a_sprintf(byte_array_phrase, a.length());
00183     astring b_s = a_sprintf(byte_array_phrase, b.length());
00184     record_failed_object_compare(a_s, b_s, class_name, test_name, func);
00185   }
00186 }
00187 
00188 void unit_base::assert_not_equal(const basis::byte_array &a, const basis::byte_array &b,
00189     const basis::astring &class_name, const basis::astring &test_name, const astring &diag)
00190 {  
00191   FUNCDEF("assert_not_equal");
00192   bool are_equal = (a == b);
00193   if (!are_equal) record_successful_assertion(class_name, test_name, func);
00194   else {
00195     astring a_s = a_sprintf(byte_array_phrase, a.length());
00196     astring b_s = a_sprintf(byte_array_phrase, b.length());
00197     record_failed_object_compare(a_s, b_s, class_name, test_name, func);
00198   }
00199 }
00200 
00201 void unit_base::assert_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
00202 {
00203   FUNCDEF("assert_equal integer");
00204   bool are_equal = a == b;
00205   if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00206   else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
00207 }
00208 
00209 void unit_base::assert_not_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag)
00210 {
00211   FUNCDEF("assert_not_equal integer");
00212   bool are_inequal = a != b;
00213   if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00214   else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
00215 }
00216 
00217 void unit_base::assert_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
00218 {
00219   FUNCDEF("assert_equal double");
00220   bool are_equal = a == b;
00221   if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00222   else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
00223 }
00224 
00225 void unit_base::assert_not_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag)
00226 {
00227   FUNCDEF("assert_not_equal double");
00228   bool are_inequal = a != b;
00229   if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00230   else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
00231 }
00232 
00233 
00234 void unit_base::assert_equal(const void *a, const void *b,
00235     const basis::astring &class_name, const astring &test_name, const astring &diag)
00236 {
00237   FUNCDEF("assert_equal void pointer");
00238   bool are_equal = a == b;
00239   if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00240   else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
00241 }
00242 
00243 void unit_base::assert_not_equal(const void *a, const void *b,
00244     const basis::astring &class_name, const astring &test_name, const astring &diag)
00245 {
00246   FUNCDEF("assert_not_equal void pointer");
00247   bool are_inequal = a != b;
00248   if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00249   else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag);
00250 }
00251 
00252 void unit_base::assert_true(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
00253 {
00254   FUNCDEF("assert_true");
00255   if (result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00256   else record_failed_tf_assertion(result, true, class_name, test_name, astring(func) + ": " + diag);
00257 }
00258 
00259 void unit_base::assert_false(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag)
00260 {
00261   FUNCDEF("assert_false");
00262   if (!result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag);
00263   else record_failed_tf_assertion(result, false, class_name, test_name, astring(func) + ": " + diag);
00264 }
00265 
00266 int unit_base::final_report()
00267 {
00268   auto_synchronizer synch(c_lock);
00269   int to_return = 0;  // return success until we know otherwise.
00270 
00271   astring keyword = "FAILURE";  // but be pessimistic about overall result at first..?
00272 
00273   // check whether we really did succeed or not.
00274   if (c_total_tests == c_passed_tests) keyword = "SUCCESS";  // success!
00275   else to_return = 12;  // a failure return.
00276 
00277   if (!c_total_tests) keyword = "LAMENESS (no tests!)";  // boring!
00278 
00279 //  astring message = keyword + " for "
00280 //    + application_configuration::application_name()
00281 //    + a_sprintf(": %d of %d atomic tests passed.",
00282 //      c_passed_tests, c_total_tests);
00283 //  BASE_LOG(message);
00284 
00285   astring message = keyword + " for "
00286     + filename(application_configuration::application_name()).basename().raw()
00287     + a_sprintf(": %d of %d unit tests passed.",
00288       c_successful.symbols(), c_successful.symbols() + c_failed.symbols());
00289   BASE_LOG(message);
00290 
00291   // send an xml file out for the build engine to analyze.
00292   write_cppunit_xml();
00293 
00294   return to_return;
00295 }
00296 
00297 void unit_base::write_cppunit_xml()
00298 {
00299   auto_synchronizer synch(c_lock);
00300   astring logs_dir = environment::get("LOGS_DIR");
00301   if (logs_dir == astring::empty_string()) logs_dir = "logs";  // uhhh.
00302   astring outfile = logs_dir + "/"
00303       + filename(application_configuration::application_name()).basename().raw()
00304       + ".xml";
00305 
00306 //BASE_LOG(astring("outfile is ") + outfile);
00307 
00308   int id = 1;
00309 
00310   xml_generator report;
00311   string_table attribs;
00312 
00313   // we are emulating a cppunit xml output.
00314 
00315   report.open_tag("TestRun");
00316 
00317   report.open_tag("FailedTests");
00318   for (int i = 0; i < c_failed.symbols(); i++) {
00319     attribs.reset();
00320     attribs.add("id", a_sprintf("%d", id++));
00321     report.open_tag("FailedTest", attribs);
00322     attribs.reset();
00323 //hmmm: does open_tag eat the attribs?  we could stop worrying about resetting.
00324     report.open_tag("Name");
00325     report.add_content(c_failed.name(i));
00326     report.close_tag("Name");
00327 
00328     report.open_tag("FailureType", attribs);
00329     report.add_content("Assertion");
00330     report.close_tag("FailureType");
00331 
00332     report.open_tag("Location", attribs);
00333 
00334     report.open_tag("File", attribs);
00335     report.add_content(application_configuration::application_name());
00336     report.close_tag("File");
00337 
00338     report.open_tag("Line", attribs);
00339     report.add_content("0");
00340     report.close_tag("Line");
00341 
00342     report.close_tag("Location");
00343 
00344     report.open_tag("Message");
00345     report.add_content(c_failed[i]);
00346     report.close_tag("Message");
00347 
00348     report.close_tag("FailedTest");
00349   }
00350   report.close_tag("FailedTests");
00351 
00352   report.open_tag("SuccessfulTests");
00353   for (int i = 0; i < c_successful.symbols(); i++) {
00354     attribs.reset();
00355     attribs.add("id", a_sprintf("%d", id++));
00356     attribs.reset();
00357     report.open_tag("Test", attribs);
00358     report.open_tag("Name");
00359     report.add_content(c_successful.name(i));
00360     report.close_tag("Name");
00361     report.close_tag("Test");
00362   }
00363   report.close_tag("SuccessfulTests");
00364 
00365   report.open_tag("Statistics");
00366   report.open_tag("Tests");
00367   report.add_content(a_sprintf("%d", c_failed.symbols() + c_successful.symbols()));
00368   report.close_tag("Tests");
00369 
00370   report.open_tag("FailuresTotal");
00371   report.add_content(a_sprintf("%d", c_failed.symbols()));
00372   report.close_tag("FailuresTotal");
00373 
00374   report.open_tag("Errors");
00375   report.add_content("0");
00376   report.close_tag("Errors");
00377 
00378   report.open_tag("Failures");
00379   report.add_content(a_sprintf("%d", c_failed.symbols()));
00380   report.close_tag("Failures");
00381 
00382   report.close_tag("Statistics");
00383 
00384   report.close_tag("TestRun");
00385 
00386   astring text_report = report.generate();
00387 //  BASE_LOG(astring("got report\n") + text_report);
00388 
00389   byte_filer xml_out(outfile, "wb");
00390   xml_out.write(text_report);
00391   xml_out.close();
00392 }
00393 
00394 } //namespace.
00395 
Generated on Sat Jan 28 04:22:36 2012 for hoople2 project by  doxygen 1.6.3