From: Ben Pfaff Date: Thu, 9 Dec 2004 02:46:18 +0000 (+0000) Subject: Begin restructuring test suite. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7329f3d75dd5db7fd1bde03ed3c8a13b285eb7e;p=pintos-anon Begin restructuring test suite. --- diff --git a/grading/filesys/child-syn-read.c b/grading/filesys/child-syn-read.c index f4e4917..5bd739e 100644 --- a/grading/filesys/child-syn-read.c +++ b/grading/filesys/child-syn-read.c @@ -18,17 +18,17 @@ main (int argc, const char *argv[]) quiet = true; - check (argc == 2, "argc must be 2, actually %d", argc); + CHECK (argc == 2, "argc must be 2, actually %d", argc); child_idx = atoi (argv[1]); random_init (0); random_bytes (buf, sizeof buf); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); for (i = 0; i < sizeof buf; i++) { char c; - check (read (fd, &c, 1) > 0, "read \"%s\"", filename); + CHECK (read (fd, &c, 1) > 0, "read \"%s\"", filename); compare_bytes (&c, buf + i, 1, i, filename); } close (fd); diff --git a/grading/filesys/child-syn-rw.c b/grading/filesys/child-syn-rw.c index 64e93c7..7f0137a 100644 --- a/grading/filesys/child-syn-rw.c +++ b/grading/filesys/child-syn-rw.c @@ -20,18 +20,18 @@ main (int argc, const char *argv[]) quiet = true; - check (argc == 2, "argc must be 2, actually %d", argc); + CHECK (argc == 2, "argc must be 2, actually %d", argc); child_idx = atoi (argv[1]); random_init (0); random_bytes (buf1, sizeof buf1); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); ofs = 0; while (ofs < sizeof buf2) { int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs); - check (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs), + CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs), "%zu-byte read on \"%s\" returned invalid value of %d", sizeof buf2 - ofs, filename, bytes_read); if (bytes_read > 0) diff --git a/grading/filesys/child-syn-wrt.c b/grading/filesys/child-syn-wrt.c index 2a57281..0a43f5a 100644 --- a/grading/filesys/child-syn-wrt.c +++ b/grading/filesys/child-syn-wrt.c @@ -16,15 +16,15 @@ main (int argc, char *argv[]) quiet = true; - check (argc == 2, "argc must be 2, actually %d", argc); + CHECK (argc == 2, "argc must be 2, actually %d", argc); child_idx = atoi (argv[1]); random_init (0); random_bytes (buf, sizeof buf); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); seek (fd, CHUNK_SIZE * child_idx); - check (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0, + CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0, "write \"%s\"", filename); msg ("close \"%s\"", filename); close (fd); diff --git a/grading/filesys/create.inc b/grading/filesys/create.inc index 3e8a9b5..71b52fb 100644 --- a/grading/filesys/create.inc +++ b/grading/filesys/create.inc @@ -9,6 +9,7 @@ static char buf[TEST_SIZE]; void test_main (void) { - check (create ("testfile", TEST_SIZE), "create testfile"); - check_file ("testfile", buf, TEST_SIZE); + const char *filename = "blargle"; + CHECK (create (filename, TEST_SIZE), "create \"%s\"", filename); + check_file (filename, buf, TEST_SIZE); } diff --git a/grading/filesys/dir-empty-name.c b/grading/filesys/dir-empty-name.c index 427eb2d..9d423c8 100644 --- a/grading/filesys/dir-empty-name.c +++ b/grading/filesys/dir-empty-name.c @@ -6,5 +6,5 @@ const char test_name[] = "dir-empty-name"; void test_main (void) { - check (!create ("", 0), "create \"\" (must return false)"); + CHECK (!create ("", 0), "create \"\" (must return false)"); } diff --git a/grading/filesys/dir-mk-vine.c b/grading/filesys/dir-mk-vine.c index c044b3a..3bed83e 100644 --- a/grading/filesys/dir-mk-vine.c +++ b/grading/filesys/dir-mk-vine.c @@ -12,11 +12,11 @@ test_main (void) dir[1] = '\0'; for (dir[0] = '0'; dir[0] <= '9'; dir[0]++) { - check (mkdir (dir), "mkdir \"%s\"", dir); - check (chdir (dir), "chdir \"%s\"", dir); + CHECK (mkdir (dir), "mkdir \"%s\"", dir); + CHECK (chdir (dir), "chdir \"%s\"", dir); } - check (create ("test", 512), "create \"test\""); - check (chdir ("/"), "chdir \"/\""); - check (open (filename) > 1, "open \"%s\"", filename); + CHECK (create ("test", 512), "create \"test\""); + CHECK (chdir ("/"), "chdir \"/\""); + CHECK (open (filename) > 1, "open \"%s\"", filename); } diff --git a/grading/filesys/dir-mkdir.c b/grading/filesys/dir-mkdir.c index 85d243e..890af9d 100644 --- a/grading/filesys/dir-mkdir.c +++ b/grading/filesys/dir-mkdir.c @@ -6,9 +6,9 @@ const char test_name[] = "dir-mkdir"; void test_main (void) { - check (mkdir ("a"), "mkdir \"a\""); - check (create ("a/b", 512), "create \"a/b\""); - check (chdir ("a"), "chdir \"a\""); - check (open ("b") > 1, "open \"b\""); + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (create ("a/b", 512), "create \"a/b\""); + CHECK (chdir ("a"), "chdir \"a\""); + CHECK (open ("b") > 1, "open \"b\""); } diff --git a/grading/filesys/dir-open.c b/grading/filesys/dir-open.c index 40550ee..37a400b 100644 --- a/grading/filesys/dir-open.c +++ b/grading/filesys/dir-open.c @@ -8,7 +8,7 @@ test_main (void) { int fd; - check (mkdir ("xyzzy"), "mkdir \"xyzzy\""); + CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\""); msg ("open \"xyzzy\""); fd = open ("xyzzy"); if (fd == -1) @@ -16,7 +16,7 @@ test_main (void) else { int retval = write (fd, "foobar", 6); - check (retval == -1, "write \"xyzzy\" (must return -1, actually %d)", + CHECK (retval == -1, "write \"xyzzy\" (must return -1, actually %d)", retval); } } diff --git a/grading/filesys/dir-over-file.c b/grading/filesys/dir-over-file.c index 01347e1..8bcb36e 100644 --- a/grading/filesys/dir-over-file.c +++ b/grading/filesys/dir-over-file.c @@ -6,6 +6,6 @@ const char test_name[] = "dir-over-file"; void test_main (void) { - check (mkdir ("abc"), "mkdir \"abc\""); - check (!create ("abc", 0), "create \"abc\" (must return false)"); + CHECK (mkdir ("abc"), "mkdir \"abc\""); + CHECK (!create ("abc", 0), "create \"abc\" (must return false)"); } diff --git a/grading/filesys/dir-rm-cwd-cd.c b/grading/filesys/dir-rm-cwd-cd.c index f1916fb..94bc0a3 100644 --- a/grading/filesys/dir-rm-cwd-cd.c +++ b/grading/filesys/dir-rm-cwd-cd.c @@ -6,12 +6,12 @@ const char test_name[] = "dir-rm-cwd-cd"; void test_main (void) { - check (mkdir ("a"), "mkdir \"a\""); - check (chdir ("a"), "chdir \"a\""); + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (chdir ("a"), "chdir \"a\""); msg ("remove \"/a\" (must not crash)"); if (remove ("/a")) - check (!chdir ("/a"), + CHECK (!chdir ("/a"), "chdir \"/a\" (remove succeeded so this must return false)"); else - check (chdir ("/a"), "chdir \"/a\" (remove failed so this must succeed)"); + CHECK (chdir ("/a"), "chdir \"/a\" (remove failed so this must succeed)"); } diff --git a/grading/filesys/dir-rm-cwd.c b/grading/filesys/dir-rm-cwd.c index 2754850..f99983b 100644 --- a/grading/filesys/dir-rm-cwd.c +++ b/grading/filesys/dir-rm-cwd.c @@ -6,8 +6,8 @@ const char test_name[] = "dir-rm-cwd"; void test_main (void) { - check (mkdir ("a"), "mkdir \"a\""); - check (chdir ("a"), "chdir \"a\""); + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (chdir ("a"), "chdir \"a\""); msg ("remove \"/a\" (must not crash)"); remove ("/a"); msg ("create \"b\" (must not crash)"); diff --git a/grading/filesys/dir-rm-parent.c b/grading/filesys/dir-rm-parent.c index 9a2136e..f71895a 100644 --- a/grading/filesys/dir-rm-parent.c +++ b/grading/filesys/dir-rm-parent.c @@ -6,10 +6,10 @@ const char test_name[] = "dir-rm-parent"; void test_main (void) { - check (mkdir ("a"), "mkdir \"a\""); - check (chdir ("a"), "chdir \"a\""); - check (mkdir ("b"), "mkdir \"b\""); - check (chdir ("b"), "chdir \"b\""); + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (chdir ("a"), "chdir \"a\""); + CHECK (mkdir ("b"), "mkdir \"b\""); + CHECK (chdir ("b"), "chdir \"b\""); msg ("remove \"/b\" (must not crash)"); remove ("/b"); msg ("remove \"/a\" (must not crash)"); diff --git a/grading/filesys/dir-rm-root.c b/grading/filesys/dir-rm-root.c index 2922824..7489507 100644 --- a/grading/filesys/dir-rm-root.c +++ b/grading/filesys/dir-rm-root.c @@ -6,6 +6,6 @@ const char test_name[] = "dir-rm-root"; void test_main (void) { - check (!remove ("/"), "remove \"/\" (must return false)"); - check (create ("/a", 243), "create \"/a\""); + CHECK (!remove ("/"), "remove \"/\" (must return false)"); + CHECK (create ("/a", 243), "create \"/a\""); } diff --git a/grading/filesys/dir-rm-tree.c b/grading/filesys/dir-rm-tree.c index 24cc21f..9bfd8ba 100644 --- a/grading/filesys/dir-rm-tree.c +++ b/grading/filesys/dir-rm-tree.c @@ -43,7 +43,7 @@ remove_tree (int at, int bt, int ct, int dt) quiet = false; snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0); - check (open (try) == -1, "open \"%s\" (must return -1)", try); + CHECK (open (try) == -1, "open \"%s\" (must return -1)", try); } static void @@ -56,5 +56,5 @@ do_remove (const char *format, ...) vsnprintf (name, sizeof name, format, args); va_end (args); - check (remove (name), "remove \"%s\"", name); + CHECK (remove (name), "remove \"%s\"", name); } diff --git a/grading/filesys/dir-rm-vine.c b/grading/filesys/dir-rm-vine.c index 20b52a2..6259d3e 100644 --- a/grading/filesys/dir-rm-vine.c +++ b/grading/filesys/dir-rm-vine.c @@ -14,22 +14,22 @@ test_main (void) tmp[1] = '\0'; for (tmp[0] = '0'; tmp[0] <= '9'; tmp[0]++) { - check (mkdir (tmp), "mkdir \"%s\"", tmp); - check (chdir (tmp), "chdir \"%s\"", tmp); + CHECK (mkdir (tmp), "mkdir \"%s\"", tmp); + CHECK (chdir (tmp), "chdir \"%s\"", tmp); } - check (create ("test", 512), "create \"test\""); + CHECK (create ("test", 512), "create \"test\""); - check (chdir ("/"), "chdir \"/\""); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK (chdir ("/"), "chdir \"/\""); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); msg ("close \"%s\"", filename); close (fd); strlcpy (tmp, filename, sizeof tmp); while (strlen (tmp) > 0) { - check (remove (tmp), "remove \"%s\"", tmp); + CHECK (remove (tmp), "remove \"%s\"", tmp); *strrchr (tmp, '/') = 0; } - check (open (filename) == -1, "open \"%s\" (must return -1)", filename); + CHECK (open (filename) == -1, "open \"%s\" (must return -1)", filename); } diff --git a/grading/filesys/dir-rmdir.c b/grading/filesys/dir-rmdir.c index ed9be64..ca5893d 100644 --- a/grading/filesys/dir-rmdir.c +++ b/grading/filesys/dir-rmdir.c @@ -6,7 +6,7 @@ const char test_name[] = "dir-rmdir"; void test_main (void) { - check (mkdir ("a"), "mkdir \"a\""); - check (remove ("a"), "rmdir \"a\""); - check (!chdir ("a"), "chdir \"a\" (must return false)"); + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (remove ("a"), "rmdir \"a\""); + CHECK (!chdir ("a"), "chdir \"a\" (must return false)"); } diff --git a/grading/filesys/dir-under-file.c b/grading/filesys/dir-under-file.c index 2565a27..08cef94 100644 --- a/grading/filesys/dir-under-file.c +++ b/grading/filesys/dir-under-file.c @@ -6,6 +6,6 @@ const char test_name[] = "dir-under-file"; void test_main (void) { - check (create ("abc", 0), "create \"abc\""); - check (!mkdir ("abc"), "mkdir \"abc\" (must return false)"); + CHECK (create ("abc", 0), "create \"abc\""); + CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)"); } diff --git a/grading/filesys/fslib.c b/grading/filesys/fslib.c index d5eeda8..78e168a 100644 --- a/grading/filesys/fslib.c +++ b/grading/filesys/fslib.c @@ -56,8 +56,8 @@ seq_test (const char *filename, void *buf, size_t size, size_t initial_size, int fd; random_bytes (buf, size); - check (create (filename, initial_size), "create \"%s\"", filename); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK (create (filename, initial_size), "create \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); ofs = 0; msg ("writing \"%s\"", filename); @@ -116,7 +116,7 @@ check_file (const char *filename, const void *buf_, size_t size) char block[512]; int fd; - check ((fd = open (filename)) > 1, "open \"%s\" for verification", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\" for verification", filename); ofs = 0; while (ofs < size) @@ -172,3 +172,31 @@ compare_bytes (const void *read_data_, const void *expected_data_, size_t size, fail ("%zu bytes read starting at offset %zu in \"%s\" differ " "from expected", j - i, ofs, filename); } + +void +exec_children (const char *child_name, pid_t pids[], size_t child_cnt) +{ + size_t i; + + for (i = 0; i < child_cnt; i++) + { + char cmd_line[128]; + snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i); + CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR, + "exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line); + } +} + +void +join_children (pid_t pids[], size_t child_cnt) +{ + size_t i; + + for (i = 0; i < child_cnt; i++) + { + int status = join (pids[i]); + CHECK (status == (int) i, + "join child %zu of %zu returned %d (expected %d)", + i + 1, child_cnt, status, i); + } +} diff --git a/grading/filesys/fslib.h b/grading/filesys/fslib.h index b1d654f..ea38624 100644 --- a/grading/filesys/fslib.h +++ b/grading/filesys/fslib.h @@ -4,6 +4,7 @@ #include #include #include +#include extern const char test_name[]; extern bool quiet; @@ -11,7 +12,21 @@ extern bool quiet; void msg (const char *, ...) PRINTF_FORMAT (1, 2); void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN; -#define check(SUCCESS, ...) \ +/* Takes an expression to test for SUCCESS and a message, which + may include printf-style arguments. Logs the message, then + tests the expression. If it is zero, indicating failure, + emits the message as a failure. + + Somewhat tricky to use: + + - SUCCESS must not have side effects that affect the + message, because that will cause the original message and + the failure message to differ. + + - The message must not have side effects of its own, because + it will be printed twice on failure, or zero times on + success if quiet is set. */ +#define CHECK(SUCCESS, ...) \ do \ { \ msg (__VA_ARGS__); \ @@ -32,6 +47,9 @@ void check_file (const char *filename, const void *buf, size_t filesize); void compare_bytes (const void *read_data, const void *expected_data, size_t size, size_t ofs, const char *filename); +void exec_children (const char *child_name, pid_t pids[], size_t child_cnt); +void join_children (pid_t pids[], size_t child_cnt); + void test_main (void); #endif /* fslib.h */ diff --git a/grading/filesys/grow-create.exp b/grading/filesys/grow-create.exp index 0bfb05f..8439e84 100644 --- a/grading/filesys/grow-create.exp +++ b/grading/filesys/grow-create.exp @@ -1,5 +1,5 @@ (grow-create) begin -(grow-create) create testfile -(grow-create) open "testfile" for verification -(grow-create) close "testfile" +(grow-create) create "blargle" +(grow-create) open "blargle" for verification +(grow-create) close "blargle" (grow-create) end diff --git a/grading/filesys/grow-dir.inc b/grading/filesys/grow-dir.inc index 86a471f..4a764f1 100644 --- a/grading/filesys/grow-dir.inc +++ b/grading/filesys/grow-dir.inc @@ -18,7 +18,7 @@ test_main (void) size_t i; #ifdef DIRECTORY - check (mkdir (DIRECTORY), "mkdir %s", DIRECTORY); + CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY); #define DIR_PREFIX DIRECTORY "/" #else #define DIR_PREFIX "" diff --git a/grading/filesys/grow-sparse.c b/grading/filesys/grow-sparse.c index 896b2fa..c8b6a8f 100644 --- a/grading/filesys/grow-sparse.c +++ b/grading/filesys/grow-sparse.c @@ -9,15 +9,16 @@ static char buf[76543]; void test_main (void) { + const char *filename = "testfile"; char zero = 0; int fd; - check (create ("testfile", 0), "create \"testfile\""); - check ((fd = open ("testfile")) > 1, "open \"testfile\""); - msg ("seek \"testfile\""); + CHECK (create (filename, 0), "create \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); + msg ("seek \"%s\"", filename); seek (fd, sizeof buf - 1); - check (write (fd, &zero, 1) > 0, "write \"testfile\""); - msg ("close \"testfile\""); + CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", filename); + msg ("close \"%s\"", filename); close (fd); - check_file ("testfile", buf, sizeof buf); + check_file (filename, buf, sizeof buf); } diff --git a/grading/filesys/grow-too-big.c b/grading/filesys/grow-too-big.c index 5711693..bf8e300 100644 --- a/grading/filesys/grow-too-big.c +++ b/grading/filesys/grow-too-big.c @@ -9,14 +9,15 @@ const char test_name[] = "grow-sparse"; void test_main (void) { + const char *filename = "fumble"; char zero = 0; int fd; - check (create ("testfile", 0), "create \"testfile\""); - check ((fd = open ("testfile")) > 1, "open \"testfile\""); - msg ("seek \"testfile\""); + CHECK (create (filename, 0), "create \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); + msg ("seek \"%s\"", filename); seek (fd, UINT_MAX); - check (write (fd, &zero, 1) > 0, "write \"testfile\""); - msg ("close \"testfile\""); + CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", filename); + msg ("close \"%s\"", filename); close (fd); } diff --git a/grading/filesys/grow-two-files.c b/grading/filesys/grow-two-files.c index aa6820a..e4b8904 100644 --- a/grading/filesys/grow-two-files.c +++ b/grading/filesys/grow-two-files.c @@ -34,11 +34,11 @@ test_main (void) random_bytes (buf_a, sizeof buf_a); random_bytes (buf_b, sizeof buf_b); - check (create ("a", 0), "create \"a\""); - check (create ("b", 0), "create \"b\""); + CHECK (create ("a", 0), "create \"a\""); + CHECK (create ("b", 0), "create \"b\""); - check ((fd_a = open ("a")) > 1, "open \"a\""); - check ((fd_b = open ("b")) > 1, "open \"b\""); + CHECK ((fd_a = open ("a")) > 1, "open \"a\""); + CHECK ((fd_b = open ("b")) > 1, "open \"b\""); msg ("write \"a\" and \"b\" alternately"); while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE) diff --git a/grading/filesys/mk-tree.c b/grading/filesys/mk-tree.c index ed0e5c3..aac5251 100644 --- a/grading/filesys/mk-tree.c +++ b/grading/filesys/mk-tree.c @@ -33,7 +33,7 @@ make_tree (int at, int bt, int ct, int dt) quiet = false; snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1); - check ((fd = open (try)) > 1, "open \"%s\"", try); + CHECK ((fd = open (try)) > 1, "open \"%s\"", try); msg ("close \"%s\"", try); close (fd); } @@ -48,7 +48,7 @@ do_mkdir (const char *format, ...) vsnprintf (dir, sizeof dir, format, args); va_end (args); - check (mkdir (dir), "mkdir \"%s\"", dir); + CHECK (mkdir (dir), "mkdir \"%s\"", dir); } static void @@ -61,5 +61,5 @@ do_touch (const char *format, ...) vsnprintf (file, sizeof file, format, args); va_end (args); - check (create (file, 0), "create \"%s\"", file); + CHECK (create (file, 0), "create \"%s\"", file); } diff --git a/grading/filesys/random.inc b/grading/filesys/random.inc index 59f52f2..895b658 100644 --- a/grading/filesys/random.inc +++ b/grading/filesys/random.inc @@ -28,8 +28,8 @@ test_main (void) for (i = 0; i < BLOCK_CNT; i++) order[i] = i; - check (create (filename, TEST_SIZE), "create \"%s\"", filename); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK (create (filename, TEST_SIZE), "create \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); msg ("write \"%s\" in random order", filename); shuffle (order, BLOCK_CNT, sizeof *order); diff --git a/grading/filesys/run-tests b/grading/filesys/run-tests index 0f86dbf..d683f7c 100755 --- a/grading/filesys/run-tests +++ b/grading/filesys/run-tests @@ -1,7 +1,7 @@ #! /usr/bin/perl # Find the directory that contains the grading files. -our ($GRADES_DIR); +use vars qw($GRADES_DIR); # Add our Perl library directory to the include path. BEGIN { @@ -15,7 +15,9 @@ use strict; use POSIX; use Algorithm::Diff; use Getopt::Long; +use Pintos::Grading; +our ($test); our ($verbose) = 0; # Verbosity of output our (@TESTS); # Tests to run. my ($clean) = 0; @@ -128,7 +130,7 @@ if ($clean) { -d ("output") || mkdir ("output") or die "output: mkdir: $!\n"; # Extract submission. -extract_tarball () if ! -d "pintos"; +obtain_sources (); # Compile submission. compile (); @@ -137,17 +139,18 @@ compile (); -d "pintos/src/threads" or die "pintos/src/threads: stat: $!\n"; # Run and grade the tests. -our $test; our %result; our %details; our %extra; for $test (@TESTS) { print "$test: "; - my ($result) = run_test ($test); + my ($result) = get_test_result (); if ($result eq 'ok') { $result = grade_test ($test); - $result =~ s/\n$//; + } elsif ($result =~ /^Timed out/) { + $result = "$result - " . grade_test ($test); } + chomp ($result); print "$result"; print " - with warnings" if $result eq 'ok' && defined $details{$test}; print "\n"; @@ -159,167 +162,6 @@ for $test (@TESTS) { write_grades (); write_details (); -sub choose_tarball { - my (@tarballs) - = grep (/^[a-z0-9]+\.[A-Za-z]+\.\d+\.\d+\.\d+\.\d+.\d+\.tar\.gz$/, - glob ("*.tar.gz")); - die "no pintos dir and no source tarball\n" if scalar (@tarballs) == 0; - - # Sort tarballs in reverse order by time. - @tarballs = sort { ext_mdyHMS ($b) cmp ext_mdyHMS ($a) } @tarballs; - - print "Multiple tarballs: choosing $tarballs[0]\n" - if scalar (@tarballs) > 1; - return $tarballs[0]; -} - -sub extract_tarball { - my ($tarball) = choose_tarball (); - - mkdir "pintos" or die "pintos: mkdir: $!\n"; - mkdir "pintos/src" or die "pintos: mkdir: $!\n"; - - print "Extracting $tarball...\n"; - xsystem ("cd pintos/src && tar xzf ../../$tarball", - DIE => "extraction failed\n"); - - if (-e "fixme.sh") { - print "Running fixme.sh...\n"; - xsystem ("sh -e fixme.sh", DIE => "fix script failed\n"); - } - - print "Patching...\n"; - xsystem ("patch -fs pintos/src/lib/debug.c < $GRADES_DIR/panic.diff", - LOG => "patch", - DIE => "patch failed\n"); - - open (CONSTANTS, ">pintos/src/constants.h") - or die "constants.h: create: $!\n"; - print CONSTANTS "#define THREAD_JOIN_IMPLEMENTED 1\n"; - close CONSTANTS; -} - -sub ext_mdyHMS { - my ($s) = @_; - my ($ms, $d, $y, $H, $M, $S) = - $s =~ /.([A-Za-z]+)\.(\d+)\.(\d+)\.(\d+)\.(\d+).(\d+)\.tar\.gz$/ - or die; - my ($m) = index ("janfebmaraprmayjunjulaugsepoctnovdec", lc $ms) / 3 - or die; - return sprintf "%02d-%02d-%02d %02d:%02d:%02d", $y, $m, $d, $H, $M, $S; -} - -sub test_source { - my ($test) = @_; - my ($src) = "$GRADES_DIR/$test.c"; - -e $src or die "$src: stat: $!\n"; - return $src; -} - -sub test_constants { - my ($defines) = ""; - return $defines; - } - -sub run_test { - my ($test) = @_; - - # Reuse older results if any - if (open (DONE, "; - chomp $status; - close (DONE); - return $status; - } - - # Really run the test. - my ($status) = really_run_test ($test); - - # Save the results for later. - open (DONE, ">output/$test/done") or die "output/$test/done: create: $!\n"; - print DONE "$status\n"; - close (DONE); - - return $status; -} - -sub compile { - print "Compiling...\n"; - xsystem ("cd pintos/src/filesys && make", LOG => "make") - or return "compile error"; -} - -sub really_run_test { - # Need to run it. - # If there's residue from an earlier test, move it to .old. - # If there's already a .old, delete it. - xsystem ("rm -rf output/$test.old", VERBOSE => 1) if -d "output/$test.old"; - rename "output/$test", "output/$test.old" or die "rename: $!\n" - if -d "output/$test"; - - # Make output directory. - mkdir "output/$test"; - my ($fs_size) = $test ne 'grow-too-big' ? 2 : .25; - xsystem ("pintos make-disk output/$test/fs.dsk $fs_size >/dev/null 2>&1", - DIE => "failed to create file system disk"); - xsystem ("pintos make-disk output/$test/swap.dsk 2 >/dev/null 2>&1", - DIE => "failed to create swap disk"); - - # Format disk, install test. - my ($pintos_base_cmd) = - "pintos " - . "--os-disk=pintos/src/filesys/build/os.dsk " - . "--fs-disk=output/$test/fs.dsk " - . "--swap-disk=output/$test/swap.dsk " - . "-v"; - unlink ("output/$test/fs.dsk", "output/$test/swap.dsk"), - return "format/put error" - if !xsystem ("$pintos_base_cmd put -f $GRADES_DIR/$test $test", - LOG => "$test/put", TIMEOUT => 60, EXPECT => 1); - - my (@extra_files); - push (@extra_files, "child-syn-read") if $test eq 'syn-read'; - push (@extra_files, "child-syn-wrt") if $test eq 'syn-write'; - push (@extra_files, "child-syn-rw") if $test eq 'syn-rw'; - for my $fn (@extra_files) { - return "format/put error" - if !xsystem ("$pintos_base_cmd put $GRADES_DIR/$fn $fn", - LOG => "$test/put-$fn", TIMEOUT => 60, EXPECT => 1); - } - - # Run. - my ($timeout) = 60; - my ($testargs) = defined ($args{$test}) ? " $args{$test}" : ""; - my ($result) = - xsystem ("$pintos_base_cmd run -q -ex \"$test$testargs\"", - LOG => "$test/run", TIMEOUT => $timeout, EXPECT => 1) - ? "ok" : "Bochs error"; - unlink ("output/$test/fs.dsk", "output/$test/swap.dsk"); - return $result; -} - -sub grade_test { - my ($test) = @_; - - my (@output) = snarf ("output/$test/run.out"); - - my ($grade_func) = "grade_$test"; - $grade_func =~ s/-/_/g; - if (-e "$GRADES_DIR/$test.exp" && !defined (&$grade_func)) { - eval { - verify_common (@output); - compare_output ("$GRADES_DIR/$test.exp", @output); - } - } else { - eval "$grade_func (\@output)"; - } - if ($@) { - die $@ if $@ =~ /at \S+ line \d+$/; - return $@; - } - return "ok"; -} sub grade_process_death { my ($proc_name, @output) = @_; @@ -392,7 +234,13 @@ EOF my (@failure) = grep (/FAIL/, @output); if (@failure != 0) { - die "Test failed: \"$failure[0]\"\n"; + eval { + my (@core) = get_core_output (@output); + $details{$test} = "Program output:\n\n" . join ('', map ("$_\n", @core)); + }; + my ($failure) = $failure[0]; + $failure =~ s/^\([^)]+\)\s+//; + die "Failed with message \"$failure\"\n"; } if (grep (/Pintos booting/, @output) > 1) { @@ -429,7 +277,6 @@ EOF sub get_core_output { my (@output) = @_; - our ($test); my ($first); for ($first = 0; $first <= $#output; $first++) { $first++, last if $output[$first] =~ /^Executing '$test.*':$/; @@ -604,7 +451,7 @@ sub write_grades { sub write_details { open (DETAILS, ">details.out"); my ($n) = 0; - for my $test (@TESTS) { + for $test (@TESTS) { next if $result{$test} eq 'ok' && !defined $details{$test}; my ($details) = $details{$test}; @@ -631,7 +478,7 @@ sub write_details { if (/PANIC/ && $panic++ > 0) { @output = @output[0...$i]; push (@output, - "[...details of recursive panic omitted...]"); + "[...details of recursive panic(s) omitted...]"); last; } } @@ -646,70 +493,6 @@ sub write_details { } -sub xsystem { - my ($command, %options) = @_; - print "$command\n" if $verbose || $options{VERBOSE}; - - my ($log) = $options{LOG}; - - my ($pid, $status); - eval { - local $SIG{ALRM} = sub { die "alarm\n" }; - alarm $options{TIMEOUT} if defined $options{TIMEOUT}; - $pid = fork; - die "fork: $!\n" if !defined $pid; - if (!$pid) { - if (defined $log) { - open (STDOUT, ">output/$log.out"); - open (STDERR, ">output/$log.err"); - } - exec ($command); - exit (-1); - } - waitpid ($pid, 0); - $status = $?; - alarm 0; - }; - - my ($ok); - if ($@) { - die unless $@ eq "alarm\n"; # propagate unexpected errors - print "Timed out: "; - for (my ($i) = 0; $i < 10; $i++) { - kill ('SIGTERM', $pid); - sleep (1); - my ($retval) = waitpid ($pid, WNOHANG); - last if $retval == $pid || $retval == -1; - print "Waiting for $pid to die" if $i == 0; - print "."; - } - $ok = 1; - } else { - if (WIFSIGNALED ($status)) { - my ($signal) = WTERMSIG ($status); - die "Interrupted\n" if $signal == SIGINT; - print "Child terminated with signal $signal\n"; - } - - my ($exp_status) = !defined ($options{EXPECT}) ? 0 : $options{EXPECT}; - $ok = WIFEXITED ($status) && WEXITSTATUS ($status) == $exp_status; - } - - - if (!$ok && defined $options{DIE}) { - my ($msg) = $options{DIE}; - if (defined ($log)) { - chomp ($msg); - $msg .= "; see output/$log.err and output/$log.out for details\n"; - } - die $msg; - } elsif (defined ($log) && $ok) { - unlink ("output/$log.err"); - } - - return $ok; -} - sub snarf { my ($file) = @_; open (OUTPUT, $file) or die "$file: open: $!\n"; diff --git a/grading/filesys/sm-create.exp b/grading/filesys/sm-create.exp index b2c53ba..4bc3b9a 100644 --- a/grading/filesys/sm-create.exp +++ b/grading/filesys/sm-create.exp @@ -1,5 +1,5 @@ (sm-create) begin -(sm-create) create testfile -(sm-create) open "testfile" for verification -(sm-create) close "testfile" +(sm-create) create "blargle" +(sm-create) open "blargle" for verification +(sm-create) close "blargle" (sm-create) end diff --git a/grading/filesys/sm-random.exp b/grading/filesys/sm-random.exp index aba5461..37adb48 100644 --- a/grading/filesys/sm-random.exp +++ b/grading/filesys/sm-random.exp @@ -4,3 +4,4 @@ (sm-random) write "bazzle" in random order (sm-random) read "bazzle" in random order (sm-random) close "bazzle" +(sm-random) end diff --git a/grading/filesys/syn-read.c b/grading/filesys/syn-read.c index cdc7f18..7d2ff3d 100644 --- a/grading/filesys/syn-read.c +++ b/grading/filesys/syn-read.c @@ -15,26 +15,14 @@ test_main (void) { pid_t children[CHILD_CNT]; int fd; - int i; - check (create (filename, sizeof buf), "create \"%s\"", filename); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK (create (filename, sizeof buf), "create \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); random_bytes (buf, sizeof buf); - check (write (fd, buf, sizeof buf) > 0, "write \"%s\"", filename); + CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", filename); msg ("close \"%s\"", filename); close (fd); - for (i = 0; i < CHILD_CNT; i++) - { - char cmd_line[128]; - snprintf (cmd_line, sizeof cmd_line, "child-syn-read %d", i); - check ((children[i] = exec (cmd_line)) != PID_ERROR, - "exec child %d of %d: \"%s\"", i + 1, (int) CHILD_CNT, cmd_line); - } - - for (i = 0; i < CHILD_CNT; i++) - { - int status = join (children[i]); - check (status == i, "join child %d of %d", i + 1, (int) CHILD_CNT); - } + exec_children ("child-syn-read", children, CHILD_CNT); + join_children (children, CHILD_CNT); } diff --git a/grading/filesys/syn-read.exp b/grading/filesys/syn-read.exp new file mode 100644 index 0000000..93bc2fb --- /dev/null +++ b/grading/filesys/syn-read.exp @@ -0,0 +1,26 @@ +(syn-read) begin +(syn-read) create "data" +(syn-read) open "data" +(syn-read) write "data" +(syn-read) close "data" +(syn-read) exec child 1 of 10: "child-syn-read 0" +(syn-read) exec child 2 of 10: "child-syn-read 1" +(syn-read) exec child 3 of 10: "child-syn-read 2" +(syn-read) exec child 4 of 10: "child-syn-read 3" +(syn-read) exec child 5 of 10: "child-syn-read 4" +(syn-read) exec child 6 of 10: "child-syn-read 5" +(syn-read) exec child 7 of 10: "child-syn-read 6" +(syn-read) exec child 8 of 10: "child-syn-read 7" +(syn-read) exec child 9 of 10: "child-syn-read 8" +(syn-read) exec child 10 of 10: "child-syn-read 9" +(syn-read) join child 1 of 10 returned 0 (expected 0) +(syn-read) join child 2 of 10 returned 1 (expected 1) +(syn-read) join child 3 of 10 returned 2 (expected 2) +(syn-read) join child 4 of 10 returned 3 (expected 3) +(syn-read) join child 5 of 10 returned 4 (expected 4) +(syn-read) join child 6 of 10 returned 5 (expected 5) +(syn-read) join child 7 of 10 returned 6 (expected 6) +(syn-read) join child 8 of 10 returned 7 (expected 7) +(syn-read) join child 9 of 10 returned 8 (expected 8) +(syn-read) join child 10 of 10 returned 9 (expected 9) +(syn-read) end diff --git a/grading/filesys/syn-remove.c b/grading/filesys/syn-remove.c index c89c0bc..a5fb86b 100644 --- a/grading/filesys/syn-remove.c +++ b/grading/filesys/syn-remove.c @@ -14,14 +14,14 @@ test_main (void) const char *filename = "deleteme"; int fd; - check (create (filename, 0), "create \"%s\"", filename); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); - check (remove (filename), "remove \"%s\"", filename); + CHECK (create (filename, 0), "create \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK (remove (filename), "remove \"%s\"", filename); random_bytes (buf1, sizeof buf1); - check (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", filename); + CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", filename); msg ("seek \"%s\" to 0", filename); seek (fd, 0); - check (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", filename); + CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", filename); compare_bytes (buf2, buf1, sizeof buf1, 0, filename); msg ("close \"%s\"", filename); close (fd); diff --git a/grading/filesys/syn-rw.c b/grading/filesys/syn-rw.c index 6c02b94..b87b063 100644 --- a/grading/filesys/syn-rw.c +++ b/grading/filesys/syn-rw.c @@ -16,30 +16,19 @@ test_main (void) pid_t children[CHILD_CNT]; size_t ofs; int fd; - int i; - check (create (filename, 0), "create \"%s\"", filename); - check ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK (create (filename, 0), "create \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); - for (i = 0; i < CHILD_CNT; i++) - { - char cmd_line[128]; - snprintf (cmd_line, sizeof cmd_line, "child-syn-rw %d", i); - check ((children[i] = exec (cmd_line)) != PID_ERROR, - "exec child %d of %d: \"%s\"", i + 1, (int) CHILD_CNT, cmd_line); - } + exec_children ("child-syn-rw", children, CHILD_CNT); random_bytes (buf, sizeof buf); quiet = true; for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE) - check (write (fd, buf + ofs, CHUNK_SIZE) > 0, + CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0, "write %d bytes at offset %zu in \"%s\"", (int) CHUNK_SIZE, ofs, filename); quiet = false; - for (i = 0; i < CHILD_CNT; i++) - { - int status = join (children[i]); - check (status == i, "join child %d of %d", i + 1, (int) CHILD_CNT); - } + join_children (children, CHILD_CNT); } diff --git a/grading/filesys/syn-write.c b/grading/filesys/syn-write.c index ee88c8b..c7eef3a 100644 --- a/grading/filesys/syn-write.c +++ b/grading/filesys/syn-write.c @@ -15,26 +15,14 @@ test_main (void) { pid_t children[CHILD_CNT]; int fd; - int i; - check (create (filename, sizeof buf1), "create \"%s\"", filename); + CHECK (create (filename, sizeof buf1), "create \"%s\"", filename); - for (i = 0; i < CHILD_CNT; i++) - { - char cmd_line[128]; - snprintf (cmd_line, sizeof cmd_line, "child-syn-wrt %d", i); - check ((children[i] = exec (cmd_line)) != PID_ERROR, - "exec child %d of %d: \"%s\"", i + 1, (int) CHILD_CNT, cmd_line); - } + exec_children ("child-syn-wrt", children, CHILD_CNT); + join_children (children, CHILD_CNT); - for (i = 0; i < CHILD_CNT; i++) - { - int status = join (children[i]); - check (status == i, "join child %d of %d", i + 1, (int) CHILD_CNT); - } - - check ((fd = open (filename)) > 1, "open \"%s\"", filename); - check (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", filename); + CHECK ((fd = open (filename)) > 1, "open \"%s\"", filename); + CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", filename); random_bytes (buf2, sizeof buf2); compare_bytes (buf1, buf2, sizeof buf1, 0, filename); } diff --git a/grading/filesys/syn-write.exp b/grading/filesys/syn-write.exp index 78ed950..6ce5a42 100644 --- a/grading/filesys/syn-write.exp +++ b/grading/filesys/syn-write.exp @@ -10,16 +10,16 @@ (syn-write) exec child 8 of 10: "child-syn-wrt 7" (syn-write) exec child 9 of 10: "child-syn-wrt 8" (syn-write) exec child 10 of 10: "child-syn-wrt 9" -(syn-write) join child 1 of 10 -(syn-write) join child 2 of 10 -(syn-write) join child 3 of 10 -(syn-write) join child 4 of 10 -(syn-write) join child 5 of 10 -(syn-write) join child 6 of 10 -(syn-write) join child 7 of 10 -(syn-write) join child 8 of 10 -(syn-write) join child 9 of 10 -(syn-write) join child 10 of 10 +(syn-write) join child 1 of 10 returned 0 (expected 0) +(syn-write) join child 2 of 10 returned 1 (expected 1) +(syn-write) join child 3 of 10 returned 2 (expected 2) +(syn-write) join child 4 of 10 returned 3 (expected 3) +(syn-write) join child 5 of 10 returned 4 (expected 4) +(syn-write) join child 6 of 10 returned 5 (expected 5) +(syn-write) join child 7 of 10 returned 6 (expected 6) +(syn-write) join child 8 of 10 returned 7 (expected 7) +(syn-write) join child 9 of 10 returned 8 (expected 8) +(syn-write) join child 10 of 10 returned 9 (expected 9) (syn-write) open "stuff" (syn-write) read "stuff" (syn-write) end diff --git a/grading/lib/Pintos/Grading.pm b/grading/lib/Pintos/Grading.pm index 91b0ab4..d0376ef 100644 --- a/grading/lib/Pintos/Grading.pm +++ b/grading/lib/Pintos/Grading.pm @@ -1,12 +1,279 @@ -package Pintos::Grading; -use Exporter 'import'; +use strict; +use warnings; -@EXPORT = qw($verbose $action); +our ($test); + +our ($GRADES_DIR); +our ($verbose); +our (%args); -use warnings; -use strict; use Getopt::Long; +use POSIX; + +# Source tarballs. + +# Extracts the group's source files into pintos/src, +# applies any patches providing in the grading directory, +# and installs a default pintos/src/constants.h +sub obtain_sources { + # Nothing to do if we already have a source tree. + return if -d "pintos"; + + my ($tarball) = choose_tarball (); + + # Extract sources. + print "Creating pintos/src...\n"; + mkdir "pintos" or die "pintos: mkdir: $!\n"; + mkdir "pintos/src" or die "pintos/src: mkdir: $!\n"; + + print "Extracting $tarball into pintos/src...\n"; + xsystem ("cd pintos/src && tar xzf ../../$tarball", + DIE => "extraction failed\n"); + + # Run custom script for this submission, if provided. + if (-e "fixme.sh") { + print "Running fixme.sh...\n"; + xsystem ("sh -e fixme.sh", DIE => "fix script failed\n"); + } else { + print "No fixme.sh, assuming no custom changes needed.\n"; + } + + # Apply patches from grading directory. + # Patches are applied in lexicographic order, so they should + # probably be named 00-debug.patch, 01-bitmap.patch, etc. + # Filenames in patches should be in the format pintos/src/... + print "Patching...\n"; + for my $patch (glob ("$GRADES_DIR/patches/*.patch")) { + my ($stem); + ($stem = $patch) =~ s%^$GRADES_DIR/patches/%% or die; + xsystem ("patch -fs -p0 < $patch", + LOG => $stem, DIE => "applying patch $stem failed\n"); + } + + # Install default pintos/src/constants.h. + open (CONSTANTS, ">pintos/src/constants.h") + or die "constants.h: create: $!\n"; + print CONSTANTS "#define THREAD_JOIN_IMPLEMENTED 1\n"; + close CONSTANTS; +} + +# Returns the name of the tarball to extract. +sub choose_tarball { + my (@tarballs) + = grep (/^[a-z0-9]+\.[A-Za-z]+\.\d+\.\d+\.\d+\.\d+.\d+\.tar\.gz$/, + glob ("*.tar.gz")); + die "no pintos dir, no files matching username.MMM.DD.YY.hh.mm.ss.tar.gz\n" + if scalar (@tarballs) == 0; + + if (@tarballs > 1) { + # Sort tarballs in order by time. + @tarballs = sort { ext_mdyHMS ($a) cmp ext_mdyHMS ($b) } @tarballs; + + print "Multiple tarballs:"; + print "\t$_ submitted ", ext_mdyHMS ($_), "\n" foreach @tarballs; + print "Choosing $tarballs[$#tarballs]\n"; + } + return $tarballs[$#tarballs]; +} + +# Extract the date within a tarball name into a string that compares +# lexicographically in chronological order. +sub ext_mdyHMS { + my ($s) = @_; + my ($ms, $d, $y, $H, $M, $S) = + $s =~ /.([A-Za-z]+)\.(\d+)\.(\d+)\.(\d+)\.(\d+).(\d+)\.tar\.gz$/ + or die; + my ($m) = index ("janfebmaraprmayjunjulaugsepoctnovdec", lc $ms) / 3 + or die; + return sprintf "%02d-%02d-%02d %02d:%02d:%02d", $y, $m, $d, $H, $M, $S; +} + +# Compiling. + +sub compile { + print "Compiling...\n"; + xsystem ("cd pintos/src/filesys && make", LOG => "make") + or return "compile error"; +} + + +sub xsystem { + my ($command, %options) = @_; + print "$command\n" if $verbose || $options{VERBOSE}; + + my ($log) = $options{LOG}; + + my ($pid, $status); + eval { + local $SIG{ALRM} = sub { die "alarm\n" }; + alarm $options{TIMEOUT} if defined $options{TIMEOUT}; + $pid = fork; + die "fork: $!\n" if !defined $pid; + if (!$pid) { + if (defined $log) { + open (STDOUT, ">output/$log.out"); + open (STDERR, ">output/$log.err"); + } + exec ($command); + exit (-1); + } + waitpid ($pid, 0); + $status = $?; + alarm 0; + }; + + my ($result); + if ($@) { + die unless $@ eq "alarm\n"; # propagate unexpected errors + my ($i); + for ($i = 0; $i < 10; $i++) { + kill ('SIGTERM', $pid); + sleep (1); + my ($retval) = waitpid ($pid, WNOHANG); + last if $retval == $pid || $retval == -1; + print "Timed out - Waiting for $pid to die" if $i == 0; + print "."; + } + print "\n" if $i; + $result = 'timeout'; + } else { + if (WIFSIGNALED ($status)) { + my ($signal) = WTERMSIG ($status); + die "Interrupted\n" if $signal == SIGINT; + print "Child terminated with signal $signal\n"; + } + + my ($exp_status) = !defined ($options{EXPECT}) ? 0 : $options{EXPECT}; + $result = WIFEXITED ($status) && WEXITSTATUS ($status) == $exp_status + ? "ok" : "error"; + } + + + if ($result eq 'error' && defined $options{DIE}) { + my ($msg) = $options{DIE}; + if (defined ($log)) { + chomp ($msg); + $msg .= "; see output/$log.err and output/$log.out for details\n"; + } + die $msg; + } elsif ($result ne 'error' && defined ($log)) { + unlink ("output/$log.err"); + } + + return $result; +} + +sub get_test_result { + my ($cache_file) = "output/$test/run.result"; + # Reuse older results if any. + if (open (RESULT, "<$cache_file")) { + my ($result); + $result = ; + chomp $result; + close (RESULT); + return $result; + } + + # If there's residue from an earlier test, move it to .old. + # If there's already a .old, delete it. + xsystem ("rm -rf output/$test.old", VERBOSE => 1) if -d "output/$test.old"; + rename "output/$test", "output/$test.old" or die "rename: $!\n" + if -d "output/$test"; + + # Run the test. + my ($result) = run_test ($test); + + # Save the results for later. + open (DONE, ">$cache_file") or die "$cache_file: create: $!\n"; + print DONE "$result\n"; + close (DONE); + + return $result; +} + +# Creates an output directory for the test, +# creates all the files needed +sub run_test { + # Make output directory. + mkdir "output/$test"; + + my ($fs_size) = $test ne 'grow-too-big' ? 2 : .25; + xsystem ("pintos make-disk output/$test/fs.dsk $fs_size >/dev/null 2>&1", + DIE => "failed to create file system disk"); + xsystem ("pintos make-disk output/$test/swap.dsk 2 >/dev/null 2>&1", + DIE => "failed to create swap disk"); + + # Format disk, install test. + my ($pintos_base_cmd) = + "pintos " + . "--os-disk=pintos/src/filesys/build/os.dsk " + . "--fs-disk=output/$test/fs.dsk " + . "--swap-disk=output/$test/swap.dsk " + . "-v"; + unlink ("output/$test/fs.dsk", "output/$test/swap.dsk"), + return "format/put error" + if xsystem ("$pintos_base_cmd put -f $GRADES_DIR/$test $test", + LOG => "$test/put", TIMEOUT => 60, EXPECT => 1) ne 'ok'; + + my (@extra_files); + push (@extra_files, "child-syn-read") if $test eq 'syn-read'; + push (@extra_files, "child-syn-wrt") if $test eq 'syn-write'; + push (@extra_files, "child-syn-rw") if $test eq 'syn-rw'; + for my $fn (@extra_files) { + return "format/put error" + if xsystem ("$pintos_base_cmd put $GRADES_DIR/$fn $fn", + LOG => "$test/put-$fn", TIMEOUT => 60, EXPECT => 1) + ne 'ok'; + } + + # Run. + my ($timeout) = 120; + my ($testargs) = defined ($args{$test}) ? " $args{$test}" : ""; + my ($retval) = + xsystem ("$pintos_base_cmd run -q -ex \"$test$testargs\"", + LOG => "$test/run", TIMEOUT => $timeout, EXPECT => 1); + my ($result); + if ($retval eq 'ok') { + $result = "ok"; + } elsif ($retval eq 'timeout') { + $result = "Timed out after $timeout seconds"; + } elsif ($retval eq 'error') { + $result = "Bochs error"; + } else { + die; + } + unlink ("output/$test/fs.dsk", "output/$test/swap.dsk"); + return $result; +} + +# Grade the test. +sub grade_test { + # Read test output. + my (@output) = snarf ("output/$test/run.out"); + # If there's a function "grade_$test", use it to evaluate the output. + # If there's a file "$GRADES_DIR/$test.exp", compare its contents + # against the output. + # (If both exist, prefer the function.) + my ($grade_func) = "grade_$test"; + $grade_func =~ s/-/_/g; + if (-e "$GRADES_DIR/$test.exp" && !defined (&$grade_func)) { + eval { + verify_common (@output); + compare_output ("$GRADES_DIR/$test.exp", @output); + } + } else { + eval "$grade_func (\@output)"; + } + if ($@) { + die $@ if $@ =~ /at \S+ line \d+$/; + return $@; + } + return "ok"; +} +sub c { + print "$test\n"; +} 1;