monotone

monotone Mtn Source Tree

Root/tester.cc

1#include "lua.h"
2#include "lualib.h"
3#include "lauxlib.h"
4
5#include "lua.hh"
6#include "tester.h"
7#include "platform.hh"
8#include "sanity.hh"
9
10#include <stdlib.h>
11
12#include <exception>
13
14#include <boost/filesystem/path.hpp>
15#include <boost/filesystem/operations.hpp>
16#include <boost/filesystem/convenience.hpp>
17#include <boost/filesystem/exception.hpp>
18#include <boost/lexical_cast.hpp>
19
20#include <boost/version.hpp>
21
22#include <map>
23#include <utility>
24
25namespace fs = boost::filesystem;
26
27using std::string;
28using std::map;
29using std::memcpy;
30using std::getenv;
31using std::exit;
32using std::make_pair;
33using boost::lexical_cast;
34
35// Lua uses the c i/o functions, so we need to too.
36struct tester_sanity : public sanity
37{
38 void inform_log(std::string const &msg)
39 {fprintf(stdout, "%s", msg.c_str());}
40 void inform_message(std::string const &msg)
41 {fprintf(stdout, "%s", msg.c_str());};
42 void inform_warning(std::string const &msg)
43 {fprintf(stderr, "warning: %s", msg.c_str());};
44 void inform_error(std::string const &msg)
45 {fprintf(stderr, "error: %s", msg.c_str());};
46};
47tester_sanity real_sanity;
48sanity & global_sanity = real_sanity;
49
50
51#ifdef WIN32
52#include <windows.h>
53bool make_accessible(string const &name)
54{
55 DWORD attrs = GetFileAttributes(name.c_str());
56 if (attrs == INVALID_FILE_ATTRIBUTES)
57 return false;
58 bool ok = SetFileAttributes(name.c_str(), attrs & ~FILE_ATTRIBUTE_READONLY);
59 return ok;
60}
61#else
62#include <unistd.h>
63#include <sys/stat.h>
64bool make_accessible(string const &name)
65{
66 struct stat st;
67 bool ok = (stat(name.c_str(), &st) == 0);
68 if (!ok)
69 return false;
70 mode_t new_mode = st.st_mode;
71 if (S_ISDIR(st.st_mode))
72 new_mode |= S_IEXEC;
73 new_mode |= S_IREAD | S_IWRITE;
74 return (chmod(name.c_str(), new_mode) == 0);
75}
76#endif
77
78
79#include <cstdlib>
80
81void set_env(char const * var, char const * val)
82{
83#if defined(WIN32)
84 SetEnvironmentVariable(var, val);
85#elif defined(HAVE_SETENV)
86 setenv(var, val, 1);
87#elif defined(HAVE_PUTENV)
88 // note: this leaks memory, but the tester is short lived so it probably
89 // doesn't matter much.
90 string * tempstr = new string(var);
91 tempstr->append("=");
92 tempstr->append(val);
93 putenv(const_cast<char *>(tempstr->c_str()));
94#else
95#error set_env needs to be ported to this platform
96#endif
97}
98
99void unset_env(char const * var)
100{
101#if defined(WIN32)
102 SetEnvironmentVariable(var, 0);
103#elif defined(HAVE_UNSETENV)
104 unsetenv(var);
105#else
106#error unset_env needs to be ported to this platform
107#endif
108}
109
110map<string, string> orig_env_vars;
111
112
113fs::path source_dir;
114fs::path run_dir;
115
116static int panic_thrower(lua_State * st)
117{
118 throw oops("lua error");
119}
120
121void do_remove_recursive(fs::path const &rem)
122{
123 if (!fs::exists(rem))
124 return;
125 make_accessible(rem.native_file_string());
126 if (fs::is_directory(rem))
127 {
128 for (fs::directory_iterator i(rem); i != fs::directory_iterator(); ++i)
129 do_remove_recursive(*i);
130 }
131 fs::remove(rem);
132}
133
134void do_make_tree_accessible(fs::path const &f)
135{
136 if (!fs::exists(f))
137 return;
138 make_accessible(f.native_file_string());
139 if (fs::is_directory(f))
140 {
141 for (fs::directory_iterator i(f); i != fs::directory_iterator(); ++i)
142 do_make_tree_accessible(*i);
143 }
144}
145
146void do_copy_recursive(fs::path const &from, fs::path to)
147{
148 if (!fs::exists(from))
149 {
150#if BOOST_VERSION < 103400
151 throw fs::filesystem_error("Source for copy does not exist", from, 0);
152#else
153 throw fs::filesystem_path_error("Source for copy does not exist", from, 0);
154#endif
155 }
156 if (fs::exists(to))
157 {
158 if (fs::is_directory(to))
159 to = to / from.leaf();
160 else
161 do_remove_recursive(to);
162 }
163 if (fs::is_directory(from))
164 {
165 fs::create_directory(to);
166 for (fs::directory_iterator i(from); i != fs::directory_iterator(); ++i)
167 do_copy_recursive(*i, to / i->leaf());
168 }
169 else
170 fs::copy_file(from, to);
171}
172
173LUAEXT(posix_umask, )
174{
175#ifdef WIN32
176 lua_pushnil(L);
177 return 1;
178#else
179 unsigned int from = (unsigned int)luaL_checknumber(L, -1);
180 mode_t mask = 64*((from / 100) % 10) + 8*((from / 10) % 10) + (from % 10);
181 mode_t oldmask = umask(mask);
182 int res = 100*(oldmask/64) + 10*((oldmask/8) % 8) + (oldmask % 8);
183 lua_pushnumber(L, res);
184 return 1;
185#endif
186}
187
188LUAEXT(go_to_test_dir, )
189{
190 try
191 {
192 char const * testname = luaL_checkstring(L, -1);
193 fs::path tname(testname);
194 fs::path testdir = run_dir / tname.leaf();
195 if (fs::exists(testdir))
196 do_remove_recursive(testdir);
197 fs::create_directory(testdir);
198 change_current_working_dir(testdir.native_file_string());
199 lua_pushstring(L, testdir.native_file_string().c_str());
200 lua_pushstring(L, tname.leaf().c_str());
201 return 2;
202 }
203 catch(fs::filesystem_error & e)
204 {
205 lua_pushnil(L);
206 return 1;
207 }
208}
209
210LUAEXT(chdir, )
211{
212 try
213 {
214 fs::path dir(luaL_checkstring(L, -1), fs::native);
215 string from = fs::current_path().native_file_string();
216 if (!dir.is_complete())
217 dir = fs::current_path() / dir;
218 if (!fs::exists(dir) || !fs::is_directory(dir))
219 {
220 lua_pushnil(L);
221 return 1;
222 }
223 change_current_working_dir(dir.native_file_string());
224 lua_pushstring(L, from.c_str());
225 return 1;
226 }
227 catch(fs::filesystem_error & e)
228 {
229 lua_pushnil(L);
230 return 1;
231 }
232}
233
234LUAEXT(clean_test_dir, )
235{
236 try
237 {
238 char const * testname = luaL_checkstring(L, -1);
239 fs::path tname(testname, fs::native);
240 fs::path testdir = run_dir / tname.leaf();
241 change_current_working_dir(run_dir.native_file_string());
242 do_remove_recursive(testdir);
243 lua_pushboolean(L, true);
244 return 1;
245 }
246 catch(fs::filesystem_error & e)
247 {
248 lua_pushnil(L);
249 return 1;
250 }
251}
252
253LUAEXT(remove_recursive, )
254{
255 try
256 {
257 fs::path dir(luaL_checkstring(L, -1));
258 do_remove_recursive(dir);
259 lua_pushboolean(L, true);
260 return 1;
261 }
262 catch(fs::filesystem_error & e)
263 {
264 lua_pushboolean(L, false);
265 lua_pushstring(L, e.what());
266 return 2;
267 }
268}
269
270LUAEXT(make_tree_accessible, )
271{
272 try
273 {
274 fs::path dir(luaL_checkstring(L, -1));
275 do_make_tree_accessible(dir);
276 lua_pushboolean(L, true);
277 return 1;
278 }
279 catch(fs::filesystem_error & e)
280 {
281 lua_pushboolean(L, false);
282 lua_pushstring(L, e.what());
283 return 2;
284 }
285}
286
287LUAEXT(copy_recursive, )
288{
289 try
290 {
291 fs::path from(luaL_checkstring(L, -2));
292 fs::path to(luaL_checkstring(L, -1));
293 do_copy_recursive(from, to);
294 lua_pushboolean(L, true);
295 return 1;
296 }
297 catch(fs::filesystem_error & e)
298 {
299 lua_pushboolean(L, false);
300 lua_pushstring(L, e.what());
301 return 2;
302 }
303}
304
305LUAEXT(leave_test_dir, )
306{
307 try
308 {
309 change_current_working_dir(run_dir.native_file_string());
310 lua_pushboolean(L, true);
311 return 1;
312 }
313 catch(fs::filesystem_error & e)
314 {
315 lua_pushnil(L);
316 return 1;
317 }
318}
319
320LUAEXT(mkdir, )
321{
322 try
323 {
324 char const * dirname = luaL_checkstring(L, -1);
325 fs::path dir(dirname, fs::native);
326 fs::create_directory(dir);
327 lua_pushboolean(L, true);
328 return 1;
329 }
330 catch(fs::filesystem_error & e)
331 {
332 lua_pushnil(L);
333 return 1;
334 }
335}
336
337LUAEXT(mtime, )
338{
339 try
340 {
341 char const * file = luaL_checkstring(L, -1);
342 fs::path fn(file);
343 std::time_t t = fs::last_write_time(file);
344 if (t == std::time_t(-1))
345 lua_pushnil(L);
346 else
347 lua_pushnumber(L, t);
348 return 1;
349 }
350 catch(fs::filesystem_error & e)
351 {
352 lua_pushnil(L);
353 return 1;
354 }
355}
356
357LUAEXT(exists, )
358{
359 try
360 {
361 char const * name = luaL_checkstring(L, -1);
362 fs::path p(name, fs::native);
363 lua_pushboolean(L, fs::exists(p));
364 }
365 catch(fs::filesystem_error & e)
366 {
367 lua_pushnil(L);
368 }
369 return 1;
370}
371
372LUAEXT(isdir, )
373{
374 try
375 {
376 char const * name = luaL_checkstring(L, -1);
377 fs::path p;
378 p = fs::path(name, fs::native);
379 lua_pushboolean(L, fs::exists(p) && fs::is_directory(p));
380 }
381 catch(fs::filesystem_error & e)
382 {
383 lua_pushnil(L);
384 }
385 return 1;
386}
387
388LUAEXT(read_directory, )
389{
390 try
391 {
392 fs::path dir(luaL_checkstring(L, -1), fs::native);
393 unsigned int n = 1;
394
395 lua_newtable(L);
396 for (fs::directory_iterator i(dir); i != fs::directory_iterator(); ++i, ++n)
397 {
398 lua_pushstring(L, i->leaf().c_str());
399 lua_rawseti(L, -2, n);
400 }
401 }
402 catch(fs::filesystem_error & e)
403 {
404 lua_pushnil(L);
405 }
406 return 1;
407}
408
409LUAEXT(get_source_dir, )
410{
411 lua_pushstring(L, source_dir.native_file_string().c_str());
412 return 1;
413}
414
415LUAEXT(save_env, )
416{
417 orig_env_vars.clear();
418 return 0;
419}
420
421LUAEXT(restore_env, )
422{
423 for (map<string,string>::const_iterator i = orig_env_vars.begin();
424 i != orig_env_vars.end(); ++i)
425 set_env(i->first.c_str(), i->second.c_str());
426 orig_env_vars.clear();
427 return 0;
428}
429
430LUAEXT(set_env, )
431{
432 char const * var = luaL_checkstring(L, -2);
433 char const * val = luaL_checkstring(L, -1);
434 if (orig_env_vars.find(string(var)) == orig_env_vars.end()) {
435 char const * old = getenv(var);
436 if (old)
437 orig_env_vars.insert(make_pair(string(var), string(old)));
438 else
439 orig_env_vars.insert(make_pair(string(var), ""));
440 }
441 set_env(var, val);
442 return 0;
443}
444
445LUAEXT(unset_env, )
446{
447 char const * var = luaL_checkstring(L, -1);
448 if (orig_env_vars.find(string(var)) == orig_env_vars.end()) {
449 char const * old = getenv(var);
450 if (old)
451 orig_env_vars.insert(make_pair(string(var), string(old)));
452 else
453 orig_env_vars.insert(make_pair(string(var), ""));
454 }
455 unset_env(var);
456 return 0;
457}
458
459LUAEXT(timed_wait, )
460{
461 pid_t pid = static_cast<pid_t>(luaL_checknumber(L, -2));
462 int time = static_cast<int>(luaL_checknumber(L, -1));
463 int res;
464 int ret;
465 ret = process_wait(pid, &res, time);
466 lua_pushnumber(L, res);
467 lua_pushnumber(L, ret);
468 return 2;
469}
470
471LUAEXT(require_not_root, )
472{
473#ifdef WIN32
474 bool running_as_root = false;
475#else
476 bool running_as_root = !geteuid();
477#endif
478 // E() doesn't work here, I just get "warning: " in the
479 // output. Why?
480 if (running_as_root)
481 {
482 P(F("This test suite cannot be run as the root user.\n"
483 "Please try again with a normal user account.\n"));
484 exit(1);
485 }
486 return 0;
487}
488
489int main(int argc, char **argv)
490{
491 int retcode = 2;
492 lua_State *st = 0;
493 try{
494// global_sanity.set_debug();
495 string testfile;
496 string firstdir;
497 bool needhelp = false;
498 for (int i = 1; i < argc; ++i)
499 if (string(argv[i]) == "--help" || string(argv[i]) == "-h")
500 needhelp = true;
501 if (argc > 1 && !needhelp)
502 {
503 fs::initial_path();
504 fs::path::default_name_check(fs::native);
505 try
506 {
507 std::string name = argv[1];
508 fs::path file = fs::complete(fs::path(name, fs::native));
509 testfile = file.native_file_string();
510 source_dir = file.branch_path();
511 }
512 catch(fs::filesystem_error & e)
513 {
514 E(false, F("Error during initialization: %s") % e.what());
515 }
516 firstdir = fs::initial_path().native_file_string();
517 run_dir = fs::initial_path() / "tester_dir";
518 fs::create_directory(run_dir);
519 change_current_working_dir(run_dir.native_file_string());
520 }
521 else
522 {
523 P(F("Usage: %s test-file [arguments]\n") % argv[0]);
524 P(F("\t-h print this message\n"));
525 P(F("\t-l print test names only; don't run them\n"));
526 P(F("\t-d don't clean the scratch directories\n"));
527 P(F("\tnum run a specific test\n"));
528 P(F("\tnum..num run tests in a range\n"));
529 P(F("\t if num is negative, count back from the end\n"));
530 P(F("\tregex run tests with matching names\n"));
531 return 1;
532 }
533 st = luaL_newstate();
534 lua_atpanic (st, &panic_thrower);
535 luaL_openlibs(st);
536 add_functions(st);
537
538 lua_pushstring(st, "initial_dir");
539 lua_pushstring(st, firstdir.c_str());
540 lua_settable(st, LUA_GLOBALSINDEX);
541
542 try
543 {
544 run_string(st, tester_constant, "tester builtin functions");
545 //printf("Loading test file %s\n", testfile.c_str());
546 run_file(st, testfile);
547 Lua ll(st);
548 ll.func("run_tests");
549 ll.push_table();
550 for (int i = 2; i < argc; ++i)
551 {
552 ll.push_int(i-1);
553 ll.push_str(argv[i]);
554 ll.set_table();
555 }
556 ll.call(1,1)
557 .extract_int(retcode);
558 }
559 catch (std::exception &e)
560 {
561 P(F("Error: %s") % e.what());
562 }
563 } catch (informative_failure & e) {
564 P(F("Error: %s\n") % e.what());
565 retcode = 1;
566 } catch (std::logic_error & e) {
567 P(F("Invariant failure: %s\n") % e.what());
568 retcode = 3;
569 }
570 if (st)
571 lua_close(st);
572 return retcode;
573}
574
575// Local Variables:
576// mode: C++
577// fill-column: 76
578// c-file-style: "gnu"
579// indent-tabs-mode: nil
580// End:
581// 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