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