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::isnil;
56  using util::contains;
57  using util::typeStr;
58 
59  typedef map<string, Launcher*> TestMap;
60  typedef shared_ptr<TestMap> PTestMap;
61  typedef map<string,PTestMap> GroupMap;
62 
63 
64 
65  namespace {
74  class Registry
75  {
76  GroupMap groups_;
77 
78  public:
79  Registry() { };
80 
81  PTestMap&
82  getGroup (string grpID)
83  {
84  return groups_[grpID];
85  };
86 
87  void
88  add2group (Launcher* test, string testID, string groupID)
89  {
90  REQUIRE( test );
91  REQUIRE( !isnil(testID) );
92  REQUIRE( !isnil(groupID) );
93 
94  PTestMap& group = getGroup(groupID);
95  if (!group)
96  group.reset( new TestMap );
97  (*group)[testID] = test;
98  }
99  };
100 
101  Registry testcases;
102  }
103 
104 
105 
106 
116  void
117  Suite::enrol (Launcher* test, string testID, string groups)
118  {
119  REQUIRE( test );
120  REQUIRE( !isnil(testID) );
121 
122  std::istringstream ss(groups);
123  string group;
124  while (ss >> group )
125  testcases.add2group(test, testID, group);
126 
127  // Magic: always add any testcase to groupID="ALL"
128  testcases.add2group(test,testID, ALLGROUP);
129  }
130 
132  const string Suite::ALLGROUP = "ALL";
133 
135  const int Suite::EXCEPTION_THROWN = 5;
136  const int Suite::TEST_OK = 0;
137 
138 
139 
145  Suite::Suite(string groupID)
146  : groupID_(groupID)
147  , exitCode_(0)
148  {
149  REQUIRE( !isnil(groupID) );
150  TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
151 
152  // Seed random number generator
153  std::srand (std::time (nullptr));
154 
155  if (!testcases.getGroup(groupID))
156  throw lumiera::error::Invalid ("empty testsuite");
157  }
158 
159 
160  int
161  Suite::getExitCode () const
162  {
163  return exitCode_;
164  }
165 
166 
167 
168 #define IS_VALID(test,testID) \
169  ASSERT ((test), "NULL testcase launcher for test '%s' found in testsuite '%s'", groupID_.c_str(),testID.c_str());
170 
171 
172  namespace { // internal helper for launching with error logging
173 
174  int
175  invokeTestCase (Test& theTest, Arg cmdline)
176  {
177  try
178  {
179  INFO (test, "++------------------- invoking TEST: %s", cStr(typeStr (theTest)));
180  theTest.run (cmdline);
181  return Suite::TEST_OK;
182  }
183  catch (lumiera::Error& failure)
184  {
185  lumiera_err errorID = lumiera_error(); // reset error flag
186  cerr << "*** Test Failure " << theTest << endl;
187  cerr << "*** : " << failure.what() << endl;
188  ERROR (test, "Error state %s", errorID);
189  WARN (progress, "Caught exception %s", failure.what());
191  } }
192  }
193 
194 
204  bool
205  Suite::run (Arg cmdline)
206  {
207  PTestMap tests = testcases.getGroup(groupID_);
208  if (!tests)
209  throw lumiera::error::Invalid ("No tests found for test group \""+groupID_+"\"");
210 
211  if (0 < cmdline.size())
212  {
213  string& testID (cmdline[0]);
214  trim(testID);
215  if ( contains (*tests, testID))
216  {
217  // first cmdline argument denotes a valid testcase registered in
218  // this group: invoke just this test with the remaining cmdline
219  Launcher* test = (*tests)[testID];
220  IS_VALID (test,testID);
221 
222  // Special contract: in case the cmdline holds no actual arguments
223  // beyond the test name, then it's cleared entirely.
224  if (1 == cmdline.size()) cmdline.clear(); // TODO this invalidates also testID -- really need to redesign the API ////TICKET #289
225 
226  exitCode_ |= invokeTestCase (*test->makeInstance(), cmdline); // TODO confusing statement, improve definition of test collection datatype Ticket #289
227  return true;
228  }
229  else
230  throw lumiera::error::Invalid ("unknown test : "+testID);
231  }
232 
233  // no test-ID was specified.
234  // Instantiate all tests cases and execute them.
235  for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
236  {
237  cout << "\n ----------"<< i->first<< "----------\n";
238  Launcher* test = (i->second);
239  IS_VALID (test, i->first);
240  exitCode_ |= invokeTestCase (*test->makeInstance(), cmdline); // actually no cmdline arguments
241  }
242  return true;
243  }
244 
245 
249  void
251  {
252  lib::Cmdline noCmdline("");
253  PTestMap tests = testcases.getGroup(groupID_);
254  ASSERT (tests);
255 
256  cout << "TESTING \"Component Test Suite: " << groupID_ << "\" ./test-components\n\n";
257 
258  for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
259  {
260  string key (i->first);
261  cout << "\n\n";
262  cout << "TEST \""<<key<<"\" "<<key<<" <<END\n";
263  Launcher* test = (i->second);
264  IS_VALID (test, i->first);
265  try
266  {
267  test->makeInstance()->run(noCmdline); // run it to insert test generated output
268  }
269  catch (...)
270  {
271  cout << "PLANNED ============= " << lumiera_error() << "\n";
272  }
273  cout << "END\n";
274  }
275  }
276 
277 
278 
279 } // namespace test
Automatically use custom string conversion in C++ stream output.
CStr cStr(std::string const &rendered)
convenience shortcut: forced conversion to c-String via string.
Definition: symbol.hpp:68
Definition: run.hpp:49
virtual CStr what() const noexcept override
std::exception interface : yield a diagnostic message
This header is for including and configuring NoBug.
helper to collect and manage the test cases.
Definition: suite.cpp:74
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:199
Abstract Base Class for all testcases.
Definition: run.hpp:62
bool run(Arg cmdline)
run all testcases contained in this Suite.
Definition: suite.cpp:205
Suite(string groupID)
create a suite comprised of all the testcases previously registered with this this group...
Definition: suite.cpp:145
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:250
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:117
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:71
bool contains(SEQ const &cont, typename SEQ::const_reference val)
shortcut for brute-force containment test in any sequential container
Definition: util.hpp:255