38#ifdef GECODE_HAS_MTRACE
61 : _name(
std::move(s)), _next(_tests),
_rand(
Gecode::Support::RandomGenerator()) {
62 _tests =
this; _n_tests++;
65 Base* Base::_tests =
nullptr;
66 unsigned int Base::_n_tests = 0;
81 for (
Base* t = _tests; t !=
nullptr; t = t->next())
87 for ( ; i < _n_tests; i++) {
88 b[i]->next(_tests); _tests = b[i];
97 void report_error(
const std::string& name,
unsigned int seed,
Options& options, std::ostream& ostream) {
98 ostream <<
"Options: -seed " << seed;
100 ostream <<
" -fixprob " << options.
fixprob;
101 ostream <<
" -test " << name << std::endl;
103 ostream <<
olog.str();
110 if (!strcmp(argv[i],
"-help") || !strcmp(argv[i],
"--help")) {
111 std::cerr <<
"Options for testing:" << std::endl
112 <<
"\t-threads (unsigned int) default: " <<
threads << std::endl
113 <<
"\t\tnumber of threads to use. If 0, as many threads as there are cores are used.\n"
114 <<
"\t\tThreaded execution and logging can not be used at the same time."
116 <<
"\t-seed (unsigned int or \"time\") default: "
118 <<
"\t\tseed for random number generator (unsigned int),"
120 <<
"\t\tor \"time\" for a random seed based on "
121 <<
"current time" << std::endl
122 <<
"\t-fixprob (unsigned int) default: "
124 <<
"\t\t1/fixprob is the probability of computing a fixpoint"
126 <<
"\t-iter (unsigned int) default: " <<
iter<< std::endl
127 <<
"\t\tthe number of iterations" << std::endl
128 <<
"\t-test (string) default: (none)" << std::endl
129 <<
"\t\tsimple pattern for the tests to run" << std::endl
130 <<
"\t\tprefixing with \"-\" negates the pattern" << std::endl
131 <<
"\t\tprefixing with \"^\" requires a match at the beginning" << std::endl
132 <<
"\t\tmultiple pattern-options may be given"
134 <<
"\t-start (string) default: (none)" << std::endl
135 <<
"\t\tsimple pattern for the first test to run" << std::endl
138 <<
"\t\tlog execution of tests"
140 <<
"\t\tthe optional argument determines the style of the log"
142 <<
"\t\twith text as the default style"
144 <<
"\t-stop (boolean) default: "
145 << (
stop ?
"true" :
"false") << std::endl
146 <<
"\t\tstop on first error or continue" << std::endl
147 <<
"\t-list" << std::endl
148 <<
"\t\toutput list of all test cases and exit" << std::endl
151 }
else if (!strcmp(argv[i],
"-threads")) {
152 if (++i == argc)
goto missing;
153 unsigned int argument =
static_cast<unsigned int>(atoi(argv[i]));
159 }
else if (!strcmp(argv[i],
"-seed")) {
160 if (++i == argc)
goto missing;
161 if (!strcmp(argv[i],
"time")) {
162 seed =
static_cast<unsigned int>(time(
nullptr));
164 seed =
static_cast<unsigned int>(atoi(argv[i]));
166 }
else if (!strcmp(argv[i],
"-iter")) {
167 if (++i == argc)
goto missing;
168 iter =
static_cast<unsigned int>(atoi(argv[i]));
169 }
else if (!strcmp(argv[i],
"-fixprob")) {
170 if (++i == argc)
goto missing;
171 fixprob =
static_cast<unsigned int>(atoi(argv[i]));
172 }
else if (!strcmp(argv[i],
"-test")) {
173 if (++i == argc)
goto missing;
174 if (argv[i][0] ==
'^')
176 else if (argv[i][0] ==
'-')
180 }
else if (!strcmp(argv[i],
"-start")) {
181 if (++i == argc)
goto missing;
183 }
else if (!strcmp(argv[i],
"-log")) {
185 }
else if (!strcmp(argv[i],
"-stop")) {
186 if (++i == argc)
goto missing;
187 if(argv[i][0] ==
't') {
189 }
else if (argv[i][0] ==
'f') {
192 }
else if (!strcmp(argv[i],
"-list")) {
199 std::cerr <<
"Logging and multi threading can not be used jointly." << std::endl;
205 std::cerr <<
"Erroneous argument (" << argv[i-1] <<
")" << std::endl
206 <<
" missing parameter" << std::endl;
212 bool positive_patterns =
false;
213 bool match_found =
false;
214 for (
const auto& type_pattern:
testpat) {
215 const auto& type = type_pattern.first;
216 const auto& pattern = type_pattern.second;
218 if (test_name.find(pattern) != std::string::npos) {
224 positive_patterns =
true;
227 if (((type ==
MT_ANY) && (test_name.find(pattern) != std::string::npos)) ||
228 ((type ==
MT_FIRST) && (test_name.find(pattern) == 0)))
234 if (positive_patterns && match_found) {
237 }
else if (positive_patterns && !match_found) {
253 ostream << test->
name() <<
" ";
256 for (
unsigned int i = options.
iter; i--;) {
262 ostream <<
"-" << std::endl;
267 ostream << std::endl;
270 ostream <<
"Exception in \"Gecode::" << e.
what()
272 <<
"Stopping..." << std::endl;
281 int result = EXIT_SUCCESS;
282 for (
auto test : tests) {
283 unsigned int test_seed = seed_sequence.
next();
284 if (!
run_test(test, test_seed, options, std::cout)) {
288 result = EXIT_FAILURE;
302 const std::vector<Base*>& tests;
308 std::atomic<size_t> next_tests;
310 std::atomic<int> result;
312 std::atomic<int> running_threads;
314 std::atomic<bool> execution_done_wait_started;
321 static size_t choose_batch_size(
size_t test_count,
int thread_count) {
322 const int batches_per_thread = 5;
323 std::vector<size_t> batch_sizes = {25, 10, 5, 2};
324 for (
auto batch_size : batch_sizes) {
325 if (test_count > batch_size * thread_count * batches_per_thread) {
334 : tests(tests0), options(options0), output_mutex(),
335 next_tests(0), result(EXIT_SUCCESS), running_threads(thread_count),
336 execution_done_wait_started(false), execution_done_event() {}
340 assert(running_threads.load() == 0);
341 return result.load();
352 execution_done_wait_started.exchange(
true);
353 assert(some_waiting ==
false &&
"Only one thread is allowed to await the result");
354 execution_done_event.wait();
359 result.store(EXIT_FAILURE);
364 if (running_threads.fetch_sub(1) == 1) {
365 execution_done_event.
signal();
371 std::pair<size_t, size_t> next_batch() {
372 const size_t current_start = next_tests.load();
373 const size_t batch_size = choose_batch_size(tests.size() - current_start, running_threads);
374 const size_t next_start = next_tests.fetch_add(batch_size);
375 return std::make_pair(next_start, batch_size);
379 bool continue_testing() {
381 return !options.stop || result.load() == EXIT_SUCCESS;
385 void write_output(std::string output) {
386 Gecode::Support::Lock lock(output_mutex);
399 const int initial_seed;
403 : tec(tec), initial_seed(initialSeed) {}
412 const std::pair<size_t, size_t>& start_and_size = tec.next_batch();
413 const size_t batch_start = start_and_size.first;
414 const size_t batch_size = start_and_size.second;
415 if (batch_start >= tec.tests.size()) {
420 for (
size_t i = batch_start; i < batch_start + batch_size && i < tec.tests.
size(); ++i) {
421 if (!tec.continue_testing()) {
424 auto test = tec.tests[i];
425 unsigned int test_seed = seed_sequence.
next();
426 std::ostringstream test_output;
427 if (!
run_test(test, test_seed, tec.options, test_output)) {
430 tec.write_output(test_output.str());
432 if (!tec.continue_testing()) {
449 for (
unsigned int i = 0; i <
opt.threads; ++i) {
461 using namespace Test;
462#ifdef GECODE_HAS_MTRACE
466 opt.parse(argc, argv);
472 std::cout << t->name() << std::endl;
477 std::vector<Base*> tests;
478 bool started =
opt.start_from ==
nullptr ? true :
false;
481 if (t->name().find(
opt.start_from) != std::string::npos) {
487 if (
opt.is_test_name_matching(t->name())) {
488 tests.emplace_back(t);
492 if (
opt.threads > 1) {
501 for (
int j=i.l; j--; )
int size(void) const
Return size of array (number of elements).
Exception: Base-class for exceptions
virtual const char * what(void) const noexcept
Return information.
An event for synchronization.
void signal(void)
Signal the event.
unsigned int next(void)
Generate next number in series.
void seed(unsigned int s)
Set the current seed to s.
A mutex for mutual exclausion among several threads.
An interface for objects that can be run by a thread.
static void run(Runnable *r)
Construct a new thread and run r.
static unsigned int npu(void)
Return number of processing units (1 if information not available).
Base class for all tests to be run
Gecode::Support::RandomGenerator _rand
Random number generator.
Base(std::string s)
Create and register test with name s.
static Base * tests(void)
Return all tests.
virtual ~Base(void)
Destructor.
virtual bool run(void)=0
Run test.
const std::string & name(void) const
Return name of test.
static void sort(void)
Sort tests alphabetically.
std::vector< std::pair< MatchType, const char * > > testpat
Patterns to test against.
bool is_test_name_matching(const std::string &test_name)
True iff a test name should be executed according to the patterns. With no patterns,...
void parse(int argc, char *argv[])
Parse commandline arguments.
bool log
Whether to log the tests.
unsigned int seed
The random seed to be used.
const char * start_from
Name of first test to start with.
static const unsigned int deffixprob
Default fixpoint probability.
bool list
Whether to list all tests.
unsigned int iter
Number of iterations for each test.
bool stop
Whether to stop on an error.
unsigned int threads
Number of threads to use.
unsigned int fixprob
The probability for computing a fixpoint.
bool operator()(Base *x, Base *y)
friend class TestExecutor
int get_result()
Get the current result (either EXIT_SUCCESS or EXIT_FAILURE). Requires all threads to be done first.
TestExecutionControl(const std::vector< Base * > &tests0, const Options &options0, int thread_count)
void await_test_runners_completed()
TestExecutor(TestExecutionControl &tec, const int initialSeed)
void run(void) override
The function that is executed when the thread starts.
Simple class for describing indentation.
Heap heap
The single global heap.
LinearCongruentialGenerator< 2147483647, 48271, 44488, 3399 > RandomGenerator
Default values for linear congruential generator.
Support algorithms and datastructures
void quicksort(Type *l, Type *r, Less &less)
Standard quick sort.
Gecode toplevel namespace
std::ostringstream olog
Stream used for logging.
int run_tests(const std::vector< Base * > &tests, const Options &options)
Run all the tests with the supplied options.
bool run_test(Base *test, unsigned int test_seed, const Options &options, std::ostream &ostream)
Run a single test, returning true iff the test succeeded.
void report_error(const std::string &name, unsigned int seed, Options &options, std::ostream &ostream)
int run_tests_parallel(const std::vector< Base * > &tests, const Options &options)
Run all the tests with the supplied options i parallel.
int main(int argc, char *argv[])
std::ostream & operator<<(std::ostream &os, const Test::ind &i)