GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
62  init_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
77  init_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  ***************************************************************************/
92 GTestSuites::GTestSuites(const std::string& name)
93 {
94  // Initialise members
95  init_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  ***************************************************************************/
193 GTestSuite* 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  ***************************************************************************/
215 const 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  ***************************************************************************/
242 GTestSuite* 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  ***************************************************************************/
300 GTestSuite* 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  ***************************************************************************/
337 void 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  ***************************************************************************/
361 void GTestSuites::extend(const GTestSuites& suites)
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  ***************************************************************************/
389 int 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  ***************************************************************************/
407 int GTestSuites::failures(void) const
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  ***************************************************************************/
425 int 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  ***************************************************************************/
481 void 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  ***************************************************************************/
503 std::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
556  m_log.buffer_size(1);
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  ***************************************************************************/
613 void 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 }
void write(GXml &xml) const
Write Test Suites into XML document.
GTestSuites & operator=(const GTestSuites &suites)
Assignment operator.
Test suite container class.
Definition: GTestSuites.hpp:53
Test suite container class interface definition.
const int & errors(void) const
Return the number of errors.
Definition: GTestSuite.hpp:198
void save(const GFilename &filename) const
Save XML document into file.
Definition: GXml.cpp:581
std::vector< GTestSuite * > m_testsuites
Vector of test suites.
XML class interface definition.
void clear(void)
Clear test suites.
Test class.
Definition: GTestCase.hpp:47
void free_members(void)
Delete class members.
#define G_INSERT
Definition: GTestSuites.cpp:40
void copy_members(const GTestSuites &suites)
Copy class members.
XML element node class.
Definition: GXmlElement.hpp:48
int failures(void) const
Return the total number of failures in all test suites.
void buffer_size(const int &size)
Set the buffer size.
Definition: GLog.hpp:327
double duration(void) const
Return the total duration of all tests.
Definition: GTestSuite.cpp:950
Gammalib tools definition.
const std::string & name(void) const
Return XML element name.
#define G_AT
Definition: GTestSuites.cpp:38
void reserve(const int &num)
Reserves space for test suites in container.
void cout(const bool &flag)
Set standard output stream (cout) flag.
Definition: GLog.hpp:275
const ErrorKind & kind(void) const
Return kind of test case.
Definition: GTestCase.hpp:195
const double & duration(void) const
Return test case duration.
Definition: GTestCase.hpp:250
time_t m_timestamp
Timestamp.
GTestSuites * clone(void) const
Clone test suites.
bool is_empty(void) const
Signals if there are no test suites in the container.
Test case class interface definition.
GTestSuite * at(const int &index)
Returns pointer to test suite.
const GXmlAttribute * attribute(const int &index) const
Return attribute.
int size(void) const
Return number of test suites in container.
GTestSuite * set(const int &index, const GTestSuite &suite)
Set test suite in container.
XML class.
Definition: GXml.hpp:172
Filename class.
Definition: GFilename.hpp:62
#define G_SET
Definition: GTestSuites.cpp:39
void save(const GFilename &filename) const
Save test report into XML file.
#define G_REMOVE
Definition: GTestSuites.cpp:41
void close(void)
Close log file.
Definition: GLog.cpp:538
virtual ~GTestSuites(void)
Destructor.
GTestSuite * append(const GTestSuite &suite)
Append test suite to container.
GChatter
Definition: GTypemaps.hpp:33
std::string print(const GChatter &chatter=NORMAL) const
Print test suites information.
Abstract test suite class for unit testing on GammaLib fixtures.
Definition: GTestSuite.hpp:53
const std::string & name(void) const
Return test suite name.
Definition: GTestSuite.hpp:158
void remove(const int &index)
Remove test suite from container.
int size(void) const
Return number of tests in test suite.
Definition: GTestSuite.hpp:148
GLog m_log
Log.
const std::string & message(void) const
Return test case message.
Definition: GTestCase.hpp:142
const time_t & timestamp(void) const
Return the timestamp.
Definition: GTestSuite.hpp:224
GTestSuite * insert(const int &index, const GTestSuite &suite)
Insert test suite into container.
const std::string & type(void) const
Return test case type.
Definition: GTestCase.hpp:167
const std::string & name(void) const
Return test suite container name.
const bool & has_passed(void) const
Return whether the test passed.
Definition: GTestCase.hpp:225
const std::string & name(void) const
Return test case name.
Definition: GTestCase.hpp:117
const int & failures(void) const
Return the number of failures.
Definition: GTestSuite.hpp:210
std::string m_name
Name of container.
void init_members(void)
Initialise class members.
GTestSuites(void)
Void constructor.
Definition: GTestSuites.cpp:59
virtual GTestSuite * clone(void) const =0
Clones object.
virtual GXmlNode * append(const GXmlNode &node)
Append XML child node.
Definition: GXmlNode.cpp:287
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition: GTools.cpp:1044
bool run(void)
Run all tests.
int tests(void) const
Return the total number of tests they are in all test suites.
int errors(void) const
Return the total number of errors in all test suites.
void extend(const GTestSuites &suites)
Append test suite container.
GXmlNode * append(const GXmlNode &node)
Append child node to XML document root.
Definition: GXml.cpp:279
void clear(void)
Clear object.
Definition: GLog.cpp:456
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489