(Perhaps we should count disk reads and writes, not time.)
- - Do we check that non-empty directories cannot be removed?
-
- Need lots more tests.
- - Add FS persistence test(s).
-
- Detect implementations that represent the cwd as a string, by
removing a directory that is the cwd of another process, then
creating a new directory of the same name and putting some files
in it, then checking whether the process that had it as cwd sees
them.
+ - dir-rm-cwd should have a related test that uses a separate process
+ to try to pin the directory as its cwd.
+
- Godmar: I'm not sure if I mentioned that already, but I passed all
tests for the filesys project without having implemented inode
deallocation. A test is needed that checks that blocks are
@menu
* File System New Code::
+* Testing File System Persistence::
@end menu
@node File System New Code
While most of your work will be in @file{filesys}, you should be
prepared for interactions with all previous parts.
+@node Testing File System Persistence
+@subsection Testing File System Persistence
+
+By now, you should be familiar with the basic process of running the
+Pintos tests. @xref{Testing}, for review, if necessary.
+
+Until now, each test invoked Pintos just once. However, an important
+purpose of a file system is to ensure that data remains accessible from
+one boot to another. Thus, the tests that are part of the file system
+project invoke Pintos a second time. The second run combines all the
+files and directories in the file system into a single file, then copies
+that file out of the Pintos file system into the host (Unix) file
+system.
+
+The grading scripts check the file system's correctness based on the
+contents of the file copied out in the second run. This means that your
+project will not pass any of the extended file system tests until the
+file system is implemented well enough to support @program{tar}, the
+Pintos user program that produces the file that is copied out. The
+@program{tar} program is fairly demanding (it requires both extensible
+file and subdirectory support), so this will take some work. Until
+then, you can ignore errors from @command{make check} regarding the
+extracted file system.
+
+Incidentally, as you may have surmised, the file format used for copying
+out the file system contents is the standard Unix ``tar'' format. You
+can use the Unix @command{tar} program to examine them. The tar file
+for test @var{t} is named @file{@var{t}.tar}.
+
@node Project 4 Requirements
@section Requirements
You must also support special file names @file{.} and @file{..}, which
have the same meanings as they do in Unix.
-Update the @code{remove} system call so that it can delete empty
-directories in addition to regular files. Directories may only be
-deleted if they do not contain any files or subdirectories (other than
-@file{.} and @file{..}).
-
Update the @code{open} system call so that it can also open directories.
Of the existing system calls, only @code{close} needs to accept a file
descriptor for a directory.
+Update the @code{remove} system call so that it can delete empty
+directories (other than the root) in addition to regular files.
+Directories may only be deleted if they do not contain any files or
+subdirectories (other than @file{.} and @file{..}). You may decide
+whether to allow deletion of a directory that is open by a process or in
+use as a process's current working directory. If it is allowed, then
+attempts to open files (including @file{.} and @file{..}) or create new
+files in a deleted directory must be disallowed.
+
Implement the following new system calls:
@deftypefn {System Call} bool chdir (const char *@var{dir})
PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS))
TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS))
-include ../../Makefile.userprog
-
-TIMEOUT = 60
-PINTOSFLAGS += -v -T $(TIMEOUT) \
- $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
-KERNELFLAGS += -q
-
-PINTOS = pintos $(SIMULATOR) $(PINTOSOPTS) $(PINTOSFLAGS) -- $(KERNELFLAGS)
-
OUTPUTS = $(addsuffix .output,$(TESTS))
ERRORS = $(addsuffix .errors,$(TESTS))
RESULTS = $(addsuffix .result,$(TESTS))
+ifdef PROGS
+include ../../Makefile.userprog
+endif
+
+TIMEOUT = 60
+
clean::
rm -f $(OUTPUTS) $(ERRORS) $(RESULTS)
$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog)))
$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES)))
-tests/%.output: RUNCMD = $(if $($*_ARGS),run '$(*F) $($*_ARGS)',run $(*F))
-tests/%.output: RUNREDIR = 2>$*.errors $(if $(VERBOSE),|tee,>) $*.output
-tests/%.output: RUNTEST = $(PINTOS) $(RUNCMD) $(RUNREDIR)
+$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test)))
+
+TESTCMD = pintos -v -T $(TIMEOUT)
+TESTCMD += $(SIMULATOR)
+TESTCMD += $(PINTOSOPTS)
+ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
+TESTCMD += --fs-disk=$(FSDISK)
+TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
+endif
+ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
+TESTCMD += --swap-disk=4
+endif
+TESTCMD += -- -q
+TESTCMD += $(KERNELFLAGS)
+ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
+TESTCMD += -f
+endif
+TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F))
+TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output
%.output: os.dsk
- $(RUNTEST)
+ $(TESTCMD)
%.result: %.ck %.output
perl -I$(SRCDIR) $< $* $@
(lg-create) close "blargle"
(lg-create) end
EOF
+pass;
(lg-full) close "quux"
(lg-full) end
EOF
+pass;
(lg-random) close "bazzle"
(lg-random) end
EOF
+pass;
(lg-seq-block) close "noodle"
(lg-seq-block) end
EOF
+pass;
(lg-seq-random) close "nibble"
(lg-seq-random) end
EOF
+pass;
(sm-create) close "blargle"
(sm-create) end
EOF
+pass;
(sm-full) close "quux"
(sm-full) end
EOF
+pass;
(sm-random) close "bazzle"
(sm-random) end
EOF
+pass;
(sm-seq-block) close "noodle"
(sm-seq-block) end
EOF
+pass;
(sm-seq-random) close "nibble"
(sm-seq-random) end
EOF
+pass;
(syn-read) wait for child 10 of 10 returned 9 (expected 9)
(syn-read) end
EOF
+pass;
(syn-remove) close "deleteme"
(syn-remove) end
EOF
+pass;
(syn-write) read "stuff"
(syn-write) end
EOF
+pass;
# -*- makefile -*-
tests/filesys/extended_TESTS = $(addprefix \
-tests/filesys/extended/,dir-empty-name dir-lsdir dir-mk-tree \
-dir-mk-vine dir-mkdir dir-open dir-over-file dir-rm-cwd-cd dir-rm-cwd \
-dir-rm-parent dir-rm-root dir-rm-tree dir-rm-vine dir-rmdir \
-dir-under-file grow-create grow-dir-lg grow-file-size grow-root-lg \
-grow-root-sm grow-seq-lg grow-seq-sm grow-sparse grow-tell \
-grow-too-big grow-two-files syn-rw)
+tests/filesys/extended/,dir-empty-name dir-mk-tree dir-mkdir dir-open \
+dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree \
+dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg \
+grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm \
+grow-sparse grow-tell grow-two-files syn-rw)
tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \
-tests/filesys/extended/child-syn-rw
+tests/filesys/extended/child-syn-rw tests/filesys/extended/tar
$(foreach prog,$(tests/filesys/extended_PROGS), \
$(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
$(foreach prog,$(tests/filesys/extended_TESTS), \
$(eval $(prog)_SRC += tests/main.c))
+$(foreach prog,$(tests/filesys/extended_TESTS), \
+ $(eval $(prog)_PUTFILES += tests/filesys/extended/tar))
+$(foreach test,$(tests/filesys/extended_TESTS), \
+ $(eval $(test).output: FSDISK = tmp.dsk))
+
tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c
tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c
-tests/filesys/extended/syn-rw_PUTFILES = tests/filesys/extended/child-syn-rw
+tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw
+
+
+GETTIMEOUT = 60
+
+GETCMD = pintos -v -T $(GETTIMEOUT)
+GETCMD += $(PINTOSOPTS)
+GETCMD += $(SIMULATOR)
+GETCMD += --fs-disk=$(FSDISK)
+GETCMD += -g fs.tar -a $(TEST).tar
+ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
+GETCMD += --swap-disk=4
+endif
+GETCMD += -- -q
+GETCMD += $(KERNELFLAGS)
+GETCMD += run 'tar fs.tar /'
+GETCMD += 2>> $(TEST).get-errors
+GETCMD += $(if $(VERBOSE),|tee -a,>>) $(TEST).get-output
+
+tests/filesys/extended/%.output: os.dsk
+ rm -f tmp.dsk
+ pintos-mkdisk tmp.dsk 2
+ $(TESTCMD)
+ $(GETCMD)
+ rm -f tmp.dsk
Functionality of extended file system:
- Test directory support.
1 dir-mkdir
-2 dir-mk-vine
3 dir-mk-tree
1 dir-rmdir
-2 dir-rm-vine
3 dir-rm-tree
-1 dir-lsdir
+5 dir-vine
- Test file growth.
1 grow-create
1 dir-over-file
1 dir-under-file
-1 dir-rm-cwd
-2 dir-rm-cwd-cd
+3 dir-rm-cwd
2 dir-rm-parent
1 dir-rm-root
-
-1 grow-too-big
(dir-empty-name) create "" (must return false)
(dir-empty-name) end
EOF
+check_archive ({});
+pass;
+++ /dev/null
-/* Lists the contents of a directory using readdir. */
-
-#include <syscall.h>
-#include "tests/lib.h"
-#include "tests/main.h"
-
-void
-test_main (void)
-{
- int fd;
- char name[READDIR_MAX_LEN + 1];
-
- CHECK ((fd = open (".")) > 1, "open .");
- CHECK (isdir (fd), "isdir(.)");
-
- while (readdir (fd, name))
- msg ("readdir: \"%s\"", name);
-
- msg ("close .");
- close (fd);
-}
+++ /dev/null
-# -*- perl -*-
-use strict;
-use warnings;
-use tests::tests;
-
-our ($test);
-my (@output) = read_text_file ("$test.output");
-
-common_checks (@output);
-@output = get_core_output (@output);
-
-must_contain_in_order (\@output,
- '(dir-lsdir) open .',
- '(dir-lsdir) isdir(.)',
- '(dir-lsdir) close .');
-
-sub must_contain_in_order {
- my ($output, @lines) = @_;
- my (@line_numbers) = map (find_line ($_, @$output), @lines);
- for my $i (0...$#lines - 1) {
- fail "\"$lines[$i]\" follows \"$lines[$i + 1]\" in output\n"
- if $line_numbers[$i] > $line_numbers[$i + 1];
- }
-}
-
-sub find_line {
- my ($line, @output) = @_;
- for my $i (0...$#output) {
- return $i if $line eq $output[$i];
- }
- fail "\"$line\" does not appear in output\n";
-}
-
-my (%count);
-for my $fn (map (/readdir: \"([^"]+)\"/, @output)) {
- fail "Unexpected file \"$fn\" in lsdir output\n"
- unless grep ($_ eq $fn, qw (dir-lsdir));
- fail "File \"$fn\" listed twice in lsdir output\n"
- if $count{$fn};
- $count{$fn}++;
-}
-fail "No files in lsdir output\n" if scalar (keys (%count)) == 0;
-fail "File \"dir-lsdir\" missing from lsdir output\n"
- if !$count{"dir-lsdir"};
-
-pass;
(dir-mk-tree) close "/0/2/0/3"
(dir-mk-tree) end
EOF
+my ($tree);
+for my $a (0...3) {
+ for my $b (0...2) {
+ for my $c (0...2) {
+ for my $d (0...3) {
+ $tree->{$a}{$b}{$c}{$d} = [''];
+ }
+ }
+ }
+}
+check_archive ($tree);
+pass;
+++ /dev/null
-/* Creates a "vine" of directories /0/1/2/3/4/5/6/7/8/9
- and changes directory into each of them,
- then creates a test file in the bottommost
- and verifies that it can be opened by absolute name. */
-
-#include <syscall.h>
-#include "tests/lib.h"
-#include "tests/main.h"
-
-void
-test_main (void)
-{
- const char *file_name = "/0/1/2/3/4/5/6/7/8/9/test";
- char dir[2];
-
- 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 (create ("test", 512), "create \"test\"");
- CHECK (chdir ("/"), "chdir \"/\"");
- CHECK (open (file_name) > 1, "open \"%s\"", file_name);
-}
-
+++ /dev/null
-# -*- perl -*-
-use strict;
-use warnings;
-use tests::tests;
-check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
-(dir-mk-vine) begin
-(dir-mk-vine) mkdir "0"
-(dir-mk-vine) chdir "0"
-(dir-mk-vine) mkdir "1"
-(dir-mk-vine) chdir "1"
-(dir-mk-vine) mkdir "2"
-(dir-mk-vine) chdir "2"
-(dir-mk-vine) mkdir "3"
-(dir-mk-vine) chdir "3"
-(dir-mk-vine) mkdir "4"
-(dir-mk-vine) chdir "4"
-(dir-mk-vine) mkdir "5"
-(dir-mk-vine) chdir "5"
-(dir-mk-vine) mkdir "6"
-(dir-mk-vine) chdir "6"
-(dir-mk-vine) mkdir "7"
-(dir-mk-vine) chdir "7"
-(dir-mk-vine) mkdir "8"
-(dir-mk-vine) chdir "8"
-(dir-mk-vine) mkdir "9"
-(dir-mk-vine) chdir "9"
-(dir-mk-vine) create "test"
-(dir-mk-vine) chdir "/"
-(dir-mk-vine) open "/0/1/2/3/4/5/6/7/8/9/test"
-(dir-mk-vine) end
-EOF
(dir-mkdir) open "b"
(dir-mkdir) end
EOF
+check_archive ({'a' => {'b' => ["\0" x 512]}});
+pass;
(dir-open) write "xyzzy"
dir-open: exit(-1)
EOF
+check_archive ({"xyzzy" => {}});
+pass;
(dir-over-file) create "abc" (must return false)
(dir-over-file) end
EOF
+check_archive ({"abc" => {}});
+pass;
+++ /dev/null
-/* Tries to remove the current directory.
- This is allowed to succeed or fail.
- If it succeeds, then it must not be possible to chdir back to
- the current directory by name (because it's been deleted).
- If it fails, then it must be possible to chdir back to the
- current directory by name (because it still exists). */
-
-#include <syscall.h>
-#include "tests/lib.h"
-#include "tests/main.h"
-
-void
-test_main (void)
-{
- CHECK (mkdir ("a"), "mkdir \"a\"");
- CHECK (chdir ("a"), "chdir \"a\"");
- msg ("remove \"/a\" (must not crash)");
- if (remove ("/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)");
-}
+++ /dev/null
-# -*- perl -*-
-use strict;
-use warnings;
-use tests::tests;
-check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF', <<'EOF']);
-(dir-rm-cwd-cd) begin
-(dir-rm-cwd-cd) mkdir "a"
-(dir-rm-cwd-cd) chdir "a"
-(dir-rm-cwd-cd) remove "/a" (must not crash)
-(dir-rm-cwd-cd) chdir "/a" (remove succeeded so this must return false)
-(dir-rm-cwd-cd) end
-EOF
-(dir-rm-cwd-cd) begin
-(dir-rm-cwd-cd) mkdir "a"
-(dir-rm-cwd-cd) chdir "a"
-(dir-rm-cwd-cd) remove "/a" (must not crash)
-(dir-rm-cwd-cd) chdir "/a" (remove failed so this must succeed)
-(dir-rm-cwd-cd) end
-EOF
-/* Tries to remove the current directory.
- This is allowed to succeed or fail.
- Then creates a file in the root to make sure that we haven't
- corrupted any directory state. */
+/* Tries to remove the current directory, which may succeed or
+ fail. The requirements in each case are different; refer to
+ the assignment for details. */
#include <syscall.h>
#include "tests/lib.h"
#include "tests/main.h"
+static int
+wrap_open (const char *name)
+{
+ static int fds[8], fd_cnt;
+ int fd, i;
+
+ CHECK ((fd = open (name)) > 1, "open \"%s\"", name);
+ for (i = 0; i < fd_cnt; i++)
+ if (fds[i] == fd)
+ fail ("fd returned is not unique");
+ fds[fd_cnt++] = fd;
+ return fd;
+}
+
void
test_main (void)
{
+ int root_fd, a_fd0;
+ char name[READDIR_MAX_LEN + 1];
+
+ root_fd = wrap_open ("/");
CHECK (mkdir ("a"), "mkdir \"a\"");
+
+ a_fd0 = wrap_open ("/a");
+ CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
+ CHECK (inumber (root_fd) != inumber (a_fd0),
+ "\"/\" and \"/a\" must have different inumbers");
+
CHECK (chdir ("a"), "chdir \"a\"");
- msg ("remove \"/a\" (must not crash)");
- remove ("/a");
- msg ("create \"b\" (must not crash)");
- create ("b", 123);
+
+ msg ("try to remove \"/a\"");
+ if (remove ("/a"))
+ {
+ msg ("remove successful");
+
+ CHECK (open ("/a") == -1, "open \"/a\" (must fail)");
+ CHECK (open (".") == -1, "open \".\" (must fail)");
+ CHECK (open ("..") == -1, "open \"..\" (must fail)");
+ CHECK (!create ("x", 512), "create \"x\" (must fail)");
+ }
+ else
+ {
+ int a_fd1, a_fd2, a_fd3;
+
+ msg ("remove failed");
+
+ CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)");
+ CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)");
+ CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)");
+
+ a_fd1 = wrap_open ("/a");
+ a_fd2 = wrap_open (".");
+ CHECK (inumber (a_fd1) == inumber (a_fd2),
+ "\"/a\" and \".\" must have same inumber");
+ CHECK (inumber (root_fd) != inumber (a_fd1),
+ "\"/\" and \"/a\" must have different inumbers");
+
+ CHECK (chdir ("/a"), "chdir \"/a\"");
+ a_fd3 = wrap_open (".");
+ CHECK (inumber (a_fd3) == inumber (a_fd1),
+ "\".\" must have same inumber as before");
+
+ CHECK (chdir ("/"), "chdir \"/\"");
+ CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)");
+ }
+ CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
}
use strict;
use warnings;
use tests::tests;
-check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1,
+ {NO => <<'EOF', YES => <<'EOF'});
(dir-rm-cwd) begin
+(dir-rm-cwd) open "/"
(dir-rm-cwd) mkdir "a"
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) "/" and "/a" must have different inumbers
(dir-rm-cwd) chdir "a"
-(dir-rm-cwd) remove "/a" (must not crash)
-(dir-rm-cwd) create "b" (must not crash)
+(dir-rm-cwd) try to remove "/a"
+(dir-rm-cwd) remove failed
+(dir-rm-cwd) try to remove "../a" (must fail)
+(dir-rm-cwd) try to remove ".././a" (must fail)
+(dir-rm-cwd) try to remove "/./a" (must fail)
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) open "."
+(dir-rm-cwd) "/a" and "." must have same inumber
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "/a"
+(dir-rm-cwd) open "."
+(dir-rm-cwd) "." must have same inumber as before
+(dir-rm-cwd) chdir "/"
+(dir-rm-cwd) try to remove "a" (must fail: still open)
+(dir-rm-cwd) verify "/a" is empty
(dir-rm-cwd) end
EOF
+(dir-rm-cwd) begin
+(dir-rm-cwd) open "/"
+(dir-rm-cwd) mkdir "a"
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "a"
+(dir-rm-cwd) try to remove "/a"
+(dir-rm-cwd) remove successful
+(dir-rm-cwd) open "/a" (must fail)
+(dir-rm-cwd) open "." (must fail)
+(dir-rm-cwd) open ".." (must fail)
+(dir-rm-cwd) create "x" (must fail)
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) end
+EOF
+
+check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}});
+pass;
-/* Tries to remove the change of parents of the current
- directory.
- This can succeed or fail as long as it doesn't crash. */
+/* Tries to remove a parent of the current directory. This must
+ fail, because that directory is non-empty. */
#include <syscall.h>
#include "tests/lib.h"
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)");
- remove ("/a");
+ CHECK (!remove ("/a"), "remove \"/a\" (must fail)");
}
(dir-rm-parent) chdir "a"
(dir-rm-parent) mkdir "b"
(dir-rm-parent) chdir "b"
-(dir-rm-parent) remove "/b" (must not crash)
-(dir-rm-parent) remove "/a" (must not crash)
+(dir-rm-parent) remove "/a" (must fail)
(dir-rm-parent) end
EOF
+check_archive ({"a" => {"b" => {}}});
+pass;
/* Try to remove the root directory.
- This must return failure. */
+ This must fail. */
#include <syscall.h>
#include "tests/lib.h"
void
test_main (void)
{
- CHECK (!remove ("/"), "remove \"/\" (must return false)");
+ CHECK (!remove ("/"), "remove \"/\" (must fail)");
CHECK (create ("/a", 243), "create \"/a\"");
}
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(dir-rm-root) begin
-(dir-rm-root) remove "/" (must return false)
+(dir-rm-root) remove "/" (must fail)
(dir-rm-root) create "/a"
(dir-rm-root) end
EOF
+check_archive ({"a" => ["\0" x 243]});
+pass;
(dir-rm-tree) open "/3/0/2/0" (must return -1)
(dir-rm-tree) end
EOF
+check_archive ({});
+pass;
+++ /dev/null
-/* Creates a "vine" of directories /0/1/2/3/4/5/6/7/8/9
- and changes directory into each of them,
- then remove them. */
-
-#include <string.h>
-#include <syscall.h>
-#include "tests/lib.h"
-#include "tests/main.h"
-
-void
-test_main (void)
-{
- const char *file_name = "/0/1/2/3/4/5/6/7/8/9/test";
- int fd;
- char tmp[128];
-
- 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 (create ("test", 512), "create \"test\"");
-
- CHECK (chdir ("/"), "chdir \"/\"");
- CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
- msg ("close \"%s\"", file_name);
- close (fd);
-
- strlcpy (tmp, file_name, sizeof tmp);
- while (strlen (tmp) > 0)
- {
- CHECK (remove (tmp), "remove \"%s\"", tmp);
- *strrchr (tmp, '/') = 0;
- }
-
- CHECK (open (file_name) == -1, "open \"%s\" (must return -1)", file_name);
-}
+++ /dev/null
-# -*- perl -*-
-use strict;
-use warnings;
-use tests::tests;
-check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
-(dir-rm-vine) begin
-(dir-rm-vine) mkdir "0"
-(dir-rm-vine) chdir "0"
-(dir-rm-vine) mkdir "1"
-(dir-rm-vine) chdir "1"
-(dir-rm-vine) mkdir "2"
-(dir-rm-vine) chdir "2"
-(dir-rm-vine) mkdir "3"
-(dir-rm-vine) chdir "3"
-(dir-rm-vine) mkdir "4"
-(dir-rm-vine) chdir "4"
-(dir-rm-vine) mkdir "5"
-(dir-rm-vine) chdir "5"
-(dir-rm-vine) mkdir "6"
-(dir-rm-vine) chdir "6"
-(dir-rm-vine) mkdir "7"
-(dir-rm-vine) chdir "7"
-(dir-rm-vine) mkdir "8"
-(dir-rm-vine) chdir "8"
-(dir-rm-vine) mkdir "9"
-(dir-rm-vine) chdir "9"
-(dir-rm-vine) create "test"
-(dir-rm-vine) chdir "/"
-(dir-rm-vine) open "/0/1/2/3/4/5/6/7/8/9/test"
-(dir-rm-vine) close "/0/1/2/3/4/5/6/7/8/9/test"
-(dir-rm-vine) remove "/0/1/2/3/4/5/6/7/8/9/test"
-(dir-rm-vine) remove "/0/1/2/3/4/5/6/7/8/9"
-(dir-rm-vine) remove "/0/1/2/3/4/5/6/7/8"
-(dir-rm-vine) remove "/0/1/2/3/4/5/6/7"
-(dir-rm-vine) remove "/0/1/2/3/4/5/6"
-(dir-rm-vine) remove "/0/1/2/3/4/5"
-(dir-rm-vine) remove "/0/1/2/3/4"
-(dir-rm-vine) remove "/0/1/2/3"
-(dir-rm-vine) remove "/0/1/2"
-(dir-rm-vine) remove "/0/1"
-(dir-rm-vine) remove "/0"
-(dir-rm-vine) open "/0/1/2/3/4/5/6/7/8/9/test" (must return -1)
-(dir-rm-vine) end
-EOF
(dir-rmdir) chdir "a" (must return false)
(dir-rmdir) end
EOF
+check_archive ({});
+pass;
(dir-under-file) mkdir "abc" (must return false)
(dir-under-file) end
EOF
+check_archive ({"abc" => ['']});
+pass;
--- /dev/null
+/* Create a very deep "vine" of directories: /dir0/dir1/dir2/...
+ and an ordinary file in each of them, until we fill up the
+ disk.
+
+ Then delete most of them, for two reasons. First, "tar"
+ limits file names to 100 characters (which could be extended
+ to 256 without much trouble). Second, a full disk has no room
+ for the tar archive. */
+
+#include <string.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+ int i;
+
+ msg ("creating many levels of files and directories...");
+ quiet = true;
+ CHECK (mkdir ("start"), "mkdir \"start\"");
+ CHECK (chdir ("start"), "chdir \"start\"");
+ for (i = 0; ; i++)
+ {
+ char name[3][READDIR_MAX_LEN + 1];
+ char file_name[16], dir_name[16];
+ char contents[128];
+ int fd;
+
+ /* Create file. */
+ snprintf (file_name, sizeof file_name, "file%d", i);
+ if (!create (file_name, 0))
+ break;
+ CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+ snprintf (contents, sizeof contents, "contents %d\n", i);
+ if (write (fd, contents, strlen (contents)) != (int) strlen (contents))
+ {
+ CHECK (remove (file_name), "remove \"%s\"", file_name);
+ close (fd);
+ break;
+ }
+ close (fd);
+
+ /* Create directory. */
+ snprintf (dir_name, sizeof dir_name, "dir%d", i);
+ if (!mkdir (dir_name))
+ {
+ CHECK (remove (file_name), "remove \"%s\"", file_name);
+ break;
+ }
+
+ /* Check for file and directory. */
+ CHECK ((fd = open (".")) > 1, "open \".\"");
+ CHECK (readdir (fd, name[0]), "readdir \".\"");
+ CHECK (readdir (fd, name[1]), "readdir \".\"");
+ CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)");
+ CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name))
+ || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)),
+ "names should be \"%s\" and \"%s\", "
+ "actually \"%s\" and \"%s\"",
+ file_name, dir_name, name[0], name[1]);
+ close (fd);
+
+ /* Descend into directory. */
+ CHECK (chdir (dir_name), "chdir \"%s\"", dir_name);
+ }
+ CHECK (i > 200, "created files and directories only to level %d", i);
+ quiet = false;
+
+ msg ("removing all but top 10 levels of files and directories...");
+ quiet = true;
+ while (i-- > 10)
+ {
+ char file_name[16], dir_name[16];
+
+ snprintf (file_name, sizeof file_name, "file%d", i);
+ snprintf (dir_name, sizeof dir_name, "dir%d", i);
+ CHECK (chdir (".."), "chdir \"..\"");
+ CHECK (remove (dir_name), "remove \"%s\"", dir_name);
+ CHECK (remove (file_name), "remove \"%s\"", file_name);
+ }
+ quiet = false;
+}
--- /dev/null
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-vine) begin
+(dir-vine) creating many levels of files and directories...
+(dir-vine) removing all but top 10 levels of files and directories...
+(dir-vine) end
+EOF
+
+# The archive should look like this:
+#
+# -rw-r--r-- 0/0 40642 2006-01-01 00:00:00 dir-vine
+# -rw-r--r-- 0/0 42479 2006-01-01 00:00:00 tar
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/file0
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/file1
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/file2
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/dir2/file3
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/file4
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/file5
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/file6
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8
+# -rw-r--r-- 0/0 11 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9
+# drwxr-xr-x 0/0 0 2006-01-01 00:00:00 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9
+my ($dir) = {};
+my ($root) = {"start" => $dir};
+for (my ($i) = 0; $i < 10; $i++) {
+ $dir->{"file$i"} = ["contents $i\n"];
+ $dir = $dir->{"dir$i"} = {};
+}
+check_archive ($root);
+pass;
(grow-create) close "blargle"
(grow-create) end
EOF
+check_archive ({"blargle" => ['']});
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-dir-lg) begin
(grow-dir-lg) mkdir /x
(grow-dir-lg) creating and checking "/x/file49"
(grow-dir-lg) end
EOF
+my ($fs);
+$fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49;
+check_archive ($fs);
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-file-size) begin
(grow-file-size) create "testfile"
(grow-file-size) close "testfile"
(grow-file-size) end
EOF
+my ($string);
+check_archive ({"testfile" => [random_bytes (2134)]});
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-root-lg) begin
(grow-root-lg) creating and checking "file0"
(grow-root-lg) creating and checking "file49"
(grow-root-lg) end
EOF
+my ($fs);
+$fs->{"file$_"} = [random_bytes (512)] foreach 0...49;
+check_archive ($fs);
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-root-sm) begin
(grow-root-sm) creating and checking "file0"
(grow-root-sm) creating and checking "file19"
(grow-root-sm) end
EOF
+my ($fs);
+$fs->{"file$_"} = [random_bytes (512)] foreach 0...19;
+check_archive ($fs);
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-seq-lg) begin
(grow-seq-lg) create "testme"
(grow-seq-lg) close "testme"
(grow-seq-lg) end
EOF
+check_archive ({"testme" => [random_bytes (72943)]});
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-seq-sm) begin
(grow-seq-sm) create "testme"
(grow-seq-sm) close "testme"
(grow-seq-sm) end
EOF
+check_archive ({"testme" => [random_bytes (5678)]});
+pass;
(grow-sparse) close "testfile"
(grow-sparse) end
EOF
+check_archive ({"testfile" => ["\0" x 76543]});
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-tell) begin
(grow-tell) create "foobar"
(grow-tell) close "foobar"
(grow-tell) end
EOF
+check_archive ({"foobar" => [random_bytes (2134)]});
+pass;
+++ /dev/null
-/* Checks that trying to grow a file beyond the capacity of the
- file system behaves reasonably (does not crash). */
-
-#include <limits.h>
-#include <syscall.h>
-#include "tests/lib.h"
-#include "tests/main.h"
-
-void
-test_main (void)
-{
- const char *file_name = "fumble";
- char zero = 0;
- int fd;
-
- CHECK (create (file_name, 0), "create \"%s\"", file_name);
- CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
- msg ("seek \"%s\"", file_name);
- seek (fd, UINT_MAX);
- msg ("write \"%s\"", file_name);
- write (fd, &zero, 1);
- msg ("close \"%s\"", file_name);
- close (fd);
-}
+++ /dev/null
-# -*- perl -*-
-use strict;
-use warnings;
-use tests::tests;
-check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
-(grow-too-big) begin
-(grow-too-big) create "fumble"
-(grow-too-big) open "fumble"
-(grow-too-big) seek "fumble"
-(grow-too-big) write "fumble"
-(grow-too-big) close "fumble"
-(grow-too-big) end
-EOF
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(grow-two-files) begin
(grow-two-files) create "a"
(grow-two-files) close "b"
(grow-two-files) end
EOF
+my ($a) = random_bytes (8143);
+my ($b) = random_bytes (8143);
+check_archive ({"a" => [$a], "b" => [$b]});
+pass;
use strict;
use warnings;
use tests::tests;
+use tests::random;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(syn-rw) begin
(syn-rw) create "logfile"
(syn-rw) wait for child 4 of 4 returned 3 (expected 3)
(syn-rw) end
EOF
+check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw",
+ "logfile" => [random_bytes (8 * 512)]});
+pass;
--- /dev/null
+/* tar.c
+
+ Creates a tar archive. */
+
+#include <syscall.h>
+#include <stdio.h>
+#include <string.h>
+
+static void usage (void);
+static bool make_tar_archive (const char *archive_name,
+ char *files[], size_t file_cnt);
+
+int
+main (int argc, char *argv[])
+{
+ if (argc < 3)
+ usage ();
+
+ return (make_tar_archive (argv[1], argv + 2, argc - 2)
+ ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void
+usage (void)
+{
+ printf ("tar, tar archive creator\n"
+ "Usage: tar ARCHIVE FILE...\n"
+ "where ARCHIVE is the tar archive to create\n"
+ " and FILE... is a list of files or directories to put into it.\n"
+ "(ARCHIVE itself will not be included in the archive, even if it\n"
+ "is in a directory to be archived.)\n");
+ exit (EXIT_FAILURE);
+}
+\f
+static bool archive_file (char file_name[], size_t file_name_size,
+ int archive_fd, bool *write_error);
+
+static bool archive_ordinary_file (const char *file_name, int file_fd,
+ int archive_fd, bool *write_error);
+static bool archive_directory (char file_name[], size_t file_name_size,
+ int file_fd, int archive_fd, bool *write_error);
+static bool write_header (const char *file_name,
+ char type_flag, int size, unsigned mode,
+ int archive_fd, bool *write_error);
+
+static bool do_write (int fd, const char *buffer, int size, bool *write_error);
+
+static bool
+make_tar_archive (const char *archive_name, char *files[], size_t file_cnt)
+{
+ static const char zeros[512];
+ int archive_fd;
+ bool success;
+ bool write_error = false;
+ size_t i;
+
+ if (!create (archive_name, 0))
+ {
+ printf ("%s: create failed\n", archive_name);
+ return false;
+ }
+ archive_fd = open (archive_name);
+ if (archive_fd < 0)
+ {
+ printf ("%s: open failed\n", archive_name);
+ return false;
+ }
+
+ for (i = 0; i < file_cnt; i++)
+ {
+ char file_name[128];
+
+ strlcpy (file_name, files[i], sizeof file_name);
+ if (!archive_file (file_name, sizeof file_name,
+ archive_fd, &write_error))
+ success = false;
+ }
+
+ if (!do_write (archive_fd, zeros, 512, &write_error)
+ || !do_write (archive_fd, zeros, 512, &write_error))
+ success = false;
+
+ close (archive_fd);
+
+ return success;
+}
+
+static bool
+archive_file (char file_name[], size_t file_name_size,
+ int archive_fd, bool *write_error)
+{
+ int file_fd = open (file_name);
+ if (file_fd >= 0)
+ {
+ bool success;
+
+ if (inumber (file_fd) != inumber (archive_fd))
+ {
+ if (!isdir (file_fd))
+ success = archive_ordinary_file (file_name, file_fd,
+ archive_fd, write_error);
+ else
+ success = archive_directory (file_name, file_name_size, file_fd,
+ archive_fd, write_error);
+ }
+ else
+ {
+ /* Nothing to do: don't try to archive the archive file. */
+ success = true;
+ }
+
+ close (file_fd);
+
+ return success;
+ }
+ else
+ {
+ printf ("%s: open failed\n", file_name);
+ return false;
+ }
+}
+
+static bool
+archive_ordinary_file (const char *file_name, int file_fd,
+ int archive_fd, bool *write_error)
+{
+ bool read_error = false;
+ bool success = true;
+ int file_size = filesize (file_fd);
+
+ if (!write_header (file_name, '0', file_size, 0644, archive_fd, write_error))
+ return false;
+
+ while (file_size > 0)
+ {
+ static char buf[512];
+ int chunk_size = file_size > 512 ? 512 : file_size;
+ int read_retval = read (file_fd, buf, chunk_size);
+ int bytes_read = read_retval > 0 ? read_retval : 0;
+
+ if (bytes_read != chunk_size && !read_error)
+ {
+ printf ("%s: read error\n", file_name);
+ read_error = true;
+ success = false;
+ }
+
+ memset (buf + bytes_read, 0, 512 - bytes_read);
+ if (!do_write (archive_fd, buf, 512, write_error))
+ success = false;
+
+ file_size -= chunk_size;
+ }
+
+ return success;
+}
+
+static bool
+archive_directory (char file_name[], size_t file_name_size, int file_fd,
+ int archive_fd, bool *write_error)
+{
+ size_t dir_len;
+ bool success = true;
+
+ dir_len = strlen (file_name);
+ if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size)
+ {
+ printf ("%s: file name too long\n", file_name);
+ return false;
+ }
+
+ if (!write_header (file_name, '5', 0, 0755, archive_fd, write_error))
+ return false;
+
+ file_name[dir_len] = '/';
+ while (readdir (file_fd, &file_name[dir_len + 1]))
+ if (!archive_file (file_name, file_name_size, archive_fd, write_error))
+ success = false;
+ file_name[dir_len] = '\0';
+
+ return success;
+}
+
+static bool
+write_header (const char *file_name,
+ char type_flag, int size, unsigned mode,
+ int archive_fd, bool *write_error)
+{
+ static char header[512];
+ unsigned chksum;
+ size_t i;
+
+ memset (header, 0, sizeof header);
+
+/* Drop confusing and possibly dangerous prefixes from
+ FILE_NAME. */
+ while (*file_name == '/'
+ || !memcmp (file_name, "./", 2)
+ || !memcmp (file_name, "../", 3))
+ file_name = strchr (file_name, '/') + 1;
+ if (*file_name == '\0')
+ {
+ /* Dropped *everything* from FILE_NAME.
+ Should only be possible for a directory. */
+ ASSERT (type_flag == '5');
+ return true;
+ }
+ else if (strlen (file_name) > 99)
+ {
+ printf ("%s: file name too long\n", file_name);
+ return false;
+ }
+
+ /* Fill in header except for final checksum. */
+ strlcpy (header, file_name, 100); /* name */
+ snprintf (header + 100, 8, "%07o", mode); /* mode */
+ strlcpy (header + 108, "0000000", 8); /* uid */
+ strlcpy (header + 116, "0000000", 8); /* gid */
+ snprintf (header + 124, 12, "%011o", size); /* size */
+ snprintf (header + 136, 12, "%011o", 1136102400); /* mtime (2006-01-01) */
+ memset (header + 148, ' ', 8); /* chksum */
+ header[156] = type_flag; /* typeflag */
+ strlcpy (header + 257, "ustar", 6); /* magic */
+ strlcpy (header + 263, "00", 3); /* version */
+
+ /* Compute and fill in final checksum. */
+ chksum = 0;
+ for (i = 0; i < 512; i++)
+ chksum += (uint8_t) header[i];
+ snprintf (header + 148, 8, "%07o", chksum);
+
+ /* Write header. */
+ return do_write (archive_fd, header, 512, write_error);
+}
+
+static bool
+do_write (int fd, const char *buffer, int size, bool *write_error)
+{
+ if (write (fd, buffer, size) == size)
+ return true;
+ else
+ {
+ if (!*write_error)
+ {
+ printf ("error writing archive\n");
+ *write_error = true;
+ }
+ return false;
+ }
+}
use warnings;
use tests::Algorithm::Diff;
use File::Temp 'tempfile';
+use Fcntl qw(SEEK_SET SEEK_CUR);
sub fail;
sub pass;
my ($msg_file) = tempfile ();
select ($msg_file);
+\f
+# Generic testing.
sub check_expected {
my ($expected) = pop @_;
my (@options) = @_;
my (@output) = read_text_file ("$test.output");
- common_checks (@output);
- compare_output (@options, \@output, $expected);
+ common_checks ("run", @output);
+ compare_output ("run", @options, \@output, $expected);
}
sub common_checks {
- my (@output) = @_;
+ my ($run, @output) = @_;
- fail "No output at all\n" if @output == 0;
+ fail "\u$run produced no output at all\n" if @output == 0;
- check_for_panic (@output);
- check_for_keyword ("FAIL", @output);
- check_for_triple_fault (@output);
- check_for_keyword ("TIMEOUT", @output);
+ check_for_panic ($run, @output);
+ check_for_keyword ($run, "FAIL", @output);
+ check_for_triple_fault ($run, @output);
+ check_for_keyword ($run, "TIMEOUT", @output);
- fail "Didn't start up properly: no \"Pintos booting\" startup message\n"
+ fail "\u$run didn't start up properly: no \"Pintos booting\" message\n"
if !grep (/Pintos booting with.*kB RAM\.\.\./, @output);
- fail "Didn't start up properly: no \"Boot complete\" startup message\n"
+ fail "\u$run didn't start up properly: no \"Boot complete\" message\n"
if !grep (/Boot complete/, @output);
- fail "Didn't shut down properly: no \"Timer: # ticks\" shutdown message\n"
+ fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n"
if !grep (/Timer: \d+ ticks/, @output);
- fail "Didn't shut down properly: no \"Powering off\" shutdown message\n"
+ fail "\u$run didn't shut down properly: no \"Powering off\" message\n"
if !grep (/Powering off/, @output);
}
sub check_for_panic {
- my (@output) = @_;
+ my ($run, @output) = @_;
my ($panic) = grep (/PANIC/, @output);
return unless defined $panic;
- print "Kernel panic: ", substr ($panic, index ($panic, "PANIC")), "\n";
+ print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")),
+ "\n";
my (@stack_line) = grep (/Call stack:/, @output);
if (@stack_line != 0) {
}
sub check_for_keyword {
- my ($keyword, @output) = @_;
+ my ($run, $keyword, @output) = @_;
my ($kw_line) = grep (/$keyword/, @output);
return unless defined $kw_line;
# Most output lines are prefixed by (test-name). Eliminate this
# from our message for brevity.
$kw_line =~ s/^\([^\)]+\)\s+//;
- print "$kw_line\n";
+ print "$run: $kw_line\n";
fail;
}
sub check_for_triple_fault {
- my (@output) = @_;
+ my ($run, @output) = @_;
- return unless grep (/Pintos booting/, @output) > 1;
+ my ($reboots) = grep (/Pintos booting/, @output) - 1;
+ return unless $reboots > 0;
print <<EOF;
-Pintos spontaneously rebooted during this test.
+\u$run spontaneously rebooted $reboots times.
This is most often caused by unhandled page faults.
EOF
# Get @output without header or trailer.
sub get_core_output {
+ my ($run, @output) = @_;
my ($p);
- do { $p = shift; } while (defined ($p) && $p !~ /^Executing '.*':$/);
- do { $p = pop; } while (defined ($p) && $p !~ /^Execution of '.*' complete.$/);
- return @_;
+
+ my ($process);
+ my ($start);
+ for my $i (0...$#_) {
+ $start = $i + 1, last
+ if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/;
+ }
+
+ my ($end);
+ for my $i ($start...$#output) {
+ $end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/;
+ }
+
+ fail "\u$run didn't start a thread or process\n" if !defined $start;
+ fail "\u$run started '$process' but it never finished\n" if !defined $end;
+
+ return @output[$start...$end];
}
sub compare_output {
+ my ($run) = shift @_;
my ($expected) = pop @_;
my ($output) = pop @_;
my (%options) = @_;
- my (@output) = get_core_output (@$output);
- fail "'$test' didn't run or didn't produce any output\n" if !@output;
+ my (@output) = get_core_output ($run, @$output);
+ fail "\u$run didn't produce any output" if !@output;
if (exists $options{IGNORE_EXIT_CODES}) {
delete $options{IGNORE_EXIT_CODES};
my ($msg);
# Compare actual output against each allowed output.
- foreach my $exp_string (@$expected) {
- my (@expected) = split ("\n", $exp_string);
+ if (ref ($expected) eq 'ARRAY') {
+ my ($i) = 0;
+ $expected = {map ((++$i => $_), @$expected)};
+ }
+ foreach my $key (keys %$expected) {
+ my (@expected) = split ("\n", $expected->{$key});
$msg .= "Acceptable output:\n";
$msg .= join ('', map (" $_\n", @expected));
for (my ($i) = 0; $i <= $#expected; $i++) {
$eq = 0 if $output[$i] ne $expected[$i];
}
- pass if $eq;
+ return $key if $eq;
}
# They differ. Output a diff.
# Failed to match. Report failure.
fail "Test output failed to match any acceptable form.\n\n$msg";
}
+\f
+# File system extraction.
+
+# check_archive (\%CONTENTS)
+#
+# Checks that the extracted file system's contents match \%CONTENTS.
+# Each key in the hash is a file name. Each value may be:
+#
+# - $FILE: Name of a host file containing the expected contents.
+#
+# - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE
+# comprising the $LENGTH bytes starting at $OFFSET.
+#
+# - [$CONTENTS]: The literal expected file contents, as a string.
+#
+# - {SUBDIR}: A subdirectory, in the same form described here,
+# recursively.
+sub check_archive {
+ my ($expected_hier) = @_;
+ my (@output) = read_text_file ("$test.get-output");
+ common_checks ("file system extraction run", @output);
+
+ @output = get_core_output ("file system extraction run", @output);
+ @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output);
+ fail join ("\n", "Error extracting file system:", @output) if @output;
+
+ my ($test_base_name) = $test;
+ $test_base_name =~ s%.*/%%;
+ $expected_hier->{$test_base_name} = $test;
+ $expected_hier->{'tar'} = 'tests/filesys/extended/tar';
+
+ my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, ""));
+ my (%actual) = read_tar ("$test.tar");
+
+ my ($errors) = 0;
+ foreach my $name (sort keys %expected) {
+ if (exists $actual{$name}) {
+ if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) {
+ print "$name is a directory but should be an ordinary file.\n";
+ $errors++;
+ } elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) {
+ print "$name is an ordinary file but should be a directory.\n";
+ $errors++;
+ }
+ } else {
+ print "$name is missing from the file system.\n";
+ $errors++;
+ }
+ }
+ foreach my $name (sort keys %actual) {
+ if (!exists $expected{$name}) {
+ if ($name =~ /^[[:print:]]+$/) {
+ print "$name exists in the file system but it should not.\n";
+ } else {
+ my ($esc_name) = $name;
+ $esc_name =~ s/[^[:print:]]/./g;
+ print <<EOF;
+$esc_name exists in the file system but should not. (The expected name
+of this file contains unusual characters that were printed as `.'.)
+EOF
+ }
+ $errors++;
+ }
+ }
+ if ($errors) {
+ print "\nActual contents of file system:\n";
+ print_fs (%actual);
+ print "\nExpected contents of file system:\n";
+ print_fs (%expected);
+ } else {
+ foreach my $name (sort keys %expected) {
+ if (!is_dir ($expected{$name})) {
+ my ($exp_file, $exp_length) = open_file ($expected{$name});
+ my ($act_file, $act_length) = open_file ($actual{$name});
+ $errors += !compare_files ($exp_file, $exp_length,
+ $act_file, $act_length, $name,
+ !$errors);
+ close ($exp_file);
+ close ($act_file);
+ }
+ }
+ }
+ fail "Extracted file system contents are not correct.\n" if $errors;
+}
+
+# open_file ([$FILE, $OFFSET, $LENGTH])
+# open_file ([$CONTENTS])
+#
+# Opens a file for the contents passed in, which must be in one of
+# the two above forms that correspond to check_archive() arguments.
+#
+# Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and
+# $LENGTH is the number of bytes in the file's content.
+sub open_file {
+ my ($value) = @_;
+ die if ref ($value) ne 'ARRAY';
+
+ my ($file) = tempfile ();
+ my ($length);
+ if (@$value == 1) {
+ $length = length ($value->[0]);
+ $file = tempfile ();
+ syswrite ($file, $value->[0]) == $length
+ or die "writing temporary file: $!\n";
+ sysseek ($file, 0, SEEK_SET);
+ } elsif (@$value == 3) {
+ $length = $value->[2];
+ open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n";
+ die "$value->[0]: file is smaller than expected\n"
+ if -s $file < $value->[1] + $length;
+ sysseek ($file, $value->[1], SEEK_SET);
+ } else {
+ die;
+ }
+ return ($file, $length);
+}
+
+# compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE)
+#
+# Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B.
+# ($A and $B are handles.)
+# If their contents differ, prints a brief message describing
+# the differences, using $NAME to identify the file.
+# The message contains more detail if $VERBOSE is nonzero.
+# Returns 1 if the contents are identical, 0 otherwise.
+sub compare_files {
+ my ($a, $a_size, $b, $b_size, $name, $verbose) = @_;
+ my ($ofs) = 0;
+ select(STDOUT);
+ for (;;) {
+ my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size;
+ my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size;
+ my ($a_data, $b_data);
+ if (!defined (sysread ($a, $a_data, $a_amt))
+ || !defined (sysread ($b, $b_data, $b_amt))) {
+ die "reading $name: $!\n";
+ }
+
+ my ($a_len) = length $a_data;
+ my ($b_len) = length $b_data;
+ last if $a_len == 0 && $b_len == 0;
+
+ if ($a_data ne $b_data) {
+ my ($min_len) = $a_len < $b_len ? $a_len : $b_len;
+ my ($diff_ofs);
+ for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) {
+ last if (substr ($a_data, $diff_ofs, 1)
+ ne substr ($b_data, $diff_ofs, 1));
+ }
+
+ printf "\nFile $name differs from expected "
+ . "starting at offset 0x%x.\n", $ofs + $diff_ofs;
+ if ($verbose ) {
+ print "Expected contents:\n";
+ hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs);
+ print "Actual contents:\n";
+ hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs);
+ }
+ return 0;
+ }
+
+ $ofs += $a_len;
+ $a_size -= $a_len;
+ $b_size -= $b_len;
+ }
+ return 1;
+}
+
+# hex_dump ($DATA, $OFS)
+#
+# Prints $DATA in hex and text formats.
+# The first byte of $DATA corresponds to logical offset $OFS
+# in whatever file the data comes from.
+sub hex_dump {
+ my ($data, $ofs) = @_;
+
+ if ($data eq '') {
+ printf " (File ends at offset %08x.)\n", $ofs;
+ return;
+ }
+
+ my ($per_line) = 16;
+ while ((my $size = length ($data)) > 0) {
+ my ($start) = $ofs % $per_line;
+ my ($end) = $per_line;
+ $end = $start + $size if $end - $start > $size;
+ my ($n) = $end - $start;
+
+ printf "0x%08x ", int ($ofs / $per_line) * $per_line;
+
+ # Hex version.
+ print " " x $start;
+ for my $i ($start...$end - 1) {
+ printf "%02x", ord (substr ($data, $i - $start, 1));
+ print $i == $per_line / 2 - 1 ? '-' : ' ';
+ }
+ print " " x ($per_line - $end);
+
+ # Character version.
+ my ($esc_data) = substr ($data, 0, $n);
+ $esc_data =~ s/[^[:print:]]/./g;
+ print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|";
+
+ print "\n";
+
+ $data = substr ($data, $n);
+ $ofs += $n;
+ }
+}
+
+# print_fs (%FS)
+#
+# Prints a list of files in %FS, which must be a file system
+# as flattened by flatten_hierarchy() and normalized by
+# normalize_fs().
+sub print_fs {
+ my (%fs) = @_;
+ foreach my $name (sort keys %fs) {
+ my ($esc_name) = $name;
+ $esc_name =~ s/[^[:print:]]/./g;
+ print "$esc_name: ";
+ if (!is_dir ($fs{$name})) {
+ print +file_size ($fs{$name}), "-byte file";
+ } else {
+ print "directory";
+ }
+ print "\n";
+ }
+}
+
+# normalize_fs (%FS)
+#
+# Takes a file system as flattened by flatten_hierarchy().
+# Returns a similar file system in which values of the form $FILE
+# are replaced by those of the form [$FILE, $OFFSET, $LENGTH].
+sub normalize_fs {
+ my (%fs) = @_;
+ foreach my $name (keys %fs) {
+ my ($value) = $fs{$name};
+ next if is_dir ($value) || ref ($value) ne '';
+ die "can't open $value\n" if !stat $value;
+ $fs{$name} = [$value, 0, -s _];
+ }
+ return %fs;
+}
+
+# is_dir ($VALUE)
+#
+# Takes a value like one in the hash returned by flatten_hierarchy()
+# and returns 1 if it represents a directory, 0 otherwise.
+sub is_dir {
+ my ($value) = @_;
+ return ref ($value) eq '' && $value eq 'directory';
+}
+
+# file_size ($VALUE)
+#
+# Takes a value like one in the hash returned by flatten_hierarchy()
+# and returns the size of the file it represents.
+sub file_size {
+ my ($value) = @_;
+ die if is_dir ($value);
+ die if ref ($value) ne 'ARRAY';
+ return @$value > 1 ? $value->[2] : length ($value->[0]);
+}
+
+# flatten_hierarchy ($HIER_FS, $PREFIX)
+#
+# Takes a file system in the format expected by check_archive() and
+# returns a "flattened" version in which file names include all parent
+# directory names and the value of directories is just "directory".
+sub flatten_hierarchy {
+ my (%hier_fs) = %{$_[0]};
+ my ($prefix) = $_[1];
+ my (%flat_fs);
+ for my $name (keys %hier_fs) {
+ my ($value) = $hier_fs{$name};
+ if (ref $value eq 'HASH') {
+ %flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/"));
+ $flat_fs{"$prefix$name"} = 'directory';
+ } else {
+ $flat_fs{"$prefix$name"} = $value;
+ }
+ }
+ return %flat_fs;
+}
+
+# read_tar ($ARCHIVE)
+#
+# Reads the ustar-format tar file in $ARCHIVE
+# and returns a flattened file system for it.
+sub read_tar {
+ my ($archive) = @_;
+ my (%content);
+ open (ARCHIVE, '<', $archive) or die "$archive: open: $1\n";
+ for (;;) {
+ my ($header);
+ if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) {
+ die "$archive: unexpected end of file\n" if $retval >= 0;
+ die "$archive: read: $!\n";
+ }
+
+ last if $header eq "\0" x 512;
+
+ # Verify magic numbers.
+ if (substr ($header, 257, 6) ne "ustar\0"
+ || substr ($header, 263, 2) ne '00') {
+ die "$archive: corrupt ustar header\n";
+ }
+
+ # Verify checksum.
+ my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8)));
+ my ($correct_chksum) = unpack ("%32a*", $header);
+ die "$archive: bad header checksum\n" if $chksum != $correct_chksum;
+
+ # Get file name.
+ my ($name) = unpack ("Z100", $header);
+ my ($prefix) = unpack ("Z*", substr ($header, 345));
+ $name = "$prefix/$name" if $prefix ne '';
+ die "$archive: contains file with empty name" if $name eq '';
+
+ # Get type.
+ my ($typeflag) = substr ($header, 156, 1);
+ $typeflag = '0' if $typeflag eq "\0";
+ die "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/;
+
+ # Get size.
+ my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
+ die "bad size $size\n" if $size < 0;
+ $size = 0 if $typeflag eq '5';
+
+ # Store content.
+ if (exists $content{$name}) {
+ die "$archive: contains multiple entries for $name\n";
+ }
+ if ($typeflag eq '5') {
+ $content{$name} = 'directory';
+ } else {
+ my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR);
+ $content{$name} = [$archive, $position, $size];
+ sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR);
+ }
+ }
+ close (ARCHIVE);
+ return %content;
+}
+\f
+# Utilities.
sub fail {
finish ("FAIL", @_);
}
sub finish {
- my ($verdict, @rest) = @_;
+ my ($verdict, @messages) = @_;
- my ($messages) = "";
seek ($msg_file, 0, 0);
- while (<$msg_file>) {
- $messages .= $_;
- }
+ push (@messages, <$msg_file>);
close ($msg_file);
+ chomp (@messages);
my ($result_fn) = "$test.result";
open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n";
- print RESULT "$verdict\n", $messages, @rest;
+ print RESULT "$verdict\n";
+ print RESULT "$_\n" foreach @messages;
close (RESULT);
if ($verdict eq 'PASS') {
} else {
print STDOUT "FAIL $test\n";
}
- print STDOUT $messages, @rest, "\n";
+ print STDOUT "$_\n" foreach @messages;
exit 0;
}
(alarm-negative) PASS
(alarm-negative) end
EOF
+pass;
(alarm-priority) Thread priority 21 woke up.
(alarm-priority) end
EOF
+pass;
(alarm-simultaneous) iteration 4, thread 4: woke up 0 ticks later
(alarm-simultaneous) end
EOF
+pass;
(alarm-zero) PASS
(alarm-zero) end
EOF
+pass;
our ($test);
@output = read_text_file ("$test.output");
- common_checks (@output);
+ common_checks ("run", @output);
my (@products);
for (my ($i) = 0; $i < $iterations; $i++) {
(mlfqs-block) Block thread should have already acquired lock.
(mlfqs-block) end
EOF
+pass;
our ($test);
my (@output) = read_text_file ("$test.output");
-common_checks (@output);
+common_checks ("run", @output);
-@output = get_core_output (@output);
+@output = get_core_output ("run", @output);
fail "missing PASS in output"
unless grep ($_ eq '(mlfqs-load-1) PASS', @output);
our ($test);
my (@output) = read_text_file ("$test.output");
-common_checks (@output);
-@output = get_core_output (@output);
+common_checks ("run", @output);
+@output = get_core_output ("run", @output);
# Get actual values.
local ($_);
our ($test);
my (@output) = read_text_file ("$test.output");
-common_checks (@output);
-@output = get_core_output (@output);
+common_checks ("run", @output);
+@output = get_core_output ("run", @output);
# Get actual values.
local ($_);
our ($test);
my (@output) = read_text_file ("$test.output");
-common_checks (@output);
-@output = get_core_output (@output);
+common_checks ("run", @output);
+@output = get_core_output ("run", @output);
# Get actual values.
local ($_);
my ($nice, $maxdiff) = @_;
our ($test);
my (@output) = read_text_file ("$test.output");
- common_checks (@output);
- @output = get_core_output (@output);
+ common_checks ("run", @output);
+ @output = get_core_output ("run", @output);
my (@actual);
local ($_);
(priority-change) Thread 2 should have just exited.
(priority-change) end
EOF
+pass;
(priority-condvar) Thread priority 21 woke up.
(priority-condvar) end
EOF
+pass;
(priority-donate-lower) Main thread should have priority 21. Actual priority: 21.
(priority-donate-lower) end
EOF
+pass;
(priority-donate-multiple) Main thread should have priority 31. Actual priority: 31.
(priority-donate-multiple) end
EOF
+pass;
(priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31.
(priority-donate-multiple2) end
EOF
+pass;
(priority-donate-nest) Low thread should have priority 31. Actual priority: 31.
(priority-donate-nest) end
EOF
+pass;
(priority-donate-one) This should be the last line before finishing this test.
(priority-donate-one) end
EOF
+pass;
(priority-donate-sema) Main thread finished.
(priority-donate-sema) end
EOF
+pass;
our ($test);
my (@output) = read_text_file ("$test.output");
-common_checks (@output);
+common_checks ("run", @output);
my ($thread_cnt) = 16;
my ($iter_cnt) = 16;
(priority-preempt) The high-priority thread should have already completed.
(priority-preempt) end
EOF
+pass;
(priority-sema) Back in main thread.
(priority-sema) end
EOF
+pass;
# -*- makefile -*-
-PINTOSFLAGS += --fs-disk=2
-KERNELFLAGS += -f
-
+tests/%.output: FSDISK = 2
tests/%.output: PUTFILES = $(filter-out os.dsk, $^)
tests/userprog_TESTS = $(addprefix tests/userprog/,args-none \
(args) end
args-dbl-space: exit(0)
EOF
+pass;
(args) end
args-many: exit(0)
EOF
+pass;
(args) end
args-multiple: exit(0)
EOF
+pass;
(args) end
args-none: exit(0)
EOF
+pass;
(args) end
args-single: exit(0)
EOF
+pass;
(close-bad-fd) begin
close-bad-fd: exit(-1)
EOF
+pass;
(close-normal) end
close-normal: exit(0)
EOF
+pass;
(close-stdin) begin
close-stdin: exit(-1)
EOF
+pass;
(close-stdout) begin
close-stdout: exit(-1)
EOF
+pass;
(close-twice) close "sample.txt" again
close-twice: exit(-1)
EOF
+pass;
(create-bad-ptr) begin
create-bad-ptr: exit(-1)
EOF
+pass;
(create-bound) end
create-bound: exit(0)
EOF
+pass;
(create-empty) begin
create-empty: exit(-1)
EOF
+pass;
(create-exists) end
create-exists: exit(0)
EOF
+pass;
(create-long) end
create-long: exit(0)
EOF
+pass;
(create-normal) end
create-normal: exit(0)
EOF
+pass;
(create-null) begin
create-null: exit(-1)
EOF
+pass;
(exec-arg) end
exec-arg: exit(0)
EOF
+pass;
(exec-bad-ptr) begin
exec-bad-ptr: exit(-1)
EOF
+pass;
(exec-missing) end
exec-missing: exit(0)
EOF
+pass;
(exec-multiple) end
exec-multiple: exit(0)
EOF
+pass;
(exec-once) end
exec-once: exit(0)
EOF
+pass;
(exit) begin
exit: exit(57)
EOF
+pass;
our ($test);
my (@output) = read_text_file ("$test.output");
-common_checks (@output);
+common_checks ("run", @output);
fail "missing 'begin' message\n"
if !grep ($_ eq '(halt) begin', @output);
(multi-child-fd) end
multi-child-fd: exit(0)
EOF
+pass;
(multi-recurse) end 15
multi-recurse: exit(15)
EOF
+pass;
our ($test);
my (@output) = read_text_file ("$test.output");
-common_checks (@output);
+common_checks ("run", @output);
-@output = get_core_output (@output);
+@output = get_core_output ("run", @output);
my ($n) = 0;
while (my ($m) = $output[0] =~ /^\(multi-oom\) begin (\d+)$/) {
fail "Child process $m started out of order.\n" if $m != $n;
check_expected ([<<'EOF']);
system call!
EOF
+pass;
(open-bad-ptr) begin
open-bad-ptr: exit(-1)
EOF
+pass;
(open-boundary) end
open-boundary: exit(0)
EOF
+pass;
(open-empty) end
open-empty: exit(0)
EOF
+pass;
(open-missing) end
open-missing: exit(0)
EOF
+pass;
(open-normal) end
open-normal: exit(0)
EOF
+pass;
(open-null) begin
open-null: exit(-1)
EOF
+pass;
(open-twice) end
open-twice: exit(0)
EOF
+pass;
(read-bad-fd) begin
read-bad-fd: exit(-1)
EOF
+pass;
(read-bad-ptr) open "sample.txt"
read-bad-ptr: exit(-1)
EOF
+pass;
(read-boundary) end
read-boundary: exit(0)
EOF
+pass;
(read-normal) end
read-normal: exit(0)
EOF
+pass;
(read-stdout) begin
read-stdout: exit(-1)
EOF
+pass;
(read-zero) end
read-zero: exit(0)
EOF
+pass;
(rox-child) end
rox-child: exit(0)
EOF
+pass;
(rox-multichild) end
rox-multichild: exit(0)
EOF
+pass;
(rox-simple) end
rox-simple: exit(0)
EOF
+pass;
(sc-bad-arg) begin
sc-bad-arg: exit(-1)
EOF
+pass;
(sc-bad-sp) begin
sc-bad-sp: exit(-1)
EOF
+pass;
(sc-boundary-2) begin
sc-boundary-2: exit(67)
EOF
+pass;
(sc-boundary) begin
sc-boundary: exit(42)
EOF
+pass;
(wait-bad-pid) begin
wait-bad-pid: exit(-1)
EOF
+pass;
(wait-killed) end
wait-killed: exit(0)
EOF
+pass;
(wait-simple) end
wait-simple: exit(0)
EOF
+pass;
(wait-twice) end
wait-twice: exit(0)
EOF
+pass;
(write-bad-fd) begin
write-bad-fd: exit(-1)
EOF
+pass;
(write-bad-ptr) open "sample.txt"
write-bad-ptr: exit(-1)
EOF
+pass;
(write-boundary) end
write-boundary: exit(0)
EOF
+pass;
(write-normal) end
write-normal: exit(0)
EOF
+pass;
(write-stdin) begin
write-stdin: exit(-1)
EOF
+pass;
(write-zero) end
write-zero: exit(0)
EOF
+pass;
# -*- makefile -*-
-PINTOSFLAGS += --swap-disk=4
-
tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha \
pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code \
pt-write-code2 pt-grow-stk-sc page-linear page-parallel page-merge-seq \
(mmap-bad-fd) try to mmap invalid fd
mmap-bad-fd: exit(-1)
EOF
+pass;
(mmap-clean) file change was retained after munmap
(mmap-clean) end
EOF
+pass;
(mmap-close) mmap "sample.txt"
(mmap-close) end
EOF
+pass;
(mmap-exit) close "sample.txt"
(mmap-exit) end
EOF
+pass;
(mmap-inherit) end
mmap-inherit: exit(0)
EOF
+pass;
(mmap-misalign) try to mmap at misaligned address
(mmap-misalign) end
EOF
+pass;
(mmap-null) try to mmap at address 0
(mmap-null) end
EOF
+pass;
(mmap-over-code) try to mmap over code segment
(mmap-over-code) end
EOF
+pass;
(mmap-over-data) try to mmap over data segment
(mmap-over-data) end
EOF
+pass;
(mmap-over-stk) try to mmap over stack segment
(mmap-over-stk) end
EOF
+pass;
(mmap-overlap) try to mmap "zeros" again
(mmap-overlap) end
EOF
+pass;
(mmap-read) mmap "sample.txt"
(mmap-read) end
EOF
+pass;
(mmap-remove) create "another"
(mmap-remove) end
EOF
+pass;
(mmap-shuffle) shuffle 9: cksum=$shuffle[9]
(mmap-shuffle) end
EOF
+pass;
(mmap-twice) compare mmap'd file 1 against data
(mmap-twice) end
EOF
+pass;
(mmap-write) compare read data against written data
(mmap-write) end
EOF
+pass;
(mmap-zero) mmap "empty"
mmap-zero: exit(-1)
EOF
+pass;
(page-linear) read pass
(page-linear) end
EOF
+pass;
(page-merge-par) success, buf_idx=1,048,576
(page-merge-par) end
EOF
+pass;
(page-merge-seq) success, buf_idx=1,032,192
(page-merge-seq) end
EOF
+pass;
(page-parallel) wait for child 2
(page-parallel) end
EOF
+pass;
(page-shuffle) shuffle 9: cksum=$shuffle[9]
(page-shuffle) end
EOF
+pass;
our ($test);
my (@output) = read_text_file ("$test.output");
- common_checks (@output);
- @output = get_core_output (@output);
+ common_checks ("run", @output);
+ @output = get_core_output ("run", @output);
fail "First line of output is not `($proc_name) begin' message.\n"
if $output[0] ne "($proc_name) begin";
fail "Output missing '$proc_name: exit(-1)' message.\n"
(pt-bad-read) open "sample.txt"
pt-bad-read: exit(-1)
EOF
+pass;
(pt-big-stk-obj) cksum: 3256410166
(pt-big-stk-obj) end
EOF
+pass;
(pt-grow-bad) begin
pt-grow-bad: exit(-1)
EOF
+pass;
(pt-grow-pusha) begin
(pt-grow-pusha) end
EOF
+pass;
(pt-grow-stack) cksum: 3424492700
(pt-grow-stack) end
EOF
+pass;
(pt-grow-stk-sc) compare written data against read data
(pt-grow-stk-sc) end
EOF
+pass;
(pt-write-code2) open "sample.txt"
pt-write-code2: exit(-1)
EOF
+pass;
endef
define run-tests
-cd $@/src/$(PROJECT) && $(MAKE) check
+cd $@/src/$(PROJECT) && time $(MAKE) check
endef
define grade-tests