GammaLib 2.0.0
Loading...
Searching...
No Matches
GTestSuites.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GTestSuites.cpp - Test suite container class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2012-2021 by Jean-Baptiste Cayrou *
5 * ----------------------------------------------------------------------- *
6 * *
7 * This program is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 * *
20 ***************************************************************************/
21/**
22 * @file GTestSuites.cpp
23 * @brief Test suite container class implementation
24 * @author Jean-Baptiste Cayrou
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <ctime>
32#include "GTools.hpp"
33#include "GXml.hpp"
34#include "GTestSuites.hpp"
35#include "GTestCase.hpp"
36
37/* __ Method name definitions ____________________________________________ */
38#define G_AT "GTestSuites::at(int&)"
39#define G_SET "GTestSuites::set(int&, GTestSuite&)"
40#define G_INSERT "GTestSuite* GTestSuites::insert(int&, GTestSuite&)"
41#define G_REMOVE "GTestSuites::remove(int&)"
42
43/* __ Macros _____________________________________________________________ */
44
45/* __ Coding definitions _________________________________________________ */
46
47/* __ Debug definitions __________________________________________________ */
48
49
50/*==========================================================================
51 = =
52 = Constructors/destructors =
53 = =
54 ==========================================================================*/
55
56/***********************************************************************//**
57 * @brief Void constructor
58 ***************************************************************************/
60{
61 // Initialise members
63
64 //Return
65 return;
66}
67
68
69/***********************************************************************//**
70 * @brief Copy constructor
71 *
72 * @param[in] suites Test suite container.
73 ***************************************************************************/
75{
76 // Initialise members
78
79 // Copy members
80 copy_members(suites);
81
82 //Return
83 return;
84}
85
86
87/***********************************************************************//**
88 * @brief Name constructor
89 *
90 * @param[in] name Name of the test suites.
91 ***************************************************************************/
92GTestSuites::GTestSuites(const std::string& name)
93{
94 // Initialise members
96
97 // Set name
98 m_name = name;
99
100 //Return
101 return;
102}
103
104
105/***********************************************************************//**
106 * @brief Destructor
107 ***************************************************************************/
109{
110 //Free members
111 free_members();
112
113 //Return
114 return;
115}
116
117
118/*==========================================================================
119 = =
120 = Operators =
121 = =
122 ==========================================================================*/
123
124/***********************************************************************//**
125 * @brief Assignment operator
126 *
127 * @param[in] suites Test suite container.
128 * @return Test suite container.
129 ***************************************************************************/
131{
132 // Execute only if object is not identical
133 if (this != &suites) {
134
135 // Free members
136 free_members();
137
138 // Initialise members
139 init_members();
140
141 // Copy members
142 copy_members(suites);
143
144 } // endif: object was not identical
145
146 // Return
147 return *this;
148}
149
150
151/*==========================================================================
152 = =
153 = Public methods =
154 = =
155 ==========================================================================*/
156
157/***********************************************************************//**
158 * @brief Clear test suites
159 ***************************************************************************/
161{
162 // Free members
163 free_members();
164
165 // Initialise members
166 init_members();
167
168 // Return
169 return;
170}
171
172
173/***********************************************************************//**
174 * @brief Clone test suites
175 *
176 * @return Pointer to deep copy of test suites.
177 ***************************************************************************/
179{
180 // Clone object
181 return new GTestSuites(*this);
182}
183
184
185/***********************************************************************//**
186 * @brief Returns pointer to test suite
187 *
188 * @param[in] index Test suite index [0,...,size()-1].
189 *
190 * @exception GException::out_of_range
191 * Test suite index is out of range.
192 ***************************************************************************/
193GTestSuite* GTestSuites::at(const int& index)
194{
195 // Compile option: raise an exception if index is out of range
196 #if defined(G_RANGE_CHECK)
197 if (index < 0 || index >= size()) {
198 throw GException::out_of_range(G_AT, "Test suite index", index, size());
199 }
200 #endif
201
202 // Return test suite
203 return (m_testsuites[index]);
204}
205
206
207/***********************************************************************//**
208 * @brief Returns pointer to test suite (const version)
209 *
210 * @param[in] index Test Suite index [0,...,size()-1].
211 *
212 * @exception GException::out_of_range
213 * Test suite index is out of range.
214 ***************************************************************************/
215const GTestSuite* GTestSuites::at(const int& index) const
216{
217 // Compile option: raise an exception if index is out of range
218 #if defined(G_RANGE_CHECK)
219 if (index < 0 || index >= size()) {
220 throw GException::out_of_range(G_AT, "Test suite index", index, size());
221 }
222 #endif
223
224 // Return test suite
225 return (m_testsuites[index]);
226}
227
228
229/***********************************************************************//**
230 * @brief Set test suite in container
231 *
232 * @param[in] index Test suite index [0,...,size()-1].
233 * @param[in] suite Test suite.
234 * @return Pointer to deep copy of test suite.
235 *
236 * @exception GException::out_of_range
237 * Test suite index is out of range.
238 *
239 * Set test suite in the container. A deep copy of the test suite will be
240 * made.
241 ***************************************************************************/
242GTestSuite* GTestSuites::set(const int& index, const GTestSuite& suite)
243{
244 // Compile option: raise exception if index is out of range
245 #if defined(G_RANGE_CHECK)
246 if (index < 0 || index >= size()) {
247 throw GException::out_of_range(G_SET, "Test suite index", index, size());
248 }
249 #endif
250
251 // Free existing test suite only if it differs from current test suite.
252 // This prevents unintential deallocation of the argument
253 if ((m_testsuites[index] != NULL) && (m_testsuites[index] != &suite)) {
254 delete m_testsuites[index];
255 }
256
257 // Assign new test suite by cloning
258 m_testsuites[index] = suite.clone();
259
260 // Return pointer to test suite
261 return m_testsuites[index];
262}
263
264
265/***********************************************************************//**
266 * @brief Append test suite to container
267 *
268 * @param[in] suite Test suite.
269 * @return Pointer to deep copy of test suite.
270 *
271 * Appends test suite to the container by making a deep copy of the test
272 * suite and storing its pointer.
273 ***************************************************************************/
275{
276 // Create deep copy of test suite
277 GTestSuite* ptr = suite.clone();
278
279 // Append deep copy to container
280 m_testsuites.push_back(ptr);
281
282 // Return pointer to test suite
283 return ptr;
284}
285
286
287/***********************************************************************//**
288 * @brief Insert test suite into container
289 *
290 * @param[in] index Test suite index [0,...,size()-1].
291 * @param[in] suite Test suite.
292 * @return Pointer to deep copy of test suite.
293 *
294 * @exception GException::out_of_range
295 * Test suite index is out of range.
296 *
297 * Inserts a test @p suite into the container before the test suite with the
298 * specified @p index.
299 ***************************************************************************/
300GTestSuite* GTestSuites::insert(const int& index, const GTestSuite& suite)
301{
302 // Compile option: raise exception if index is out of range
303 #if defined(G_RANGE_CHECK)
304 if (is_empty()) {
305 if (index > 0) {
306 throw GException::out_of_range(G_INSERT, "Test suite index", index, size());
307 }
308 }
309 else {
310 if (index < 0 || index >= size()) {
311 throw GException::out_of_range(G_INSERT, "Test suite index", index, size());
312 }
313 }
314 #endif
315
316 // Create deep copy of test suite
317 GTestSuite* ptr = suite.clone();
318
319 // Append deep copy to container
320 m_testsuites.insert(m_testsuites.begin()+index, ptr);
321
322 // Return pointer to test suite
323 return ptr;
324}
325
326
327/***********************************************************************//**
328 * @brief Remove test suite from container
329 *
330 * @param[in] index Test suite index [0,...,size()-1].
331 *
332 * @exception GException::out_of_range
333 * Test suite index is out of range.
334 *
335 * Remove test suite of specified @p index from container.
336 ***************************************************************************/
337void GTestSuites::remove(const int& index)
338{
339 // Compile option: raise exception if index is out of range
340 #if defined(G_RANGE_CHECK)
341 if (index < 0 || index >= size()) {
342 throw GException::out_of_range(G_REMOVE, "Test suite index", index, size());
343 }
344 #endif
345
346 // Erase test suite from container
347 m_testsuites.erase(m_testsuites.begin() + index);
348
349 // Return
350 return;
351}
352
353
354/***********************************************************************//**
355 * @brief Append test suite container
356 *
357 * @param[in] suites Test suite container.
358 *
359 * Append test suite container to the container.
360 ***************************************************************************/
362{
363 // Do nothing if test suite container is empty
364 if (!suites.is_empty()) {
365
366 // Get size. Note that we extract the size first to avoid an
367 // endless loop that arises when a container is appended to
368 // itself.
369 int num = suites.size();
370
371 // Reserve enough space
372 reserve(size() + num);
373
374 // Append deep copies of test suites to container
375 for (int i = 0; i < num; ++i) {
376 m_testsuites.push_back(suites[i]->clone());
377 }
378
379 } // endif: test suite container was not empty
380
381 // Return
382 return;
383}
384
385
386/***********************************************************************//**
387 * @brief Return the total number of errors in all test suites
388 ***************************************************************************/
389int GTestSuites::errors(void) const
390{
391 // Initialise number of errors
392 int errors = 0;
393
394 // Add up the errors from all test suites
395 for (int i = 0; i < m_testsuites.size(); ++i) {
396 errors += m_testsuites[i]->errors();
397 }
398
399 // Return errors
400 return errors;
401}
402
403
404/***********************************************************************//**
405 * @brief Return the total number of failures in all test suites
406 ***************************************************************************/
408{
409 // Initialise number of failures
410 int failures=0;
411
412 // Add up the failures from all test suites
413 for (int i = 0; i < m_testsuites.size(); ++i) {
414 failures += m_testsuites[i]->failures();
415 }
416
417 // Return failures
418 return failures;
419}
420
421
422/***********************************************************************//**
423 * @brief Return the total number of tests they are in all test suites
424 ***************************************************************************/
425int GTestSuites::tests(void) const
426{
427 // Initialise number of tests
428 int tests = 0;
429
430 // Add up the number of tests from all test suites
431 for (int i = 0; i < m_testsuites.size(); ++i) {
432 tests += m_testsuites[i]->size();
433 }
434
435 // Return number of tests
436 return tests;
437}
438
439
440/***********************************************************************//**
441 * @brief Run all tests
442 *
443 * Runs all tests that are in the test suite container. The method returns
444 * true if all test suites succeeded. If one test suite failed, false is
445 * returned.
446 ***************************************************************************/
448{
449 // Set header
450 std::string text = "* " + name() + " *";
451 std::string frame = gammalib::fill("*", text.length());
452
453 // Log name of test suites
454 std::cout << std::endl;
455 std::cout << frame << std::endl;
456 std::cout << text << std::endl;
457 std::cout << frame << std::endl;
458
459 // Initialise success flag
460 bool success = true;
461
462 // Run all test suites
463 for (int i = 0; i < m_testsuites.size(); ++i) {
464 if (!m_testsuites[i]->run()) {
465 success = false;
466 }
467 }
468
469 // Return success flag
470 return success;
471}
472
473
474/***********************************************************************//**
475 * @brief Save test report into XML file.
476 *
477 * @param[in] filename Name of XML file.
478 *
479 * Saves the test results in a JUnit compliant format into an XML file.
480 ***************************************************************************/
481void GTestSuites::save(const GFilename& filename) const
482{
483 // Declare empty XML document
484 GXml xml;
485
486 // Write observations into XML file
487 write(xml);
488
489 // Save XML document
490 xml.save(filename);
491
492 // Return
493 return;
494}
495
496
497/***********************************************************************//**
498 * @brief Print test suites information
499 *
500 * @param[in] chatter Chattiness (defaults to NORMAL).
501 * @return String containing test suites information.
502 ***************************************************************************/
503std::string GTestSuites::print(const GChatter& chatter) const
504{
505 // Initialise result string
506 std::string result;
507
508 // Continue only if chatter is not silent
509 if (chatter != SILENT) {
510
511 // Append header
512 result.append("=== GTestSuites ===");
513
514 // Append information
515 result.append("\n"+gammalib::parformat("Name")+m_name);
516 result.append("\n"+gammalib::parformat("Number of test suites"));
517 result.append(gammalib::str(size()));
518 result.append("\n"+gammalib::parformat("Number of tests"));
519 result.append(gammalib::str(tests()));
520 result.append("\n"+gammalib::parformat("Number of errors"));
521 result.append(gammalib::str(errors()));
522 result.append("\n"+gammalib::parformat("Number of failures"));
523 result.append(gammalib::str(failures()));
524
525 // Append test suites
526 for (int i = 0; i < size(); ++i) {
527 result.append("\n");
528 result.append((*this)[i]->print(chatter));
529 }
530
531 } // endif: chatter was not silent
532
533 // Return result
534 return result;
535}
536
537
538/*==========================================================================
539 = =
540 = Private methods =
541 = =
542 ==========================================================================*/
543
544/***********************************************************************//**
545 * @brief Initialise class members
546 ***************************************************************************/
548{
549 // Initialise members
550 m_name = "Unnamed Test Suites";
551 m_testsuites.clear();
552 m_timestamp = time(NULL);
553 m_log.clear();
554
555 // Set logger parameters
557 m_log.cout(true);
558
559 // Return
560 return;
561}
562
563/***********************************************************************//**
564 * @brief Copy class members
565 *
566 * @param[in] suites Test suites container.
567 *
568 * This method just clone the container not the test suite.
569 ***************************************************************************/
571{
572 // Copy members
573 m_name = suites.m_name;
574 m_timestamp = suites.m_timestamp;
575 m_log = suites.m_log;
576
577 // Clone test suites
578 for (int i = 0; i < suites.m_testsuites.size(); ++i) {
579 m_testsuites[i] = suites.m_testsuites[i]->clone();
580 }
581
582 // Return
583 return;
584}
585
586
587/***********************************************************************//**
588 * @brief Delete class members
589 ***************************************************************************/
591{
592 // Delete test cases
593 for (int i = 0; i < m_testsuites.size(); ++i) {
594 delete m_testsuites[i];
595 m_testsuites[i] = NULL;
596 }
597
598 // Close logger
599 m_log.close();
600
601 // Return
602 return;
603}
604
605
606/***********************************************************************//**
607 * @brief Write Test Suites into XML document
608 *
609 * @param[in] xml XML document.
610 *
611 * Write test suites into the XML document.
612 ***************************************************************************/
613void GTestSuites::write(GXml& xml) const
614{
615 // Append XML element for Test Suites
616 GXmlElement* testsuites = xml.append("testsuites");
617
618 // Set attributes
619 testsuites->attribute("name", m_name);
620
621 // Loop over all test suites in the container
622 for (int i = 0; i < m_testsuites.size(); ++i) {
623
624 // Get a pointer on the current test suite
625 GTestSuite* testsuite = m_testsuites[i];
626
627 // Append XML element for this test suite
628 GXmlElement* element_testsuite = testsuites->append("testsuite");
629
630 // Set attributes
631 element_testsuite->attribute("errors",gammalib::str(testsuite->errors()));
632 element_testsuite->attribute("failures",gammalib::str(testsuite->failures()));
633 element_testsuite->attribute("hostname",""); // not used
634 element_testsuite->attribute("id",gammalib::str(i));
635 element_testsuite->attribute("name",testsuite->name());
636 element_testsuite->attribute("package",""); // not used
637 element_testsuite->attribute("skipped",""); // not used
638 element_testsuite->attribute("tests",gammalib::str(testsuite->size()));
639 element_testsuite->attribute("time",gammalib::str(testsuite->duration(), 3));
640 element_testsuite->attribute("timestamp",gammalib::str(testsuite->timestamp()));
641
642 // Loop over all test cases in the test suite
643 for (int j = 0; j < testsuite->size(); ++j) {
644
645 // Reference to the current test case
646 GTestCase& testcase = (*testsuite)[j];
647
648 // Append XML element for this test case
649 GXmlElement* element_testcase = element_testsuite->append("testcase");
650
651 // Set attributes
652 element_testcase->attribute("classname",name());
653 element_testcase->attribute("name",testcase.name());
654 element_testcase->attribute("time",gammalib::str(testcase.duration(), 3));
655
656 // If a failure or error occured then append the message to the
657 // XML element.
658 if (!testcase.has_passed()) {
659
660 // Append XML element for the test case problem
661 GXmlElement* element_testcase_problem = element_testcase->append("error");
662
663 // Set attributes
664 element_testcase_problem->attribute("message",testcase.message());
665 element_testcase_problem->attribute("type",testcase.type());
666
667 // Set tag name dependent on the type of problem
668 if (testcase.kind() == GTestCase::ERROR_TEST) {
669 element_testcase_problem->name("error");
670 }
671 else {
672 element_testcase_problem->name("failure");
673 }
674
675 } // endif: failure or error occured
676
677 } // endfor: looped over all test cases
678
679 } // endfor: looped over all test suites
680
681 // Return
682 return;
683}
#define G_AT
#define G_SET
Definition GEnergies.cpp:46
#define G_INSERT
#define G_REMOVE
Test case class interface definition.
Test suite container class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
XML class interface definition.
Filename class.
Definition GFilename.hpp:62
void buffer_size(const int &size)
Set the buffer size.
Definition GLog.hpp:327
void close(void)
Close log file.
Definition GLog.cpp:538
void clear(void)
Clear object.
Definition GLog.cpp:456
void cout(const bool &flag)
Set standard output stream (cout) flag.
Definition GLog.hpp:275
Test class.
Definition GTestCase.hpp:47
const double & duration(void) const
Return test case duration.
const bool & has_passed(void) const
Return whether the test passed.
const ErrorKind & kind(void) const
Return kind of test case.
const std::string & name(void) const
Return test case name.
const std::string & type(void) const
Return test case type.
const std::string & message(void) const
Return test case message.
Abstract test suite class for unit testing on GammaLib fixtures.
const int & failures(void) const
Return the number of failures.
int size(void) const
Return number of tests in test suite.
const int & errors(void) const
Return the number of errors.
const time_t & timestamp(void) const
Return the timestamp.
virtual GTestSuite * clone(void) const =0
Clones object.
const std::string & name(void) const
Return test suite name.
double duration(void) const
Return the total duration of all tests.
Test suite container class.
void init_members(void)
Initialise class members.
void reserve(const int &num)
Reserves space for test suites in container.
void extend(const GTestSuites &suites)
Append test suite container.
bool run(void)
Run all tests.
int size(void) const
Return number of test suites in container.
GTestSuites & operator=(const GTestSuites &suites)
Assignment operator.
GTestSuite * append(const GTestSuite &suite)
Append test suite to container.
GLog m_log
Log.
void copy_members(const GTestSuites &suites)
Copy class members.
GTestSuite * at(const int &index)
Returns pointer to test suite.
time_t m_timestamp
Timestamp.
GTestSuites(void)
Void constructor.
GTestSuite * set(const int &index, const GTestSuite &suite)
Set test suite in container.
void save(const GFilename &filename) const
Save test report into XML file.
void remove(const int &index)
Remove test suite from container.
virtual ~GTestSuites(void)
Destructor.
const std::string & name(void) const
Return test suite container name.
bool is_empty(void) const
Signals if there are no test suites in the container.
int failures(void) const
Return the total number of failures in all test suites.
GTestSuite * insert(const int &index, const GTestSuite &suite)
Insert test suite into container.
void write(GXml &xml) const
Write Test Suites into XML document.
std::string print(const GChatter &chatter=NORMAL) const
Print test suites information.
int tests(void) const
Return the total number of tests they are in all test suites.
void clear(void)
Clear test suites.
std::vector< GTestSuite * > m_testsuites
Vector of test suites.
GTestSuites * clone(void) const
Clone test suites.
int errors(void) const
Return the total number of errors in all test suites.
void free_members(void)
Delete class members.
std::string m_name
Name of container.
XML element node class.
const GXmlAttribute * attribute(const int &index) const
Return attribute.
const std::string & name(void) const
Return XML element name.
virtual GXmlNode * append(const GXmlNode &node)
Append XML child node.
Definition GXmlNode.cpp:287
XML class.
Definition GXml.hpp:172
void save(const GFilename &filename) const
Save XML document into file.
Definition GXml.cpp:581
GXmlNode * append(const GXmlNode &node)
Append child node to XML document root.
Definition GXml.cpp:279
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition GTools.cpp:1044