Begin restructuring test suite.
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 9 Dec 2004 02:46:18 +0000 (02:46 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 9 Dec 2004 02:46:18 +0000 (02:46 +0000)
36 files changed:
grading/filesys/child-syn-read.c
grading/filesys/child-syn-rw.c
grading/filesys/child-syn-wrt.c
grading/filesys/create.inc
grading/filesys/dir-empty-name.c
grading/filesys/dir-mk-vine.c
grading/filesys/dir-mkdir.c
grading/filesys/dir-open.c
grading/filesys/dir-over-file.c
grading/filesys/dir-rm-cwd-cd.c
grading/filesys/dir-rm-cwd.c
grading/filesys/dir-rm-parent.c
grading/filesys/dir-rm-root.c
grading/filesys/dir-rm-tree.c
grading/filesys/dir-rm-vine.c
grading/filesys/dir-rmdir.c
grading/filesys/dir-under-file.c
grading/filesys/fslib.c
grading/filesys/fslib.h
grading/filesys/grow-create.exp
grading/filesys/grow-dir.inc
grading/filesys/grow-sparse.c
grading/filesys/grow-too-big.c
grading/filesys/grow-two-files.c
grading/filesys/mk-tree.c
grading/filesys/random.inc
grading/filesys/run-tests
grading/filesys/sm-create.exp
grading/filesys/sm-random.exp
grading/filesys/syn-read.c
grading/filesys/syn-read.exp [new file with mode: 0644]
grading/filesys/syn-remove.c
grading/filesys/syn-rw.c
grading/filesys/syn-write.c
grading/filesys/syn-write.exp
grading/lib/Pintos/Grading.pm

index f4e49179f1178225285e817a59ad260f8f00681d..5bd739ec79f72a77b9f63a408d5ac206ea8a20e6 100644 (file)
@@ -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);
index 64e93c7a8a3d873582cc2a4fd18be74fd89fb0c3..7f0137aa94086782bb37e39eba4afd4499812df2 100644 (file)
@@ -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) 
index 2a5728153cfb708273873c2ac8d8ca30f9dcec53..0a43f5a7f008418eee3bf06fe21068df97289850 100644 (file)
@@ -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);
index 3e8a9b561ff316f4247e01b6ef26c6d72d26eabd..71b52fbae7842e133306b084890815a1beeec87c 100644 (file)
@@ -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);
 }
index 427eb2d23c37b806ad99957aab1af84e9c10d4d0..9d423c89f243883bb3ae253b98a5c4f03c323ecb 100644 (file)
@@ -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)");
 }
index c044b3aa9af61e361ce40cbd0ff9ae96e3b13a52..3bed83e9ddbbd65e51acfc77d802fa777c71112a 100644 (file)
@@ -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);
 }
 
index 85d243e43c317b9bc63914ac589588896f58a8ae..890af9d70193f84f1b8e02e86fc786c857573389 100644 (file)
@@ -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\"");
 }
 
index 40550ee81e1d8457922264e1fb17ca44f84dd7e4..37a400bb8f8b37b0005a3f5692f92b788b90c902 100644 (file)
@@ -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);
     }
 }
index 01347e14befb6530cd717a0cb8f4fab51a257e82..8bcb36e43507cde802954b2b3410dd4c495c9355 100644 (file)
@@ -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)");
 }
index f1916fbe1ccf6f5db93618ab1e558baf48aac3d2..94bc0a38bd24448e320ae9d8cf5f1e2823b10c1e 100644 (file)
@@ -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)");
 }
index 2754850a702050e5cde7afc1cb3d3c2091db54f0..f99983be6f333dc1ad729cb9a150db7290468173 100644 (file)
@@ -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)");
index 9a2136ed381e6dae0f8efeab99e5e9d89ae87129..f71895aebc88adaafa9ef2a784e56a6ba35db808 100644 (file)
@@ -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)");
index 2922824e4bd11e1038056c194f9737369f7ad90e..7489507561cc612651ce7392583afd00c70d8dd5 100644 (file)
@@ -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\"");
 }
index 24cc21f21f5217e2ca47fbb593f975266bc04d3b..9bfd8ba3f028352b65827b5e73339fd338211b78 100644 (file)
@@ -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);
 }
index 20b52a233e3486fdb6ad713cd1322daa28bfc30a..6259d3e54785a0ead9346c768c161b93c54d44c6 100644 (file)
@@ -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);
 }
index ed9be6424281a2b0d6a6891993df184fb4f3123d..ca5893daa277ac139e9d66aa63159616d09d1c9d 100644 (file)
@@ -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)");
 }
index 2565a272a92d46f12376a436697fdb94f2911c40..08cef94791ad82f769bd3afcb6a8927ec5b74d84 100644 (file)
@@ -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)");
 }
index d5eeda8c1ac220f60d6b631644c315ffe33f5c0e..78e168a96af36cadc723c326fd711b21922949b4 100644 (file)
@@ -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);
+    }
+}
index b1d654fda4b857a84451f14e5462472151de8537..ea3862490a3d028c807cfc3fcb15aae8097c6d27 100644 (file)
@@ -4,6 +4,7 @@
 #include <debug.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <syscall.h>
 
 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 */
index 0bfb05ff512a36cd8e6870e4137c7227aedd841c..8439e84fc6144f67a05bff4f43e73f4f07016b83 100644 (file)
@@ -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
index 86a471fe3b5d34dae583702c1ae095956777367d..4a764f16ce09dc25cad3c87635dd8b6186118e31 100644 (file)
@@ -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 ""
index 896b2facb5913d1ae0099fe5e4a5446ba660d867..c8b6a8f36a6948f568e247d5a201c03762774c8f 100644 (file)
@@ -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);
 }
index 5711693b2f5fbfd8e9f28911a2618ae8e8d87b01..bf8e3000ffd18c864f321504f19c4c6b16fe8270 100644 (file)
@@ -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);
 }
index aa6820a122cf7ac922e7766d5cad1b6f5bb32a81..e4b890464a67402c3f69230fae62e6045c2ff2bf 100644 (file)
@@ -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) 
index ed0e5c364e96a1018e632ce5d1a69cd446bc0d5d..aac52519da9ba9cea5c11156eed9f15323039a7f 100644 (file)
@@ -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);
 }
index 59f52f23d900df3027e460e9809dfa05aaf9c36f..895b65884694b41061b79996093add1fa49d1550 100644 (file)
@@ -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);
index 0f86dbf0e30fe80a791e699ef5751b09464bee82..d683f7ce1103d93b5fb59cf08c8618c4e550e871 100755 (executable)
@@ -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 ();
 \f
-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;
-}
-\f
-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, "<output/$test/done")) {
-       my ($status);
-       $status = <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";
-}
 \f
 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 {
 
 }
 \f
-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";
index b2c53bac3e29f5b5a74daf31d57d7045cbbcf418..4bc3b9a2db0611b4b91bad4fdc42549bda84e368 100644 (file)
@@ -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
index aba5461a75dbac27a39a10926120235d30977bf5..37adb48ff9f803342265dc2b279b625ea8cee901 100644 (file)
@@ -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
index cdc7f189b56dd3e91390b91c2c9d4ab40cd8b54c..7d2ff3da2ecdbd52cd5adfbbb28f1357f8f9f79c 100644 (file)
@@ -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 (file)
index 0000000..93bc2fb
--- /dev/null
@@ -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
index c89c0bc0b9d7d64d71ca4cbd51df7e8dba0ad6ea..a5fb86baf0a222ddefaacbceecc68cb6d4f20c8c 100644 (file)
@@ -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);
index 6c02b94f91f3605ce0217b85fcd027dc1c94240d..b87b0631d4732be6a8ffcf49a992d7a44a8520bf 100644 (file)
@@ -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);
 }
index ee88c8b5442fce15ea8536cc630f6a80cb32a2f5..c7eef3a58f24bebaffb5d96e9ce2189fc6306e20 100644 (file)
@@ -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);
 }
index 78ed9509fb6c833f3de9fd2f9998d5a4f3b4082e..6ce5a42a1a9d55c1740dab8ce7e1b8b92825019c 100644 (file)
 (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
index 91b0ab478ae1dd46600976e9b91540304ecbb9fd..d0376ef1c601111fac34d227e041e8cc54d34010 100644 (file)
-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;
+\f
+# 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;
+}
+\f
+# Compiling.
+
+sub compile {
+    print "Compiling...\n";
+    xsystem ("cd pintos/src/filesys && make", LOG => "make")
+       or return "compile error";
+}
+\f
+
+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;
+}
+\f
+sub get_test_result {
+    my ($cache_file) = "output/$test/run.result";
+    # Reuse older results if any.
+    if (open (RESULT, "<$cache_file")) {
+       my ($result);
+       $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;