Make tests public. Rewrite most tests. Add tests.
[pintos-anon] / src / tests / lib.c
diff --git a/src/tests/lib.c b/src/tests/lib.c
new file mode 100644 (file)
index 0000000..b1a1cbb
--- /dev/null
@@ -0,0 +1,195 @@
+#include "tests/lib.h"
+#include <random.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+const char *test_name;
+bool quiet = false;
+
+static void
+vmsg (const char *format, va_list args, const char *suffix) 
+{
+  /* We go to some trouble to stuff the entire message into a
+     single buffer and output it in a single system call, because
+     that'll (typically) ensure that it gets sent to the console
+     atomically.  Otherwise kernel messages like "foo: exit(0)"
+     can end up being interleaved if we're unlucky. */
+  static char buf[1024];
+
+  snprintf (buf, sizeof buf, "(%s) ", test_name);
+  vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args);
+  strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf));
+  write (STDOUT_FILENO, buf, strlen (buf));
+}
+
+void
+msg (const char *format, ...) 
+{
+  va_list args;
+
+  if (quiet)
+    return;
+  va_start (args, format);
+  vmsg (format, args, "\n");
+  va_end (args);
+}
+
+void
+fail (const char *format, ...) 
+{
+  va_list args;
+
+  va_start (args, format);
+  vmsg (format, args, ": FAILED\n");
+  va_end (args);
+
+  exit (1);
+}
+
+static void
+swap (void *a_, void *b_, size_t size) 
+{
+  uint8_t *a = a_;
+  uint8_t *b = b_;
+  size_t i;
+
+  for (i = 0; i < size; i++) 
+    {
+      uint8_t t = a[i];
+      a[i] = b[i];
+      b[i] = t;
+    }
+}
+
+void
+shuffle (void *buf_, size_t cnt, size_t size) 
+{
+  char *buf = buf_;
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = i + random_ulong () % (cnt - i);
+      swap (buf + i * size, buf + j * size, size);
+    }
+}
+
+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
+wait_children (pid_t pids[], size_t child_cnt) 
+{
+  size_t i;
+  
+  for (i = 0; i < child_cnt; i++) 
+    {
+      int status = wait (pids[i]);
+      CHECK (status == (int) i,
+             "wait for child %zu of %zu returned %d (expected %zu)",
+             i + 1, child_cnt, status, i);
+    }
+}
+
+void
+check_file_handle (int fd,
+                   const char *filename, const void *buf_, size_t size) 
+{
+  const char *buf = buf_;
+  size_t ofs = 0;
+  size_t file_size;
+
+  /* Warn about file of wrong size.  Don't fail yet because we
+     may still be able to get more information by reading the
+     file. */
+  file_size = filesize (fd);
+  if (file_size != size)
+    msg ("size of %s (%zu) differs from expected (%zu)",
+          filename, file_size, size);
+
+  /* Read the file block-by-block, comparing data as we go. */
+  while (ofs < size)
+    {
+      char block[512];
+      size_t block_size, ret_val;
+
+      block_size = size - ofs;
+      if (block_size > sizeof block)
+        block_size = sizeof block;
+
+      ret_val = read (fd, block, block_size);
+      if (ret_val != block_size)
+        fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu",
+              block_size, ofs, filename, ret_val);
+
+      compare_bytes (block, buf + ofs, block_size, ofs, filename);
+      ofs += block_size;
+    }
+
+  /* Now fail due to wrong file size. */
+  if (file_size != size)
+    fail ("size of %s (%zu) differs from expected (%zu)",
+          filename, file_size, size);
+
+  msg ("verified contents of \"%s\"", filename);
+}
+
+void
+check_file (const char *filename, const void *buf, size_t size) 
+{
+  int fd;
+
+  CHECK ((fd = open (filename)) > 1, "open \"%s\" for verification", filename);
+  check_file_handle (fd, filename, buf, size);
+  msg ("close \"%s\"", filename);
+  close (fd);
+}
+
+void
+compare_bytes (const void *read_data_, const void *expected_data_, size_t size,
+               size_t ofs, const char *filename) 
+{
+  const uint8_t *read_data = read_data_;
+  const uint8_t *expected_data = expected_data_;
+  size_t i, j;
+  size_t show_cnt;
+
+  if (!memcmp (read_data, expected_data, size))
+    return;
+  
+  for (i = 0; i < size; i++)
+    if (read_data[i] != expected_data[i])
+      break;
+  for (j = i + 1; j < size; j++)
+    if (read_data[j] == expected_data[j])
+      break;
+
+  quiet = false;
+  msg ("%zu bytes read starting at offset %zu in \"%s\" differ "
+       "from expected.", j - i, ofs + i, filename);
+  show_cnt = j - i;
+  if (j - i > 64) 
+    {
+      show_cnt = 64;
+      msg ("Showing first differing %zu bytes.", show_cnt);
+    }
+  msg ("Data actually read:");
+  hex_dump (ofs + i, read_data + i, show_cnt, true);
+  msg ("Expected data:");
+  hex_dump (ofs + i, expected_data + i, show_cnt, true);
+  fail ("%zu bytes read starting at offset %zu in \"%s\" differ "
+        "from expected", j - i, ofs, filename);
+}