monotone

monotone Mtn Source Tree

Root/tester.cc

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