monotone

monotone Mtn Source Tree

Root/tester.cc

1
2extern "C" {
3#include <lua.h>
4#include <lualib.h>
5#include <lauxlib.h>
6}
7
8#include "lua.hh"
9#include "tester.h"
10#include "paths.hh"
11#include "platform.hh"
12#include "sanity.hh"
13
14#include <cstdio>
15
16#include <exception>
17
18#include <boost/filesystem/path.hpp>
19#include <boost/filesystem/operations.hpp>
20#include <boost/filesystem/convenience.hpp>
21#include <boost/filesystem/exception.hpp>
22#include <boost/lexical_cast.hpp>
23
24#include <map>
25#include <utility>
26
27using std::string;
28using std::map;
29using std::make_pair;
30using boost::lexical_cast;
31
32namespace redirect
33{
34 enum what {in, out, err};
35}
36
37#ifdef WIN32
38#include <windows.h>
39namespace redirect {typedef HANDLE savetype;}
40HANDLE set_redirect(redirect::what what, string where)
41{
42 HANDLE file;
43 SECURITY_ATTRIBUTES sa;
44 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
45 sa.lpSecurityDescriptor = 0;
46 sa.bInheritHandle = true;
47 if (what == redirect::in)
48 {
49 file = CreateFile(where.c_str(),
50 GENERIC_READ,
51 FILE_SHARE_READ,
52 &sa,
53 OPEN_EXISTING,
54 FILE_ATTRIBUTE_NORMAL,
55 NULL);
56 }
57 else
58 {
59 file = CreateFile(where.c_str(),
60 GENERIC_WRITE,
61 FILE_SHARE_READ,
62 &sa,
63 CREATE_ALWAYS,
64 FILE_ATTRIBUTE_NORMAL,
65 NULL);
66 }
67 HANDLE old;
68 switch(what)
69 {
70 case redirect::in:
71 old = GetStdHandle(STD_INPUT_HANDLE);
72 SetStdHandle(STD_INPUT_HANDLE, file);
73 break;
74 case redirect::out:
75 old = GetStdHandle(STD_OUTPUT_HANDLE);
76 SetStdHandle(STD_OUTPUT_HANDLE, file);
77 break;
78 case redirect::err:
79 old = GetStdHandle(STD_ERROR_HANDLE);
80 SetStdHandle(STD_ERROR_HANDLE, file);
81 break;
82 }
83 return old;
84}
85void clear_redirect(redirect::what what, HANDLE saved)
86{
87 switch(what)
88 {
89 case redirect::in:
90 CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
91 SetStdHandle(STD_INPUT_HANDLE, saved);
92 break;
93 case redirect::out:
94 CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
95 SetStdHandle(STD_OUTPUT_HANDLE, saved);
96 break;
97 case redirect::err:
98 CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
99 SetStdHandle(STD_ERROR_HANDLE, saved);
100 break;
101 }
102}
103bool make_accessible(string const &name)
104{
105 DWORD attrs = GetFileAttributes(name.c_str());
106 if (attrs == INVALID_FILE_ATTRIBUTES)
107 return false;
108 bool ok = SetFileAttributes(name.c_str(), attrs & ~FILE_ATTRIBUTE_READONLY);
109 return ok;
110}
111#else
112#include <unistd.h>
113#include <sys/stat.h>
114namespace redirect {typedef int savetype;}
115int set_redirect(redirect::what what, string where)
116{
117 int from;
118 char const *mode;
119 switch(what)
120 {
121 case redirect::in:
122 from = 0;
123 mode = "r";
124 break;
125 case redirect::out:
126 from = 1;
127 mode = "w";
128 break;
129 case redirect::err:
130 from = 2;
131 mode = "w";
132 break;
133 default:
134 from = -1;
135 mode = "";
136 break;
137 };
138 int saved = dup(from);
139 FILE *f = fopen(where.c_str(), mode);
140 if (!f)
141 return -1;
142 dup2(fileno(f), from);
143 fclose(f);
144 return saved;
145}
146void clear_redirect(redirect::what what, int saved)
147{
148 int from;
149 switch(what)
150 {
151 case redirect::in:
152 from = 0;
153 break;
154 case redirect::out:
155 from = 1;
156 break;
157 case redirect::err:
158 from = 2;
159 break;
160 default:
161 from = -1;
162 break;
163 };
164 dup2(saved, from);
165 close(saved);
166}
167bool make_accessible(string const &name)
168{
169 struct stat st;
170 bool ok = (stat(name.c_str(), &st) == 0);
171 if (!ok)
172 return false;
173 return (chmod(name.c_str(), st.st_mode | S_IREAD | S_IWRITE | S_IEXEC) == 0);
174}
175#endif
176namespace redirect
177{
178 struct saveblock
179 {
180 savetype in;
181 savetype out;
182 savetype err;
183 };
184}
185
186#include <cstdlib>
187map<string, string> orig_env_vars;
188void save_env() { orig_env_vars.clear(); }
189#if defined(WIN32) && !defined(__MINGW32__)
190void restore_env()
191{
192 for (map<string,string>::const_iterator i = orig_env_vars.begin();
193 i != orig_env_vars.end(); ++i)
194 {
195 _putenv_s(i->first.c_str(), i->second.c_str());
196 }
197 orig_env_vars.clear();
198}
199void set_env(string const &var, string const &val)
200{
201 char const *old = getenv(var.c_str());
202 if (old)
203 orig_env_vars.insert(make_pair(var, string(old)));
204 else
205 orig_env_vars.insert(make_pair(var, ""));
206 _putenv_s(var.c_str(), val.c_str());
207}
208#else
209void putenv2(string const &var, string const &val)
210{
211 string tempstr = var + "=" + val;
212 char const *s = tempstr.c_str();
213 size_t len = var.size() + val.size() + 2;
214 char *cp = new char[len];
215 memcpy(cp, s, len);
216 putenv(cp);
217}
218void restore_env()
219{
220 for (map<string,string>::const_iterator i = orig_env_vars.begin();
221 i != orig_env_vars.end(); ++i)
222 {
223 putenv2(i->first, i->second);
224 }
225 orig_env_vars.clear();
226}
227void set_env(string const &var, string const &val)
228{
229 char const *old = getenv(var.c_str());
230 if (old)
231 orig_env_vars.insert(make_pair(var, string(old)));
232 else
233 orig_env_vars.insert(make_pair(var, ""));
234 putenv2(var, val);
235}
236#endif
237
238
239fs::path source_dir;
240fs::path run_dir;
241
242static int panic_thrower(lua_State * st)
243{
244 throw oops("lua error");
245}
246
247void do_remove_recursive(fs::path const &rem)
248{
249 if (!fs::exists(rem))
250 return;
251 make_accessible(rem.native_file_string());
252 if (fs::is_directory(rem))
253 {
254 for (fs::directory_iterator i(rem); i != fs::directory_iterator(); ++i)
255 do_remove_recursive(*i);
256 }
257 fs::remove(rem);
258}
259
260void do_make_tree_accessible(fs::path const &f)
261{
262 if (!fs::exists(f))
263 return;
264 make_accessible(f.native_file_string());
265 if (fs::is_directory(f))
266 {
267 for (fs::directory_iterator i(f); i != fs::directory_iterator(); ++i)
268 do_make_tree_accessible(*i);
269 }
270}
271
272void do_copy_recursive(fs::path const &from, fs::path to)
273{
274 if (!fs::exists(from))
275 throw fs::filesystem_error("Source for copy does not exist", from, 0);
276 if (fs::exists(to))
277 {
278 if (fs::is_directory(to))
279 to = to / from.leaf();
280 else
281 do_remove_recursive(to);
282 }
283 if (fs::is_directory(from))
284 {
285 fs::create_directory(to);
286 for (fs::directory_iterator i(from); i != fs::directory_iterator(); ++i)
287 do_copy_recursive(*i, to / i->leaf());
288 }
289 else
290 fs::copy_file(from, to);
291}
292
293extern "C"
294{
295 static int
296 posix_umask(lua_State * L)
297 {
298#ifdef WIN32
299 lua_pushnil(L);
300 return 1;
301#else
302 int from = luaL_checknumber(L, -1);
303 mode_t mask = 64*((from / 100) % 10) + 8*((from / 10) % 10) + (from % 10);
304 mode_t oldmask = umask(mask);
305 int res = 100*(oldmask/64) + 10*((oldmask/8) % 8) + (oldmask % 8);
306 lua_pushnumber(L, res);
307 return 1;
308#endif
309 }
310
311 static int
312 go_to_test_dir(lua_State * L)
313 {
314 try
315 {
316 char const * testname = luaL_checkstring(L, -1);
317 fs::path tname(testname);
318 fs::path testdir = run_dir / tname.leaf();
319 if (fs::exists(testdir))
320 do_remove_recursive(testdir);
321 fs::create_directory(testdir);
322 go_to_workspace(testdir.native_file_string());
323 lua_pushstring(L, testdir.native_file_string().c_str());
324 lua_pushstring(L, tname.leaf().c_str());
325 return 2;
326 }
327 catch(fs::filesystem_error & e)
328 {
329 lua_pushnil(L);
330 return 1;
331 }
332 }
333
334 static int
335 go_to_dir(lua_State * L)
336 {
337 try
338 {
339 fs::path dir(luaL_checkstring(L, -1), fs::native);
340 string from = fs::current_path().native_file_string();
341 if (!dir.is_complete())
342 dir = fs::current_path() / dir;
343 if (!fs::exists(dir) || !fs::is_directory(dir))
344 {
345 lua_pushnil(L);
346 return 1;
347 }
348 go_to_workspace(dir.native_file_string());
349 lua_pushstring(L, from.c_str());
350 return 1;
351 }
352 catch(fs::filesystem_error & e)
353 {
354 lua_pushnil(L);
355 return 1;
356 }
357 }
358
359 static int
360 clean_test_dir(lua_State *L)
361 {
362 try
363 {
364 char const * testname = luaL_checkstring(L, -1);
365 fs::path tname(testname, fs::native);
366 fs::path testdir = run_dir / tname.leaf();
367 go_to_workspace(run_dir.native_file_string());
368 do_remove_recursive(testdir);
369 lua_pushboolean(L, true);
370 return 1;
371 }
372 catch(fs::filesystem_error & e)
373 {
374 lua_pushnil(L);
375 return 1;
376 }
377 }
378
379 static int
380 remove_recursive(lua_State *L)
381 {
382 try
383 {
384 fs::path dir(luaL_checkstring(L, -1));
385 do_remove_recursive(dir);
386 lua_pushboolean(L, true);
387 return 1;
388 }
389 catch(fs::filesystem_error & e)
390 {
391 lua_pushboolean(L, false);
392 lua_pushstring(L, e.what());
393 return 2;
394 }
395 }
396
397 static int
398 make_tree_accessible(lua_State *L)
399 {
400 try
401 {
402 fs::path dir(luaL_checkstring(L, -1));
403 do_make_tree_accessible(dir);
404 lua_pushboolean(L, true);
405 return 1;
406 }
407 catch(fs::filesystem_error & e)
408 {
409 lua_pushboolean(L, false);
410 lua_pushstring(L, e.what());
411 return 2;
412 }
413 }
414
415 static int
416 copy_recursive(lua_State *L)
417 {
418 try
419 {
420 fs::path from(luaL_checkstring(L, -2));
421 fs::path to(luaL_checkstring(L, -1));
422 do_copy_recursive(from, to);
423 lua_pushboolean(L, true);
424 return 1;
425 }
426 catch(fs::filesystem_error & e)
427 {
428 lua_pushboolean(L, false);
429 lua_pushstring(L, e.what());
430 return 2;
431 }
432 }
433
434 static int
435 leave_test_dir(lua_State *L)
436 {
437 try
438 {
439 go_to_workspace(run_dir.native_file_string());
440 lua_pushboolean(L, true);
441 return 1;
442 }
443 catch(fs::filesystem_error & e)
444 {
445 lua_pushnil(L);
446 return 1;
447 }
448 }
449
450 static int
451 make_dir(lua_State *L)
452 {
453 try
454 {
455 char const * dirname = luaL_checkstring(L, -1);
456 fs::path dir(dirname, fs::native);
457 fs::create_directory(dir);
458 lua_pushboolean(L, true);
459 return 1;
460 }
461 catch(fs::filesystem_error & e)
462 {
463 lua_pushnil(L);
464 return 1;
465 }
466 }
467
468 static int
469 mtime(lua_State *L)
470 {
471 try
472 {
473 char const * file = luaL_checkstring(L, -1);
474 fs::path fn(file);
475 std::time_t t = fs::last_write_time(file);
476 if (t == std::time_t(-1))
477 lua_pushnil(L);
478 else
479 lua_pushnumber(L, t);
480 return 1;
481 }
482 catch(fs::filesystem_error & e)
483 {
484 lua_pushnil(L);
485 return 1;
486 }
487 }
488
489 static int
490 exists(lua_State *L)
491 {
492 try
493 {
494 char const * name = luaL_checkstring(L, -1);
495 fs::path p;
496 p = fs::path(name, fs::native);
497 lua_pushboolean(L, fs::exists(p));
498 }
499 catch(fs::filesystem_error & e)
500 {
501 lua_pushnil(L);
502 }
503 return 1;
504 }
505
506 static int
507 isdir(lua_State *L)
508 {
509 try
510 {
511 char const * name = luaL_checkstring(L, -1);
512 fs::path p;
513 p = fs::path(name, fs::native);
514 lua_pushboolean(L, fs::exists(p) && fs::is_directory(p));
515 }
516 catch(fs::filesystem_error & e)
517 {
518 lua_pushnil(L);
519 }
520 return 1;
521 }
522
523 static int
524 get_source_dir(lua_State * L)
525 {
526 lua_pushstring(L, source_dir.native_file_string().c_str());
527 return 1;
528 }
529
530 static int
531 clear_redirect(lua_State * L)
532 {
533 typedef redirect::saveblock rsb;
534 rsb const *sb = static_cast<rsb const*>(lua_topointer(L, 1));
535 clear_redirect(redirect::in, sb->in);
536 clear_redirect(redirect::out, sb->out);
537 clear_redirect(redirect::err, sb->err);
538 return 0;
539 }
540
541 static int
542 set_redirect(lua_State * L)
543 {
544 char const * infile = luaL_checkstring(L, -3);
545 char const * outfile = luaL_checkstring(L, -2);
546 char const * errfile = luaL_checkstring(L, -1);
547
548 typedef redirect::saveblock rsb;
549 rsb *sb = static_cast<rsb*> (lua_newuserdata(L, sizeof(rsb)));
550 sb->in = set_redirect(redirect::in, infile);
551 sb->out = set_redirect(redirect::out, outfile);
552 sb->err = set_redirect(redirect::err, errfile);
553 lua_newtable(L);
554 lua_pushstring(L, "__index");
555 lua_pushvalue(L, -2);
556 lua_settable(L, -3);
557
558 lua_pushstring(L, "restore");
559 lua_pushcfunction(L,clear_redirect);
560 lua_settable(L, -3);
561 lua_setmetatable(L, -2);
562
563 return 1;
564 }
565
566 static int
567 get_ostype(lua_State * L)
568 {
569 string str;
570 get_system_flavour(str);
571 lua_pushstring(L, str.c_str());
572 return 1;
573 }
574
575 static int
576 do_save_env(lua_State * L)
577 {
578 save_env();
579 return 0;
580 }
581
582 static int
583 do_restore_env(lua_State * L)
584 {
585 restore_env();
586 return 0;
587 }
588
589 static int
590 do_set_env(lua_State * L)
591 {
592 char const * var = luaL_checkstring(L, -2);
593 char const * val = luaL_checkstring(L, -1);
594 set_env(var, val);
595 return 0;
596 }
597
598 static int
599 timed_wait(lua_State * L)
600 {
601 pid_t pid = static_cast<pid_t>(luaL_checknumber(L, -2));
602 int time = static_cast<int>(luaL_checknumber(L, -1));
603 int res;
604 int ret;
605 ret = process_wait(pid, &res, time);
606 lua_pushnumber(L, res);
607 lua_pushnumber(L, ret);
608 return 2;
609 }
610}
611
612int main(int argc, char **argv)
613{
614// global_sanity.set_debug();
615 string testfile;
616 string firstdir;
617 bool needhelp = false;
618 for (int i = 1; i < argc; ++i)
619 if (string(argv[i]) == "--help" || string(argv[i]) == "-h")
620 needhelp = true;
621 if (argc > 1 && !needhelp)
622 {
623 save_initial_path();
624 try
625 {
626 std::string name = argv[1];
627 fs::path file = fs::complete(fs::path(name, fs::native));
628 testfile = file.native_file_string();
629 source_dir = file.branch_path();
630 }
631 catch(fs::filesystem_error & e)
632 {
633 fprintf(stderr, "Error during initialization: %s", e.what());
634 exit(1);
635 }
636 firstdir = fs::initial_path().native_file_string();
637 run_dir = fs::initial_path() / "tester_dir";
638 fs::create_directory(run_dir);
639 go_to_workspace(run_dir.native_file_string());
640 }
641 else
642 {
643 fprintf(stderr, "Usage: %s test-file [arguments]\n", argv[0]);
644 fprintf(stderr, "\t-h print this message\n");
645 fprintf(stderr, "\t-l print test names only; don't run them\n");
646 fprintf(stderr, "\t-d don't clean the scratch directories\n");
647 fprintf(stderr, "\tnum run a specific test\n");
648 fprintf(stderr, "\tnum..num run tests in a range\n");
649 fprintf(stderr, "\t if num is negative, count back from the end\n");
650 fprintf(stderr, "\tregex run tests with matching names\n");
651 return 1;
652 }
653 lua_State *st = lua_open();
654 lua_atpanic (st, &panic_thrower);
655 luaopen_base(st);
656 luaopen_io(st);
657 luaopen_string(st);
658 luaopen_math(st);
659 luaopen_table(st);
660 luaopen_debug(st);
661 add_functions(st);
662 lua_register(st, "go_to_test_dir", go_to_test_dir);
663 lua_register(st, "get_source_dir", get_source_dir);
664 lua_register(st, "set_redirect", set_redirect);
665 lua_register(st, "clear_redirect", clear_redirect);
666 lua_register(st, "clean_test_dir", clean_test_dir);
667 lua_register(st, "leave_test_dir", leave_test_dir);
668 lua_register(st, "mkdir", make_dir);
669 lua_register(st, "chdir", go_to_dir);
670 lua_register(st, "mtime", mtime);
671 lua_register(st, "remove_recursive", remove_recursive);
672 lua_register(st, "make_tree_accessible", make_tree_accessible);
673 lua_register(st, "copy_recursive", copy_recursive);
674 lua_register(st, "exists", exists);
675 lua_register(st, "isdir", isdir);
676 lua_register(st, "get_ostype", get_ostype);
677 lua_register(st, "save_env", do_save_env);
678 lua_register(st, "restore_env", do_restore_env);
679 lua_register(st, "set_env", do_set_env);
680 lua_register(st, "timed_wait", timed_wait);
681
682 lua_register(st, "posix_umask", posix_umask);
683
684 lua_pushstring(st, "initial_dir");
685 lua_pushstring(st, firstdir.c_str());
686 lua_settable(st, LUA_GLOBALSINDEX);
687
688 int ret = 2;
689 try
690 {
691 run_string(st, tester_constant, "tester builtin functions");
692 //printf("Loading test file %s\n", testfile.c_str());
693 run_file(st, testfile);
694 Lua ll(st);
695 ll.func("run_tests");
696 ll.push_table();
697 for (int i = 2; i < argc; ++i)
698 {
699 ll.push_int(i-1);
700 ll.push_str(argv[i]);
701 ll.set_table();
702 }
703 ll.call(1,1)
704 .extract_int(ret);
705 }
706 catch (std::exception &e)
707 {
708 fprintf(stderr, "Error: %s", e.what());
709 }
710
711 lua_close(st);
712 return ret;
713}
714
715// Local Variables:
716// mode: C++
717// fill-column: 76
718// c-file-style: "gnu"
719// indent-tabs-mode: nil
720// End:
721// 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