Lumiera  0.pre.03
»edit your freedom«
suite.cpp
Go to the documentation of this file.
1 /*
2  Suite - helper class for running collections of tests
3 
4  Copyright (C) Lumiera.org
5  2008, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (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, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
23 
30 #include "include/logging.h"
31 #include "lib/hash-standard.hpp"
32 #include "lib/format-cout.hpp"
33 #include "lib/test/suite.hpp"
34 #include "lib/test/run.hpp"
35 #include "lib/cmdline.hpp"
36 #include "lib/error.hpp"
37 #include "lib/util.hpp"
38 
39 #include <boost/algorithm/string.hpp>
40 #include <memory>
41 #include <sstream>
42 #include <string>
43 #include <memory>
44 #include <vector>
45 #include <map>
46 
47 
48 namespace test {
49 
50  using std::map;
51  using std::vector;
52  using std::shared_ptr;
53  using boost::algorithm::trim;
54 
55  using util::cStr;
56  using util::isnil;
57  using util::contains;
58  using util::typeStr;
59 
60  typedef map<string, Launcher*> TestMap;
61  typedef shared_ptr<TestMap> PTestMap;
62  typedef map<string,PTestMap> GroupMap;
63 
64 
65 
66  namespace {
75  class Registry
76  {
77  GroupMap groups_;
78 
79  public:
80  Registry() { };
81 
82  PTestMap&
83  getGroup (string grpID)
84  {
85  return groups_[grpID];
86  };
87 
88  void
89  add2group (Launcher* test, string testID, string groupID)
90  {
91  REQUIRE( test );
92  REQUIRE( !isnil(testID) );
93  REQUIRE( !isnil(groupID) );
94 
95  PTestMap& group = getGroup(groupID);
96  if (!group)
97  group.reset( new TestMap );
98  (*group)[testID] = test;
99  }
100  };
101 
102  Registry testcases;
103  }
104 
105 
106 
107 
117  void
118  Suite::enrol (Launcher* test, string testID, string groups)
119  {
120  REQUIRE( test );
121  REQUIRE( !isnil(testID) );
122 
123  std::istringstream ss(groups);
124  string group;
125  while (ss >> group )
126  testcases.add2group(test, testID, group);
127 
128  // Magic: always add any testcase to groupID="ALL"
129  testcases.add2group(test,testID, ALLGROUP);
130  }
131 
133  const string Suite::ALLGROUP = "ALL";
134 
136  const int Suite::EXCEPTION_THROWN = 5;
137  const int Suite::TEST_OK = 0;
138 
139 
140 
146  Suite::Suite(string groupID)
147  : groupID_(groupID)
148  , exitCode_(0)
149  {
150  REQUIRE( !isnil(groupID) );
151  TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
152 
153  // Seed random number generator
154  std::srand (std::time (nullptr));
155 
156  if (!testcases.getGroup(groupID))
157  throw lumiera::error::Invalid ("empty testsuite");
158  }
159 
160 
161  int
162  Suite::getExitCode () const
163  {
164  return exitCode_;
165  }
166 
167 
168 
169 #define IS_VALID(test,testID) \
170  ASSERT ((test), "NULL testcase launcher for test '%s' found in testsuite '%s'", groupID_.c_str(),testID.c_str());
171 
172 
173  namespace { // internal helper for launching with error logging
174 
175  int
176  invokeTestCase (Test& theTest, Arg cmdline)
177  {
178  try
179  {
180  INFO (test, "++------------------- invoking TEST: %s", cStr(typeStr (theTest)));
181  theTest.run (cmdline);
182  return Suite::TEST_OK;
183  }
184  catch (lumiera::Error& failure)
185  {
186  lumiera_err errorID = lumiera_error(); // reset error flag
187  cerr << "*** Test Failure " << theTest << endl;
188  cerr << "*** : " << failure.what() << endl;
189  ERROR (test, "Error state %s", errorID);
190  WARN (progress, "Caught exception %s", failure.what());
192  } }
193  }
194 
195 
205  bool
206  Suite::run (Arg cmdline)
207  {
208  PTestMap tests = testcases.getGroup(groupID_);
209  if (!tests)
210  throw lumiera::error::Invalid ("No tests found for test group \""+groupID_+"\"");
211 
212  if (0 < cmdline.size())
213  {
214  string& testID (cmdline[0]);
215  trim(testID);
216  if ( contains (*tests, testID))
217  {
218  // first cmdline argument denotes a valid testcase registered in
219  // this group: invoke just this test with the remaining cmdline
220  Launcher* test = (*tests)[testID];
221  IS_VALID (test,testID);
222 
223  // Special contract: in case the cmdline holds no actual arguments
224  // beyond the test name, then it's cleared entirely.
225  if (1 == cmdline.size()) cmdline.clear(); // TODO this invalidates also testID -- really need to redesign the API ////TICKET #289
226 
227  exitCode_ |= invokeTestCase (*test->makeInstance(), cmdline); // TODO confusing statement, improve definition of test collection datatype Ticket #289
228  return true;
229  }
230  else
231  throw lumiera::error::Invalid ("unknown test : "+testID);
232  }
233 
234  // no test-ID was specified.
235  // Instantiate all tests cases and execute them.
236  for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
237  {
238  cout << "\n ----------"<< i->first<< "----------\n";
239  Launcher* test = (i->second);
240  IS_VALID (test, i->first);
241  exitCode_ |= invokeTestCase (*test->makeInstance(), cmdline); // actually no cmdline arguments
242  }
243  return true;
244  }
245 
246 
250  void
252  {
253  lib::Cmdline noCmdline("");
254  PTestMap tests = testcases.getGroup(groupID_);
255  ASSERT (tests);
256 
257  cout << "TESTING \"Component Test Suite: " << groupID_ << "\" ./test-components\n\n";
258 
259  for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
260  {
261  string key (i->first);
262  cout << "\n\n";
263  cout << "TEST \""<<key<<"\" "<<key<<" <<END\n";
264  Launcher* test = (i->second);
265  IS_VALID (test, i->first);
266  try
267  {
268  test->makeInstance()->run(noCmdline); // run it to insert test generated output
269  }
270  catch (...)
271  {
272  cout << "PLANNED ============= " << lumiera_error() << "\n";
273  }
274  cout << "END\n";
275  }
276  }
277 
278 
279 
280 } // namespace test
Automatically use custom string conversion in C++ stream output.
Definition: run.hpp:49
virtual CStr what() const noexcept override
std::exception interface : yield a diagnostic message
const char * cStr(string const &org)
convenience shortcut: conversion to c-String via string.
Definition: util.hpp:423
This header is for including and configuring NoBug.
helper to collect and manage the test cases.
Definition: suite.cpp:75
static const int EXCEPTION_THROWN
exit code returned when any individual test threw
Definition: suite.hpp:80
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:196
Abstract Base Class for all testcases.
Definition: run.hpp:62
bool run(Arg cmdline)
run all testcases contained in this Suite.
Definition: suite.cpp:206
Suite(string groupID)
create a suite comprised of all the testcases previously registered with this this group...
Definition: suite.cpp:146
Class to encapsulate the typical C-style commandline definition.
Simple test class runner.
Building and running a suite of tests, implemented as test classes.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
lumiera_err lumiera_error(void)
Get and clear current error state.
Definition: error-state.c:124
static const string ALLGROUP
"magic" groupID containing all registered testcases
Definition: suite.hpp:78
Helper to use a single extension point for specialised hash functions.
interface: generic testcase creating functor.
Definition: run.hpp:72
Lumiera error handling (C++ interface).
void describe()
print to stdout an enumeration of all testcases in this suite, in a format suitable for use with Ceht...
Definition: suite.cpp:251
static void enrol(Launcher *test, string testID, string groups)
register the given test-launcher, so it can be later accessed either as a member of one of the specif...
Definition: suite.cpp:118
Abstraction of the usual int argc, int** argv-Commandline, to be able to treat it as a vector of stri...
Definition: cmdline.hpp:57
Interface and Base definition for all Lumiera Exceptions.
Definition: error.hpp:69
bool contains(SEQ const &cont, typename SEQ::const_reference val)
shortcut for brute-force containment test in any sequential container
Definition: util.hpp:230