// --- // // $Id: htmloutput.cpp,v 1.7 2008/07/15 20:33:31 hartwork Exp $ // // CppTest - A C++ Unit Testing Framework // Copyright (c) 2003 Niklas Lundell // // --- // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. // // --- #include #include #if (defined(__WIN32__) || defined(WIN32)) # include "winconfig.h" #else # include "config.h" #endif #include "cpptest-htmloutput.h" #include "utils.h" using namespace std; namespace Test { namespace { void strreplace(string &value, char search, const string &replace) { string::size_type idx = 0; while((idx = value.find(search, idx)) != string::npos) { value.replace(idx, 1, replace); idx += replace.size(); } } string escape(string value) { strreplace(value, '&', "&"); strreplace(value, '<', "<"); strreplace(value, '>', ">"); strreplace(value, '"', """); strreplace(value, '\'', "'"); return value; } void header(ostream& os, string name) { if (!name.empty()) name += " "; name = escape(name); os << "\n" "\n" "\n" " \n" " \n" " \n" " " << name << "Unit Tests Results\n" " \n" " \n" "\n" "\n" "\n" "\n" "

" << name << "Unit Tests Results

\n" "\n" "
\n" "Designed by CppTest\n" "
\n" "
\n" "\n"; } void footer(ostream& os) { os << "\n" "

\n" " \n" " Valid XHTML 1.0 Strict\n" " \n" "

\n" "\n\n"; } void back_ref(ostream& os, const string& ref, bool prepend_newline = true) { os << "

Back to " << escape(ref) << "\n

\n"; } void sub_title(ostream& os, const string& title, int size) { ostringstream h; h << "h" << size; os << "<" << h.str() << ">" << escape(title) << "\n"; } void sub_title(ostream& os, const string& title, int size, const string& mark) { ostringstream h; h << "h" << size; os << "<" << h.str() << ">" << escape(title) << "\n"; } enum ClassTableType { TableClass_Summary, TableClass_Suites, TableClass_Suite, TableClass_Result }; void table_header(ostream& os, ClassTableType type, const string &summary = "") { static const char* class_tabletypes[] = { "summary", "suites", "suite", "result" }; os << "\n"; } void table_footer(ostream& os) { os << "
\n"; } void table_tr_header(ostream& os) { os << " \n"; } void table_tr_footer(ostream& os) { os << " \n"; } enum ClassType { Title, Success, Error }; void table_entry(ostream& os, ClassType type, const string& s, int width = 0, const string& link = "") { static const char* class_types[] = { "title", "success", "error" }; os << " " << escape(s) << ""; else os << " class=\"tablecell_" << class_types[type] << "\">" << escape(s); os << "\n"; } } // anonymous namespace // Test suite table // struct HtmlOutput::SuiteRow { ostream& _os; SuiteRow(ostream& os) : _os(os) {} void operator()(const SuiteInfo& si) { ClassType type(si._errors > 0 ? Error : Success); ostringstream ss; table_tr_header(_os); table_entry(_os, type, si._name, 0, si._name); ss.str(""), ss << si._tests.size(); table_entry(_os, type, ss.str(), 10); ss.str(""), ss << si._errors; table_entry(_os, type, ss.str(), 10); ss.str(""), ss << correct(si._tests.size(), si._errors) << "%"; table_entry(_os, type, ss.str(), 10); ss.str(""), ss << si._time; table_entry(_os, type, ss.str(), 10); table_tr_footer(_os); } }; // Individual tests tables, tests // struct HtmlOutput::TestRow { bool _incl_ok_tests; ostream& _os; TestRow(ostream& os, bool incl_ok_tests) : _incl_ok_tests(incl_ok_tests), _os(os) {} void operator()(const TestInfo& ti) { if (!ti._success || _incl_ok_tests) { string link = ti._success ? string(""): ti._sources.front().suite() + "_" + ti._name; ClassType type(ti._success ? Success : Error); ostringstream ss; table_tr_header(_os); table_entry(_os, type, ti._name, 0, link); ss.str(""), ss << ti._sources.size(); table_entry(_os, type, ss.str()); table_entry(_os, type, ti._success ? "true" : "false"); ss.str(""), ss << ti._time; table_entry(_os, type, ss.str()); table_tr_footer(_os); } } }; // Individual tests tables, header // struct HtmlOutput::TestSuiteRow { bool _incl_ok_tests; ostream& _os; TestSuiteRow(ostream& os, bool incl_ok_tests) : _incl_ok_tests(incl_ok_tests), _os(os) {} void operator()(const SuiteInfo& si) { ostringstream ss; sub_title(_os, "Suite: " + si._name, 3, si._name); table_header(_os, TableClass_Suite, "Details for suite " + si._name); table_tr_header(_os); table_entry(_os, Title, "Name"); table_entry(_os, Title, "Errors", 10); table_entry(_os, Title, "Success", 10); table_entry(_os, Title, "Time (s)", 10); table_tr_footer(_os); for_each(si._tests.begin(), si._tests.end(), TestRow(_os, _incl_ok_tests)); table_footer(_os); back_ref(_os, "top"); } }; // Individual tests result tables // struct HtmlOutput::TestResult { ostream& _os; TestResult(ostream& os) : _os(os) {} void operator()(const Source& s) { const int TitleSize = 15; ostringstream ss; table_header(_os, TableClass_Result, "Test Failure"); table_tr_header(_os); table_entry(_os, Title, "Test", TitleSize); table_entry(_os, Success, s.suite() + "::" + s.test()); table_tr_footer(_os); table_tr_header(_os); table_entry(_os, Title, "File", TitleSize); ss << s.file() << ":" << s.line(); table_entry(_os, Success, ss.str()); table_tr_footer(_os); table_tr_header(_os); table_entry(_os, Title, "Message", TitleSize); table_entry(_os, Success, s.message()); table_tr_footer(_os); table_footer(_os); } }; // All tests result tables // struct HtmlOutput::TestResultAll { ostream& _os; TestResultAll(ostream& os) : _os(os) {} void operator()(const TestInfo& ti) { if (!ti._success) { const string& suite = ti._sources.front().suite(); sub_title(_os, suite + "::" + ti._name, 3, suite + "_" + ti._name); for_each(ti._sources.begin(), ti._sources.end(), TestResult(_os)); back_ref(_os, suite, false); } } }; // Individual tests result tables, iterator // struct HtmlOutput::SuiteTestResult { ostream& _os; SuiteTestResult(ostream& os) : _os(os) {} void operator()(const SuiteInfo& si) { for_each(si._tests.begin(), si._tests.end(), TestResultAll(_os)); } }; /// Generates the HTML table. This function should only be called after /// run(), when all tests have been executed. /// /// \param os Output stream. /// \param incl_ok_tests Set if successful tests should be shown; /// false otherwise. /// \param name Name of generated report. /// void HtmlOutput::generate(ostream& os, bool incl_ok_tests, const string& name) { ClassType type(_total_errors > 0 ? Error : Success); ostringstream ss; header(os, name); // Table: Summary // sub_title(os, "Summary", 2); table_header(os, TableClass_Summary, "Summary of test results"); table_tr_header(os); table_entry(os, Title, "Tests", 30); table_entry(os, Title, "Errors", 30); table_entry(os, Title, "Success", 30); table_entry(os, Title, "Time (s)", 10); table_tr_footer(os); table_tr_header(os); ss.str(""), ss << _total_tests; table_entry(os, type, ss.str(), 30); ss.str(""), ss << _total_errors; table_entry(os, type, ss.str(), 30); ss.str(""), ss << correct(_total_tests, _total_errors) << "%"; table_entry(os, type, ss.str(), 30); ss.str(""), ss << _total_time; table_entry(os, type, ss.str(), 10); table_tr_footer(os); table_footer(os); os << "
\n\n"; // Table: Test suites // sub_title(os, "Test suites", 2); table_header(os, TableClass_Suites, "Test Suites"); table_tr_header(os); table_entry(os, Title, "Name"); table_entry(os, Title, "Tests", 10); table_entry(os, Title, "Errors", 10); table_entry(os, Title, "Success", 10); table_entry(os, Title, "Time (s)", 10); table_tr_footer(os); for_each(_suites.begin(), _suites.end(), SuiteRow(os)); table_footer(os); os << "
\n\n"; // Individual tests tables // for_each(_suites.begin(), _suites.end(), TestSuiteRow(os, incl_ok_tests)); os << "
\n\n"; // Individual tests result tables // if(_total_errors != 0) { sub_title(os, "Test results", 2); for_each(_suites.begin(), _suites.end(), SuiteTestResult(os)); os << "
\n\n"; } // EOF // footer(os); } } // namespace Test