3 // $Id: htmloutput.cpp,v 1.7 2008/07/15 20:33:31 hartwork Exp $
5 // CppTest - A C++ Unit Testing Framework
6 // Copyright (c) 2003 Niklas Lundell
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2 of the License, or (at your option) any later version.
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the
22 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 // Boston, MA 02111-1307, USA.
30 #if (defined(__WIN32__) || defined(WIN32))
31 # include "winconfig.h"
36 #include "cpptest-htmloutput.h"
46 strreplace(string
&value
, char search
, const string
&replace
)
48 string::size_type idx
= 0;
49 while((idx
= value
.find(search
, idx
)) != string::npos
)
51 value
.replace(idx
, 1, replace
);
52 idx
+= replace
.size();
59 strreplace(value
, '&', "&");
60 strreplace(value
, '<', "<");
61 strreplace(value
, '>', ">");
62 strreplace(value
, '"', """);
63 strreplace(value
, '\'', "'");
68 header(ostream
& os
, string name
)
74 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
75 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
77 " <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n"
78 " <meta name=\"generator\" content=\"CppTest - http://cpptest.sourceforge.net\" />\n"
80 " <title>" << name
<< "Unit Tests Results</title>\n"
82 " <style type=\"text/css\" media=\"screen\">\n"
86 " border-width: 0px;\n"
89 " background-color: #cccccc;\n"
95 " border-collapse:separate;\n"
96 " border-spacing: 2px;\n"
107 " .table_summary {\n"
114 " margin: 0px 0px 1em 0px;\n"
116 " .tablecell_title {\n"
117 " background-color: #a5cef7;\n"
118 " font-weight: bold;\n"
121 " .tablecell_success {\n"
122 " background-color: #efefe7;\n"
125 " .tablecell_error {\n"
127 " background-color: #efefe7;\n"
128 " font-weight: bold;\n"
132 " padding: 1em 0px 2em 0px;\n"
136 " padding: 0px 0px 2em 0px;\n"
144 "<h1><a name=\"top\"></a>" << name
<< "Unit Tests Results</h1>\n"
146 "<div style=\"text-align:right\">\n"
147 "Designed by <a href=\"http://cpptest.sourceforge.net\">CppTest</a>\n"
159 " <a href=\"http://validator.w3.org/#validate-by-upload\">\n"
160 " Valid XHTML 1.0 Strict\n"
163 "</body>\n</html>\n";
167 back_ref(ostream
& os
, const string
& ref
, bool prepend_newline
= true)
170 os
<< "<p class=\"" << (prepend_newline
? "spaced" : "unspaced") << "\"><a href=\"#" << ref
171 << "\">Back to " << escape(ref
) << "</a>\n</p>\n";
175 sub_title(ostream
& os
, const string
& title
, int size
)
179 os
<< "<" << h
.str() << ">" << escape(title
) << "</" << h
.str() << ">\n";
183 sub_title(ostream
& os
, const string
& title
, int size
, const string
& mark
)
187 os
<< "<" << h
.str() << "><a name=\"" << mark
<< "\"></a>" << escape(title
) << "</" << h
.str() << ">\n";
190 enum ClassTableType
{ TableClass_Summary
, TableClass_Suites
, TableClass_Suite
, TableClass_Result
};
193 table_header(ostream
& os
, ClassTableType type
, const string
&summary
= "")
195 static const char* class_tabletypes
[] = { "summary", "suites", "suite", "result" };
197 os
<< "<table summary=\"" << escape(summary
) << "\" class=\"table_" << class_tabletypes
[type
] << "\">\n";
201 table_footer(ostream
& os
)
207 table_tr_header(ostream
& os
)
213 table_tr_footer(ostream
& os
)
218 enum ClassType
{ Title
, Success
, Error
};
221 table_entry(ostream
& os
, ClassType type
, const string
& s
,
222 int width
= 0, const string
& link
= "")
224 static const char* class_types
[] = { "title", "success", "error" };
228 os
<< " style=\"width:" << width
<< "%\"";
230 os
<< " class=\"tablecell_" << class_types
[type
] << "\"><a href=\"#" << link
<< "\">" << escape(s
) << "</a>";
232 os
<< " class=\"tablecell_" << class_types
[type
] << "\">" << escape(s
);
236 } // anonymous namespace
240 struct HtmlOutput::SuiteRow
243 SuiteRow(ostream
& os
) : _os(os
) {}
244 void operator()(const SuiteInfo
& si
)
246 ClassType
type(si
._errors
> 0 ? Error
: Success
);
249 table_tr_header(_os
);
250 table_entry(_os
, type
, si
._name
, 0, si
._name
);
251 ss
.str(""), ss
<< si
._tests
.size();
252 table_entry(_os
, type
, ss
.str(), 10);
253 ss
.str(""), ss
<< si
._errors
;
254 table_entry(_os
, type
, ss
.str(), 10);
255 ss
.str(""), ss
<< correct(si
._tests
.size(), si
._errors
) << "%";
256 table_entry(_os
, type
, ss
.str(), 10);
257 ss
.str(""), ss
<< si
._time
;
258 table_entry(_os
, type
, ss
.str(), 10);
259 table_tr_footer(_os
);
263 // Individual tests tables, tests
265 struct HtmlOutput::TestRow
269 TestRow(ostream
& os
, bool incl_ok_tests
)
270 : _incl_ok_tests(incl_ok_tests
), _os(os
) {}
271 void operator()(const TestInfo
& ti
)
273 if (!ti
._success
|| _incl_ok_tests
)
275 string link
= ti
._success
? string(""):
276 ti
._sources
.front().suite() + "_" + ti
._name
;
277 ClassType
type(ti
._success
? Success
: Error
);
280 table_tr_header(_os
);
281 table_entry(_os
, type
, ti
._name
, 0, link
);
282 ss
.str(""), ss
<< ti
._sources
.size();
283 table_entry(_os
, type
, ss
.str());
284 table_entry(_os
, type
, ti
._success
? "true" : "false");
285 ss
.str(""), ss
<< ti
._time
;
286 table_entry(_os
, type
, ss
.str());
287 table_tr_footer(_os
);
292 // Individual tests tables, header
294 struct HtmlOutput::TestSuiteRow
298 TestSuiteRow(ostream
& os
, bool incl_ok_tests
)
299 : _incl_ok_tests(incl_ok_tests
), _os(os
) {}
300 void operator()(const SuiteInfo
& si
)
304 sub_title(_os
, "Suite: " + si
._name
, 3, si
._name
);
305 table_header(_os
, TableClass_Suite
, "Details for suite " + si
._name
);
306 table_tr_header(_os
);
307 table_entry(_os
, Title
, "Name");
308 table_entry(_os
, Title
, "Errors", 10);
309 table_entry(_os
, Title
, "Success", 10);
310 table_entry(_os
, Title
, "Time (s)", 10);
311 table_tr_footer(_os
);
312 for_each(si
._tests
.begin(), si
._tests
.end(),
313 TestRow(_os
, _incl_ok_tests
));
315 back_ref(_os
, "top");
319 // Individual tests result tables
321 struct HtmlOutput::TestResult
324 TestResult(ostream
& os
) : _os(os
) {}
325 void operator()(const Source
& s
)
327 const int TitleSize
= 15;
331 table_header(_os
, TableClass_Result
, "Test Failure");
332 table_tr_header(_os
);
333 table_entry(_os
, Title
, "Test", TitleSize
);
334 table_entry(_os
, Success
, s
.suite() + "::" + s
.test());
335 table_tr_footer(_os
);
336 table_tr_header(_os
);
337 table_entry(_os
, Title
, "File", TitleSize
);
338 ss
<< s
.file() << ":" << s
.line();
339 table_entry(_os
, Success
, ss
.str());
340 table_tr_footer(_os
);
341 table_tr_header(_os
);
342 table_entry(_os
, Title
, "Message", TitleSize
);
343 table_entry(_os
, Success
, s
.message());
344 table_tr_footer(_os
);
349 // All tests result tables
351 struct HtmlOutput::TestResultAll
354 TestResultAll(ostream
& os
) : _os(os
) {}
355 void operator()(const TestInfo
& ti
)
359 const string
& suite
= ti
._sources
.front().suite();
361 sub_title(_os
, suite
+ "::" + ti
._name
, 3, suite
+ "_" + ti
._name
);
362 for_each(ti
._sources
.begin(), ti
._sources
.end(), TestResult(_os
));
363 back_ref(_os
, suite
, false);
368 // Individual tests result tables, iterator
370 struct HtmlOutput::SuiteTestResult
373 SuiteTestResult(ostream
& os
) : _os(os
) {}
374 void operator()(const SuiteInfo
& si
)
376 for_each(si
._tests
.begin(), si
._tests
.end(), TestResultAll(_os
));
380 /// Generates the HTML table. This function should only be called after
381 /// run(), when all tests have been executed.
383 /// \param os Output stream.
384 /// \param incl_ok_tests Set if successful tests should be shown;
386 /// \param name Name of generated report.
389 HtmlOutput::generate(ostream
& os
, bool incl_ok_tests
, const string
& name
)
391 ClassType
type(_total_errors
> 0 ? Error
: Success
);
398 sub_title(os
, "Summary", 2);
399 table_header(os
, TableClass_Summary
, "Summary of test results");
401 table_entry(os
, Title
, "Tests", 30);
402 table_entry(os
, Title
, "Errors", 30);
403 table_entry(os
, Title
, "Success", 30);
404 table_entry(os
, Title
, "Time (s)", 10);
407 ss
.str(""), ss
<< _total_tests
;
408 table_entry(os
, type
, ss
.str(), 30);
409 ss
.str(""), ss
<< _total_errors
;
410 table_entry(os
, type
, ss
.str(), 30);
411 ss
.str(""), ss
<< correct(_total_tests
, _total_errors
) << "%";
412 table_entry(os
, type
, ss
.str(), 30);
413 ss
.str(""), ss
<< _total_time
;
414 table_entry(os
, type
, ss
.str(), 10);
419 // Table: Test suites
421 sub_title(os
, "Test suites", 2);
422 table_header(os
, TableClass_Suites
, "Test Suites");
424 table_entry(os
, Title
, "Name");
425 table_entry(os
, Title
, "Tests", 10);
426 table_entry(os
, Title
, "Errors", 10);
427 table_entry(os
, Title
, "Success", 10);
428 table_entry(os
, Title
, "Time (s)", 10);
430 for_each(_suites
.begin(), _suites
.end(), SuiteRow(os
));
434 // Individual tests tables
436 for_each(_suites
.begin(), _suites
.end(), TestSuiteRow(os
, incl_ok_tests
));
439 // Individual tests result tables
441 if(_total_errors
!= 0)
443 sub_title(os
, "Test results", 2);
444 for_each(_suites
.begin(), _suites
.end(), SuiteTestResult(os
));