monotone

monotone Mtn Source Tree

Root/tester.cc

1#include "base.hh"
2#include "lua.hh"
3#include "paths.hh"
4#include "platform.hh"
5#include "platform-wrapped.hh"
6#include "tester-plaf.hh"
7#include "vector.hh"
8#include "sanity.hh"
9#include "botan/botan.h"
10#include "botan_pipe_cache.hh"
11#include <boost/lexical_cast.hpp>
12#include <cstring>
13
14#ifdef WIN32
15#define WIN32_LEAN_AND_MEAN // no gui definitions
16#include <windows.h>
17#endif
18
19using std::string;
20using std::map;
21using std::vector;
22using boost::lexical_cast;
23using boost::bad_lexical_cast;
24
25// defined in testlib.c, generated from testlib.lua
26extern char const testlib_constant[];
27
28// Lua uses the c i/o functions, so we need to too.
29struct tester_sanity : public sanity
30{
31 void inform_log(std::string const &msg)
32 {fprintf(stdout, "%s", msg.c_str());}
33 void inform_message(std::string const &msg)
34 {fprintf(stdout, "%s", msg.c_str());};
35 void inform_warning(std::string const &msg)
36 {fprintf(stderr, "warning: %s", msg.c_str());};
37 void inform_error(std::string const &msg)
38 {fprintf(stderr, "error: %s", msg.c_str());};
39};
40tester_sanity real_sanity;
41sanity & global_sanity = real_sanity;
42
43// define the global objects needed by botan_pipe_cache.hh
44pipe_cache_cleanup * global_pipe_cleanup_object;
45Botan::Pipe * unfiltered_pipe;
46static unsigned char unfiltered_pipe_cleanup_mem[sizeof(cached_botan_pipe)];
47
48string basename(string const & s)
49{
50 string::size_type sep = s.rfind('/');
51 if (sep == string::npos)
52 return s; // force use of short circuit
53 if (sep == s.size())
54 return "";
55 return s.substr(sep + 1);
56}
57
58string dirname(string const & s)
59{
60 string::size_type sep = s.rfind('/');
61 if (sep == string::npos)
62 return ".";
63 if (sep == s.size() - 1) // dirname() of the root directory is itself
64 return s;
65
66 return s.substr(0, sep);
67}
68
69// Ensure the existence of DIR before proceeding.
70static void ensure_dir(string const & dir)
71{
72 try
73 {
74 do_mkdir(dir);
75 }
76 catch (informative_failure &)
77 {
78 if (get_path_status(dir) != path::directory)
79 throw;
80 }
81}
82
83map<string, string> orig_env_vars;
84
85static string argv0;
86static string firstdir;
87static string source_dir;
88static string run_dir;
89static string testfile;
90
91static int panic_thrower(lua_State * st)
92{
93 throw oops((FL("lua error: %s\n") % luaL_checkstring(st, -1)).str().c_str());
94}
95
96// N.B. some of this code is copied from file_io.cc
97
98namespace
99{
100 struct fill_vec : public dirent_consumer
101 {
102 fill_vec(vector<string> & v) : v(v) { v.clear(); }
103 virtual void consume(char const * s)
104 { v.push_back(s); }
105
106 private:
107 vector<string> & v;
108 };
109
110 struct file_deleter : public dirent_consumer
111 {
112 file_deleter(string const & p) : parent(p) {}
113 virtual void consume(char const * f)
114 {
115 string e(parent + "/" + f);
116 make_accessible(e);
117 do_remove(e);
118 }
119
120 private:
121 string const & parent;
122 };
123
124 struct file_accessible_maker : public dirent_consumer
125 {
126 file_accessible_maker(string const & p) : parent(p) {}
127 virtual void consume(char const * f)
128 { make_accessible(parent + "/" + f); }
129
130 private:
131 string const & parent;
132 };
133
134 struct file_copier : public dirent_consumer
135 {
136 file_copier(string const & f, string const & t) : from(f), to(t) {}
137 virtual void consume(char const * f)
138 {
139 do_copy_file(from + "/" + f, to + "/" + f);
140 }
141
142 private:
143 string const & from;
144 string const & to;
145 };
146}
147
148void do_remove_recursive(string const & p)
149{
150 switch (get_path_status(p))
151 {
152 case path::directory:
153 {
154 make_accessible(p);
155 vector<string> subdirs;
156 struct fill_vec get_subdirs(subdirs);
157 struct file_deleter del_files(p);
158
159 do_read_directory(p, del_files, get_subdirs, del_files);
160 for(vector<string>::const_iterator i = subdirs.begin();
161 i != subdirs.end(); i++)
162 do_remove_recursive(p + "/" + *i);
163 do_remove(p);
164 }
165 return;
166
167 case path::file:
168 make_accessible(p);
169 do_remove(p);
170 return;
171
172 case path::nonexistent:
173 return;
174 }
175}
176
177void do_make_tree_accessible(string const & p)
178{
179 switch (get_path_status(p))
180 {
181 case path::directory:
182 {
183 make_accessible(p);
184 vector<string> subdirs;
185 struct fill_vec get_subdirs(subdirs);
186 struct file_accessible_maker access_files(p);
187
188 do_read_directory(p, access_files, get_subdirs, access_files);
189 for(vector<string>::const_iterator i = subdirs.begin();
190 i != subdirs.end(); i++)
191 do_make_tree_accessible(p + "/" + *i);
192 }
193 return;
194
195 case path::file:
196 make_accessible(p);
197 return;
198
199 case path::nonexistent:
200 return;
201 }
202}
203
204void do_copy_recursive(string const & from, string to)
205{
206 path::status fromstat = get_path_status(from);
207
208 E(fromstat != path::nonexistent,
209 F("Source '%s' for copy does not exist") % from);
210
211 switch (get_path_status(to))
212 {
213 case path::nonexistent:
214 if (fromstat == path::directory)
215 do_mkdir(to);
216 break;
217
218 case path::file:
219 do_remove(to);
220 if (fromstat == path::directory)
221 do_mkdir(to);
222 break;
223
224 case path::directory:
225 to = to + "/" + basename(from);
226 break;
227 }
228
229 if (fromstat == path::directory)
230 {
231 vector<string> subdirs, specials;
232 struct fill_vec get_subdirs(subdirs), get_specials(specials);
233 struct file_copier copy_files(from, to);
234
235 do_read_directory(from, copy_files, get_subdirs, get_specials);
236 E(specials.empty(), F("cannot copy special files in '%s'") % from);
237 for (vector<string>::const_iterator i = subdirs.begin();
238 i != subdirs.end(); i++)
239 do_copy_recursive(from + "/" + *i, to + "/" + *i);
240 }
241 else
242 do_copy_file(from, to);
243}
244
245// For convenience in calling from Lua (which has no syntax for writing
246// octal numbers) this function takes a three-digit *decimal* number and
247// treats each digit as octal. For example, 777 (decimal) is converted to
248// 0777 (octal) for the system call. Note that the system always forces the
249// high three bits of the supplied mode to zero; i.e. it is impossible to
250// have the setuid, setgid, or sticky bits on in the process umask.
251// Therefore, there is no point accepting arguments higher than 777.
252LUAEXT(posix_umask, )
253{
254 unsigned int decmask = (unsigned int)luaL_checknumber(L, -1);
255 E(decmask <= 777,
256 F("invalid argument %d to umask") % decmask);
257
258 unsigned int a = decmask / 100 % 10;
259 unsigned int b = decmask / 10 % 10;
260 unsigned int c = decmask / 1 % 10;
261
262 E(a <= 7 && b <= 7 && c <= 7,
263 F("invalid octal number %d in umask") % decmask);
264
265 int oldmask = do_umask((a*8 + b)*8 + c);
266 if (oldmask == -1)
267 {
268 lua_pushinteger(L, 0);
269 return 1;
270 }
271 else
272 {
273 a = ((unsigned int)oldmask) / 64 % 8;
274 b = ((unsigned int)oldmask) / 8 % 8;
275 c = ((unsigned int)oldmask) / 1 % 8;
276
277 lua_pushinteger(L, (a*10 + b)*10 + c);
278 return 1;
279 }
280}
281
282LUAEXT(chdir, )
283{
284 try
285 {
286 string from = get_current_working_dir();
287 change_current_working_dir(luaL_checkstring(L, -1));
288 lua_pushstring(L, from.c_str());
289 return 1;
290 }
291 catch(informative_failure & e)
292 {
293 lua_pushnil(L);
294 return 1;
295 }
296}
297
298LUAEXT(remove_recursive, )
299{
300 try
301 {
302 do_remove_recursive(luaL_checkstring(L, -1));
303 lua_pushboolean(L, true);
304 return 1;
305 }
306 catch(informative_failure & e)
307 {
308 lua_pushboolean(L, false);
309 lua_pushstring(L, e.what());
310 return 2;
311 }
312}
313
314LUAEXT(make_tree_accessible, )
315{
316 try
317 {
318 do_make_tree_accessible(luaL_checkstring(L, -1));
319 lua_pushboolean(L, true);
320 return 1;
321 }
322 catch(informative_failure & e)
323 {
324 lua_pushboolean(L, false);
325 lua_pushstring(L, e.what());
326 return 2;
327 }
328}
329
330LUAEXT(copy_recursive, )
331{
332 try
333 {
334 string from(luaL_checkstring(L, -2));
335 string to(luaL_checkstring(L, -1));
336 do_copy_recursive(from, to);
337 lua_pushboolean(L, true);
338 return 1;
339 }
340 catch(informative_failure & e)
341 {
342 lua_pushboolean(L, false);
343 lua_pushstring(L, e.what());
344 return 2;
345 }
346}
347
348LUAEXT(mkdir, )
349{
350 try
351 {
352 char const * dirname = luaL_checkstring(L, -1);
353 do_mkdir(dirname);
354 lua_pushboolean(L, true);
355 return 1;
356 }
357 catch(informative_failure & e)
358 {
359 lua_pushnil(L);
360 return 1;
361 }
362}
363
364LUAEXT(make_temp_dir, )
365{
366 try
367 {
368 char * tmpdir = make_temp_dir();
369
370 lua_pushstring(L, tmpdir);
371 delete [] tmpdir;
372 return 1;
373 }
374 catch(informative_failure & e)
375 {
376 lua_pushnil(L);
377 return 1;
378 }
379}
380
381
382LUAEXT(mtime, )
383{
384 try
385 {
386 char const * file = luaL_checkstring(L, -1);
387
388 time_t t = get_last_write_time(file);
389 if (t == time_t(-1))
390 lua_pushnil(L);
391 else
392 lua_pushnumber(L, t);
393 return 1;
394 }
395 catch(informative_failure & e)
396 {
397 lua_pushnil(L);
398 return 1;
399 }
400}
401
402LUAEXT(exists, )
403{
404 try
405 {
406 char const * name = luaL_checkstring(L, -1);
407 switch (get_path_status(name))
408 {
409 case path::nonexistent: lua_pushboolean(L, false); break;
410 case path::file:
411 case path::directory: lua_pushboolean(L, true); break;
412 }
413 }
414 catch(informative_failure & e)
415 {
416 lua_pushnil(L);
417 }
418 return 1;
419}
420
421LUAEXT(isdir, )
422{
423 try
424 {
425 char const * name = luaL_checkstring(L, -1);
426 switch (get_path_status(name))
427 {
428 case path::nonexistent:
429 case path::file: lua_pushboolean(L, false); break;
430 case path::directory: lua_pushboolean(L, true); break;
431 }
432 }
433 catch(informative_failure & e)
434 {
435 lua_pushnil(L);
436 }
437 return 1;
438}
439
440namespace
441{
442 struct build_table : public dirent_consumer
443 {
444 build_table(lua_State * st) : st(st), n(1)
445 {
446 lua_newtable(st);
447 }
448 virtual void consume(const char *s)
449 {
450 lua_pushstring(st, s);
451 lua_rawseti(st, -2, n);
452 n++;
453 }
454 private:
455 lua_State * st;
456 unsigned int n;
457 };
458}
459
460LUAEXT(read_directory, )
461{
462 int top = lua_gettop(L);
463 try
464 {
465 string path(luaL_checkstring(L, -1));
466 build_table tbl(L);
467
468 do_read_directory(path, tbl, tbl, tbl);
469 }
470 catch(informative_failure &)
471 {
472 // discard the table and any pending path element
473 lua_settop(L, top);
474 lua_pushnil(L);
475 }
476 catch (...)
477 {
478 lua_settop(L, top);
479 throw;
480 }
481 return 1;
482}
483
484LUAEXT(get_source_dir, )
485{
486 lua_pushstring(L, source_dir.c_str());
487 return 1;
488}
489
490LUAEXT(save_env, )
491{
492 orig_env_vars.clear();
493 return 0;
494}
495
496LUAEXT(restore_env, )
497{
498 for (map<string,string>::const_iterator i = orig_env_vars.begin();
499 i != orig_env_vars.end(); ++i)
500 set_env(i->first.c_str(), i->second.c_str());
501 orig_env_vars.clear();
502 return 0;
503}
504
505LUAEXT(set_env, )
506{
507 char const * var = luaL_checkstring(L, -2);
508 char const * val = luaL_checkstring(L, -1);
509 if (orig_env_vars.find(string(var)) == orig_env_vars.end()) {
510 char const * old = getenv(var);
511 if (old)
512 orig_env_vars.insert(make_pair(string(var), string(old)));
513 else
514 orig_env_vars.insert(make_pair(string(var), ""));
515 }
516 set_env(var, val);
517 return 0;
518}
519
520LUAEXT(unset_env, )
521{
522 char const * var = luaL_checkstring(L, -1);
523 if (orig_env_vars.find(string(var)) == orig_env_vars.end()) {
524 char const * old = getenv(var);
525 if (old)
526 orig_env_vars.insert(make_pair(string(var), string(old)));
527 else
528 orig_env_vars.insert(make_pair(string(var), ""));
529 }
530 unset_env(var);
531 return 0;
532}
533
534LUAEXT(timed_wait, )
535{
536 pid_t pid = static_cast<pid_t>(luaL_checknumber(L, -2));
537 int time = static_cast<int>(luaL_checknumber(L, -1));
538 int res;
539 int ret;
540 ret = process_wait(pid, &res, time);
541 lua_pushnumber(L, res);
542 lua_pushnumber(L, ret);
543 return 2;
544}
545
546LUAEXT(require_not_root, )
547{
548 // E() doesn't work here, I just get "warning: " in the
549 // output. Why?
550 if (running_as_root())
551 {
552 P(F("This test suite cannot be run as the root user.\n"
553 "Please try again with a normal user account.\n"));
554 exit(1);
555 }
556 return 0;
557}
558
559// run_tests_in_children (to_run, reporter)
560//
561// Run all of the tests in TO_RUN, each in its own isolated directory and
562// child process. As each exits, call REPORTER with the test number and
563// name, and the exit status. If REPORTER returns true, delete the test
564// directory, otherwise leave it alone.
565//
566// The meat of the work done by this function is so system-specific that it
567// gets shoved off into tester-plaf.cc. However, all interaction with the
568// Lua layer needs to remain in this file, so we have a mess of callback
569// "closures" (or as close as C++ lets you get, anyway).
570
571// Iterate over the Lua table containing all the tests to run.
572bool test_enumerator::operator()(test_to_run & next_test) const
573{
574 int top = lua_gettop(st);
575 luaL_checkstack(st, 2, "preparing to retrieve next test");
576
577 lua_rawgeti(st, LUA_REGISTRYINDEX, table_ref);
578 if (iteration_begun)
579 lua_pushinteger(st, last_index);
580 else
581 lua_pushnil(st);
582
583 if (lua_next(st, -2) == 0)
584 {
585 lua_settop(st, top);
586 return false;
587 }
588 else
589 {
590 iteration_begun = true;
591 next_test.number = last_index = luaL_checkinteger(st, -2);
592 next_test.name = luaL_checkstring(st, -1);
593 lua_settop(st, top);
594 return true;
595 }
596}
597
598// Invoke one test case in the child. This may be called by
599// run_tests_in_children, or by main, because Windows doesn't have fork.
600// It is not allowed to write to standard output or standard error under
601// any circumstances whatsoever. Not calling lua_close is deliberate.
602
603int test_invoker::operator()(std::string const & testname) const
604{
605 int retcode;
606 try
607 {
608 luaL_checkstack(st, 2, "preparing call to run_one_test");
609 lua_getglobal(st, "run_one_test");
610 I(lua_isfunction(st, -1));
611
612 lua_pushstring(st, testname.c_str());
613 lua_call(st, 1, 1);
614
615 retcode = luaL_checkinteger(st, -1);
616 lua_remove(st, -1);
617 }
618 catch (std::exception & e)
619 {
620 E(false, F("test %s: %s") % testname % e.what());
621 retcode = 124;
622 }
623 return retcode;
624}
625
626
627// Clean up after one child process.
628
629bool test_cleaner::operator()(test_to_run const & test,
630 int status) const
631{
632 // call reporter(testno, testname, status)
633 luaL_checkstack(st, 4, "preparing call to reporter");
634
635 lua_rawgeti(st, LUA_REGISTRYINDEX, reporter_ref);
636 lua_pushinteger(st, test.number);
637 lua_pushstring(st, test.name.c_str());
638 lua_pushinteger(st, status);
639 lua_call(st, 3, 1);
640
641 // return is a boolean. There is, for no apparent reason, no
642 // luaL_checkboolean().
643 I(lua_isboolean(st, -1));
644 bool ret = lua_toboolean(st, -1);
645 lua_remove(st, -1);
646 return ret;
647}
648
649LUAEXT(run_tests_in_children, )
650{
651 if (lua_gettop(L) != 2)
652 return luaL_error(L, "wrong number of arguments");
653
654 luaL_argcheck(L, lua_istable(L, 1), 1, "expected a table");
655 luaL_argcheck(L, lua_isfunction(L, 2), 2, "expected a function");
656
657 int reporter_ref = luaL_ref(L, LUA_REGISTRYINDEX);
658 int table_ref = luaL_ref(L, LUA_REGISTRYINDEX);
659
660 run_tests_in_children(test_enumerator(L, table_ref),
661 test_invoker(L),
662 test_cleaner(L, reporter_ref),
663 run_dir, argv0, testfile, firstdir);
664
665 luaL_unref(L, LUA_REGISTRYINDEX, table_ref);
666 luaL_unref(L, LUA_REGISTRYINDEX, reporter_ref);
667 return 0;
668}
669
670// Write all arguments to standard output. This is not a normal LUAEXT
671// because it is only made available to run_tests as an argument, not
672// established as globally visible. (Only a very limited number of places
673// at the Lua level are allowed to talk to standard output.)
674int run_tests_progress(lua_State *st)
675{
676 int n = lua_gettop(st);
677 for (int i = 1; i <= n; i++)
678 fputs(luaL_checkstring(st, i), stdout);
679 return 0;
680}
681
682// RAII wrapper around a Lua state structure; also takes care of doing the
683// initialization as we want it. Of note is that we do not want any
684// Lua-level code getting its grubby fingers on stdin/out/err, so we have to
685// take just about everything out of the io table, and we do not trust
686// testlib.lua to do this for us.
687
688namespace {
689 struct lua_lib
690 {
691 lua_lib(string const & initial_dir, string const & suite);
692 ~lua_lib() { lua_close(st); }
693 lua_State * operator()() { return st; }
694 private:
695 lua_State * st;
696 };
697 lua_lib::lua_lib(string const & initial_dir, string const & suite)
698 : st(luaL_newstate())
699 {
700 static char const * const allowed_io_funcs[] = {
701 "open", "lines", "type", "tmpfile"
702 };
703
704 lua_atpanic (st, &panic_thrower);
705 luaL_openlibs(st);
706 add_functions(st);
707
708 lua_getglobal(st, "io");
709 lua_newtable(st);
710
711 for (unsigned int i = 0;
712 i < sizeof allowed_io_funcs / sizeof allowed_io_funcs[0]; i++)
713 {
714 // this looks like it's a no-op, but the trick is that stack element
715 // -2 is the original "io" table in the getfield operation, but the
716 // new table we are constructing in the setfield operation (because
717 // getfield leaves its value at top of stack, and setfield pops it).
718 lua_getfield(st, -2, allowed_io_funcs[i]);
719 lua_setfield(st, -2, allowed_io_funcs[i]);
720 }
721
722 lua_remove(st, -2); // oldtable newtable -- newtable
723
724 // establish our new table as the value of
725 // package.loaded["io"].
726 lua_getglobal(st, "package"); // -- newtable package
727 lua_getfield(st, -1, "loaded"); // -- newtable package loaded
728 lua_remove(st, -2); // -- newtable loaded
729 lua_pushvalue(st, -2); // -- newtable loaded newtable
730 lua_setfield(st, -2, "io"); // -- newtable loaded
731 lua_remove(st, -1); // -- newtable
732
733 // also establish it as the value of the global "io" variable.
734 lua_setglobal(st, "io"); // --
735
736 // we can now load testlib.lua.
737 run_string(st, testlib_constant, "testlib.lua");
738
739 // the suite definition may know the initial working directory.
740 lua_pushstring(st, initial_dir.c_str());
741 lua_setglobal(st, "initial_dir");
742
743 run_file(st, suite.c_str());
744 }
745}
746
747// This function is cloned from simplestring_xform.cc, which we cannot use
748// here. It does not cover several possibilities handled by the real
749// version but of no interest here.
750
751static vector<string> split_into_words(string const & in)
752{
753 vector<string> out;
754
755 string::size_type begin = 0;
756 string::size_type end = in.find_first_of(" ", begin);
757
758 while (end != string::npos && end >= begin)
759 {
760 out.push_back(in.substr(begin, end-begin));
761 begin = end + 1;
762 if (begin >= in.size())
763 break;
764 end = in.find_first_of(" ", begin);
765 }
766 if (begin < in.size())
767 out.push_back(in.substr(begin, in.size() - begin));
768
769 return out;
770}
771
772
773// Parse a boolean command line option: if ARG is either SHORTOPT or
774// LONGOPT, return true, else false.
775static bool
776bool_option(char const * arg, char const * shortopt, char const * longopt)
777{
778 return ((shortopt && !strcmp(arg, shortopt))
779 || (longopt && !strcmp(arg, longopt)));
780}
781
782// Parse an integer-valued command line option: if ARG is either SHORTOPT
783// or LONGOPT and a decimal integer follows, write that integer to VAL and
784// return true, else leave VAL untouched and return false.
785static bool
786int_option(char const * arg, char const * shortopt, char const * longopt,
787 int & val)
788{
789 if (shortopt && !strncmp(arg, shortopt, strlen(shortopt)))
790 {
791 char *end;
792 int v = strtoul(arg + strlen(shortopt), &end, 10);
793 if (end != arg + strlen(shortopt) && *end == '\0')
794 {
795 val = v;
796 return true;
797 }
798 }
799
800 if (longopt && !strncmp(arg, longopt, strlen(longopt)))
801 {
802 char *end;
803 int v = strtoul(arg + strlen(longopt), &end, 10);
804 if (end != arg + strlen(longopt) && *end == '\0')
805 {
806 val = v;
807 return true;
808 }
809 }
810
811 return false;
812}
813
814// Parse a two-integer-valued command line option: if ARG begins with OPT
815// and continues with a pair of decimal integers separated by a comma, write
816// the integers to VAL1 and VAL2 and return true; else leave VAL1 and VAL2
817// untouched and return false.
818static bool
819int_int_option(char const * arg, char const * opt, int & val1, int & val2)
820{
821 if (strncmp(arg, opt, strlen(opt)))
822 return false;
823
824 char *end1, *end2, *p;
825 int v1, v2;
826
827 p = const_cast<char *>(arg + strlen(opt));
828
829 v1 = strtoul(p, &end1, 10);
830
831 if (end1 == p || *end1 != ',')
832 return false;
833
834 v2 = strtoul(end1 + 1, &end2, 10);
835
836 if (end1 == end2 || *end2 != '\0')
837 return false;
838
839 val1 = v1;
840 val2 = v2;
841 return true;
842}
843
844// Extract parallelization-related options from MFLAGS. We can rely on
845// Make to pass these arguments in a particular form:
846// -j [N] no more than N parallel jobs (absent = no limit)
847// -l [N] no more jobs if the system load average rises above N
848// (absent = no limit) (not supported except with no N)
849// --jobserver-fds=M,N talk to a job server on fds M and N to limit
850// concurrency
851// Anything else in MFLAGS is ignored.
852// The first word in MFLAGS should have a dash prepended to it unless it
853// already has one.
854
855static void
856parse_makeflags(char const * mflags,
857 int & jobs,
858 int & jread,
859 int & jwrite)
860{
861 if (mflags == 0)
862 return;
863
864 while (*mflags == ' ') mflags++;
865
866 vector<string> mf(split_into_words(mflags));
867
868 if (mf.size() == 0 || (mf.size() == 1 && mf[0] == ""))
869 return;
870
871 if (mf[0][0] != '-')
872 mf[0] = string("-") + mf[0];
873
874 int jxx = 0;
875 for (vector<string>::const_iterator i = mf.begin(); i != mf.end(); i++)
876 {
877 if (*i == "-j")
878 {
879 jxx = -1;
880 i++;
881 if (i == mf.end())
882 break;
883 try
884 {
885 jxx = lexical_cast<int>(*i);
886 if (jxx <= 0)
887 {
888 W(F("-j %d makes no sense, option ignored") % jxx);
889 jxx = 0;
890 }
891 }
892 catch (bad_lexical_cast &)
893 {
894 i--;
895 }
896 }
897 else if (*i == "-l")
898 {
899 i++;
900 if (i == mf.end())
901 break;
902 try
903 {
904 double dummy = lexical_cast<double>(*i);
905 W(F("no support for -l %f: forcing -j1") % dummy);
906 jxx = 1;
907 }
908 catch (bad_lexical_cast &)
909 {
910 i--;
911 }
912 }
913 else if (int_int_option(i->c_str(), "--jobserver-fds=", jread, jwrite))
914 0;
915 }
916
917 // do not permit -j in MAKEFLAGS to override -j on the command line.
918 if (jxx != 0 && jobs == 0)
919 jobs = jxx;
920}
921
922static void
923parse_command_line(int argc, char const * const * argv,
924 bool & want_help, bool & need_help,
925 bool & debugging, bool & list_only,
926 bool & run_one, int & jobs,
927 vector<string> & tests_to_run)
928{
929 int i;
930 int jxx = 0;
931
932 for (i = 1; i < argc; i++)
933 {
934 if (string(argv[i]) == "--")
935 break;
936
937 if (bool_option(argv[i], "-h", "--help"))
938 want_help = true;
939 else if (bool_option(argv[i], "-d", "--debug"))
940 debugging = true;
941 else if (bool_option(argv[i], "-l", "--list-only"))
942 list_only = true;
943 else if (bool_option(argv[i], "-r", 0))
944 run_one = true;
945 else if (bool_option(argv[i], "-j", "--jobs"))
946 {
947 // if there turns out not to be a number, this is -j infinity.
948 jxx = -1;
949
950 if (i+1 < argc)
951 try
952 {
953 jxx = lexical_cast<int>(argv[i]);
954 if (jxx <= 0)
955 {
956 W(F("-j %d makes no sense, option ignored") % jxx);
957 jxx = 0;
958 }
959 i++;
960 }
961 catch (bad_lexical_cast &)
962 {
963 // it wasn't a number.
964 }
965 }
966 else if (int_option(argv[i], "-j", "--jobs=", jobs))
967 /* no action required */;
968 else if (argv[i][0] == '-')
969 {
970 P(F("unrecognized option '%s'") % argv[i]);
971 need_help = true;
972 }
973 else
974 tests_to_run.push_back(argv[i]);
975 }
976
977 // all argv elements from i+1 to argc go into tests_to_run without further
978 // interpretation.
979 if (i < argc)
980 for (i++; i < argc; i++)
981 tests_to_run.push_back(argv[i]);
982
983 if (jxx != 0)
984 jobs = jxx;
985
986 E(!run_one || (!want_help && !debugging && !list_only
987 && tests_to_run.size() == 3 && jobs == 0),
988 F("incorrect self-invocation; -r <abs path to lua-testsuite.lua> <abs path to tester_dir> <test>"));
989
990 if (tests_to_run.size() == 0)
991 {
992 P(F("%s: no test suite specified\n") % argv[0]);
993 need_help = true;
994 }
995}
996
997int main(int argc, char **argv)
998{
999 int retcode = 2;
1000
1001 vector<string> tests_to_run;
1002 bool want_help = false;
1003 bool need_help = false;
1004 bool debugging = false;
1005 bool list_only = false;
1006 bool run_one = false;
1007 int jobs = 0;
1008 int jread = -1;
1009 int jwrite = -1;
1010
1011 try
1012 {
1013 global_sanity.initialize(argc, argv, "C");
1014 // Set up secure memory allocation etc
1015 Botan::LibraryInitializer acquire_botan("thread_safe=0 selftest=0 "
1016 "seed_rng=1 use_engines=0 "
1017 "secure_memory=1 fips140=0");
1018
1019 // and caching for botan pipes
1020 pipe_cache_cleanup acquire_botan_pipe_caching;
1021 unfiltered_pipe = new Botan::Pipe;
1022 new (unfiltered_pipe_cleanup_mem) cached_botan_pipe(unfiltered_pipe);
1023
1024 parse_command_line(argc, argv,
1025 want_help, need_help, debugging, list_only,
1026 run_one, jobs, tests_to_run);
1027
1028 parse_makeflags(getenv("MAKEFLAGS"), jobs, jread, jwrite);
1029
1030 if (want_help || need_help)
1031 {
1032 P(F("Usage: %s test-file testsuite [options] [tests]\n") % argv[0]);
1033 P(F("Testsuite: a Lua script defining the test suite to run.\n"
1034 "Options:\n"
1035 " -l, --list just list tests that would be run\n"
1036 " -d, --debug don't erase working dirs of successful tests\n"
1037 " -j N, --jobs=N run N test cases in parallel\n"
1038 " (note: unlike make, the N is not optional)\n"
1039 " -h, --help display this help message\n"
1040 // -r is deliberately not mentioned.
1041 "Tests may be specified as:\n"
1042 " nothing - run all tests.\n"
1043 " numbers - run the tests with those numbers\n"
1044 " negative numbers count back from the end\n"
1045 " ranges may be specified as A..B (inclusive)\n"
1046 " regexes - run the tests whose names match (unanchored)\n"));
1047
1048 return want_help ? 0 : 2;
1049 }
1050
1051 if (jobs == 0) // no setting from command line or MAKEFLAGS
1052 jobs = 1;
1053
1054 if (run_one)
1055 {
1056#ifdef WIN32
1057 // This is a self-invocation, requesting that we actually run a
1058 // single named test. Contra the help above, the command line
1059 // arguments are the absolute pathname of the testsuite definition,
1060 // the original working directory, and the name of the test, in
1061 // that order. No other options are valid in combination with -r.
1062 // We have been invoked inside the directory where we should run
1063 // the test. Stdout and stderr have been redirected to a per-test
1064 // logfile.
1065 source_dir = dirname(tests_to_run[0]);
1066 lua_lib st(tests_to_run[1], tests_to_run[0]);
1067 return test_invoker(st())(tests_to_run[2]);
1068#else
1069 E(false, F("self-invocation should not be used on Unix\n"));
1070#endif
1071 }
1072 else
1073 {
1074 firstdir = get_current_working_dir();
1075 run_dir = firstdir + "/tester_dir";
1076 testfile = tests_to_run.front();
1077
1078#if defined(WIN32)
1079 char name[MAX_PATH];
1080 int len = 0;
1081 len = (int)GetModuleFileName(0, name, MAX_PATH);
1082 if(len != 0) {
1083 argv0 = system_path(name).as_external();
1084 }
1085#else
1086 if (argv[0][0] == '/')
1087 argv0 = argv[0];
1088 else
1089 argv0 = firstdir + "/" + argv[0];
1090#endif
1091
1092 change_current_working_dir(dirname(testfile));
1093 source_dir = get_current_working_dir();
1094 testfile = source_dir + "/" + basename(testfile);
1095
1096 ensure_dir(run_dir);
1097 change_current_working_dir(run_dir);
1098
1099 lua_lib st(firstdir, testfile);
1100
1101 // arrange for isolation between different test suites running in
1102 // the same build directory.
1103 lua_getglobal(st(), "testdir");
1104 const char *testdir = lua_tostring(st(), 1);
1105 I(testdir);
1106 string testdir_base = basename(testdir);
1107 run_dir = run_dir + "/" + testdir_base;
1108 string logfile = run_dir + ".log";
1109
1110 ensure_dir(run_dir);
1111
1112 prepare_for_parallel_testcases(jobs, jread, jwrite);
1113
1114 Lua ll(st());
1115 ll.func("run_tests");
1116 ll.push_bool(debugging);
1117 ll.push_bool(list_only);
1118 ll.push_str(run_dir);
1119 ll.push_str(logfile);
1120 ll.push_table();
1121 // i = 1 skips the first element of tests_to_run, which is the
1122 // testsuite definition.
1123 for (unsigned int i = 1; i < tests_to_run.size(); i++)
1124 {
1125 ll.push_int(i);
1126 ll.push_str(tests_to_run.at(i).c_str());
1127 ll.set_table();
1128 }
1129
1130 // the Lua object doesn't wrap this
1131 if (ll.ok())
1132 lua_pushcfunction(st(), run_tests_progress);
1133
1134 ll.call(6,1)
1135 .extract_int(retcode);
1136 }
1137 }
1138 catch (informative_failure & e)
1139 {
1140 P(F("%s\n") % e.what());
1141 retcode = 1;
1142 }
1143 catch (std::logic_error & e)
1144 {
1145 P(F("Invariant failure: %s\n") % e.what());
1146 retcode = 3;
1147 }
1148 catch (std::exception & e)
1149 {
1150 P(F("Uncaught exception: %s") % e.what());
1151 retcode = 3;
1152 }
1153 catch (...)
1154 {
1155 P(F("Uncaught exception of unknown type"));
1156 retcode = 3;
1157 }
1158
1159 return retcode;
1160}
1161
1162// Local Variables:
1163// mode: C++
1164// fill-column: 76
1165// c-file-style: "gnu"
1166// indent-tabs-mode: nil
1167// End:
1168// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status