Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
suite.cpp
Go to the documentation of this file.
1/*
2 Suite - helper class for running collections of tests
3
4 Copyright (C)
5 2008, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
14
21#include "lib/error.hpp"
22#include "lib/symbol.hpp"
23#include "lib/format-cout.hpp"
24#include "lib/test/suite.hpp"
25#include "lib/test/run.hpp"
26#include "lib/cmdline.hpp"
27#include "lib/random.hpp"
28#include "lib/util.hpp"
29
30#include <boost/algorithm/string.hpp>
31#include <optional>
32#include <sstream>
33#include <string>
34#include <memory>
35#include <vector>
36#include <map>
37
38
39namespace test {
40
41 using std::map;
42 using std::vector;
43 using std::optional;
44 using std::shared_ptr;
45 using std::string_literals::operator ""s;
46 using boost::algorithm::trim;
47
48 using util::isnil;
49 using util::contains;
50 using util::toString;
51 using util::typeStr;
52 using lib::SeedNucleus;
53 using lib::Random;
54
55 typedef map<string, Launcher*> TestMap;
56 typedef shared_ptr<TestMap> PTestMap;
57 typedef map<string,PTestMap> GroupMap;
58
59
60
61 namespace {
71 {
73
74 public:
75 Registry() { };
76
78 getGroup (string grpID)
79 {
80 return groups_[grpID];
81 };
82
83 void
84 add2group (Launcher* test, string testID, string groupID)
85 {
86 REQUIRE( test );
87 REQUIRE( !isnil(testID) );
88 REQUIRE( !isnil(groupID) );
89
90 PTestMap& group = getGroup(groupID);
91 if (!group)
92 group.reset( new TestMap );
93 (*group)[testID] = test;
94 }
95 };
96
98 : public SeedNucleus
99 {
100 public:
103
104 uint64_t
105 getSeed() override
106 {
107 auto seed = fixedSeed? *fixedSeed : lib::entropyGen.u64();
108 auto kind = fixedSeed? "!fix" : "rand";
109 NOTICE (test, " ++>>> SEED(%s) <<<: %s", kind, toString(seed).c_str());
110 return seed;
111 }
112 };
113
114 /* ===== global implementation state ===== */
117 }
118
119
120
121
130 void
131 Suite::enrol (Launcher* test, string testID, string groups)
132 {
133 REQUIRE( test );
134 REQUIRE( !isnil(testID) );
135
136 std::istringstream ss(groups);
137 string group;
138 while (ss >> group )
139 testcases.add2group(test, testID, group);
140
141 // Magic: always add any testcase to groupID="ALL"
142 testcases.add2group(test,testID, ALLGROUP);
143 }
144
146 const string Suite::ALLGROUP = "ALL";
147
149 const int Suite::EXCEPTION_THROWN = 5;
150 const int Suite::TEST_OK = 0;
151
152
153
158 Suite::Suite (string groupID, opt_uint64 optSeed)
159 : groupID_(groupID)
160 , exitCode_(0)
161 {
162 REQUIRE( !isnil(groupID) );
163 TRACE(test, "Test-Suite( groupID=%s )\n", groupID.c_str () );
164
165 // Seed random number generator
166 std::srand (std::time (nullptr));
167
168 suiteSeed.fixedSeed = optSeed;
169
170 if (!testcases.getGroup(groupID))
171 throw lumiera::error::Invalid ("empty testsuite");
172 }
173
174
175 int
177 {
178 return exitCode_;
179 }
180
181
182
183#define IS_VALID(test,testID) \
184 ASSERT ((test), "NULL testcase launcher for test '%s' found in testsuite '%s'", groupID_.c_str(),testID.c_str());
185
186
187 namespace { // internal helper for launching with error logging
188
189 int
190 invokeTestCase (Test& theTest, Arg cmdline)
191 {
192 try {
193 INFO (test, "++------------------- invoking TEST: %s", cStr(typeStr (theTest)));
194 theTest.run (cmdline);
195 return Suite::TEST_OK;
196 }
197 catch (lumiera::Error& failure)
198 {
199 lumiera_err errorID = lumiera_error(); // reset error flag
200 cerr << "*** Test Failure " << theTest << endl;
201 cerr << "*** : " << failure.what() << endl;
202 ERROR (test, "Error state %s", errorID);
203 WARN (progress, "Caught exception %s", failure.what());
205 } }
206 }
207
208
209
210 void
212 {
213 lib::defaultGen.reseed (suiteSeed);
214 }
215
216
217 Random
219 {
220 return Random{lib::seedFromDefaultGen()};
221 }
222
223
224 uint
225 Test::firstVal (Arg arg, uint someNumber)
226 {
227 if (not isnil(arg))
228 someNumber = boost::lexical_cast<uint> (arg[1]); // may throw
229 return someNumber;
230 }
231
232 string
234 {
235 return isnil(arg)? util::BOTTOM_INDICATOR
236 : arg[1];
237 }
238
239
240
250 bool
251 Suite::run (Arg cmdline)
252 {
253 PTestMap tests = testcases.getGroup(groupID_);
254 if (!tests)
255 throw lumiera::error::Invalid ("No tests found for test group \""+groupID_+"\"");
256
257 if (0 < cmdline.size())
258 {
259 string& testID (cmdline[0]);
260 trim(testID);
261 if ( contains (*tests, testID))
262 {
263 // first cmdline argument denotes a valid testcase registered in
264 // this group: invoke just this test with the remaining cmdline
265 Launcher* test = (*tests)[testID];
266 IS_VALID (test,testID);
267
268 // Special contract: in case the cmdline holds no actual arguments
269 // beyond the test name, then it's cleared entirely.
270 if (1 == cmdline.size()) cmdline.clear(); // TODO this invalidates also testID -- really need to redesign the API ////TICKET #289
271
272 exitCode_ |= invokeTestCase (*test->makeInstance(), cmdline); // TODO confusing statement, improve definition of test collection datatype Ticket #289
273 return true;
274 }
275 else
276 throw lumiera::error::Invalid ("unknown test : "+testID);
277 }
278
279 // no test-ID was specified.
280 // Instantiate all tests cases and execute them.
281 for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
282 {
283 cout << "\n ----------"<< i->first<< "----------\n";
284 Launcher* test = (i->second);
285 IS_VALID (test, i->first);
286 exitCode_ |= invokeTestCase (*test->makeInstance(), cmdline); // actually no cmdline arguments
287 }
288 return true;
289 }
290
291
298 void
300 {
301 lib::Cmdline noCmdline("");
302 PTestMap tests = testcases.getGroup(groupID_);
303 ASSERT (tests);
304
305 cout << "TESTING \"Component Test Suite: " << groupID_ << "\" ./test-components\n\n";
306
307 for ( TestMap::iterator i=tests->begin(); i!=tests->end(); ++i )
308 {
309 string key (i->first);
310 cout << "\n\n";
311 cout << "TEST \""<<key<<"\" "<<key<<" <<END\n";
312 Launcher* test = (i->second);
313 IS_VALID (test, i->first);
314 try
315 {
316 test->makeInstance()->run(noCmdline); // run it to insert test generated output
317 }
318 catch (...)
319 {
320 cout << "PLANNED ============= " << lumiera_error() << "\n";
321 }
322 cout << "END\n";
323 }
324 }
325
326
327
328} // namespace test
Abstraction of the usual int argc, int** argv-Commandline, to be able to treat it as a vector of stri...
Definition cmdline.hpp:49
uint64_t u64()
random 64bit number from full range.
Definition random.hpp:224
void reseed(SeedNucleus &)
inject controlled randomisation
Definition random.hpp:188
Establishes a seed point for any instance or performance.
Definition random.hpp:49
Interface and Base definition for all Lumiera Exceptions.
Definition error.hpp:65
virtual CStr what() const noexcept override
std::exception interface : yield a diagnostic message
interface: generic testcase creating functor.
Definition run.hpp:69
int exitCode_
Definition suite.hpp:62
static const int TEST_OK
Definition suite.hpp:72
void describe()
print to stdout an enumeration of all testcases in this suite, in a format suitable for use with Ceht...
Definition suite.cpp:299
static const int EXCEPTION_THROWN
exit code returned when any individual test threw
Definition suite.hpp:73
Suite(string groupID, opt_uint64 seed)
create a suite comprised of all the testcases previously registered with this this group.
Definition suite.cpp:158
int getExitCode() const
Definition suite.cpp:176
string groupID_
Definition suite.hpp:61
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:131
static const string ALLGROUP
"magic" groupID containing all registered testcases
Definition suite.hpp:71
bool run(Arg cmdline)
run all testcases contained in this Suite.
Definition suite.cpp:251
Abstract Base Class for all testcases.
Definition run.hpp:54
virtual void run(Arg arg)=0
lib::Random makeRandGen()
build a dedicated new RandomGen, seeded from the default-Gen
Definition suite.cpp:218
static uint firstVal(Arg, uint=1)
conveniently use some number given as argument, with optional default
Definition suite.cpp:225
void seedRand()
draw a new random seed from a common nucleus, and re-seed the default-Gen.
Definition suite.cpp:211
static string firstTok(Arg)
conveniently pick the first token from the argument line
Definition suite.cpp:233
helper to collect and manage the test cases.
Definition suite.cpp:71
void add2group(Launcher *test, string testID, string groupID)
Definition suite.cpp:84
opt_uint64 fixedSeed
optionally a fixed random seed to inject in each invoked test
Definition suite.cpp:102
Class to encapsulate the typical C-style commandline definition.
lumiera_err lumiera_error(void)
Get and clear current error state.
const char * lumiera_err
Definition error.h:48
Lumiera error handling (C++ interface).
Automatically use custom string conversion in C++ stream output.
unsigned int uint
Definition integral.hpp:29
SeedNucleus & seedFromDefaultGen()
draw seed another Generator from the default RandomSequencer
Definition random.cpp:74
Random entropyGen
a global RandomSequencer seeded with real entropy
Definition random.cpp:71
Random defaultGen
a global default RandomSequencer for mundane purposes
Definition random.cpp:70
LumieraError< LERR_(INVALID)> Invalid
Definition error.hpp:211
int invokeTestCase(Test &theTest, Arg cmdline)
Definition suite.cpp:190
Test runner and basic definitions for tests.
map< string, PTestMap > GroupMap
Definition suite.cpp:57
shared_ptr< TestMap > PTestMap
Definition suite.cpp:56
map< string, Launcher * > TestMap
Definition suite.cpp:55
std::vector< string > & Arg
Definition run.hpp:45
std::optional< uint64_t > opt_uint64
Definition suite.hpp:43
bool contains(MAP &map, typename MAP::key_type const &key)
shortcut for containment test on a map
Definition util.hpp:230
std::string toString(TY const &val) noexcept
get some string representation of any object, reliably.
bool isnil(lib::time::Duration const &dur)
Generating (pseudo) random numbers with controlled seed.
Simplistic test class runner.
#define IS_VALID(test, testID)
Definition suite.cpp:183
Building and running a suite of tests, implemented as test classes.
Marker types to indicate a literal string and a Symbol.
CStr cStr(std::string const &rendered)
convenience shortcut: forced conversion to c-String via string.
Definition symbol.hpp:60
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...