summaryrefslogtreecommitdiffstats
path: root/pintos-progos/tests
diff options
context:
space:
mode:
Diffstat (limited to 'pintos-progos/tests')
-rw-r--r--pintos-progos/tests/Algorithm/Diff.pm1713
-rw-r--r--pintos-progos/tests/Make.tests76
-rw-r--r--pintos-progos/tests/arc4.c53
-rw-r--r--pintos-progos/tests/arc4.h17
-rw-r--r--pintos-progos/tests/arc4.pm29
-rw-r--r--pintos-progos/tests/cksum.c92
-rw-r--r--pintos-progos/tests/cksum.h8
-rw-r--r--pintos-progos/tests/cksum.pm87
-rw-r--r--pintos-progos/tests/filesys/Grading.no-vm18
-rw-r--r--pintos-progos/tests/filesys/Grading.with-vm22
-rw-r--r--pintos-progos/tests/filesys/base/Make.tests18
-rw-r--r--pintos-progos/tests/filesys/base/Rubric19
-rw-r--r--pintos-progos/tests/filesys/base/child-syn-read.c44
-rw-r--r--pintos-progos/tests/filesys/base/child-syn-wrt.c35
-rw-r--r--pintos-progos/tests/filesys/base/full.inc20
-rw-r--r--pintos-progos/tests/filesys/base/lg-create.c5
-rw-r--r--pintos-progos/tests/filesys/base/lg-create.ck13
-rw-r--r--pintos-progos/tests/filesys/base/lg-full.c6
-rw-r--r--pintos-progos/tests/filesys/base/lg-full.ck16
-rw-r--r--pintos-progos/tests/filesys/base/lg-random.c7
-rw-r--r--pintos-progos/tests/filesys/base/lg-random.ck14
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-block.c7
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-block.ck16
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-random.c6
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-random.ck16
-rw-r--r--pintos-progos/tests/filesys/base/random.inc59
-rw-r--r--pintos-progos/tests/filesys/base/seq-block.inc20
-rw-r--r--pintos-progos/tests/filesys/base/seq-random.inc22
-rw-r--r--pintos-progos/tests/filesys/base/sm-create.c5
-rw-r--r--pintos-progos/tests/filesys/base/sm-create.ck13
-rw-r--r--pintos-progos/tests/filesys/base/sm-full.c6
-rw-r--r--pintos-progos/tests/filesys/base/sm-full.ck16
-rw-r--r--pintos-progos/tests/filesys/base/sm-random.c7
-rw-r--r--pintos-progos/tests/filesys/base/sm-random.ck14
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-block.c7
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-block.ck16
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-random.c6
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-random.ck16
-rw-r--r--pintos-progos/tests/filesys/base/syn-read.c31
-rw-r--r--pintos-progos/tests/filesys/base/syn-read.ck33
-rw-r--r--pintos-progos/tests/filesys/base/syn-read.h7
-rw-r--r--pintos-progos/tests/filesys/base/syn-remove.c30
-rw-r--r--pintos-progos/tests/filesys/base/syn-remove.ck16
-rw-r--r--pintos-progos/tests/filesys/base/syn-write.c31
-rw-r--r--pintos-progos/tests/filesys/base/syn-write.ck32
-rw-r--r--pintos-progos/tests/filesys/base/syn-write.h9
-rw-r--r--pintos-progos/tests/filesys/create.inc15
-rw-r--r--pintos-progos/tests/filesys/extended/Make.tests61
-rw-r--r--pintos-progos/tests/filesys/extended/Rubric.functionality26
-rw-r--r--pintos-progos/tests/filesys/extended/Rubric.persistence24
-rw-r--r--pintos-progos/tests/filesys/extended/Rubric.robustness9
-rw-r--r--pintos-progos/tests/filesys/extended/child-syn-rw.c53
-rw-r--r--pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-empty-name.c12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-empty-name.ck10
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck16
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mk-tree.c12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mk-tree.ck12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mkdir.c15
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mkdir.ck13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-open-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-open.c21
-rw-r--r--pintos-progos/tests/filesys/extended/dir-open.ck20
-rw-r--r--pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-over-file.c13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-over-file.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck8
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-cwd.c75
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-cwd.ck51
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-parent.c16
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-parent.ck14
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-root.c13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-root.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-tree.c62
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-tree.ck14
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rmdir.c14
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rmdir.ck12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-under-file.c13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-under-file.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/dir-vine-persistence.ck37
-rw-r--r--pintos-progos/tests/filesys/extended/dir-vine.c85
-rw-r--r--pintos-progos/tests/filesys/extended/dir-vine.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/grow-create-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/grow-create.c4
-rw-r--r--pintos-progos/tests/filesys/extended/grow-create.ck13
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir-lg.c6
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir-lg.ck61
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir.inc41
-rw-r--r--pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-file-size.c33
-rw-r--r--pintos-progos/tests/filesys/extended/grow-file-size.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-lg.c4
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-lg.ck60
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-sm.c4
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-sm.ck30
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-lg.c5
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-lg.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-sm.c5
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-sm.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq.inc20
-rw-r--r--pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/grow-sparse.c25
-rw-r--r--pintos-progos/tests/filesys/extended/grow-sparse.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-tell-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-tell.c32
-rw-r--r--pintos-progos/tests/filesys/extended/grow-tell.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-two-files.c62
-rw-r--r--pintos-progos/tests/filesys/extended/grow-two-files.ck23
-rw-r--r--pintos-progos/tests/filesys/extended/mk-tree.c67
-rw-r--r--pintos-progos/tests/filesys/extended/mk-tree.h6
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw-persistence.ck8
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw.c35
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw.ck20
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw.h9
-rw-r--r--pintos-progos/tests/filesys/extended/tar.c208
-rw-r--r--pintos-progos/tests/filesys/seq-test.c37
-rw-r--r--pintos-progos/tests/filesys/seq-test.h11
-rw-r--r--pintos-progos/tests/internal/list.c174
-rw-r--r--pintos-progos/tests/internal/stdio.c208
-rw-r--r--pintos-progos/tests/internal/stdlib.c114
-rw-r--r--pintos-progos/tests/intro/Grading5
-rw-r--r--pintos-progos/tests/intro/alarm-clock/Make.tests15
-rw-r--r--pintos-progos/tests/intro/alarm-clock/Rubric7
l---------pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-negative.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-negative.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-single.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-wait.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-zero.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-zero.ck1
-rw-r--r--pintos-progos/tests/intro/alarm-clock/tests.c80
-rw-r--r--pintos-progos/tests/intro/userprog-args/Make.tests27
-rw-r--r--pintos-progos/tests/intro/userprog-args/Rubric7
l---------pintos-progos/tests/intro/userprog-args/args-dbl-space.ck1
-rw-r--r--pintos-progos/tests/intro/userprog-args/args-limit.c70
-rw-r--r--pintos-progos/tests/intro/userprog-args/args-limit.ck11
l---------pintos-progos/tests/intro/userprog-args/args-many.ck1
l---------pintos-progos/tests/intro/userprog-args/args-multiple.ck1
l---------pintos-progos/tests/intro/userprog-args/args-none.ck1
l---------pintos-progos/tests/intro/userprog-args/args-single.ck1
l---------pintos-progos/tests/intro/userprog-args/args.c1
l---------pintos-progos/tests/intro/userprog-args/child-simple.c1
-rw-r--r--pintos-progos/tests/lib.c196
-rw-r--r--pintos-progos/tests/lib.h50
-rw-r--r--pintos-progos/tests/lib.pm19
-rw-r--r--pintos-progos/tests/main.c15
-rw-r--r--pintos-progos/tests/main.h6
-rwxr-xr-xpintos-progos/tests/make-grade152
-rw-r--r--pintos-progos/tests/random.pm27
-rw-r--r--pintos-progos/tests/tests.pm625
-rw-r--r--pintos-progos/tests/threads/Grading11
-rw-r--r--pintos-progos/tests/threads/Make.tests56
-rw-r--r--pintos-progos/tests/threads/Rubric.alarm8
-rw-r--r--pintos-progos/tests/threads/Rubric.mlfqs14
-rw-r--r--pintos-progos/tests/threads/Rubric.priority15
-rw-r--r--pintos-progos/tests/threads/alarm-multiple.ck4
-rw-r--r--pintos-progos/tests/threads/alarm-negative.c15
-rw-r--r--pintos-progos/tests/threads/alarm-negative.ck10
-rw-r--r--pintos-progos/tests/threads/alarm-priority.c58
-rw-r--r--pintos-progos/tests/threads/alarm-priority.ck19
-rw-r--r--pintos-progos/tests/threads/alarm-simultaneous.c94
-rw-r--r--pintos-progos/tests/threads/alarm-simultaneous.ck27
-rw-r--r--pintos-progos/tests/threads/alarm-single.ck4
-rw-r--r--pintos-progos/tests/threads/alarm-wait.c152
-rw-r--r--pintos-progos/tests/threads/alarm-zero.c15
-rw-r--r--pintos-progos/tests/threads/alarm-zero.ck10
-rw-r--r--pintos-progos/tests/threads/alarm.pm32
-rw-r--r--pintos-progos/tests/threads/mlfqs-block.c64
-rw-r--r--pintos-progos/tests/threads/mlfqs-block.ck17
-rw-r--r--pintos-progos/tests/threads/mlfqs-fair-2.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-fair-20.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-fair.c124
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-1.c60
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-1.ck15
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-60.c155
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-60.ck36
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-avg.c167
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-avg.ck36
-rw-r--r--pintos-progos/tests/threads/mlfqs-nice-10.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-nice-2.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-recent-1.c144
-rw-r--r--pintos-progos/tests/threads/mlfqs-recent-1.ck31
-rw-r--r--pintos-progos/tests/threads/mlfqs.pm146
-rw-r--r--pintos-progos/tests/threads/priority-change.c31
-rw-r--r--pintos-progos/tests/threads/priority-change.ck14
-rw-r--r--pintos-progos/tests/threads/priority-condvar.c53
-rw-r--r--pintos-progos/tests/threads/priority-condvar.ck39
-rw-r--r--pintos-progos/tests/threads/priority-donate-chain.c114
-rw-r--r--pintos-progos/tests/threads/priority-donate-chain.ck46
-rw-r--r--pintos-progos/tests/threads/priority-donate-lower.c51
-rw-r--r--pintos-progos/tests/threads/priority-donate-lower.ck16
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple.c77
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple.ck19
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple2.c90
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple2.ck19
-rw-r--r--pintos-progos/tests/threads/priority-donate-nest.c94
-rw-r--r--pintos-progos/tests/threads/priority-donate-nest.ck19
-rw-r--r--pintos-progos/tests/threads/priority-donate-one.c65
-rw-r--r--pintos-progos/tests/threads/priority-donate-one.ck17
-rw-r--r--pintos-progos/tests/threads/priority-donate-sema.c82
-rw-r--r--pintos-progos/tests/threads/priority-donate-sema.ck16
-rw-r--r--pintos-progos/tests/threads/priority-fifo.c99
-rw-r--r--pintos-progos/tests/threads/priority-fifo.ck63
-rw-r--r--pintos-progos/tests/threads/priority-preempt.c41
-rw-r--r--pintos-progos/tests/threads/priority-preempt.ck16
-rw-r--r--pintos-progos/tests/threads/priority-sema.c45
-rw-r--r--pintos-progos/tests/threads/priority-sema.ck29
-rw-r--r--pintos-progos/tests/threads/tests.c102
-rw-r--r--pintos-progos/tests/threads/tests.h41
-rw-r--r--pintos-progos/tests/userprog/Make.tests133
-rw-r--r--pintos-progos/tests/userprog/Rubric.functionality52
-rw-r--r--pintos-progos/tests/userprog/Rubric.robustness48
-rw-r--r--pintos-progos/tests/userprog/args-dbl-space.ck15
-rw-r--r--pintos-progos/tests/userprog/args-many.ck35
-rw-r--r--pintos-progos/tests/userprog/args-multiple.ck17
-rw-r--r--pintos-progos/tests/userprog/args-none.ck13
-rw-r--r--pintos-progos/tests/userprog/args-single.ck14
-rw-r--r--pintos-progos/tests/userprog/args.c25
-rw-r--r--pintos-progos/tests/userprog/bad-jump.c13
-rw-r--r--pintos-progos/tests/userprog/bad-jump.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-jump2.c13
-rw-r--r--pintos-progos/tests/userprog/bad-jump2.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-read.c13
-rw-r--r--pintos-progos/tests/userprog/bad-read.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-read2.c13
-rw-r--r--pintos-progos/tests/userprog/bad-read2.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-write.c12
-rw-r--r--pintos-progos/tests/userprog/bad-write.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-write2.c12
-rw-r--r--pintos-progos/tests/userprog/bad-write2.ck9
-rw-r--r--pintos-progos/tests/userprog/boundary.c33
-rw-r--r--pintos-progos/tests/userprog/boundary.h7
-rw-r--r--pintos-progos/tests/userprog/child-bad.c14
-rw-r--r--pintos-progos/tests/userprog/child-close.c28
-rw-r--r--pintos-progos/tests/userprog/child-rox.c55
-rw-r--r--pintos-progos/tests/userprog/child-simple.c15
-rw-r--r--pintos-progos/tests/userprog/close-bad-fd.c11
-rw-r--r--pintos-progos/tests/userprog/close-bad-fd.ck13
-rw-r--r--pintos-progos/tests/userprog/close-normal.c14
-rw-r--r--pintos-progos/tests/userprog/close-normal.ck12
-rw-r--r--pintos-progos/tests/userprog/close-stdin.c11
-rw-r--r--pintos-progos/tests/userprog/close-stdin.ck13
-rw-r--r--pintos-progos/tests/userprog/close-stdout.c11
-rw-r--r--pintos-progos/tests/userprog/close-stdout.ck13
-rw-r--r--pintos-progos/tests/userprog/close-twice.c18
-rw-r--r--pintos-progos/tests/userprog/close-twice.ck19
-rw-r--r--pintos-progos/tests/userprog/create-bad-ptr.c12
-rw-r--r--pintos-progos/tests/userprog/create-bad-ptr.ck9
-rw-r--r--pintos-progos/tests/userprog/create-bound.c14
-rw-r--r--pintos-progos/tests/userprog/create-bound.ck11
-rw-r--r--pintos-progos/tests/userprog/create-empty.c10
-rw-r--r--pintos-progos/tests/userprog/create-empty.ck14
-rw-r--r--pintos-progos/tests/userprog/create-exists.c16
-rw-r--r--pintos-progos/tests/userprog/create-exists.ck15
-rw-r--r--pintos-progos/tests/userprog/create-long.c17
-rw-r--r--pintos-progos/tests/userprog/create-long.ck11
-rw-r--r--pintos-progos/tests/userprog/create-normal.c10
-rw-r--r--pintos-progos/tests/userprog/create-normal.ck11
-rw-r--r--pintos-progos/tests/userprog/create-null.c11
-rw-r--r--pintos-progos/tests/userprog/create-null.ck9
-rw-r--r--pintos-progos/tests/userprog/exec-arg.c10
-rw-r--r--pintos-progos/tests/userprog/exec-arg.ck17
-rw-r--r--pintos-progos/tests/userprog/exec-bad-ptr.c11
-rw-r--r--pintos-progos/tests/userprog/exec-bad-ptr.ck13
-rw-r--r--pintos-progos/tests/userprog/exec-missing.c12
-rw-r--r--pintos-progos/tests/userprog/exec-missing.ck31
-rw-r--r--pintos-progos/tests/userprog/exec-multiple.c14
-rw-r--r--pintos-progos/tests/userprog/exec-multiple.ck18
-rw-r--r--pintos-progos/tests/userprog/exec-once.c11
-rw-r--r--pintos-progos/tests/userprog/exec-once.ck12
-rw-r--r--pintos-progos/tests/userprog/exit.c11
-rw-r--r--pintos-progos/tests/userprog/exit.ck9
-rw-r--r--pintos-progos/tests/userprog/halt.c11
-rw-r--r--pintos-progos/tests/userprog/halt.ck15
-rw-r--r--pintos-progos/tests/userprog/lib/.gitignore1
-rw-r--r--pintos-progos/tests/userprog/lib/user/.dummy0
-rw-r--r--pintos-progos/tests/userprog/lib/user/.gitignore1
-rw-r--r--pintos-progos/tests/userprog/multi-child-fd.c25
-rw-r--r--pintos-progos/tests/userprog/multi-child-fd.ck25
-rw-r--r--pintos-progos/tests/userprog/multi-recurse.c34
-rw-r--r--pintos-progos/tests/userprog/multi-recurse.ck70
-rw-r--r--pintos-progos/tests/userprog/no-vm/Make.tests8
-rw-r--r--pintos-progos/tests/userprog/no-vm/Rubric3
-rw-r--r--pintos-progos/tests/userprog/no-vm/multi-oom.c179
-rw-r--r--pintos-progos/tests/userprog/no-vm/multi-oom.ck10
-rw-r--r--pintos-progos/tests/userprog/null.ck8
-rw-r--r--pintos-progos/tests/userprog/open-bad-ptr.c13
-rw-r--r--pintos-progos/tests/userprog/open-bad-ptr.ck13
-rw-r--r--pintos-progos/tests/userprog/open-boundary.c14
-rw-r--r--pintos-progos/tests/userprog/open-boundary.ck11
-rw-r--r--pintos-progos/tests/userprog/open-empty.c13
-rw-r--r--pintos-progos/tests/userprog/open-empty.ck10
-rw-r--r--pintos-progos/tests/userprog/open-missing.c13
-rw-r--r--pintos-progos/tests/userprog/open-missing.ck10
-rw-r--r--pintos-progos/tests/userprog/open-normal.c13
-rw-r--r--pintos-progos/tests/userprog/open-normal.ck10
-rw-r--r--pintos-progos/tests/userprog/open-null.c12
-rw-r--r--pintos-progos/tests/userprog/open-null.ck13
-rw-r--r--pintos-progos/tests/userprog/open-twice.c19
-rw-r--r--pintos-progos/tests/userprog/open-twice.ck12
-rw-r--r--pintos-progos/tests/userprog/read-bad-fd.c21
-rw-r--r--pintos-progos/tests/userprog/read-bad-fd.ck13
-rw-r--r--pintos-progos/tests/userprog/read-bad-ptr.c16
-rw-r--r--pintos-progos/tests/userprog/read-bad-ptr.ck15
-rw-r--r--pintos-progos/tests/userprog/read-boundary.c30
-rw-r--r--pintos-progos/tests/userprog/read-boundary.ck11
-rw-r--r--pintos-progos/tests/userprog/read-normal.c11
-rw-r--r--pintos-progos/tests/userprog/read-normal.ck13
-rw-r--r--pintos-progos/tests/userprog/read-stdout.c14
-rw-r--r--pintos-progos/tests/userprog/read-stdout.ck13
-rw-r--r--pintos-progos/tests/userprog/read-zero.c22
-rw-r--r--pintos-progos/tests/userprog/read-zero.ck11
-rw-r--r--pintos-progos/tests/userprog/rox-child.c5
-rw-r--r--pintos-progos/tests/userprog/rox-child.ck20
-rw-r--r--pintos-progos/tests/userprog/rox-child.inc33
-rw-r--r--pintos-progos/tests/userprog/rox-multichild.c5
-rw-r--r--pintos-progos/tests/userprog/rox-multichild.ck44
-rw-r--r--pintos-progos/tests/userprog/rox-simple.c19
-rw-r--r--pintos-progos/tests/userprog/rox-simple.ck13
-rw-r--r--pintos-progos/tests/userprog/sample.inc6
-rw-r--r--pintos-progos/tests/userprog/sample.txt4
-rw-r--r--pintos-progos/tests/userprog/sc-bad-arg.c17
-rw-r--r--pintos-progos/tests/userprog/sc-bad-arg.ck9
-rw-r--r--pintos-progos/tests/userprog/sc-bad-sp.c20
-rw-r--r--pintos-progos/tests/userprog/sc-bad-sp.ck9
-rw-r--r--pintos-progos/tests/userprog/sc-boundary-2.c22
-rw-r--r--pintos-progos/tests/userprog/sc-boundary-2.ck9
-rw-r--r--pintos-progos/tests/userprog/sc-boundary.c22
-rw-r--r--pintos-progos/tests/userprog/sc-boundary.ck9
-rw-r--r--pintos-progos/tests/userprog/wait-bad-pid.c11
-rw-r--r--pintos-progos/tests/userprog/wait-bad-pid.ck13
-rw-r--r--pintos-progos/tests/userprog/wait-killed.c11
-rw-r--r--pintos-progos/tests/userprog/wait-killed.ck13
-rw-r--r--pintos-progos/tests/userprog/wait-simple.c11
-rw-r--r--pintos-progos/tests/userprog/wait-simple.ck13
-rw-r--r--pintos-progos/tests/userprog/wait-twice.c15
-rw-r--r--pintos-progos/tests/userprog/wait-twice.ck14
-rw-r--r--pintos-progos/tests/userprog/write-bad-fd.c20
-rw-r--r--pintos-progos/tests/userprog/write-bad-fd.ck13
-rw-r--r--pintos-progos/tests/userprog/write-bad-ptr.c16
-rw-r--r--pintos-progos/tests/userprog/write-bad-ptr.ck15
-rw-r--r--pintos-progos/tests/userprog/write-boundary.c25
-rw-r--r--pintos-progos/tests/userprog/write-boundary.ck11
-rw-r--r--pintos-progos/tests/userprog/write-normal.c20
-rw-r--r--pintos-progos/tests/userprog/write-normal.ck12
-rw-r--r--pintos-progos/tests/userprog/write-stdin.c14
-rw-r--r--pintos-progos/tests/userprog/write-stdin.ck13
-rw-r--r--pintos-progos/tests/userprog/write-zero.c20
-rw-r--r--pintos-progos/tests/userprog/write-zero.ck11
-rw-r--r--pintos-progos/tests/vm/Grading12
-rw-r--r--pintos-progos/tests/vm/Make.tests107
-rw-r--r--pintos-progos/tests/vm/Rubric.functionality21
-rw-r--r--pintos-progos/tests/vm/Rubric.paging8
-rw-r--r--pintos-progos/tests/vm/Rubric.robustness21
-rw-r--r--pintos-progos/tests/vm/child-inherit.c16
-rw-r--r--pintos-progos/tests/vm/child-linear.c36
-rw-r--r--pintos-progos/tests/vm/child-mm-wrt.c24
-rw-r--r--pintos-progos/tests/vm/child-qsort-mm.c25
-rw-r--r--pintos-progos/tests/vm/child-qsort.c32
-rw-r--r--pintos-progos/tests/vm/child-sort.c42
-rw-r--r--pintos-progos/tests/vm/mmap-bad-fd.c15
-rw-r--r--pintos-progos/tests/vm/mmap-bad-fd.ck15
-rw-r--r--pintos-progos/tests/vm/mmap-clean.c53
-rw-r--r--pintos-progos/tests/vm/mmap-clean.ck16
-rw-r--r--pintos-progos/tests/vm/mmap-close.c27
-rw-r--r--pintos-progos/tests/vm/mmap-close.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-exit.c22
-rw-r--r--pintos-progos/tests/vm/mmap-exit.ck17
-rw-r--r--pintos-progos/tests/vm/mmap-inherit.c32
-rw-r--r--pintos-progos/tests/vm/mmap-inherit.ck16
-rw-r--r--pintos-progos/tests/vm/mmap-lazy-seq.c52
-rw-r--r--pintos-progos/tests/vm/mmap-lazy-seq.ck27
-rw-r--r--pintos-progos/tests/vm/mmap-misalign.c16
-rw-r--r--pintos-progos/tests/vm/mmap-misalign.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-null.c15
-rw-r--r--pintos-progos/tests/vm/mmap-null.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-over-code.c19
-rw-r--r--pintos-progos/tests/vm/mmap-over-code.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-over-data.c21
-rw-r--r--pintos-progos/tests/vm/mmap-over-data.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-over-stk.c19
-rw-r--r--pintos-progos/tests/vm/mmap-over-stk.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-overlap.c20
-rw-r--r--pintos-progos/tests/vm/mmap-overlap.ck13
-rw-r--r--pintos-progos/tests/vm/mmap-read.c32
-rw-r--r--pintos-progos/tests/vm/mmap-read.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-remove.c43
-rw-r--r--pintos-progos/tests/vm/mmap-remove.ck14
-rw-r--r--pintos-progos/tests/vm/mmap-shuffle.c38
-rw-r--r--pintos-progos/tests/vm/mmap-shuffle.ck47
-rw-r--r--pintos-progos/tests/vm/mmap-twice.c28
-rw-r--r--pintos-progos/tests/vm/mmap-twice.ck15
-rw-r--r--pintos-progos/tests/vm/mmap-unmap.c23
-rw-r--r--pintos-progos/tests/vm/mmap-unmap.ck7
-rw-r--r--pintos-progos/tests/vm/mmap-write.c32
-rw-r--r--pintos-progos/tests/vm/mmap-write.ck13
-rw-r--r--pintos-progos/tests/vm/mmap-zero.c27
-rw-r--r--pintos-progos/tests/vm/mmap-zero.ck12
-rw-r--r--pintos-progos/tests/vm/page-linear.c44
-rw-r--r--pintos-progos/tests/vm/page-linear.ck14
-rw-r--r--pintos-progos/tests/vm/page-merge-mm.c8
-rw-r--r--pintos-progos/tests/vm/page-merge-mm.ck29
-rw-r--r--pintos-progos/tests/vm/page-merge-par.c8
-rw-r--r--pintos-progos/tests/vm/page-merge-par.ck29
-rw-r--r--pintos-progos/tests/vm/page-merge-seq.c137
-rw-r--r--pintos-progos/tests/vm/page-merge-seq.ck29
-rw-r--r--pintos-progos/tests/vm/page-merge-stk.c8
-rw-r--r--pintos-progos/tests/vm/page-merge-stk.ck29
-rw-r--r--pintos-progos/tests/vm/page-parallel.c21
-rw-r--r--pintos-progos/tests/vm/page-parallel.ck17
-rw-r--r--pintos-progos/tests/vm/page-shuffle.c30
-rw-r--r--pintos-progos/tests/vm/page-shuffle.ck44
-rw-r--r--pintos-progos/tests/vm/parallel-merge.c149
-rw-r--r--pintos-progos/tests/vm/parallel-merge.h6
-rw-r--r--pintos-progos/tests/vm/process_death.pm22
-rw-r--r--pintos-progos/tests/vm/pt-bad-addr.c11
-rw-r--r--pintos-progos/tests/vm/pt-bad-addr.ck7
-rw-r--r--pintos-progos/tests/vm/pt-bad-read.c16
-rw-r--r--pintos-progos/tests/vm/pt-bad-read.ck10
-rw-r--r--pintos-progos/tests/vm/pt-big-stk-obj.c20
-rw-r--r--pintos-progos/tests/vm/pt-big-stk-obj.ck10
-rw-r--r--pintos-progos/tests/vm/pt-grow-bad.c14
-rw-r--r--pintos-progos/tests/vm/pt-grow-bad.ck9
-rw-r--r--pintos-progos/tests/vm/pt-grow-pusha.c20
-rw-r--r--pintos-progos/tests/vm/pt-grow-pusha.ck9
-rw-r--r--pintos-progos/tests/vm/pt-grow-stack.c20
-rw-r--r--pintos-progos/tests/vm/pt-grow-stack.ck10
-rw-r--r--pintos-progos/tests/vm/pt-grow-stk-sc.c32
-rw-r--r--pintos-progos/tests/vm/pt-grow-stk-sc.ck15
-rw-r--r--pintos-progos/tests/vm/pt-write-code-2.c15
-rw-r--r--pintos-progos/tests/vm/pt-write-code.c12
-rw-r--r--pintos-progos/tests/vm/pt-write-code.ck7
-rw-r--r--pintos-progos/tests/vm/pt-write-code2.ck10
-rw-r--r--pintos-progos/tests/vm/qsort.c136
-rw-r--r--pintos-progos/tests/vm/qsort.h8
-rw-r--r--pintos-progos/tests/vm/sample.inc19
-rw-r--r--pintos-progos/tests/vm/sample.txt17
451 files changed, 13915 insertions, 0 deletions
diff --git a/pintos-progos/tests/Algorithm/Diff.pm b/pintos-progos/tests/Algorithm/Diff.pm
new file mode 100644
index 0000000..904c530
--- /dev/null
+++ b/pintos-progos/tests/Algorithm/Diff.pm
@@ -0,0 +1,1713 @@
1package Algorithm::Diff;
2# Skip to first "=head" line for documentation.
3use strict;
4
5use integer; # see below in _replaceNextLargerWith() for mod to make
6 # if you don't use this
7use vars qw( $VERSION @EXPORT_OK );
8$VERSION = 1.19_01;
9# ^ ^^ ^^-- Incremented at will
10# | \+----- Incremented for non-trivial changes to features
11# \-------- Incremented for fundamental changes
12require Exporter;
13*import = \&Exporter::import;
14@EXPORT_OK = qw(
15 prepare LCS LCDidx LCS_length
16 diff sdiff compact_diff
17 traverse_sequences traverse_balanced
18);
19
20# McIlroy-Hunt diff algorithm
21# Adapted from the Smalltalk code of Mario I. Wolczko, <mario@wolczko.com>
22# by Ned Konz, perl@bike-nomad.com
23# Updates by Tye McQueen, http://perlmonks.org/?node=tye
24
25# Create a hash that maps each element of $aCollection to the set of
26# positions it occupies in $aCollection, restricted to the elements
27# within the range of indexes specified by $start and $end.
28# The fourth parameter is a subroutine reference that will be called to
29# generate a string to use as a key.
30# Additional parameters, if any, will be passed to this subroutine.
31#
32# my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
33
34sub _withPositionsOfInInterval
35{
36 my $aCollection = shift; # array ref
37 my $start = shift;
38 my $end = shift;
39 my $keyGen = shift;
40 my %d;
41 my $index;
42 for ( $index = $start ; $index <= $end ; $index++ )
43 {
44 my $element = $aCollection->[$index];
45 my $key = &$keyGen( $element, @_ );
46 if ( exists( $d{$key} ) )
47 {
48 unshift ( @{ $d{$key} }, $index );
49 }
50 else
51 {
52 $d{$key} = [$index];
53 }
54 }
55 return wantarray ? %d : \%d;
56}
57
58# Find the place at which aValue would normally be inserted into the
59# array. If that place is already occupied by aValue, do nothing, and
60# return undef. If the place does not exist (i.e., it is off the end of
61# the array), add it to the end, otherwise replace the element at that
62# point with aValue. It is assumed that the array's values are numeric.
63# This is where the bulk (75%) of the time is spent in this module, so
64# try to make it fast!
65
66sub _replaceNextLargerWith
67{
68 my ( $array, $aValue, $high ) = @_;
69 $high ||= $#$array;
70
71 # off the end?
72 if ( $high == -1 || $aValue > $array->[-1] )
73 {
74 push ( @$array, $aValue );
75 return $high + 1;
76 }
77
78 # binary search for insertion point...
79 my $low = 0;
80 my $index;
81 my $found;
82 while ( $low <= $high )
83 {
84 $index = ( $high + $low ) / 2;
85
86 # $index = int(( $high + $low ) / 2); # without 'use integer'
87 $found = $array->[$index];
88
89 if ( $aValue == $found )
90 {
91 return undef;
92 }
93 elsif ( $aValue > $found )
94 {
95 $low = $index + 1;
96 }
97 else
98 {
99 $high = $index - 1;
100 }
101 }
102
103 # now insertion point is in $low.
104 $array->[$low] = $aValue; # overwrite next larger
105 return $low;
106}
107
108# This method computes the longest common subsequence in $a and $b.
109
110# Result is array or ref, whose contents is such that
111# $a->[ $i ] == $b->[ $result[ $i ] ]
112# foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined.
113
114# An additional argument may be passed; this is a hash or key generating
115# function that should return a string that uniquely identifies the given
116# element. It should be the case that if the key is the same, the elements
117# will compare the same. If this parameter is undef or missing, the key
118# will be the element as a string.
119
120# By default, comparisons will use "eq" and elements will be turned into keys
121# using the default stringizing operator '""'.
122
123# Additional parameters, if any, will be passed to the key generation
124# routine.
125
126sub _longestCommonSubsequence
127{
128 my $a = shift; # array ref or hash ref
129 my $b = shift; # array ref or hash ref
130 my $counting = shift; # scalar
131 my $keyGen = shift; # code ref
132 my $compare; # code ref
133
134 if ( ref($a) eq 'HASH' )
135 { # prepared hash must be in $b
136 my $tmp = $b;
137 $b = $a;
138 $a = $tmp;
139 }
140
141 # Check for bogus (non-ref) argument values
142 if ( !ref($a) || !ref($b) )
143 {
144 my @callerInfo = caller(1);
145 die 'error: must pass array or hash references to ' . $callerInfo[3];
146 }
147
148 # set up code refs
149 # Note that these are optimized.
150 if ( !defined($keyGen) ) # optimize for strings
151 {
152 $keyGen = sub { $_[0] };
153 $compare = sub { my ( $a, $b ) = @_; $a eq $b };
154 }
155 else
156 {
157 $compare = sub {
158 my $a = shift;
159 my $b = shift;
160 &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ );
161 };
162 }
163
164 my ( $aStart, $aFinish, $matchVector ) = ( 0, $#$a, [] );
165 my ( $prunedCount, $bMatches ) = ( 0, {} );
166
167 if ( ref($b) eq 'HASH' ) # was $bMatches prepared for us?
168 {
169 $bMatches = $b;
170 }
171 else
172 {
173 my ( $bStart, $bFinish ) = ( 0, $#$b );
174
175 # First we prune off any common elements at the beginning
176 while ( $aStart <= $aFinish
177 and $bStart <= $bFinish
178 and &$compare( $a->[$aStart], $b->[$bStart], @_ ) )
179 {
180 $matchVector->[ $aStart++ ] = $bStart++;
181 $prunedCount++;
182 }
183
184 # now the end
185 while ( $aStart <= $aFinish
186 and $bStart <= $bFinish
187 and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) )
188 {
189 $matchVector->[ $aFinish-- ] = $bFinish--;
190 $prunedCount++;
191 }
192
193 # Now compute the equivalence classes of positions of elements
194 $bMatches =
195 _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ );
196 }
197 my $thresh = [];
198 my $links = [];
199
200 my ( $i, $ai, $j, $k );
201 for ( $i = $aStart ; $i <= $aFinish ; $i++ )
202 {
203 $ai = &$keyGen( $a->[$i], @_ );
204 if ( exists( $bMatches->{$ai} ) )
205 {
206 $k = 0;
207 for $j ( @{ $bMatches->{$ai} } )
208 {
209
210 # optimization: most of the time this will be true
211 if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j )
212 {
213 $thresh->[$k] = $j;
214 }
215 else
216 {
217 $k = _replaceNextLargerWith( $thresh, $j, $k );
218 }
219
220 # oddly, it's faster to always test this (CPU cache?).
221 if ( defined($k) )
222 {
223 $links->[$k] =
224 [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ];
225 }
226 }
227 }
228 }
229
230 if (@$thresh)
231 {
232 return $prunedCount + @$thresh if $counting;
233 for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] )
234 {
235 $matchVector->[ $link->[1] ] = $link->[2];
236 }
237 }
238 elsif ($counting)
239 {
240 return $prunedCount;
241 }
242
243 return wantarray ? @$matchVector : $matchVector;
244}
245
246sub traverse_sequences
247{
248 my $a = shift; # array ref
249 my $b = shift; # array ref
250 my $callbacks = shift || {};
251 my $keyGen = shift;
252 my $matchCallback = $callbacks->{'MATCH'} || sub { };
253 my $discardACallback = $callbacks->{'DISCARD_A'} || sub { };
254 my $finishedACallback = $callbacks->{'A_FINISHED'};
255 my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { };
256 my $finishedBCallback = $callbacks->{'B_FINISHED'};
257 my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ );
258
259 # Process all the lines in @$matchVector
260 my $lastA = $#$a;
261 my $lastB = $#$b;
262 my $bi = 0;
263 my $ai;
264
265 for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ )
266 {
267 my $bLine = $matchVector->[$ai];
268 if ( defined($bLine) ) # matched
269 {
270 &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine;
271 &$matchCallback( $ai, $bi++, @_ );
272 }
273 else
274 {
275 &$discardACallback( $ai, $bi, @_ );
276 }
277 }
278
279 # The last entry (if any) processed was a match.
280 # $ai and $bi point just past the last matching lines in their sequences.
281
282 while ( $ai <= $lastA or $bi <= $lastB )
283 {
284
285 # last A?
286 if ( $ai == $lastA + 1 and $bi <= $lastB )
287 {
288 if ( defined($finishedACallback) )
289 {
290 &$finishedACallback( $lastA, @_ );
291 $finishedACallback = undef;
292 }
293 else
294 {
295 &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB;
296 }
297 }
298
299 # last B?
300 if ( $bi == $lastB + 1 and $ai <= $lastA )
301 {
302 if ( defined($finishedBCallback) )
303 {
304 &$finishedBCallback( $lastB, @_ );
305 $finishedBCallback = undef;
306 }
307 else
308 {
309 &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA;
310 }
311 }
312
313 &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA;
314 &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB;
315 }
316
317 return 1;
318}
319
320sub traverse_balanced
321{
322 my $a = shift; # array ref
323 my $b = shift; # array ref
324 my $callbacks = shift || {};
325 my $keyGen = shift;
326 my $matchCallback = $callbacks->{'MATCH'} || sub { };
327 my $discardACallback = $callbacks->{'DISCARD_A'} || sub { };
328 my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { };
329 my $changeCallback = $callbacks->{'CHANGE'};
330 my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ );
331
332 # Process all the lines in match vector
333 my $lastA = $#$a;
334 my $lastB = $#$b;
335 my $bi = 0;
336 my $ai = 0;
337 my $ma = -1;
338 my $mb;
339
340 while (1)
341 {
342
343 # Find next match indices $ma and $mb
344 do {
345 $ma++;
346 } while(
347 $ma <= $#$matchVector
348 && !defined $matchVector->[$ma]
349 );
350
351 last if $ma > $#$matchVector; # end of matchVector?
352 $mb = $matchVector->[$ma];
353
354 # Proceed with discard a/b or change events until
355 # next match
356 while ( $ai < $ma || $bi < $mb )
357 {
358
359 if ( $ai < $ma && $bi < $mb )
360 {
361
362 # Change
363 if ( defined $changeCallback )
364 {
365 &$changeCallback( $ai++, $bi++, @_ );
366 }
367 else
368 {
369 &$discardACallback( $ai++, $bi, @_ );
370 &$discardBCallback( $ai, $bi++, @_ );
371 }
372 }
373 elsif ( $ai < $ma )
374 {
375 &$discardACallback( $ai++, $bi, @_ );
376 }
377 else
378 {
379
380 # $bi < $mb
381 &$discardBCallback( $ai, $bi++, @_ );
382 }
383 }
384
385 # Match
386 &$matchCallback( $ai++, $bi++, @_ );
387 }
388
389 while ( $ai <= $lastA || $bi <= $lastB )
390 {
391 if ( $ai <= $lastA && $bi <= $lastB )
392 {
393
394 # Change
395 if ( defined $changeCallback )
396 {
397 &$changeCallback( $ai++, $bi++, @_ );
398 }
399 else
400 {
401 &$discardACallback( $ai++, $bi, @_ );
402 &$discardBCallback( $ai, $bi++, @_ );
403 }
404 }
405 elsif ( $ai <= $lastA )
406 {
407 &$discardACallback( $ai++, $bi, @_ );
408 }
409 else
410 {
411
412 # $bi <= $lastB
413 &$discardBCallback( $ai, $bi++, @_ );
414 }
415 }
416
417 return 1;
418}
419
420sub prepare
421{
422 my $a = shift; # array ref
423 my $keyGen = shift; # code ref
424
425 # set up code ref
426 $keyGen = sub { $_[0] } unless defined($keyGen);
427
428 return scalar _withPositionsOfInInterval( $a, 0, $#$a, $keyGen, @_ );
429}
430
431sub LCS
432{
433 my $a = shift; # array ref
434 my $b = shift; # array ref or hash ref
435 my $matchVector = _longestCommonSubsequence( $a, $b, 0, @_ );
436 my @retval;
437 my $i;
438 for ( $i = 0 ; $i <= $#$matchVector ; $i++ )
439 {
440 if ( defined( $matchVector->[$i] ) )
441 {
442 push ( @retval, $a->[$i] );
443 }
444 }
445 return wantarray ? @retval : \@retval;
446}
447
448sub LCS_length
449{
450 my $a = shift; # array ref
451 my $b = shift; # array ref or hash ref
452 return _longestCommonSubsequence( $a, $b, 1, @_ );
453}
454
455sub LCSidx
456{
457 my $a= shift @_;
458 my $b= shift @_;
459 my $match= _longestCommonSubsequence( $a, $b, 0, @_ );
460 my @am= grep defined $match->[$_], 0..$#$match;
461 my @bm= @{$match}[@am];
462 return \@am, \@bm;
463}
464
465sub compact_diff
466{
467 my $a= shift @_;
468 my $b= shift @_;
469 my( $am, $bm )= LCSidx( $a, $b, @_ );
470 my @cdiff;
471 my( $ai, $bi )= ( 0, 0 );
472 push @cdiff, $ai, $bi;
473 while( 1 ) {
474 while( @$am && $ai == $am->[0] && $bi == $bm->[0] ) {
475 shift @$am;
476 shift @$bm;
477 ++$ai, ++$bi;
478 }
479 push @cdiff, $ai, $bi;
480 last if ! @$am;
481 $ai = $am->[0];
482 $bi = $bm->[0];
483 push @cdiff, $ai, $bi;
484 }
485 push @cdiff, 0+@$a, 0+@$b
486 if $ai < @$a || $bi < @$b;
487 return wantarray ? @cdiff : \@cdiff;
488}
489
490sub diff
491{
492 my $a = shift; # array ref
493 my $b = shift; # array ref
494 my $retval = [];
495 my $hunk = [];
496 my $discard = sub {
497 push @$hunk, [ '-', $_[0], $a->[ $_[0] ] ];
498 };
499 my $add = sub {
500 push @$hunk, [ '+', $_[1], $b->[ $_[1] ] ];
501 };
502 my $match = sub {
503 push @$retval, $hunk
504 if 0 < @$hunk;
505 $hunk = []
506 };
507 traverse_sequences( $a, $b,
508 { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ );
509 &$match();
510 return wantarray ? @$retval : $retval;
511}
512
513sub sdiff
514{
515 my $a = shift; # array ref
516 my $b = shift; # array ref
517 my $retval = [];
518 my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) };
519 my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) };
520 my $change = sub {
521 push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] );
522 };
523 my $match = sub {
524 push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] );
525 };
526 traverse_balanced(
527 $a,
528 $b,
529 {
530 MATCH => $match,
531 DISCARD_A => $discard,
532 DISCARD_B => $add,
533 CHANGE => $change,
534 },
535 @_
536 );
537 return wantarray ? @$retval : $retval;
538}
539
540########################################
541my $Root= __PACKAGE__;
542package Algorithm::Diff::_impl;
543use strict;
544
545sub _Idx() { 0 } # $me->[_Idx]: Ref to array of hunk indices
546 # 1 # $me->[1]: Ref to first sequence
547 # 2 # $me->[2]: Ref to second sequence
548sub _End() { 3 } # $me->[_End]: Diff between forward and reverse pos
549sub _Same() { 4 } # $me->[_Same]: 1 if pos 1 contains unchanged items
550sub _Base() { 5 } # $me->[_Base]: Added to range's min and max
551sub _Pos() { 6 } # $me->[_Pos]: Which hunk is currently selected
552sub _Off() { 7 } # $me->[_Off]: Offset into _Idx for current position
553sub _Min() { -2 } # Added to _Off to get min instead of max+1
554
555sub Die
556{
557 require Carp;
558 Carp::confess( @_ );
559}
560
561sub _ChkPos
562{
563 my( $me )= @_;
564 return if $me->[_Pos];
565 my $meth= ( caller(1) )[3];
566 Die( "Called $meth on 'reset' object" );
567}
568
569sub _ChkSeq
570{
571 my( $me, $seq )= @_;
572 return $seq + $me->[_Off]
573 if 1 == $seq || 2 == $seq;
574 my $meth= ( caller(1) )[3];
575 Die( "$meth: Invalid sequence number ($seq); must be 1 or 2" );
576}
577
578sub getObjPkg
579{
580 my( $us )= @_;
581 return ref $us if ref $us;
582 return $us . "::_obj";
583}
584
585sub new
586{
587 my( $us, $seq1, $seq2, $opts ) = @_;
588 my @args;
589 for( $opts->{keyGen} ) {
590 push @args, $_ if $_;
591 }
592 for( $opts->{keyGenArgs} ) {
593 push @args, @$_ if $_;
594 }
595 my $cdif= Algorithm::Diff::compact_diff( $seq1, $seq2, @args );
596 my $same= 1;
597 if( 0 == $cdif->[2] && 0 == $cdif->[3] ) {
598 $same= 0;
599 splice @$cdif, 0, 2;
600 }
601 my @obj= ( $cdif, $seq1, $seq2 );
602 $obj[_End] = (1+@$cdif)/2;
603 $obj[_Same] = $same;
604 $obj[_Base] = 0;
605 my $me = bless \@obj, $us->getObjPkg();
606 $me->Reset( 0 );
607 return $me;
608}
609
610sub Reset
611{
612 my( $me, $pos )= @_;
613 $pos= int( $pos || 0 );
614 $pos += $me->[_End]
615 if $pos < 0;
616 $pos= 0
617 if $pos < 0 || $me->[_End] <= $pos;
618 $me->[_Pos]= $pos || !1;
619 $me->[_Off]= 2*$pos - 1;
620 return $me;
621}
622
623sub Base
624{
625 my( $me, $base )= @_;
626 my $oldBase= $me->[_Base];
627 $me->[_Base]= 0+$base if defined $base;
628 return $oldBase;
629}
630
631sub Copy
632{
633 my( $me, $pos, $base )= @_;
634 my @obj= @$me;
635 my $you= bless \@obj, ref($me);
636 $you->Reset( $pos ) if defined $pos;
637 $you->Base( $base );
638 return $you;
639}
640
641sub Next {
642 my( $me, $steps )= @_;
643 $steps= 1 if ! defined $steps;
644 if( $steps ) {
645 my $pos= $me->[_Pos];
646 my $new= $pos + $steps;
647 $new= 0 if $pos && $new < 0;
648 $me->Reset( $new )
649 }
650 return $me->[_Pos];
651}
652
653sub Prev {
654 my( $me, $steps )= @_;
655 $steps= 1 if ! defined $steps;
656 my $pos= $me->Next(-$steps);
657 $pos -= $me->[_End] if $pos;
658 return $pos;
659}
660
661sub Diff {
662 my( $me )= @_;
663 $me->_ChkPos();
664 return 0 if $me->[_Same] == ( 1 & $me->[_Pos] );
665 my $ret= 0;
666 my $off= $me->[_Off];
667 for my $seq ( 1, 2 ) {
668 $ret |= $seq
669 if $me->[_Idx][ $off + $seq + _Min ]
670 < $me->[_Idx][ $off + $seq ];
671 }
672 return $ret;
673}
674
675sub Min {
676 my( $me, $seq, $base )= @_;
677 $me->_ChkPos();
678 my $off= $me->_ChkSeq($seq);
679 $base= $me->[_Base] if !defined $base;
680 return $base + $me->[_Idx][ $off + _Min ];
681}
682
683sub Max {
684 my( $me, $seq, $base )= @_;
685 $me->_ChkPos();
686 my $off= $me->_ChkSeq($seq);
687 $base= $me->[_Base] if !defined $base;
688 return $base + $me->[_Idx][ $off ] -1;
689}
690
691sub Range {
692 my( $me, $seq, $base )= @_;
693 $me->_ChkPos();
694 my $off = $me->_ChkSeq($seq);
695 if( !wantarray ) {
696 return $me->[_Idx][ $off ]
697 - $me->[_Idx][ $off + _Min ];
698 }
699 $base= $me->[_Base] if !defined $base;
700 return ( $base + $me->[_Idx][ $off + _Min ] )
701 .. ( $base + $me->[_Idx][ $off ] - 1 );
702}
703
704sub Items {
705 my( $me, $seq )= @_;
706 $me->_ChkPos();
707 my $off = $me->_ChkSeq($seq);
708 if( !wantarray ) {
709 return $me->[_Idx][ $off ]
710 - $me->[_Idx][ $off + _Min ];
711 }
712 return
713 @{$me->[$seq]}[
714 $me->[_Idx][ $off + _Min ]
715 .. ( $me->[_Idx][ $off ] - 1 )
716 ];
717}
718
719sub Same {
720 my( $me )= @_;
721 $me->_ChkPos();
722 return wantarray ? () : 0
723 if $me->[_Same] != ( 1 & $me->[_Pos] );
724 return $me->Items(1);
725}
726
727my %getName;
728BEGIN {
729 %getName= (
730 same => \&Same,
731 diff => \&Diff,
732 base => \&Base,
733 min => \&Min,
734 max => \&Max,
735 range=> \&Range,
736 items=> \&Items, # same thing
737 );
738}
739
740sub Get
741{
742 my $me= shift @_;
743 $me->_ChkPos();
744 my @value;
745 for my $arg ( @_ ) {
746 for my $word ( split ' ', $arg ) {
747 my $meth;
748 if( $word !~ /^(-?\d+)?([a-zA-Z]+)([12])?$/
749 || not $meth= $getName{ lc $2 }
750 ) {
751 Die( $Root, ", Get: Invalid request ($word)" );
752 }
753 my( $base, $name, $seq )= ( $1, $2, $3 );
754 push @value, scalar(
755 4 == length($name)
756 ? $meth->( $me )
757 : $meth->( $me, $seq, $base )
758 );
759 }
760 }
761 if( wantarray ) {
762 return @value;
763 } elsif( 1 == @value ) {
764 return $value[0];
765 }
766 Die( 0+@value, " values requested from ",
767 $Root, "'s Get in scalar context" );
768}
769
770
771my $Obj= getObjPkg($Root);
772no strict 'refs';
773
774for my $meth ( qw( new getObjPkg ) ) {
775 *{$Root."::".$meth} = \&{$meth};
776 *{$Obj ."::".$meth} = \&{$meth};
777}
778for my $meth ( qw(
779 Next Prev Reset Copy Base Diff
780 Same Items Range Min Max Get
781 _ChkPos _ChkSeq
782) ) {
783 *{$Obj."::".$meth} = \&{$meth};
784}
785
7861;
787__END__
788
789=head1 NAME
790
791Algorithm::Diff - Compute `intelligent' differences between two files / lists
792
793=head1 SYNOPSIS
794
795 require Algorithm::Diff;
796
797 # This example produces traditional 'diff' output:
798
799 my $diff = Algorithm::Diff->new( \@seq1, \@seq2 );
800
801 $diff->Base( 1 ); # Return line numbers, not indices
802 while( $diff->Next() ) {
803 next if $diff->Same();
804 my $sep = '';
805 if( ! $diff->Items(2) ) {
806 sprintf "%d,%dd%d\n",
807 $diff->Get(qw( Min1 Max1 Max2 ));
808 } elsif( ! $diff->Items(1) ) {
809 sprint "%da%d,%d\n",
810 $diff->Get(qw( Max1 Min2 Max2 ));
811 } else {
812 $sep = "---\n";
813 sprintf "%d,%dc%d,%d\n",
814 $diff->Get(qw( Min1 Max1 Min2 Max2 ));
815 }
816 print "< $_" for $diff->Items(1);
817 print $sep;
818 print "> $_" for $diff->Items(2);
819 }
820
821
822 # Alternate interfaces:
823
824 use Algorithm::Diff qw(
825 LCS LCS_length LCSidx
826 diff sdiff compact_diff
827 traverse_sequences traverse_balanced );
828
829 @lcs = LCS( \@seq1, \@seq2 );
830 $lcsref = LCS( \@seq1, \@seq2 );
831 $count = LCS_length( \@seq1, \@seq2 );
832
833 ( $seq1idxref, $seq2idxref ) = LCSidx( \@seq1, \@seq2 );
834
835
836 # Complicated interfaces:
837
838 @diffs = diff( \@seq1, \@seq2 );
839
840 @sdiffs = sdiff( \@seq1, \@seq2 );
841
842 @cdiffs = compact_diff( \@seq1, \@seq2 );
843
844 traverse_sequences(
845 \@seq1,
846 \@seq2,
847 { MATCH => \&callback1,
848 DISCARD_A => \&callback2,
849 DISCARD_B => \&callback3,
850 },
851 \&key_generator,
852 @extra_args,
853 );
854
855 traverse_balanced(
856 \@seq1,
857 \@seq2,
858 { MATCH => \&callback1,
859 DISCARD_A => \&callback2,
860 DISCARD_B => \&callback3,
861 CHANGE => \&callback4,
862 },
863 \&key_generator,
864 @extra_args,
865 );
866
867
868=head1 INTRODUCTION
869
870(by Mark-Jason Dominus)
871
872I once read an article written by the authors of C<diff>; they said
873that they worked very hard on the algorithm until they found the
874right one.
875
876I think what they ended up using (and I hope someone will correct me,
877because I am not very confident about this) was the `longest common
878subsequence' method. In the LCS problem, you have two sequences of
879items:
880
881 a b c d f g h j q z
882
883 a b c d e f g i j k r x y z
884
885and you want to find the longest sequence of items that is present in
886both original sequences in the same order. That is, you want to find
887a new sequence I<S> which can be obtained from the first sequence by
888deleting some items, and from the secend sequence by deleting other
889items. You also want I<S> to be as long as possible. In this case I<S>
890is
891
892 a b c d f g j z
893
894From there it's only a small step to get diff-like output:
895
896 e h i k q r x y
897 + - + + - + + +
898
899This module solves the LCS problem. It also includes a canned function
900to generate C<diff>-like output.
901
902It might seem from the example above that the LCS of two sequences is
903always pretty obvious, but that's not always the case, especially when
904the two sequences have many repeated elements. For example, consider
905
906 a x b y c z p d q
907 a b c a x b y c z
908
909A naive approach might start by matching up the C<a> and C<b> that
910appear at the beginning of each sequence, like this:
911
912 a x b y c z p d q
913 a b c a b y c z
914
915This finds the common subsequence C<a b c z>. But actually, the LCS
916is C<a x b y c z>:
917
918 a x b y c z p d q
919 a b c a x b y c z
920
921or
922
923 a x b y c z p d q
924 a b c a x b y c z
925
926=head1 USAGE
927
928(See also the README file and several example
929scripts include with this module.)
930
931This module now provides an object-oriented interface that uses less
932memory and is easier to use than most of the previous procedural
933interfaces. It also still provides several exportable functions. We'll
934deal with these in ascending order of difficulty: C<LCS>,
935C<LCS_length>, C<LCSidx>, OO interface, C<prepare>, C<diff>, C<sdiff>,
936C<traverse_sequences>, and C<traverse_balanced>.
937
938=head2 C<LCS>
939
940Given references to two lists of items, LCS returns an array containing
941their longest common subsequence. In scalar context, it returns a
942reference to such a list.
943
944 @lcs = LCS( \@seq1, \@seq2 );
945 $lcsref = LCS( \@seq1, \@seq2 );
946
947C<LCS> may be passed an optional third parameter; this is a CODE
948reference to a key generation function. See L</KEY GENERATION
949FUNCTIONS>.
950
951 @lcs = LCS( \@seq1, \@seq2, \&keyGen, @args );
952 $lcsref = LCS( \@seq1, \@seq2, \&keyGen, @args );
953
954Additional parameters, if any, will be passed to the key generation
955routine.
956
957=head2 C<LCS_length>
958
959This is just like C<LCS> except it only returns the length of the
960longest common subsequence. This provides a performance gain of about
9619% compared to C<LCS>.
962
963=head2 C<LCSidx>
964
965Like C<LCS> except it returns references to two arrays. The first array
966contains the indices into @seq1 where the LCS items are located. The
967second array contains the indices into @seq2 where the LCS items are located.
968
969Therefore, the following three lists will contain the same values:
970
971 my( $idx1, $idx2 ) = LCSidx( \@seq1, \@seq2 );
972 my @list1 = @seq1[ @$idx1 ];
973 my @list2 = @seq2[ @$idx2 ];
974 my @list3 = LCS( \@seq1, \@seq2 );
975
976=head2 C<new>
977
978 $diff = Algorithm::Diffs->new( \@seq1, \@seq2 );
979 $diff = Algorithm::Diffs->new( \@seq1, \@seq2, \%opts );
980
981C<new> computes the smallest set of additions and deletions necessary
982to turn the first sequence into the second and compactly records them
983in the object.
984
985You use the object to iterate over I<hunks>, where each hunk represents
986a contiguous section of items which should be added, deleted, replaced,
987or left unchanged.
988
989=over 4
990
991The following summary of all of the methods looks a lot like Perl code
992but some of the symbols have different meanings:
993
994 [ ] Encloses optional arguments
995 : Is followed by the default value for an optional argument
996 | Separates alternate return results
997
998Method summary:
999
1000 $obj = Algorithm::Diff->new( \@seq1, \@seq2, [ \%opts ] );
1001 $pos = $obj->Next( [ $count : 1 ] );
1002 $revPos = $obj->Prev( [ $count : 1 ] );
1003 $obj = $obj->Reset( [ $pos : 0 ] );
1004 $copy = $obj->Copy( [ $pos, [ $newBase ] ] );
1005 $oldBase = $obj->Base( [ $newBase ] );
1006
1007Note that all of the following methods C<die> if used on an object that
1008is "reset" (not currently pointing at any hunk).
1009
1010 $bits = $obj->Diff( );
1011 @items|$cnt = $obj->Same( );
1012 @items|$cnt = $obj->Items( $seqNum );
1013 @idxs |$cnt = $obj->Range( $seqNum, [ $base ] );
1014 $minIdx = $obj->Min( $seqNum, [ $base ] );
1015 $maxIdx = $obj->Max( $seqNum, [ $base ] );
1016 @values = $obj->Get( @names );
1017
1018Passing in C<undef> for an optional argument is always treated the same
1019as if no argument were passed in.
1020
1021=item C<Next>
1022
1023 $pos = $diff->Next(); # Move forward 1 hunk
1024 $pos = $diff->Next( 2 ); # Move forward 2 hunks
1025 $pos = $diff->Next(-5); # Move backward 5 hunks
1026
1027C<Next> moves the object to point at the next hunk. The object starts
1028out "reset", which means it isn't pointing at any hunk. If the object
1029is reset, then C<Next()> moves to the first hunk.
1030
1031C<Next> returns a true value iff the move didn't go past the last hunk.
1032So C<Next(0)> will return true iff the object is not reset.
1033
1034Actually, C<Next> returns the object's new position, which is a number
1035between 1 and the number of hunks (inclusive), or returns a false value.
1036
1037=item C<Prev>
1038
1039C<Prev($N)> is almost identical to C<Next(-$N)>; it moves to the $Nth
1040previous hunk. On a 'reset' object, C<Prev()> [and C<Next(-1)>] move
1041to the last hunk.
1042
1043The position returned by C<Prev> is relative to the I<end> of the
1044hunks; -1 for the last hunk, -2 for the second-to-last, etc.
1045
1046=item C<Reset>
1047
1048 $diff->Reset(); # Reset the object's position
1049 $diff->Reset($pos); # Move to the specified hunk
1050 $diff->Reset(1); # Move to the first hunk
1051 $diff->Reset(-1); # Move to the last hunk
1052
1053C<Reset> returns the object, so, for example, you could use
1054C<< $diff->Reset()->Next(-1) >> to get the number of hunks.
1055
1056=item C<Copy>
1057
1058 $copy = $diff->Copy( $newPos, $newBase );
1059
1060C<Copy> returns a copy of the object. The copy and the orignal object
1061share most of their data, so making copies takes very little memory.
1062The copy maintains its own position (separate from the original), which
1063is the main purpose of copies. It also maintains its own base.
1064
1065By default, the copy's position starts out the same as the original
1066object's position. But C<Copy> takes an optional first argument to set the
1067new position, so the following three snippets are equivalent:
1068
1069 $copy = $diff->Copy($pos);
1070
1071 $copy = $diff->Copy();
1072 $copy->Reset($pos);
1073
1074 $copy = $diff->Copy()->Reset($pos);
1075
1076C<Copy> takes an optional second argument to set the base for
1077the copy. If you wish to change the base of the copy but leave
1078the position the same as in the original, here are two
1079equivalent ways:
1080
1081 $copy = $diff->Copy();
1082 $copy->Base( 0 );
1083
1084 $copy = $diff->Copy(undef,0);
1085
1086Here are two equivalent way to get a "reset" copy:
1087
1088 $copy = $diff->Copy(0);
1089
1090 $copy = $diff->Copy()->Reset();
1091
1092=item C<Diff>
1093
1094 $bits = $obj->Diff();
1095
1096C<Diff> returns a true value iff the current hunk contains items that are
1097different between the two sequences. It actually returns one of the
1098follow 4 values:
1099
1100=over 4
1101
1102=item 3
1103
1104C<3==(1|2)>. This hunk contains items from @seq1 and the items
1105from @seq2 that should replace them. Both sequence 1 and 2
1106contain changed items so both the 1 and 2 bits are set.
1107
1108=item 2
1109
1110This hunk only contains items from @seq2 that should be inserted (not
1111items from @seq1). Only sequence 2 contains changed items so only the 2
1112bit is set.
1113
1114=item 1
1115
1116This hunk only contains items from @seq1 that should be deleted (not
1117items from @seq2). Only sequence 1 contains changed items so only the 1
1118bit is set.
1119
1120=item 0
1121
1122This means that the items in this hunk are the same in both sequences.
1123Neither sequence 1 nor 2 contain changed items so neither the 1 nor the
11242 bits are set.
1125
1126=back
1127
1128=item C<Same>
1129
1130C<Same> returns a true value iff the current hunk contains items that
1131are the same in both sequences. It actually returns the list of items
1132if they are the same or an emty list if they aren't. In a scalar
1133context, it returns the size of the list.
1134
1135=item C<Items>
1136
1137 $count = $diff->Items(2);
1138 @items = $diff->Items($seqNum);
1139
1140C<Items> returns the (number of) items from the specified sequence that
1141are part of the current hunk.
1142
1143If the current hunk contains only insertions, then
1144C<< $diff->Items(1) >> will return an empty list (0 in a scalar conext).
1145If the current hunk contains only deletions, then C<< $diff->Items(2) >>
1146will return an empty list (0 in a scalar conext).
1147
1148If the hunk contains replacements, then both C<< $diff->Items(1) >> and
1149C<< $diff->Items(2) >> will return different, non-empty lists.
1150
1151Otherwise, the hunk contains identical items and all of the following
1152will return the same lists:
1153
1154 @items = $diff->Items(1);
1155 @items = $diff->Items(2);
1156 @items = $diff->Same();
1157
1158=item C<Range>
1159
1160 $count = $diff->Range( $seqNum );
1161 @indices = $diff->Range( $seqNum );
1162 @indices = $diff->Range( $seqNum, $base );
1163
1164C<Range> is like C<Items> except that it returns a list of I<indices> to
1165the items rather than the items themselves. By default, the index of
1166the first item (in each sequence) is 0 but this can be changed by
1167calling the C<Base> method. So, by default, the following two snippets
1168return the same lists:
1169
1170 @list = $diff->Items(2);
1171 @list = @seq2[ $diff->Range(2) ];
1172
1173You can also specify the base to use as the second argument. So the
1174following two snippets I<always> return the same lists:
1175
1176 @list = $diff->Items(1);
1177 @list = @seq1[ $diff->Range(1,0) ];
1178
1179=item C<Base>
1180
1181 $curBase = $diff->Base();
1182 $oldBase = $diff->Base($newBase);
1183
1184C<Base> sets and/or returns the current base (usually 0 or 1) that is
1185used when you request range information. The base defaults to 0 so
1186that range information is returned as array indices. You can set the
1187base to 1 if you want to report traditional line numbers instead.
1188
1189=item C<Min>
1190
1191 $min1 = $diff->Min(1);
1192 $min = $diff->Min( $seqNum, $base );
1193
1194C<Min> returns the first value that C<Range> would return (given the
1195same arguments) or returns C<undef> if C<Range> would return an empty
1196list.
1197
1198=item C<Max>
1199
1200C<Max> returns the last value that C<Range> would return or C<undef>.
1201
1202=item C<Get>
1203
1204 ( $n, $x, $r ) = $diff->Get(qw( min1 max1 range1 ));
1205 @values = $diff->Get(qw( 0min2 1max2 range2 same base ));
1206
1207C<Get> returns one or more scalar values. You pass in a list of the
1208names of the values you want returned. Each name must match one of the
1209following regexes:
1210
1211 /^(-?\d+)?(min|max)[12]$/i
1212 /^(range[12]|same|diff|base)$/i
1213
1214The 1 or 2 after a name says which sequence you want the information
1215for (and where allowed, it is required). The optional number before
1216"min" or "max" is the base to use. So the following equalities hold:
1217
1218 $diff->Get('min1') == $diff->Min(1)
1219 $diff->Get('0min2') == $diff->Min(2,0)
1220
1221Using C<Get> in a scalar context when you've passed in more than one
1222name is a fatal error (C<die> is called).
1223
1224=back
1225
1226=head2 C<prepare>
1227
1228Given a reference to a list of items, C<prepare> returns a reference
1229to a hash which can be used when comparing this sequence to other
1230sequences with C<LCS> or C<LCS_length>.
1231
1232 $prep = prepare( \@seq1 );
1233 for $i ( 0 .. 10_000 )
1234 {
1235 @lcs = LCS( $prep, $seq[$i] );
1236 # do something useful with @lcs
1237 }
1238
1239C<prepare> may be passed an optional third parameter; this is a CODE
1240reference to a key generation function. See L</KEY GENERATION
1241FUNCTIONS>.
1242
1243 $prep = prepare( \@seq1, \&keyGen );
1244 for $i ( 0 .. 10_000 )
1245 {
1246 @lcs = LCS( $seq[$i], $prep, \&keyGen );
1247 # do something useful with @lcs
1248 }
1249
1250Using C<prepare> provides a performance gain of about 50% when calling LCS
1251many times compared with not preparing.
1252
1253=head2 C<diff>
1254
1255 @diffs = diff( \@seq1, \@seq2 );
1256 $diffs_ref = diff( \@seq1, \@seq2 );
1257
1258C<diff> computes the smallest set of additions and deletions necessary
1259to turn the first sequence into the second, and returns a description
1260of these changes. The description is a list of I<hunks>; each hunk
1261represents a contiguous section of items which should be added,
1262deleted, or replaced. (Hunks containing unchanged items are not
1263included.)
1264
1265The return value of C<diff> is a list of hunks, or, in scalar context, a
1266reference to such a list. If there are no differences, the list will be
1267empty.
1268
1269Here is an example. Calling C<diff> for the following two sequences:
1270
1271 a b c e h j l m n p
1272 b c d e f j k l m r s t
1273
1274would produce the following list:
1275
1276 (
1277 [ [ '-', 0, 'a' ] ],
1278
1279 [ [ '+', 2, 'd' ] ],
1280
1281 [ [ '-', 4, 'h' ],
1282 [ '+', 4, 'f' ] ],
1283
1284 [ [ '+', 6, 'k' ] ],
1285
1286 [ [ '-', 8, 'n' ],
1287 [ '-', 9, 'p' ],
1288 [ '+', 9, 'r' ],
1289 [ '+', 10, 's' ],
1290 [ '+', 11, 't' ] ],
1291 )
1292
1293There are five hunks here. The first hunk says that the C<a> at
1294position 0 of the first sequence should be deleted (C<->). The second
1295hunk says that the C<d> at position 2 of the second sequence should
1296be inserted (C<+>). The third hunk says that the C<h> at position 4
1297of the first sequence should be removed and replaced with the C<f>
1298from position 4 of the second sequence. And so on.
1299
1300C<diff> may be passed an optional third parameter; this is a CODE
1301reference to a key generation function. See L</KEY GENERATION
1302FUNCTIONS>.
1303
1304Additional parameters, if any, will be passed to the key generation
1305routine.
1306
1307=head2 C<sdiff>
1308
1309 @sdiffs = sdiff( \@seq1, \@seq2 );
1310 $sdiffs_ref = sdiff( \@seq1, \@seq2 );
1311
1312C<sdiff> computes all necessary components to show two sequences
1313and their minimized differences side by side, just like the
1314Unix-utility I<sdiff> does:
1315
1316 same same
1317 before | after
1318 old < -
1319 - > new
1320
1321It returns a list of array refs, each pointing to an array of
1322display instructions. In scalar context it returns a reference
1323to such a list. If there are no differences, the list will have one
1324entry per item, each indicating that the item was unchanged.
1325
1326Display instructions consist of three elements: A modifier indicator
1327(C<+>: Element added, C<->: Element removed, C<u>: Element unmodified,
1328C<c>: Element changed) and the value of the old and new elements, to
1329be displayed side-by-side.
1330
1331An C<sdiff> of the following two sequences:
1332
1333 a b c e h j l m n p
1334 b c d e f j k l m r s t
1335
1336results in
1337
1338 ( [ '-', 'a', '' ],
1339 [ 'u', 'b', 'b' ],
1340 [ 'u', 'c', 'c' ],
1341 [ '+', '', 'd' ],
1342 [ 'u', 'e', 'e' ],
1343 [ 'c', 'h', 'f' ],
1344 [ 'u', 'j', 'j' ],
1345 [ '+', '', 'k' ],
1346 [ 'u', 'l', 'l' ],
1347 [ 'u', 'm', 'm' ],
1348 [ 'c', 'n', 'r' ],
1349 [ 'c', 'p', 's' ],
1350 [ '+', '', 't' ],
1351 )
1352
1353C<sdiff> may be passed an optional third parameter; this is a CODE
1354reference to a key generation function. See L</KEY GENERATION
1355FUNCTIONS>.
1356
1357Additional parameters, if any, will be passed to the key generation
1358routine.
1359
1360=head2 C<compact_diff>
1361
1362C<compact_diff> is much like C<sdiff> except it returns a much more
1363compact description consisting of just one flat list of indices. An
1364example helps explain the format:
1365
1366 my @a = qw( a b c e h j l m n p );
1367 my @b = qw( b c d e f j k l m r s t );
1368 @cdiff = compact_diff( \@a, \@b );
1369 # Returns:
1370 # @a @b @a @b
1371 # start start values values
1372 ( 0, 0, # =
1373 0, 0, # a !
1374 1, 0, # b c = b c
1375 3, 2, # ! d
1376 3, 3, # e = e
1377 4, 4, # f ! h
1378 5, 5, # j = j
1379 6, 6, # ! k
1380 6, 7, # l m = l m
1381 8, 9, # n p ! r s t
1382 10, 12, #
1383 );
1384
1385The 0th, 2nd, 4th, etc. entries are all indices into @seq1 (@a in the
1386above example) indicating where a hunk begins. The 1st, 3rd, 5th, etc.
1387entries are all indices into @seq2 (@b in the above example) indicating
1388where the same hunk begins.
1389
1390So each pair of indices (except the last pair) describes where a hunk
1391begins (in each sequence). Since each hunk must end at the item just
1392before the item that starts the next hunk, the next pair of indices can
1393be used to determine where the hunk ends.
1394
1395So, the first 4 entries (0..3) describe the first hunk. Entries 0 and 1
1396describe where the first hunk begins (and so are always both 0).
1397Entries 2 and 3 describe where the next hunk begins, so subtracting 1
1398from each tells us where the first hunk ends. That is, the first hunk
1399contains items C<$diff[0]> through C<$diff[2] - 1> of the first sequence
1400and contains items C<$diff[1]> through C<$diff[3] - 1> of the second
1401sequence.
1402
1403In other words, the first hunk consists of the following two lists of items:
1404
1405 # 1st pair 2nd pair
1406 # of indices of indices
1407 @list1 = @a[ $cdiff[0] .. $cdiff[2]-1 ];
1408 @list2 = @b[ $cdiff[1] .. $cdiff[3]-1 ];
1409 # Hunk start Hunk end
1410
1411Note that the hunks will always alternate between those that are part of
1412the LCS (those that contain unchanged items) and those that contain
1413changes. This means that all we need to be told is whether the first
1414hunk is a 'same' or 'diff' hunk and we can determine which of the other
1415hunks contain 'same' items or 'diff' items.
1416
1417By convention, we always make the first hunk contain unchanged items.
1418So the 1st, 3rd, 5th, etc. hunks (all odd-numbered hunks if you start
1419counting from 1) all contain unchanged items. And the 2nd, 4th, 6th,
1420etc. hunks (all even-numbered hunks if you start counting from 1) all
1421contain changed items.
1422
1423Since @a and @b don't begin with the same value, the first hunk in our
1424example is empty (otherwise we'd violate the above convention). Note
1425that the first 4 index values in our example are all zero. Plug these
1426values into our previous code block and we get:
1427
1428 @hunk1a = @a[ 0 .. 0-1 ];
1429 @hunk1b = @b[ 0 .. 0-1 ];
1430
1431And C<0..-1> returns the empty list.
1432
1433Move down one pair of indices (2..5) and we get the offset ranges for
1434the second hunk, which contains changed items.
1435
1436Since C<@diff[2..5]> contains (0,0,1,0) in our example, the second hunk
1437consists of these two lists of items:
1438
1439 @hunk2a = @a[ $cdiff[2] .. $cdiff[4]-1 ];
1440 @hunk2b = @b[ $cdiff[3] .. $cdiff[5]-1 ];
1441 # or
1442 @hunk2a = @a[ 0 .. 1-1 ];
1443 @hunk2b = @b[ 0 .. 0-1 ];
1444 # or
1445 @hunk2a = @a[ 0 .. 0 ];
1446 @hunk2b = @b[ 0 .. -1 ];
1447 # or
1448 @hunk2a = ( 'a' );
1449 @hunk2b = ( );
1450
1451That is, we would delete item 0 ('a') from @a.
1452
1453Since C<@diff[4..7]> contains (1,0,3,2) in our example, the third hunk
1454consists of these two lists of items:
1455
1456 @hunk3a = @a[ $cdiff[4] .. $cdiff[6]-1 ];
1457 @hunk3a = @b[ $cdiff[5] .. $cdiff[7]-1 ];
1458 # or
1459 @hunk3a = @a[ 1 .. 3-1 ];
1460 @hunk3a = @b[ 0 .. 2-1 ];
1461 # or
1462 @hunk3a = @a[ 1 .. 2 ];
1463 @hunk3a = @b[ 0 .. 1 ];
1464 # or
1465 @hunk3a = qw( b c );
1466 @hunk3a = qw( b c );
1467
1468Note that this third hunk contains unchanged items as our convention demands.
1469
1470You can continue this process until you reach the last two indices,
1471which will always be the number of items in each sequence. This is
1472required so that subtracting one from each will give you the indices to
1473the last items in each sequence.
1474
1475=head2 C<traverse_sequences>
1476
1477C<traverse_sequences> used to be the most general facility provided by
1478this module (the new OO interface is more powerful and much easier to
1479use).
1480
1481Imagine that there are two arrows. Arrow A points to an element of
1482sequence A, and arrow B points to an element of the sequence B.
1483Initially, the arrows point to the first elements of the respective
1484sequences. C<traverse_sequences> will advance the arrows through the
1485sequences one element at a time, calling an appropriate user-specified
1486callback function before each advance. It willadvance the arrows in
1487such a way that if there are equal elements C<$A[$i]> and C<$B[$j]>
1488which are equal and which are part of the LCS, there will be some moment
1489during the execution of C<traverse_sequences> when arrow A is pointing
1490to C<$A[$i]> and arrow B is pointing to C<$B[$j]>. When this happens,
1491C<traverse_sequences> will call the C<MATCH> callback function and then
1492it will advance both arrows.
1493
1494Otherwise, one of the arrows is pointing to an element of its sequence
1495that is not part of the LCS. C<traverse_sequences> will advance that
1496arrow and will call the C<DISCARD_A> or the C<DISCARD_B> callback,
1497depending on which arrow it advanced. If both arrows point to elements
1498that are not part of the LCS, then C<traverse_sequences> will advance
1499one of them and call the appropriate callback, but it is not specified
1500which it will call.
1501
1502The arguments to C<traverse_sequences> are the two sequences to
1503traverse, and a hash which specifies the callback functions, like this:
1504
1505 traverse_sequences(
1506 \@seq1, \@seq2,
1507 { MATCH => $callback_1,
1508 DISCARD_A => $callback_2,
1509 DISCARD_B => $callback_3,
1510 }
1511 );
1512
1513Callbacks for MATCH, DISCARD_A, and DISCARD_B are invoked with at least
1514the indices of the two arrows as their arguments. They are not expected
1515to return any values. If a callback is omitted from the table, it is
1516not called.
1517
1518Callbacks for A_FINISHED and B_FINISHED are invoked with at least the
1519corresponding index in A or B.
1520
1521If arrow A reaches the end of its sequence, before arrow B does,
1522C<traverse_sequences> will call the C<A_FINISHED> callback when it
1523advances arrow B, if there is such a function; if not it will call
1524C<DISCARD_B> instead. Similarly if arrow B finishes first.
1525C<traverse_sequences> returns when both arrows are at the ends of their
1526respective sequences. It returns true on success and false on failure.
1527At present there is no way to fail.
1528
1529C<traverse_sequences> may be passed an optional fourth parameter; this
1530is a CODE reference to a key generation function. See L</KEY GENERATION
1531FUNCTIONS>.
1532
1533Additional parameters, if any, will be passed to the key generation function.
1534
1535If you want to pass additional parameters to your callbacks, but don't
1536need a custom key generation function, you can get the default by
1537passing undef:
1538
1539 traverse_sequences(
1540 \@seq1, \@seq2,
1541 { MATCH => $callback_1,
1542 DISCARD_A => $callback_2,
1543 DISCARD_B => $callback_3,
1544 },
1545 undef, # default key-gen
1546 $myArgument1,
1547 $myArgument2,
1548 $myArgument3,
1549 );
1550
1551C<traverse_sequences> does not have a useful return value; you are
1552expected to plug in the appropriate behavior with the callback
1553functions.
1554
1555=head2 C<traverse_balanced>
1556
1557C<traverse_balanced> is an alternative to C<traverse_sequences>. It
1558uses a different algorithm to iterate through the entries in the
1559computed LCS. Instead of sticking to one side and showing element changes
1560as insertions and deletions only, it will jump back and forth between
1561the two sequences and report I<changes> occurring as deletions on one
1562side followed immediatly by an insertion on the other side.
1563
1564In addition to the C<DISCARD_A>, C<DISCARD_B>, and C<MATCH> callbacks
1565supported by C<traverse_sequences>, C<traverse_balanced> supports
1566a C<CHANGE> callback indicating that one element got C<replaced> by another:
1567
1568 traverse_balanced(
1569 \@seq1, \@seq2,
1570 { MATCH => $callback_1,
1571 DISCARD_A => $callback_2,
1572 DISCARD_B => $callback_3,
1573 CHANGE => $callback_4,
1574 }
1575 );
1576
1577If no C<CHANGE> callback is specified, C<traverse_balanced>
1578will map C<CHANGE> events to C<DISCARD_A> and C<DISCARD_B> actions,
1579therefore resulting in a similar behaviour as C<traverse_sequences>
1580with different order of events.
1581
1582C<traverse_balanced> might be a bit slower than C<traverse_sequences>,
1583noticable only while processing huge amounts of data.
1584
1585The C<sdiff> function of this module
1586is implemented as call to C<traverse_balanced>.
1587
1588C<traverse_balanced> does not have a useful return value; you are expected to
1589plug in the appropriate behavior with the callback functions.
1590
1591=head1 KEY GENERATION FUNCTIONS
1592
1593Most of the functions accept an optional extra parameter. This is a
1594CODE reference to a key generating (hashing) function that should return
1595a string that uniquely identifies a given element. It should be the
1596case that if two elements are to be considered equal, their keys should
1597be the same (and the other way around). If no key generation function
1598is provided, the key will be the element as a string.
1599
1600By default, comparisons will use "eq" and elements will be turned into keys
1601using the default stringizing operator '""'.
1602
1603Where this is important is when you're comparing something other than
1604strings. If it is the case that you have multiple different objects
1605that should be considered to be equal, you should supply a key
1606generation function. Otherwise, you have to make sure that your arrays
1607contain unique references.
1608
1609For instance, consider this example:
1610
1611 package Person;
1612
1613 sub new
1614 {
1615 my $package = shift;
1616 return bless { name => '', ssn => '', @_ }, $package;
1617 }
1618
1619 sub clone
1620 {
1621 my $old = shift;
1622 my $new = bless { %$old }, ref($old);
1623 }
1624
1625 sub hash
1626 {
1627 return shift()->{'ssn'};
1628 }
1629
1630 my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' );
1631 my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' );
1632 my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' );
1633 my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' );
1634 my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' );
1635
1636If you did this:
1637
1638 my $array1 = [ $person1, $person2, $person4 ];
1639 my $array2 = [ $person1, $person3, $person4, $person5 ];
1640 Algorithm::Diff::diff( $array1, $array2 );
1641
1642everything would work out OK (each of the objects would be converted
1643into a string like "Person=HASH(0x82425b0)" for comparison).
1644
1645But if you did this:
1646
1647 my $array1 = [ $person1, $person2, $person4 ];
1648 my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
1649 Algorithm::Diff::diff( $array1, $array2 );
1650
1651$person4 and $person4->clone() (which have the same name and SSN)
1652would be seen as different objects. If you wanted them to be considered
1653equivalent, you would have to pass in a key generation function:
1654
1655 my $array1 = [ $person1, $person2, $person4 ];
1656 my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
1657 Algorithm::Diff::diff( $array1, $array2, \&Person::hash );
1658
1659This would use the 'ssn' field in each Person as a comparison key, and
1660so would consider $person4 and $person4->clone() as equal.
1661
1662You may also pass additional parameters to the key generation function
1663if you wish.
1664
1665=head1 ERROR CHECKING
1666
1667If you pass these routines a non-reference and they expect a reference,
1668they will die with a message.
1669
1670=head1 AUTHOR
1671
1672This version released by Tye McQueen (http://perlmonks.org/?node=tye).
1673
1674=head1 LICENSE
1675
1676Parts Copyright (c) 2000-2004 Ned Konz. All rights reserved.
1677Parts by Tye McQueen.
1678
1679This program is free software; you can redistribute it and/or modify it
1680under the same terms as Perl.
1681
1682=head1 MAILING LIST
1683
1684Mark-Jason still maintains a mailing list. To join a low-volume mailing
1685list for announcements related to diff and Algorithm::Diff, send an
1686empty mail message to mjd-perl-diff-request@plover.com.
1687
1688=head1 CREDITS
1689
1690Versions through 0.59 (and much of this documentation) were written by:
1691
1692Mark-Jason Dominus, mjd-perl-diff@plover.com
1693
1694This version borrows some documentation and routine names from
1695Mark-Jason's, but Diff.pm's code was completely replaced.
1696
1697This code was adapted from the Smalltalk code of Mario Wolczko
1698<mario@wolczko.com>, which is available at
1699ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
1700
1701C<sdiff> and C<traverse_balanced> were written by Mike Schilli
1702<m@perlmeister.com>.
1703
1704The algorithm is that described in
1705I<A Fast Algorithm for Computing Longest Common Subsequences>,
1706CACM, vol.20, no.5, pp.350-353, May 1977, with a few
1707minor improvements to improve the speed.
1708
1709Much work was done by Ned Konz (perl@bike-nomad.com).
1710
1711The OO interface and some other changes are by Tye McQueen.
1712
1713=cut
diff --git a/pintos-progos/tests/Make.tests b/pintos-progos/tests/Make.tests
new file mode 100644
index 0000000..358e697
--- /dev/null
+++ b/pintos-progos/tests/Make.tests
@@ -0,0 +1,76 @@
1# -*- makefile -*-
2
3include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS))
4
5PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS))
6TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS))
7EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES))
8
9OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES))
10ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES))
11RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES))
12
13ifdef PROGS
14include ../../Makefile.userprog
15endif
16
17TIMEOUT = 60
18
19clean::
20 rm -f $(OUTPUTS) $(ERRORS) $(RESULTS)
21
22grade:: results
23 $(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@
24
25check:: results
26 @cat $<
27 @COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \
28 FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \
29 if [ $$FAILURES = 0 ]; then \
30 echo "All $$COUNT tests passed."; \
31 else \
32 echo "$$FAILURES of $$COUNT tests failed."; \
33 exit 1; \
34 fi
35
36results: $(RESULTS)
37 @for d in $(TESTS) $(EXTRA_GRADES); do \
38 if echo PASS | cmp -s $$d.result -; then \
39 echo "pass $$d"; \
40 else \
41 echo "FAIL $$d"; \
42 fi; \
43 done > $@
44
45outputs:: $(OUTPUTS)
46
47$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog)))
48$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES)))
49$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test)))
50
51# Prevent an environment variable VERBOSE from surprising us.
52VERBOSE =
53
54TESTCMD = pintos -v -k -T $(TIMEOUT)
55TESTCMD += $(SIMULATOR)
56TESTCMD += $(PINTOSOPTS)
57ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
58TESTCMD += $(FILESYSSOURCE)
59TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
60endif
61ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
62TESTCMD += --swap-size=4
63endif
64TESTCMD += -- -q
65TESTCMD += $(KERNELFLAGS)
66ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
67TESTCMD += -f
68endif
69TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F))
70TESTCMD += < /dev/null
71TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output
72%.output: kernel.bin loader.bin
73 $(TESTCMD)
74
75%.result: %.ck %.output
76 perl -I$(SRCDIR) $< $* $@
diff --git a/pintos-progos/tests/arc4.c b/pintos-progos/tests/arc4.c
new file mode 100644
index 0000000..b033cc6
--- /dev/null
+++ b/pintos-progos/tests/arc4.c
@@ -0,0 +1,53 @@
1#include <stdint.h>
2#include "tests/arc4.h"
3
4/* Swap bytes. */
5static inline void
6swap_byte (uint8_t *a, uint8_t *b)
7{
8 uint8_t t = *a;
9 *a = *b;
10 *b = t;
11}
12
13void
14arc4_init (struct arc4 *arc4, const void *key_, size_t size)
15{
16 const uint8_t *key = key_;
17 size_t key_idx;
18 uint8_t *s;
19 int i, j;
20
21 s = arc4->s;
22 arc4->i = arc4->j = 0;
23 for (i = 0; i < 256; i++)
24 s[i] = i;
25 for (key_idx = 0, i = j = 0; i < 256; i++)
26 {
27 j = (j + s[i] + key[key_idx]) & 255;
28 swap_byte (s + i, s + j);
29 if (++key_idx >= size)
30 key_idx = 0;
31 }
32}
33
34void
35arc4_crypt (struct arc4 *arc4, void *buf_, size_t size)
36{
37 uint8_t *buf = buf_;
38 uint8_t *s;
39 uint8_t i, j;
40
41 s = arc4->s;
42 i = arc4->i;
43 j = arc4->j;
44 while (size-- > 0)
45 {
46 i += 1;
47 j += s[i];
48 swap_byte (s + i, s + j);
49 *buf++ ^= s[(s[i] + s[j]) & 255];
50 }
51 arc4->i = i;
52 arc4->j = j;
53}
diff --git a/pintos-progos/tests/arc4.h b/pintos-progos/tests/arc4.h
new file mode 100644
index 0000000..61c533a
--- /dev/null
+++ b/pintos-progos/tests/arc4.h
@@ -0,0 +1,17 @@
1#ifndef TESTS_ARC4_H
2#define TESTS_ARC4_H
3
4#include <stddef.h>
5#include <stdint.h>
6
7/* Alleged RC4 algorithm encryption state. */
8struct arc4
9 {
10 uint8_t s[256];
11 uint8_t i, j;
12 };
13
14void arc4_init (struct arc4 *, const void *, size_t);
15void arc4_crypt (struct arc4 *, void *, size_t);
16
17#endif /* tests/arc4.h */
diff --git a/pintos-progos/tests/arc4.pm b/pintos-progos/tests/arc4.pm
new file mode 100644
index 0000000..df19216
--- /dev/null
+++ b/pintos-progos/tests/arc4.pm
@@ -0,0 +1,29 @@
1use strict;
2use warnings;
3
4sub arc4_init {
5 my ($key) = @_;
6 my (@s) = 0...255;
7 my ($j) = 0;
8 for my $i (0...255) {
9 $j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff;
10 @s[$i, $j] = @s[$j, $i];
11 }
12 return (0, 0, @s);
13}
14
15sub arc4_crypt {
16 my ($arc4, $buf) = @_;
17 my ($i, $j, @s) = @$arc4;
18 my ($out) = "";
19 for my $c (split (//, $buf)) {
20 $i = ($i + 1) & 0xff;
21 $j = ($j + $s[$i]) & 0xff;
22 @s[$i, $j] = @s[$j, $i];
23 $out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]);
24 }
25 @$arc4 = ($i, $j, @s);
26 return $out;
27}
28
291;
diff --git a/pintos-progos/tests/cksum.c b/pintos-progos/tests/cksum.c
new file mode 100644
index 0000000..92a2995
--- /dev/null
+++ b/pintos-progos/tests/cksum.c
@@ -0,0 +1,92 @@
1/* crctab[] and cksum() are from the `cksum' entry in SUSv3. */
2
3#include <stdint.h>
4#include "tests/cksum.h"
5
6static unsigned long crctab[] = {
7 0x00000000,
8 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
9 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
10 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
11 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
12 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
13 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
14 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
15 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
16 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
17 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
18 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
19 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
20 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
21 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
22 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
23 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
24 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
25 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
26 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
27 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
28 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
29 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
30 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
31 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
32 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
33 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
34 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
35 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
36 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
37 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
38 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
39 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
40 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
41 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
42 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
43 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
44 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
45 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
46 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
47 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
48 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
49 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
50 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
51 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
52 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
53 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
54 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
55 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
56 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
57 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
58 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
59};
60
61/* This is the algorithm used by the Posix `cksum' utility. */
62unsigned long
63cksum (const void *b_, size_t n)
64{
65 const unsigned char *b = b_;
66 uint32_t s = 0;
67 size_t i;
68 for (i = n; i > 0; --i)
69 {
70 unsigned char c = *b++;
71 s = (s << 8) ^ crctab[(s >> 24) ^ c];
72 }
73 while (n != 0)
74 {
75 unsigned char c = n;
76 n >>= 8;
77 s = (s << 8) ^ crctab[(s >> 24) ^ c];
78 }
79 return ~s;
80}
81
82#ifdef STANDALONE_TEST
83#include <stdio.h>
84int
85main (void)
86{
87 char buf[65536];
88 int n = fread (buf, 1, sizeof buf, stdin);
89 printf ("%lu\n", cksum (buf, n));
90 return 0;
91}
92#endif
diff --git a/pintos-progos/tests/cksum.h b/pintos-progos/tests/cksum.h
new file mode 100644
index 0000000..23a1fe9
--- /dev/null
+++ b/pintos-progos/tests/cksum.h
@@ -0,0 +1,8 @@
1#ifndef TESTS_CKSUM_H
2#define TESTS_CKSUM_H
3
4#include <stddef.h>
5
6unsigned long cksum(const void *, size_t);
7
8#endif /* tests/cksum.h */
diff --git a/pintos-progos/tests/cksum.pm b/pintos-progos/tests/cksum.pm
new file mode 100644
index 0000000..73be5f2
--- /dev/null
+++ b/pintos-progos/tests/cksum.pm
@@ -0,0 +1,87 @@
1# From the `cksum' entry in SUSv3.
2
3use strict;
4use warnings;
5
6my (@crctab) =
7 (0x00000000,
8 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
9 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
10 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
11 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
12 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
13 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
14 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
15 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
16 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
17 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
18 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
19 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
20 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
21 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
22 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
23 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
24 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
25 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
26 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
27 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
28 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
29 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
30 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
31 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
32 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
33 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
34 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
35 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
36 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
37 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
38 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
39 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
40 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
41 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
42 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
43 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
44 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
45 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
46 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
47 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
48 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
49 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
50 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
51 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
52 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
53 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
54 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
55 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
56 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
57 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
58 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4);
59
60sub cksum {
61 my ($b) = @_;
62 my ($n) = length ($b);
63 my ($s) = 0;
64 for my $i (0...$n - 1) {
65 my ($c) = ord (substr ($b, $i, 1));
66 $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
67 $s &= 0xffff_ffff;
68 }
69 while ($n != 0) {
70 my ($c) = $n & 0xff;
71 $n >>= 8;
72 $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
73 $s &= 0xffff_ffff;
74 }
75 return ~$s & 0xffff_ffff;
76}
77
78sub cksum_file {
79 my ($file) = @_;
80 open (FILE, '<', $file) or die "$file: open: $!\n";
81 my ($data);
82 sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n";
83 close (FILE);
84 return cksum ($data);
85}
86
871;
diff --git a/pintos-progos/tests/filesys/Grading.no-vm b/pintos-progos/tests/filesys/Grading.no-vm
new file mode 100644
index 0000000..ee98fc1
--- /dev/null
+++ b/pintos-progos/tests/filesys/Grading.no-vm
@@ -0,0 +1,18 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# This project is primarily about implementing the file system, but
5# all the previous functionality should work too. It's not too easy
6# to screw it up, thus the emphasis.
7
8# 65% for extended file system features.
930% tests/filesys/extended/Rubric.functionality
1015% tests/filesys/extended/Rubric.robustness
1120% tests/filesys/extended/Rubric.persistence
12
13# 20% to not break the provided file system features.
1420% tests/filesys/base/Rubric
15
16# 15% for the rest.
1710% tests/userprog/Rubric.functionality
185% tests/userprog/Rubric.robustness
diff --git a/pintos-progos/tests/filesys/Grading.with-vm b/pintos-progos/tests/filesys/Grading.with-vm
new file mode 100644
index 0000000..e7c041e
--- /dev/null
+++ b/pintos-progos/tests/filesys/Grading.with-vm
@@ -0,0 +1,22 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# This project is primarily about implementing the file system, but
5# all the previous functionality should work too. It's not too easy
6# to screw it up, thus the emphasis.
7
8# 65% for extended file system features.
930% tests/filesys/extended/Rubric.functionality
1015% tests/filesys/extended/Rubric.robustness
1120% tests/filesys/extended/Rubric.persistence
12
13# 20% to not break the provided file system features.
1420% tests/filesys/base/Rubric
15
16# 15% for the rest.
1710% tests/userprog/Rubric.functionality
185% tests/userprog/Rubric.robustness
19
20# Up to 10% bonus for working VM functionality.
218% tests/vm/Rubric.functionality
222% tests/vm/Rubric.robustness
diff --git a/pintos-progos/tests/filesys/base/Make.tests b/pintos-progos/tests/filesys/base/Make.tests
new file mode 100644
index 0000000..e475222
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/Make.tests
@@ -0,0 +1,18 @@
1# -*- makefile -*-
2
3tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \
4lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \
5sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write)
6
7tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \
8tests/filesys/base/,child-syn-read child-syn-wrt)
9
10$(foreach prog,$(tests/filesys/base_PROGS), \
11 $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
12$(foreach prog,$(tests/filesys/base_TESTS), \
13 $(eval $(prog)_SRC += tests/main.c))
14
15tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read
16tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt
17
18tests/filesys/base/syn-read.output: TIMEOUT = 300
diff --git a/pintos-progos/tests/filesys/base/Rubric b/pintos-progos/tests/filesys/base/Rubric
new file mode 100644
index 0000000..49a9d15
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/Rubric
@@ -0,0 +1,19 @@
1Functionality of base file system:
2- Test basic support for small files.
31 sm-create
42 sm-full
52 sm-random
62 sm-seq-block
73 sm-seq-random
8
9- Test basic support for large files.
101 lg-create
112 lg-full
122 lg-random
132 lg-seq-block
143 lg-seq-random
15
16- Test synchronized multiprogram access to files.
174 syn-read
184 syn-write
192 syn-remove
diff --git a/pintos-progos/tests/filesys/base/child-syn-read.c b/pintos-progos/tests/filesys/base/child-syn-read.c
new file mode 100644
index 0000000..77a5e26
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/child-syn-read.c
@@ -0,0 +1,44 @@
1/* Child process for syn-read test.
2 Reads the contents of a test file a byte at a time, in the
3 hope that this will take long enough that we can get a
4 significant amount of contention in the kernel file system
5 code. */
6
7#include <random.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <syscall.h>
11#include "tests/lib.h"
12#include "tests/filesys/base/syn-read.h"
13
14const char *test_name = "child-syn-read";
15
16static char buf[BUF_SIZE];
17
18int
19main (int argc, const char *argv[])
20{
21 int child_idx;
22 int fd;
23 size_t i;
24
25 quiet = true;
26
27 CHECK (argc == 2, "argc must be 2, actually %d", argc);
28 child_idx = atoi (argv[1]);
29
30 random_init (0);
31 random_bytes (buf, sizeof buf);
32
33 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
34 for (i = 0; i < sizeof buf; i++)
35 {
36 char c;
37 CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name);
38 compare_bytes (&c, buf + i, 1, i, file_name);
39 }
40 close (fd);
41
42 return child_idx;
43}
44
diff --git a/pintos-progos/tests/filesys/base/child-syn-wrt.c b/pintos-progos/tests/filesys/base/child-syn-wrt.c
new file mode 100644
index 0000000..1b52584
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/child-syn-wrt.c
@@ -0,0 +1,35 @@
1/* Child process for syn-read test.
2 Writes into part of a test file. Other processes will be
3 writing into other parts at the same time. */
4
5#include <random.h>
6#include <stdlib.h>
7#include <syscall.h>
8#include "tests/lib.h"
9#include "tests/filesys/base/syn-write.h"
10
11char buf[BUF_SIZE];
12
13int
14main (int argc, char *argv[])
15{
16 int child_idx;
17 int fd;
18
19 quiet = true;
20
21 CHECK (argc == 2, "argc must be 2, actually %d", argc);
22 child_idx = atoi (argv[1]);
23
24 random_init (0);
25 random_bytes (buf, sizeof buf);
26
27 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
28 seek (fd, CHUNK_SIZE * child_idx);
29 CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0,
30 "write \"%s\"", file_name);
31 msg ("close \"%s\"", file_name);
32 close (fd);
33
34 return child_idx;
35}
diff --git a/pintos-progos/tests/filesys/base/full.inc b/pintos-progos/tests/filesys/base/full.inc
new file mode 100644
index 0000000..38a0396
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/full.inc
@@ -0,0 +1,20 @@
1/* -*- c -*- */
2
3#include "tests/filesys/seq-test.h"
4#include "tests/main.h"
5
6static char buf[TEST_SIZE];
7
8static size_t
9return_test_size (void)
10{
11 return TEST_SIZE;
12}
13
14void
15test_main (void)
16{
17 seq_test ("quux",
18 buf, sizeof buf, sizeof buf,
19 return_test_size, NULL);
20}
diff --git a/pintos-progos/tests/filesys/base/lg-create.c b/pintos-progos/tests/filesys/base/lg-create.c
new file mode 100644
index 0000000..5c45eee
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-create.c
@@ -0,0 +1,5 @@
1/* Tests that create properly zeros out the contents of a fairly
2 large file. */
3
4#define TEST_SIZE 75678
5#include "tests/filesys/create.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-create.ck b/pintos-progos/tests/filesys/base/lg-create.ck
new file mode 100644
index 0000000..86b2c51
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-create.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-create) begin
7(lg-create) create "blargle"
8(lg-create) open "blargle" for verification
9(lg-create) verified contents of "blargle"
10(lg-create) close "blargle"
11(lg-create) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/base/lg-full.c b/pintos-progos/tests/filesys/base/lg-full.c
new file mode 100644
index 0000000..5f7234d
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-full.c
@@ -0,0 +1,6 @@
1/* Writes out the contents of a fairly large file all at once,
2 and then reads it back to make sure that it was written
3 properly. */
4
5#define TEST_SIZE 75678
6#include "tests/filesys/base/full.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-full.ck b/pintos-progos/tests/filesys/base/lg-full.ck
new file mode 100644
index 0000000..ee6c7f9
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-full.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-full) begin
7(lg-full) create "quux"
8(lg-full) open "quux"
9(lg-full) writing "quux"
10(lg-full) close "quux"
11(lg-full) open "quux" for verification
12(lg-full) verified contents of "quux"
13(lg-full) close "quux"
14(lg-full) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/lg-random.c b/pintos-progos/tests/filesys/base/lg-random.c
new file mode 100644
index 0000000..b6f8873
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-random.c
@@ -0,0 +1,7 @@
1/* Writes out the content of a fairly large file in random order,
2 then reads it back in random order to verify that it was
3 written properly. */
4
5#define BLOCK_SIZE 512
6#define TEST_SIZE (512 * 150)
7#include "tests/filesys/base/random.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-random.ck b/pintos-progos/tests/filesys/base/lg-random.ck
new file mode 100644
index 0000000..dd9f1dd
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-random.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-random) begin
7(lg-random) create "bazzle"
8(lg-random) open "bazzle"
9(lg-random) write "bazzle" in random order
10(lg-random) read "bazzle" in random order
11(lg-random) close "bazzle"
12(lg-random) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.c b/pintos-progos/tests/filesys/base/lg-seq-block.c
new file mode 100644
index 0000000..580c30b
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-block.c
@@ -0,0 +1,7 @@
1/* Writes out a fairly large file sequentially, one fixed-size
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 75678
6#define BLOCK_SIZE 513
7#include "tests/filesys/base/seq-block.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.ck b/pintos-progos/tests/filesys/base/lg-seq-block.ck
new file mode 100644
index 0000000..b789081
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-block.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-seq-block) begin
7(lg-seq-block) create "noodle"
8(lg-seq-block) open "noodle"
9(lg-seq-block) writing "noodle"
10(lg-seq-block) close "noodle"
11(lg-seq-block) open "noodle" for verification
12(lg-seq-block) verified contents of "noodle"
13(lg-seq-block) close "noodle"
14(lg-seq-block) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.c b/pintos-progos/tests/filesys/base/lg-seq-random.c
new file mode 100644
index 0000000..fbb6bba
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-random.c
@@ -0,0 +1,6 @@
1/* Writes out a fairly large file sequentially, one random-sized
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 75678
6#include "tests/filesys/base/seq-random.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.ck b/pintos-progos/tests/filesys/base/lg-seq-random.ck
new file mode 100644
index 0000000..6b2dc82
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-random.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-seq-random) begin
7(lg-seq-random) create "nibble"
8(lg-seq-random) open "nibble"
9(lg-seq-random) writing "nibble"
10(lg-seq-random) close "nibble"
11(lg-seq-random) open "nibble" for verification
12(lg-seq-random) verified contents of "nibble"
13(lg-seq-random) close "nibble"
14(lg-seq-random) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/random.inc b/pintos-progos/tests/filesys/base/random.inc
new file mode 100644
index 0000000..eeeea68
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/random.inc
@@ -0,0 +1,59 @@
1/* -*- c -*- */
2
3#include <random.h>
4#include <stdio.h>
5#include <string.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10#if TEST_SIZE % BLOCK_SIZE != 0
11#error TEST_SIZE must be a multiple of BLOCK_SIZE
12#endif
13
14#define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE)
15
16char buf[TEST_SIZE];
17int order[BLOCK_CNT];
18
19void
20test_main (void)
21{
22 const char *file_name = "bazzle";
23 int fd;
24 size_t i;
25
26 random_init (57);
27 random_bytes (buf, sizeof buf);
28
29 for (i = 0; i < BLOCK_CNT; i++)
30 order[i] = i;
31
32 CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
33 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
34
35 msg ("write \"%s\" in random order", file_name);
36 shuffle (order, BLOCK_CNT, sizeof *order);
37 for (i = 0; i < BLOCK_CNT; i++)
38 {
39 size_t ofs = BLOCK_SIZE * order[i];
40 seek (fd, ofs);
41 if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE)
42 fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
43 }
44
45 msg ("read \"%s\" in random order", file_name);
46 shuffle (order, BLOCK_CNT, sizeof *order);
47 for (i = 0; i < BLOCK_CNT; i++)
48 {
49 char block[BLOCK_SIZE];
50 size_t ofs = BLOCK_SIZE * order[i];
51 seek (fd, ofs);
52 if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE)
53 fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
54 compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name);
55 }
56
57 msg ("close \"%s\"", file_name);
58 close (fd);
59}
diff --git a/pintos-progos/tests/filesys/base/seq-block.inc b/pintos-progos/tests/filesys/base/seq-block.inc
new file mode 100644
index 0000000..d4c1f57
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/seq-block.inc
@@ -0,0 +1,20 @@
1/* -*- c -*- */
2
3#include "tests/filesys/seq-test.h"
4#include "tests/main.h"
5
6static char buf[TEST_SIZE];
7
8static size_t
9return_block_size (void)
10{
11 return BLOCK_SIZE;
12}
13
14void
15test_main (void)
16{
17 seq_test ("noodle",
18 buf, sizeof buf, sizeof buf,
19 return_block_size, NULL);
20}
diff --git a/pintos-progos/tests/filesys/base/seq-random.inc b/pintos-progos/tests/filesys/base/seq-random.inc
new file mode 100644
index 0000000..a4da4c5
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/seq-random.inc
@@ -0,0 +1,22 @@
1/* -*- c -*- */
2
3#include <random.h>
4#include "tests/filesys/seq-test.h"
5#include "tests/main.h"
6
7static char buf[TEST_SIZE];
8
9static size_t
10return_random (void)
11{
12 return random_ulong () % 1031 + 1;
13}
14
15void
16test_main (void)
17{
18 random_init (-1);
19 seq_test ("nibble",
20 buf, sizeof buf, sizeof buf,
21 return_random, NULL);
22}
diff --git a/pintos-progos/tests/filesys/base/sm-create.c b/pintos-progos/tests/filesys/base/sm-create.c
new file mode 100644
index 0000000..6b97ac1
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-create.c
@@ -0,0 +1,5 @@
1/* Tests that create properly zeros out the contents of a fairly
2 small file. */
3
4#define TEST_SIZE 5678
5#include "tests/filesys/create.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-create.ck b/pintos-progos/tests/filesys/base/sm-create.ck
new file mode 100644
index 0000000..8ca80dc
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-create.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-create) begin
7(sm-create) create "blargle"
8(sm-create) open "blargle" for verification
9(sm-create) verified contents of "blargle"
10(sm-create) close "blargle"
11(sm-create) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/base/sm-full.c b/pintos-progos/tests/filesys/base/sm-full.c
new file mode 100644
index 0000000..23ff3d4
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-full.c
@@ -0,0 +1,6 @@
1/* Writes out the contents of a fairly small file all at once,
2 and then reads it back to make sure that it was written
3 properly. */
4
5#define TEST_SIZE 5678
6#include "tests/filesys/base/full.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-full.ck b/pintos-progos/tests/filesys/base/sm-full.ck
new file mode 100644
index 0000000..2e0eb36
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-full.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-full) begin
7(sm-full) create "quux"
8(sm-full) open "quux"
9(sm-full) writing "quux"
10(sm-full) close "quux"
11(sm-full) open "quux" for verification
12(sm-full) verified contents of "quux"
13(sm-full) close "quux"
14(sm-full) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/sm-random.c b/pintos-progos/tests/filesys/base/sm-random.c
new file mode 100644
index 0000000..42d670f
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-random.c
@@ -0,0 +1,7 @@
1/* Writes out the content of a fairly small file in random order,
2 then reads it back in random order to verify that it was
3 written properly. */
4
5#define BLOCK_SIZE 13
6#define TEST_SIZE (13 * 123)
7#include "tests/filesys/base/random.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-random.ck b/pintos-progos/tests/filesys/base/sm-random.ck
new file mode 100644
index 0000000..bda049d
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-random.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-random) begin
7(sm-random) create "bazzle"
8(sm-random) open "bazzle"
9(sm-random) write "bazzle" in random order
10(sm-random) read "bazzle" in random order
11(sm-random) close "bazzle"
12(sm-random) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.c b/pintos-progos/tests/filesys/base/sm-seq-block.c
new file mode 100644
index 0000000..e368327
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-block.c
@@ -0,0 +1,7 @@
1/* Writes out a fairly small file sequentially, one fixed-size
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 5678
6#define BLOCK_SIZE 513
7#include "tests/filesys/base/seq-block.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.ck b/pintos-progos/tests/filesys/base/sm-seq-block.ck
new file mode 100644
index 0000000..0e2939d
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-block.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-seq-block) begin
7(sm-seq-block) create "noodle"
8(sm-seq-block) open "noodle"
9(sm-seq-block) writing "noodle"
10(sm-seq-block) close "noodle"
11(sm-seq-block) open "noodle" for verification
12(sm-seq-block) verified contents of "noodle"
13(sm-seq-block) close "noodle"
14(sm-seq-block) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.c b/pintos-progos/tests/filesys/base/sm-seq-random.c
new file mode 100644
index 0000000..89e5b71
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-random.c
@@ -0,0 +1,6 @@
1/* Writes out a fairly large file sequentially, one random-sized
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 5678
6#include "tests/filesys/base/seq-random.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.ck b/pintos-progos/tests/filesys/base/sm-seq-random.ck
new file mode 100644
index 0000000..2fb368b
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-random.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-seq-random) begin
7(sm-seq-random) create "nibble"
8(sm-seq-random) open "nibble"
9(sm-seq-random) writing "nibble"
10(sm-seq-random) close "nibble"
11(sm-seq-random) open "nibble" for verification
12(sm-seq-random) verified contents of "nibble"
13(sm-seq-random) close "nibble"
14(sm-seq-random) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/syn-read.c b/pintos-progos/tests/filesys/base/syn-read.c
new file mode 100644
index 0000000..7c36a42
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-read.c
@@ -0,0 +1,31 @@
1/* Spawns 10 child processes, all of which read from the same
2 file and make sure that the contents are what they should
3 be. */
4
5#include <random.h>
6#include <stdio.h>
7#include <syscall.h>
8#include "tests/lib.h"
9#include "tests/main.h"
10#include "tests/filesys/base/syn-read.h"
11
12static char buf[BUF_SIZE];
13
14#define CHILD_CNT 10
15
16void
17test_main (void)
18{
19 pid_t children[CHILD_CNT];
20 int fd;
21
22 CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name);
23 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
24 random_bytes (buf, sizeof buf);
25 CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name);
26 msg ("close \"%s\"", file_name);
27 close (fd);
28
29 exec_children ("child-syn-read", children, CHILD_CNT);
30 wait_children (children, CHILD_CNT);
31}
diff --git a/pintos-progos/tests/filesys/base/syn-read.ck b/pintos-progos/tests/filesys/base/syn-read.ck
new file mode 100644
index 0000000..e2f68e8
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-read.ck
@@ -0,0 +1,33 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(syn-read) begin
7(syn-read) create "data"
8(syn-read) open "data"
9(syn-read) write "data"
10(syn-read) close "data"
11(syn-read) exec child 1 of 10: "child-syn-read 0"
12(syn-read) exec child 2 of 10: "child-syn-read 1"
13(syn-read) exec child 3 of 10: "child-syn-read 2"
14(syn-read) exec child 4 of 10: "child-syn-read 3"
15(syn-read) exec child 5 of 10: "child-syn-read 4"
16(syn-read) exec child 6 of 10: "child-syn-read 5"
17(syn-read) exec child 7 of 10: "child-syn-read 6"
18(syn-read) exec child 8 of 10: "child-syn-read 7"
19(syn-read) exec child 9 of 10: "child-syn-read 8"
20(syn-read) exec child 10 of 10: "child-syn-read 9"
21(syn-read) wait for child 1 of 10 returned 0 (expected 0)
22(syn-read) wait for child 2 of 10 returned 1 (expected 1)
23(syn-read) wait for child 3 of 10 returned 2 (expected 2)
24(syn-read) wait for child 4 of 10 returned 3 (expected 3)
25(syn-read) wait for child 5 of 10 returned 4 (expected 4)
26(syn-read) wait for child 6 of 10 returned 5 (expected 5)
27(syn-read) wait for child 7 of 10 returned 6 (expected 6)
28(syn-read) wait for child 8 of 10 returned 7 (expected 7)
29(syn-read) wait for child 9 of 10 returned 8 (expected 8)
30(syn-read) wait for child 10 of 10 returned 9 (expected 9)
31(syn-read) end
32EOF
33pass;
diff --git a/pintos-progos/tests/filesys/base/syn-read.h b/pintos-progos/tests/filesys/base/syn-read.h
new file mode 100644
index 0000000..bff8082
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-read.h
@@ -0,0 +1,7 @@
1#ifndef TESTS_FILESYS_BASE_SYN_READ_H
2#define TESTS_FILESYS_BASE_SYN_READ_H
3
4#define BUF_SIZE 1024
5static const char file_name[] = "data";
6
7#endif /* tests/filesys/base/syn-read.h */
diff --git a/pintos-progos/tests/filesys/base/syn-remove.c b/pintos-progos/tests/filesys/base/syn-remove.c
new file mode 100644
index 0000000..c9ba110
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-remove.c
@@ -0,0 +1,30 @@
1/* Verifies that a deleted file may still be written to and read
2 from. */
3
4#include <random.h>
5#include <string.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10char buf1[1234];
11char buf2[1234];
12
13void
14test_main (void)
15{
16 const char *file_name = "deleteme";
17 int fd;
18
19 CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
20 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
21 CHECK (remove (file_name), "remove \"%s\"", file_name);
22 random_bytes (buf1, sizeof buf1);
23 CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name);
24 msg ("seek \"%s\" to 0", file_name);
25 seek (fd, 0);
26 CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name);
27 compare_bytes (buf2, buf1, sizeof buf1, 0, file_name);
28 msg ("close \"%s\"", file_name);
29 close (fd);
30}
diff --git a/pintos-progos/tests/filesys/base/syn-remove.ck b/pintos-progos/tests/filesys/base/syn-remove.ck
new file mode 100644
index 0000000..16ff11e
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-remove.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(syn-remove) begin
7(syn-remove) create "deleteme"
8(syn-remove) open "deleteme"
9(syn-remove) remove "deleteme"
10(syn-remove) write "deleteme"
11(syn-remove) seek "deleteme" to 0
12(syn-remove) read "deleteme"
13(syn-remove) close "deleteme"
14(syn-remove) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/syn-write.c b/pintos-progos/tests/filesys/base/syn-write.c
new file mode 100644
index 0000000..1439862
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-write.c
@@ -0,0 +1,31 @@
1/* Spawns several child processes to write out different parts of
2 the contents of a file and waits for them to finish. Then
3 reads back the file and verifies its contents. */
4
5#include <random.h>
6#include <stdio.h>
7#include <string.h>
8#include <syscall.h>
9#include "tests/filesys/base/syn-write.h"
10#include "tests/lib.h"
11#include "tests/main.h"
12
13char buf1[BUF_SIZE];
14char buf2[BUF_SIZE];
15
16void
17test_main (void)
18{
19 pid_t children[CHILD_CNT];
20 int fd;
21
22 CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
23
24 exec_children ("child-syn-wrt", children, CHILD_CNT);
25 wait_children (children, CHILD_CNT);
26
27 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
28 CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name);
29 random_bytes (buf2, sizeof buf2);
30 compare_bytes (buf1, buf2, sizeof buf1, 0, file_name);
31}
diff --git a/pintos-progos/tests/filesys/base/syn-write.ck b/pintos-progos/tests/filesys/base/syn-write.ck
new file mode 100644
index 0000000..629a7a2
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-write.ck
@@ -0,0 +1,32 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(syn-write) begin
7(syn-write) create "stuff"
8(syn-write) exec child 1 of 10: "child-syn-wrt 0"
9(syn-write) exec child 2 of 10: "child-syn-wrt 1"
10(syn-write) exec child 3 of 10: "child-syn-wrt 2"
11(syn-write) exec child 4 of 10: "child-syn-wrt 3"
12(syn-write) exec child 5 of 10: "child-syn-wrt 4"
13(syn-write) exec child 6 of 10: "child-syn-wrt 5"
14(syn-write) exec child 7 of 10: "child-syn-wrt 6"
15(syn-write) exec child 8 of 10: "child-syn-wrt 7"
16(syn-write) exec child 9 of 10: "child-syn-wrt 8"
17(syn-write) exec child 10 of 10: "child-syn-wrt 9"
18(syn-write) wait for child 1 of 10 returned 0 (expected 0)
19(syn-write) wait for child 2 of 10 returned 1 (expected 1)
20(syn-write) wait for child 3 of 10 returned 2 (expected 2)
21(syn-write) wait for child 4 of 10 returned 3 (expected 3)
22(syn-write) wait for child 5 of 10 returned 4 (expected 4)
23(syn-write) wait for child 6 of 10 returned 5 (expected 5)
24(syn-write) wait for child 7 of 10 returned 6 (expected 6)
25(syn-write) wait for child 8 of 10 returned 7 (expected 7)
26(syn-write) wait for child 9 of 10 returned 8 (expected 8)
27(syn-write) wait for child 10 of 10 returned 9 (expected 9)
28(syn-write) open "stuff"
29(syn-write) read "stuff"
30(syn-write) end
31EOF
32pass;
diff --git a/pintos-progos/tests/filesys/base/syn-write.h b/pintos-progos/tests/filesys/base/syn-write.h
new file mode 100644
index 0000000..07a6d5a
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-write.h
@@ -0,0 +1,9 @@
1#ifndef TESTS_FILESYS_BASE_SYN_WRITE_H
2#define TESTS_FILESYS_BASE_SYN_WRITE_H
3
4#define CHILD_CNT 10
5#define CHUNK_SIZE 512
6#define BUF_SIZE (CHILD_CNT * CHUNK_SIZE)
7static const char file_name[] = "stuff";
8
9#endif /* tests/filesys/base/syn-write.h */
diff --git a/pintos-progos/tests/filesys/create.inc b/pintos-progos/tests/filesys/create.inc
new file mode 100644
index 0000000..4baf771
--- /dev/null
+++ b/pintos-progos/tests/filesys/create.inc
@@ -0,0 +1,15 @@
1/* -*- c -*- */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7static char buf[TEST_SIZE];
8
9void
10test_main (void)
11{
12 const char *file_name = "blargle";
13 CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
14 check_file (file_name, buf, TEST_SIZE);
15}
diff --git a/pintos-progos/tests/filesys/extended/Make.tests b/pintos-progos/tests/filesys/extended/Make.tests
new file mode 100644
index 0000000..e03b98d
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Make.tests
@@ -0,0 +1,61 @@
1# -*- makefile -*-
2
3raw_tests = dir-empty-name dir-mk-tree dir-mkdir dir-open \
4dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree \
5dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg \
6grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm \
7grow-sparse grow-tell grow-two-files syn-rw
8
9tests/filesys/extended_TESTS = $(patsubst %,tests/filesys/extended/%,$(raw_tests))
10tests/filesys/extended_EXTRA_GRADES = $(patsubst %,tests/filesys/extended/%-persistence,$(raw_tests))
11
12tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \
13tests/filesys/extended/child-syn-rw tests/filesys/extended/tar
14
15$(foreach prog,$(tests/filesys/extended_PROGS), \
16 $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
17$(foreach prog,$(tests/filesys/extended_TESTS), \
18 $(eval $(prog)_SRC += tests/main.c))
19$(foreach prog,$(tests/filesys/extended_TESTS), \
20 $(eval $(prog)_PUTFILES += tests/filesys/extended/tar))
21# The version of GNU make 3.80 on vine barfs if this is split at
22# the last comma.
23$(foreach test,$(tests/filesys/extended_TESTS),$(eval $(test).output: FILESYSSOURCE = --disk=tmp.dsk))
24
25tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c
26tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c
27
28tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw
29
30tests/filesys/extended/dir-vine.output: TIMEOUT = 150
31
32GETTIMEOUT = 60
33
34GETCMD = pintos -v -k -T $(GETTIMEOUT)
35GETCMD += $(PINTOSOPTS)
36GETCMD += $(SIMULATOR)
37GETCMD += $(FILESYSSOURCE)
38GETCMD += -g fs.tar -a $(TEST).tar
39ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
40GETCMD += --swap-size=4
41endif
42GETCMD += -- -q
43GETCMD += $(KERNELFLAGS)
44GETCMD += run 'tar fs.tar /'
45GETCMD += < /dev/null
46GETCMD += 2> $(TEST)-persistence.errors $(if $(VERBOSE),|tee,>) $(TEST)-persistence.output
47
48tests/filesys/extended/%.output: kernel.bin
49 rm -f tmp.dsk
50 pintos-mkdisk tmp.dsk --filesys-size=2
51 $(TESTCMD)
52 $(GETCMD)
53 rm -f tmp.dsk
54$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.output: tests/filesys/extended/$(raw_test).output))
55$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.result: tests/filesys/extended/$(raw_test).result))
56
57TARS = $(addsuffix .tar,$(tests/filesys/extended_TESTS))
58
59clean::
60 rm -f $(TARS)
61 rm -f tests/filesys/extended/can-rmdir-cwd
diff --git a/pintos-progos/tests/filesys/extended/Rubric.functionality b/pintos-progos/tests/filesys/extended/Rubric.functionality
new file mode 100644
index 0000000..91ed6f0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Rubric.functionality
@@ -0,0 +1,26 @@
1Functionality of extended file system:
2- Test directory support.
31 dir-mkdir
43 dir-mk-tree
5
61 dir-rmdir
73 dir-rm-tree
8
95 dir-vine
10
11- Test file growth.
121 grow-create
131 grow-seq-sm
143 grow-seq-lg
153 grow-sparse
163 grow-two-files
171 grow-tell
181 grow-file-size
19
20- Test directory growth.
211 grow-dir-lg
221 grow-root-sm
231 grow-root-lg
24
25- Test writing from multiple processes.
265 syn-rw
diff --git a/pintos-progos/tests/filesys/extended/Rubric.persistence b/pintos-progos/tests/filesys/extended/Rubric.persistence
new file mode 100644
index 0000000..405620a
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Rubric.persistence
@@ -0,0 +1,24 @@
1Persistence of file system:
21 dir-empty-name-persistence
31 dir-mk-tree-persistence
41 dir-mkdir-persistence
51 dir-open-persistence
61 dir-over-file-persistence
71 dir-rm-cwd-persistence
81 dir-rm-parent-persistence
91 dir-rm-root-persistence
101 dir-rm-tree-persistence
111 dir-rmdir-persistence
121 dir-under-file-persistence
131 dir-vine-persistence
141 grow-create-persistence
151 grow-dir-lg-persistence
161 grow-file-size-persistence
171 grow-root-lg-persistence
181 grow-root-sm-persistence
191 grow-seq-lg-persistence
201 grow-seq-sm-persistence
211 grow-sparse-persistence
221 grow-tell-persistence
231 grow-two-files-persistence
241 syn-rw-persistence
diff --git a/pintos-progos/tests/filesys/extended/Rubric.robustness b/pintos-progos/tests/filesys/extended/Rubric.robustness
new file mode 100644
index 0000000..fb9f32f
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Rubric.robustness
@@ -0,0 +1,9 @@
1Robustness of file system:
21 dir-empty-name
31 dir-open
41 dir-over-file
51 dir-under-file
6
73 dir-rm-cwd
82 dir-rm-parent
91 dir-rm-root
diff --git a/pintos-progos/tests/filesys/extended/child-syn-rw.c b/pintos-progos/tests/filesys/extended/child-syn-rw.c
new file mode 100644
index 0000000..0e2217d
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/child-syn-rw.c
@@ -0,0 +1,53 @@
1/* Child process for syn-rw.
2 Reads from a file created by our parent process, which is
3 growing it. We loop until we've read the whole file
4 successfully. Many iterations through the loop will return 0
5 bytes, because the file has not grown in the meantime. That
6 is, we are "busy waiting" for the file to grow.
7 (This test could be improved by adding a "yield" system call
8 and calling yield whenever we receive a 0-byte read.) */
9
10#include <random.h>
11#include <stdlib.h>
12#include <syscall.h>
13#include "tests/filesys/extended/syn-rw.h"
14#include "tests/lib.h"
15
16const char *test_name = "child-syn-rw";
17
18static char buf1[BUF_SIZE];
19static char buf2[BUF_SIZE];
20
21int
22main (int argc, const char *argv[])
23{
24 int child_idx;
25 int fd;
26 size_t ofs;
27
28 quiet = true;
29
30 CHECK (argc == 2, "argc must be 2, actually %d", argc);
31 child_idx = atoi (argv[1]);
32
33 random_init (0);
34 random_bytes (buf1, sizeof buf1);
35
36 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
37 ofs = 0;
38 while (ofs < sizeof buf2)
39 {
40 int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs);
41 CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs),
42 "%zu-byte read on \"%s\" returned invalid value of %d",
43 sizeof buf2 - ofs, file_name, bytes_read);
44 if (bytes_read > 0)
45 {
46 compare_bytes (buf2 + ofs, buf1 + ofs, bytes_read, ofs, file_name);
47 ofs += bytes_read;
48 }
49 }
50 close (fd);
51
52 return child_idx;
53}
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.c b/pintos-progos/tests/filesys/extended/dir-empty-name.c
new file mode 100644
index 0000000..c4859d2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-empty-name.c
@@ -0,0 +1,12 @@
1/* Tries to create a directory named as the empty string,
2 which must return failure. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (!mkdir (""), "mkdir \"\" (must return false)");
12}
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.ck b/pintos-progos/tests/filesys/extended/dir-empty-name.ck
new file mode 100644
index 0000000..d6c5621
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-empty-name.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-empty-name) begin
7(dir-empty-name) mkdir "" (must return false)
8(dir-empty-name) end
9EOF
10pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck
new file mode 100644
index 0000000..fb16afd
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5my ($tree);
6for my $a (0...3) {
7 for my $b (0...2) {
8 for my $c (0...2) {
9 for my $d (0...3) {
10 $tree->{$a}{$b}{$c}{$d} = [''];
11 }
12 }
13 }
14}
15check_archive ($tree);
16pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.c b/pintos-progos/tests/filesys/extended/dir-mk-tree.c
new file mode 100644
index 0000000..a714ff3
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.c
@@ -0,0 +1,12 @@
1/* Creates directories /0/0/0 through /3/2/2 and creates files in
2 the leaf directories. */
3
4#include "tests/filesys/extended/mk-tree.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 make_tree (4, 3, 3, 4);
11}
12
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck
new file mode 100644
index 0000000..a8507e2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-mk-tree) begin
7(dir-mk-tree) creating /0/0/0/0 through /3/2/2/3...
8(dir-mk-tree) open "/0/2/0/3"
9(dir-mk-tree) close "/0/2/0/3"
10(dir-mk-tree) end
11EOF
12pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck
new file mode 100644
index 0000000..7682900
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({'a' => {'b' => ["\0" x 512]}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.c b/pintos-progos/tests/filesys/extended/dir-mkdir.c
new file mode 100644
index 0000000..994f41c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mkdir.c
@@ -0,0 +1,15 @@
1/* Tests mkdir(). */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 CHECK (mkdir ("a"), "mkdir \"a\"");
11 CHECK (create ("a/b", 512), "create \"a/b\"");
12 CHECK (chdir ("a"), "chdir \"a\"");
13 CHECK (open ("b") > 1, "open \"b\"");
14}
15
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.ck b/pintos-progos/tests/filesys/extended/dir-mkdir.ck
new file mode 100644
index 0000000..4644f80
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mkdir.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-mkdir) begin
7(dir-mkdir) mkdir "a"
8(dir-mkdir) create "a/b"
9(dir-mkdir) chdir "a"
10(dir-mkdir) open "b"
11(dir-mkdir) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-open-persistence.ck b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck
new file mode 100644
index 0000000..26ff2f1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"xyzzy" => {}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-open.c b/pintos-progos/tests/filesys/extended/dir-open.c
new file mode 100644
index 0000000..29d18b8
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-open.c
@@ -0,0 +1,21 @@
1/* Opens a directory, then tries to write to it, which must
2 fail. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int fd;
12 int retval;
13
14 CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\"");
15 CHECK ((fd = open ("xyzzy")) > 1, "open \"xyzzy\"");
16
17 msg ("write \"xyzzy\"");
18 retval = write (fd, "foobar", 6);
19 CHECK (retval == -1,
20 "write \"xyzzy\" (must return -1, actually %d)", retval);
21}
diff --git a/pintos-progos/tests/filesys/extended/dir-open.ck b/pintos-progos/tests/filesys/extended/dir-open.ck
new file mode 100644
index 0000000..fccc563
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-open.ck
@@ -0,0 +1,20 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(dir-open) begin
7(dir-open) mkdir "xyzzy"
8(dir-open) open "xyzzy"
9(dir-open) write "xyzzy"
10(dir-open) write "xyzzy" (must return -1, actually -1)
11(dir-open) end
12dir-open: exit(0)
13EOF
14(dir-open) begin
15(dir-open) mkdir "xyzzy"
16(dir-open) open "xyzzy"
17(dir-open) write "xyzzy"
18dir-open: exit(-1)
19EOF
20pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck
new file mode 100644
index 0000000..56b4ed2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"abc" => {}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.c b/pintos-progos/tests/filesys/extended/dir-over-file.c
new file mode 100644
index 0000000..cdd2c62
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-over-file.c
@@ -0,0 +1,13 @@
1/* Tries to create a file with the same name as an existing
2 directory, which must return failure. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (mkdir ("abc"), "mkdir \"abc\"");
12 CHECK (!create ("abc", 0), "create \"abc\" (must return false)");
13}
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.ck b/pintos-progos/tests/filesys/extended/dir-over-file.ck
new file mode 100644
index 0000000..aae1c1e
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-over-file.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-over-file) begin
7(dir-over-file) mkdir "abc"
8(dir-over-file) create "abc" (must return false)
9(dir-over-file) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck
new file mode 100644
index 0000000..7533570
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck
@@ -0,0 +1,8 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5my ($cwd_removable) = read_text_file ("tests/filesys/extended/can-rmdir-cwd");
6$cwd_removable eq 'YES' || $cwd_removable eq 'NO' or die;
7check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}});
8pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.c b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c
new file mode 100644
index 0000000..78e13de
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c
@@ -0,0 +1,75 @@
1/* Tries to remove the current directory, which may succeed or
2 fail. The requirements in each case are different; refer to
3 the assignment for details. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static int
10wrap_open (const char *name)
11{
12 static int fds[8], fd_cnt;
13 int fd, i;
14
15 CHECK ((fd = open (name)) > 1, "open \"%s\"", name);
16 for (i = 0; i < fd_cnt; i++)
17 if (fds[i] == fd)
18 fail ("fd returned is not unique");
19 fds[fd_cnt++] = fd;
20 return fd;
21}
22
23void
24test_main (void)
25{
26 int root_fd, a_fd0;
27 char name[READDIR_MAX_LEN + 1];
28
29 root_fd = wrap_open ("/");
30 CHECK (mkdir ("a"), "mkdir \"a\"");
31
32 a_fd0 = wrap_open ("/a");
33 CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
34 CHECK (inumber (root_fd) != inumber (a_fd0),
35 "\"/\" and \"/a\" must have different inumbers");
36
37 CHECK (chdir ("a"), "chdir \"a\"");
38
39 msg ("try to remove \"/a\"");
40 if (remove ("/a"))
41 {
42 msg ("remove successful");
43
44 CHECK (open ("/a") == -1, "open \"/a\" (must fail)");
45 CHECK (open (".") == -1, "open \".\" (must fail)");
46 CHECK (open ("..") == -1, "open \"..\" (must fail)");
47 CHECK (!create ("x", 512), "create \"x\" (must fail)");
48 }
49 else
50 {
51 int a_fd1, a_fd2, a_fd3;
52
53 msg ("remove failed");
54
55 CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)");
56 CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)");
57 CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)");
58
59 a_fd1 = wrap_open ("/a");
60 a_fd2 = wrap_open (".");
61 CHECK (inumber (a_fd1) == inumber (a_fd2),
62 "\"/a\" and \".\" must have same inumber");
63 CHECK (inumber (root_fd) != inumber (a_fd1),
64 "\"/\" and \"/a\" must have different inumbers");
65
66 CHECK (chdir ("/a"), "chdir \"/a\"");
67 a_fd3 = wrap_open (".");
68 CHECK (inumber (a_fd3) == inumber (a_fd1),
69 "\".\" must have same inumber as before");
70
71 CHECK (chdir ("/"), "chdir \"/\"");
72 CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)");
73 }
74 CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
75}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck
new file mode 100644
index 0000000..6fa4739
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck
@@ -0,0 +1,51 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1,
6 {NO => <<'EOF', YES => <<'EOF'});
7(dir-rm-cwd) begin
8(dir-rm-cwd) open "/"
9(dir-rm-cwd) mkdir "a"
10(dir-rm-cwd) open "/a"
11(dir-rm-cwd) verify "/a" is empty
12(dir-rm-cwd) "/" and "/a" must have different inumbers
13(dir-rm-cwd) chdir "a"
14(dir-rm-cwd) try to remove "/a"
15(dir-rm-cwd) remove failed
16(dir-rm-cwd) try to remove "../a" (must fail)
17(dir-rm-cwd) try to remove ".././a" (must fail)
18(dir-rm-cwd) try to remove "/./a" (must fail)
19(dir-rm-cwd) open "/a"
20(dir-rm-cwd) open "."
21(dir-rm-cwd) "/a" and "." must have same inumber
22(dir-rm-cwd) "/" and "/a" must have different inumbers
23(dir-rm-cwd) chdir "/a"
24(dir-rm-cwd) open "."
25(dir-rm-cwd) "." must have same inumber as before
26(dir-rm-cwd) chdir "/"
27(dir-rm-cwd) try to remove "a" (must fail: still open)
28(dir-rm-cwd) verify "/a" is empty
29(dir-rm-cwd) end
30EOF
31(dir-rm-cwd) begin
32(dir-rm-cwd) open "/"
33(dir-rm-cwd) mkdir "a"
34(dir-rm-cwd) open "/a"
35(dir-rm-cwd) verify "/a" is empty
36(dir-rm-cwd) "/" and "/a" must have different inumbers
37(dir-rm-cwd) chdir "a"
38(dir-rm-cwd) try to remove "/a"
39(dir-rm-cwd) remove successful
40(dir-rm-cwd) open "/a" (must fail)
41(dir-rm-cwd) open "." (must fail)
42(dir-rm-cwd) open ".." (must fail)
43(dir-rm-cwd) create "x" (must fail)
44(dir-rm-cwd) verify "/a" is empty
45(dir-rm-cwd) end
46EOF
47open (CAN_RMDIR_CWD, ">tests/filesys/extended/can-rmdir-cwd")
48 or die "tests/filesys/extended/can-rmdir-cwd: create: $!\n";
49print CAN_RMDIR_CWD "$cwd_removable";
50close (CAN_RMDIR_CWD);
51pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck
new file mode 100644
index 0000000..f30b04a
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"a" => {"b" => {}}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.c b/pintos-progos/tests/filesys/extended/dir-rm-parent.c
new file mode 100644
index 0000000..eb43f5b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.c
@@ -0,0 +1,16 @@
1/* Tries to remove a parent of the current directory. This must
2 fail, because that directory is non-empty. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (mkdir ("a"), "mkdir \"a\"");
12 CHECK (chdir ("a"), "chdir \"a\"");
13 CHECK (mkdir ("b"), "mkdir \"b\"");
14 CHECK (chdir ("b"), "chdir \"b\"");
15 CHECK (!remove ("/a"), "remove \"/a\" (must fail)");
16}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck
new file mode 100644
index 0000000..9fea8f2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rm-parent) begin
7(dir-rm-parent) mkdir "a"
8(dir-rm-parent) chdir "a"
9(dir-rm-parent) mkdir "b"
10(dir-rm-parent) chdir "b"
11(dir-rm-parent) remove "/a" (must fail)
12(dir-rm-parent) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck
new file mode 100644
index 0000000..6315107
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"a" => ["\0" x 243]});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.c b/pintos-progos/tests/filesys/extended/dir-rm-root.c
new file mode 100644
index 0000000..c47f1eb
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-root.c
@@ -0,0 +1,13 @@
1/* Try to remove the root directory.
2 This must fail. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (!remove ("/"), "remove \"/\" (must fail)");
12 CHECK (create ("/a", 243), "create \"/a\"");
13}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.ck b/pintos-progos/tests/filesys/extended/dir-rm-root.ck
new file mode 100644
index 0000000..8a69ff3
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-root.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rm-root) begin
7(dir-rm-root) remove "/" (must fail)
8(dir-rm-root) create "/a"
9(dir-rm-root) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.c b/pintos-progos/tests/filesys/extended/dir-rm-tree.c
new file mode 100644
index 0000000..bab41a6
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.c
@@ -0,0 +1,62 @@
1/* Creates directories /0/0/0 through /3/2/2 and files in the
2 leaf directories, then removes them. */
3
4#include <stdarg.h>
5#include <stdio.h>
6#include <syscall.h>
7#include "tests/filesys/extended/mk-tree.h"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11static void remove_tree (int at, int bt, int ct, int dt);
12
13void
14test_main (void)
15{
16 make_tree (4, 3, 3, 4);
17 remove_tree (4, 3, 3, 4);
18}
19
20static void do_remove (const char *format, ...) PRINTF_FORMAT (1, 2);
21
22static void
23remove_tree (int at, int bt, int ct, int dt)
24{
25 char try[128];
26 int a, b, c, d;
27
28 msg ("removing /0/0/0/0 through /%d/%d/%d/%d...",
29 at - 1, bt - 1, ct - 1, dt - 1);
30 quiet = true;
31 for (a = 0; a < at; a++)
32 {
33 for (b = 0; b < bt; b++)
34 {
35 for (c = 0; c < ct; c++)
36 {
37 for (d = 0; d < dt; d++)
38 do_remove ("/%d/%d/%d/%d", a, b, c, d);
39 do_remove ("/%d/%d/%d", a, b, c);
40 }
41 do_remove ("/%d/%d", a, b);
42 }
43 do_remove ("/%d", a);
44 }
45 quiet = false;
46
47 snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0);
48 CHECK (open (try) == -1, "open \"%s\" (must return -1)", try);
49}
50
51static void
52do_remove (const char *format, ...)
53{
54 char name[128];
55 va_list args;
56
57 va_start (args, format);
58 vsnprintf (name, sizeof name, format, args);
59 va_end (args);
60
61 CHECK (remove (name), "remove \"%s\"", name);
62}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck
new file mode 100644
index 0000000..587b493
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rm-tree) begin
7(dir-rm-tree) creating /0/0/0/0 through /3/2/2/3...
8(dir-rm-tree) open "/0/2/0/3"
9(dir-rm-tree) close "/0/2/0/3"
10(dir-rm-tree) removing /0/0/0/0 through /3/2/2/3...
11(dir-rm-tree) open "/3/0/2/0" (must return -1)
12(dir-rm-tree) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.c b/pintos-progos/tests/filesys/extended/dir-rmdir.c
new file mode 100644
index 0000000..b3cbc6f
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rmdir.c
@@ -0,0 +1,14 @@
1/* Creates and removes a directory, then makes sure that it's
2 really gone. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (mkdir ("a"), "mkdir \"a\"");
12 CHECK (remove ("a"), "rmdir \"a\"");
13 CHECK (!chdir ("a"), "chdir \"a\" (must return false)");
14}
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.ck b/pintos-progos/tests/filesys/extended/dir-rmdir.ck
new file mode 100644
index 0000000..e0d8922
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rmdir.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rmdir) begin
7(dir-rmdir) mkdir "a"
8(dir-rmdir) rmdir "a"
9(dir-rmdir) chdir "a" (must return false)
10(dir-rmdir) end
11EOF
12pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck
new file mode 100644
index 0000000..67ca528
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"abc" => ['']});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.c b/pintos-progos/tests/filesys/extended/dir-under-file.c
new file mode 100644
index 0000000..973a8b1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-under-file.c
@@ -0,0 +1,13 @@
1/* Tries to create a directory with the same name as an existing
2 file, which must return failure. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (create ("abc", 0), "create \"abc\"");
12 CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)");
13}
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.ck b/pintos-progos/tests/filesys/extended/dir-under-file.ck
new file mode 100644
index 0000000..cce23b4
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-under-file.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-under-file) begin
7(dir-under-file) create "abc"
8(dir-under-file) mkdir "abc" (must return false)
9(dir-under-file) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck
new file mode 100644
index 0000000..698ef01
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck
@@ -0,0 +1,37 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5# The archive should look like this:
6#
7# 40642 dir-vine
8# 42479 tar
9# 0 start
10# 11 start/file0
11# 0 start/dir0
12# 11 start/dir0/file1
13# 0 start/dir0/dir1
14# 11 start/dir0/dir1/file2
15# 0 start/dir0/dir1/dir2
16# 11 start/dir0/dir1/dir2/file3
17# 0 start/dir0/dir1/dir2/dir3
18# 11 start/dir0/dir1/dir2/dir3/file4
19# 0 start/dir0/dir1/dir2/dir3/dir4
20# 11 start/dir0/dir1/dir2/dir3/dir4/file5
21# 0 start/dir0/dir1/dir2/dir3/dir4/dir5
22# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/file6
23# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6
24# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7
25# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7
26# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8
27# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8
28# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9
29# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9
30my ($dir) = {};
31my ($root) = {"start" => $dir};
32for (my ($i) = 0; $i < 10; $i++) {
33 $dir->{"file$i"} = ["contents $i\n"];
34 $dir = $dir->{"dir$i"} = {};
35}
36check_archive ($root);
37pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.c b/pintos-progos/tests/filesys/extended/dir-vine.c
new file mode 100644
index 0000000..8a31c38
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-vine.c
@@ -0,0 +1,85 @@
1/* Create a very deep "vine" of directories: /dir0/dir1/dir2/...
2 and an ordinary file in each of them, until we fill up the
3 disk.
4
5 Then delete most of them, for two reasons. First, "tar"
6 limits file names to 100 characters (which could be extended
7 to 256 without much trouble). Second, a full disk has no room
8 for the tar archive. */
9
10#include <string.h>
11#include <stdio.h>
12#include <syscall.h>
13#include "tests/lib.h"
14#include "tests/main.h"
15
16void
17test_main (void)
18{
19 int i;
20
21 msg ("creating many levels of files and directories...");
22 quiet = true;
23 CHECK (mkdir ("start"), "mkdir \"start\"");
24 CHECK (chdir ("start"), "chdir \"start\"");
25 for (i = 0; ; i++)
26 {
27 char name[3][READDIR_MAX_LEN + 1];
28 char file_name[16], dir_name[16];
29 char contents[128];
30 int fd;
31
32 /* Create file. */
33 snprintf (file_name, sizeof file_name, "file%d", i);
34 if (!create (file_name, 0))
35 break;
36 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
37 snprintf (contents, sizeof contents, "contents %d\n", i);
38 if (write (fd, contents, strlen (contents)) != (int) strlen (contents))
39 {
40 CHECK (remove (file_name), "remove \"%s\"", file_name);
41 close (fd);
42 break;
43 }
44 close (fd);
45
46 /* Create directory. */
47 snprintf (dir_name, sizeof dir_name, "dir%d", i);
48 if (!mkdir (dir_name))
49 {
50 CHECK (remove (file_name), "remove \"%s\"", file_name);
51 break;
52 }
53
54 /* Check for file and directory. */
55 CHECK ((fd = open (".")) > 1, "open \".\"");
56 CHECK (readdir (fd, name[0]), "readdir \".\"");
57 CHECK (readdir (fd, name[1]), "readdir \".\"");
58 CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)");
59 CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name))
60 || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)),
61 "names should be \"%s\" and \"%s\", "
62 "actually \"%s\" and \"%s\"",
63 file_name, dir_name, name[0], name[1]);
64 close (fd);
65
66 /* Descend into directory. */
67 CHECK (chdir (dir_name), "chdir \"%s\"", dir_name);
68 }
69 CHECK (i > 200, "created files and directories only to level %d", i);
70 quiet = false;
71
72 msg ("removing all but top 10 levels of files and directories...");
73 quiet = true;
74 while (i-- > 10)
75 {
76 char file_name[16], dir_name[16];
77
78 snprintf (file_name, sizeof file_name, "file%d", i);
79 snprintf (dir_name, sizeof dir_name, "dir%d", i);
80 CHECK (chdir (".."), "chdir \"..\"");
81 CHECK (remove (dir_name), "remove \"%s\"", dir_name);
82 CHECK (remove (file_name), "remove \"%s\"", file_name);
83 }
84 quiet = false;
85}
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.ck b/pintos-progos/tests/filesys/extended/dir-vine.ck
new file mode 100644
index 0000000..db452b0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-vine.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-vine) begin
7(dir-vine) creating many levels of files and directories...
8(dir-vine) removing all but top 10 levels of files and directories...
9(dir-vine) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-create-persistence.ck b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck
new file mode 100644
index 0000000..bbcb24f
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"blargle" => ['']});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-create.c b/pintos-progos/tests/filesys/extended/grow-create.c
new file mode 100644
index 0000000..9ccc4ea
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-create.c
@@ -0,0 +1,4 @@
1/* Create a file of size 0. */
2
3#define TEST_SIZE 0
4#include "tests/filesys/create.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-create.ck b/pintos-progos/tests/filesys/extended/grow-create.ck
new file mode 100644
index 0000000..b2e69d1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-create.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(grow-create) begin
7(grow-create) create "blargle"
8(grow-create) open "blargle" for verification
9(grow-create) verified contents of "blargle"
10(grow-create) close "blargle"
11(grow-create) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck
new file mode 100644
index 0000000..989a322
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($fs);
7$fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49;
8check_archive ($fs);
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.c b/pintos-progos/tests/filesys/extended/grow-dir-lg.c
new file mode 100644
index 0000000..20a194b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.c
@@ -0,0 +1,6 @@
1/* Creates a directory,
2 then creates 50 files in that directory. */
3
4#define FILE_CNT 50
5#define DIRECTORY "/x"
6#include "tests/filesys/extended/grow-dir.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck
new file mode 100644
index 0000000..ec58bd3
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck
@@ -0,0 +1,61 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-dir-lg) begin
8(grow-dir-lg) mkdir /x
9(grow-dir-lg) creating and checking "/x/file0"
10(grow-dir-lg) creating and checking "/x/file1"
11(grow-dir-lg) creating and checking "/x/file2"
12(grow-dir-lg) creating and checking "/x/file3"
13(grow-dir-lg) creating and checking "/x/file4"
14(grow-dir-lg) creating and checking "/x/file5"
15(grow-dir-lg) creating and checking "/x/file6"
16(grow-dir-lg) creating and checking "/x/file7"
17(grow-dir-lg) creating and checking "/x/file8"
18(grow-dir-lg) creating and checking "/x/file9"
19(grow-dir-lg) creating and checking "/x/file10"
20(grow-dir-lg) creating and checking "/x/file11"
21(grow-dir-lg) creating and checking "/x/file12"
22(grow-dir-lg) creating and checking "/x/file13"
23(grow-dir-lg) creating and checking "/x/file14"
24(grow-dir-lg) creating and checking "/x/file15"
25(grow-dir-lg) creating and checking "/x/file16"
26(grow-dir-lg) creating and checking "/x/file17"
27(grow-dir-lg) creating and checking "/x/file18"
28(grow-dir-lg) creating and checking "/x/file19"
29(grow-dir-lg) creating and checking "/x/file20"
30(grow-dir-lg) creating and checking "/x/file21"
31(grow-dir-lg) creating and checking "/x/file22"
32(grow-dir-lg) creating and checking "/x/file23"
33(grow-dir-lg) creating and checking "/x/file24"
34(grow-dir-lg) creating and checking "/x/file25"
35(grow-dir-lg) creating and checking "/x/file26"
36(grow-dir-lg) creating and checking "/x/file27"
37(grow-dir-lg) creating and checking "/x/file28"
38(grow-dir-lg) creating and checking "/x/file29"
39(grow-dir-lg) creating and checking "/x/file30"
40(grow-dir-lg) creating and checking "/x/file31"
41(grow-dir-lg) creating and checking "/x/file32"
42(grow-dir-lg) creating and checking "/x/file33"
43(grow-dir-lg) creating and checking "/x/file34"
44(grow-dir-lg) creating and checking "/x/file35"
45(grow-dir-lg) creating and checking "/x/file36"
46(grow-dir-lg) creating and checking "/x/file37"
47(grow-dir-lg) creating and checking "/x/file38"
48(grow-dir-lg) creating and checking "/x/file39"
49(grow-dir-lg) creating and checking "/x/file40"
50(grow-dir-lg) creating and checking "/x/file41"
51(grow-dir-lg) creating and checking "/x/file42"
52(grow-dir-lg) creating and checking "/x/file43"
53(grow-dir-lg) creating and checking "/x/file44"
54(grow-dir-lg) creating and checking "/x/file45"
55(grow-dir-lg) creating and checking "/x/file46"
56(grow-dir-lg) creating and checking "/x/file47"
57(grow-dir-lg) creating and checking "/x/file48"
58(grow-dir-lg) creating and checking "/x/file49"
59(grow-dir-lg) end
60EOF
61pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-dir.inc b/pintos-progos/tests/filesys/extended/grow-dir.inc
new file mode 100644
index 0000000..bee0ba0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir.inc
@@ -0,0 +1,41 @@
1/* -*- c -*- */
2
3#include <syscall.h>
4#include <stdio.h>
5#include "tests/filesys/seq-test.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static char buf[512];
10
11static size_t
12return_block_size (void)
13{
14 return sizeof buf;
15}
16
17void
18test_main (void)
19{
20 size_t i;
21
22#ifdef DIRECTORY
23 CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY);
24#define DIR_PREFIX DIRECTORY "/"
25#else
26#define DIR_PREFIX ""
27#endif
28 for (i = 0; i < FILE_CNT; i++)
29 {
30 char file_name[128];
31 snprintf (file_name, sizeof file_name, "%sfile%zu", DIR_PREFIX, i);
32
33 msg ("creating and checking \"%s\"", file_name);
34
35 quiet = true;
36 seq_test (file_name,
37 buf, sizeof buf, sizeof buf,
38 return_block_size, NULL);
39 quiet = false;
40 }
41}
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck
new file mode 100644
index 0000000..150f383
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"testfile" => [random_bytes (2134)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.c b/pintos-progos/tests/filesys/extended/grow-file-size.c
new file mode 100644
index 0000000..3ce8588
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-file-size.c
@@ -0,0 +1,33 @@
1/* Grows a file from 0 bytes to 2,134 bytes, 37 bytes at a time,
2 and checks that the file's size is reported correctly at each
3 step. */
4
5#include <syscall.h>
6#include "tests/filesys/seq-test.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10static char buf[2134];
11
12static size_t
13return_block_size (void)
14{
15 return 37;
16}
17
18static void
19check_file_size (int fd, long ofs)
20{
21 long size = filesize (fd);
22 if (size != ofs)
23 fail ("filesize not updated properly: should be %ld, actually %ld",
24 ofs, size);
25}
26
27void
28test_main (void)
29{
30 seq_test ("testfile",
31 buf, sizeof buf, 0,
32 return_block_size, check_file_size);
33}
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.ck b/pintos-progos/tests/filesys/extended/grow-file-size.ck
new file mode 100644
index 0000000..d81feff
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-file-size.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-file-size) begin
8(grow-file-size) create "testfile"
9(grow-file-size) open "testfile"
10(grow-file-size) writing "testfile"
11(grow-file-size) close "testfile"
12(grow-file-size) open "testfile" for verification
13(grow-file-size) verified contents of "testfile"
14(grow-file-size) close "testfile"
15(grow-file-size) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck
new file mode 100644
index 0000000..1692f46
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($fs);
7$fs->{"file$_"} = [random_bytes (512)] foreach 0...49;
8check_archive ($fs);
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.c b/pintos-progos/tests/filesys/extended/grow-root-lg.c
new file mode 100644
index 0000000..d8d6c09
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-lg.c
@@ -0,0 +1,4 @@
1/* Creates 50 files in the root directory. */
2
3#define FILE_CNT 50
4#include "tests/filesys/extended/grow-dir.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.ck b/pintos-progos/tests/filesys/extended/grow-root-lg.ck
new file mode 100644
index 0000000..b174bc9
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-lg.ck
@@ -0,0 +1,60 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-root-lg) begin
8(grow-root-lg) creating and checking "file0"
9(grow-root-lg) creating and checking "file1"
10(grow-root-lg) creating and checking "file2"
11(grow-root-lg) creating and checking "file3"
12(grow-root-lg) creating and checking "file4"
13(grow-root-lg) creating and checking "file5"
14(grow-root-lg) creating and checking "file6"
15(grow-root-lg) creating and checking "file7"
16(grow-root-lg) creating and checking "file8"
17(grow-root-lg) creating and checking "file9"
18(grow-root-lg) creating and checking "file10"
19(grow-root-lg) creating and checking "file11"
20(grow-root-lg) creating and checking "file12"
21(grow-root-lg) creating and checking "file13"
22(grow-root-lg) creating and checking "file14"
23(grow-root-lg) creating and checking "file15"
24(grow-root-lg) creating and checking "file16"
25(grow-root-lg) creating and checking "file17"
26(grow-root-lg) creating and checking "file18"
27(grow-root-lg) creating and checking "file19"
28(grow-root-lg) creating and checking "file20"
29(grow-root-lg) creating and checking "file21"
30(grow-root-lg) creating and checking "file22"
31(grow-root-lg) creating and checking "file23"
32(grow-root-lg) creating and checking "file24"
33(grow-root-lg) creating and checking "file25"
34(grow-root-lg) creating and checking "file26"
35(grow-root-lg) creating and checking "file27"
36(grow-root-lg) creating and checking "file28"
37(grow-root-lg) creating and checking "file29"
38(grow-root-lg) creating and checking "file30"
39(grow-root-lg) creating and checking "file31"
40(grow-root-lg) creating and checking "file32"
41(grow-root-lg) creating and checking "file33"
42(grow-root-lg) creating and checking "file34"
43(grow-root-lg) creating and checking "file35"
44(grow-root-lg) creating and checking "file36"
45(grow-root-lg) creating and checking "file37"
46(grow-root-lg) creating and checking "file38"
47(grow-root-lg) creating and checking "file39"
48(grow-root-lg) creating and checking "file40"
49(grow-root-lg) creating and checking "file41"
50(grow-root-lg) creating and checking "file42"
51(grow-root-lg) creating and checking "file43"
52(grow-root-lg) creating and checking "file44"
53(grow-root-lg) creating and checking "file45"
54(grow-root-lg) creating and checking "file46"
55(grow-root-lg) creating and checking "file47"
56(grow-root-lg) creating and checking "file48"
57(grow-root-lg) creating and checking "file49"
58(grow-root-lg) end
59EOF
60pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck
new file mode 100644
index 0000000..2b0b8ab
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($fs);
7$fs->{"file$_"} = [random_bytes (512)] foreach 0...19;
8check_archive ($fs);
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.c b/pintos-progos/tests/filesys/extended/grow-root-sm.c
new file mode 100644
index 0000000..ee375d5
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-sm.c
@@ -0,0 +1,4 @@
1/* Creates 20 files in the root directory. */
2
3#define FILE_CNT 20
4#include "tests/filesys/extended/grow-dir.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.ck b/pintos-progos/tests/filesys/extended/grow-root-sm.ck
new file mode 100644
index 0000000..1aac7e9
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-sm.ck
@@ -0,0 +1,30 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-root-sm) begin
8(grow-root-sm) creating and checking "file0"
9(grow-root-sm) creating and checking "file1"
10(grow-root-sm) creating and checking "file2"
11(grow-root-sm) creating and checking "file3"
12(grow-root-sm) creating and checking "file4"
13(grow-root-sm) creating and checking "file5"
14(grow-root-sm) creating and checking "file6"
15(grow-root-sm) creating and checking "file7"
16(grow-root-sm) creating and checking "file8"
17(grow-root-sm) creating and checking "file9"
18(grow-root-sm) creating and checking "file10"
19(grow-root-sm) creating and checking "file11"
20(grow-root-sm) creating and checking "file12"
21(grow-root-sm) creating and checking "file13"
22(grow-root-sm) creating and checking "file14"
23(grow-root-sm) creating and checking "file15"
24(grow-root-sm) creating and checking "file16"
25(grow-root-sm) creating and checking "file17"
26(grow-root-sm) creating and checking "file18"
27(grow-root-sm) creating and checking "file19"
28(grow-root-sm) end
29EOF
30pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck
new file mode 100644
index 0000000..41aaae0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"testme" => [random_bytes (72943)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.c b/pintos-progos/tests/filesys/extended/grow-seq-lg.c
new file mode 100644
index 0000000..3108d17
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.c
@@ -0,0 +1,5 @@
1/* Grows a file from 0 bytes to 72,943 bytes, 1,234 bytes at a
2 time. */
3
4#define TEST_SIZE 72943
5#include "tests/filesys/extended/grow-seq.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck
new file mode 100644
index 0000000..90fcd8c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-seq-lg) begin
8(grow-seq-lg) create "testme"
9(grow-seq-lg) open "testme"
10(grow-seq-lg) writing "testme"
11(grow-seq-lg) close "testme"
12(grow-seq-lg) open "testme" for verification
13(grow-seq-lg) verified contents of "testme"
14(grow-seq-lg) close "testme"
15(grow-seq-lg) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck
new file mode 100644
index 0000000..6cb0bd8
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"testme" => [random_bytes (5678)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.c b/pintos-progos/tests/filesys/extended/grow-seq-sm.c
new file mode 100644
index 0000000..3656e2e
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.c
@@ -0,0 +1,5 @@
1/* Grows a file from 0 bytes to 5,678 bytes, 1,234 bytes at a
2 time. */
3
4#define TEST_SIZE 5678
5#include "tests/filesys/extended/grow-seq.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck
new file mode 100644
index 0000000..5cf4518
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-seq-sm) begin
8(grow-seq-sm) create "testme"
9(grow-seq-sm) open "testme"
10(grow-seq-sm) writing "testme"
11(grow-seq-sm) close "testme"
12(grow-seq-sm) open "testme" for verification
13(grow-seq-sm) verified contents of "testme"
14(grow-seq-sm) close "testme"
15(grow-seq-sm) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq.inc b/pintos-progos/tests/filesys/extended/grow-seq.inc
new file mode 100644
index 0000000..1b7710c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq.inc
@@ -0,0 +1,20 @@
1/* -*- c -*- */
2
3#include "tests/filesys/seq-test.h"
4#include "tests/main.h"
5
6static char buf[TEST_SIZE];
7
8static size_t
9return_block_size (void)
10{
11 return 1234;
12}
13
14void
15test_main (void)
16{
17 seq_test ("testme",
18 buf, sizeof buf, 0,
19 return_block_size, NULL);
20}
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck
new file mode 100644
index 0000000..3f06a5b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"testfile" => ["\0" x 76543]});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.c b/pintos-progos/tests/filesys/extended/grow-sparse.c
new file mode 100644
index 0000000..6eab210
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-sparse.c
@@ -0,0 +1,25 @@
1/* Tests that seeking past the end of a file and writing will
2 properly zero out the region in between. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8static char buf[76543];
9
10void
11test_main (void)
12{
13 const char *file_name = "testfile";
14 char zero = 0;
15 int fd;
16
17 CHECK (create (file_name, 0), "create \"%s\"", file_name);
18 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
19 msg ("seek \"%s\"", file_name);
20 seek (fd, sizeof buf - 1);
21 CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", file_name);
22 msg ("close \"%s\"", file_name);
23 close (fd);
24 check_file (file_name, buf, sizeof buf);
25}
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.ck b/pintos-progos/tests/filesys/extended/grow-sparse.ck
new file mode 100644
index 0000000..379ba2c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-sparse.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(grow-sparse) begin
7(grow-sparse) create "testfile"
8(grow-sparse) open "testfile"
9(grow-sparse) seek "testfile"
10(grow-sparse) write "testfile"
11(grow-sparse) close "testfile"
12(grow-sparse) open "testfile" for verification
13(grow-sparse) verified contents of "testfile"
14(grow-sparse) close "testfile"
15(grow-sparse) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck
new file mode 100644
index 0000000..d93a422
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"foobar" => [random_bytes (2134)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.c b/pintos-progos/tests/filesys/extended/grow-tell.c
new file mode 100644
index 0000000..5f5da5b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-tell.c
@@ -0,0 +1,32 @@
1/* Checks that growing a file updates the file position
2 correctly. */
3
4#include <syscall.h>
5#include "tests/filesys/seq-test.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static char buf[2134];
10
11static size_t
12return_block_size (void)
13{
14 return 37;
15}
16
17static void
18check_tell (int fd, long ofs)
19{
20 long pos = tell (fd);
21 if (pos != ofs)
22 fail ("file position not updated properly: should be %ld, actually %ld",
23 ofs, pos);
24}
25
26void
27test_main (void)
28{
29 seq_test ("foobar",
30 buf, sizeof buf, 0,
31 return_block_size, check_tell);
32}
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.ck b/pintos-progos/tests/filesys/extended/grow-tell.ck
new file mode 100644
index 0000000..fe94707
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-tell.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-tell) begin
8(grow-tell) create "foobar"
9(grow-tell) open "foobar"
10(grow-tell) writing "foobar"
11(grow-tell) close "foobar"
12(grow-tell) open "foobar" for verification
13(grow-tell) verified contents of "foobar"
14(grow-tell) close "foobar"
15(grow-tell) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck
new file mode 100644
index 0000000..1c4ced1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($a) = random_bytes (8143);
7my ($b) = random_bytes (8143);
8check_archive ({"a" => [$a], "b" => [$b]});
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.c b/pintos-progos/tests/filesys/extended/grow-two-files.c
new file mode 100644
index 0000000..6a8fb1c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-two-files.c
@@ -0,0 +1,62 @@
1/* Grows two files in parallel and checks that their contents are
2 correct. */
3
4#include <random.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9#define FILE_SIZE 8143
10static char buf_a[FILE_SIZE];
11static char buf_b[FILE_SIZE];
12
13static void
14write_some_bytes (const char *file_name, int fd, const char *buf, size_t *ofs)
15{
16 if (*ofs < FILE_SIZE)
17 {
18 size_t block_size = random_ulong () % (FILE_SIZE / 8) + 1;
19 size_t ret_val;
20 if (block_size > FILE_SIZE - *ofs)
21 block_size = FILE_SIZE - *ofs;
22
23 ret_val = write (fd, buf + *ofs, block_size);
24 if (ret_val != block_size)
25 fail ("write %zu bytes at offset %zu in \"%s\" returned %zu",
26 block_size, *ofs, file_name, ret_val);
27 *ofs += block_size;
28 }
29}
30
31void
32test_main (void)
33{
34 int fd_a, fd_b;
35 size_t ofs_a = 0, ofs_b = 0;
36
37 random_init (0);
38 random_bytes (buf_a, sizeof buf_a);
39 random_bytes (buf_b, sizeof buf_b);
40
41 CHECK (create ("a", 0), "create \"a\"");
42 CHECK (create ("b", 0), "create \"b\"");
43
44 CHECK ((fd_a = open ("a")) > 1, "open \"a\"");
45 CHECK ((fd_b = open ("b")) > 1, "open \"b\"");
46
47 msg ("write \"a\" and \"b\" alternately");
48 while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE)
49 {
50 write_some_bytes ("a", fd_a, buf_a, &ofs_a);
51 write_some_bytes ("b", fd_b, buf_b, &ofs_b);
52 }
53
54 msg ("close \"a\"");
55 close (fd_a);
56
57 msg ("close \"b\"");
58 close (fd_b);
59
60 check_file ("a", buf_a, FILE_SIZE);
61 check_file ("b", buf_b, FILE_SIZE);
62}
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.ck b/pintos-progos/tests/filesys/extended/grow-two-files.ck
new file mode 100644
index 0000000..b5e754a
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-two-files.ck
@@ -0,0 +1,23 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-two-files) begin
8(grow-two-files) create "a"
9(grow-two-files) create "b"
10(grow-two-files) open "a"
11(grow-two-files) open "b"
12(grow-two-files) write "a" and "b" alternately
13(grow-two-files) close "a"
14(grow-two-files) close "b"
15(grow-two-files) open "a" for verification
16(grow-two-files) verified contents of "a"
17(grow-two-files) close "a"
18(grow-two-files) open "b" for verification
19(grow-two-files) verified contents of "b"
20(grow-two-files) close "b"
21(grow-two-files) end
22EOF
23pass;
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.c b/pintos-progos/tests/filesys/extended/mk-tree.c
new file mode 100644
index 0000000..a36bb88
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/mk-tree.c
@@ -0,0 +1,67 @@
1/* Library function for creating a tree of directories. */
2
3#include <stdio.h>
4#include <syscall.h>
5#include "tests/filesys/extended/mk-tree.h"
6#include "tests/lib.h"
7
8static void do_mkdir (const char *format, ...) PRINTF_FORMAT (1, 2);
9static void do_touch (const char *format, ...) PRINTF_FORMAT (1, 2);
10
11void
12make_tree (int at, int bt, int ct, int dt)
13{
14 char try[128];
15 int a, b, c, d;
16 int fd;
17
18 msg ("creating /0/0/0/0 through /%d/%d/%d/%d...",
19 at - 1, bt - 1, ct - 1, dt - 1);
20 quiet = true;
21 for (a = 0; a < at; a++)
22 {
23 do_mkdir ("/%d", a);
24 for (b = 0; b < bt; b++)
25 {
26 do_mkdir ("/%d/%d", a, b);
27 for (c = 0; c < ct; c++)
28 {
29 do_mkdir ("/%d/%d/%d", a, b, c);
30 for (d = 0; d < dt; d++)
31 do_touch ("/%d/%d/%d/%d", a, b, c, d);
32 }
33 }
34 }
35 quiet = false;
36
37 snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1);
38 CHECK ((fd = open (try)) > 1, "open \"%s\"", try);
39 msg ("close \"%s\"", try);
40 close (fd);
41}
42
43static void
44do_mkdir (const char *format, ...)
45{
46 char dir[128];
47 va_list args;
48
49 va_start (args, format);
50 vsnprintf (dir, sizeof dir, format, args);
51 va_end (args);
52
53 CHECK (mkdir (dir), "mkdir \"%s\"", dir);
54}
55
56static void
57do_touch (const char *format, ...)
58{
59 char file[128];
60 va_list args;
61
62 va_start (args, format);
63 vsnprintf (file, sizeof file, format, args);
64 va_end (args);
65
66 CHECK (create (file, 0), "create \"%s\"", file);
67}
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.h b/pintos-progos/tests/filesys/extended/mk-tree.h
new file mode 100644
index 0000000..df0d5a6
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/mk-tree.h
@@ -0,0 +1,6 @@
1#ifndef TESTS_FILESYS_EXTENDED_MK_TREE_H
2#define TESTS_FILESYS_EXTENDED_MK_TREE_H
3
4void make_tree (int at, int bt, int ct, int dt);
5
6#endif /* tests/filesys/extended/mk-tree.h */
diff --git a/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck
new file mode 100644
index 0000000..62d57ee
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck
@@ -0,0 +1,8 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw",
7 "logfile" => [random_bytes (8 * 512)]});
8pass;
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.c b/pintos-progos/tests/filesys/extended/syn-rw.c
new file mode 100644
index 0000000..657dfb5
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw.c
@@ -0,0 +1,35 @@
1/* Grows a file in chunks while subprocesses read the growing
2 file. */
3
4#include <random.h>
5#include <syscall.h>
6#include "tests/filesys/extended/syn-rw.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10char buf[BUF_SIZE];
11
12#define CHILD_CNT 4
13
14void
15test_main (void)
16{
17 pid_t children[CHILD_CNT];
18 size_t ofs;
19 int fd;
20
21 CHECK (create (file_name, 0), "create \"%s\"", file_name);
22 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
23
24 exec_children ("child-syn-rw", children, CHILD_CNT);
25
26 random_bytes (buf, sizeof buf);
27 quiet = true;
28 for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE)
29 CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0,
30 "write %d bytes at offset %zu in \"%s\"",
31 (int) CHUNK_SIZE, ofs, file_name);
32 quiet = false;
33
34 wait_children (children, CHILD_CNT);
35}
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.ck b/pintos-progos/tests/filesys/extended/syn-rw.ck
new file mode 100644
index 0000000..ac82aa8
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw.ck
@@ -0,0 +1,20 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(syn-rw) begin
8(syn-rw) create "logfile"
9(syn-rw) open "logfile"
10(syn-rw) exec child 1 of 4: "child-syn-rw 0"
11(syn-rw) exec child 2 of 4: "child-syn-rw 1"
12(syn-rw) exec child 3 of 4: "child-syn-rw 2"
13(syn-rw) exec child 4 of 4: "child-syn-rw 3"
14(syn-rw) wait for child 1 of 4 returned 0 (expected 0)
15(syn-rw) wait for child 2 of 4 returned 1 (expected 1)
16(syn-rw) wait for child 3 of 4 returned 2 (expected 2)
17(syn-rw) wait for child 4 of 4 returned 3 (expected 3)
18(syn-rw) end
19EOF
20pass;
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.h b/pintos-progos/tests/filesys/extended/syn-rw.h
new file mode 100644
index 0000000..170aeb4
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw.h
@@ -0,0 +1,9 @@
1#ifndef TESTS_FILESYS_EXTENDED_SYN_RW_H
2#define TESTS_FILESYS_EXTENDED_SYN_RW_H
3
4#define CHUNK_SIZE 8
5#define CHUNK_CNT 512
6#define BUF_SIZE (CHUNK_SIZE * CHUNK_CNT)
7static const char file_name[] = "logfile";
8
9#endif /* tests/filesys/extended/syn-rw.h */
diff --git a/pintos-progos/tests/filesys/extended/tar.c b/pintos-progos/tests/filesys/extended/tar.c
new file mode 100644
index 0000000..9801484
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/tar.c
@@ -0,0 +1,208 @@
1/* tar.c
2
3 Creates a tar archive. */
4
5#include <ustar.h>
6#include <syscall.h>
7#include <stdio.h>
8#include <string.h>
9
10static void usage (void);
11static bool make_tar_archive (const char *archive_name,
12 char *files[], size_t file_cnt);
13
14int
15main (int argc, char *argv[])
16{
17 if (argc < 3)
18 usage ();
19
20 return (make_tar_archive (argv[1], argv + 2, argc - 2)
21 ? EXIT_SUCCESS : EXIT_FAILURE);
22}
23
24static void
25usage (void)
26{
27 printf ("tar, tar archive creator\n"
28 "Usage: tar ARCHIVE FILE...\n"
29 "where ARCHIVE is the tar archive to create\n"
30 " and FILE... is a list of files or directories to put into it.\n"
31 "(ARCHIVE itself will not be included in the archive, even if it\n"
32 "is in a directory to be archived.)\n");
33 exit (EXIT_FAILURE);
34}
35
36static bool archive_file (char file_name[], size_t file_name_size,
37 int archive_fd, bool *write_error);
38
39static bool archive_ordinary_file (const char *file_name, int file_fd,
40 int archive_fd, bool *write_error);
41static bool archive_directory (char file_name[], size_t file_name_size,
42 int file_fd, int archive_fd, bool *write_error);
43static bool write_header (const char *file_name, enum ustar_type, int size,
44 int archive_fd, bool *write_error);
45
46static bool do_write (int fd, const char *buffer, int size, bool *write_error);
47
48static bool
49make_tar_archive (const char *archive_name, char *files[], size_t file_cnt)
50{
51 static const char zeros[512];
52 int archive_fd;
53 bool success = true;
54 bool write_error = false;
55 size_t i;
56
57 if (!create (archive_name, 0))
58 {
59 printf ("%s: create failed\n", archive_name);
60 return false;
61 }
62 archive_fd = open (archive_name);
63 if (archive_fd < 0)
64 {
65 printf ("%s: open failed\n", archive_name);
66 return false;
67 }
68
69 for (i = 0; i < file_cnt; i++)
70 {
71 char file_name[128];
72
73 strlcpy (file_name, files[i], sizeof file_name);
74 if (!archive_file (file_name, sizeof file_name,
75 archive_fd, &write_error))
76 success = false;
77 }
78
79 if (!do_write (archive_fd, zeros, 512, &write_error)
80 || !do_write (archive_fd, zeros, 512, &write_error))
81 success = false;
82
83 close (archive_fd);
84
85 return success;
86}
87
88static bool
89archive_file (char file_name[], size_t file_name_size,
90 int archive_fd, bool *write_error)
91{
92 int file_fd = open (file_name);
93 if (file_fd >= 0)
94 {
95 bool success;
96
97 if (inumber (file_fd) != inumber (archive_fd))
98 {
99 if (!isdir (file_fd))
100 success = archive_ordinary_file (file_name, file_fd,
101 archive_fd, write_error);
102 else
103 success = archive_directory (file_name, file_name_size, file_fd,
104 archive_fd, write_error);
105 }
106 else
107 {
108 /* Nothing to do: don't try to archive the archive file. */
109 success = true;
110 }
111
112 close (file_fd);
113
114 return success;
115 }
116 else
117 {
118 printf ("%s: open failed\n", file_name);
119 return false;
120 }
121}
122
123static bool
124archive_ordinary_file (const char *file_name, int file_fd,
125 int archive_fd, bool *write_error)
126{
127 bool read_error = false;
128 bool success = true;
129 int file_size = filesize (file_fd);
130
131 if (!write_header (file_name, USTAR_REGULAR, file_size,
132 archive_fd, write_error))
133 return false;
134
135 while (file_size > 0)
136 {
137 static char buf[512];
138 int chunk_size = file_size > 512 ? 512 : file_size;
139 int read_retval = read (file_fd, buf, chunk_size);
140 int bytes_read = read_retval > 0 ? read_retval : 0;
141
142 if (bytes_read != chunk_size && !read_error)
143 {
144 printf ("%s: read error\n", file_name);
145 read_error = true;
146 success = false;
147 }
148
149 memset (buf + bytes_read, 0, 512 - bytes_read);
150 if (!do_write (archive_fd, buf, 512, write_error))
151 success = false;
152
153 file_size -= chunk_size;
154 }
155
156 return success;
157}
158
159static bool
160archive_directory (char file_name[], size_t file_name_size, int file_fd,
161 int archive_fd, bool *write_error)
162{
163 size_t dir_len;
164 bool success = true;
165
166 dir_len = strlen (file_name);
167 if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size)
168 {
169 printf ("%s: file name too long\n", file_name);
170 return false;
171 }
172
173 if (!write_header (file_name, USTAR_DIRECTORY, 0, archive_fd, write_error))
174 return false;
175
176 file_name[dir_len] = '/';
177 while (readdir (file_fd, &file_name[dir_len + 1]))
178 if (!archive_file (file_name, file_name_size, archive_fd, write_error))
179 success = false;
180 file_name[dir_len] = '\0';
181
182 return success;
183}
184
185static bool
186write_header (const char *file_name, enum ustar_type type, int size,
187 int archive_fd, bool *write_error)
188{
189 static char header[512];
190 return (ustar_make_header (file_name, type, size, header)
191 && do_write (archive_fd, header, 512, write_error));
192}
193
194static bool
195do_write (int fd, const char *buffer, int size, bool *write_error)
196{
197 if (write (fd, buffer, size) == size)
198 return true;
199 else
200 {
201 if (!*write_error)
202 {
203 printf ("error writing archive\n");
204 *write_error = true;
205 }
206 return false;
207 }
208}
diff --git a/pintos-progos/tests/filesys/seq-test.c b/pintos-progos/tests/filesys/seq-test.c
new file mode 100644
index 0000000..8ce222c
--- /dev/null
+++ b/pintos-progos/tests/filesys/seq-test.c
@@ -0,0 +1,37 @@
1#include "tests/filesys/seq-test.h"
2#include <random.h>
3#include <syscall.h>
4#include "tests/lib.h"
5
6void
7seq_test (const char *file_name, void *buf, size_t size, size_t initial_size,
8 size_t (*block_size_func) (void),
9 void (*check_func) (int fd, long ofs))
10{
11 size_t ofs;
12 int fd;
13
14 random_bytes (buf, size);
15 CHECK (create (file_name, initial_size), "create \"%s\"", file_name);
16 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
17
18 ofs = 0;
19 msg ("writing \"%s\"", file_name);
20 while (ofs < size)
21 {
22 size_t block_size = block_size_func ();
23 if (block_size > size - ofs)
24 block_size = size - ofs;
25
26 if (write (fd, buf + ofs, block_size) != (int) block_size)
27 fail ("write %zu bytes at offset %zu in \"%s\" failed",
28 block_size, ofs, file_name);
29
30 ofs += block_size;
31 if (check_func != NULL)
32 check_func (fd, ofs);
33 }
34 msg ("close \"%s\"", file_name);
35 close (fd);
36 check_file (file_name, buf, size);
37}
diff --git a/pintos-progos/tests/filesys/seq-test.h b/pintos-progos/tests/filesys/seq-test.h
new file mode 100644
index 0000000..0697381
--- /dev/null
+++ b/pintos-progos/tests/filesys/seq-test.h
@@ -0,0 +1,11 @@
1#ifndef TESTS_FILESYS_SEQ_TEST_H
2#define TESTS_FILESYS_SEQ_TEST_H
3
4#include <stddef.h>
5
6void seq_test (const char *file_name,
7 void *buf, size_t size, size_t initial_size,
8 size_t (*block_size_func) (void),
9 void (*check_func) (int fd, long ofs));
10
11#endif /* tests/filesys/seq-test.h */
diff --git a/pintos-progos/tests/internal/list.c b/pintos-progos/tests/internal/list.c
new file mode 100644
index 0000000..836c69e
--- /dev/null
+++ b/pintos-progos/tests/internal/list.c
@@ -0,0 +1,174 @@
1/* Test program for lib/kernel/list.c.
2
3 Attempts to test the list functionality that is not
4 sufficiently tested elsewhere in Pintos.
5
6 This is not a test we will run on your submitted projects.
7 It is here for completeness.
8*/
9
10#undef NDEBUG
11#include <debug.h>
12#include <list.h>
13#include <random.h>
14#include <stdio.h>
15#include "threads/test.h"
16
17/* Maximum number of elements in a linked list that we will
18 test. */
19#define MAX_SIZE 64
20
21/* A linked list element. */
22struct value
23 {
24 struct list_elem elem; /* List element. */
25 int value; /* Item value. */
26 };
27
28static void shuffle (struct value[], size_t);
29static bool value_less (const struct list_elem *, const struct list_elem *,
30 void *);
31static void verify_list_fwd (struct list *, int size);
32static void verify_list_bkwd (struct list *, int size);
33
34/* Test the linked list implementation. */
35void
36test (void)
37{
38 int size;
39
40 printf ("testing various size lists:");
41 for (size = 0; size < MAX_SIZE; size++)
42 {
43 int repeat;
44
45 printf (" %d", size);
46 for (repeat = 0; repeat < 10; repeat++)
47 {
48 static struct value values[MAX_SIZE * 4];
49 struct list list;
50 struct list_elem *e;
51 int i, ofs;
52
53 /* Put values 0...SIZE in random order in VALUES. */
54 for (i = 0; i < size; i++)
55 values[i].value = i;
56 shuffle (values, size);
57
58 /* Assemble list. */
59 list_init (&list);
60 for (i = 0; i < size; i++)
61 list_push_back (&list, &values[i].elem);
62
63 /* Verify correct minimum and maximum elements. */
64 e = list_min (&list, value_less, NULL);
65 ASSERT (size ? list_entry (e, struct value, elem)->value == 0
66 : e == list_begin (&list));
67 e = list_max (&list, value_less, NULL);
68 ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1
69 : e == list_begin (&list));
70
71 /* Sort and verify list. */
72 list_sort (&list, value_less, NULL);
73 verify_list_fwd (&list, size);
74
75 /* Reverse and verify list. */
76 list_reverse (&list);
77 verify_list_bkwd (&list, size);
78
79 /* Shuffle, insert using list_insert_ordered(),
80 and verify ordering. */
81 shuffle (values, size);
82 list_init (&list);
83 for (i = 0; i < size; i++)
84 list_insert_ordered (&list, &values[i].elem,
85 value_less, NULL);
86 verify_list_fwd (&list, size);
87
88 /* Duplicate some items, uniquify, and verify. */
89 ofs = size;
90 for (e = list_begin (&list); e != list_end (&list);
91 e = list_next (e))
92 {
93 struct value *v = list_entry (e, struct value, elem);
94 int copies = random_ulong () % 4;
95 while (copies-- > 0)
96 {
97 values[ofs].value = v->value;
98 list_insert (e, &values[ofs++].elem);
99 }
100 }
101 ASSERT ((size_t) ofs < sizeof values / sizeof *values);
102 list_unique (&list, NULL, value_less, NULL);
103 verify_list_fwd (&list, size);
104 }
105 }
106
107 printf (" done\n");
108 printf ("list: PASS\n");
109}
110
111/* Shuffles the CNT elements in ARRAY into random order. */
112static void
113shuffle (struct value *array, size_t cnt)
114{
115 size_t i;
116
117 for (i = 0; i < cnt; i++)
118 {
119 size_t j = i + random_ulong () % (cnt - i);
120 struct value t = array[j];
121 array[j] = array[i];
122 array[i] = t;
123 }
124}
125
126/* Returns true if value A is less than value B, false
127 otherwise. */
128static bool
129value_less (const struct list_elem *a_, const struct list_elem *b_,
130 void *aux UNUSED)
131{
132 const struct value *a = list_entry (a_, struct value, elem);
133 const struct value *b = list_entry (b_, struct value, elem);
134
135 return a->value < b->value;
136}
137
138/* Verifies that LIST contains the values 0...SIZE when traversed
139 in forward order. */
140static void
141verify_list_fwd (struct list *list, int size)
142{
143 struct list_elem *e;
144 int i;
145
146 for (i = 0, e = list_begin (list);
147 i < size && e != list_end (list);
148 i++, e = list_next (e))
149 {
150 struct value *v = list_entry (e, struct value, elem);
151 ASSERT (i == v->value);
152 }
153 ASSERT (i == size);
154 ASSERT (e == list_end (list));
155}
156
157/* Verifies that LIST contains the values 0...SIZE when traversed
158 in reverse order. */
159static void
160verify_list_bkwd (struct list *list, int size)
161{
162 struct list_elem *e;
163 int i;
164
165 for (i = 0, e = list_rbegin (list);
166 i < size && e != list_rend (list);
167 i++, e = list_prev (e))
168 {
169 struct value *v = list_entry (e, struct value, elem);
170 ASSERT (i == v->value);
171 }
172 ASSERT (i == size);
173 ASSERT (e == list_rend (list));
174}
diff --git a/pintos-progos/tests/internal/stdio.c b/pintos-progos/tests/internal/stdio.c
new file mode 100644
index 0000000..fb60cda
--- /dev/null
+++ b/pintos-progos/tests/internal/stdio.c
@@ -0,0 +1,208 @@
1/* Test program for printf() in lib/stdio.c.
2
3 Attempts to test printf() functionality that is not
4 sufficiently tested elsewhere in Pintos.
5
6 This is not a test we will run on your submitted projects.
7 It is here for completeness.
8*/
9
10#undef NDEBUG
11#include <limits.h>
12#include <stdarg.h>
13#include <stddef.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include <string.h>
17#include "threads/test.h"
18
19/* Number of failures so far. */
20static int failure_cnt;
21
22static void
23checkf (const char *expect, const char *format, ...)
24{
25 char output[128];
26 va_list args;
27
28 printf ("\"%s\" -> \"%s\": ", format, expect);
29
30 va_start (args, format);
31 vsnprintf (output, sizeof output, format, args);
32 va_end (args);
33
34 if (strcmp (expect, output))
35 {
36 printf ("\nFAIL: actual output \"%s\"\n", output);
37 failure_cnt++;
38 }
39 else
40 printf ("okay\n");
41}
42
43/* Test printf() implementation. */
44void
45test (void)
46{
47 printf ("Testing formats:");
48
49 /* Check that commas show up in the right places, for positive
50 numbers. */
51 checkf ("1", "%'d", 1);
52 checkf ("12", "%'d", 12);
53 checkf ("123", "%'d", 123);
54 checkf ("1,234", "%'d", 1234);
55 checkf ("12,345", "%'d", 12345);
56 checkf ("123,456", "%'ld", 123456L);
57 checkf ("1,234,567", "%'ld", 1234567L);
58 checkf ("12,345,678", "%'ld", 12345678L);
59 checkf ("123,456,789", "%'ld", 123456789L);
60 checkf ("1,234,567,890", "%'ld", 1234567890L);
61 checkf ("12,345,678,901", "%'lld", 12345678901LL);
62 checkf ("123,456,789,012", "%'lld", 123456789012LL);
63 checkf ("1,234,567,890,123", "%'lld", 1234567890123LL);
64 checkf ("12,345,678,901,234", "%'lld", 12345678901234LL);
65 checkf ("123,456,789,012,345", "%'lld", 123456789012345LL);
66 checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL);
67 checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL);
68 checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL);
69 checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL);
70
71 /* Check that commas show up in the right places, for positive
72 numbers. */
73 checkf ("-1", "%'d", -1);
74 checkf ("-12", "%'d", -12);
75 checkf ("-123", "%'d", -123);
76 checkf ("-1,234", "%'d", -1234);
77 checkf ("-12,345", "%'d", -12345);
78 checkf ("-123,456", "%'ld", -123456L);
79 checkf ("-1,234,567", "%'ld", -1234567L);
80 checkf ("-12,345,678", "%'ld", -12345678L);
81 checkf ("-123,456,789", "%'ld", -123456789L);
82 checkf ("-1,234,567,890", "%'ld", -1234567890L);
83 checkf ("-12,345,678,901", "%'lld", -12345678901LL);
84 checkf ("-123,456,789,012", "%'lld", -123456789012LL);
85 checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL);
86 checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL);
87 checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL);
88 checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL);
89 checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL);
90 checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL);
91 checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL);
92
93 /* Check signed integer conversions. */
94 checkf (" 0", "%5d", 0);
95 checkf ("0 ", "%-5d", 0);
96 checkf (" +0", "%+5d", 0);
97 checkf ("+0 ", "%+-5d", 0);
98 checkf (" 0", "% 5d", 0);
99 checkf ("00000", "%05d", 0);
100 checkf (" ", "%5.0d", 0);
101 checkf (" 00", "%5.2d", 0);
102 checkf ("0", "%d", 0);
103
104 checkf (" 1", "%5d", 1);
105 checkf ("1 ", "%-5d", 1);
106 checkf (" +1", "%+5d", 1);
107 checkf ("+1 ", "%+-5d", 1);
108 checkf (" 1", "% 5d", 1);
109 checkf ("00001", "%05d", 1);
110 checkf (" 1", "%5.0d", 1);
111 checkf (" 01", "%5.2d", 1);
112 checkf ("1", "%d", 1);
113
114 checkf (" -1", "%5d", -1);
115 checkf ("-1 ", "%-5d", -1);
116 checkf (" -1", "%+5d", -1);
117 checkf ("-1 ", "%+-5d", -1);
118 checkf (" -1", "% 5d", -1);
119 checkf ("-0001", "%05d", -1);
120 checkf (" -1", "%5.0d", -1);
121 checkf (" -01", "%5.2d", -1);
122 checkf ("-1", "%d", -1);
123
124 checkf ("12345", "%5d", 12345);
125 checkf ("12345", "%-5d", 12345);
126 checkf ("+12345", "%+5d", 12345);
127 checkf ("+12345", "%+-5d", 12345);
128 checkf (" 12345", "% 5d", 12345);
129 checkf ("12345", "%05d", 12345);
130 checkf ("12345", "%5.0d", 12345);
131 checkf ("12345", "%5.2d", 12345);
132 checkf ("12345", "%d", 12345);
133
134 checkf ("123456", "%5d", 123456);
135 checkf ("123456", "%-5d", 123456);
136 checkf ("+123456", "%+5d", 123456);
137 checkf ("+123456", "%+-5d", 123456);
138 checkf (" 123456", "% 5d", 123456);
139 checkf ("123456", "%05d", 123456);
140 checkf ("123456", "%5.0d", 123456);
141 checkf ("123456", "%5.2d", 123456);
142 checkf ("123456", "%d", 123456);
143
144 /* Check unsigned integer conversions. */
145 checkf (" 0", "%5u", 0);
146 checkf (" 0", "%5o", 0);
147 checkf (" 0", "%5x", 0);
148 checkf (" 0", "%5X", 0);
149 checkf (" 0", "%#5o", 0);
150 checkf (" 0", "%#5x", 0);
151 checkf (" 0", "%#5X", 0);
152 checkf (" 00000000", "%#10.8x", 0);
153
154 checkf (" 1", "%5u", 1);
155 checkf (" 1", "%5o", 1);
156 checkf (" 1", "%5x", 1);
157 checkf (" 1", "%5X", 1);
158 checkf (" 01", "%#5o", 1);
159 checkf (" 0x1", "%#5x", 1);
160 checkf (" 0X1", "%#5X", 1);
161 checkf ("0x00000001", "%#10.8x", 1);
162
163 checkf ("123456", "%5u", 123456);
164 checkf ("361100", "%5o", 123456);
165 checkf ("1e240", "%5x", 123456);
166 checkf ("1E240", "%5X", 123456);
167 checkf ("0361100", "%#5o", 123456);
168 checkf ("0x1e240", "%#5x", 123456);
169 checkf ("0X1E240", "%#5X", 123456);
170 checkf ("0x0001e240", "%#10.8x", 123456);
171
172 /* Character and string conversions. */
173 checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r');
174 checkf (" left-right ", "%6s%s%-7s", "left", "-", "right");
175 checkf ("trim", "%.4s", "trimoff");
176 checkf ("%%", "%%%%");
177
178 /* From Cristian Cadar's automatic test case generator. */
179 checkf (" abcdefgh", "%9s", "abcdefgh");
180 checkf ("36657730000", "%- o", (unsigned) 036657730000);
181 checkf ("4139757568", "%- u", (unsigned) 4139757568UL);
182 checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000);
183 checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000);
184 checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL);
185 checkf ("-155209728", "%-zi", (size_t) -155209728);
186 checkf ("-155209728", "%-zd", (size_t) -155209728);
187 checkf ("036657730000", "%+#o", (unsigned) 036657730000);
188 checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000);
189 checkf ("-155209728", "% zi", (size_t) -155209728);
190 checkf ("-155209728", "% zd", (size_t) -155209728);
191 checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL);
192 checkf ("036657730000", "% #o", (unsigned) 036657730000);
193 checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000);
194 checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000);
195 checkf ("-155209728", "%#zd", (size_t) -155209728);
196 checkf ("-155209728", "%0zi", (size_t) -155209728);
197 checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL);
198 checkf ("-155,209,728", "%-'d", -155209728);
199 checkf ("-155209728", "%.zi", (size_t) -155209728);
200 checkf ("-155209728", "%zi", (size_t) -155209728);
201 checkf ("-155209728", "%zd", (size_t) -155209728);
202 checkf ("-155209728", "%+zi", (size_t) -155209728);
203
204 if (failure_cnt == 0)
205 printf ("\nstdio: PASS\n");
206 else
207 printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt);
208}
diff --git a/pintos-progos/tests/internal/stdlib.c b/pintos-progos/tests/internal/stdlib.c
new file mode 100644
index 0000000..ad0f0f9
--- /dev/null
+++ b/pintos-progos/tests/internal/stdlib.c
@@ -0,0 +1,114 @@
1/* Test program for sorting and searching in lib/stdlib.c.
2
3 Attempts to test the sorting and searching functionality that
4 is not sufficiently tested elsewhere in Pintos.
5
6 This is not a test we will run on your submitted projects.
7 It is here for completeness.
8*/
9
10#undef NDEBUG
11#include <debug.h>
12#include <limits.h>
13#include <random.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include "threads/test.h"
17
18/* Maximum number of elements in an array that we will test. */
19#define MAX_CNT 4096
20
21static void shuffle (int[], size_t);
22static int compare_ints (const void *, const void *);
23static void verify_order (const int[], size_t);
24static void verify_bsearch (const int[], size_t);
25
26/* Test sorting and searching implementations. */
27void
28test (void)
29{
30 int cnt;
31
32 printf ("testing various size arrays:");
33 for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1)
34 {
35 int repeat;
36
37 printf (" %zu", cnt);
38 for (repeat = 0; repeat < 10; repeat++)
39 {
40 static int values[MAX_CNT];
41 int i;
42
43 /* Put values 0...CNT in random order in VALUES. */
44 for (i = 0; i < cnt; i++)
45 values[i] = i;
46 shuffle (values, cnt);
47
48 /* Sort VALUES, then verify ordering. */
49 qsort (values, cnt, sizeof *values, compare_ints);
50 verify_order (values, cnt);
51 verify_bsearch (values, cnt);
52 }
53 }
54
55 printf (" done\n");
56 printf ("stdlib: PASS\n");
57}
58
59/* Shuffles the CNT elements in ARRAY into random order. */
60static void
61shuffle (int *array, size_t cnt)
62{
63 size_t i;
64
65 for (i = 0; i < cnt; i++)
66 {
67 size_t j = i + random_ulong () % (cnt - i);
68 int t = array[j];
69 array[j] = array[i];
70 array[i] = t;
71 }
72}
73
74/* Returns 1 if *A is greater than *B,
75 0 if *A equals *B,
76 -1 if *A is less than *B. */
77static int
78compare_ints (const void *a_, const void *b_)
79{
80 const int *a = a_;
81 const int *b = b_;
82
83 return *a < *b ? -1 : *a > *b;
84}
85
86/* Verifies that ARRAY contains the CNT ints 0...CNT-1. */
87static void
88verify_order (const int *array, size_t cnt)
89{
90 int i;
91
92 for (i = 0; (size_t) i < cnt; i++)
93 ASSERT (array[i] == i);
94}
95
96/* Checks that bsearch() works properly in ARRAY. ARRAY must
97 contain the values 0...CNT-1. */
98static void
99verify_bsearch (const int *array, size_t cnt)
100{
101 int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2};
102 int i;
103
104 /* Check that all the values in the array are found properly. */
105 for (i = 0; (size_t) i < cnt; i++)
106 ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints)
107 == array + i);
108
109 /* Check that some values not in the array are not found. */
110 not_in_array[0] = cnt;
111 for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++)
112 ASSERT (bsearch (&not_in_array[i], array, cnt, sizeof *array, compare_ints)
113 == NULL);
114}
diff --git a/pintos-progos/tests/intro/Grading b/pintos-progos/tests/intro/Grading
new file mode 100644
index 0000000..b1ff6b8
--- /dev/null
+++ b/pintos-progos/tests/intro/Grading
@@ -0,0 +1,5 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
450.0% tests/intro/alarm-clock/Rubric
550.0% tests/intro/userprog-args/Rubric
diff --git a/pintos-progos/tests/intro/alarm-clock/Make.tests b/pintos-progos/tests/intro/alarm-clock/Make.tests
new file mode 100644
index 0000000..55ad443
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/Make.tests
@@ -0,0 +1,15 @@
1# -*- makefile -*-
2tests/intro/alarm-clock/%.output: SIMULATOR = bochs
3tests/intro/alarm-clock/%.output: PINTOSOPTS += --kernel-test
4tests/intro/alarm-clock/%.output: FILESYSSOURCE = --filesys-size=1
5
6# Test names.
7tests/intro/alarm-clock_TESTS = $(addprefix tests/intro/alarm-clock/,alarm-single \
8alarm-multiple alarm-simultaneous alarm-zero alarm-negative)
9
10# Sources for tests.
11tests/intro/alarm-clock_SRC = tests/intro/alarm-clock/tests.c
12tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-wait.c
13tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-simultaneous.c
14tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-zero.c
15tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-negative.c
diff --git a/pintos-progos/tests/intro/alarm-clock/Rubric b/pintos-progos/tests/intro/alarm-clock/Rubric
new file mode 100644
index 0000000..0cf3dc1
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/Rubric
@@ -0,0 +1,7 @@
1Functionality and robustness of alarm clock:
24 alarm-single
34 alarm-multiple
44 alarm-simultaneous
51 alarm-zero
61 alarm-negative
7
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck
new file mode 120000
index 0000000..f3a9edc
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck
@@ -0,0 +1 @@
../../threads/alarm-multiple.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.c b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c
new file mode 120000
index 0000000..483aa63
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c
@@ -0,0 +1 @@
../../threads/alarm-negative.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck
new file mode 120000
index 0000000..279520e
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck
@@ -0,0 +1 @@
../../threads/alarm-negative.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c
new file mode 120000
index 0000000..6362b61
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c
@@ -0,0 +1 @@
../../threads/alarm-simultaneous.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck
new file mode 120000
index 0000000..7226d0c
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck
@@ -0,0 +1 @@
../../threads/alarm-simultaneous.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-single.ck b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck
new file mode 120000
index 0000000..7f98a51
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck
@@ -0,0 +1 @@
../../threads/alarm-single.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-wait.c b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c
new file mode 120000
index 0000000..2755ae5
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c
@@ -0,0 +1 @@
../../threads/alarm-wait.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.c b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c
new file mode 120000
index 0000000..a1f3ca7
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c
@@ -0,0 +1 @@
../../threads/alarm-zero.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck
new file mode 120000
index 0000000..3f98d64
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck
@@ -0,0 +1 @@
../../threads/alarm-zero.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/tests.c b/pintos-progos/tests/intro/alarm-clock/tests.c
new file mode 100644
index 0000000..4a96360
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/tests.c
@@ -0,0 +1,80 @@
1#include "tests/threads/tests.h"
2#include <debug.h>
3#include <string.h>
4#include <stdio.h>
5
6struct test
7 {
8 const char *name;
9 test_func *function;
10 };
11
12static const struct test tests[] =
13 {
14 {"alarm-single", test_alarm_single},
15 {"alarm-multiple", test_alarm_multiple},
16 {"alarm-simultaneous", test_alarm_simultaneous},
17 {"alarm-zero", test_alarm_zero},
18 {"alarm-negative", test_alarm_negative},
19 };
20
21static const char *test_name;
22
23/* Runs the test named NAME. */
24void
25run_test (const char *name)
26{
27 const struct test *t;
28
29 for (t = tests; t < tests + sizeof tests / sizeof *tests; t++)
30 if (!strcmp (name, t->name))
31 {
32 test_name = name;
33 msg ("begin");
34 t->function ();
35 msg ("end");
36 return;
37 }
38 PANIC ("no test named \"%s\"", name);
39}
40
41/* Prints FORMAT as if with printf(),
42 prefixing the output by the name of the test
43 and following it with a new-line character. */
44void
45msg (const char *format, ...)
46{
47 va_list args;
48
49 printf ("(%s) ", test_name);
50 va_start (args, format);
51 vprintf (format, args);
52 va_end (args);
53 putchar ('\n');
54}
55
56/* Prints failure message FORMAT as if with printf(),
57 prefixing the output by the name of the test and FAIL:
58 and following it with a new-line character,
59 and then panics the kernel. */
60void
61fail (const char *format, ...)
62{
63 va_list args;
64
65 printf ("(%s) FAIL: ", test_name);
66 va_start (args, format);
67 vprintf (format, args);
68 va_end (args);
69 putchar ('\n');
70
71 PANIC ("test failed");
72}
73
74/* Prints a message indicating the current test passed. */
75void
76pass (void)
77{
78 printf ("(%s) PASS\n", test_name);
79}
80
diff --git a/pintos-progos/tests/intro/userprog-args/Make.tests b/pintos-progos/tests/intro/userprog-args/Make.tests
new file mode 100644
index 0000000..6f7a474
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/Make.tests
@@ -0,0 +1,27 @@
1# -*- makefile -*-
2
3tests/intro/userprog-args/%.output: FILESYSSOURCE = --filesys-size=2
4tests/intro/userprog-args/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^)
5tests/intro/userprog-args/%.output: SIMULATOR = --qemu
6tests/intro/userprog-args_TESTS = $(addprefix tests/intro/userprog-args/,args-none \
7args-single args-multiple args-many args-dbl-space args-limit)
8
9tests/intro/userprog-args_PROGS = $(tests/intro/userprog-args_TESTS) $(addprefix \
10tests/intro/userprog-args/,child-simple child-args)
11
12tests/intro/userprog-args/args-none_SRC = tests/intro/userprog-args/args.c
13tests/intro/userprog-args/args-single_SRC = tests/intro/userprog-args/args.c
14tests/intro/userprog-args/args-multiple_SRC = tests/intro/userprog-args/args.c
15tests/intro/userprog-args/args-many_SRC = tests/intro/userprog-args/args.c
16tests/intro/userprog-args/args-dbl-space_SRC = tests/intro/userprog-args/args.c
17tests/intro/userprog-args/args-limit_SRC = tests/intro/userprog-args/args-limit.c
18
19tests/intro/userprog-args/child-simple_SRC = tests/intro/userprog-args/child-simple.c
20tests/intro/userprog-args/child-args_SRC = tests/intro/userprog-args/args.c
21
22$(foreach prog,$(tests/intro/userprog-args_PROGS),$(eval $(prog)_SRC += tests/lib.c))
23
24tests/intro/userprog-args/args-single_ARGS = onearg
25tests/intro/userprog-args/args-multiple_ARGS = some arguments for you!
26tests/intro/userprog-args/args-many_ARGS = a b c d e f g h i j k l m n o p q r s t u v
27tests/intro/userprog-args/args-dbl-space_ARGS = two spaces!
diff --git a/pintos-progos/tests/intro/userprog-args/Rubric b/pintos-progos/tests/intro/userprog-args/Rubric
new file mode 100644
index 0000000..f5474c5
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/Rubric
@@ -0,0 +1,7 @@
1Functionality of stack setup:
23 args-none
33 args-single
43 args-multiple
53 args-many
63 args-dbl-space
73 args-limit
diff --git a/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck
new file mode 120000
index 0000000..730d787
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck
@@ -0,0 +1 @@
../../userprog/args-dbl-space.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.c b/pintos-progos/tests/intro/userprog-args/args-limit.c
new file mode 100644
index 0000000..159d868
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-limit.c
@@ -0,0 +1,70 @@
1/* Test the limit for (1) number of arguments and (2) total size of arguments */
2#include <syscall.h>
3#include <string.h>
4#include "tests/lib.h"
5
6#define MAX_SIZE 4096
7
8static bool recurse (int, int);
9
10char cmd[MAX_SIZE * 4];
11
12static bool
13recurse (int argsize, int argcount)
14{
15 int i, j;
16 char *p;
17 strlcpy (cmd, "args-limit", 11);
18 p = cmd+strlen(cmd);
19 for (i = 0; i < argcount; i++) {
20 *p++ = ' ';
21 for (j = 0; j < argsize; j++) {
22 *p++ = 'X';
23 }
24 }
25 *p = 0;
26 if (wait (exec (cmd)) < 0) {
27 return false;
28 } else {
29 return true;
30 }
31}
32
33int
34main (int argc, char **argv)
35{
36 test_name = argv[0];
37 if(argc <= 1) {
38 int step;
39 int max_args = 0, max_size = 0;
40
41 msg ("begin");
42
43 /* Binary search number of arguments */
44 for (step = MAX_SIZE; step > 0 && max_args < MAX_SIZE; step>>=1) {
45 int t = max_args + step;
46 if (recurse (1, t)) {
47 max_args = t;
48 }
49 }
50 if (max_args > 63) {
51 msg ("success. at least 64 command line arguments are supported.");
52 } else {
53 msg ("FAIL: Only %d command line arguments are supported",max_args);
54 }
55 /* Binary search size of arguments */
56 for (step = MAX_SIZE; step > 0 && max_size < MAX_SIZE; step>>=1) {
57 int t = max_size + step;
58 if (recurse (t, 1)) {
59 max_size = t;
60 }
61 }
62 if (max_size >= 100) {
63 msg ("success. arguments with at least 100 bytes are supported.");
64 } else {
65 msg ("FAIL: Arguments with more than %d bytes are not supported.",max_size);
66 }
67 msg ("end");
68 }
69 return 0;
70}
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.ck b/pintos-progos/tests/intro/userprog-args/args-limit.ck
new file mode 100644
index 0000000..ed8ead2
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-limit.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(args-limit) begin
7(args-limit) success. at least 64 command line arguments are supported.
8(args-limit) success. arguments with at least 100 bytes are supported.
9(args-limit) end
10EOF
11pass;
diff --git a/pintos-progos/tests/intro/userprog-args/args-many.ck b/pintos-progos/tests/intro/userprog-args/args-many.ck
new file mode 120000
index 0000000..ed175bd
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-many.ck
@@ -0,0 +1 @@
../../userprog/args-many.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-multiple.ck b/pintos-progos/tests/intro/userprog-args/args-multiple.ck
new file mode 120000
index 0000000..4f9935f
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-multiple.ck
@@ -0,0 +1 @@
../../userprog/args-multiple.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-none.ck b/pintos-progos/tests/intro/userprog-args/args-none.ck
new file mode 120000
index 0000000..861d319
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-none.ck
@@ -0,0 +1 @@
../../userprog/args-none.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-single.ck b/pintos-progos/tests/intro/userprog-args/args-single.ck
new file mode 120000
index 0000000..df8e737
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-single.ck
@@ -0,0 +1 @@
../../userprog/args-single.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args.c b/pintos-progos/tests/intro/userprog-args/args.c
new file mode 120000
index 0000000..0d798ac
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args.c
@@ -0,0 +1 @@
../../userprog/args.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/child-simple.c b/pintos-progos/tests/intro/userprog-args/child-simple.c
new file mode 120000
index 0000000..f8a3ce7
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/child-simple.c
@@ -0,0 +1 @@
../../userprog/child-simple.c \ No newline at end of file
diff --git a/pintos-progos/tests/lib.c b/pintos-progos/tests/lib.c
new file mode 100644
index 0000000..ee36505
--- /dev/null
+++ b/pintos-progos/tests/lib.c
@@ -0,0 +1,196 @@
1#include "tests/lib.h"
2#include <random.h>
3#include <stdarg.h>
4#include <stdio.h>
5#include <string.h>
6#include <syscall.h>
7
8const char *test_name;
9bool quiet = false;
10
11static void
12vmsg (const char *format, va_list args, const char *suffix)
13{
14 /* We go to some trouble to stuff the entire message into a
15 single buffer and output it in a single system call, because
16 that'll (typically) ensure that it gets sent to the console
17 atomically. Otherwise kernel messages like "foo: exit(0)"
18 can end up being interleaved if we're unlucky. */
19 static char buf[1024];
20
21 snprintf (buf, sizeof buf, "(%s) ", test_name);
22 vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args);
23 strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf));
24 write (STDOUT_FILENO, buf, strlen (buf));
25}
26
27void
28msg (const char *format, ...)
29{
30 va_list args;
31
32 if (quiet)
33 return;
34 va_start (args, format);
35 vmsg (format, args, "\n");
36 va_end (args);
37}
38
39void
40fail (const char *format, ...)
41{
42 va_list args;
43
44 va_start (args, format);
45 vmsg (format, args, ": FAILED\n");
46 va_end (args);
47
48 exit (1);
49}
50
51static void
52swap (void *a_, void *b_, size_t size)
53{
54 uint8_t *a = a_;
55 uint8_t *b = b_;
56 size_t i;
57
58 for (i = 0; i < size; i++)
59 {
60 uint8_t t = a[i];
61 a[i] = b[i];
62 b[i] = t;
63 }
64}
65
66void
67shuffle (void *buf_, size_t cnt, size_t size)
68{
69 char *buf = buf_;
70 size_t i;
71
72 for (i = 0; i < cnt; i++)
73 {
74 size_t j = i + random_ulong () % (cnt - i);
75 swap (buf + i * size, buf + j * size, size);
76 }
77}
78
79void
80exec_children (const char *child_name, pid_t pids[], size_t child_cnt)
81{
82 size_t i;
83
84 for (i = 0; i < child_cnt; i++)
85 {
86 char cmd_line[128];
87 snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i);
88 CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR,
89 "exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line);
90 }
91}
92
93void
94wait_children (pid_t pids[], size_t child_cnt)
95{
96 size_t i;
97
98 for (i = 0; i < child_cnt; i++)
99 {
100 int status = wait (pids[i]);
101 CHECK (status == (int) i,
102 "wait for child %zu of %zu returned %d (expected %zu)",
103 i + 1, child_cnt, status, i);
104 }
105}
106
107void
108check_file_handle (int fd,
109 const char *file_name, const void *buf_, size_t size)
110{
111 const char *buf = buf_;
112 size_t ofs = 0;
113 size_t file_size;
114
115 /* Warn about file of wrong size. Don't fail yet because we
116 may still be able to get more information by reading the
117 file. */
118 file_size = filesize (fd);
119 if (file_size != size)
120 msg ("size of %s (%zu) differs from expected (%zu)",
121 file_name, file_size, size);
122
123 /* Read the file block-by-block, comparing data as we go. */
124 while (ofs < size)
125 {
126 char block[512];
127 size_t block_size, ret_val;
128
129 block_size = size - ofs;
130 if (block_size > sizeof block)
131 block_size = sizeof block;
132
133 ret_val = read (fd, block, block_size);
134 if (ret_val != block_size)
135 fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu",
136 block_size, ofs, file_name, ret_val);
137
138 compare_bytes (block, buf + ofs, block_size, ofs, file_name);
139 ofs += block_size;
140 }
141
142 /* Now fail due to wrong file size. */
143 if (file_size != size)
144 fail ("size of %s (%zu) differs from expected (%zu)",
145 file_name, file_size, size);
146
147 msg ("verified contents of \"%s\"", file_name);
148}
149
150void
151check_file (const char *file_name, const void *buf, size_t size)
152{
153 int fd;
154
155 CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification",
156 file_name);
157 check_file_handle (fd, file_name, buf, size);
158 msg ("close \"%s\"", file_name);
159 close (fd);
160}
161
162void
163compare_bytes (const void *read_data_, const void *expected_data_, size_t size,
164 size_t ofs, const char *file_name)
165{
166 const uint8_t *read_data = read_data_;
167 const uint8_t *expected_data = expected_data_;
168 size_t i, j;
169 size_t show_cnt;
170
171 if (!memcmp (read_data, expected_data, size))
172 return;
173
174 for (i = 0; i < size; i++)
175 if (read_data[i] != expected_data[i])
176 break;
177 for (j = i + 1; j < size; j++)
178 if (read_data[j] == expected_data[j])
179 break;
180
181 quiet = false;
182 msg ("%zu bytes read starting at offset %zu in \"%s\" differ "
183 "from expected.", j - i, ofs + i, file_name);
184 show_cnt = j - i;
185 if (j - i > 64)
186 {
187 show_cnt = 64;
188 msg ("Showing first differing %zu bytes.", show_cnt);
189 }
190 msg ("Data actually read:");
191 hex_dump (ofs + i, read_data + i, show_cnt, true);
192 msg ("Expected data:");
193 hex_dump (ofs + i, expected_data + i, show_cnt, true);
194 fail ("%zu bytes read starting at offset %zu in \"%s\" differ "
195 "from expected", j - i, ofs + i, file_name);
196}
diff --git a/pintos-progos/tests/lib.h b/pintos-progos/tests/lib.h
new file mode 100644
index 0000000..648327b
--- /dev/null
+++ b/pintos-progos/tests/lib.h
@@ -0,0 +1,50 @@
1#ifndef TESTS_LIB_H
2#define TESTS_LIB_H
3
4#include <debug.h>
5#include <stdbool.h>
6#include <stddef.h>
7#include <syscall.h>
8
9extern const char *test_name;
10extern bool quiet;
11
12void msg (const char *, ...) PRINTF_FORMAT (1, 2);
13void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN;
14
15/* Takes an expression to test for SUCCESS and a message, which
16 may include printf-style arguments. Logs the message, then
17 tests the expression. If it is zero, indicating failure,
18 emits the message as a failure.
19
20 Somewhat tricky to use:
21
22 - SUCCESS must not have side effects that affect the
23 message, because that will cause the original message and
24 the failure message to differ.
25
26 - The message must not have side effects of its own, because
27 it will be printed twice on failure, or zero times on
28 success if quiet is set. */
29#define CHECK(SUCCESS, ...) \
30 do \
31 { \
32 msg (__VA_ARGS__); \
33 if (!(SUCCESS)) \
34 fail (__VA_ARGS__); \
35 } \
36 while (0)
37
38void shuffle (void *, size_t cnt, size_t size);
39
40void exec_children (const char *child_name, pid_t pids[], size_t child_cnt);
41void wait_children (pid_t pids[], size_t child_cnt);
42
43void check_file_handle (int fd, const char *file_name,
44 const void *buf_, size_t filesize);
45void check_file (const char *file_name, const void *buf, size_t filesize);
46
47void compare_bytes (const void *read_data, const void *expected_data,
48 size_t size, size_t ofs, const char *file_name);
49
50#endif /* test/lib.h */
diff --git a/pintos-progos/tests/lib.pm b/pintos-progos/tests/lib.pm
new file mode 100644
index 0000000..bc37ae5
--- /dev/null
+++ b/pintos-progos/tests/lib.pm
@@ -0,0 +1,19 @@
1use strict;
2use warnings;
3
4use tests::random;
5
6sub shuffle {
7 my ($in, $cnt, $sz) = @_;
8 $cnt * $sz == length $in or die;
9 my (@a) = 0...$cnt - 1;
10 for my $i (0...$cnt - 1) {
11 my ($j) = $i + random_ulong () % ($cnt - $i);
12 @a[$i, $j] = @a[$j, $i];
13 }
14 my ($out) = "";
15 $out .= substr ($in, $_ * $sz, $sz) foreach @a;
16 return $out;
17}
18
191;
diff --git a/pintos-progos/tests/main.c b/pintos-progos/tests/main.c
new file mode 100644
index 0000000..ad1b0f1
--- /dev/null
+++ b/pintos-progos/tests/main.c
@@ -0,0 +1,15 @@
1#include <random.h>
2#include "tests/lib.h"
3#include "tests/main.h"
4
5int
6main (int argc UNUSED, char *argv[])
7{
8 test_name = argv[0];
9
10 msg ("begin");
11 random_init (0);
12 test_main ();
13 msg ("end");
14 return 0;
15}
diff --git a/pintos-progos/tests/main.h b/pintos-progos/tests/main.h
new file mode 100644
index 0000000..f0e8818
--- /dev/null
+++ b/pintos-progos/tests/main.h
@@ -0,0 +1,6 @@
1#ifndef TESTS_MAIN_H
2#define TESTS_MAIN_H
3
4void test_main (void);
5
6#endif /* tests/main.h */
diff --git a/pintos-progos/tests/make-grade b/pintos-progos/tests/make-grade
new file mode 100755
index 0000000..a3faa0e
--- /dev/null
+++ b/pintos-progos/tests/make-grade
@@ -0,0 +1,152 @@
1#! /usr/bin/perl
2
3use strict;
4use warnings;
5
6@ARGV == 3 || die;
7my ($src_dir, $results_file, $grading_file) = @ARGV;
8
9# Read pass/file verdicts from $results_file.
10open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n";
11my (%verdicts, %verdict_counts);
12while (<RESULTS>) {
13 my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die;
14 $verdicts{$test} = $verdict eq 'pass';
15}
16close RESULTS;
17
18my (@failures);
19my (@overall, @rubrics, @summary);
20my ($pct_actual, $pct_possible) = (0, 0);
21
22# Read grading file.
23my (@items);
24open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n";
25while (<GRADING>) {
26 s/#.*//;
27 next if /^\s*$/;
28 my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die;
29 my ($dir) = $rubric_suffix =~ /^(.*)\//;
30 my ($rubric_file) = "$src_dir/$rubric_suffix";
31 open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n";
32
33 # Rubric file must begin with title line.
34 my $title = <RUBRIC>;
35 chomp $title;
36 $title =~ s/:$// or die;
37 $title .= " ($rubric_suffix):";
38 push (@rubrics, $title);
39
40 my ($score, $possible) = (0, 0);
41 my ($cnt, $passed) = (0, 0);
42 my ($was_score) = 0;
43 while (<RUBRIC>) {
44 chomp;
45 push (@rubrics, "\t$_"), next if /^-/;
46 push (@rubrics, ""), next if /^\s*$/;
47 my ($poss, $name) = /^(\d+)\t(.*)$/ or die;
48 my ($test) = "$dir/$name";
49 my ($points) = 0;
50 if (!defined $verdicts{$test}) {
51 push (@overall, "warning: $test not tested, assuming failure");
52 } elsif ($verdicts{$test}) {
53 $points = $poss;
54 $passed++;
55 }
56 push (@failures, $test) if !$points;
57 $verdict_counts{$test}++;
58 push (@rubrics, sprintf ("\t%4s%2d/%2d %s",
59 $points ? '' : '**', $points, $poss, $test));
60 $score += $points;
61 $possible += $poss;
62 $cnt++;
63 }
64 close (RUBRIC);
65
66 push (@rubrics, "");
67 push (@rubrics, "\t- Section summary.");
68 push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
69 '', $passed, $cnt, 'tests passed'));
70 push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
71 '', $score, $possible, 'points subtotal'));
72 push (@rubrics, '');
73
74 my ($pct) = ($score / $possible) * $max_pct;
75 push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%",
76 $rubric_suffix,
77 $score, $possible,
78 $pct, $max_pct));
79 $pct_actual += $pct;
80 $pct_possible += $max_pct;
81}
82close GRADING;
83
84my ($sum_line)
85 = "--------------------------------------------- --- --- ------ ------";
86unshift (@summary,
87 "SUMMARY BY TEST SET",
88 '',
89 sprintf ("%-45s %3s %3s %6s %6s",
90 "Test Set", "Pts", "Max", "% Ttl", "% Max"),
91 $sum_line);
92push (@summary,
93 $sum_line,
94 sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%",
95 'Total', '', '', $pct_actual, $pct_possible));
96
97unshift (@rubrics,
98 "SUMMARY OF INDIVIDUAL TESTS",
99 '');
100
101foreach my $name (keys (%verdicts)) {
102 my ($count) = $verdict_counts{$name};
103 if (!defined ($count) || $count != 1) {
104 if (!defined ($count) || !$count) {
105 push (@overall, "warning: test $name doesn't count for grading");
106 } else {
107 push (@overall,
108 "warning: test $name counted $count times in grading");
109 }
110 }
111}
112push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual));
113if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) {
114 push (@overall, "ALL TESTED PASSED -- PERFECT SCORE");
115}
116
117my (@divider) = ('', '- ' x 38, '');
118
119print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics);
120
121for my $test (@failures) {
122 print map ("$_\n", @divider);
123 print "DETAILS OF $test FAILURE:\n\n";
124
125 if (open (RESULT, '<', "$test.result")) {
126 my $first_line = <RESULT>;
127 my ($cnt) = 0;
128 while (<RESULT>) {
129 print;
130 $cnt++;
131 }
132 close (RESULT);
133 }
134
135 if (open (OUTPUT, '<', "$test.output")) {
136 print "\nOUTPUT FROM $test:\n\n";
137
138 my ($panics, $boots) = (0, 0);
139 while (<OUTPUT>) {
140 if (/PANIC/ && ++$panics > 2) {
141 print "[...details of additional panic(s) omitted...]\n";
142 last;
143 }
144 print;
145 if (/Pintos booting/ && ++$boots > 1) {
146 print "[...details of reboot(s) omitted...]\n";
147 last;
148 }
149 }
150 close (OUTPUT);
151 }
152}
diff --git a/pintos-progos/tests/random.pm b/pintos-progos/tests/random.pm
new file mode 100644
index 0000000..be008ff
--- /dev/null
+++ b/pintos-progos/tests/random.pm
@@ -0,0 +1,27 @@
1use strict;
2use warnings;
3
4use tests::arc4;
5
6my (@arc4);
7
8sub random_init {
9 if (@arc4 == 0) {
10 my ($seed) = @_;
11 $seed = 0 if !defined $seed;
12 @arc4 = arc4_init (pack ("V", $seed));
13 }
14}
15
16sub random_bytes {
17 random_init ();
18 my ($n) = @_;
19 return arc4_crypt (\@arc4, "\0" x $n);
20}
21
22sub random_ulong {
23 random_init ();
24 return unpack ("V", random_bytes (4));
25}
26
271;
diff --git a/pintos-progos/tests/tests.pm b/pintos-progos/tests/tests.pm
new file mode 100644
index 0000000..4599cb9
--- /dev/null
+++ b/pintos-progos/tests/tests.pm
@@ -0,0 +1,625 @@
1use strict;
2use warnings;
3use tests::Algorithm::Diff;
4use File::Temp 'tempfile';
5use Fcntl qw(SEEK_SET SEEK_CUR);
6
7sub fail;
8sub pass;
9
10die if @ARGV != 2;
11our ($test, $src_dir) = @ARGV;
12
13my ($msg_file) = tempfile ();
14select ($msg_file);
15
16our (@prereq_tests) = ();
17if ($test =~ /^(.*)-persistence$/) {
18 push (@prereq_tests, $1);
19}
20for my $prereq_test (@prereq_tests) {
21 my (@result) = read_text_file ("$prereq_test.result");
22 fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS';
23}
24
25
26# Generic testing.
27
28sub check_expected {
29 my ($expected) = pop @_;
30 my (@options) = @_;
31 my (@output) = read_text_file ("$test.output");
32 common_checks ("run", @output);
33 compare_output ("run", @options, \@output, $expected);
34}
35
36sub common_checks {
37 my ($run, @output) = @_;
38
39 fail "\u$run produced no output at all\n" if @output == 0;
40
41 check_for_panic ($run, @output);
42 check_for_keyword ($run, "FAIL", @output);
43 check_for_triple_fault ($run, @output);
44 check_for_keyword ($run, "TIMEOUT", @output);
45
46 fail "\u$run didn't start up properly: no \"Pintos booting\" message\n"
47 if !grep (/Pintos booting with.*kB RAM\.\.\./, @output);
48 fail "\u$run didn't start up properly: no \"Boot complete\" message\n"
49 if !grep (/Boot complete/, @output);
50 fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n"
51 if !grep (/Timer: \d+ ticks/, @output);
52 fail "\u$run didn't shut down properly: no \"Powering off\" message\n"
53 if !grep (/Powering off/, @output);
54}
55
56sub check_for_panic {
57 my ($run, @output) = @_;
58
59 my ($panic) = grep (/PANIC/, @output);
60 return unless defined $panic;
61
62 print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")),
63 "\n";
64
65 my (@stack_line) = grep (/Call stack:/, @output);
66 if (@stack_line != 0) {
67 my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/;
68
69 # Find a user program to translate user virtual addresses.
70 my ($userprog) = "";
71 $userprog = "$test"
72 if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test;
73
74 # Get and print the backtrace.
75 my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`);
76 print "Call stack:$addrs\n";
77 print "Translation of call stack:\n";
78 print $trace;
79
80 # Print disclaimer.
81 if ($userprog ne '' && index ($trace, $userprog) >= 0) {
82 print <<EOF;
83Translations of user virtual addresses above are based on a guess at
84the binary to use. If this guess is incorrect, then those
85translations will be misleading.
86EOF
87 }
88 }
89
90 if ($panic =~ /sec_no \< d-\>capacity/) {
91 print <<EOF;
92\nThis assertion commonly fails when accessing a file via an inode that
93has been closed and freed. Freeing an inode clears all its sector
94indexes to 0xcccccccc, which is not a valid sector number for disks
95smaller than about 1.6 TB.
96EOF
97 }
98
99 fail;
100}
101
102sub check_for_keyword {
103 my ($run, $keyword, @output) = @_;
104
105 my ($kw_line) = grep (/$keyword/, @output);
106 return unless defined $kw_line;
107
108 # Most output lines are prefixed by (test-name). Eliminate this
109 # from our message for brevity.
110 $kw_line =~ s/^\([^\)]+\)\s+//;
111 print "$run: $kw_line\n";
112
113 fail;
114}
115
116sub check_for_triple_fault {
117 my ($run, @output) = @_;
118
119 my ($reboots) = grep (/Pintos booting/, @output) - 1;
120 return unless $reboots > 0;
121
122 print <<EOF;
123\u$run spontaneously rebooted $reboots times.
124This is most often caused by unhandled page faults.
125Read the Triple Faults section in the Debugging chapter
126of the Pintos manual for more information.
127EOF
128
129 fail;
130}
131
132# Get @output without header or trailer.
133sub get_core_output {
134 my ($run, @output) = @_;
135 my ($p);
136
137 my ($process);
138 my ($start);
139 for my $i (0...$#_) {
140 $start = $i + 1, last
141 if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/;
142 }
143
144 my ($end);
145 for my $i ($start...$#output) {
146 $end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/;
147 }
148
149 fail "\u$run didn't start a thread or process\n" if !defined $start;
150 fail "\u$run started '$process' but it never finished\n" if !defined $end;
151
152 return @output[$start...$end];
153}
154
155sub compare_output {
156 my ($run) = shift @_;
157 my ($expected) = pop @_;
158 my ($output) = pop @_;
159 my (%options) = @_;
160
161 my (@output) = get_core_output ($run, @$output);
162 fail "\u$run didn't produce any output" if !@output;
163
164 my $ignore_exit_codes = exists $options{IGNORE_EXIT_CODES};
165 if ($ignore_exit_codes) {
166 delete $options{IGNORE_EXIT_CODES};
167 @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\-?\d+\)$/, @output);
168 }
169 my $ignore_user_faults = exists $options{IGNORE_USER_FAULTS};
170 if ($ignore_user_faults) {
171 delete $options{IGNORE_USER_FAULTS};
172 @output = grep (!/^Page fault at.*in user context\.$/
173 && !/: dying due to interrupt 0x0e \(.*\).$/
174 && !/^Interrupt 0x0e \(.*\) at eip=/
175 && !/^ cr2=.* error=.*/
176 && !/^ eax=.* ebx=.* ecx=.* edx=.*/
177 && !/^ esi=.* edi=.* esp=.* ebp=.*/
178 && !/^ cs=.* ds=.* es=.* ss=.*/, @output);
179 }
180 die "unknown option " . (keys (%options))[0] . "\n" if %options;
181
182 my ($msg);
183
184 # Compare actual output against each allowed output.
185 if (ref ($expected) eq 'ARRAY') {
186 my ($i) = 0;
187 $expected = {map ((++$i => $_), @$expected)};
188 }
189 foreach my $key (keys %$expected) {
190 my (@expected) = split ("\n", $expected->{$key});
191
192 $msg .= "Acceptable output:\n";
193 $msg .= join ('', map (" $_\n", @expected));
194
195 # Check whether actual and expected match.
196 # If it's a perfect match, we're done.
197 if ($#output == $#expected) {
198 my ($eq) = 1;
199 for (my ($i) = 0; $i <= $#expected; $i++) {
200 $eq = 0 if $output[$i] ne $expected[$i];
201 }
202 return $key if $eq;
203 }
204
205 # They differ. Output a diff.
206 my (@diff) = "";
207 my ($d) = Algorithm::Diff->new (\@expected, \@output);
208 while ($d->Next ()) {
209 my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
210 if ($d->Same ()) {
211 push (@diff, map (" $_\n", $d->Items (1)));
212 } else {
213 push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1);
214 push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2);
215 }
216 }
217
218 $msg .= "Differences in `diff -u' format:\n";
219 $msg .= join ('', @diff);
220 }
221
222 # Failed to match. Report failure.
223 $msg .= "\n(Process exit codes are excluded for matching purposes.)\n"
224 if $ignore_exit_codes;
225 $msg .= "\n(User fault messages are excluded for matching purposes.)\n"
226 if $ignore_user_faults;
227 fail "Test output failed to match any acceptable form.\n\n$msg";
228}
229
230# File system extraction.
231
232# check_archive (\%CONTENTS)
233#
234# Checks that the extracted file system's contents match \%CONTENTS.
235# Each key in the hash is a file name. Each value may be:
236#
237# - $FILE: Name of a host file containing the expected contents.
238#
239# - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE
240# comprising the $LENGTH bytes starting at $OFFSET.
241#
242# - [$CONTENTS]: The literal expected file contents, as a string.
243#
244# - {SUBDIR}: A subdirectory, in the same form described here,
245# recursively.
246sub check_archive {
247 my ($expected_hier) = @_;
248
249 my (@output) = read_text_file ("$test.output");
250 common_checks ("file system extraction run", @output);
251
252 @output = get_core_output ("file system extraction run", @output);
253 @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output);
254 fail join ("\n", "Error extracting file system:", @output) if @output;
255
256 my ($test_base_name) = $test;
257 $test_base_name =~ s%.*/%%;
258 $test_base_name =~ s%-persistence$%%;
259 $expected_hier->{$test_base_name} = $prereq_tests[0];
260 $expected_hier->{'tar'} = 'tests/filesys/extended/tar';
261
262 my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, ""));
263 my (%actual) = read_tar ("$prereq_tests[0].tar");
264
265 my ($errors) = 0;
266 foreach my $name (sort keys %expected) {
267 if (exists $actual{$name}) {
268 if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) {
269 print "$name is a directory but should be an ordinary file.\n";
270 $errors++;
271 } elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) {
272 print "$name is an ordinary file but should be a directory.\n";
273 $errors++;
274 }
275 } else {
276 print "$name is missing from the file system.\n";
277 $errors++;
278 }
279 }
280 foreach my $name (sort keys %actual) {
281 if (!exists $expected{$name}) {
282 if ($name =~ /^[[:print:]]+$/) {
283 print "$name exists in the file system but it should not.\n";
284 } else {
285 my ($esc_name) = $name;
286 $esc_name =~ s/[^[:print:]]/./g;
287 print <<EOF;
288$esc_name exists in the file system but should not. (The name
289of this file contains unusual characters that were printed as `.'.)
290EOF
291 }
292 $errors++;
293 }
294 }
295 if ($errors) {
296 print "\nActual contents of file system:\n";
297 print_fs (%actual);
298 print "\nExpected contents of file system:\n";
299 print_fs (%expected);
300 } else {
301 foreach my $name (sort keys %expected) {
302 if (!is_dir ($expected{$name})) {
303 my ($exp_file, $exp_length) = open_file ($expected{$name});
304 my ($act_file, $act_length) = open_file ($actual{$name});
305 $errors += !compare_files ($exp_file, $exp_length,
306 $act_file, $act_length, $name,
307 !$errors);
308 close ($exp_file);
309 close ($act_file);
310 }
311 }
312 }
313 fail "Extracted file system contents are not correct.\n" if $errors;
314}
315
316# open_file ([$FILE, $OFFSET, $LENGTH])
317# open_file ([$CONTENTS])
318#
319# Opens a file for the contents passed in, which must be in one of
320# the two above forms that correspond to check_archive() arguments.
321#
322# Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and
323# $LENGTH is the number of bytes in the file's content.
324sub open_file {
325 my ($value) = @_;
326 die if ref ($value) ne 'ARRAY';
327
328 my ($file) = tempfile ();
329 my ($length);
330 if (@$value == 1) {
331 $length = length ($value->[0]);
332 $file = tempfile ();
333 syswrite ($file, $value->[0]) == $length
334 or die "writing temporary file: $!\n";
335 sysseek ($file, 0, SEEK_SET);
336 } elsif (@$value == 3) {
337 $length = $value->[2];
338 open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n";
339 die "$value->[0]: file is smaller than expected\n"
340 if -s $file < $value->[1] + $length;
341 sysseek ($file, $value->[1], SEEK_SET);
342 } else {
343 die;
344 }
345 return ($file, $length);
346}
347
348# compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE)
349#
350# Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B.
351# ($A and $B are handles.)
352# If their contents differ, prints a brief message describing
353# the differences, using $NAME to identify the file.
354# The message contains more detail if $VERBOSE is nonzero.
355# Returns 1 if the contents are identical, 0 otherwise.
356sub compare_files {
357 my ($a, $a_size, $b, $b_size, $name, $verbose) = @_;
358 my ($ofs) = 0;
359 select(STDOUT);
360 for (;;) {
361 my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size;
362 my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size;
363 my ($a_data, $b_data);
364 if (!defined (sysread ($a, $a_data, $a_amt))
365 || !defined (sysread ($b, $b_data, $b_amt))) {
366 die "reading $name: $!\n";
367 }
368
369 my ($a_len) = length $a_data;
370 my ($b_len) = length $b_data;
371 last if $a_len == 0 && $b_len == 0;
372
373 if ($a_data ne $b_data) {
374 my ($min_len) = $a_len < $b_len ? $a_len : $b_len;
375 my ($diff_ofs);
376 for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) {
377 last if (substr ($a_data, $diff_ofs, 1)
378 ne substr ($b_data, $diff_ofs, 1));
379 }
380
381 printf "\nFile $name differs from expected "
382 . "starting at offset 0x%x.\n", $ofs + $diff_ofs;
383 if ($verbose ) {
384 print "Expected contents:\n";
385 hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs);
386 print "Actual contents:\n";
387 hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs);
388 }
389 return 0;
390 }
391
392 $ofs += $a_len;
393 $a_size -= $a_len;
394 $b_size -= $b_len;
395 }
396 return 1;
397}
398
399# hex_dump ($DATA, $OFS)
400#
401# Prints $DATA in hex and text formats.
402# The first byte of $DATA corresponds to logical offset $OFS
403# in whatever file the data comes from.
404sub hex_dump {
405 my ($data, $ofs) = @_;
406
407 if ($data eq '') {
408 printf " (File ends at offset %08x.)\n", $ofs;
409 return;
410 }
411
412 my ($per_line) = 16;
413 while ((my $size = length ($data)) > 0) {
414 my ($start) = $ofs % $per_line;
415 my ($end) = $per_line;
416 $end = $start + $size if $end - $start > $size;
417 my ($n) = $end - $start;
418
419 printf "0x%08x ", int ($ofs / $per_line) * $per_line;
420
421 # Hex version.
422 print " " x $start;
423 for my $i ($start...$end - 1) {
424 printf "%02x", ord (substr ($data, $i - $start, 1));
425 print $i == $per_line / 2 - 1 ? '-' : ' ';
426 }
427 print " " x ($per_line - $end);
428
429 # Character version.
430 my ($esc_data) = substr ($data, 0, $n);
431 $esc_data =~ s/[^[:print:]]/./g;
432 print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|";
433
434 print "\n";
435
436 $data = substr ($data, $n);
437 $ofs += $n;
438 }
439}
440
441# print_fs (%FS)
442#
443# Prints a list of files in %FS, which must be a file system
444# as flattened by flatten_hierarchy() and normalized by
445# normalize_fs().
446sub print_fs {
447 my (%fs) = @_;
448 foreach my $name (sort keys %fs) {
449 my ($esc_name) = $name;
450 $esc_name =~ s/[^[:print:]]/./g;
451 print "$esc_name: ";
452 if (!is_dir ($fs{$name})) {
453 print +file_size ($fs{$name}), "-byte file";
454 } else {
455 print "directory";
456 }
457 print "\n";
458 }
459 print "(empty)\n" if !@_;
460}
461
462# normalize_fs (%FS)
463#
464# Takes a file system as flattened by flatten_hierarchy().
465# Returns a similar file system in which values of the form $FILE
466# are replaced by those of the form [$FILE, $OFFSET, $LENGTH].
467sub normalize_fs {
468 my (%fs) = @_;
469 foreach my $name (keys %fs) {
470 my ($value) = $fs{$name};
471 next if is_dir ($value) || ref ($value) ne '';
472 die "can't open $value\n" if !stat $value;
473 $fs{$name} = [$value, 0, -s _];
474 }
475 return %fs;
476}
477
478# is_dir ($VALUE)
479#
480# Takes a value like one in the hash returned by flatten_hierarchy()
481# and returns 1 if it represents a directory, 0 otherwise.
482sub is_dir {
483 my ($value) = @_;
484 return ref ($value) eq '' && $value eq 'directory';
485}
486
487# file_size ($VALUE)
488#
489# Takes a value like one in the hash returned by flatten_hierarchy()
490# and returns the size of the file it represents.
491sub file_size {
492 my ($value) = @_;
493 die if is_dir ($value);
494 die if ref ($value) ne 'ARRAY';
495 return @$value > 1 ? $value->[2] : length ($value->[0]);
496}
497
498# flatten_hierarchy ($HIER_FS, $PREFIX)
499#
500# Takes a file system in the format expected by check_archive() and
501# returns a "flattened" version in which file names include all parent
502# directory names and the value of directories is just "directory".
503sub flatten_hierarchy {
504 my (%hier_fs) = %{$_[0]};
505 my ($prefix) = $_[1];
506 my (%flat_fs);
507 for my $name (keys %hier_fs) {
508 my ($value) = $hier_fs{$name};
509 if (ref $value eq 'HASH') {
510 %flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/"));
511 $flat_fs{"$prefix$name"} = 'directory';
512 } else {
513 $flat_fs{"$prefix$name"} = $value;
514 }
515 }
516 return %flat_fs;
517}
518
519# read_tar ($ARCHIVE)
520#
521# Reads the ustar-format tar file in $ARCHIVE
522# and returns a flattened file system for it.
523sub read_tar {
524 my ($archive) = @_;
525 my (%content);
526 open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n";
527 for (;;) {
528 my ($header);
529 if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) {
530 fail "$archive: unexpected end of file\n" if $retval >= 0;
531 fail "$archive: read: $!\n";
532 }
533
534 last if $header eq "\0" x 512;
535
536 # Verify magic numbers.
537 if (substr ($header, 257, 6) ne "ustar\0"
538 || substr ($header, 263, 2) ne '00') {
539 fail "$archive: corrupt ustar header\n";
540 }
541
542 # Verify checksum.
543 my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8)));
544 my ($correct_chksum) = unpack ("%32a*", $header);
545 fail "$archive: bad header checksum\n" if $chksum != $correct_chksum;
546
547 # Get file name.
548 my ($name) = unpack ("Z100", $header);
549 my ($prefix) = unpack ("Z*", substr ($header, 345));
550 $name = "$prefix/$name" if $prefix ne '';
551 fail "$archive: contains file with empty name" if $name eq '';
552
553 # Get type.
554 my ($typeflag) = substr ($header, 156, 1);
555 $typeflag = '0' if $typeflag eq "\0";
556 fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/;
557
558 # Get size.
559 my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
560 fail "bad size $size\n" if $size < 0;
561 $size = 0 if $typeflag eq '5';
562
563 # Store content.
564 $name =~ s%^(/|\./|\.\./)*%%; # Strip leading "/", "./", "../".
565 $name = '' if $name eq '.' || $name eq '..';
566 if (exists $content{$name}) {
567 fail "$archive: contains multiple entries for $name\n";
568 }
569 if ($typeflag eq '5') {
570 $content{$name} = 'directory' if $name ne '';
571 } else {
572 fail "$archive: contains file with empty name\n" if $name eq '';
573 my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR);
574 $content{$name} = [$archive, $position, $size];
575 sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR);
576 }
577 }
578 close (ARCHIVE);
579 return %content;
580}
581
582# Utilities.
583
584sub fail {
585 finish ("FAIL", @_);
586}
587
588sub pass {
589 finish ("PASS", @_);
590}
591
592sub finish {
593 my ($verdict, @messages) = @_;
594
595 seek ($msg_file, 0, 0);
596 push (@messages, <$msg_file>);
597 close ($msg_file);
598 chomp (@messages);
599
600 my ($result_fn) = "$test.result";
601 open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n";
602 print RESULT "$verdict\n";
603 print RESULT "$_\n" foreach @messages;
604 close (RESULT);
605
606 if ($verdict eq 'PASS') {
607 print STDOUT "pass $test\n";
608 } else {
609 print STDOUT "FAIL $test\n";
610 }
611 print STDOUT "$_\n" foreach @messages;
612
613 exit 0;
614}
615
616sub read_text_file {
617 my ($file_name) = @_;
618 open (FILE, '<', $file_name) or die "$file_name: open: $!\n";
619 my (@content) = <FILE>;
620 chomp (@content);
621 close (FILE);
622 return @content;
623}
624
6251;
diff --git a/pintos-progos/tests/threads/Grading b/pintos-progos/tests/threads/Grading
new file mode 100644
index 0000000..cc235f2
--- /dev/null
+++ b/pintos-progos/tests/threads/Grading
@@ -0,0 +1,11 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# Priority Scheduling
590.0% tests/threads/Rubric.priority
6
7# Robustness
810.0% tests/threads/Rubric.alarm
9
10# Not used in SS 2012
11# XX.0% tests/threads/Rubric.mlfqs
diff --git a/pintos-progos/tests/threads/Make.tests b/pintos-progos/tests/threads/Make.tests
new file mode 100644
index 0000000..dbdfd0c
--- /dev/null
+++ b/pintos-progos/tests/threads/Make.tests
@@ -0,0 +1,56 @@
1# -*- makefile -*-
2
3# Test names.
4tests/threads_TESTS = $(addprefix tests/threads/,alarm-single \
5alarm-multiple alarm-simultaneous alarm-priority alarm-zero \
6alarm-negative priority-change priority-donate-one \
7priority-donate-multiple priority-donate-multiple2 \
8priority-donate-nest priority-donate-sema priority-donate-lower \
9priority-fifo priority-preempt priority-sema priority-condvar \
10priority-donate-chain)
11
12
13# Sources for tests.
14tests/threads_SRC = tests/threads/tests.c
15tests/threads_SRC += tests/threads/alarm-wait.c
16tests/threads_SRC += tests/threads/alarm-simultaneous.c
17tests/threads_SRC += tests/threads/alarm-priority.c
18tests/threads_SRC += tests/threads/alarm-zero.c
19tests/threads_SRC += tests/threads/alarm-negative.c
20tests/threads_SRC += tests/threads/priority-change.c
21tests/threads_SRC += tests/threads/priority-donate-one.c
22tests/threads_SRC += tests/threads/priority-donate-multiple.c
23tests/threads_SRC += tests/threads/priority-donate-multiple2.c
24tests/threads_SRC += tests/threads/priority-donate-nest.c
25tests/threads_SRC += tests/threads/priority-donate-sema.c
26tests/threads_SRC += tests/threads/priority-donate-lower.c
27tests/threads_SRC += tests/threads/priority-fifo.c
28tests/threads_SRC += tests/threads/priority-preempt.c
29tests/threads_SRC += tests/threads/priority-sema.c
30tests/threads_SRC += tests/threads/priority-condvar.c
31tests/threads_SRC += tests/threads/priority-donate-chain.c
32
33# Not used in SS 2012
34MLFQS_TESTS = mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 \
35mlfqs-fair-2 mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block)
36
37tests/threads_SRC += tests/threads/mlfqs-load-1.c
38tests/threads_SRC += tests/threads/mlfqs-load-60.c
39tests/threads_SRC += tests/threads/mlfqs-load-avg.c
40tests/threads_SRC += tests/threads/mlfqs-recent-1.c
41tests/threads_SRC += tests/threads/mlfqs-fair.c
42tests/threads_SRC += tests/threads/mlfqs-block.c
43
44MLFQS_OUTPUTS = \
45tests/threads/mlfqs-load-1.output \
46tests/threads/mlfqs-load-60.output \
47tests/threads/mlfqs-load-avg.output \
48tests/threads/mlfqs-recent-1.output \
49tests/threads/mlfqs-fair-2.output \
50tests/threads/mlfqs-fair-20.output \
51tests/threads/mlfqs-nice-2.output \
52tests/threads/mlfqs-nice-10.output \
53tests/threads/mlfqs-block.output
54
55$(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs
56$(MLFQS_OUTPUTS): TIMEOUT = 480
diff --git a/pintos-progos/tests/threads/Rubric.alarm b/pintos-progos/tests/threads/Rubric.alarm
new file mode 100644
index 0000000..61abe85
--- /dev/null
+++ b/pintos-progos/tests/threads/Rubric.alarm
@@ -0,0 +1,8 @@
1Functionality and robustness of alarm clock:
24 alarm-single
34 alarm-multiple
44 alarm-simultaneous
54 alarm-priority
6
71 alarm-zero
81 alarm-negative
diff --git a/pintos-progos/tests/threads/Rubric.mlfqs b/pintos-progos/tests/threads/Rubric.mlfqs
new file mode 100644
index 0000000..f260091
--- /dev/null
+++ b/pintos-progos/tests/threads/Rubric.mlfqs
@@ -0,0 +1,14 @@
1Functionality of advanced scheduler:
25 mlfqs-load-1
35 mlfqs-load-60
43 mlfqs-load-avg
5
65 mlfqs-recent-1
7
85 mlfqs-fair-2
93 mlfqs-fair-20
10
114 mlfqs-nice-2
122 mlfqs-nice-10
13
145 mlfqs-block
diff --git a/pintos-progos/tests/threads/Rubric.priority b/pintos-progos/tests/threads/Rubric.priority
new file mode 100644
index 0000000..652bc99
--- /dev/null
+++ b/pintos-progos/tests/threads/Rubric.priority
@@ -0,0 +1,15 @@
1Functionality of priority scheduler:
23 priority-change
33 priority-preempt
4
53 priority-fifo
63 priority-sema
73 priority-condvar
8
93 priority-donate-one
103 priority-donate-multiple
113 priority-donate-multiple2
123 priority-donate-nest
135 priority-donate-chain
143 priority-donate-sema
153 priority-donate-lower
diff --git a/pintos-progos/tests/threads/alarm-multiple.ck b/pintos-progos/tests/threads/alarm-multiple.ck
new file mode 100644
index 0000000..fd83bcd
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-multiple.ck
@@ -0,0 +1,4 @@
1# -*- perl -*-
2use tests::tests;
3use tests::threads::alarm;
4check_alarm (7);
diff --git a/pintos-progos/tests/threads/alarm-negative.c b/pintos-progos/tests/threads/alarm-negative.c
new file mode 100644
index 0000000..aec52cf
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-negative.c
@@ -0,0 +1,15 @@
1/* Tests timer_sleep(-100). Only requirement is that it not crash. */
2
3#include <stdio.h>
4#include "tests/threads/tests.h"
5#include "threads/malloc.h"
6#include "threads/synch.h"
7#include "threads/thread.h"
8#include "devices/timer.h"
9
10void
11test_alarm_negative (void)
12{
13 timer_sleep (-100);
14 pass ();
15}
diff --git a/pintos-progos/tests/threads/alarm-negative.ck b/pintos-progos/tests/threads/alarm-negative.ck
new file mode 100644
index 0000000..0d2bab0
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-negative.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-negative) begin
7(alarm-negative) PASS
8(alarm-negative) end
9EOF
10pass;
diff --git a/pintos-progos/tests/threads/alarm-priority.c b/pintos-progos/tests/threads/alarm-priority.c
new file mode 100644
index 0000000..2288ff6
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-priority.c
@@ -0,0 +1,58 @@
1/* Checks that when the alarm clock wakes up threads, the
2 higher-priority threads run first. */
3
4#include <stdio.h>
5#include "tests/threads/tests.h"
6#include "threads/init.h"
7#include "threads/malloc.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10#include "devices/timer.h"
11
12static thread_func alarm_priority_thread;
13static int64_t wake_time;
14static struct semaphore wait_sema;
15
16void
17test_alarm_priority (void)
18{
19 int i;
20
21 /* This test does not work with the MLFQS. */
22 ASSERT (!thread_mlfqs);
23
24 wake_time = timer_ticks () + 5 * TIMER_FREQ;
25 sema_init (&wait_sema, 0);
26
27 for (i = 0; i < 10; i++)
28 {
29 int priority = PRI_DEFAULT - (i + 5) % 10 - 1;
30 char name[16];
31 snprintf (name, sizeof name, "priority %d", priority);
32 thread_create (name, priority, alarm_priority_thread, NULL);
33 }
34
35 thread_set_priority (PRI_MIN);
36
37 for (i = 0; i < 10; i++)
38 sema_down (&wait_sema);
39}
40
41static void
42alarm_priority_thread (void *aux UNUSED)
43{
44 /* Busy-wait until the current time changes. */
45 int64_t start_time = timer_ticks ();
46 while (timer_elapsed (start_time) == 0)
47 continue;
48
49 /* Now we know we're at the very beginning of a timer tick, so
50 we can call timer_sleep() without worrying about races
51 between checking the time and a timer interrupt. */
52 timer_sleep (wake_time - timer_ticks ());
53
54 /* Print a message on wake-up. */
55 msg ("Thread %s woke up.", thread_name ());
56
57 sema_up (&wait_sema);
58}
diff --git a/pintos-progos/tests/threads/alarm-priority.ck b/pintos-progos/tests/threads/alarm-priority.ck
new file mode 100644
index 0000000..b57c78b
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-priority.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-priority) begin
7(alarm-priority) Thread priority 30 woke up.
8(alarm-priority) Thread priority 29 woke up.
9(alarm-priority) Thread priority 28 woke up.
10(alarm-priority) Thread priority 27 woke up.
11(alarm-priority) Thread priority 26 woke up.
12(alarm-priority) Thread priority 25 woke up.
13(alarm-priority) Thread priority 24 woke up.
14(alarm-priority) Thread priority 23 woke up.
15(alarm-priority) Thread priority 22 woke up.
16(alarm-priority) Thread priority 21 woke up.
17(alarm-priority) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.c b/pintos-progos/tests/threads/alarm-simultaneous.c
new file mode 100644
index 0000000..844eea4
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-simultaneous.c
@@ -0,0 +1,94 @@
1/* Creates N threads, each of which sleeps a different, fixed
2 duration, M times. Records the wake-up order and verifies
3 that it is valid. */
4
5#include <stdio.h>
6#include "tests/threads/tests.h"
7#include "threads/init.h"
8#include "threads/malloc.h"
9#include "threads/synch.h"
10#include "threads/thread.h"
11#include "devices/timer.h"
12
13static void test_sleep (int thread_cnt, int iterations);
14
15void
16test_alarm_simultaneous (void)
17{
18 test_sleep (3, 5);
19}
20
21/* Information about the test. */
22struct sleep_test
23 {
24 int64_t start; /* Current time at start of test. */
25 int iterations; /* Number of iterations per thread. */
26 int *output_pos; /* Current position in output buffer. */
27 };
28
29static void sleeper (void *);
30
31/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
32static void
33test_sleep (int thread_cnt, int iterations)
34{
35 struct sleep_test test;
36 int *output;
37 int i;
38
39 /* This test does not work with the MLFQS. */
40 ASSERT (!thread_mlfqs);
41
42 msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
43 msg ("Each thread sleeps 10 ticks each time.");
44 msg ("Within an iteration, all threads should wake up on the same tick.");
45
46 /* Allocate memory. */
47 output = malloc (sizeof *output * iterations * thread_cnt * 2);
48 if (output == NULL)
49 PANIC ("couldn't allocate memory for test");
50
51 /* Initialize test. */
52 test.start = timer_ticks () + 100;
53 test.iterations = iterations;
54 test.output_pos = output;
55
56 /* Start threads. */
57 ASSERT (output != NULL);
58 for (i = 0; i < thread_cnt; i++)
59 {
60 char name[16];
61 snprintf (name, sizeof name, "thread %d", i);
62 thread_create (name, PRI_DEFAULT, sleeper, &test);
63 }
64
65 /* Wait long enough for all the threads to finish. */
66 timer_sleep (100 + iterations * 10 + 100);
67
68 /* Print completion order. */
69 msg ("iteration 0, thread 0: woke up after %d ticks", output[0]);
70 for (i = 1; i < test.output_pos - output; i++)
71 msg ("iteration %d, thread %d: woke up %d ticks later",
72 i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]);
73
74 free (output);
75}
76
77/* Sleeper thread. */
78static void
79sleeper (void *test_)
80{
81 struct sleep_test *test = test_;
82 int i;
83
84 /* Make sure we're at the beginning of a timer tick. */
85 timer_sleep (1);
86
87 for (i = 1; i <= test->iterations; i++)
88 {
89 int64_t sleep_until = test->start + i * 10;
90 timer_sleep (sleep_until - timer_ticks ());
91 *test->output_pos++ = timer_ticks () - test->start;
92 thread_yield ();
93 }
94}
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.ck b/pintos-progos/tests/threads/alarm-simultaneous.ck
new file mode 100644
index 0000000..406b8b0
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-simultaneous.ck
@@ -0,0 +1,27 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-simultaneous) begin
7(alarm-simultaneous) Creating 3 threads to sleep 5 times each.
8(alarm-simultaneous) Each thread sleeps 10 ticks each time.
9(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick.
10(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks
11(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later
12(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later
13(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later
14(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later
15(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later
16(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later
17(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later
18(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later
19(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later
20(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later
21(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later
22(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later
23(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later
24(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later
25(alarm-simultaneous) end
26EOF
27pass;
diff --git a/pintos-progos/tests/threads/alarm-single.ck b/pintos-progos/tests/threads/alarm-single.ck
new file mode 100644
index 0000000..31215df
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-single.ck
@@ -0,0 +1,4 @@
1# -*- perl -*-
2use tests::tests;
3use tests::threads::alarm;
4check_alarm (1);
diff --git a/pintos-progos/tests/threads/alarm-wait.c b/pintos-progos/tests/threads/alarm-wait.c
new file mode 100644
index 0000000..37d3afc
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-wait.c
@@ -0,0 +1,152 @@
1/* Creates N threads, each of which sleeps a different, fixed
2 duration, M times. Records the wake-up order and verifies
3 that it is valid. */
4
5#include <stdio.h>
6#include "tests/threads/tests.h"
7#include "threads/init.h"
8#include "threads/malloc.h"
9#include "threads/synch.h"
10#include "threads/thread.h"
11#include "devices/timer.h"
12
13static void test_sleep (int thread_cnt, int iterations);
14
15void
16test_alarm_single (void)
17{
18 test_sleep (5, 1);
19}
20
21void
22test_alarm_multiple (void)
23{
24 test_sleep (5, 7);
25}
26
27/* Information about the test. */
28struct sleep_test
29 {
30 int64_t start; /* Current time at start of test. */
31 int iterations; /* Number of iterations per thread. */
32
33 /* Output. */
34 struct lock output_lock; /* Lock protecting output buffer. */
35 int *output_pos; /* Current position in output buffer. */
36 };
37
38/* Information about an individual thread in the test. */
39struct sleep_thread
40 {
41 struct sleep_test *test; /* Info shared between all threads. */
42 int id; /* Sleeper ID. */
43 int duration; /* Number of ticks to sleep. */
44 int iterations; /* Iterations counted so far. */
45 };
46
47static void sleeper (void *);
48
49/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
50static void
51test_sleep (int thread_cnt, int iterations)
52{
53 struct sleep_test test;
54 struct sleep_thread *threads;
55 int *output, *op;
56 int product;
57 int i;
58
59 /* This test does not work with the MLFQS. */
60 ASSERT (!thread_mlfqs);
61
62 msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
63 msg ("Thread 0 sleeps 10 ticks each time,");
64 msg ("thread 1 sleeps 20 ticks each time, and so on.");
65 msg ("If successful, product of iteration count and");
66 msg ("sleep duration will appear in nondescending order.");
67
68 /* Allocate memory. */
69 threads = malloc (sizeof *threads * thread_cnt);
70 output = malloc (sizeof *output * iterations * thread_cnt * 2);
71 if (threads == NULL || output == NULL)
72 PANIC ("couldn't allocate memory for test");
73
74 /* Initialize test. */
75 test.start = timer_ticks () + 100;
76 test.iterations = iterations;
77 lock_init (&test.output_lock);
78 test.output_pos = output;
79
80 /* Start threads. */
81 ASSERT (output != NULL);
82 for (i = 0; i < thread_cnt; i++)
83 {
84 struct sleep_thread *t = threads + i;
85 char name[16];
86
87 t->test = &test;
88 t->id = i;
89 t->duration = (i + 1) * 10;
90 t->iterations = 0;
91
92 snprintf (name, sizeof name, "thread %d", i);
93 thread_create (name, PRI_DEFAULT, sleeper, t);
94 }
95
96 /* Wait long enough for all the threads to finish. */
97 timer_sleep (100 + thread_cnt * iterations * 10 + 100);
98
99 /* Acquire the output lock in case some rogue thread is still
100 running. */
101 lock_acquire (&test.output_lock);
102
103 /* Print completion order. */
104 product = 0;
105 for (op = output; op < test.output_pos; op++)
106 {
107 struct sleep_thread *t;
108 int new_prod;
109
110 ASSERT (*op >= 0 && *op < thread_cnt);
111 t = threads + *op;
112
113 new_prod = ++t->iterations * t->duration;
114
115 msg ("thread %d: duration=%d, iteration=%d, product=%d",
116 t->id, t->duration, t->iterations, new_prod);
117
118 if (new_prod >= product)
119 product = new_prod;
120 else
121 fail ("thread %d woke up out of order (%d > %d)!",
122 t->id, product, new_prod);
123 }
124
125 /* Verify that we had the proper number of wakeups. */
126 for (i = 0; i < thread_cnt; i++)
127 if (threads[i].iterations != iterations)
128 fail ("thread %d woke up %d times instead of %d",
129 i, threads[i].iterations, iterations);
130
131 lock_release (&test.output_lock);
132 free (output);
133 free (threads);
134}
135
136/* Sleeper thread. */
137static void
138sleeper (void *t_)
139{
140 struct sleep_thread *t = t_;
141 struct sleep_test *test = t->test;
142 int i;
143
144 for (i = 1; i <= test->iterations; i++)
145 {
146 int64_t sleep_until = test->start + i * t->duration;
147 timer_sleep (sleep_until - timer_ticks ());
148 lock_acquire (&test->output_lock);
149 *test->output_pos++ = t->id;
150 lock_release (&test->output_lock);
151 }
152}
diff --git a/pintos-progos/tests/threads/alarm-zero.c b/pintos-progos/tests/threads/alarm-zero.c
new file mode 100644
index 0000000..c8a3ee2
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-zero.c
@@ -0,0 +1,15 @@
1/* Tests timer_sleep(0), which should return immediately. */
2
3#include <stdio.h>
4#include "tests/threads/tests.h"
5#include "threads/malloc.h"
6#include "threads/synch.h"
7#include "threads/thread.h"
8#include "devices/timer.h"
9
10void
11test_alarm_zero (void)
12{
13 timer_sleep (0);
14 pass ();
15}
diff --git a/pintos-progos/tests/threads/alarm-zero.ck b/pintos-progos/tests/threads/alarm-zero.ck
new file mode 100644
index 0000000..a6b1a3c
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-zero.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-zero) begin
7(alarm-zero) PASS
8(alarm-zero) end
9EOF
10pass;
diff --git a/pintos-progos/tests/threads/alarm.pm b/pintos-progos/tests/threads/alarm.pm
new file mode 100644
index 0000000..84b3b7f
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm.pm
@@ -0,0 +1,32 @@
1sub check_alarm {
2 my ($iterations) = @_;
3 our ($test);
4
5 @output = read_text_file ("$test.output");
6 common_checks ("run", @output);
7
8 my (@products);
9 for (my ($i) = 0; $i < $iterations; $i++) {
10 for (my ($t) = 0; $t < 5; $t++) {
11 push (@products, ($i + 1) * ($t + 1) * 10);
12 }
13 }
14 @products = sort {$a <=> $b} @products;
15
16 local ($_);
17 foreach (@output) {
18 fail $_ if /out of order/i;
19
20 my ($p) = /product=(\d+)$/;
21 next if !defined $p;
22
23 my ($q) = shift (@products);
24 fail "Too many wakeups.\n" if !defined $q;
25 fail "Out of order wakeups ($p vs. $q).\n" if $p != $q; # FIXME
26 }
27 fail scalar (@products) . " fewer wakeups than expected.\n"
28 if @products != 0;
29 pass;
30}
31
321;
diff --git a/pintos-progos/tests/threads/mlfqs-block.c b/pintos-progos/tests/threads/mlfqs-block.c
new file mode 100644
index 0000000..6d4992d
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-block.c
@@ -0,0 +1,64 @@
1/* Checks that recent_cpu and priorities are updated for blocked
2 threads.
3
4 The main thread sleeps for 25 seconds, spins for 5 seconds,
5 then releases a lock. The "block" thread spins for 20 seconds
6 then attempts to acquire the lock, which will block for 10
7 seconds (until the main thread releases it). If recent_cpu
8 decays properly while the "block" thread sleeps, then the
9 block thread should be immediately scheduled when the main
10 thread releases the lock. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/malloc.h"
16#include "threads/synch.h"
17#include "threads/thread.h"
18#include "devices/timer.h"
19
20static void block_thread (void *lock_);
21
22void
23test_mlfqs_block (void)
24{
25 int64_t start_time;
26 struct lock lock;
27
28 ASSERT (thread_mlfqs);
29
30 msg ("Main thread acquiring lock.");
31 lock_init (&lock);
32 lock_acquire (&lock);
33
34 msg ("Main thread creating block thread, sleeping 25 seconds...");
35 thread_create ("block", PRI_DEFAULT, block_thread, &lock);
36 timer_sleep (25 * TIMER_FREQ);
37
38 msg ("Main thread spinning for 5 seconds...");
39 start_time = timer_ticks ();
40 while (timer_elapsed (start_time) < 5 * TIMER_FREQ)
41 continue;
42
43 msg ("Main thread releasing lock.");
44 lock_release (&lock);
45
46 msg ("Block thread should have already acquired lock.");
47}
48
49static void
50block_thread (void *lock_)
51{
52 struct lock *lock = lock_;
53 int64_t start_time;
54
55 msg ("Block thread spinning for 20 seconds...");
56 start_time = timer_ticks ();
57 while (timer_elapsed (start_time) < 20 * TIMER_FREQ)
58 continue;
59
60 msg ("Block thread acquiring lock...");
61 lock_acquire (lock);
62
63 msg ("...got it.");
64}
diff --git a/pintos-progos/tests/threads/mlfqs-block.ck b/pintos-progos/tests/threads/mlfqs-block.ck
new file mode 100644
index 0000000..8833a3a
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-block.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(mlfqs-block) begin
7(mlfqs-block) Main thread acquiring lock.
8(mlfqs-block) Main thread creating block thread, sleeping 25 seconds...
9(mlfqs-block) Block thread spinning for 20 seconds...
10(mlfqs-block) Block thread acquiring lock...
11(mlfqs-block) Main thread spinning for 5 seconds...
12(mlfqs-block) Main thread releasing lock.
13(mlfqs-block) ...got it.
14(mlfqs-block) Block thread should have already acquired lock.
15(mlfqs-block) end
16EOF
17pass;
diff --git a/pintos-progos/tests/threads/mlfqs-fair-2.ck b/pintos-progos/tests/threads/mlfqs-fair-2.ck
new file mode 100644
index 0000000..5b19ff1
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-fair-2.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([0, 0], 50);
diff --git a/pintos-progos/tests/threads/mlfqs-fair-20.ck b/pintos-progos/tests/threads/mlfqs-fair-20.ck
new file mode 100644
index 0000000..bb4d051
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-fair-20.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([(0) x 20], 20);
diff --git a/pintos-progos/tests/threads/mlfqs-fair.c b/pintos-progos/tests/threads/mlfqs-fair.c
new file mode 100644
index 0000000..3b1bea5
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-fair.c
@@ -0,0 +1,124 @@
1/* Measures the correctness of the "nice" implementation.
2
3 The "fair" tests run either 2 or 20 threads all niced to 0.
4 The threads should all receive approximately the same number
5 of ticks. Each test runs for 30 seconds, so the ticks should
6 also sum to approximately 30 * 100 == 3000 ticks.
7
8 The mlfqs-nice-2 test runs 2 threads, one with nice 0, the
9 other with nice 5, which should receive 1,904 and 1,096 ticks,
10 respectively, over 30 seconds.
11
12 The mlfqs-nice-10 test runs 10 threads with nice 0 through 9.
13 They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40,
14 and 8 ticks, respectively, over 30 seconds.
15
16 (The above are computed via simulation in mlfqs.pm.) */
17
18#include <stdio.h>
19#include <inttypes.h>
20#include "tests/threads/tests.h"
21#include "threads/init.h"
22#include "threads/malloc.h"
23#include "threads/palloc.h"
24#include "threads/synch.h"
25#include "threads/thread.h"
26#include "devices/timer.h"
27
28static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step);
29
30void
31test_mlfqs_fair_2 (void)
32{
33 test_mlfqs_fair (2, 0, 0);
34}
35
36void
37test_mlfqs_fair_20 (void)
38{
39 test_mlfqs_fair (20, 0, 0);
40}
41
42void
43test_mlfqs_nice_2 (void)
44{
45 test_mlfqs_fair (2, 0, 5);
46}
47
48void
49test_mlfqs_nice_10 (void)
50{
51 test_mlfqs_fair (10, 0, 1);
52}
53
54#define MAX_THREAD_CNT 20
55
56struct thread_info
57 {
58 int64_t start_time;
59 int tick_count;
60 int nice;
61 };
62
63static void load_thread (void *aux);
64
65static void
66test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step)
67{
68 struct thread_info info[MAX_THREAD_CNT];
69 int64_t start_time;
70 int nice;
71 int i;
72
73 ASSERT (thread_mlfqs);
74 ASSERT (thread_cnt <= MAX_THREAD_CNT);
75 ASSERT (nice_min >= -10);
76 ASSERT (nice_step >= 0);
77 ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20);
78
79 thread_set_nice (-20);
80
81 start_time = timer_ticks ();
82 msg ("Starting %d threads...", thread_cnt);
83 nice = nice_min;
84 for (i = 0; i < thread_cnt; i++)
85 {
86 struct thread_info *ti = &info[i];
87 char name[16];
88
89 ti->start_time = start_time;
90 ti->tick_count = 0;
91 ti->nice = nice;
92
93 snprintf(name, sizeof name, "load %d", i);
94 thread_create (name, PRI_DEFAULT, load_thread, ti);
95
96 nice += nice_step;
97 }
98 msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time));
99
100 msg ("Sleeping 40 seconds to let threads run, please wait...");
101 timer_sleep (40 * TIMER_FREQ);
102
103 for (i = 0; i < thread_cnt; i++)
104 msg ("Thread %d received %d ticks.", i, info[i].tick_count);
105}
106
107static void
108load_thread (void *ti_)
109{
110 struct thread_info *ti = ti_;
111 int64_t sleep_time = 5 * TIMER_FREQ;
112 int64_t spin_time = sleep_time + 30 * TIMER_FREQ;
113 int64_t last_time = 0;
114
115 thread_set_nice (ti->nice);
116 timer_sleep (sleep_time - timer_elapsed (ti->start_time));
117 while (timer_elapsed (ti->start_time) < spin_time)
118 {
119 int64_t cur_time = timer_ticks ();
120 if (cur_time != last_time)
121 ti->tick_count++;
122 last_time = cur_time;
123 }
124}
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.c b/pintos-progos/tests/threads/mlfqs-load-1.c
new file mode 100644
index 0000000..a39eea2
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-1.c
@@ -0,0 +1,60 @@
1/* Verifies that a single busy thread raises the load average to
2 0.5 in 38 to 45 seconds. The expected time is 42 seconds, as
3 you can verify:
4 perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"'
5
6 Then, verifies that 10 seconds of inactivity drop the load
7 average back below 0.5 again. */
8
9#include <stdio.h>
10#include "tests/threads/tests.h"
11#include "threads/init.h"
12#include "threads/malloc.h"
13#include "threads/synch.h"
14#include "threads/thread.h"
15#include "devices/timer.h"
16
17void
18test_mlfqs_load_1 (void)
19{
20 int64_t start_time;
21 int elapsed;
22 int load_avg;
23
24 ASSERT (thread_mlfqs);
25
26 msg ("spinning for up to 45 seconds, please wait...");
27
28 start_time = timer_ticks ();
29 for (;;)
30 {
31 load_avg = thread_get_load_avg ();
32 ASSERT (load_avg >= 0);
33 elapsed = timer_elapsed (start_time) / TIMER_FREQ;
34 if (load_avg > 100)
35 fail ("load average is %d.%02d "
36 "but should be between 0 and 1 (after %d seconds)",
37 load_avg / 100, load_avg % 100, elapsed);
38 else if (load_avg > 50)
39 break;
40 else if (elapsed > 45)
41 fail ("load average stayed below 0.5 for more than 45 seconds");
42 }
43
44 if (elapsed < 38)
45 fail ("load average took only %d seconds to rise above 0.5", elapsed);
46 msg ("load average rose to 0.5 after %d seconds", elapsed);
47
48 msg ("sleeping for another 10 seconds, please wait...");
49 timer_sleep (TIMER_FREQ * 10);
50
51 load_avg = thread_get_load_avg ();
52 if (load_avg < 0)
53 fail ("load average fell below 0");
54 if (load_avg > 50)
55 fail ("load average stayed above 0.5 for more than 10 seconds");
56 msg ("load average fell back below 0.5 (to %d.%02d)",
57 load_avg / 100, load_avg % 100);
58
59 pass ();
60}
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.ck b/pintos-progos/tests/threads/mlfqs-load-1.ck
new file mode 100644
index 0000000..faf0ffa
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-1.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5
6our ($test);
7my (@output) = read_text_file ("$test.output");
8
9common_checks ("run", @output);
10
11@output = get_core_output ("run", @output);
12fail "missing PASS in output"
13 unless grep ($_ eq '(mlfqs-load-1) PASS', @output);
14
15pass;
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.c b/pintos-progos/tests/threads/mlfqs-load-60.c
new file mode 100644
index 0000000..b6a3eb6
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-60.c
@@ -0,0 +1,155 @@
1/* Starts 60 threads that each sleep for 10 seconds, then spin in
2 a tight loop for 60 seconds, and sleep for another 60 seconds.
3 Every 2 seconds after the initial sleep, the main thread
4 prints the load average.
5
6 The expected output is this (some margin of error is allowed):
7
8 After 0 seconds, load average=1.00.
9 After 2 seconds, load average=2.95.
10 After 4 seconds, load average=4.84.
11 After 6 seconds, load average=6.66.
12 After 8 seconds, load average=8.42.
13 After 10 seconds, load average=10.13.
14 After 12 seconds, load average=11.78.
15 After 14 seconds, load average=13.37.
16 After 16 seconds, load average=14.91.
17 After 18 seconds, load average=16.40.
18 After 20 seconds, load average=17.84.
19 After 22 seconds, load average=19.24.
20 After 24 seconds, load average=20.58.
21 After 26 seconds, load average=21.89.
22 After 28 seconds, load average=23.15.
23 After 30 seconds, load average=24.37.
24 After 32 seconds, load average=25.54.
25 After 34 seconds, load average=26.68.
26 After 36 seconds, load average=27.78.
27 After 38 seconds, load average=28.85.
28 After 40 seconds, load average=29.88.
29 After 42 seconds, load average=30.87.
30 After 44 seconds, load average=31.84.
31 After 46 seconds, load average=32.77.
32 After 48 seconds, load average=33.67.
33 After 50 seconds, load average=34.54.
34 After 52 seconds, load average=35.38.
35 After 54 seconds, load average=36.19.
36 After 56 seconds, load average=36.98.
37 After 58 seconds, load average=37.74.
38 After 60 seconds, load average=37.48.
39 After 62 seconds, load average=36.24.
40 After 64 seconds, load average=35.04.
41 After 66 seconds, load average=33.88.
42 After 68 seconds, load average=32.76.
43 After 70 seconds, load average=31.68.
44 After 72 seconds, load average=30.63.
45 After 74 seconds, load average=29.62.
46 After 76 seconds, load average=28.64.
47 After 78 seconds, load average=27.69.
48 After 80 seconds, load average=26.78.
49 After 82 seconds, load average=25.89.
50 After 84 seconds, load average=25.04.
51 After 86 seconds, load average=24.21.
52 After 88 seconds, load average=23.41.
53 After 90 seconds, load average=22.64.
54 After 92 seconds, load average=21.89.
55 After 94 seconds, load average=21.16.
56 After 96 seconds, load average=20.46.
57 After 98 seconds, load average=19.79.
58 After 100 seconds, load average=19.13.
59 After 102 seconds, load average=18.50.
60 After 104 seconds, load average=17.89.
61 After 106 seconds, load average=17.30.
62 After 108 seconds, load average=16.73.
63 After 110 seconds, load average=16.17.
64 After 112 seconds, load average=15.64.
65 After 114 seconds, load average=15.12.
66 After 116 seconds, load average=14.62.
67 After 118 seconds, load average=14.14.
68 After 120 seconds, load average=13.67.
69 After 122 seconds, load average=13.22.
70 After 124 seconds, load average=12.78.
71 After 126 seconds, load average=12.36.
72 After 128 seconds, load average=11.95.
73 After 130 seconds, load average=11.56.
74 After 132 seconds, load average=11.17.
75 After 134 seconds, load average=10.80.
76 After 136 seconds, load average=10.45.
77 After 138 seconds, load average=10.10.
78 After 140 seconds, load average=9.77.
79 After 142 seconds, load average=9.45.
80 After 144 seconds, load average=9.13.
81 After 146 seconds, load average=8.83.
82 After 148 seconds, load average=8.54.
83 After 150 seconds, load average=8.26.
84 After 152 seconds, load average=7.98.
85 After 154 seconds, load average=7.72.
86 After 156 seconds, load average=7.47.
87 After 158 seconds, load average=7.22.
88 After 160 seconds, load average=6.98.
89 After 162 seconds, load average=6.75.
90 After 164 seconds, load average=6.53.
91 After 166 seconds, load average=6.31.
92 After 168 seconds, load average=6.10.
93 After 170 seconds, load average=5.90.
94 After 172 seconds, load average=5.70.
95 After 174 seconds, load average=5.52.
96 After 176 seconds, load average=5.33.
97 After 178 seconds, load average=5.16.
98*/
99
100#include <stdio.h>
101#include "tests/threads/tests.h"
102#include "threads/init.h"
103#include "threads/malloc.h"
104#include "threads/synch.h"
105#include "threads/thread.h"
106#include "devices/timer.h"
107
108static int64_t start_time;
109
110static void load_thread (void *aux);
111
112#define THREAD_CNT 60
113
114void
115test_mlfqs_load_60 (void)
116{
117 int i;
118
119 ASSERT (thread_mlfqs);
120
121 start_time = timer_ticks ();
122 msg ("Starting %d niced load threads...", THREAD_CNT);
123 for (i = 0; i < THREAD_CNT; i++)
124 {
125 char name[16];
126 snprintf(name, sizeof name, "load %d", i);
127 thread_create (name, PRI_DEFAULT, load_thread, NULL);
128 }
129 msg ("Starting threads took %d seconds.",
130 timer_elapsed (start_time) / TIMER_FREQ);
131
132 for (i = 0; i < 90; i++)
133 {
134 int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
135 int load_avg;
136 timer_sleep (sleep_until - timer_ticks ());
137 load_avg = thread_get_load_avg ();
138 msg ("After %d seconds, load average=%d.%02d.",
139 i * 2, load_avg / 100, load_avg % 100);
140 }
141}
142
143static void
144load_thread (void *aux UNUSED)
145{
146 int64_t sleep_time = 10 * TIMER_FREQ;
147 int64_t spin_time = sleep_time + 60 * TIMER_FREQ;
148 int64_t exit_time = spin_time + 60 * TIMER_FREQ;
149
150 thread_set_nice (20);
151 timer_sleep (sleep_time - timer_elapsed (start_time));
152 while (timer_elapsed (start_time) < spin_time)
153 continue;
154 timer_sleep (exit_time - timer_elapsed (start_time));
155}
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.ck b/pintos-progos/tests/threads/mlfqs-load-60.ck
new file mode 100644
index 0000000..cb69220
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-60.ck
@@ -0,0 +1,36 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7our ($test);
8
9my (@output) = read_text_file ("$test.output");
10common_checks ("run", @output);
11@output = get_core_output ("run", @output);
12
13# Get actual values.
14local ($_);
15my (@actual);
16foreach (@output) {
17 my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
18 or next;
19 $actual[$t] = $load_avg;
20}
21
22# Calculate expected values.
23my ($load_avg) = 0;
24my ($recent) = 0;
25my (@expected);
26for (my ($t) = 0; $t < 180; $t++) {
27 my ($ready) = $t < 60 ? 60 : 0;
28 $load_avg = (59/60) * $load_avg + (1/60) * $ready;
29 $expected[$t] = $load_avg;
30}
31
32mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2],
33 "Some load average values were missing or "
34 . "differed from those expected "
35 . "by more than 3.5.");
36pass;
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.c b/pintos-progos/tests/threads/mlfqs-load-avg.c
new file mode 100644
index 0000000..50e83e2
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-avg.c
@@ -0,0 +1,167 @@
1/* Starts 60 threads numbered 0 through 59. Thread #i sleeps for
2 (10+i) seconds, then spins in a loop for 60 seconds, then
3 sleeps until a total of 120 seconds have passed. Every 2
4 seconds, starting 10 seconds in, the main thread prints the
5 load average.
6
7 The expected output is listed below. Some margin of error is
8 allowed.
9
10 If your implementation fails this test but passes most other
11 tests, then consider whether you are doing too much work in
12 the timer interrupt. If the timer interrupt handler takes too
13 long, then the test's main thread will not have enough time to
14 do its own work (printing a message) and go back to sleep
15 before the next tick arrives. Then the main thread will be
16 ready, instead of sleeping, when the tick arrives,
17 artificially driving up the load average.
18
19 After 0 seconds, load average=0.00.
20 After 2 seconds, load average=0.05.
21 After 4 seconds, load average=0.16.
22 After 6 seconds, load average=0.34.
23 After 8 seconds, load average=0.58.
24 After 10 seconds, load average=0.87.
25 After 12 seconds, load average=1.22.
26 After 14 seconds, load average=1.63.
27 After 16 seconds, load average=2.09.
28 After 18 seconds, load average=2.60.
29 After 20 seconds, load average=3.16.
30 After 22 seconds, load average=3.76.
31 After 24 seconds, load average=4.42.
32 After 26 seconds, load average=5.11.
33 After 28 seconds, load average=5.85.
34 After 30 seconds, load average=6.63.
35 After 32 seconds, load average=7.46.
36 After 34 seconds, load average=8.32.
37 After 36 seconds, load average=9.22.
38 After 38 seconds, load average=10.15.
39 After 40 seconds, load average=11.12.
40 After 42 seconds, load average=12.13.
41 After 44 seconds, load average=13.16.
42 After 46 seconds, load average=14.23.
43 After 48 seconds, load average=15.33.
44 After 50 seconds, load average=16.46.
45 After 52 seconds, load average=17.62.
46 After 54 seconds, load average=18.81.
47 After 56 seconds, load average=20.02.
48 After 58 seconds, load average=21.26.
49 After 60 seconds, load average=22.52.
50 After 62 seconds, load average=23.71.
51 After 64 seconds, load average=24.80.
52 After 66 seconds, load average=25.78.
53 After 68 seconds, load average=26.66.
54 After 70 seconds, load average=27.45.
55 After 72 seconds, load average=28.14.
56 After 74 seconds, load average=28.75.
57 After 76 seconds, load average=29.27.
58 After 78 seconds, load average=29.71.
59 After 80 seconds, load average=30.06.
60 After 82 seconds, load average=30.34.
61 After 84 seconds, load average=30.55.
62 After 86 seconds, load average=30.68.
63 After 88 seconds, load average=30.74.
64 After 90 seconds, load average=30.73.
65 After 92 seconds, load average=30.66.
66 After 94 seconds, load average=30.52.
67 After 96 seconds, load average=30.32.
68 After 98 seconds, load average=30.06.
69 After 100 seconds, load average=29.74.
70 After 102 seconds, load average=29.37.
71 After 104 seconds, load average=28.95.
72 After 106 seconds, load average=28.47.
73 After 108 seconds, load average=27.94.
74 After 110 seconds, load average=27.36.
75 After 112 seconds, load average=26.74.
76 After 114 seconds, load average=26.07.
77 After 116 seconds, load average=25.36.
78 After 118 seconds, load average=24.60.
79 After 120 seconds, load average=23.81.
80 After 122 seconds, load average=23.02.
81 After 124 seconds, load average=22.26.
82 After 126 seconds, load average=21.52.
83 After 128 seconds, load average=20.81.
84 After 130 seconds, load average=20.12.
85 After 132 seconds, load average=19.46.
86 After 134 seconds, load average=18.81.
87 After 136 seconds, load average=18.19.
88 After 138 seconds, load average=17.59.
89 After 140 seconds, load average=17.01.
90 After 142 seconds, load average=16.45.
91 After 144 seconds, load average=15.90.
92 After 146 seconds, load average=15.38.
93 After 148 seconds, load average=14.87.
94 After 150 seconds, load average=14.38.
95 After 152 seconds, load average=13.90.
96 After 154 seconds, load average=13.44.
97 After 156 seconds, load average=13.00.
98 After 158 seconds, load average=12.57.
99 After 160 seconds, load average=12.15.
100 After 162 seconds, load average=11.75.
101 After 164 seconds, load average=11.36.
102 After 166 seconds, load average=10.99.
103 After 168 seconds, load average=10.62.
104 After 170 seconds, load average=10.27.
105 After 172 seconds, load average=9.93.
106 After 174 seconds, load average=9.61.
107 After 176 seconds, load average=9.29.
108 After 178 seconds, load average=8.98.
109*/
110
111#include <stdio.h>
112#include "tests/threads/tests.h"
113#include "threads/init.h"
114#include "threads/malloc.h"
115#include "threads/synch.h"
116#include "threads/thread.h"
117#include "devices/timer.h"
118
119static int64_t start_time;
120
121static void load_thread (void *seq_no);
122
123#define THREAD_CNT 60
124
125void
126test_mlfqs_load_avg (void)
127{
128 int i;
129
130 ASSERT (thread_mlfqs);
131
132 start_time = timer_ticks ();
133 msg ("Starting %d load threads...", THREAD_CNT);
134 for (i = 0; i < THREAD_CNT; i++)
135 {
136 char name[16];
137 snprintf(name, sizeof name, "load %d", i);
138 thread_create (name, PRI_DEFAULT, load_thread, (void *) i);
139 }
140 msg ("Starting threads took %d seconds.",
141 timer_elapsed (start_time) / TIMER_FREQ);
142 thread_set_nice (-20);
143
144 for (i = 0; i < 90; i++)
145 {
146 int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
147 int load_avg;
148 timer_sleep (sleep_until - timer_ticks ());
149 load_avg = thread_get_load_avg ();
150 msg ("After %d seconds, load average=%d.%02d.",
151 i * 2, load_avg / 100, load_avg % 100);
152 }
153}
154
155static void
156load_thread (void *seq_no_)
157{
158 int seq_no = (int) seq_no_;
159 int sleep_time = TIMER_FREQ * (10 + seq_no);
160 int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT;
161 int exit_time = TIMER_FREQ * (THREAD_CNT * 2);
162
163 timer_sleep (sleep_time - timer_elapsed (start_time));
164 while (timer_elapsed (start_time) < spin_time)
165 continue;
166 timer_sleep (exit_time - timer_elapsed (start_time));
167}
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.ck b/pintos-progos/tests/threads/mlfqs-load-avg.ck
new file mode 100644
index 0000000..2254d05
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-avg.ck
@@ -0,0 +1,36 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7our ($test);
8my (@output) = read_text_file ("$test.output");
9
10common_checks ("run", @output);
11@output = get_core_output ("run", @output);
12
13# Get actual values.
14local ($_);
15my (@actual);
16foreach (@output) {
17 my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
18 or next;
19 $actual[$t] = $load_avg;
20}
21
22# Calculate expected values.
23my ($load_avg) = 0;
24my ($recent) = 0;
25my (@expected);
26for (my ($t) = 0; $t < 180; $t++) {
27 my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0;
28 $load_avg = (59/60) * $load_avg + (1/60) * $ready;
29 $expected[$t] = $load_avg;
30}
31
32mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
33 "Some load average values were missing or "
34 . "differed from those expected "
35 . "by more than 2.5.");
36pass;
diff --git a/pintos-progos/tests/threads/mlfqs-nice-10.ck b/pintos-progos/tests/threads/mlfqs-nice-10.ck
new file mode 100644
index 0000000..53e0abe
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-nice-10.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([0...9], 25);
diff --git a/pintos-progos/tests/threads/mlfqs-nice-2.ck b/pintos-progos/tests/threads/mlfqs-nice-2.ck
new file mode 100644
index 0000000..ada366b
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-nice-2.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([0, 5], 50);
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.c b/pintos-progos/tests/threads/mlfqs-recent-1.c
new file mode 100644
index 0000000..4258671
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-recent-1.c
@@ -0,0 +1,144 @@
1/* Checks that recent_cpu is calculated properly for the case of
2 a single ready process.
3
4 The expected output is this (some margin of error is allowed):
5
6 After 2 seconds, recent_cpu is 6.40, load_avg is 0.03.
7 After 4 seconds, recent_cpu is 12.60, load_avg is 0.07.
8 After 6 seconds, recent_cpu is 18.61, load_avg is 0.10.
9 After 8 seconds, recent_cpu is 24.44, load_avg is 0.13.
10 After 10 seconds, recent_cpu is 30.08, load_avg is 0.15.
11 After 12 seconds, recent_cpu is 35.54, load_avg is 0.18.
12 After 14 seconds, recent_cpu is 40.83, load_avg is 0.21.
13 After 16 seconds, recent_cpu is 45.96, load_avg is 0.24.
14 After 18 seconds, recent_cpu is 50.92, load_avg is 0.26.
15 After 20 seconds, recent_cpu is 55.73, load_avg is 0.29.
16 After 22 seconds, recent_cpu is 60.39, load_avg is 0.31.
17 After 24 seconds, recent_cpu is 64.90, load_avg is 0.33.
18 After 26 seconds, recent_cpu is 69.27, load_avg is 0.35.
19 After 28 seconds, recent_cpu is 73.50, load_avg is 0.38.
20 After 30 seconds, recent_cpu is 77.60, load_avg is 0.40.
21 After 32 seconds, recent_cpu is 81.56, load_avg is 0.42.
22 After 34 seconds, recent_cpu is 85.40, load_avg is 0.44.
23 After 36 seconds, recent_cpu is 89.12, load_avg is 0.45.
24 After 38 seconds, recent_cpu is 92.72, load_avg is 0.47.
25 After 40 seconds, recent_cpu is 96.20, load_avg is 0.49.
26 After 42 seconds, recent_cpu is 99.57, load_avg is 0.51.
27 After 44 seconds, recent_cpu is 102.84, load_avg is 0.52.
28 After 46 seconds, recent_cpu is 106.00, load_avg is 0.54.
29 After 48 seconds, recent_cpu is 109.06, load_avg is 0.55.
30 After 50 seconds, recent_cpu is 112.02, load_avg is 0.57.
31 After 52 seconds, recent_cpu is 114.89, load_avg is 0.58.
32 After 54 seconds, recent_cpu is 117.66, load_avg is 0.60.
33 After 56 seconds, recent_cpu is 120.34, load_avg is 0.61.
34 After 58 seconds, recent_cpu is 122.94, load_avg is 0.62.
35 After 60 seconds, recent_cpu is 125.46, load_avg is 0.64.
36 After 62 seconds, recent_cpu is 127.89, load_avg is 0.65.
37 After 64 seconds, recent_cpu is 130.25, load_avg is 0.66.
38 After 66 seconds, recent_cpu is 132.53, load_avg is 0.67.
39 After 68 seconds, recent_cpu is 134.73, load_avg is 0.68.
40 After 70 seconds, recent_cpu is 136.86, load_avg is 0.69.
41 After 72 seconds, recent_cpu is 138.93, load_avg is 0.70.
42 After 74 seconds, recent_cpu is 140.93, load_avg is 0.71.
43 After 76 seconds, recent_cpu is 142.86, load_avg is 0.72.
44 After 78 seconds, recent_cpu is 144.73, load_avg is 0.73.
45 After 80 seconds, recent_cpu is 146.54, load_avg is 0.74.
46 After 82 seconds, recent_cpu is 148.29, load_avg is 0.75.
47 After 84 seconds, recent_cpu is 149.99, load_avg is 0.76.
48 After 86 seconds, recent_cpu is 151.63, load_avg is 0.76.
49 After 88 seconds, recent_cpu is 153.21, load_avg is 0.77.
50 After 90 seconds, recent_cpu is 154.75, load_avg is 0.78.
51 After 92 seconds, recent_cpu is 156.23, load_avg is 0.79.
52 After 94 seconds, recent_cpu is 157.67, load_avg is 0.79.
53 After 96 seconds, recent_cpu is 159.06, load_avg is 0.80.
54 After 98 seconds, recent_cpu is 160.40, load_avg is 0.81.
55 After 100 seconds, recent_cpu is 161.70, load_avg is 0.81.
56 After 102 seconds, recent_cpu is 162.96, load_avg is 0.82.
57 After 104 seconds, recent_cpu is 164.18, load_avg is 0.83.
58 After 106 seconds, recent_cpu is 165.35, load_avg is 0.83.
59 After 108 seconds, recent_cpu is 166.49, load_avg is 0.84.
60 After 110 seconds, recent_cpu is 167.59, load_avg is 0.84.
61 After 112 seconds, recent_cpu is 168.66, load_avg is 0.85.
62 After 114 seconds, recent_cpu is 169.69, load_avg is 0.85.
63 After 116 seconds, recent_cpu is 170.69, load_avg is 0.86.
64 After 118 seconds, recent_cpu is 171.65, load_avg is 0.86.
65 After 120 seconds, recent_cpu is 172.58, load_avg is 0.87.
66 After 122 seconds, recent_cpu is 173.49, load_avg is 0.87.
67 After 124 seconds, recent_cpu is 174.36, load_avg is 0.88.
68 After 126 seconds, recent_cpu is 175.20, load_avg is 0.88.
69 After 128 seconds, recent_cpu is 176.02, load_avg is 0.88.
70 After 130 seconds, recent_cpu is 176.81, load_avg is 0.89.
71 After 132 seconds, recent_cpu is 177.57, load_avg is 0.89.
72 After 134 seconds, recent_cpu is 178.31, load_avg is 0.89.
73 After 136 seconds, recent_cpu is 179.02, load_avg is 0.90.
74 After 138 seconds, recent_cpu is 179.72, load_avg is 0.90.
75 After 140 seconds, recent_cpu is 180.38, load_avg is 0.90.
76 After 142 seconds, recent_cpu is 181.03, load_avg is 0.91.
77 After 144 seconds, recent_cpu is 181.65, load_avg is 0.91.
78 After 146 seconds, recent_cpu is 182.26, load_avg is 0.91.
79 After 148 seconds, recent_cpu is 182.84, load_avg is 0.92.
80 After 150 seconds, recent_cpu is 183.41, load_avg is 0.92.
81 After 152 seconds, recent_cpu is 183.96, load_avg is 0.92.
82 After 154 seconds, recent_cpu is 184.49, load_avg is 0.92.
83 After 156 seconds, recent_cpu is 185.00, load_avg is 0.93.
84 After 158 seconds, recent_cpu is 185.49, load_avg is 0.93.
85 After 160 seconds, recent_cpu is 185.97, load_avg is 0.93.
86 After 162 seconds, recent_cpu is 186.43, load_avg is 0.93.
87 After 164 seconds, recent_cpu is 186.88, load_avg is 0.94.
88 After 166 seconds, recent_cpu is 187.31, load_avg is 0.94.
89 After 168 seconds, recent_cpu is 187.73, load_avg is 0.94.
90 After 170 seconds, recent_cpu is 188.14, load_avg is 0.94.
91 After 172 seconds, recent_cpu is 188.53, load_avg is 0.94.
92 After 174 seconds, recent_cpu is 188.91, load_avg is 0.95.
93 After 176 seconds, recent_cpu is 189.27, load_avg is 0.95.
94 After 178 seconds, recent_cpu is 189.63, load_avg is 0.95.
95 After 180 seconds, recent_cpu is 189.97, load_avg is 0.95.
96*/
97
98#include <stdio.h>
99#include "tests/threads/tests.h"
100#include "threads/init.h"
101#include "threads/malloc.h"
102#include "threads/synch.h"
103#include "threads/thread.h"
104#include "devices/timer.h"
105
106/* Sensitive to assumption that recent_cpu updates happen exactly
107 when timer_ticks() % TIMER_FREQ == 0. */
108
109void
110test_mlfqs_recent_1 (void)
111{
112 int64_t start_time;
113 int last_elapsed = 0;
114
115 ASSERT (thread_mlfqs);
116
117 do
118 {
119 msg ("Sleeping 10 seconds to allow recent_cpu to decay, please wait...");
120 start_time = timer_ticks ();
121 timer_sleep (DIV_ROUND_UP (start_time, TIMER_FREQ) - start_time
122 + 10 * TIMER_FREQ);
123 }
124 while (thread_get_recent_cpu () > 700);
125
126 start_time = timer_ticks ();
127 for (;;)
128 {
129 int elapsed = timer_elapsed (start_time);
130 if (elapsed % (TIMER_FREQ * 2) == 0 && elapsed > last_elapsed)
131 {
132 int recent_cpu = thread_get_recent_cpu ();
133 int load_avg = thread_get_load_avg ();
134 int elapsed_seconds = elapsed / TIMER_FREQ;
135 msg ("After %d seconds, recent_cpu is %d.%02d, load_avg is %d.%02d.",
136 elapsed_seconds,
137 recent_cpu / 100, recent_cpu % 100,
138 load_avg / 100, load_avg % 100);
139 if (elapsed_seconds >= 180)
140 break;
141 }
142 last_elapsed = elapsed;
143 }
144}
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.ck b/pintos-progos/tests/threads/mlfqs-recent-1.ck
new file mode 100644
index 0000000..a2ba44d
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-recent-1.ck
@@ -0,0 +1,31 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7our ($test);
8my (@output) = read_text_file ("$test.output");
9common_checks ("run", @output);
10@output = get_core_output ("run", @output);
11
12# Get actual values.
13local ($_);
14my (@actual);
15foreach (@output) {
16 my ($t, $recent_cpu) = /After (\d+) seconds, recent_cpu is (\d+\.\d+),/
17 or next;
18 $actual[$t] = $recent_cpu;
19}
20
21# Calculate expected values.
22my ($expected_load_avg, $expected_recent_cpu)
23 = mlfqs_expected_load ([(1) x 180], [(100) x 180]);
24my (@expected) = @$expected_recent_cpu;
25
26# Compare actual and expected values.
27mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
28 "Some recent_cpu values were missing or "
29 . "differed from those expected "
30 . "by more than 2.5.");
31pass;
diff --git a/pintos-progos/tests/threads/mlfqs.pm b/pintos-progos/tests/threads/mlfqs.pm
new file mode 100644
index 0000000..184ac16
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs.pm
@@ -0,0 +1,146 @@
1# -*- perl -*-
2use strict;
3use warnings;
4
5sub mlfqs_expected_load {
6 my ($ready, $recent_delta) = @_;
7 my (@load_avg) = 0;
8 my (@recent_cpu) = 0;
9 my ($load_avg) = 0;
10 my ($recent_cpu) = 0;
11 for my $i (0...$#$ready) {
12 $load_avg = (59/60) * $load_avg + (1/60) * $ready->[$i];
13 push (@load_avg, $load_avg);
14
15 if (defined $recent_delta->[$i]) {
16 my ($twice_load) = $load_avg * 2;
17 my ($load_factor) = $twice_load / ($twice_load + 1);
18 $recent_cpu = ($recent_cpu + $recent_delta->[$i]) * $load_factor;
19 push (@recent_cpu, $recent_cpu);
20 }
21 }
22 return (\@load_avg, \@recent_cpu);
23}
24
25sub mlfqs_expected_ticks {
26 my (@nice) = @_;
27 my ($thread_cnt) = scalar (@nice);
28 my (@recent_cpu) = (0) x $thread_cnt;
29 my (@slices) = (0) x $thread_cnt;
30 my (@fifo) = (0) x $thread_cnt;
31 my ($next_fifo) = 1;
32 my ($load_avg) = 0;
33 for my $i (1...750) {
34 if ($i % 25 == 0) {
35 # Update load average.
36 $load_avg = (59/60) * $load_avg + (1/60) * $thread_cnt;
37
38 # Update recent_cpu.
39 my ($twice_load) = $load_avg * 2;
40 my ($load_factor) = $twice_load / ($twice_load + 1);
41 $recent_cpu[$_] = $recent_cpu[$_] * $load_factor + $nice[$_]
42 foreach 0...($thread_cnt - 1);
43 }
44
45 # Update priorities.
46 my (@priority);
47 foreach my $j (0...($thread_cnt - 1)) {
48 my ($priority) = int ($recent_cpu[$j] / 4 + $nice[$j] * 2);
49 $priority = 0 if $priority < 0;
50 $priority = 63 if $priority > 63;
51 push (@priority, $priority);
52 }
53
54 # Choose thread to run.
55 my $max = 0;
56 for my $j (1...$#priority) {
57 if ($priority[$j] < $priority[$max]
58 || ($priority[$j] == $priority[$max]
59 && $fifo[$j] < $fifo[$max])) {
60 $max = $j;
61 }
62 }
63 $fifo[$max] = $next_fifo++;
64
65 # Run thread.
66 $recent_cpu[$max] += 4;
67 $slices[$max] += 4;
68 }
69 return @slices;
70}
71
72sub check_mlfqs_fair {
73 my ($nice, $maxdiff) = @_;
74 our ($test);
75 my (@output) = read_text_file ("$test.output");
76 common_checks ("run", @output);
77 @output = get_core_output ("run", @output);
78
79 my (@actual);
80 local ($_);
81 foreach (@output) {
82 my ($id, $count) = /Thread (\d+) received (\d+) ticks\./ or next;
83 $actual[$id] = $count;
84 }
85
86 my (@expected) = mlfqs_expected_ticks (@$nice);
87 mlfqs_compare ("thread", "%d",
88 \@actual, \@expected, $maxdiff, [0, $#$nice, 1],
89 "Some tick counts were missing or differed from those "
90 . "expected by more than $maxdiff.");
91 pass;
92}
93
94sub mlfqs_compare {
95 my ($indep_var, $format,
96 $actual_ref, $expected_ref, $maxdiff, $t_range, $message) = @_;
97 my ($t_min, $t_max, $t_step) = @$t_range;
98
99 my ($ok) = 1;
100 for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) {
101 my ($actual) = $actual_ref->[$t];
102 my ($expected) = $expected_ref->[$t];
103 $ok = 0, last
104 if !defined ($actual) || abs ($actual - $expected) > $maxdiff + .01;
105 }
106 return if $ok;
107
108 print "$message\n";
109 mlfqs_row ($indep_var, "actual", "<->", "expected", "explanation");
110 mlfqs_row ("------", "--------", "---", "--------", '-' x 40);
111 for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) {
112 my ($actual) = $actual_ref->[$t];
113 my ($expected) = $expected_ref->[$t];
114 my ($diff, $rationale);
115 if (!defined $actual) {
116 $actual = 'undef' ;
117 $diff = '';
118 $rationale = 'Missing value.';
119 } else {
120 my ($delta) = abs ($actual - $expected);
121 if ($delta > $maxdiff + .01) {
122 my ($excess) = $delta - $maxdiff;
123 if ($actual > $expected) {
124 $diff = '>>>';
125 $rationale = sprintf "Too big, by $format.", $excess;
126 } else {
127 $diff = '<<<';
128 $rationale = sprintf "Too small, by $format.", $excess;
129 }
130 } else {
131 $diff = ' = ';
132 $rationale = '';
133 }
134 $actual = sprintf ($format, $actual);
135 }
136 $expected = sprintf ($format, $expected);
137 mlfqs_row ($t, $actual, $diff, $expected, $rationale);
138 }
139 fail;
140}
141
142sub mlfqs_row {
143 printf "%6s %8s %3s %-8s %s\n", @_;
144}
145
1461;
diff --git a/pintos-progos/tests/threads/priority-change.c b/pintos-progos/tests/threads/priority-change.c
new file mode 100644
index 0000000..810b05a
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-change.c
@@ -0,0 +1,31 @@
1/* Verifies that lowering a thread's priority so that it is no
2 longer the highest-priority thread in the system causes it to
3 yield immediately. */
4
5#include <stdio.h>
6#include "tests/threads/tests.h"
7#include "threads/init.h"
8#include "threads/thread.h"
9
10static thread_func changing_thread;
11
12void
13test_priority_change (void)
14{
15 /* This test does not work with the MLFQS. */
16 ASSERT (!thread_mlfqs);
17
18 msg ("Creating a high-priority thread 2.");
19 thread_create ("thread 2", PRI_DEFAULT + 1, changing_thread, NULL);
20 msg ("Thread 2 should have just lowered its priority.");
21 thread_set_priority (PRI_DEFAULT - 2);
22 msg ("Thread 2 should have just exited.");
23}
24
25static void
26changing_thread (void *aux UNUSED)
27{
28 msg ("Thread 2 now lowering priority.");
29 thread_set_priority (PRI_DEFAULT - 1);
30 msg ("Thread 2 exiting.");
31}
diff --git a/pintos-progos/tests/threads/priority-change.ck b/pintos-progos/tests/threads/priority-change.ck
new file mode 100644
index 0000000..f4d9b2f
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-change.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-change) begin
7(priority-change) Creating a high-priority thread 2.
8(priority-change) Thread 2 now lowering priority.
9(priority-change) Thread 2 should have just lowered its priority.
10(priority-change) Thread 2 exiting.
11(priority-change) Thread 2 should have just exited.
12(priority-change) end
13EOF
14pass;
diff --git a/pintos-progos/tests/threads/priority-condvar.c b/pintos-progos/tests/threads/priority-condvar.c
new file mode 100644
index 0000000..c1efb1b
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-condvar.c
@@ -0,0 +1,53 @@
1/* Tests that cond_signal() wakes up the highest-priority thread
2 waiting in cond_wait(). */
3
4#include <stdio.h>
5#include "tests/threads/tests.h"
6#include "threads/init.h"
7#include "threads/malloc.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10#include "devices/timer.h"
11
12static thread_func priority_condvar_thread;
13static struct lock lock;
14static struct condition condition;
15
16void
17test_priority_condvar (void)
18{
19 int i;
20
21 /* This test does not work with the MLFQS. */
22 ASSERT (!thread_mlfqs);
23
24 lock_init (&lock);
25 cond_init (&condition);
26
27 thread_set_priority (PRI_MIN);
28 for (i = 0; i < 10; i++)
29 {
30 int priority = PRI_DEFAULT - (i + 7) % 10 - 1;
31 char name[16];
32 snprintf (name, sizeof name, "priority %d", priority);
33 thread_create (name, priority, priority_condvar_thread, NULL);
34 }
35
36 for (i = 0; i < 10; i++)
37 {
38 lock_acquire (&lock);
39 msg ("Signaling...");
40 cond_signal (&condition, &lock);
41 lock_release (&lock);
42 }
43}
44
45static void
46priority_condvar_thread (void *aux UNUSED)
47{
48 msg ("Thread %s starting.", thread_name ());
49 lock_acquire (&lock);
50 cond_wait (&condition, &lock);
51 msg ("Thread %s woke up.", thread_name ());
52 lock_release (&lock);
53}
diff --git a/pintos-progos/tests/threads/priority-condvar.ck b/pintos-progos/tests/threads/priority-condvar.ck
new file mode 100644
index 0000000..195c1ab
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-condvar.ck
@@ -0,0 +1,39 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-condvar) begin
7(priority-condvar) Thread priority 23 starting.
8(priority-condvar) Thread priority 22 starting.
9(priority-condvar) Thread priority 21 starting.
10(priority-condvar) Thread priority 30 starting.
11(priority-condvar) Thread priority 29 starting.
12(priority-condvar) Thread priority 28 starting.
13(priority-condvar) Thread priority 27 starting.
14(priority-condvar) Thread priority 26 starting.
15(priority-condvar) Thread priority 25 starting.
16(priority-condvar) Thread priority 24 starting.
17(priority-condvar) Signaling...
18(priority-condvar) Thread priority 30 woke up.
19(priority-condvar) Signaling...
20(priority-condvar) Thread priority 29 woke up.
21(priority-condvar) Signaling...
22(priority-condvar) Thread priority 28 woke up.
23(priority-condvar) Signaling...
24(priority-condvar) Thread priority 27 woke up.
25(priority-condvar) Signaling...
26(priority-condvar) Thread priority 26 woke up.
27(priority-condvar) Signaling...
28(priority-condvar) Thread priority 25 woke up.
29(priority-condvar) Signaling...
30(priority-condvar) Thread priority 24 woke up.
31(priority-condvar) Signaling...
32(priority-condvar) Thread priority 23 woke up.
33(priority-condvar) Signaling...
34(priority-condvar) Thread priority 22 woke up.
35(priority-condvar) Signaling...
36(priority-condvar) Thread priority 21 woke up.
37(priority-condvar) end
38EOF
39pass;
diff --git a/pintos-progos/tests/threads/priority-donate-chain.c b/pintos-progos/tests/threads/priority-donate-chain.c
new file mode 100644
index 0000000..3ffabca
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-chain.c
@@ -0,0 +1,114 @@
1/* The main thread set its priority to PRI_MIN and creates 7 threads
2 (thread 1..7) with priorities PRI_MIN + 3, 6, 9, 12, ...
3 The main thread initializes 8 locks: lock 0..7 and acquires lock 0.
4
5 When thread[i] starts, it first acquires lock[i] (unless i == 7.)
6 Subsequently, thread[i] attempts to acquire lock[i-1], which is held by
7 thread[i-1], except for lock[0], which is held by the main thread.
8 Because the lock is held, thread[i] donates its priority to thread[i-1],
9 which donates to thread[i-2], and so on until the main thread
10 receives the donation.
11
12 After threads[1..7] have been created and are blocked on locks[0..7],
13 the main thread releases lock[0], unblocking thread[1], and being
14 preempted by it.
15 Thread[1] then completes acquiring lock[0], then releases lock[0],
16 then releases lock[1], unblocking thread[2], etc.
17 Thread[7] finally acquires & releases lock[7] and exits, allowing
18 thread[6], then thread[5] etc. to run and exit until finally the
19 main thread exits.
20
21 In addition, interloper threads are created at priority levels
22 p = PRI_MIN + 2, 5, 8, 11, ... which should not be run until the
23 corresponding thread with priority p + 1 has finished.
24
25 Written by Godmar Back <gback@cs.vt.edu> */
26
27#include <stdio.h>
28#include "tests/threads/tests.h"
29#include "threads/init.h"
30#include "threads/synch.h"
31#include "threads/thread.h"
32
33#define NESTING_DEPTH 8
34
35struct lock_pair
36 {
37 struct lock *second;
38 struct lock *first;
39 };
40
41static thread_func donor_thread_func;
42static thread_func interloper_thread_func;
43
44void
45test_priority_donate_chain (void)
46{
47 int i;
48 struct lock locks[NESTING_DEPTH - 1];
49 struct lock_pair lock_pairs[NESTING_DEPTH];
50
51 /* This test does not work with the MLFQS. */
52 ASSERT (!thread_mlfqs);
53
54 thread_set_priority (PRI_MIN);
55
56 for (i = 0; i < NESTING_DEPTH - 1; i++)
57 lock_init (&locks[i]);
58
59 lock_acquire (&locks[0]);
60 msg ("%s got lock.", thread_name ());
61
62 for (i = 1; i < NESTING_DEPTH; i++)
63 {
64 char name[16];
65 int thread_priority;
66
67 snprintf (name, sizeof name, "thread %d", i);
68 thread_priority = PRI_MIN + i * 3;
69 lock_pairs[i].first = i < NESTING_DEPTH - 1 ? locks + i: NULL;
70 lock_pairs[i].second = locks + i - 1;
71
72 thread_create (name, thread_priority, donor_thread_func, lock_pairs + i);
73 msg ("%s should have priority %d. Actual priority: %d.",
74 thread_name (), thread_priority, thread_get_priority ());
75
76 snprintf (name, sizeof name, "interloper %d", i);
77 thread_create (name, thread_priority - 1, interloper_thread_func, NULL);
78 }
79
80 lock_release (&locks[0]);
81 msg ("%s finishing with priority %d.", thread_name (),
82 thread_get_priority ());
83}
84
85static void
86donor_thread_func (void *locks_)
87{
88 struct lock_pair *locks = locks_;
89
90 if (locks->first)
91 lock_acquire (locks->first);
92
93 lock_acquire (locks->second);
94 msg ("%s got lock", thread_name ());
95
96 lock_release (locks->second);
97 msg ("%s should have priority %d. Actual priority: %d",
98 thread_name (), (NESTING_DEPTH - 1) * 3,
99 thread_get_priority ());
100
101 if (locks->first)
102 lock_release (locks->first);
103
104 msg ("%s finishing with priority %d.", thread_name (),
105 thread_get_priority ());
106}
107
108static void
109interloper_thread_func (void *arg_ UNUSED)
110{
111 msg ("%s finished.", thread_name ());
112}
113
114// vim: sw=2
diff --git a/pintos-progos/tests/threads/priority-donate-chain.ck b/pintos-progos/tests/threads/priority-donate-chain.ck
new file mode 100644
index 0000000..213e649
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-chain.ck
@@ -0,0 +1,46 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-chain) begin
7(priority-donate-chain) main got lock.
8(priority-donate-chain) main should have priority 3. Actual priority: 3.
9(priority-donate-chain) main should have priority 6. Actual priority: 6.
10(priority-donate-chain) main should have priority 9. Actual priority: 9.
11(priority-donate-chain) main should have priority 12. Actual priority: 12.
12(priority-donate-chain) main should have priority 15. Actual priority: 15.
13(priority-donate-chain) main should have priority 18. Actual priority: 18.
14(priority-donate-chain) main should have priority 21. Actual priority: 21.
15(priority-donate-chain) thread 1 got lock
16(priority-donate-chain) thread 1 should have priority 21. Actual priority: 21
17(priority-donate-chain) thread 2 got lock
18(priority-donate-chain) thread 2 should have priority 21. Actual priority: 21
19(priority-donate-chain) thread 3 got lock
20(priority-donate-chain) thread 3 should have priority 21. Actual priority: 21
21(priority-donate-chain) thread 4 got lock
22(priority-donate-chain) thread 4 should have priority 21. Actual priority: 21
23(priority-donate-chain) thread 5 got lock
24(priority-donate-chain) thread 5 should have priority 21. Actual priority: 21
25(priority-donate-chain) thread 6 got lock
26(priority-donate-chain) thread 6 should have priority 21. Actual priority: 21
27(priority-donate-chain) thread 7 got lock
28(priority-donate-chain) thread 7 should have priority 21. Actual priority: 21
29(priority-donate-chain) thread 7 finishing with priority 21.
30(priority-donate-chain) interloper 7 finished.
31(priority-donate-chain) thread 6 finishing with priority 18.
32(priority-donate-chain) interloper 6 finished.
33(priority-donate-chain) thread 5 finishing with priority 15.
34(priority-donate-chain) interloper 5 finished.
35(priority-donate-chain) thread 4 finishing with priority 12.
36(priority-donate-chain) interloper 4 finished.
37(priority-donate-chain) thread 3 finishing with priority 9.
38(priority-donate-chain) interloper 3 finished.
39(priority-donate-chain) thread 2 finishing with priority 6.
40(priority-donate-chain) interloper 2 finished.
41(priority-donate-chain) thread 1 finishing with priority 3.
42(priority-donate-chain) interloper 1 finished.
43(priority-donate-chain) main finishing with priority 0.
44(priority-donate-chain) end
45EOF
46pass;
diff --git a/pintos-progos/tests/threads/priority-donate-lower.c b/pintos-progos/tests/threads/priority-donate-lower.c
new file mode 100644
index 0000000..4965d75
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-lower.c
@@ -0,0 +1,51 @@
1/* The main thread acquires a lock. Then it creates a
2 higher-priority thread that blocks acquiring the lock, causing
3 it to donate their priorities to the main thread. The main
4 thread attempts to lower its priority, which should not take
5 effect until the donation is released. */
6
7#include <stdio.h>
8#include "tests/threads/tests.h"
9#include "threads/init.h"
10#include "threads/synch.h"
11#include "threads/thread.h"
12
13static thread_func acquire_thread_func;
14
15void
16test_priority_donate_lower (void)
17{
18 struct lock lock;
19
20 /* This test does not work with the MLFQS. */
21 ASSERT (!thread_mlfqs);
22
23 /* Make sure our priority is the default. */
24 ASSERT (thread_get_priority () == PRI_DEFAULT);
25
26 lock_init (&lock);
27 lock_acquire (&lock);
28 thread_create ("acquire", PRI_DEFAULT + 10, acquire_thread_func, &lock);
29 msg ("Main thread should have priority %d. Actual priority: %d.",
30 PRI_DEFAULT + 10, thread_get_priority ());
31
32 msg ("Lowering base priority...");
33 thread_set_priority (PRI_DEFAULT - 10);
34 msg ("Main thread should have priority %d. Actual priority: %d.",
35 PRI_DEFAULT + 10, thread_get_priority ());
36 lock_release (&lock);
37 msg ("acquire must already have finished.");
38 msg ("Main thread should have priority %d. Actual priority: %d.",
39 PRI_DEFAULT - 10, thread_get_priority ());
40}
41
42static void
43acquire_thread_func (void *lock_)
44{
45 struct lock *lock = lock_;
46
47 lock_acquire (lock);
48 msg ("acquire: got the lock");
49 lock_release (lock);
50 msg ("acquire: done");
51}
diff --git a/pintos-progos/tests/threads/priority-donate-lower.ck b/pintos-progos/tests/threads/priority-donate-lower.ck
new file mode 100644
index 0000000..c9bb61b
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-lower.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-lower) begin
7(priority-donate-lower) Main thread should have priority 41. Actual priority: 41.
8(priority-donate-lower) Lowering base priority...
9(priority-donate-lower) Main thread should have priority 41. Actual priority: 41.
10(priority-donate-lower) acquire: got the lock
11(priority-donate-lower) acquire: done
12(priority-donate-lower) acquire must already have finished.
13(priority-donate-lower) Main thread should have priority 21. Actual priority: 21.
14(priority-donate-lower) end
15EOF
16pass;
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.c b/pintos-progos/tests/threads/priority-donate-multiple.c
new file mode 100644
index 0000000..df4689c
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple.c
@@ -0,0 +1,77 @@
1/* The main thread acquires locks A and B, then it creates two
2 higher-priority threads. Each of these threads blocks
3 acquiring one of the locks and thus donate their priority to
4 the main thread. The main thread releases the locks in turn
5 and relinquishes its donated priorities.
6
7 Based on a test originally submitted for Stanford's CS 140 in
8 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
9 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
10 <yph@cs.stanford.edu>. Modified by arens. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18static thread_func a_thread_func;
19static thread_func b_thread_func;
20
21void
22test_priority_donate_multiple (void)
23{
24 struct lock a, b;
25
26 /* This test does not work with the MLFQS. */
27 ASSERT (!thread_mlfqs);
28
29 /* Make sure our priority is the default. */
30 ASSERT (thread_get_priority () == PRI_DEFAULT);
31
32 lock_init (&a);
33 lock_init (&b);
34
35 lock_acquire (&a);
36 lock_acquire (&b);
37
38 thread_create ("a", PRI_DEFAULT + 1, a_thread_func, &a);
39 msg ("Main thread should have priority %d. Actual priority: %d.",
40 PRI_DEFAULT + 1, thread_get_priority ());
41
42 thread_create ("b", PRI_DEFAULT + 2, b_thread_func, &b);
43 msg ("Main thread should have priority %d. Actual priority: %d.",
44 PRI_DEFAULT + 2, thread_get_priority ());
45
46 lock_release (&b);
47 msg ("Thread b should have just finished.");
48 msg ("Main thread should have priority %d. Actual priority: %d.",
49 PRI_DEFAULT + 1, thread_get_priority ());
50
51 lock_release (&a);
52 msg ("Thread a should have just finished.");
53 msg ("Main thread should have priority %d. Actual priority: %d.",
54 PRI_DEFAULT, thread_get_priority ());
55}
56
57static void
58a_thread_func (void *lock_)
59{
60 struct lock *lock = lock_;
61
62 lock_acquire (lock);
63 msg ("Thread a acquired lock a.");
64 lock_release (lock);
65 msg ("Thread a finished.");
66}
67
68static void
69b_thread_func (void *lock_)
70{
71 struct lock *lock = lock_;
72
73 lock_acquire (lock);
74 msg ("Thread b acquired lock b.");
75 lock_release (lock);
76 msg ("Thread b finished.");
77}
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.ck b/pintos-progos/tests/threads/priority-donate-multiple.ck
new file mode 100644
index 0000000..0afd20b
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-multiple) begin
7(priority-donate-multiple) Main thread should have priority 32. Actual priority: 32.
8(priority-donate-multiple) Main thread should have priority 33. Actual priority: 33.
9(priority-donate-multiple) Thread b acquired lock b.
10(priority-donate-multiple) Thread b finished.
11(priority-donate-multiple) Thread b should have just finished.
12(priority-donate-multiple) Main thread should have priority 32. Actual priority: 32.
13(priority-donate-multiple) Thread a acquired lock a.
14(priority-donate-multiple) Thread a finished.
15(priority-donate-multiple) Thread a should have just finished.
16(priority-donate-multiple) Main thread should have priority 31. Actual priority: 31.
17(priority-donate-multiple) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.c b/pintos-progos/tests/threads/priority-donate-multiple2.c
new file mode 100644
index 0000000..7f65fef
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple2.c
@@ -0,0 +1,90 @@
1/* The main thread acquires locks A and B, then it creates three
2 higher-priority threads. The first two of these threads block
3 acquiring one of the locks and thus donate their priority to
4 the main thread. The main thread releases the locks in turn
5 and relinquishes its donated priorities, allowing the third thread
6 to run.
7
8 In this test, the main thread releases the locks in a different
9 order compared to priority-donate-multiple.c.
10
11 Written by Godmar Back <gback@cs.vt.edu>.
12 Based on a test originally submitted for Stanford's CS 140 in
13 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
14 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
15 <yph@cs.stanford.edu>. Modified by arens. */
16
17#include <stdio.h>
18#include "tests/threads/tests.h"
19#include "threads/init.h"
20#include "threads/synch.h"
21#include "threads/thread.h"
22
23static thread_func a_thread_func;
24static thread_func b_thread_func;
25static thread_func c_thread_func;
26
27void
28test_priority_donate_multiple2 (void)
29{
30 struct lock a, b;
31
32 /* This test does not work with the MLFQS. */
33 ASSERT (!thread_mlfqs);
34
35 /* Make sure our priority is the default. */
36 ASSERT (thread_get_priority () == PRI_DEFAULT);
37
38 lock_init (&a);
39 lock_init (&b);
40
41 lock_acquire (&a);
42 lock_acquire (&b);
43
44 thread_create ("a", PRI_DEFAULT + 3, a_thread_func, &a);
45 msg ("Main thread should have priority %d. Actual priority: %d.",
46 PRI_DEFAULT + 3, thread_get_priority ());
47
48 thread_create ("c", PRI_DEFAULT + 1, c_thread_func, NULL);
49
50 thread_create ("b", PRI_DEFAULT + 5, b_thread_func, &b);
51 msg ("Main thread should have priority %d. Actual priority: %d.",
52 PRI_DEFAULT + 5, thread_get_priority ());
53
54 lock_release (&a);
55 msg ("Main thread should have priority %d. Actual priority: %d.",
56 PRI_DEFAULT + 5, thread_get_priority ());
57
58 lock_release (&b);
59 msg ("Threads b, a, c should have just finished, in that order.");
60 msg ("Main thread should have priority %d. Actual priority: %d.",
61 PRI_DEFAULT, thread_get_priority ());
62}
63
64static void
65a_thread_func (void *lock_)
66{
67 struct lock *lock = lock_;
68
69 lock_acquire (lock);
70 msg ("Thread a acquired lock a.");
71 lock_release (lock);
72 msg ("Thread a finished.");
73}
74
75static void
76b_thread_func (void *lock_)
77{
78 struct lock *lock = lock_;
79
80 lock_acquire (lock);
81 msg ("Thread b acquired lock b.");
82 lock_release (lock);
83 msg ("Thread b finished.");
84}
85
86static void
87c_thread_func (void *a_ UNUSED)
88{
89 msg ("Thread c finished.");
90}
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.ck b/pintos-progos/tests/threads/priority-donate-multiple2.ck
new file mode 100644
index 0000000..b23533a
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple2.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-multiple2) begin
7(priority-donate-multiple2) Main thread should have priority 34. Actual priority: 34.
8(priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36.
9(priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36.
10(priority-donate-multiple2) Thread b acquired lock b.
11(priority-donate-multiple2) Thread b finished.
12(priority-donate-multiple2) Thread a acquired lock a.
13(priority-donate-multiple2) Thread a finished.
14(priority-donate-multiple2) Thread c finished.
15(priority-donate-multiple2) Threads b, a, c should have just finished, in that order.
16(priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31.
17(priority-donate-multiple2) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/priority-donate-nest.c b/pintos-progos/tests/threads/priority-donate-nest.c
new file mode 100644
index 0000000..3a3a9a5
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-nest.c
@@ -0,0 +1,94 @@
1/* Low-priority main thread L acquires lock A. Medium-priority
2 thread M then acquires lock B then blocks on acquiring lock A.
3 High-priority thread H then blocks on acquiring lock B. Thus,
4 thread H donates its priority to M, which in turn donates it
5 to thread L.
6
7 Based on a test originally submitted for Stanford's CS 140 in
8 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
9 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
10 <yph@cs.stanford.edu>. Modified by arens. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18struct locks
19 {
20 struct lock *a;
21 struct lock *b;
22 };
23
24static thread_func medium_thread_func;
25static thread_func high_thread_func;
26
27void
28test_priority_donate_nest (void)
29{
30 struct lock a, b;
31 struct locks locks;
32
33 /* This test does not work with the MLFQS. */
34 ASSERT (!thread_mlfqs);
35
36 /* Make sure our priority is the default. */
37 ASSERT (thread_get_priority () == PRI_DEFAULT);
38
39 lock_init (&a);
40 lock_init (&b);
41
42 lock_acquire (&a);
43
44 locks.a = &a;
45 locks.b = &b;
46 thread_create ("medium", PRI_DEFAULT + 1, medium_thread_func, &locks);
47 thread_yield ();
48 msg ("Low thread should have priority %d. Actual priority: %d.",
49 PRI_DEFAULT + 1, thread_get_priority ());
50
51 thread_create ("high", PRI_DEFAULT + 2, high_thread_func, &b);
52 thread_yield ();
53 msg ("Low thread should have priority %d. Actual priority: %d.",
54 PRI_DEFAULT + 2, thread_get_priority ());
55
56 lock_release (&a);
57 thread_yield ();
58 msg ("Medium thread should just have finished.");
59 msg ("Low thread should have priority %d. Actual priority: %d.",
60 PRI_DEFAULT, thread_get_priority ());
61}
62
63static void
64medium_thread_func (void *locks_)
65{
66 struct locks *locks = locks_;
67
68 lock_acquire (locks->b);
69 lock_acquire (locks->a);
70
71 msg ("Medium thread should have priority %d. Actual priority: %d.",
72 PRI_DEFAULT + 2, thread_get_priority ());
73 msg ("Medium thread got the lock.");
74
75 lock_release (locks->a);
76 thread_yield ();
77
78 lock_release (locks->b);
79 thread_yield ();
80
81 msg ("High thread should have just finished.");
82 msg ("Middle thread finished.");
83}
84
85static void
86high_thread_func (void *lock_)
87{
88 struct lock *lock = lock_;
89
90 lock_acquire (lock);
91 msg ("High thread got the lock.");
92 lock_release (lock);
93 msg ("High thread finished.");
94}
diff --git a/pintos-progos/tests/threads/priority-donate-nest.ck b/pintos-progos/tests/threads/priority-donate-nest.ck
new file mode 100644
index 0000000..923460e
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-nest.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-nest) begin
7(priority-donate-nest) Low thread should have priority 32. Actual priority: 32.
8(priority-donate-nest) Low thread should have priority 33. Actual priority: 33.
9(priority-donate-nest) Medium thread should have priority 33. Actual priority: 33.
10(priority-donate-nest) Medium thread got the lock.
11(priority-donate-nest) High thread got the lock.
12(priority-donate-nest) High thread finished.
13(priority-donate-nest) High thread should have just finished.
14(priority-donate-nest) Middle thread finished.
15(priority-donate-nest) Medium thread should just have finished.
16(priority-donate-nest) Low thread should have priority 31. Actual priority: 31.
17(priority-donate-nest) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/priority-donate-one.c b/pintos-progos/tests/threads/priority-donate-one.c
new file mode 100644
index 0000000..3189f3a
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-one.c
@@ -0,0 +1,65 @@
1/* The main thread acquires a lock. Then it creates two
2 higher-priority threads that block acquiring the lock, causing
3 them to donate their priorities to the main thread. When the
4 main thread releases the lock, the other threads should
5 acquire it in priority order.
6
7 Based on a test originally submitted for Stanford's CS 140 in
8 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
9 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
10 <yph@cs.stanford.edu>. Modified by arens. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18static thread_func acquire1_thread_func;
19static thread_func acquire2_thread_func;
20
21void
22test_priority_donate_one (void)
23{
24 struct lock lock;
25
26 /* This test does not work with the MLFQS. */
27 ASSERT (!thread_mlfqs);
28
29 /* Make sure our priority is the default. */
30 ASSERT (thread_get_priority () == PRI_DEFAULT);
31
32 lock_init (&lock);
33 lock_acquire (&lock);
34 thread_create ("acquire1", PRI_DEFAULT + 1, acquire1_thread_func, &lock);
35 msg ("This thread should have priority %d. Actual priority: %d.",
36 PRI_DEFAULT + 1, thread_get_priority ());
37 thread_create ("acquire2", PRI_DEFAULT + 2, acquire2_thread_func, &lock);
38 msg ("This thread should have priority %d. Actual priority: %d.",
39 PRI_DEFAULT + 2, thread_get_priority ());
40 lock_release (&lock);
41 msg ("acquire2, acquire1 must already have finished, in that order.");
42 msg ("This should be the last line before finishing this test.");
43}
44
45static void
46acquire1_thread_func (void *lock_)
47{
48 struct lock *lock = lock_;
49
50 lock_acquire (lock);
51 msg ("acquire1: got the lock");
52 lock_release (lock);
53 msg ("acquire1: done");
54}
55
56static void
57acquire2_thread_func (void *lock_)
58{
59 struct lock *lock = lock_;
60
61 lock_acquire (lock);
62 msg ("acquire2: got the lock");
63 lock_release (lock);
64 msg ("acquire2: done");
65}
diff --git a/pintos-progos/tests/threads/priority-donate-one.ck b/pintos-progos/tests/threads/priority-donate-one.ck
new file mode 100644
index 0000000..b7c8e6f
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-one.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-one) begin
7(priority-donate-one) This thread should have priority 32. Actual priority: 32.
8(priority-donate-one) This thread should have priority 33. Actual priority: 33.
9(priority-donate-one) acquire2: got the lock
10(priority-donate-one) acquire2: done
11(priority-donate-one) acquire1: got the lock
12(priority-donate-one) acquire1: done
13(priority-donate-one) acquire2, acquire1 must already have finished, in that order.
14(priority-donate-one) This should be the last line before finishing this test.
15(priority-donate-one) end
16EOF
17pass;
diff --git a/pintos-progos/tests/threads/priority-donate-sema.c b/pintos-progos/tests/threads/priority-donate-sema.c
new file mode 100644
index 0000000..b33cb72
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-sema.c
@@ -0,0 +1,82 @@
1/* Low priority thread L acquires a lock, then blocks downing a
2 semaphore. Medium priority thread M then blocks waiting on
3 the same semaphore. Next, high priority thread H attempts to
4 acquire the lock, donating its priority to L.
5
6 Next, the main thread ups the semaphore, waking up L. L
7 releases the lock, which wakes up H. H "up"s the semaphore,
8 waking up M. H terminates, then M, then L, and finally the
9 main thread.
10
11 Written by Godmar Back <gback@cs.vt.edu>. */
12
13#include <stdio.h>
14#include "tests/threads/tests.h"
15#include "threads/init.h"
16#include "threads/synch.h"
17#include "threads/thread.h"
18
19struct lock_and_sema
20 {
21 struct lock lock;
22 struct semaphore sema;
23 };
24
25static thread_func l_thread_func;
26static thread_func m_thread_func;
27static thread_func h_thread_func;
28
29void
30test_priority_donate_sema (void)
31{
32 struct lock_and_sema ls;
33
34 /* This test does not work with the MLFQS. */
35 ASSERT (!thread_mlfqs);
36
37 /* Make sure our priority is the default. */
38 ASSERT (thread_get_priority () == PRI_DEFAULT);
39
40 lock_init (&ls.lock);
41 sema_init (&ls.sema, 0);
42 thread_create ("low", PRI_DEFAULT + 1, l_thread_func, &ls);
43 thread_create ("med", PRI_DEFAULT + 3, m_thread_func, &ls);
44 thread_create ("high", PRI_DEFAULT + 5, h_thread_func, &ls);
45 sema_up (&ls.sema);
46 msg ("Main thread finished.");
47}
48
49static void
50l_thread_func (void *ls_)
51{
52 struct lock_and_sema *ls = ls_;
53
54 lock_acquire (&ls->lock);
55 msg ("Thread L acquired lock.");
56 sema_down (&ls->sema);
57 msg ("Thread L downed semaphore.");
58 lock_release (&ls->lock);
59 msg ("Thread L finished.");
60}
61
62static void
63m_thread_func (void *ls_)
64{
65 struct lock_and_sema *ls = ls_;
66
67 sema_down (&ls->sema);
68 msg ("Thread M finished.");
69}
70
71static void
72h_thread_func (void *ls_)
73{
74 struct lock_and_sema *ls = ls_;
75
76 lock_acquire (&ls->lock);
77 msg ("Thread H acquired lock.");
78
79 sema_up (&ls->sema);
80 lock_release (&ls->lock);
81 msg ("Thread H finished.");
82}
diff --git a/pintos-progos/tests/threads/priority-donate-sema.ck b/pintos-progos/tests/threads/priority-donate-sema.ck
new file mode 100644
index 0000000..92b8d07
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-sema.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-sema) begin
7(priority-donate-sema) Thread L acquired lock.
8(priority-donate-sema) Thread L downed semaphore.
9(priority-donate-sema) Thread H acquired lock.
10(priority-donate-sema) Thread H finished.
11(priority-donate-sema) Thread M finished.
12(priority-donate-sema) Thread L finished.
13(priority-donate-sema) Main thread finished.
14(priority-donate-sema) end
15EOF
16pass;
diff --git a/pintos-progos/tests/threads/priority-fifo.c b/pintos-progos/tests/threads/priority-fifo.c
new file mode 100644
index 0000000..3af98a3
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-fifo.c
@@ -0,0 +1,99 @@
1/* Creates several threads all at the same priority and ensures
2 that they consistently run in the same round-robin order.
3
4 Based on a test originally submitted for Stanford's CS 140 in
5 winter 1999 by by Matt Franklin
6 <startled@leland.stanford.edu>, Greg Hutchins
7 <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>.
8 Modified by arens. */
9
10#include <stdio.h>
11#include "tests/threads/tests.h"
12#include "threads/init.h"
13#include "devices/timer.h"
14#include "threads/malloc.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18struct simple_thread_data
19 {
20 int id; /* Sleeper ID. */
21 int iterations; /* Iterations so far. */
22 struct lock *lock; /* Lock on output. */
23 int **op; /* Output buffer position. */
24 };
25
26#define THREAD_CNT 16
27#define ITER_CNT 16
28
29static thread_func simple_thread_func;
30
31void
32test_priority_fifo (void)
33{
34 struct simple_thread_data data[THREAD_CNT];
35 struct lock lock;
36 int *output, *op;
37 int i, cnt;
38
39 /* This test does not work with the MLFQS. */
40 ASSERT (!thread_mlfqs);
41
42 /* Make sure our priority is the default. */
43 ASSERT (thread_get_priority () == PRI_DEFAULT);
44
45 msg ("%d threads will iterate %d times in the same order each time.",
46 THREAD_CNT, ITER_CNT);
47 msg ("If the order varies then there is a bug.");
48
49 output = op = malloc (sizeof *output * THREAD_CNT * ITER_CNT * 2);
50 ASSERT (output != NULL);
51 lock_init (&lock);
52
53 thread_set_priority (PRI_DEFAULT + 2);
54 for (i = 0; i < THREAD_CNT; i++)
55 {
56 char name[16];
57 struct simple_thread_data *d = data + i;
58 snprintf (name, sizeof name, "%d", i);
59 d->id = i;
60 d->iterations = 0;
61 d->lock = &lock;
62 d->op = &op;
63 thread_create (name, PRI_DEFAULT + 1, simple_thread_func, d);
64 }
65
66 thread_set_priority (PRI_DEFAULT);
67 /* All the other threads now run to termination here. */
68 ASSERT (lock.holder == NULL);
69
70 cnt = 0;
71 for (; output < op; output++)
72 {
73 struct simple_thread_data *d;
74
75 ASSERT (*output >= 0 && *output < THREAD_CNT);
76 d = data + *output;
77 if (cnt % THREAD_CNT == 0)
78 printf ("(priority-fifo) iteration:");
79 printf (" %d", d->id);
80 if (++cnt % THREAD_CNT == 0)
81 printf ("\n");
82 d->iterations++;
83 }
84}
85
86static void
87simple_thread_func (void *data_)
88{
89 struct simple_thread_data *data = data_;
90 int i;
91
92 for (i = 0; i < ITER_CNT; i++)
93 {
94 lock_acquire (data->lock);
95 *(*data->op)++ = data->id;
96 lock_release (data->lock);
97 thread_yield ();
98 }
99}
diff --git a/pintos-progos/tests/threads/priority-fifo.ck b/pintos-progos/tests/threads/priority-fifo.ck
new file mode 100644
index 0000000..11f1dd3
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-fifo.ck
@@ -0,0 +1,63 @@
1# -*- perl -*-
2
3# The expected output looks like this:
4#
5# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
6# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
7# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
8# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
9# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
10# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
11# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
12# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
13# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
14# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
15# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
17# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
18# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
19# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
20# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
21#
22# A different permutation of 0...15 is acceptable, but every line must
23# be in the same order.
24
25use strict;
26use warnings;
27use tests::tests;
28
29our ($test);
30my (@output) = read_text_file ("$test.output");
31
32common_checks ("run", @output);
33
34my ($thread_cnt) = 16;
35my ($iter_cnt) = 16;
36my (@order);
37my (@t) = (-1) x $thread_cnt;
38
39my (@iterations) = grep (/iteration:/, @output);
40fail "No iterations found in output.\n" if !@iterations;
41
42my (@numbering) = $iterations[0] =~ /(\d+)/g;
43fail "First iteration does not list exactly $thread_cnt threads.\n"
44 if @numbering != $thread_cnt;
45
46my (@sorted_numbering) = sort { $a <=> $b } @numbering;
47for my $i (0...$#sorted_numbering) {
48 if ($sorted_numbering[$i] != $i) {
49 fail "First iteration does not list all threads "
50 . "0...$#sorted_numbering\n";
51 }
52}
53
54for my $i (1...$#iterations) {
55 if ($iterations[$i] ne $iterations[0]) {
56 fail "Iteration $i differs from iteration 0\n";
57 }
58}
59
60fail "$iter_cnt iterations expected but " . scalar (@iterations) . " found\n"
61 if $iter_cnt != @iterations;
62
63pass;
diff --git a/pintos-progos/tests/threads/priority-preempt.c b/pintos-progos/tests/threads/priority-preempt.c
new file mode 100644
index 0000000..3c3aacb
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-preempt.c
@@ -0,0 +1,41 @@
1/* Ensures that a high-priority thread really preempts.
2
3 Based on a test originally submitted for Stanford's CS 140 in
4 winter 1999 by by Matt Franklin
5 <startled@leland.stanford.edu>, Greg Hutchins
6 <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>.
7 Modified by arens. */
8
9#include <stdio.h>
10#include "tests/threads/tests.h"
11#include "threads/init.h"
12#include "threads/synch.h"
13#include "threads/thread.h"
14
15static thread_func simple_thread_func;
16
17void
18test_priority_preempt (void)
19{
20 /* This test does not work with the MLFQS. */
21 ASSERT (!thread_mlfqs);
22
23 /* Make sure our priority is the default. */
24 ASSERT (thread_get_priority () == PRI_DEFAULT);
25
26 thread_create ("high-priority", PRI_DEFAULT + 1, simple_thread_func, NULL);
27 msg ("The high-priority thread should have already completed.");
28}
29
30static void
31simple_thread_func (void *aux UNUSED)
32{
33 int i;
34
35 for (i = 0; i < 5; i++)
36 {
37 msg ("Thread %s iteration %d", thread_name (), i);
38 thread_yield ();
39 }
40 msg ("Thread %s done!", thread_name ());
41}
diff --git a/pintos-progos/tests/threads/priority-preempt.ck b/pintos-progos/tests/threads/priority-preempt.ck
new file mode 100644
index 0000000..43a26ee
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-preempt.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-preempt) begin
7(priority-preempt) Thread high-priority iteration 0
8(priority-preempt) Thread high-priority iteration 1
9(priority-preempt) Thread high-priority iteration 2
10(priority-preempt) Thread high-priority iteration 3
11(priority-preempt) Thread high-priority iteration 4
12(priority-preempt) Thread high-priority done!
13(priority-preempt) The high-priority thread should have already completed.
14(priority-preempt) end
15EOF
16pass;
diff --git a/pintos-progos/tests/threads/priority-sema.c b/pintos-progos/tests/threads/priority-sema.c
new file mode 100644
index 0000000..2834a88
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-sema.c
@@ -0,0 +1,45 @@
1/* Tests that the highest-priority thread waiting on a semaphore
2 is the first to wake up. */
3
4#include <stdio.h>
5#include "tests/threads/tests.h"
6#include "threads/init.h"
7#include "threads/malloc.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10#include "devices/timer.h"
11
12static thread_func priority_sema_thread;
13static struct semaphore sema;
14
15void
16test_priority_sema (void)
17{
18 int i;
19
20 /* This test does not work with the MLFQS. */
21 ASSERT (!thread_mlfqs);
22
23 sema_init (&sema, 0);
24 thread_set_priority (PRI_MIN);
25 for (i = 0; i < 10; i++)
26 {
27 int priority = PRI_DEFAULT - (i + 3) % 10 - 1;
28 char name[16];
29 snprintf (name, sizeof name, "priority %d", priority);
30 thread_create (name, priority, priority_sema_thread, NULL);
31 }
32
33 for (i = 0; i < 10; i++)
34 {
35 sema_up (&sema);
36 msg ("Back in main thread.");
37 }
38}
39
40static void
41priority_sema_thread (void *aux UNUSED)
42{
43 sema_down (&sema);
44 msg ("Thread %s woke up.", thread_name ());
45}
diff --git a/pintos-progos/tests/threads/priority-sema.ck b/pintos-progos/tests/threads/priority-sema.ck
new file mode 100644
index 0000000..559988d
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-sema.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-sema) begin
7(priority-sema) Thread priority 30 woke up.
8(priority-sema) Back in main thread.
9(priority-sema) Thread priority 29 woke up.
10(priority-sema) Back in main thread.
11(priority-sema) Thread priority 28 woke up.
12(priority-sema) Back in main thread.
13(priority-sema) Thread priority 27 woke up.
14(priority-sema) Back in main thread.
15(priority-sema) Thread priority 26 woke up.
16(priority-sema) Back in main thread.
17(priority-sema) Thread priority 25 woke up.
18(priority-sema) Back in main thread.
19(priority-sema) Thread priority 24 woke up.
20(priority-sema) Back in main thread.
21(priority-sema) Thread priority 23 woke up.
22(priority-sema) Back in main thread.
23(priority-sema) Thread priority 22 woke up.
24(priority-sema) Back in main thread.
25(priority-sema) Thread priority 21 woke up.
26(priority-sema) Back in main thread.
27(priority-sema) end
28EOF
29pass;
diff --git a/pintos-progos/tests/threads/tests.c b/pintos-progos/tests/threads/tests.c
new file mode 100644
index 0000000..af15aee
--- /dev/null
+++ b/pintos-progos/tests/threads/tests.c
@@ -0,0 +1,102 @@
1#include "tests/threads/tests.h"
2#include <debug.h>
3#include <string.h>
4#include <stdio.h>
5
6struct test
7 {
8 const char *name;
9 test_func *function;
10 };
11
12static const struct test tests[] =
13 {
14 {"alarm-single", test_alarm_single},
15 {"alarm-multiple", test_alarm_multiple},
16 {"alarm-simultaneous", test_alarm_simultaneous},
17 {"alarm-priority", test_alarm_priority},
18 {"alarm-zero", test_alarm_zero},
19 {"alarm-negative", test_alarm_negative},
20 {"priority-change", test_priority_change},
21 {"priority-donate-one", test_priority_donate_one},
22 {"priority-donate-multiple", test_priority_donate_multiple},
23 {"priority-donate-multiple2", test_priority_donate_multiple2},
24 {"priority-donate-nest", test_priority_donate_nest},
25 {"priority-donate-sema", test_priority_donate_sema},
26 {"priority-donate-lower", test_priority_donate_lower},
27 {"priority-donate-chain", test_priority_donate_chain},
28 {"priority-fifo", test_priority_fifo},
29 {"priority-preempt", test_priority_preempt},
30 {"priority-sema", test_priority_sema},
31 {"priority-condvar", test_priority_condvar},
32 {"mlfqs-load-1", test_mlfqs_load_1},
33 {"mlfqs-load-60", test_mlfqs_load_60},
34 {"mlfqs-load-avg", test_mlfqs_load_avg},
35 {"mlfqs-recent-1", test_mlfqs_recent_1},
36 {"mlfqs-fair-2", test_mlfqs_fair_2},
37 {"mlfqs-fair-20", test_mlfqs_fair_20},
38 {"mlfqs-nice-2", test_mlfqs_nice_2},
39 {"mlfqs-nice-10", test_mlfqs_nice_10},
40 {"mlfqs-block", test_mlfqs_block},
41 };
42
43static const char *test_name;
44
45/* Runs the test named NAME. */
46void
47run_test (const char *name)
48{
49 const struct test *t;
50
51 for (t = tests; t < tests + sizeof tests / sizeof *tests; t++)
52 if (!strcmp (name, t->name))
53 {
54 test_name = name;
55 msg ("begin");
56 t->function ();
57 msg ("end");
58 return;
59 }
60 PANIC ("no test named \"%s\"", name);
61}
62
63/* Prints FORMAT as if with printf(),
64 prefixing the output by the name of the test
65 and following it with a new-line character. */
66void
67msg (const char *format, ...)
68{
69 va_list args;
70
71 printf ("(%s) ", test_name);
72 va_start (args, format);
73 vprintf (format, args);
74 va_end (args);
75 putchar ('\n');
76}
77
78/* Prints failure message FORMAT as if with printf(),
79 prefixing the output by the name of the test and FAIL:
80 and following it with a new-line character,
81 and then panics the kernel. */
82void
83fail (const char *format, ...)
84{
85 va_list args;
86
87 printf ("(%s) FAIL: ", test_name);
88 va_start (args, format);
89 vprintf (format, args);
90 va_end (args);
91 putchar ('\n');
92
93 PANIC ("test failed");
94}
95
96/* Prints a message indicating the current test passed. */
97void
98pass (void)
99{
100 printf ("(%s) PASS\n", test_name);
101}
102
diff --git a/pintos-progos/tests/threads/tests.h b/pintos-progos/tests/threads/tests.h
new file mode 100644
index 0000000..cd9d489
--- /dev/null
+++ b/pintos-progos/tests/threads/tests.h
@@ -0,0 +1,41 @@
1#ifndef TESTS_THREADS_TESTS_H
2#define TESTS_THREADS_TESTS_H
3
4void run_test (const char *);
5
6typedef void test_func (void);
7
8extern test_func test_alarm_single;
9extern test_func test_alarm_multiple;
10extern test_func test_alarm_simultaneous;
11extern test_func test_alarm_priority;
12extern test_func test_alarm_zero;
13extern test_func test_alarm_negative;
14extern test_func test_priority_change;
15extern test_func test_priority_donate_one;
16extern test_func test_priority_donate_multiple;
17extern test_func test_priority_donate_multiple2;
18extern test_func test_priority_donate_sema;
19extern test_func test_priority_donate_nest;
20extern test_func test_priority_donate_lower;
21extern test_func test_priority_donate_chain;
22extern test_func test_priority_fifo;
23extern test_func test_priority_preempt;
24extern test_func test_priority_sema;
25extern test_func test_priority_condvar;
26extern test_func test_mlfqs_load_1;
27extern test_func test_mlfqs_load_60;
28extern test_func test_mlfqs_load_avg;
29extern test_func test_mlfqs_recent_1;
30extern test_func test_mlfqs_fair_2;
31extern test_func test_mlfqs_fair_20;
32extern test_func test_mlfqs_nice_2;
33extern test_func test_mlfqs_nice_10;
34extern test_func test_mlfqs_block;
35
36void msg (const char *, ...);
37void fail (const char *, ...);
38void pass (void);
39
40#endif /* tests/threads/tests.h */
41
diff --git a/pintos-progos/tests/userprog/Make.tests b/pintos-progos/tests/userprog/Make.tests
new file mode 100644
index 0000000..88b0737
--- /dev/null
+++ b/pintos-progos/tests/userprog/Make.tests
@@ -0,0 +1,133 @@
1# -*- makefile -*-
2
3tests/%.output: FILESYSSOURCE = --filesys-size=2
4tests/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^)
5
6tests/userprog_TESTS = $(addprefix tests/userprog/,args-none \
7args-single args-multiple args-many args-dbl-space sc-bad-sp \
8sc-bad-arg sc-boundary sc-boundary-2 halt exit create-normal \
9create-empty create-null create-bad-ptr create-long create-exists \
10create-bound open-normal open-missing open-boundary open-empty \
11open-null open-bad-ptr open-twice close-normal close-twice close-stdin \
12close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \
13read-zero read-stdout read-bad-fd write-normal write-bad-ptr \
14write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \
15exec-multiple exec-missing exec-bad-ptr wait-simple wait-twice \
16wait-killed wait-bad-pid multi-recurse multi-child-fd rox-simple \
17rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \
18bad-jump bad-jump2)
19
20tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \
21tests/userprog/,child-simple child-args child-bad child-close child-rox)
22
23tests/userprog/args-none_SRC = tests/userprog/args.c
24tests/userprog/args-single_SRC = tests/userprog/args.c
25tests/userprog/args-multiple_SRC = tests/userprog/args.c
26tests/userprog/args-many_SRC = tests/userprog/args.c
27tests/userprog/args-dbl-space_SRC = tests/userprog/args.c
28tests/userprog/sc-bad-sp_SRC = tests/userprog/sc-bad-sp.c tests/main.c
29tests/userprog/sc-bad-arg_SRC = tests/userprog/sc-bad-arg.c tests/main.c
30tests/userprog/bad-read_SRC = tests/userprog/bad-read.c tests/main.c
31tests/userprog/bad-write_SRC = tests/userprog/bad-write.c tests/main.c
32tests/userprog/bad-jump_SRC = tests/userprog/bad-jump.c tests/main.c
33tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c
34tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c
35tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c
36tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \
37tests/userprog/boundary.c tests/main.c
38tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \
39tests/userprog/boundary.c tests/main.c
40tests/userprog/halt_SRC = tests/userprog/halt.c tests/main.c
41tests/userprog/exit_SRC = tests/userprog/exit.c tests/main.c
42tests/userprog/create-normal_SRC = tests/userprog/create-normal.c tests/main.c
43tests/userprog/create-empty_SRC = tests/userprog/create-empty.c tests/main.c
44tests/userprog/create-null_SRC = tests/userprog/create-null.c tests/main.c
45tests/userprog/create-bad-ptr_SRC = tests/userprog/create-bad-ptr.c \
46tests/main.c
47tests/userprog/create-long_SRC = tests/userprog/create-long.c tests/main.c
48tests/userprog/create-exists_SRC = tests/userprog/create-exists.c tests/main.c
49tests/userprog/create-bound_SRC = tests/userprog/create-bound.c \
50tests/userprog/boundary.c tests/main.c
51tests/userprog/open-normal_SRC = tests/userprog/open-normal.c tests/main.c
52tests/userprog/open-missing_SRC = tests/userprog/open-missing.c tests/main.c
53tests/userprog/open-boundary_SRC = tests/userprog/open-boundary.c \
54tests/userprog/boundary.c tests/main.c
55tests/userprog/open-empty_SRC = tests/userprog/open-empty.c tests/main.c
56tests/userprog/open-null_SRC = tests/userprog/open-null.c tests/main.c
57tests/userprog/open-bad-ptr_SRC = tests/userprog/open-bad-ptr.c tests/main.c
58tests/userprog/open-twice_SRC = tests/userprog/open-twice.c tests/main.c
59tests/userprog/close-normal_SRC = tests/userprog/close-normal.c tests/main.c
60tests/userprog/close-twice_SRC = tests/userprog/close-twice.c tests/main.c
61tests/userprog/close-stdin_SRC = tests/userprog/close-stdin.c tests/main.c
62tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c
63tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c
64tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c
65tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c
66tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \
67tests/userprog/boundary.c tests/main.c
68tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c
69tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c
70tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c
71tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c
72tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c
73tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \
74tests/userprog/boundary.c tests/main.c
75tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c
76tests/userprog/write-stdin_SRC = tests/userprog/write-stdin.c tests/main.c
77tests/userprog/write-bad-fd_SRC = tests/userprog/write-bad-fd.c tests/main.c
78tests/userprog/exec-once_SRC = tests/userprog/exec-once.c tests/main.c
79tests/userprog/exec-arg_SRC = tests/userprog/exec-arg.c tests/main.c
80tests/userprog/exec-multiple_SRC = tests/userprog/exec-multiple.c tests/main.c
81tests/userprog/exec-missing_SRC = tests/userprog/exec-missing.c tests/main.c
82tests/userprog/exec-bad-ptr_SRC = tests/userprog/exec-bad-ptr.c tests/main.c
83tests/userprog/wait-simple_SRC = tests/userprog/wait-simple.c tests/main.c
84tests/userprog/wait-twice_SRC = tests/userprog/wait-twice.c tests/main.c
85tests/userprog/wait-killed_SRC = tests/userprog/wait-killed.c tests/main.c
86tests/userprog/wait-bad-pid_SRC = tests/userprog/wait-bad-pid.c tests/main.c
87tests/userprog/multi-recurse_SRC = tests/userprog/multi-recurse.c
88tests/userprog/multi-child-fd_SRC = tests/userprog/multi-child-fd.c \
89tests/main.c
90tests/userprog/rox-simple_SRC = tests/userprog/rox-simple.c tests/main.c
91tests/userprog/rox-child_SRC = tests/userprog/rox-child.c tests/main.c
92tests/userprog/rox-multichild_SRC = tests/userprog/rox-multichild.c \
93tests/main.c
94
95tests/userprog/child-simple_SRC = tests/userprog/child-simple.c
96tests/userprog/child-args_SRC = tests/userprog/args.c
97tests/userprog/child-bad_SRC = tests/userprog/child-bad.c tests/main.c
98tests/userprog/child-close_SRC = tests/userprog/child-close.c
99tests/userprog/child-rox_SRC = tests/userprog/child-rox.c
100
101$(foreach prog,$(tests/userprog_PROGS),$(eval $(prog)_SRC += tests/lib.c))
102
103tests/userprog/args-single_ARGS = onearg
104tests/userprog/args-multiple_ARGS = some arguments for you!
105tests/userprog/args-many_ARGS = a b c d e f g h i j k l m n o p q r s t u v
106tests/userprog/args-dbl-space_ARGS = two spaces!
107tests/userprog/multi-recurse_ARGS = 15
108
109tests/userprog/open-normal_PUTFILES += tests/userprog/sample.txt
110tests/userprog/open-boundary_PUTFILES += tests/userprog/sample.txt
111tests/userprog/open-twice_PUTFILES += tests/userprog/sample.txt
112tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt
113tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt
114tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt
115tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt
116tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt
117tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt
118tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt
119tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt
120tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt
121tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt
122tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt
123
124tests/userprog/exec-once_PUTFILES += tests/userprog/child-simple
125tests/userprog/exec-multiple_PUTFILES += tests/userprog/child-simple
126tests/userprog/wait-simple_PUTFILES += tests/userprog/child-simple
127tests/userprog/wait-twice_PUTFILES += tests/userprog/child-simple
128
129tests/userprog/exec-arg_PUTFILES += tests/userprog/child-args
130tests/userprog/multi-child-fd_PUTFILES += tests/userprog/child-close
131tests/userprog/wait-killed_PUTFILES += tests/userprog/child-bad
132tests/userprog/rox-child_PUTFILES += tests/userprog/child-rox
133tests/userprog/rox-multichild_PUTFILES += tests/userprog/child-rox
diff --git a/pintos-progos/tests/userprog/Rubric.functionality b/pintos-progos/tests/userprog/Rubric.functionality
new file mode 100644
index 0000000..ea76c44
--- /dev/null
+++ b/pintos-progos/tests/userprog/Rubric.functionality
@@ -0,0 +1,52 @@
1Functionality of system calls:
2- Test argument passing on Pintos command line.
33 args-none
43 args-single
53 args-multiple
63 args-many
73 args-dbl-space
8
9- Test "create" system call.
103 create-empty
113 create-long
123 create-normal
133 create-exists
14
15- Test "open" system call.
163 open-missing
173 open-normal
183 open-twice
19
20- Test "read" system call.
213 read-normal
223 read-zero
23
24- Test "write" system call.
253 write-normal
263 write-zero
27
28- Test "close" system call.
293 close-normal
30
31- Test "exec" system call.
325 exec-once
335 exec-multiple
345 exec-arg
35
36- Test "wait" system call.
375 wait-simple
385 wait-twice
39
40- Test "exit" system call.
415 exit
42
43- Test "halt" system call.
443 halt
45
46- Test recursive execution of user programs.
4715 multi-recurse
48
49- Test read-only executable feature.
503 rox-simple
513 rox-child
523 rox-multichild
diff --git a/pintos-progos/tests/userprog/Rubric.robustness b/pintos-progos/tests/userprog/Rubric.robustness
new file mode 100644
index 0000000..b7d1035
--- /dev/null
+++ b/pintos-progos/tests/userprog/Rubric.robustness
@@ -0,0 +1,48 @@
1Robustness of system calls:
2- Test robustness of file descriptor handling.
32 close-stdin
42 close-stdout
52 close-bad-fd
62 close-twice
72 read-bad-fd
82 read-stdout
92 write-bad-fd
102 write-stdin
112 multi-child-fd
12
13- Test robustness of pointer handling.
143 create-bad-ptr
153 exec-bad-ptr
163 open-bad-ptr
173 read-bad-ptr
183 write-bad-ptr
19
20- Test robustness of buffer copying across page boundaries.
213 create-bound
223 open-boundary
233 read-boundary
243 write-boundary
25
26- Test handling of null pointer and empty strings.
272 create-null
282 open-null
292 open-empty
30
31- Test robustness of system call implementation.
323 sc-bad-arg
333 sc-bad-sp
345 sc-boundary
355 sc-boundary-2
36
37- Test robustness of "exec" and "wait" system calls.
385 exec-missing
395 wait-bad-pid
405 wait-killed
41
42- Test robustness of exception handling.
431 bad-read
441 bad-write
451 bad-jump
461 bad-read2
471 bad-write2
481 bad-jump2
diff --git a/pintos-progos/tests/userprog/args-dbl-space.ck b/pintos-progos/tests/userprog/args-dbl-space.ck
new file mode 100644
index 0000000..dfbcf4b
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-dbl-space.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 3
8(args) argv[0] = 'args-dbl-space'
9(args) argv[1] = 'two'
10(args) argv[2] = 'spaces!'
11(args) argv[3] = null
12(args) end
13args-dbl-space: exit(0)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/args-many.ck b/pintos-progos/tests/userprog/args-many.ck
new file mode 100644
index 0000000..214574a
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-many.ck
@@ -0,0 +1,35 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 23
8(args) argv[0] = 'args-many'
9(args) argv[1] = 'a'
10(args) argv[2] = 'b'
11(args) argv[3] = 'c'
12(args) argv[4] = 'd'
13(args) argv[5] = 'e'
14(args) argv[6] = 'f'
15(args) argv[7] = 'g'
16(args) argv[8] = 'h'
17(args) argv[9] = 'i'
18(args) argv[10] = 'j'
19(args) argv[11] = 'k'
20(args) argv[12] = 'l'
21(args) argv[13] = 'm'
22(args) argv[14] = 'n'
23(args) argv[15] = 'o'
24(args) argv[16] = 'p'
25(args) argv[17] = 'q'
26(args) argv[18] = 'r'
27(args) argv[19] = 's'
28(args) argv[20] = 't'
29(args) argv[21] = 'u'
30(args) argv[22] = 'v'
31(args) argv[23] = null
32(args) end
33args-many: exit(0)
34EOF
35pass;
diff --git a/pintos-progos/tests/userprog/args-multiple.ck b/pintos-progos/tests/userprog/args-multiple.ck
new file mode 100644
index 0000000..227e6cc
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-multiple.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 5
8(args) argv[0] = 'args-multiple'
9(args) argv[1] = 'some'
10(args) argv[2] = 'arguments'
11(args) argv[3] = 'for'
12(args) argv[4] = 'you!'
13(args) argv[5] = null
14(args) end
15args-multiple: exit(0)
16EOF
17pass;
diff --git a/pintos-progos/tests/userprog/args-none.ck b/pintos-progos/tests/userprog/args-none.ck
new file mode 100644
index 0000000..146318e
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-none.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 1
8(args) argv[0] = 'args-none'
9(args) argv[1] = null
10(args) end
11args-none: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/args-single.ck b/pintos-progos/tests/userprog/args-single.ck
new file mode 100644
index 0000000..24582b4
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-single.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 2
8(args) argv[0] = 'args-single'
9(args) argv[1] = 'onearg'
10(args) argv[2] = null
11(args) end
12args-single: exit(0)
13EOF
14pass;
diff --git a/pintos-progos/tests/userprog/args.c b/pintos-progos/tests/userprog/args.c
new file mode 100644
index 0000000..20eda44
--- /dev/null
+++ b/pintos-progos/tests/userprog/args.c
@@ -0,0 +1,25 @@
1/* Prints the command-line arguments.
2 This program is used for all of the args-* tests. Grading is
3 done differently for each of the args-* tests based on the
4 output. */
5
6#include "tests/lib.h"
7
8int
9main (int argc, char *argv[])
10{
11 int i;
12
13 test_name = "args";
14
15 msg ("begin");
16 msg ("argc = %d", argc);
17 for (i = 0; i <= argc; i++)
18 if (argv[i] != NULL)
19 msg ("argv[%d] = '%s'", i, argv[i]);
20 else
21 msg ("argv[%d] = null", i);
22 msg ("end");
23
24 return 0;
25}
diff --git a/pintos-progos/tests/userprog/bad-jump.c b/pintos-progos/tests/userprog/bad-jump.c
new file mode 100644
index 0000000..51b7c9f
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump.c
@@ -0,0 +1,13 @@
1/* This program attempts to execute code at address 0, which is not mapped.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully called NULL: %d",
11 ((int (*)(void))NULL)());
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-jump.ck b/pintos-progos/tests/userprog/bad-jump.ck
new file mode 100644
index 0000000..e1c178b
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-jump) begin
7bad-jump: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-jump2.c b/pintos-progos/tests/userprog/bad-jump2.c
new file mode 100644
index 0000000..dc7c2a7
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump2.c
@@ -0,0 +1,13 @@
1/* This program attempts to execute code at a kernel virtual address.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully called kernel code: %d",
11 ((int (*)(void))0xC0000000)());
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-jump2.ck b/pintos-progos/tests/userprog/bad-jump2.ck
new file mode 100644
index 0000000..35f0f97
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-jump2) begin
7bad-jump2: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-read.c b/pintos-progos/tests/userprog/bad-read.c
new file mode 100644
index 0000000..904c278
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read.c
@@ -0,0 +1,13 @@
1/* This program attempts to read memory at an address that is not mapped.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully dereferenced NULL: %d",
11 *(int *)NULL);
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-read.ck b/pintos-progos/tests/userprog/bad-read.ck
new file mode 100644
index 0000000..4d4d926
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-read) begin
7bad-read: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-read2.c b/pintos-progos/tests/userprog/bad-read2.c
new file mode 100644
index 0000000..a2fc237
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read2.c
@@ -0,0 +1,13 @@
1/* This program attempts to read kernel memory.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully read kernel memory: %d",
11 *(int *)0xC0000000);
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-read2.ck b/pintos-progos/tests/userprog/bad-read2.ck
new file mode 100644
index 0000000..fa27c7d
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-read2) begin
7bad-read2: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-write.c b/pintos-progos/tests/userprog/bad-write.c
new file mode 100644
index 0000000..000c26b
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write.c
@@ -0,0 +1,12 @@
1/* This program attempts to write to memory at an address that is not mapped.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 *(int *)NULL = 42;
11 fail ("should have exited with -1");
12}
diff --git a/pintos-progos/tests/userprog/bad-write.ck b/pintos-progos/tests/userprog/bad-write.ck
new file mode 100644
index 0000000..d213b49
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-write) begin
7bad-write: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-write2.c b/pintos-progos/tests/userprog/bad-write2.c
new file mode 100644
index 0000000..753da1e
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write2.c
@@ -0,0 +1,12 @@
1/* This program attempts to write to kernel memory.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 *(int *)0xC0000000 = 42;
11 fail ("should have exited with -1");
12}
diff --git a/pintos-progos/tests/userprog/bad-write2.ck b/pintos-progos/tests/userprog/bad-write2.ck
new file mode 100644
index 0000000..c6a3420
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-write2) begin
7bad-write2: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/boundary.c b/pintos-progos/tests/userprog/boundary.c
new file mode 100644
index 0000000..59907ec
--- /dev/null
+++ b/pintos-progos/tests/userprog/boundary.c
@@ -0,0 +1,33 @@
1/* Utility function for tests that try to break system calls by
2 passing them data that crosses from one virtual page to
3 another. */
4
5#include <inttypes.h>
6#include <round.h>
7#include <string.h>
8#include "tests/userprog/boundary.h"
9
10static char dst[8192];
11
12/* Returns the beginning of a page. There are at least 2048
13 modifiable bytes on either side of the pointer returned. */
14void *
15get_boundary_area (void)
16{
17 char *p = (char *) ROUND_UP ((uintptr_t) dst, 4096);
18 if (p - dst < 2048)
19 p += 4096;
20 return p;
21}
22
23/* Returns a copy of SRC split across the boundary between two
24 pages. */
25char *
26copy_string_across_boundary (const char *src)
27{
28 char *p = get_boundary_area ();
29 p -= strlen (src) < 4096 ? strlen (src) / 2 : 4096;
30 strlcpy (p, src, 4096);
31 return p;
32}
33
diff --git a/pintos-progos/tests/userprog/boundary.h b/pintos-progos/tests/userprog/boundary.h
new file mode 100644
index 0000000..c8e4b3b
--- /dev/null
+++ b/pintos-progos/tests/userprog/boundary.h
@@ -0,0 +1,7 @@
1#ifndef TESTS_USERPROG_BOUNDARY_H
2#define TESTS_USERPROG_BOUNDARY_H
3
4void *get_boundary_area (void);
5char *copy_string_across_boundary (const char *);
6
7#endif /* tests/userprog/boundary.h */
diff --git a/pintos-progos/tests/userprog/child-bad.c b/pintos-progos/tests/userprog/child-bad.c
new file mode 100644
index 0000000..77d7a69
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-bad.c
@@ -0,0 +1,14 @@
1/* Child process run by wait-killed test.
2 Sets the stack pointer (%esp) to an invalid value and invokes
3 a system call, which should then terminate the process with a
4 -1 exit code. */
5
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 asm volatile ("movl $0x20101234, %esp; int $0x30");
13 fail ("should have exited with -1");
14}
diff --git a/pintos-progos/tests/userprog/child-close.c b/pintos-progos/tests/userprog/child-close.c
new file mode 100644
index 0000000..ac948c8
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-close.c
@@ -0,0 +1,28 @@
1/* Child process run by multi-child-fd test.
2
3 Attempts to close the file descriptor passed as the first
4 command-line argument. This is invalid, because file
5 descriptors are not inherited in Pintos. Two results are
6 allowed: either the system call should return without taking
7 any action, or the kernel should terminate the process with a
8 -1 exit code. */
9
10#include <ctype.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <syscall.h>
14#include "tests/lib.h"
15
16const char *test_name = "child-close";
17
18int
19main (int argc UNUSED, char *argv[])
20{
21 msg ("begin");
22 if (!isdigit (*argv[1]))
23 fail ("bad command-line arguments");
24 close (atoi (argv[1]));
25 msg ("end");
26
27 return 0;
28}
diff --git a/pintos-progos/tests/userprog/child-rox.c b/pintos-progos/tests/userprog/child-rox.c
new file mode 100644
index 0000000..aba808b
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-rox.c
@@ -0,0 +1,55 @@
1/* Child process run by rox-child and rox-multichild tests.
2 Opens and tries to write to its own executable, verifying that
3 that is disallowed.
4 Then recursively executes itself to the depth indicated by the
5 first command-line argument. */
6
7#include <ctype.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <syscall.h>
11#include "tests/lib.h"
12
13const char *test_name = "child-rox";
14
15static void
16try_write (void)
17{
18 int handle;
19 char buffer[19];
20
21 quiet = true;
22 CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\"");
23 quiet = false;
24
25 CHECK (write (handle, buffer, sizeof buffer) == 0,
26 "try to write \"child-rox\"");
27
28 close (handle);
29}
30
31int
32main (int argc UNUSED, char *argv[])
33{
34 msg ("begin");
35 try_write ();
36
37 if (!isdigit (*argv[1]))
38 fail ("bad command-line arguments");
39 if (atoi (argv[1]) > 1)
40 {
41 char cmd[128];
42 int child;
43
44 snprintf (cmd, sizeof cmd, "child-rox %d", atoi (argv[1]) - 1);
45 CHECK ((child = exec (cmd)) != -1, "exec \"%s\"", cmd);
46 quiet = true;
47 CHECK (wait (child) == 12, "wait for \"child-rox\"");
48 quiet = false;
49 }
50
51 try_write ();
52 msg ("end");
53
54 return 12;
55}
diff --git a/pintos-progos/tests/userprog/child-simple.c b/pintos-progos/tests/userprog/child-simple.c
new file mode 100644
index 0000000..0d2dacf
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-simple.c
@@ -0,0 +1,15 @@
1/* Child process run by exec-multiple, exec-one, wait-simple, and
2 wait-twice tests.
3 Just prints a single message and terminates. */
4
5#include <stdio.h>
6#include "tests/lib.h"
7
8const char *test_name = "child-simple";
9
10int
11main (void)
12{
13 msg ("run");
14 return 81;
15}
diff --git a/pintos-progos/tests/userprog/close-bad-fd.c b/pintos-progos/tests/userprog/close-bad-fd.c
new file mode 100644
index 0000000..f63bb9a
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-bad-fd.c
@@ -0,0 +1,11 @@
1/* Tries to close an invalid fd, which must either fail silently
2 or terminate with exit code -1. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 close (0x20101234);
11}
diff --git a/pintos-progos/tests/userprog/close-bad-fd.ck b/pintos-progos/tests/userprog/close-bad-fd.ck
new file mode 100644
index 0000000..497b17c
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-bad-fd.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-bad-fd) begin
7(close-bad-fd) end
8close-bad-fd: exit(0)
9EOF
10(close-bad-fd) begin
11close-bad-fd: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/close-normal.c b/pintos-progos/tests/userprog/close-normal.c
new file mode 100644
index 0000000..8ce04e3
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-normal.c
@@ -0,0 +1,14 @@
1/* Opens a file and then closes it. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
12 msg ("close \"sample.txt\"");
13 close (handle);
14}
diff --git a/pintos-progos/tests/userprog/close-normal.ck b/pintos-progos/tests/userprog/close-normal.ck
new file mode 100644
index 0000000..fe41342
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-normal.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(close-normal) begin
7(close-normal) open "sample.txt"
8(close-normal) close "sample.txt"
9(close-normal) end
10close-normal: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/close-stdin.c b/pintos-progos/tests/userprog/close-stdin.c
new file mode 100644
index 0000000..9bbf9f2
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdin.c
@@ -0,0 +1,11 @@
1/* Tries to close the keyboard input stream, which must either
2 fail silently or terminate with exit code -1. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 close (0);
11}
diff --git a/pintos-progos/tests/userprog/close-stdin.ck b/pintos-progos/tests/userprog/close-stdin.ck
new file mode 100644
index 0000000..3d28507
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdin.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-stdin) begin
7(close-stdin) end
8close-stdin: exit(0)
9EOF
10(close-stdin) begin
11close-stdin: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/close-stdout.c b/pintos-progos/tests/userprog/close-stdout.c
new file mode 100644
index 0000000..886523f
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdout.c
@@ -0,0 +1,11 @@
1/* Tries to close the console output stream, which must either
2 fail silently or terminate with exit code -1. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 close (1);
11}
diff --git a/pintos-progos/tests/userprog/close-stdout.ck b/pintos-progos/tests/userprog/close-stdout.ck
new file mode 100644
index 0000000..3cbbcff
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdout.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-stdout) begin
7(close-stdout) end
8close-stdout: exit(0)
9EOF
10(close-stdout) begin
11close-stdout: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/close-twice.c b/pintos-progos/tests/userprog/close-twice.c
new file mode 100644
index 0000000..830bccf
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-twice.c
@@ -0,0 +1,18 @@
1/* Opens a file and then tries to close it twice. The second
2 close must either fail silently or terminate with exit code
3 -1. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 int handle;
13 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
14 msg ("close \"sample.txt\"");
15 close (handle);
16 msg ("close \"sample.txt\" again");
17 close (handle);
18}
diff --git a/pintos-progos/tests/userprog/close-twice.ck b/pintos-progos/tests/userprog/close-twice.ck
new file mode 100644
index 0000000..deb55a6
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-twice.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-twice) begin
7(close-twice) open "sample.txt"
8(close-twice) close "sample.txt"
9(close-twice) close "sample.txt" again
10(close-twice) end
11close-twice: exit(0)
12EOF
13(close-twice) begin
14(close-twice) open "sample.txt"
15(close-twice) close "sample.txt"
16(close-twice) close "sample.txt" again
17close-twice: exit(-1)
18EOF
19pass;
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.c b/pintos-progos/tests/userprog/create-bad-ptr.c
new file mode 100644
index 0000000..4a07bb3
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bad-ptr.c
@@ -0,0 +1,12 @@
1/* Passes a bad pointer to the create system call,
2 which must cause the process to be terminated with exit code
3 -1. */
4
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 msg ("create(0x20101234): %d", create ((char *) 0x20101234, 0));
12}
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.ck b/pintos-progos/tests/userprog/create-bad-ptr.ck
new file mode 100644
index 0000000..ac13405
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bad-ptr.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-bad-ptr) begin
7create-bad-ptr: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/create-bound.c b/pintos-progos/tests/userprog/create-bound.c
new file mode 100644
index 0000000..0a829f3
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bound.c
@@ -0,0 +1,14 @@
1/* Opens a file whose name spans the boundary between two pages.
2 This is valid, so it must succeed. */
3
4#include <syscall.h>
5#include "tests/userprog/boundary.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 msg ("create(\"quux.dat\"): %d",
13 create (copy_string_across_boundary ("quux.dat"), 0));
14}
diff --git a/pintos-progos/tests/userprog/create-bound.ck b/pintos-progos/tests/userprog/create-bound.ck
new file mode 100644
index 0000000..7656b7f
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bound.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-bound) begin
7(create-bound) create("quux.dat"): 1
8(create-bound) end
9create-bound: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/create-empty.c b/pintos-progos/tests/userprog/create-empty.c
new file mode 100644
index 0000000..fa26b43
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-empty.c
@@ -0,0 +1,10 @@
1/* Tries to create a file with the empty string as its name. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 msg ("create(\"\"): %d", create ("", 0));
10}
diff --git a/pintos-progos/tests/userprog/create-empty.ck b/pintos-progos/tests/userprog/create-empty.ck
new file mode 100644
index 0000000..93a1058
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-empty.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(create-empty) begin
7(create-empty) create(""): 0
8(create-empty) end
9create-empty: exit(0)
10EOF
11(create-empty) begin
12create-empty: exit(-1)
13EOF
14pass;
diff --git a/pintos-progos/tests/userprog/create-exists.c b/pintos-progos/tests/userprog/create-exists.c
new file mode 100644
index 0000000..d395008
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-exists.c
@@ -0,0 +1,16 @@
1/* Verifies that trying to create a file under a name that
2 already exists will fail. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (create ("quux.dat", 0), "create quux.dat");
12 CHECK (create ("warble.dat", 0), "create warble.dat");
13 CHECK (!create ("quux.dat", 0), "try to re-create quux.dat");
14 CHECK (create ("baffle.dat", 0), "create baffle.dat");
15 CHECK (!create ("warble.dat", 0), "try to re-create quux.dat");
16}
diff --git a/pintos-progos/tests/userprog/create-exists.ck b/pintos-progos/tests/userprog/create-exists.ck
new file mode 100644
index 0000000..006885e
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-exists.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-exists) begin
7(create-exists) create quux.dat
8(create-exists) create warble.dat
9(create-exists) try to re-create quux.dat
10(create-exists) create baffle.dat
11(create-exists) try to re-create quux.dat
12(create-exists) end
13create-exists: exit(0)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/create-long.c b/pintos-progos/tests/userprog/create-long.c
new file mode 100644
index 0000000..16b31bd
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-long.c
@@ -0,0 +1,17 @@
1/* Tries to create a file with a name that is much too long,
2 which must fail. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 static char name[512];
13 memset (name, 'x', sizeof name);
14 name[sizeof name - 1] = '\0';
15
16 msg ("create(\"x...\"): %d", create (name, 0));
17}
diff --git a/pintos-progos/tests/userprog/create-long.ck b/pintos-progos/tests/userprog/create-long.ck
new file mode 100644
index 0000000..628411c
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-long.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-long) begin
7(create-long) create("x..."): 0
8(create-long) end
9create-long: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/create-normal.c b/pintos-progos/tests/userprog/create-normal.c
new file mode 100644
index 0000000..3cbc463
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-normal.c
@@ -0,0 +1,10 @@
1/* Creates an ordinary empty file. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 CHECK (create ("quux.dat", 0), "create quux.dat");
10}
diff --git a/pintos-progos/tests/userprog/create-normal.ck b/pintos-progos/tests/userprog/create-normal.ck
new file mode 100644
index 0000000..ca74a6e
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-normal.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-normal) begin
7(create-normal) create quux.dat
8(create-normal) end
9create-normal: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/create-null.c b/pintos-progos/tests/userprog/create-null.c
new file mode 100644
index 0000000..287cb23
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-null.c
@@ -0,0 +1,11 @@
1/* Tries to create a file with the null pointer as its name.
2 The process must be terminated with exit code -1. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("create(NULL): %d", create (NULL, 0));
11}
diff --git a/pintos-progos/tests/userprog/create-null.ck b/pintos-progos/tests/userprog/create-null.ck
new file mode 100644
index 0000000..09b7872
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-null.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-null) begin
7create-null: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/exec-arg.c b/pintos-progos/tests/userprog/exec-arg.c
new file mode 100644
index 0000000..82d0744
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-arg.c
@@ -0,0 +1,10 @@
1/* Tests argument passing to child processes. */
2
3#include <syscall.h>
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 wait (exec ("child-args childarg"));
10}
diff --git a/pintos-progos/tests/userprog/exec-arg.ck b/pintos-progos/tests/userprog/exec-arg.ck
new file mode 100644
index 0000000..b7533ed
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-arg.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exec-arg) begin
7(args) begin
8(args) argc = 2
9(args) argv[0] = 'child-args'
10(args) argv[1] = 'childarg'
11(args) argv[2] = null
12(args) end
13child-args: exit(0)
14(exec-arg) end
15exec-arg: exit(0)
16EOF
17pass;
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.c b/pintos-progos/tests/userprog/exec-bad-ptr.c
new file mode 100644
index 0000000..0abadd3
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-bad-ptr.c
@@ -0,0 +1,11 @@
1/* Passes an invalid pointer to the exec system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 exec ((char *) 0x20101234);
11}
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.ck b/pintos-progos/tests/userprog/exec-bad-ptr.ck
new file mode 100644
index 0000000..63f5f78
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-bad-ptr.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(exec-bad-ptr) begin
7(exec-bad-ptr) end
8exec-bad-ptr: exit(0)
9EOF
10(exec-bad-ptr) begin
11exec-bad-ptr: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/exec-missing.c b/pintos-progos/tests/userprog/exec-missing.c
new file mode 100644
index 0000000..bf08cad
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-missing.c
@@ -0,0 +1,12 @@
1/* Tries to execute a nonexistent process.
2 The exec system call must return -1. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 msg ("exec(\"no-such-file\"): %d", exec ("no-such-file"));
12}
diff --git a/pintos-progos/tests/userprog/exec-missing.ck b/pintos-progos/tests/userprog/exec-missing.ck
new file mode 100644
index 0000000..0ef7aaa
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-missing.ck
@@ -0,0 +1,31 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF', <<'EOF', <<'EOF']);
6(exec-missing) begin
7load: no-such-file: open failed
8(exec-missing) exec("no-such-file"): -1
9(exec-missing) end
10exec-missing: exit(0)
11EOF
12(exec-missing) begin
13(exec-missing) exec("no-such-file"): -1
14(exec-missing) end
15exec-missing: exit(0)
16EOF
17(exec-missing) begin
18load: no-such-file: open failed
19no-such-file: exit(-1)
20(exec-missing) exec("no-such-file"): -1
21(exec-missing) end
22exec-missing: exit(0)
23EOF
24(exec-missing) begin
25load: no-such-file: open failed
26(exec-missing) exec("no-such-file"): -1
27no-such-file: exit(-1)
28(exec-missing) end
29exec-missing: exit(0)
30EOF
31pass;
diff --git a/pintos-progos/tests/userprog/exec-multiple.c b/pintos-progos/tests/userprog/exec-multiple.c
new file mode 100644
index 0000000..ba4c26e
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-multiple.c
@@ -0,0 +1,14 @@
1/* Executes and waits for multiple child processes. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 wait (exec ("child-simple"));
11 wait (exec ("child-simple"));
12 wait (exec ("child-simple"));
13 wait (exec ("child-simple"));
14}
diff --git a/pintos-progos/tests/userprog/exec-multiple.ck b/pintos-progos/tests/userprog/exec-multiple.ck
new file mode 100644
index 0000000..99624cd
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-multiple.ck
@@ -0,0 +1,18 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exec-multiple) begin
7(child-simple) run
8child-simple: exit(81)
9(child-simple) run
10child-simple: exit(81)
11(child-simple) run
12child-simple: exit(81)
13(child-simple) run
14child-simple: exit(81)
15(exec-multiple) end
16exec-multiple: exit(0)
17EOF
18pass;
diff --git a/pintos-progos/tests/userprog/exec-once.c b/pintos-progos/tests/userprog/exec-once.c
new file mode 100644
index 0000000..7bae5a1
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-once.c
@@ -0,0 +1,11 @@
1/* Executes and waits for a single child process. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 wait (exec ("child-simple"));
11}
diff --git a/pintos-progos/tests/userprog/exec-once.ck b/pintos-progos/tests/userprog/exec-once.ck
new file mode 100644
index 0000000..00b59ed
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-once.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exec-once) begin
7(child-simple) run
8child-simple: exit(81)
9(exec-once) end
10exec-once: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/exit.c b/pintos-progos/tests/userprog/exit.c
new file mode 100644
index 0000000..cb4eb8f
--- /dev/null
+++ b/pintos-progos/tests/userprog/exit.c
@@ -0,0 +1,11 @@
1/* Tests the exit system call. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 exit (57);
10 fail ("should have called exit(57)");
11}
diff --git a/pintos-progos/tests/userprog/exit.ck b/pintos-progos/tests/userprog/exit.ck
new file mode 100644
index 0000000..a552702
--- /dev/null
+++ b/pintos-progos/tests/userprog/exit.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exit) begin
7exit: exit(57)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/halt.c b/pintos-progos/tests/userprog/halt.c
new file mode 100644
index 0000000..4a99bce
--- /dev/null
+++ b/pintos-progos/tests/userprog/halt.c
@@ -0,0 +1,11 @@
1/* Tests the halt system call. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 halt ();
10 fail ("should have halted");
11}
diff --git a/pintos-progos/tests/userprog/halt.ck b/pintos-progos/tests/userprog/halt.ck
new file mode 100644
index 0000000..1b701ed
--- /dev/null
+++ b/pintos-progos/tests/userprog/halt.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5
6our ($test);
7my (@output) = read_text_file ("$test.output");
8
9common_checks ("run", @output);
10
11fail "missing 'begin' message\n"
12 if !grep ($_ eq '(halt) begin', @output);
13fail "found 'fail' message--halt didn't really halt\n"
14 if grep ($_ eq '(halt) fail', @output);
15pass;
diff --git a/pintos-progos/tests/userprog/lib/.gitignore b/pintos-progos/tests/userprog/lib/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/tests/userprog/lib/.gitignore
@@ -0,0 +1 @@
*.d
diff --git a/pintos-progos/tests/userprog/lib/user/.dummy b/pintos-progos/tests/userprog/lib/user/.dummy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pintos-progos/tests/userprog/lib/user/.dummy
diff --git a/pintos-progos/tests/userprog/lib/user/.gitignore b/pintos-progos/tests/userprog/lib/user/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/tests/userprog/lib/user/.gitignore
@@ -0,0 +1 @@
*.d
diff --git a/pintos-progos/tests/userprog/multi-child-fd.c b/pintos-progos/tests/userprog/multi-child-fd.c
new file mode 100644
index 0000000..48de4b4
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-child-fd.c
@@ -0,0 +1,25 @@
1/* Opens a file and then runs a subprocess that tries to close
2 the file. (Pintos does not have inheritance of file handles,
3 so this must fail.) The parent process then attempts to use
4 the file handle, which must succeed. */
5
6#include <stdio.h>
7#include <syscall.h>
8#include "tests/userprog/sample.inc"
9#include "tests/lib.h"
10#include "tests/main.h"
11
12void
13test_main (void)
14{
15 char child_cmd[128];
16 int handle;
17
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19
20 snprintf (child_cmd, sizeof child_cmd, "child-close %d", handle);
21
22 msg ("wait(exec()) = %d", wait (exec (child_cmd)));
23
24 check_file_handle (handle, "sample.txt", sample, sizeof sample - 1);
25}
diff --git a/pintos-progos/tests/userprog/multi-child-fd.ck b/pintos-progos/tests/userprog/multi-child-fd.ck
new file mode 100644
index 0000000..d0b3a33
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-child-fd.ck
@@ -0,0 +1,25 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(multi-child-fd) begin
7(multi-child-fd) open "sample.txt"
8(child-close) begin
9(child-close) end
10child-close: exit(0)
11(multi-child-fd) wait(exec()) = 0
12(multi-child-fd) verified contents of "sample.txt"
13(multi-child-fd) end
14multi-child-fd: exit(0)
15EOF
16(multi-child-fd) begin
17(multi-child-fd) open "sample.txt"
18(child-close) begin
19child-close: exit(-1)
20(multi-child-fd) wait(exec()) = -1
21(multi-child-fd) verified contents of "sample.txt"
22(multi-child-fd) end
23multi-child-fd: exit(0)
24EOF
25pass;
diff --git a/pintos-progos/tests/userprog/multi-recurse.c b/pintos-progos/tests/userprog/multi-recurse.c
new file mode 100644
index 0000000..7172ec3
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-recurse.c
@@ -0,0 +1,34 @@
1/* Executes itself recursively to the depth indicated by the
2 first command-line argument. */
3
4#include <debug.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <syscall.h>
8#include "tests/lib.h"
9
10const char *test_name = "multi-recurse";
11
12int
13main (int argc UNUSED, char *argv[])
14{
15 int n = atoi (argv[1]);
16
17 msg ("begin %d", n);
18 if (n != 0)
19 {
20 char child_cmd[128];
21 pid_t child_pid;
22 int code;
23
24 snprintf (child_cmd, sizeof child_cmd, "multi-recurse %d", n - 1);
25 CHECK ((child_pid = exec (child_cmd)) != -1, "exec(\"%s\")", child_cmd);
26
27 code = wait (child_pid);
28 if (code != n - 1)
29 fail ("wait(exec(\"%s\")) returned %d", child_cmd, code);
30 }
31
32 msg ("end %d", n);
33 return n;
34}
diff --git a/pintos-progos/tests/userprog/multi-recurse.ck b/pintos-progos/tests/userprog/multi-recurse.ck
new file mode 100644
index 0000000..41eb4a6
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-recurse.ck
@@ -0,0 +1,70 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(multi-recurse) begin 15
7(multi-recurse) exec("multi-recurse 14")
8(multi-recurse) begin 14
9(multi-recurse) exec("multi-recurse 13")
10(multi-recurse) begin 13
11(multi-recurse) exec("multi-recurse 12")
12(multi-recurse) begin 12
13(multi-recurse) exec("multi-recurse 11")
14(multi-recurse) begin 11
15(multi-recurse) exec("multi-recurse 10")
16(multi-recurse) begin 10
17(multi-recurse) exec("multi-recurse 9")
18(multi-recurse) begin 9
19(multi-recurse) exec("multi-recurse 8")
20(multi-recurse) begin 8
21(multi-recurse) exec("multi-recurse 7")
22(multi-recurse) begin 7
23(multi-recurse) exec("multi-recurse 6")
24(multi-recurse) begin 6
25(multi-recurse) exec("multi-recurse 5")
26(multi-recurse) begin 5
27(multi-recurse) exec("multi-recurse 4")
28(multi-recurse) begin 4
29(multi-recurse) exec("multi-recurse 3")
30(multi-recurse) begin 3
31(multi-recurse) exec("multi-recurse 2")
32(multi-recurse) begin 2
33(multi-recurse) exec("multi-recurse 1")
34(multi-recurse) begin 1
35(multi-recurse) exec("multi-recurse 0")
36(multi-recurse) begin 0
37(multi-recurse) end 0
38multi-recurse: exit(0)
39(multi-recurse) end 1
40multi-recurse: exit(1)
41(multi-recurse) end 2
42multi-recurse: exit(2)
43(multi-recurse) end 3
44multi-recurse: exit(3)
45(multi-recurse) end 4
46multi-recurse: exit(4)
47(multi-recurse) end 5
48multi-recurse: exit(5)
49(multi-recurse) end 6
50multi-recurse: exit(6)
51(multi-recurse) end 7
52multi-recurse: exit(7)
53(multi-recurse) end 8
54multi-recurse: exit(8)
55(multi-recurse) end 9
56multi-recurse: exit(9)
57(multi-recurse) end 10
58multi-recurse: exit(10)
59(multi-recurse) end 11
60multi-recurse: exit(11)
61(multi-recurse) end 12
62multi-recurse: exit(12)
63(multi-recurse) end 13
64multi-recurse: exit(13)
65(multi-recurse) end 14
66multi-recurse: exit(14)
67(multi-recurse) end 15
68multi-recurse: exit(15)
69EOF
70pass;
diff --git a/pintos-progos/tests/userprog/no-vm/Make.tests b/pintos-progos/tests/userprog/no-vm/Make.tests
new file mode 100644
index 0000000..a545e18
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/Make.tests
@@ -0,0 +1,8 @@
1# -*- makefile -*-
2
3tests/userprog/no-vm_TESTS = tests/userprog/no-vm/multi-oom
4tests/userprog/no-vm_PROGS = $(tests/userprog/no-vm_TESTS)
5tests/userprog/no-vm/multi-oom_SRC = tests/userprog/no-vm/multi-oom.c \
6tests/lib.c
7
8tests/userprog/no-vm/multi-oom.output: TIMEOUT = 360
diff --git a/pintos-progos/tests/userprog/no-vm/Rubric b/pintos-progos/tests/userprog/no-vm/Rubric
new file mode 100644
index 0000000..c3816c6
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/Rubric
@@ -0,0 +1,3 @@
1Functionality of features that VM might break:
2
31 multi-oom
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.c b/pintos-progos/tests/userprog/no-vm/multi-oom.c
new file mode 100644
index 0000000..6a4472d
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/multi-oom.c
@@ -0,0 +1,179 @@
1/* Recursively executes itself until the child fails to execute.
2 We expect that at least 30 copies can run.
3
4 We count how many children your kernel was able to execute
5 before it fails to start a new process. We require that,
6 if a process doesn't actually get to start, exec() must
7 return -1, not a valid PID.
8
9 We repeat this process 10 times, checking that your kernel
10 allows for the same level of depth every time.
11
12 In addition, some processes will spawn children that terminate
13 abnormally after allocating some resources.
14
15 Written by Godmar Back <godmar@gmail.com>
16 */
17
18#include <debug.h>
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#include <stdbool.h>
23#include <syscall.h>
24#include <random.h>
25#include "tests/lib.h"
26
27static const int EXPECTED_DEPTH_TO_PASS = 30;
28static const int EXPECTED_REPETITIONS = 10;
29
30const char *test_name = "multi-oom";
31
32enum child_termination_mode { RECURSE, CRASH };
33
34/* Spawn a recursive copy of ourselves, passing along instructions
35 for the child. */
36static pid_t
37spawn_child (int c, enum child_termination_mode mode)
38{
39 char child_cmd[128];
40 snprintf (child_cmd, sizeof child_cmd,
41 "%s %d %s", test_name, c, mode == CRASH ? "-k" : "");
42 return exec (child_cmd);
43}
44
45/* Open a number of files (and fail to close them).
46 The kernel must free any kernel resources associated
47 with these file descriptors. */
48static void
49consume_some_resources (void)
50{
51 int fd, fdmax = 126;
52
53 /* Open as many files as we can, up to fdmax.
54 Depending on how file descriptors are allocated inside
55 the kernel, open() may fail if the kernel is low on memory.
56 A low-memory condition in open() should not lead to the
57 termination of the process. */
58 for (fd = 0; fd < fdmax; fd++)
59 if (open (test_name) == -1)
60 break;
61}
62
63/* Consume some resources, then terminate this process
64 in some abnormal way. */
65static int NO_INLINE
66consume_some_resources_and_die (int seed)
67{
68 consume_some_resources ();
69 random_init (seed);
70 int *PHYS_BASE = (int *)0xC0000000;
71
72 switch (random_ulong () % 5)
73 {
74 case 0:
75 *(int *) NULL = 42;
76
77 case 1:
78 return *(int *) NULL;
79
80 case 2:
81 return *PHYS_BASE;
82
83 case 3:
84 *PHYS_BASE = 42;
85
86 case 4:
87 open ((char *)PHYS_BASE);
88 exit (-1);
89
90 default:
91 NOT_REACHED ();
92 }
93 return 0;
94}
95
96/* The first copy is invoked without command line arguments.
97 Subsequent copies are invoked with a parameter 'depth'
98 that describes how many parent processes preceded them.
99 Each process spawns one or multiple recursive copies of
100 itself, passing 'depth+1' as depth.
101
102 Some children are started with the '-k' flag, which will
103 result in abnormal termination.
104 */
105int
106main (int argc, char *argv[])
107{
108 int n;
109
110 n = argc > 1 ? atoi (argv[1]) : 0;
111 bool is_at_root = (n == 0);
112 if (is_at_root)
113 msg ("begin");
114
115 /* If -k is passed, crash this process. */
116 if (argc > 2 && !strcmp(argv[2], "-k"))
117 {
118 consume_some_resources_and_die (n);
119 NOT_REACHED ();
120 }
121
122 int howmany = is_at_root ? EXPECTED_REPETITIONS : 1;
123 int i, expected_depth = -1;
124
125 for (i = 0; i < howmany; i++)
126 {
127 pid_t child_pid;
128
129 /* Spawn a child that will be abnormally terminated.
130 To speed the test up, do this only for processes
131 spawned at a certain depth. */
132 if (n > EXPECTED_DEPTH_TO_PASS/2)
133 {
134 child_pid = spawn_child (n + 1, CRASH);
135 if (child_pid != -1)
136 {
137 if (wait (child_pid) != -1)
138 fail ("crashed child should return -1.");
139 }
140 /* If spawning this child failed, so should
141 the next spawn_child below. */
142 }
143
144 /* Now spawn the child that will recurse. */
145 child_pid = spawn_child (n + 1, RECURSE);
146
147 /* If maximum depth is reached, return result. */
148 if (child_pid == -1)
149 return n;
150
151 /* Else wait for child to report how deeply it was able to recurse. */
152 int reached_depth = wait (child_pid);
153 if (reached_depth == -1)
154 fail ("wait returned -1.");
155
156 /* Record the depth reached during the first run; on subsequent
157 runs, fail if those runs do not match the depth achieved on the
158 first run. */
159 if (i == 0)
160 expected_depth = reached_depth;
161 else if (expected_depth != reached_depth)
162 fail ("after run %d/%d, expected depth %d, actual depth %d.",
163 i, howmany, expected_depth, reached_depth);
164 ASSERT (expected_depth == reached_depth);
165 }
166
167 consume_some_resources ();
168
169 if (n == 0)
170 {
171 if (expected_depth < EXPECTED_DEPTH_TO_PASS)
172 fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS);
173 msg ("success. program forked %d times.", howmany);
174 msg ("end");
175 }
176
177 return expected_depth;
178}
179// vim: sw=2
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.ck b/pintos-progos/tests/userprog/no-vm/multi-oom.ck
new file mode 100644
index 0000000..59a0bcd
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/multi-oom.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(multi-oom) begin
7(multi-oom) success. program forked 10 times.
8(multi-oom) end
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/null.ck b/pintos-progos/tests/userprog/null.ck
new file mode 100644
index 0000000..980de35
--- /dev/null
+++ b/pintos-progos/tests/userprog/null.ck
@@ -0,0 +1,8 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6system call!
7EOF
8pass;
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.c b/pintos-progos/tests/userprog/open-bad-ptr.c
new file mode 100644
index 0000000..9cd4edf
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-bad-ptr.c
@@ -0,0 +1,13 @@
1/* Passes an invalid pointer to the open system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 msg ("open(0x20101234): %d", open ((char *) 0x20101234));
12 fail ("should have called exit(-1)");
13}
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.ck b/pintos-progos/tests/userprog/open-bad-ptr.ck
new file mode 100644
index 0000000..45349e2
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-bad-ptr.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(open-bad-ptr) begin
7(open-bad-ptr) end
8open-bad-ptr: exit(0)
9EOF
10(open-bad-ptr) begin
11open-bad-ptr: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/open-boundary.c b/pintos-progos/tests/userprog/open-boundary.c
new file mode 100644
index 0000000..cc8ff8b
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-boundary.c
@@ -0,0 +1,14 @@
1/* Creates a file whose name spans the boundary between two pages.
2 This is valid, so it must succeed. */
3
4#include <syscall.h>
5#include "tests/userprog/boundary.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 CHECK (open (copy_string_across_boundary ("sample.txt")) > 1,
13 "open \"sample.txt\"");
14}
diff --git a/pintos-progos/tests/userprog/open-boundary.ck b/pintos-progos/tests/userprog/open-boundary.ck
new file mode 100644
index 0000000..8060d22
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-boundary.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-boundary) begin
7(open-boundary) open "sample.txt"
8(open-boundary) end
9open-boundary: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/open-empty.c b/pintos-progos/tests/userprog/open-empty.c
new file mode 100644
index 0000000..3ea9907
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-empty.c
@@ -0,0 +1,13 @@
1/* Tries to open a file with the empty string as its name. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle = open ("");
11 if (handle != -1)
12 fail ("open() returned %d instead of -1", handle);
13}
diff --git a/pintos-progos/tests/userprog/open-empty.ck b/pintos-progos/tests/userprog/open-empty.ck
new file mode 100644
index 0000000..885fb41
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-empty.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-empty) begin
7(open-empty) end
8open-empty: exit(0)
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/open-missing.c b/pintos-progos/tests/userprog/open-missing.c
new file mode 100644
index 0000000..13ecbda
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-missing.c
@@ -0,0 +1,13 @@
1/* Tries to open a nonexistent file. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle = open ("no-such-file");
11 if (handle != -1)
12 fail ("open() returned %d", handle);
13}
diff --git a/pintos-progos/tests/userprog/open-missing.ck b/pintos-progos/tests/userprog/open-missing.ck
new file mode 100644
index 0000000..d72d878
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-missing.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-missing) begin
7(open-missing) end
8open-missing: exit(0)
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/open-normal.c b/pintos-progos/tests/userprog/open-normal.c
new file mode 100644
index 0000000..5132465
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-normal.c
@@ -0,0 +1,13 @@
1/* Open a file. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle = open ("sample.txt");
11 if (handle < 2)
12 fail ("open() returned %d", handle);
13}
diff --git a/pintos-progos/tests/userprog/open-normal.ck b/pintos-progos/tests/userprog/open-normal.ck
new file mode 100644
index 0000000..4f6c342
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-normal.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-normal) begin
7(open-normal) end
8open-normal: exit(0)
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/open-null.c b/pintos-progos/tests/userprog/open-null.c
new file mode 100644
index 0000000..bb418b8
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-null.c
@@ -0,0 +1,12 @@
1/* Tries to open a file with the null pointer as its name.
2 The process must be terminated with exit code -1. */
3
4#include <stddef.h>
5#include <syscall.h>
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 open (NULL);
12}
diff --git a/pintos-progos/tests/userprog/open-null.ck b/pintos-progos/tests/userprog/open-null.ck
new file mode 100644
index 0000000..b4a3bcb
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-null.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(open-null) begin
7(open-null) end
8open-null: exit(0)
9EOF
10(open-null) begin
11open-null: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/open-twice.c b/pintos-progos/tests/userprog/open-twice.c
new file mode 100644
index 0000000..dd333af
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-twice.c
@@ -0,0 +1,19 @@
1/* Tries to open the same file twice,
2 which must succeed and must return a different file descriptor
3 in each case. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 int h1 = open ("sample.txt");
13 int h2 = open ("sample.txt");
14
15 CHECK ((h1 = open ("sample.txt")) > 1, "open \"sample.txt\" once");
16 CHECK ((h2 = open ("sample.txt")) > 1, "open \"sample.txt\" again");
17 if (h1 == h2)
18 fail ("open() returned %d both times", h1);
19}
diff --git a/pintos-progos/tests/userprog/open-twice.ck b/pintos-progos/tests/userprog/open-twice.ck
new file mode 100644
index 0000000..64fa805
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-twice.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-twice) begin
7(open-twice) open "sample.txt" once
8(open-twice) open "sample.txt" again
9(open-twice) end
10open-twice: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/read-bad-fd.c b/pintos-progos/tests/userprog/read-bad-fd.c
new file mode 100644
index 0000000..a8b190d
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-fd.c
@@ -0,0 +1,21 @@
1/* Tries to read from an invalid fd,
2 which must either fail silently or terminate the process with
3 exit code -1. */
4
5#include <limits.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char buf;
14 read (0x20101234, &buf, 1);
15 read (5, &buf, 1);
16 read (1234, &buf, 1);
17 read (-1, &buf, 1);
18 read (-1024, &buf, 1);
19 read (INT_MIN, &buf, 1);
20 read (INT_MAX, &buf, 1);
21}
diff --git a/pintos-progos/tests/userprog/read-bad-fd.ck b/pintos-progos/tests/userprog/read-bad-fd.ck
new file mode 100644
index 0000000..5fedcc7
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-fd.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(read-bad-fd) begin
7(read-bad-fd) end
8read-bad-fd: exit(0)
9EOF
10(read-bad-fd) begin
11read-bad-fd: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.c b/pintos-progos/tests/userprog/read-bad-ptr.c
new file mode 100644
index 0000000..8fe756e
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-ptr.c
@@ -0,0 +1,16 @@
1/* Passes an invalid pointer to the read system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13
14 read (handle, (char *) 0xc0100000, 123);
15 fail ("should not have survived read()");
16}
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.ck b/pintos-progos/tests/userprog/read-bad-ptr.ck
new file mode 100644
index 0000000..d10accf
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-ptr.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(read-bad-ptr) begin
7(read-bad-ptr) open "sample.txt"
8(read-bad-ptr) end
9read-bad-ptr: exit(0)
10EOF
11(read-bad-ptr) begin
12(read-bad-ptr) open "sample.txt"
13read-bad-ptr: exit(-1)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/read-boundary.c b/pintos-progos/tests/userprog/read-boundary.c
new file mode 100644
index 0000000..9c19966
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-boundary.c
@@ -0,0 +1,30 @@
1/* Reads data spanning two pages in virtual address space,
2 which must succeed. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/userprog/boundary.h"
7#include "tests/userprog/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 int handle;
15 int byte_cnt;
16 char *buffer;
17
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19
20 buffer = get_boundary_area () - sizeof sample / 2;
21 byte_cnt = read (handle, buffer, sizeof sample - 1);
22 if (byte_cnt != sizeof sample - 1)
23 fail ("read() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
24 else if (strcmp (sample, buffer))
25 {
26 msg ("expected text:\n%s", sample);
27 msg ("text actually read:\n%s", buffer);
28 fail ("expected text differs from actual");
29 }
30}
diff --git a/pintos-progos/tests/userprog/read-boundary.ck b/pintos-progos/tests/userprog/read-boundary.ck
new file mode 100644
index 0000000..08dc161
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-boundary.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(read-boundary) begin
7(read-boundary) open "sample.txt"
8(read-boundary) end
9read-boundary: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/read-normal.c b/pintos-progos/tests/userprog/read-normal.c
new file mode 100644
index 0000000..16d15cc
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-normal.c
@@ -0,0 +1,11 @@
1/* Try reading a file in the most normal way. */
2
3#include "tests/userprog/sample.inc"
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 check_file ("sample.txt", sample, sizeof sample - 1);
11}
diff --git a/pintos-progos/tests/userprog/read-normal.ck b/pintos-progos/tests/userprog/read-normal.ck
new file mode 100644
index 0000000..0ed2998
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-normal.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(read-normal) begin
7(read-normal) open "sample.txt" for verification
8(read-normal) verified contents of "sample.txt"
9(read-normal) close "sample.txt"
10(read-normal) end
11read-normal: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/read-stdout.c b/pintos-progos/tests/userprog/read-stdout.c
new file mode 100644
index 0000000..d0630b9
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-stdout.c
@@ -0,0 +1,14 @@
1/* Try reading from fd 1 (stdout),
2 which may just fail or terminate the process with -1 exit
3 code. */
4
5#include <stdio.h>
6#include <syscall.h>
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char buf;
13 read (STDOUT_FILENO, &buf, 1);
14}
diff --git a/pintos-progos/tests/userprog/read-stdout.ck b/pintos-progos/tests/userprog/read-stdout.ck
new file mode 100644
index 0000000..7d87b52
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-stdout.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(read-stdout) begin
7(read-stdout) end
8read-stdout: exit(0)
9EOF
10(read-stdout) begin
11read-stdout: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/read-zero.c b/pintos-progos/tests/userprog/read-zero.c
new file mode 100644
index 0000000..e441817
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-zero.c
@@ -0,0 +1,22 @@
1/* Try a 0-byte read, which should return 0 without reading
2 anything. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle, byte_cnt;
12 char buf;
13
14 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
15
16 buf = 123;
17 byte_cnt = read (handle, &buf, 0);
18 if (byte_cnt != 0)
19 fail ("read() returned %d instead of 0", byte_cnt);
20 else if (buf != 123)
21 fail ("0-byte read() modified buffer");
22}
diff --git a/pintos-progos/tests/userprog/read-zero.ck b/pintos-progos/tests/userprog/read-zero.ck
new file mode 100644
index 0000000..8346dbc
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-zero.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(read-zero) begin
7(read-zero) open "sample.txt"
8(read-zero) end
9read-zero: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/rox-child.c b/pintos-progos/tests/userprog/rox-child.c
new file mode 100644
index 0000000..30afba2
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-child.c
@@ -0,0 +1,5 @@
1/* Ensure that the executable of a running process cannot be
2 modified, even by a child process. */
3
4#define CHILD_CNT "1"
5#include "tests/userprog/rox-child.inc"
diff --git a/pintos-progos/tests/userprog/rox-child.ck b/pintos-progos/tests/userprog/rox-child.ck
new file mode 100644
index 0000000..e6363fb
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-child.ck
@@ -0,0 +1,20 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(rox-child) begin
7(rox-child) open "child-rox"
8(rox-child) read "child-rox"
9(rox-child) write "child-rox"
10(rox-child) exec "child-rox 1"
11(child-rox) begin
12(child-rox) try to write "child-rox"
13(child-rox) try to write "child-rox"
14(child-rox) end
15child-rox: exit(12)
16(rox-child) write "child-rox"
17(rox-child) end
18rox-child: exit(0)
19EOF
20pass;
diff --git a/pintos-progos/tests/userprog/rox-child.inc b/pintos-progos/tests/userprog/rox-child.inc
new file mode 100644
index 0000000..1e2ade9
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-child.inc
@@ -0,0 +1,33 @@
1/* -*- c -*- */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 const char *child_cmd = "child-rox " CHILD_CNT;
11 int handle;
12 pid_t child;
13 char buffer[16];
14
15 /* Open child-rox, read from it, write back same data. */
16 CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\"");
17 CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer,
18 "read \"child-rox\"");
19 seek (handle, 0);
20 CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer,
21 "write \"child-rox\"");
22
23 /* Execute child-rox and wait for it. */
24 CHECK ((child = exec (child_cmd)) != -1, "exec \"%s\"", child_cmd);
25 quiet = true;
26 CHECK (wait (child) == 12, "wait for child");
27 quiet = false;
28
29 /* Write to child-rox again. */
30 seek (handle, 0);
31 CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer,
32 "write \"child-rox\"");
33}
diff --git a/pintos-progos/tests/userprog/rox-multichild.c b/pintos-progos/tests/userprog/rox-multichild.c
new file mode 100644
index 0000000..8e74dab
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-multichild.c
@@ -0,0 +1,5 @@
1/* Ensure that the executable of a running process cannot be
2 modified, even in the presence of multiple children. */
3
4#define CHILD_CNT "5"
5#include "tests/userprog/rox-child.inc"
diff --git a/pintos-progos/tests/userprog/rox-multichild.ck b/pintos-progos/tests/userprog/rox-multichild.ck
new file mode 100644
index 0000000..14b27db
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-multichild.ck
@@ -0,0 +1,44 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(rox-multichild) begin
7(rox-multichild) open "child-rox"
8(rox-multichild) read "child-rox"
9(rox-multichild) write "child-rox"
10(rox-multichild) exec "child-rox 5"
11(child-rox) begin
12(child-rox) try to write "child-rox"
13(child-rox) exec "child-rox 4"
14(child-rox) begin
15(child-rox) try to write "child-rox"
16(child-rox) exec "child-rox 3"
17(child-rox) begin
18(child-rox) try to write "child-rox"
19(child-rox) exec "child-rox 2"
20(child-rox) begin
21(child-rox) try to write "child-rox"
22(child-rox) exec "child-rox 1"
23(child-rox) begin
24(child-rox) try to write "child-rox"
25(child-rox) try to write "child-rox"
26(child-rox) end
27child-rox: exit(12)
28(child-rox) try to write "child-rox"
29(child-rox) end
30child-rox: exit(12)
31(child-rox) try to write "child-rox"
32(child-rox) end
33child-rox: exit(12)
34(child-rox) try to write "child-rox"
35(child-rox) end
36child-rox: exit(12)
37(child-rox) try to write "child-rox"
38(child-rox) end
39child-rox: exit(12)
40(rox-multichild) write "child-rox"
41(rox-multichild) end
42rox-multichild: exit(0)
43EOF
44pass;
diff --git a/pintos-progos/tests/userprog/rox-simple.c b/pintos-progos/tests/userprog/rox-simple.c
new file mode 100644
index 0000000..e84a064
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-simple.c
@@ -0,0 +1,19 @@
1/* Ensure that the executable of a running process cannot be
2 modified. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12 char buffer[16];
13
14 CHECK ((handle = open ("rox-simple")) > 1, "open \"rox-simple\"");
15 CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer,
16 "read \"rox-simple\"");
17 CHECK (write (handle, buffer, sizeof buffer) == 0,
18 "try to write \"rox-simple\"");
19}
diff --git a/pintos-progos/tests/userprog/rox-simple.ck b/pintos-progos/tests/userprog/rox-simple.ck
new file mode 100644
index 0000000..c9dcc66
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-simple.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(rox-simple) begin
7(rox-simple) open "rox-simple"
8(rox-simple) read "rox-simple"
9(rox-simple) try to write "rox-simple"
10(rox-simple) end
11rox-simple: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/sample.inc b/pintos-progos/tests/userprog/sample.inc
new file mode 100644
index 0000000..59f2bcb
--- /dev/null
+++ b/pintos-progos/tests/userprog/sample.inc
@@ -0,0 +1,6 @@
1char sample[] = {
2 "\"Amazing Electronic Fact: If you scuffed your feet long enough without\n"
3 " touching anything, you would build up so many electrons that your\n"
4 " finger would explode! But this is nothing to worry about unless you\n"
5 " have carpeting.\" --Dave Barry\n"
6};
diff --git a/pintos-progos/tests/userprog/sample.txt b/pintos-progos/tests/userprog/sample.txt
new file mode 100644
index 0000000..5050fec
--- /dev/null
+++ b/pintos-progos/tests/userprog/sample.txt
@@ -0,0 +1,4 @@
1"Amazing Electronic Fact: If you scuffed your feet long enough without
2 touching anything, you would build up so many electrons that your
3 finger would explode! But this is nothing to worry about unless you
4 have carpeting." --Dave Barry
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.c b/pintos-progos/tests/userprog/sc-bad-arg.c
new file mode 100644
index 0000000..0b512a0
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-arg.c
@@ -0,0 +1,17 @@
1/* Sticks a system call number (SYS_EXIT) at the very top of the
2 stack, then invokes a system call with the stack pointer
3 (%esp) set to its address. The process must be terminated
4 with -1 exit code because the argument to the system call
5 would be above the top of the user address space. */
6
7#include <syscall-nr.h>
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 asm volatile ("movl $0xbffffffc, %%esp; movl %0, (%%esp); int $0x30"
15 : : "i" (SYS_EXIT));
16 fail ("should have called exit(-1)");
17}
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.ck b/pintos-progos/tests/userprog/sc-bad-arg.ck
new file mode 100644
index 0000000..8981105
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-arg.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-bad-arg) begin
7sc-bad-arg: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.c b/pintos-progos/tests/userprog/sc-bad-sp.c
new file mode 100644
index 0000000..39cce84
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-sp.c
@@ -0,0 +1,20 @@
1/* Invokes a system call with the stack pointer (%esp) set to a
2 bad address. The process must be terminated with -1 exit
3 code.
4
5 For Project 3: The bad address lies approximately 64MB below
6 the code segment, so there is no ambiguity that this attempt
7 must be rejected even after stack growth is implemented.
8 Moreover, a good stack growth heuristics should probably not
9 grow the stack for the purpose of reading the system call
10 number and arguments. */
11
12#include "tests/lib.h"
13#include "tests/main.h"
14
15void
16test_main (void)
17{
18 asm volatile ("movl $.-(64*1024*1024), %esp; int $0x30");
19 fail ("should have called exit(-1)");
20}
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.ck b/pintos-progos/tests/userprog/sc-bad-sp.ck
new file mode 100644
index 0000000..498cec1
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-sp.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-bad-sp) begin
7sc-bad-sp: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.c b/pintos-progos/tests/userprog/sc-boundary-2.c
new file mode 100644
index 0000000..8acf036
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary-2.c
@@ -0,0 +1,22 @@
1/* Invokes a system call with one byte of the system call's
2 argument on a separate page from the rest of the bytes. This
3 must work. */
4
5#include <syscall-nr.h>
6#include "tests/userprog/boundary.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 /* Make one byte of a syscall argument hang over into a second
14 page. */
15 int *p = (int *) ((char *) get_boundary_area () - 7);
16 p[0] = SYS_EXIT;
17 p[1] = 67;
18
19 /* Invoke the system call. */
20 asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
21 fail ("should have called exit(67)");
22}
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.ck b/pintos-progos/tests/userprog/sc-boundary-2.ck
new file mode 100644
index 0000000..43766bf
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary-2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-boundary-2) begin
7sc-boundary-2: exit(67)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/sc-boundary.c b/pintos-progos/tests/userprog/sc-boundary.c
new file mode 100644
index 0000000..d889535
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary.c
@@ -0,0 +1,22 @@
1/* Invokes a system call with the system call number and its
2 argument on separate pages. This must work. */
3
4#include <syscall-nr.h>
5#include "tests/userprog/boundary.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 /* Put a syscall number at the end of one page
13 and its argument at the beginning of another. */
14 int *p = get_boundary_area ();
15 p--;
16 p[0] = SYS_EXIT;
17 p[1] = 42;
18
19 /* Invoke the system call. */
20 asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
21 fail ("should have called exit(42)");
22}
diff --git a/pintos-progos/tests/userprog/sc-boundary.ck b/pintos-progos/tests/userprog/sc-boundary.ck
new file mode 100644
index 0000000..3f7cbaf
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-boundary) begin
7sc-boundary: exit(42)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.c b/pintos-progos/tests/userprog/wait-bad-pid.c
new file mode 100644
index 0000000..3fe8ee4
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-bad-pid.c
@@ -0,0 +1,11 @@
1/* Waits for an invalid pid. This may fail or terminate the
2 process with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 wait ((pid_t) 0x0c020301);
11}
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.ck b/pintos-progos/tests/userprog/wait-bad-pid.ck
new file mode 100644
index 0000000..db63fb9
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-bad-pid.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(wait-bad-pid) begin
7(wait-bad-pid) end
8wait-bad-pid: exit(0)
9EOF
10(wait-bad-pid) begin
11wait-bad-pid: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/wait-killed.c b/pintos-progos/tests/userprog/wait-killed.c
new file mode 100644
index 0000000..6a2a6b5
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-killed.c
@@ -0,0 +1,11 @@
1/* Wait for a process that will be killed for bad behavior. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("wait(exec()) = %d", wait (exec ("child-bad")));
11}
diff --git a/pintos-progos/tests/userprog/wait-killed.ck b/pintos-progos/tests/userprog/wait-killed.ck
new file mode 100644
index 0000000..5df0e9c
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-killed.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(wait-killed) begin
7(child-bad) begin
8child-bad: exit(-1)
9(wait-killed) wait(exec()) = -1
10(wait-killed) end
11wait-killed: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/wait-simple.c b/pintos-progos/tests/userprog/wait-simple.c
new file mode 100644
index 0000000..d3afcf3
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-simple.c
@@ -0,0 +1,11 @@
1/* Wait for a subprocess to finish. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("wait(exec()) = %d", wait (exec ("child-simple")));
11}
diff --git a/pintos-progos/tests/userprog/wait-simple.ck b/pintos-progos/tests/userprog/wait-simple.ck
new file mode 100644
index 0000000..93dd577
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-simple.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(wait-simple) begin
7(child-simple) run
8child-simple: exit(81)
9(wait-simple) wait(exec()) = 81
10(wait-simple) end
11wait-simple: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/wait-twice.c b/pintos-progos/tests/userprog/wait-twice.c
new file mode 100644
index 0000000..785e684
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-twice.c
@@ -0,0 +1,15 @@
1/* Wait for a subprocess to finish, twice.
2 The first call must wait in the usual way and return the exit code.
3 The second wait call must return -1 immediately. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 pid_t child = exec ("child-simple");
13 msg ("wait(exec()) = %d", wait (child));
14 msg ("wait(exec()) = %d", wait (child));
15}
diff --git a/pintos-progos/tests/userprog/wait-twice.ck b/pintos-progos/tests/userprog/wait-twice.ck
new file mode 100644
index 0000000..6d53843
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-twice.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(wait-twice) begin
7(child-simple) run
8child-simple: exit(81)
9(wait-twice) wait(exec()) = 81
10(wait-twice) wait(exec()) = -1
11(wait-twice) end
12wait-twice: exit(0)
13EOF
14pass;
diff --git a/pintos-progos/tests/userprog/write-bad-fd.c b/pintos-progos/tests/userprog/write-bad-fd.c
new file mode 100644
index 0000000..f3b1151
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-fd.c
@@ -0,0 +1,20 @@
1/* Tries to write to an invalid fd,
2 which must either fail silently or terminate the process with
3 exit code -1. */
4
5#include <limits.h>
6#include <syscall.h>
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char buf = 123;
13 write (0x01012342, &buf, 1);
14 write (7, &buf, 1);
15 write (2546, &buf, 1);
16 write (-5, &buf, 1);
17 write (-8192, &buf, 1);
18 write (INT_MIN + 1, &buf, 1);
19 write (INT_MAX - 1, &buf, 1);
20}
diff --git a/pintos-progos/tests/userprog/write-bad-fd.ck b/pintos-progos/tests/userprog/write-bad-fd.ck
new file mode 100644
index 0000000..8da7a8b
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-fd.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(write-bad-fd) begin
7(write-bad-fd) end
8write-bad-fd: exit(0)
9EOF
10(write-bad-fd) begin
11write-bad-fd: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.c b/pintos-progos/tests/userprog/write-bad-ptr.c
new file mode 100644
index 0000000..5336479
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-ptr.c
@@ -0,0 +1,16 @@
1/* Passes an invalid pointer to the write system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13
14 write (handle, (char *) 0x10123420, 123);
15 fail ("should have exited with -1");
16}
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.ck b/pintos-progos/tests/userprog/write-bad-ptr.ck
new file mode 100644
index 0000000..ad9f399
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-ptr.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(write-bad-ptr) begin
7(write-bad-ptr) open "sample.txt"
8(write-bad-ptr) end
9write-bad-ptr: exit(0)
10EOF
11(write-bad-ptr) begin
12(write-bad-ptr) open "sample.txt"
13write-bad-ptr: exit(-1)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/write-boundary.c b/pintos-progos/tests/userprog/write-boundary.c
new file mode 100644
index 0000000..d2de1d4
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-boundary.c
@@ -0,0 +1,25 @@
1/* Writes data spanning two pages in virtual address space,
2 which must succeed. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/userprog/boundary.h"
7#include "tests/userprog/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 int handle;
15 int byte_cnt;
16 char *sample_p;
17
18 sample_p = copy_string_across_boundary (sample);
19
20 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
21
22 byte_cnt = write (handle, sample_p, sizeof sample - 1);
23 if (byte_cnt != sizeof sample - 1)
24 fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
25}
diff --git a/pintos-progos/tests/userprog/write-boundary.ck b/pintos-progos/tests/userprog/write-boundary.ck
new file mode 100644
index 0000000..7883781
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-boundary.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(write-boundary) begin
7(write-boundary) open "sample.txt"
8(write-boundary) end
9write-boundary: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/write-normal.c b/pintos-progos/tests/userprog/write-normal.c
new file mode 100644
index 0000000..e0297aa
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-normal.c
@@ -0,0 +1,20 @@
1/* Try writing a file in the most normal way. */
2
3#include <syscall.h>
4#include "tests/userprog/sample.inc"
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle, byte_cnt;
12
13 CHECK (create ("test.txt", sizeof sample - 1), "create \"test.txt\"");
14 CHECK ((handle = open ("test.txt")) > 1, "open \"test.txt\"");
15
16 byte_cnt = write (handle, sample, sizeof sample - 1);
17 if (byte_cnt != sizeof sample - 1)
18 fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
19}
20
diff --git a/pintos-progos/tests/userprog/write-normal.ck b/pintos-progos/tests/userprog/write-normal.ck
new file mode 100644
index 0000000..9fa6024
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-normal.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(write-normal) begin
7(write-normal) create "test.txt"
8(write-normal) open "test.txt"
9(write-normal) end
10write-normal: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/write-stdin.c b/pintos-progos/tests/userprog/write-stdin.c
new file mode 100644
index 0000000..491ea53
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-stdin.c
@@ -0,0 +1,14 @@
1/* Try writing to fd 0 (stdin),
2 which may just fail or terminate the process with -1 exit
3 code. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char buf = 123;
13 write (0, &buf, 1);
14}
diff --git a/pintos-progos/tests/userprog/write-stdin.ck b/pintos-progos/tests/userprog/write-stdin.ck
new file mode 100644
index 0000000..a6caf81
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-stdin.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(write-stdin) begin
7(write-stdin) end
8write-stdin: exit(0)
9EOF
10(write-stdin) begin
11write-stdin: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/write-zero.c b/pintos-progos/tests/userprog/write-zero.c
new file mode 100644
index 0000000..d8dac9b
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-zero.c
@@ -0,0 +1,20 @@
1/* Try a 0-byte write, which should return 0 without writing
2 anything. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle, byte_cnt;
12 char buf;
13
14 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
15
16 buf = 123;
17 byte_cnt = write (handle, &buf, 0);
18 if (byte_cnt != 0)
19 fail("write() returned %d instead of 0", byte_cnt);
20}
diff --git a/pintos-progos/tests/userprog/write-zero.ck b/pintos-progos/tests/userprog/write-zero.ck
new file mode 100644
index 0000000..cc4cd60
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-zero.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(write-zero) begin
7(write-zero) open "sample.txt"
8(write-zero) end
9write-zero: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/Grading b/pintos-progos/tests/vm/Grading
new file mode 100644
index 0000000..f0c2c13
--- /dev/null
+++ b/pintos-progos/tests/vm/Grading
@@ -0,0 +1,12 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# This project is primarily about virtual memory, but all the previous
5# functionality should work too, and it's easy to screw it up, thus
6# the equal weight placed on each.
7
850% tests/vm/Rubric.functionality
915% tests/vm/Rubric.robustness
1010% tests/userprog/Rubric.functionality
115% tests/userprog/Rubric.robustness
1220% tests/filesys/base/Rubric
diff --git a/pintos-progos/tests/vm/Make.tests b/pintos-progos/tests/vm/Make.tests
new file mode 100644
index 0000000..0307ccc
--- /dev/null
+++ b/pintos-progos/tests/vm/Make.tests
@@ -0,0 +1,107 @@
1# -*- makefile -*-
2
3tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha \
4pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code \
5pt-write-code2 pt-grow-stk-sc mmap-read \
6mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit \
7mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign \
8mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove \
9mmap-zero mmap-lazy-seq)
10
11# Deactivated
12PAGE_TESTS=page-linear page-parallel page-merge-seq \
13page-merge-par page-merge-stk page-merge-mm page-shuffle
14
15tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear \
16child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit)
17
18tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c \
19tests/cksum.c tests/lib.c tests/main.c
20tests/vm/pt-grow-pusha_SRC = tests/vm/pt-grow-pusha.c tests/lib.c \
21tests/main.c
22tests/vm/pt-grow-bad_SRC = tests/vm/pt-grow-bad.c tests/lib.c tests/main.c
23tests/vm/pt-big-stk-obj_SRC = tests/vm/pt-big-stk-obj.c tests/arc4.c \
24tests/cksum.c tests/lib.c tests/main.c
25tests/vm/pt-bad-addr_SRC = tests/vm/pt-bad-addr.c tests/lib.c tests/main.c
26tests/vm/pt-bad-read_SRC = tests/vm/pt-bad-read.c tests/lib.c tests/main.c
27tests/vm/pt-write-code_SRC = tests/vm/pt-write-code.c tests/lib.c tests/main.c
28tests/vm/pt-write-code2_SRC = tests/vm/pt-write-code-2.c tests/lib.c tests/main.c
29tests/vm/pt-grow-stk-sc_SRC = tests/vm/pt-grow-stk-sc.c tests/lib.c tests/main.c
30tests/vm/page-linear_SRC = tests/vm/page-linear.c tests/arc4.c \
31tests/lib.c tests/main.c
32tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c
33tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c \
34tests/lib.c tests/main.c
35tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \
36tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
37tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \
38tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
39tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \
40tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
41tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c \
42tests/cksum.c tests/lib.c tests/main.c
43tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c
44tests/vm/mmap-close_SRC = tests/vm/mmap-close.c tests/lib.c tests/main.c
45tests/vm/mmap-unmap_SRC = tests/vm/mmap-unmap.c tests/lib.c tests/main.c
46tests/vm/mmap-overlap_SRC = tests/vm/mmap-overlap.c tests/lib.c tests/main.c
47tests/vm/mmap-twice_SRC = tests/vm/mmap-twice.c tests/lib.c tests/main.c
48tests/vm/mmap-write_SRC = tests/vm/mmap-write.c tests/lib.c tests/main.c
49tests/vm/mmap-lazy-seq_SRC = tests/vm/mmap-lazy-seq.c tests/lib.c tests/main.c
50tests/vm/mmap-exit_SRC = tests/vm/mmap-exit.c tests/lib.c tests/main.c
51tests/vm/mmap-shuffle_SRC = tests/vm/mmap-shuffle.c tests/arc4.c \
52tests/cksum.c tests/lib.c tests/main.c
53tests/vm/mmap-bad-fd_SRC = tests/vm/mmap-bad-fd.c tests/lib.c tests/main.c
54tests/vm/mmap-clean_SRC = tests/vm/mmap-clean.c tests/lib.c tests/main.c
55tests/vm/mmap-inherit_SRC = tests/vm/mmap-inherit.c tests/lib.c tests/main.c
56tests/vm/mmap-misalign_SRC = tests/vm/mmap-misalign.c tests/lib.c \
57tests/main.c
58tests/vm/mmap-null_SRC = tests/vm/mmap-null.c tests/lib.c tests/main.c
59tests/vm/mmap-over-code_SRC = tests/vm/mmap-over-code.c tests/lib.c \
60tests/main.c
61tests/vm/mmap-over-data_SRC = tests/vm/mmap-over-data.c tests/lib.c \
62tests/main.c
63tests/vm/mmap-over-stk_SRC = tests/vm/mmap-over-stk.c tests/lib.c tests/main.c
64tests/vm/mmap-remove_SRC = tests/vm/mmap-remove.c tests/lib.c tests/main.c
65tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c
66
67tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c
68tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c
69tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \
70tests/lib.c
71tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c
72tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c
73tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c
74
75tests/vm/pt-bad-read_PUTFILES = tests/vm/sample.txt
76tests/vm/pt-write-code2_PUTFILES = tests/vm/sample.txt
77tests/vm/mmap-close_PUTFILES = tests/vm/sample.txt
78tests/vm/mmap-read_PUTFILES = tests/vm/sample.txt
79tests/vm/mmap-unmap_PUTFILES = tests/vm/sample.txt
80tests/vm/mmap-twice_PUTFILES = tests/vm/sample.txt
81tests/vm/mmap-overlap_PUTFILES = tests/vm/zeros
82tests/vm/mmap-exit_PUTFILES = tests/vm/child-mm-wrt
83tests/vm/page-parallel_PUTFILES = tests/vm/child-linear
84tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort
85tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort
86tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort
87tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm
88tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt
89tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit
90tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt
91tests/vm/mmap-null_PUTFILES = tests/vm/sample.txt
92tests/vm/mmap-over-code_PUTFILES = tests/vm/sample.txt
93tests/vm/mmap-over-data_PUTFILES = tests/vm/sample.txt
94tests/vm/mmap-over-stk_PUTFILES = tests/vm/sample.txt
95tests/vm/mmap-remove_PUTFILES = tests/vm/sample.txt
96
97tests/vm/page-linear.output: TIMEOUT = 300
98tests/vm/page-shuffle.output: TIMEOUT = 600
99tests/vm/mmap-shuffle.output: TIMEOUT = 600
100tests/vm/page-merge-seq.output: TIMEOUT = 600
101tests/vm/page-merge-par.output: TIMEOUT = 600
102
103tests/vm/zeros:
104 dd if=/dev/zero of=$@ bs=1024 count=6
105
106clean::
107 rm -f tests/vm/zeros
diff --git a/pintos-progos/tests/vm/Rubric.functionality b/pintos-progos/tests/vm/Rubric.functionality
new file mode 100644
index 0000000..ede0221
--- /dev/null
+++ b/pintos-progos/tests/vm/Rubric.functionality
@@ -0,0 +1,21 @@
1Functionality of virtual memory subsystem:
2- Test stack growth.
33 pt-grow-stack
43 pt-grow-stk-sc
53 pt-big-stk-obj
63 pt-grow-pusha
7
8- Test "mmap" system call.
92 mmap-read
102 mmap-write
112 mmap-shuffle
12
132 mmap-twice
14
152 mmap-unmap
161 mmap-exit
17
183 mmap-clean
19
202 mmap-close
212 mmap-remove
diff --git a/pintos-progos/tests/vm/Rubric.paging b/pintos-progos/tests/vm/Rubric.paging
new file mode 100644
index 0000000..cf1d871
--- /dev/null
+++ b/pintos-progos/tests/vm/Rubric.paging
@@ -0,0 +1,8 @@
1- Test paging behavior.
23 page-linear
33 page-parallel
43 page-shuffle
54 page-merge-seq
64 page-merge-par
74 page-merge-mm
84 page-merge-stk
diff --git a/pintos-progos/tests/vm/Rubric.robustness b/pintos-progos/tests/vm/Rubric.robustness
new file mode 100644
index 0000000..eca0af4
--- /dev/null
+++ b/pintos-progos/tests/vm/Rubric.robustness
@@ -0,0 +1,21 @@
1Robustness of virtual memory subsystem:
2- Test robustness of page table support.
32 pt-bad-addr
43 pt-bad-read
52 pt-write-code
63 pt-write-code2
74 pt-grow-bad
8
9- Test robustness of "mmap" system call.
101 mmap-bad-fd
111 mmap-inherit
121 mmap-null
131 mmap-zero
14
152 mmap-misalign
16
172 mmap-over-code
182 mmap-over-data
192 mmap-over-stk
202 mmap-overlap
214 mmap-lazy-seq
diff --git a/pintos-progos/tests/vm/child-inherit.c b/pintos-progos/tests/vm/child-inherit.c
new file mode 100644
index 0000000..d3186a1
--- /dev/null
+++ b/pintos-progos/tests/vm/child-inherit.c
@@ -0,0 +1,16 @@
1/* Child process for mmap-inherit test.
2 Tries to write to a mapping present in the parent.
3 The process must be terminated with -1 exit code. */
4
5#include <string.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 memset ((char *) 0x54321000, 0, 4096);
14 fail ("child can modify parent's memory mappings");
15}
16
diff --git a/pintos-progos/tests/vm/child-linear.c b/pintos-progos/tests/vm/child-linear.c
new file mode 100644
index 0000000..eca3e3f
--- /dev/null
+++ b/pintos-progos/tests/vm/child-linear.c
@@ -0,0 +1,36 @@
1/* Child process of page-parallel.
2 Encrypts 1 MB of zeros, then decrypts it, and ensures that
3 the zeros are back. */
4
5#include <string.h>
6#include "tests/arc4.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10const char *test_name = "child-linear";
11
12#define SIZE (1024 * 1024)
13static char buf[SIZE];
14
15int
16main (int argc, char *argv[])
17{
18 const char *key = argv[argc - 1];
19 struct arc4 arc4;
20 size_t i;
21
22 /* Encrypt zeros. */
23 arc4_init (&arc4, key, strlen (key));
24 arc4_crypt (&arc4, buf, SIZE);
25
26 /* Decrypt back to zeros. */
27 arc4_init (&arc4, key, strlen (key));
28 arc4_crypt (&arc4, buf, SIZE);
29
30 /* Check that it's all zeros. */
31 for (i = 0; i < SIZE; i++)
32 if (buf[i] != '\0')
33 fail ("byte %zu != 0", i);
34
35 return 0x42;
36}
diff --git a/pintos-progos/tests/vm/child-mm-wrt.c b/pintos-progos/tests/vm/child-mm-wrt.c
new file mode 100644
index 0000000..8419788
--- /dev/null
+++ b/pintos-progos/tests/vm/child-mm-wrt.c
@@ -0,0 +1,24 @@
1/* Child process of mmap-exit.
2 Mmaps a file and writes to it via the mmap'ing, then exits
3 without calling munmap. The data in the mapped region must be
4 written out at program termination. */
5
6#include <string.h>
7#include <syscall.h>
8#include "tests/vm/sample.inc"
9#include "tests/lib.h"
10#include "tests/main.h"
11
12#define ACTUAL ((void *) 0x10000000)
13
14void
15test_main (void)
16{
17 int handle;
18
19 CHECK (create ("sample.txt", sizeof sample), "create \"sample.txt\"");
20 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
21 CHECK (mmap (handle, ACTUAL) != MAP_FAILED, "mmap \"sample.txt\"");
22 memcpy (ACTUAL, sample, sizeof sample);
23}
24
diff --git a/pintos-progos/tests/vm/child-qsort-mm.c b/pintos-progos/tests/vm/child-qsort-mm.c
new file mode 100644
index 0000000..db45499
--- /dev/null
+++ b/pintos-progos/tests/vm/child-qsort-mm.c
@@ -0,0 +1,25 @@
1/* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort,
2 a multi-pass divide and conquer algorithm. */
3
4#include <debug.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8#include "tests/vm/qsort.h"
9
10const char *test_name = "child-qsort-mm";
11
12int
13main (int argc UNUSED, char *argv[])
14{
15 int handle;
16 unsigned char *p = (unsigned char *) 0x10000000;
17
18 quiet = true;
19
20 CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
21 CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]);
22 qsort_bytes (p, 1024 * 128);
23
24 return 80;
25}
diff --git a/pintos-progos/tests/vm/child-qsort.c b/pintos-progos/tests/vm/child-qsort.c
new file mode 100644
index 0000000..355f4eb
--- /dev/null
+++ b/pintos-progos/tests/vm/child-qsort.c
@@ -0,0 +1,32 @@
1/* Reads a 128 kB file onto the stack and "sorts" the bytes in
2 it, using quick sort, a multi-pass divide and conquer
3 algorithm. The sorted data is written back to the same file
4 in-place. */
5
6#include <debug.h>
7#include <syscall.h>
8#include "tests/lib.h"
9#include "tests/main.h"
10#include "tests/vm/qsort.h"
11
12const char *test_name = "child-qsort";
13
14int
15main (int argc UNUSED, char *argv[])
16{
17 int handle;
18 unsigned char buf[128 * 1024];
19 size_t size;
20
21 quiet = true;
22
23 CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
24
25 size = read (handle, buf, sizeof buf);
26 qsort_bytes (buf, sizeof buf);
27 seek (handle, 0);
28 write (handle, buf, size);
29 close (handle);
30
31 return 72;
32}
diff --git a/pintos-progos/tests/vm/child-sort.c b/pintos-progos/tests/vm/child-sort.c
new file mode 100644
index 0000000..dff2c77
--- /dev/null
+++ b/pintos-progos/tests/vm/child-sort.c
@@ -0,0 +1,42 @@
1/* Reads a 128 kB file into static data and "sorts" the bytes in
2 it, using counting sort, a single-pass algorithm. The sorted
3 data is written back to the same file in-place. */
4
5#include <debug.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10const char *test_name = "child-sort";
11
12unsigned char buf[128 * 1024];
13size_t histogram[256];
14
15int
16main (int argc UNUSED, char *argv[])
17{
18 int handle;
19 unsigned char *p;
20 size_t size;
21 size_t i;
22
23 quiet = true;
24
25 CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
26
27 size = read (handle, buf, sizeof buf);
28 for (i = 0; i < size; i++)
29 histogram[buf[i]]++;
30 p = buf;
31 for (i = 0; i < sizeof histogram / sizeof *histogram; i++)
32 {
33 size_t j = histogram[i];
34 while (j-- > 0)
35 *p++ = i;
36 }
37 seek (handle, 0);
38 write (handle, buf, size);
39 close (handle);
40
41 return 123;
42}
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.c b/pintos-progos/tests/vm/mmap-bad-fd.c
new file mode 100644
index 0000000..76a7b50
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-bad-fd.c
@@ -0,0 +1,15 @@
1/* Tries to mmap an invalid fd,
2 which must either fail silently or terminate the process with
3 exit code -1. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 CHECK (mmap (0x5678, (void *) 0x10000000) == MAP_FAILED,
13 "try to mmap invalid fd");
14}
15
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.ck b/pintos-progos/tests/vm/mmap-bad-fd.ck
new file mode 100644
index 0000000..f3f58d5
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-bad-fd.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(mmap-bad-fd) begin
7(mmap-bad-fd) try to mmap invalid fd
8(mmap-bad-fd) end
9mmap-bad-fd: exit(0)
10EOF
11(mmap-bad-fd) begin
12(mmap-bad-fd) try to mmap invalid fd
13mmap-bad-fd: exit(-1)
14EOF
15pass;
diff --git a/pintos-progos/tests/vm/mmap-clean.c b/pintos-progos/tests/vm/mmap-clean.c
new file mode 100644
index 0000000..ea1dc9c
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-clean.c
@@ -0,0 +1,53 @@
1/* Verifies that mmap'd regions are only written back on munmap
2 if the data was actually modified in memory. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 static const char overwrite[] = "Now is the time for all good...";
14 static char buffer[sizeof sample - 1];
15 char *actual = (char *) 0x54321000;
16 int handle;
17 mapid_t map;
18
19 /* Open file, map, verify data. */
20 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
21 CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
22 if (memcmp (actual, sample, strlen (sample)))
23 fail ("read of mmap'd file reported bad data");
24
25 /* Modify file. */
26 CHECK (write (handle, overwrite, strlen (overwrite))
27 == (int) strlen (overwrite),
28 "write \"sample.txt\"");
29
30 /* Close mapping. Data should not be written back, because we
31 didn't modify it via the mapping. */
32 msg ("munmap \"sample.txt\"");
33 munmap (map);
34
35 /* Read file back. */
36 msg ("seek \"sample.txt\"");
37 seek (handle, 0);
38 CHECK (read (handle, buffer, sizeof buffer) == sizeof buffer,
39 "read \"sample.txt\"");
40
41 /* Verify that file overwrite worked. */
42 if (memcmp (buffer, overwrite, strlen (overwrite))
43 || memcmp (buffer + strlen (overwrite), sample + strlen (overwrite),
44 strlen (sample) - strlen (overwrite)))
45 {
46 if (!memcmp (buffer, sample, strlen (sample)))
47 fail ("munmap wrote back clean page");
48 else
49 fail ("read surprising data from file");
50 }
51 else
52 msg ("file change was retained after munmap");
53}
diff --git a/pintos-progos/tests/vm/mmap-clean.ck b/pintos-progos/tests/vm/mmap-clean.ck
new file mode 100644
index 0000000..1666d6c
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-clean.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-clean) begin
7(mmap-clean) open "sample.txt"
8(mmap-clean) mmap "sample.txt"
9(mmap-clean) write "sample.txt"
10(mmap-clean) munmap "sample.txt"
11(mmap-clean) seek "sample.txt"
12(mmap-clean) read "sample.txt"
13(mmap-clean) file change was retained after munmap
14(mmap-clean) end
15EOF
16pass;
diff --git a/pintos-progos/tests/vm/mmap-close.c b/pintos-progos/tests/vm/mmap-close.c
new file mode 100644
index 0000000..d016ee3
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-close.c
@@ -0,0 +1,27 @@
1/* Verifies that memory mappings persist after file close. */
2
3#include <string.h>
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/arc4.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10#define ACTUAL ((void *) 0x10000000)
11
12void
13test_main (void)
14{
15 int handle;
16 mapid_t map;
17
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19 CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
20
21 close (handle);
22
23 if (memcmp (ACTUAL, sample, strlen (sample)))
24 fail ("read of mmap'd file reported bad data");
25
26 munmap (map);
27}
diff --git a/pintos-progos/tests/vm/mmap-close.ck b/pintos-progos/tests/vm/mmap-close.ck
new file mode 100644
index 0000000..d15e41a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-close.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-close) begin
7(mmap-close) open "sample.txt"
8(mmap-close) mmap "sample.txt"
9(mmap-close) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-exit.c b/pintos-progos/tests/vm/mmap-exit.c
new file mode 100644
index 0000000..7a2278a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-exit.c
@@ -0,0 +1,22 @@
1/* Executes child-mm-wrt and verifies that the writes that should
2 have occurred really did. */
3
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 pid_t child;
13
14 /* Make child write file. */
15 quiet = true;
16 CHECK ((child = exec ("child-mm-wrt")) != -1, "exec \"child-mm-wrt\"");
17 CHECK (wait (child) == 0, "wait for child (should return 0)");
18 quiet = false;
19
20 /* Check file contents. */
21 check_file ("sample.txt", sample, sizeof sample);
22}
diff --git a/pintos-progos/tests/vm/mmap-exit.ck b/pintos-progos/tests/vm/mmap-exit.ck
new file mode 100644
index 0000000..457d34a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-exit.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-exit) begin
7(child-mm-wrt) begin
8(child-mm-wrt) create "sample.txt"
9(child-mm-wrt) open "sample.txt"
10(child-mm-wrt) mmap "sample.txt"
11(child-mm-wrt) end
12(mmap-exit) open "sample.txt" for verification
13(mmap-exit) verified contents of "sample.txt"
14(mmap-exit) close "sample.txt"
15(mmap-exit) end
16EOF
17pass;
diff --git a/pintos-progos/tests/vm/mmap-inherit.c b/pintos-progos/tests/vm/mmap-inherit.c
new file mode 100644
index 0000000..7fa9607
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-inherit.c
@@ -0,0 +1,32 @@
1/* Maps a file into memory and runs child-inherit to verify that
2 mappings are not inherited. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *actual = (char *) 0x54321000;
14 int handle;
15 pid_t child;
16
17 /* Open file, map, verify data. */
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19 CHECK (mmap (handle, actual) != MAP_FAILED, "mmap \"sample.txt\"");
20 if (memcmp (actual, sample, strlen (sample)))
21 fail ("read of mmap'd file reported bad data");
22
23 /* Spawn child and wait. */
24 CHECK ((child = exec ("child-inherit")) != -1, "exec \"child-inherit\"");
25 quiet = true;
26 CHECK (wait (child) == -1, "wait for child (should return -1)");
27 quiet = false;
28
29 /* Verify data again. */
30 CHECK (!memcmp (actual, sample, strlen (sample)),
31 "checking that mmap'd file still has same data");
32}
diff --git a/pintos-progos/tests/vm/mmap-inherit.ck b/pintos-progos/tests/vm/mmap-inherit.ck
new file mode 100644
index 0000000..c54638a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-inherit.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(mmap-inherit) begin
7(mmap-inherit) open "sample.txt"
8(mmap-inherit) mmap "sample.txt"
9(mmap-inherit) exec "child-inherit"
10(child-inherit) begin
11child-inherit: exit(-1)
12(mmap-inherit) checking that mmap'd file still has same data
13(mmap-inherit) end
14mmap-inherit: exit(0)
15EOF
16pass;
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.c b/pintos-progos/tests/vm/mmap-lazy-seq.c
new file mode 100644
index 0000000..b8f07bd
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-lazy-seq.c
@@ -0,0 +1,52 @@
1/* Create a large file, and mmap it several times, writing to
2 different pages. Then unmaps the file, and reads the data back
3 to verify */
4
5#include <string.h>
6#include <syscall.h>
7#include "tests/vm/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11/* Offset needs to be larger or equal to page size */
12#define OFFSET(i) (8192*(i))
13/* Number of times file is mmapped */
14#define N (8)
15/* Size of file */
16#define FILE_SIZE (1024*1024)
17/* Address for mmap */
18#define ACTUAL(i) ((void *) (0x10000000 + (i)*FILE_SIZE))
19
20
21void
22test_main (void)
23{
24 int i;
25 int handle;
26 mapid_t map[N];
27 char buf[1024];
28 /* create file */
29 CHECK (create ("sample.txt", FILE_SIZE), "create \"sample.txt\"");
30 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
31 /* mmap */
32 for (i = 0; i < N; i++) {
33 CHECK ((map[i] = mmap (handle, ACTUAL(i))) != MAP_FAILED, "mmap \"sample.txt\"");
34 }
35 /* write */
36 for (i = 0; i < N; i++) {
37 memcpy (buf, ACTUAL(i)+OFFSET(i+N), 1024); /* not checked */
38 memcpy (ACTUAL(i)+OFFSET(i), sample, strlen (sample));
39 }
40 /* munmap */
41 for (i = 0; i < N; i++) {
42 munmap (map[i]);
43 }
44 /* Read back via read(). */
45 for (i = 0; i < N; i++) {
46 seek (handle, OFFSET(i));
47 read (handle, buf, strlen (sample));
48 CHECK (!memcmp (buf, sample, strlen (sample)),
49 "compare read data against written data");
50 }
51 close (handle);
52}
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.ck b/pintos-progos/tests/vm/mmap-lazy-seq.ck
new file mode 100644
index 0000000..dd0e240
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-lazy-seq.ck
@@ -0,0 +1,27 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-lazy-seq) begin
7(mmap-lazy-seq) create "sample.txt"
8(mmap-lazy-seq) open "sample.txt"
9(mmap-lazy-seq) mmap "sample.txt"
10(mmap-lazy-seq) mmap "sample.txt"
11(mmap-lazy-seq) mmap "sample.txt"
12(mmap-lazy-seq) mmap "sample.txt"
13(mmap-lazy-seq) mmap "sample.txt"
14(mmap-lazy-seq) mmap "sample.txt"
15(mmap-lazy-seq) mmap "sample.txt"
16(mmap-lazy-seq) mmap "sample.txt"
17(mmap-lazy-seq) compare read data against written data
18(mmap-lazy-seq) compare read data against written data
19(mmap-lazy-seq) compare read data against written data
20(mmap-lazy-seq) compare read data against written data
21(mmap-lazy-seq) compare read data against written data
22(mmap-lazy-seq) compare read data against written data
23(mmap-lazy-seq) compare read data against written data
24(mmap-lazy-seq) compare read data against written data
25(mmap-lazy-seq) end
26EOF
27pass;
diff --git a/pintos-progos/tests/vm/mmap-misalign.c b/pintos-progos/tests/vm/mmap-misalign.c
new file mode 100644
index 0000000..34141a9
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-misalign.c
@@ -0,0 +1,16 @@
1/* Verifies that misaligned memory mappings are disallowed. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13 CHECK (mmap (handle, (void *) 0x10001234) == MAP_FAILED,
14 "try to mmap at misaligned address");
15}
16
diff --git a/pintos-progos/tests/vm/mmap-misalign.ck b/pintos-progos/tests/vm/mmap-misalign.ck
new file mode 100644
index 0000000..145a2e8
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-misalign.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-misalign) begin
7(mmap-misalign) open "sample.txt"
8(mmap-misalign) try to mmap at misaligned address
9(mmap-misalign) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-null.c b/pintos-progos/tests/vm/mmap-null.c
new file mode 100644
index 0000000..f8ef075
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-null.c
@@ -0,0 +1,15 @@
1/* Verifies that memory mappings at address 0 are disallowed. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13 CHECK (mmap (handle, NULL) == MAP_FAILED, "try to mmap at address 0");
14}
15
diff --git a/pintos-progos/tests/vm/mmap-null.ck b/pintos-progos/tests/vm/mmap-null.ck
new file mode 100644
index 0000000..aacdd65
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-null.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-null) begin
7(mmap-null) open "sample.txt"
8(mmap-null) try to mmap at address 0
9(mmap-null) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-over-code.c b/pintos-progos/tests/vm/mmap-over-code.c
new file mode 100644
index 0000000..d3619a3
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-code.c
@@ -0,0 +1,19 @@
1/* Verifies that mapping over the code segment is disallowed. */
2
3#include <stdint.h>
4#include <round.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 uintptr_t test_main_page = ROUND_DOWN ((uintptr_t) test_main, 4096);
13 int handle;
14
15 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
16 CHECK (mmap (handle, (void *) test_main_page) == MAP_FAILED,
17 "try to mmap over code segment");
18}
19
diff --git a/pintos-progos/tests/vm/mmap-over-code.ck b/pintos-progos/tests/vm/mmap-over-code.ck
new file mode 100644
index 0000000..b5b23c7
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-code.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-over-code) begin
7(mmap-over-code) open "sample.txt"
8(mmap-over-code) try to mmap over code segment
9(mmap-over-code) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-over-data.c b/pintos-progos/tests/vm/mmap-over-data.c
new file mode 100644
index 0000000..9ea5d49
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-data.c
@@ -0,0 +1,21 @@
1/* Verifies that mapping over the data segment is disallowed. */
2
3#include <stdint.h>
4#include <round.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static char x;
10
11void
12test_main (void)
13{
14 uintptr_t x_page = ROUND_DOWN ((uintptr_t) &x, 4096);
15 int handle;
16
17 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
18 CHECK (mmap (handle, (void *) x_page) == MAP_FAILED,
19 "try to mmap over data segment");
20}
21
diff --git a/pintos-progos/tests/vm/mmap-over-data.ck b/pintos-progos/tests/vm/mmap-over-data.ck
new file mode 100644
index 0000000..98770cc
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-data.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-over-data) begin
7(mmap-over-data) open "sample.txt"
8(mmap-over-data) try to mmap over data segment
9(mmap-over-data) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-over-stk.c b/pintos-progos/tests/vm/mmap-over-stk.c
new file mode 100644
index 0000000..4e241e8
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-stk.c
@@ -0,0 +1,19 @@
1/* Verifies that mapping over the stack segment is disallowed. */
2
3#include <stdint.h>
4#include <round.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 int handle;
13 uintptr_t handle_page = ROUND_DOWN ((uintptr_t) &handle, 4096);
14
15 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
16 CHECK (mmap (handle, (void *) handle_page) == MAP_FAILED,
17 "try to mmap over stack segment");
18}
19
diff --git a/pintos-progos/tests/vm/mmap-over-stk.ck b/pintos-progos/tests/vm/mmap-over-stk.ck
new file mode 100644
index 0000000..e6880cf
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-stk.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-over-stk) begin
7(mmap-over-stk) open "sample.txt"
8(mmap-over-stk) try to mmap over stack segment
9(mmap-over-stk) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-overlap.c b/pintos-progos/tests/vm/mmap-overlap.c
new file mode 100644
index 0000000..668ae5f
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-overlap.c
@@ -0,0 +1,20 @@
1/* Verifies that overlapping memory mappings are disallowed. */
2
3#include <syscall.h>
4#include "tests/vm/sample.inc"
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 char *start = (char *) 0x10000000;
12 int fd[2];
13
14 CHECK ((fd[0] = open ("zeros")) > 1, "open \"zeros\" once");
15 CHECK (mmap (fd[0], start) != MAP_FAILED, "mmap \"zeros\"");
16 CHECK ((fd[1] = open ("zeros")) > 1 && fd[0] != fd[1],
17 "open \"zeros\" again");
18 CHECK (mmap (fd[1], start + 4096) == MAP_FAILED,
19 "try to mmap \"zeros\" again");
20}
diff --git a/pintos-progos/tests/vm/mmap-overlap.ck b/pintos-progos/tests/vm/mmap-overlap.ck
new file mode 100644
index 0000000..f13801e
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-overlap.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-overlap) begin
7(mmap-overlap) open "zeros" once
8(mmap-overlap) mmap "zeros"
9(mmap-overlap) open "zeros" again
10(mmap-overlap) try to mmap "zeros" again
11(mmap-overlap) end
12EOF
13pass;
diff --git a/pintos-progos/tests/vm/mmap-read.c b/pintos-progos/tests/vm/mmap-read.c
new file mode 100644
index 0000000..c0f23a1
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-read.c
@@ -0,0 +1,32 @@
1/* Uses a memory mapping to read a file. */
2
3#include <string.h>
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char *actual = (char *) 0x10000000;
13 int handle;
14 mapid_t map;
15 size_t i;
16
17 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
18 CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
19
20 /* Check that data is correct. */
21 if (memcmp (actual, sample, strlen (sample)))
22 fail ("read of mmap'd file reported bad data");
23
24 /* Verify that data is followed by zeros. */
25 for (i = strlen (sample); i < 4096; i++)
26 if (actual[i] != 0)
27 fail ("byte %zu of mmap'd region has value %02hhx (should be 0)",
28 i, actual[i]);
29
30 munmap (map);
31 close (handle);
32}
diff --git a/pintos-progos/tests/vm/mmap-read.ck b/pintos-progos/tests/vm/mmap-read.ck
new file mode 100644
index 0000000..95ab790
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-read.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-read) begin
7(mmap-read) open "sample.txt"
8(mmap-read) mmap "sample.txt"
9(mmap-read) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-remove.c b/pintos-progos/tests/vm/mmap-remove.c
new file mode 100644
index 0000000..5f7444d
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-remove.c
@@ -0,0 +1,43 @@
1/* Deletes and closes file that is mapped into memory
2 and verifies that it can still be read through the mapping. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *actual = (char *) 0x10000000;
14 int handle;
15 mapid_t map;
16 size_t i;
17
18 /* Map file. */
19 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
20 CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
21
22 /* Close file and delete it. */
23 close (handle);
24 CHECK (remove ("sample.txt"), "remove \"sample.txt\"");
25 CHECK (open ("sample.txt") == -1, "try to open \"sample.txt\"");
26
27 /* Create a new file in hopes of overwriting data from the old
28 one, in case the file system has incorrectly freed the
29 file's data. */
30 CHECK (create ("another", 4096 * 10), "create \"another\"");
31
32 /* Check that mapped data is correct. */
33 if (memcmp (actual, sample, strlen (sample)))
34 fail ("read of mmap'd file reported bad data");
35
36 /* Verify that data is followed by zeros. */
37 for (i = strlen (sample); i < 4096; i++)
38 if (actual[i] != 0)
39 fail ("byte %zu of mmap'd region has value %02hhx (should be 0)",
40 i, actual[i]);
41
42 munmap (map);
43}
diff --git a/pintos-progos/tests/vm/mmap-remove.ck b/pintos-progos/tests/vm/mmap-remove.ck
new file mode 100644
index 0000000..d3cc938
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-remove.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-remove) begin
7(mmap-remove) open "sample.txt"
8(mmap-remove) mmap "sample.txt"
9(mmap-remove) remove "sample.txt"
10(mmap-remove) try to open "sample.txt"
11(mmap-remove) create "another"
12(mmap-remove) end
13EOF
14pass;
diff --git a/pintos-progos/tests/vm/mmap-shuffle.c b/pintos-progos/tests/vm/mmap-shuffle.c
new file mode 100644
index 0000000..29921ad
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-shuffle.c
@@ -0,0 +1,38 @@
1/* Creates a 128 kB file and repeatedly shuffles data in it
2 through a memory mapping. */
3
4#include <stdio.h>
5#include <string.h>
6#include <syscall.h>
7#include "tests/arc4.h"
8#include "tests/cksum.h"
9#include "tests/lib.h"
10#include "tests/main.h"
11
12#define SIZE (128 * 1024)
13
14static char *buf = (char *) 0x10000000;
15
16void
17test_main (void)
18{
19 size_t i;
20 int handle;
21
22 /* Create file, mmap. */
23 CHECK (create ("buffer", SIZE), "create \"buffer\"");
24 CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
25 CHECK (mmap (handle, buf) != MAP_FAILED, "mmap \"buffer\"");
26
27 /* Initialize. */
28 for (i = 0; i < SIZE; i++)
29 buf[i] = i * 257;
30 msg ("init: cksum=%lu", cksum (buf, SIZE));
31
32 /* Shuffle repeatedly. */
33 for (i = 0; i < 10; i++)
34 {
35 shuffle (buf, SIZE, 1);
36 msg ("shuffle %zu: cksum=%lu", i, cksum (buf, SIZE));
37 }
38}
diff --git a/pintos-progos/tests/vm/mmap-shuffle.ck b/pintos-progos/tests/vm/mmap-shuffle.ck
new file mode 100644
index 0000000..c158301
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-shuffle.ck
@@ -0,0 +1,47 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::cksum;
6use tests::lib;
7
8my ($init, @shuffle);
9if (1) {
10 # Use precalculated values.
11 $init = 3115322833;
12 @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467,
13 3022003332, 3614934266, 2704001777, 735775156, 1864109763);
14} else {
15 # Recalculate values.
16 my ($buf) = "";
17 for my $i (0...128 * 1024 - 1) {
18 $buf .= chr (($i * 257) & 0xff);
19 }
20 $init = cksum ($buf);
21
22 random_init (0);
23 for my $i (1...10) {
24 $buf = shuffle ($buf, length ($buf), 1);
25 push (@shuffle, cksum ($buf));
26 }
27}
28
29check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]);
30(mmap-shuffle) begin
31(mmap-shuffle) create "buffer"
32(mmap-shuffle) open "buffer"
33(mmap-shuffle) mmap "buffer"
34(mmap-shuffle) init: cksum=$init
35(mmap-shuffle) shuffle 0: cksum=$shuffle[0]
36(mmap-shuffle) shuffle 1: cksum=$shuffle[1]
37(mmap-shuffle) shuffle 2: cksum=$shuffle[2]
38(mmap-shuffle) shuffle 3: cksum=$shuffle[3]
39(mmap-shuffle) shuffle 4: cksum=$shuffle[4]
40(mmap-shuffle) shuffle 5: cksum=$shuffle[5]
41(mmap-shuffle) shuffle 6: cksum=$shuffle[6]
42(mmap-shuffle) shuffle 7: cksum=$shuffle[7]
43(mmap-shuffle) shuffle 8: cksum=$shuffle[8]
44(mmap-shuffle) shuffle 9: cksum=$shuffle[9]
45(mmap-shuffle) end
46EOF
47pass;
diff --git a/pintos-progos/tests/vm/mmap-twice.c b/pintos-progos/tests/vm/mmap-twice.c
new file mode 100644
index 0000000..d277a37
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-twice.c
@@ -0,0 +1,28 @@
1/* Maps the same file into memory twice and verifies that the
2 same data is readable in both. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *actual[2] = {(char *) 0x10000000, (char *) 0x20000000};
14 size_t i;
15 int handle[2];
16
17 for (i = 0; i < 2; i++)
18 {
19 CHECK ((handle[i] = open ("sample.txt")) > 1,
20 "open \"sample.txt\" #%zu", i);
21 CHECK (mmap (handle[i], actual[i]) != MAP_FAILED,
22 "mmap \"sample.txt\" #%zu at %p", i, (void *) actual[i]);
23 }
24
25 for (i = 0; i < 2; i++)
26 CHECK (!memcmp (actual[i], sample, strlen (sample)),
27 "compare mmap'd file %zu against data", i);
28}
diff --git a/pintos-progos/tests/vm/mmap-twice.ck b/pintos-progos/tests/vm/mmap-twice.ck
new file mode 100644
index 0000000..05e9724
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-twice.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-twice) begin
7(mmap-twice) open "sample.txt" #0
8(mmap-twice) mmap "sample.txt" #0 at 0x10000000
9(mmap-twice) open "sample.txt" #1
10(mmap-twice) mmap "sample.txt" #1 at 0x20000000
11(mmap-twice) compare mmap'd file 0 against data
12(mmap-twice) compare mmap'd file 1 against data
13(mmap-twice) end
14EOF
15pass;
diff --git a/pintos-progos/tests/vm/mmap-unmap.c b/pintos-progos/tests/vm/mmap-unmap.c
new file mode 100644
index 0000000..d35a79e
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-unmap.c
@@ -0,0 +1,23 @@
1/* Maps and unmaps a file and verifies that the mapped region is
2 inaccessible afterward. */
3
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9#define ACTUAL ((void *) 0x10000000)
10
11void
12test_main (void)
13{
14 int handle;
15 mapid_t map;
16
17 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
18 CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
19
20 munmap (map);
21
22 fail ("unmapped memory is readable (%d)", *(int *) ACTUAL);
23}
diff --git a/pintos-progos/tests/vm/mmap-unmap.ck b/pintos-progos/tests/vm/mmap-unmap.ck
new file mode 100644
index 0000000..119658c
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-unmap.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::vm::process_death;
6
7check_process_death ('mmap-unmap');
diff --git a/pintos-progos/tests/vm/mmap-write.c b/pintos-progos/tests/vm/mmap-write.c
new file mode 100644
index 0000000..46e8043
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-write.c
@@ -0,0 +1,32 @@
1/* Writes to a file through a mapping, and unmaps the file,
2 then reads the data in the file back using the read system
3 call to verify. */
4
5#include <string.h>
6#include <syscall.h>
7#include "tests/vm/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11#define ACTUAL ((void *) 0x10000000)
12
13void
14test_main (void)
15{
16 int handle;
17 mapid_t map;
18 char buf[1024];
19
20 /* Write file via mmap. */
21 CHECK (create ("sample.txt", strlen (sample)), "create \"sample.txt\"");
22 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
23 CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
24 memcpy (ACTUAL, sample, strlen (sample));
25 munmap (map);
26
27 /* Read back via read(). */
28 read (handle, buf, strlen (sample));
29 CHECK (!memcmp (buf, sample, strlen (sample)),
30 "compare read data against written data");
31 close (handle);
32}
diff --git a/pintos-progos/tests/vm/mmap-write.ck b/pintos-progos/tests/vm/mmap-write.ck
new file mode 100644
index 0000000..d2c9cc5
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-write.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-write) begin
7(mmap-write) create "sample.txt"
8(mmap-write) open "sample.txt"
9(mmap-write) mmap "sample.txt"
10(mmap-write) compare read data against written data
11(mmap-write) end
12EOF
13pass;
diff --git a/pintos-progos/tests/vm/mmap-zero.c b/pintos-progos/tests/vm/mmap-zero.c
new file mode 100644
index 0000000..368b759
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-zero.c
@@ -0,0 +1,27 @@
1/* Tries to map a zero-length file, which may or may not work but
2 should not terminate the process or crash.
3 Then dereferences the address that we tried to map,
4 and the process must be terminated with -1 exit code. */
5
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *data = (char *) 0x7f000000;
14 int handle;
15
16 CHECK (create ("empty", 0), "create empty file \"empty\"");
17 CHECK ((handle = open ("empty")) > 1, "open \"empty\"");
18
19 /* Calling mmap() might succeed or fail. We don't care. */
20 msg ("mmap \"empty\"");
21 mmap (handle, data);
22
23 /* Regardless of whether the call worked, *data should cause
24 the process to be terminated. */
25 fail ("unmapped memory is readable (%d)", *data);
26}
27
diff --git a/pintos-progos/tests/vm/mmap-zero.ck b/pintos-progos/tests/vm/mmap-zero.ck
new file mode 100644
index 0000000..6033d5d
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-zero.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(mmap-zero) begin
7(mmap-zero) create empty file "empty"
8(mmap-zero) open "empty"
9(mmap-zero) mmap "empty"
10mmap-zero: exit(-1)
11EOF
12pass;
diff --git a/pintos-progos/tests/vm/page-linear.c b/pintos-progos/tests/vm/page-linear.c
new file mode 100644
index 0000000..652a47b
--- /dev/null
+++ b/pintos-progos/tests/vm/page-linear.c
@@ -0,0 +1,44 @@
1/* Encrypts, then decrypts, 2 MB of memory and verifies that the
2 values are as they should be. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9#define SIZE (2 * 1024 * 1024)
10
11static char buf[SIZE];
12
13void
14test_main (void)
15{
16 struct arc4 arc4;
17 size_t i;
18
19 /* Initialize to 0x5a. */
20 msg ("initialize");
21 memset (buf, 0x5a, sizeof buf);
22
23 /* Check that it's all 0x5a. */
24 msg ("read pass");
25 for (i = 0; i < SIZE; i++)
26 if (buf[i] != 0x5a)
27 fail ("byte %zu != 0x5a", i);
28
29 /* Encrypt zeros. */
30 msg ("read/modify/write pass one");
31 arc4_init (&arc4, "foobar", 6);
32 arc4_crypt (&arc4, buf, SIZE);
33
34 /* Decrypt back to zeros. */
35 msg ("read/modify/write pass two");
36 arc4_init (&arc4, "foobar", 6);
37 arc4_crypt (&arc4, buf, SIZE);
38
39 /* Check that it's all 0x5a. */
40 msg ("read pass");
41 for (i = 0; i < SIZE; i++)
42 if (buf[i] != 0x5a)
43 fail ("byte %zu != 0x5a", i);
44}
diff --git a/pintos-progos/tests/vm/page-linear.ck b/pintos-progos/tests/vm/page-linear.ck
new file mode 100644
index 0000000..dcbc884
--- /dev/null
+++ b/pintos-progos/tests/vm/page-linear.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-linear) begin
7(page-linear) initialize
8(page-linear) read pass
9(page-linear) read/modify/write pass one
10(page-linear) read/modify/write pass two
11(page-linear) read pass
12(page-linear) end
13EOF
14pass;
diff --git a/pintos-progos/tests/vm/page-merge-mm.c b/pintos-progos/tests/vm/page-merge-mm.c
new file mode 100644
index 0000000..908c71c
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-mm.c
@@ -0,0 +1,8 @@
1#include "tests/main.h"
2#include "tests/vm/parallel-merge.h"
3
4void
5test_main (void)
6{
7 parallel_merge ("child-qsort-mm", 80);
8}
diff --git a/pintos-progos/tests/vm/page-merge-mm.ck b/pintos-progos/tests/vm/page-merge-mm.ck
new file mode 100644
index 0000000..74fa980
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-mm.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-mm) begin
7(page-merge-mm) init
8(page-merge-mm) sort chunk 0
9(page-merge-mm) sort chunk 1
10(page-merge-mm) sort chunk 2
11(page-merge-mm) sort chunk 3
12(page-merge-mm) sort chunk 4
13(page-merge-mm) sort chunk 5
14(page-merge-mm) sort chunk 6
15(page-merge-mm) sort chunk 7
16(page-merge-mm) wait for child 0
17(page-merge-mm) wait for child 1
18(page-merge-mm) wait for child 2
19(page-merge-mm) wait for child 3
20(page-merge-mm) wait for child 4
21(page-merge-mm) wait for child 5
22(page-merge-mm) wait for child 6
23(page-merge-mm) wait for child 7
24(page-merge-mm) merge
25(page-merge-mm) verify
26(page-merge-mm) success, buf_idx=1,048,576
27(page-merge-mm) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-merge-par.c b/pintos-progos/tests/vm/page-merge-par.c
new file mode 100644
index 0000000..e7e1609
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-par.c
@@ -0,0 +1,8 @@
1#include "tests/main.h"
2#include "tests/vm/parallel-merge.h"
3
4void
5test_main (void)
6{
7 parallel_merge ("child-sort", 123);
8}
diff --git a/pintos-progos/tests/vm/page-merge-par.ck b/pintos-progos/tests/vm/page-merge-par.ck
new file mode 100644
index 0000000..31f8aa7
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-par.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-par) begin
7(page-merge-par) init
8(page-merge-par) sort chunk 0
9(page-merge-par) sort chunk 1
10(page-merge-par) sort chunk 2
11(page-merge-par) sort chunk 3
12(page-merge-par) sort chunk 4
13(page-merge-par) sort chunk 5
14(page-merge-par) sort chunk 6
15(page-merge-par) sort chunk 7
16(page-merge-par) wait for child 0
17(page-merge-par) wait for child 1
18(page-merge-par) wait for child 2
19(page-merge-par) wait for child 3
20(page-merge-par) wait for child 4
21(page-merge-par) wait for child 5
22(page-merge-par) wait for child 6
23(page-merge-par) wait for child 7
24(page-merge-par) merge
25(page-merge-par) verify
26(page-merge-par) success, buf_idx=1,048,576
27(page-merge-par) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-merge-seq.c b/pintos-progos/tests/vm/page-merge-seq.c
new file mode 100644
index 0000000..12e3880
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-seq.c
@@ -0,0 +1,137 @@
1/* Generates about 1 MB of random data that is then divided into
2 16 chunks. A separate subprocess sorts each chunk in
3 sequence. Then we merge the chunks and verify that the result
4 is what it should be. */
5
6#include <syscall.h>
7#include "tests/arc4.h"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11/* This is the max file size for an older version of the Pintos
12 file system that had 126 direct blocks each pointing to a
13 single disk sector. We could raise it now. */
14#define CHUNK_SIZE (126 * 512)
15#define CHUNK_CNT 16 /* Number of chunks. */
16#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */
17
18unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
19size_t histogram[256];
20
21/* Initialize buf1 with random data,
22 then count the number of instances of each value within it. */
23static void
24init (void)
25{
26 struct arc4 arc4;
27 size_t i;
28
29 msg ("init");
30
31 arc4_init (&arc4, "foobar", 6);
32 arc4_crypt (&arc4, buf1, sizeof buf1);
33 for (i = 0; i < sizeof buf1; i++)
34 histogram[buf1[i]]++;
35}
36
37/* Sort each chunk of buf1 using a subprocess. */
38static void
39sort_chunks (void)
40{
41 size_t i;
42
43 create ("buffer", CHUNK_SIZE);
44 for (i = 0; i < CHUNK_CNT; i++)
45 {
46 pid_t child;
47 int handle;
48
49 msg ("sort chunk %zu", i);
50
51 /* Write this chunk to a file. */
52 quiet = true;
53 CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
54 write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
55 close (handle);
56
57 /* Sort with subprocess. */
58 CHECK ((child = exec ("child-sort buffer")) != -1,
59 "exec \"child-sort buffer\"");
60 CHECK (wait (child) == 123, "wait for child-sort");
61
62 /* Read chunk back from file. */
63 CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
64 read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
65 close (handle);
66
67 quiet = false;
68 }
69}
70
71/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
72static void
73merge (void)
74{
75 unsigned char *mp[CHUNK_CNT];
76 size_t mp_left;
77 unsigned char *op;
78 size_t i;
79
80 msg ("merge");
81
82 /* Initialize merge pointers. */
83 mp_left = CHUNK_CNT;
84 for (i = 0; i < CHUNK_CNT; i++)
85 mp[i] = buf1 + CHUNK_SIZE * i;
86
87 /* Merge. */
88 op = buf2;
89 while (mp_left > 0)
90 {
91 /* Find smallest value. */
92 size_t min = 0;
93 for (i = 1; i < mp_left; i++)
94 if (*mp[i] < *mp[min])
95 min = i;
96
97 /* Append value to buf2. */
98 *op++ = *mp[min];
99
100 /* Advance merge pointer.
101 Delete this chunk from the set if it's emptied. */
102 if ((++mp[min] - buf1) % CHUNK_SIZE == 0)
103 mp[min] = mp[--mp_left];
104 }
105}
106
107static void
108verify (void)
109{
110 size_t buf_idx;
111 size_t hist_idx;
112
113 msg ("verify");
114
115 buf_idx = 0;
116 for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
117 hist_idx++)
118 {
119 while (histogram[hist_idx]-- > 0)
120 {
121 if (buf2[buf_idx] != hist_idx)
122 fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
123 buf_idx++;
124 }
125 }
126
127 msg ("success, buf_idx=%'zu", buf_idx);
128}
129
130void
131test_main (void)
132{
133 init ();
134 sort_chunks ();
135 merge ();
136 verify ();
137}
diff --git a/pintos-progos/tests/vm/page-merge-seq.ck b/pintos-progos/tests/vm/page-merge-seq.ck
new file mode 100644
index 0000000..d78f69d
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-seq.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-seq) begin
7(page-merge-seq) init
8(page-merge-seq) sort chunk 0
9(page-merge-seq) sort chunk 1
10(page-merge-seq) sort chunk 2
11(page-merge-seq) sort chunk 3
12(page-merge-seq) sort chunk 4
13(page-merge-seq) sort chunk 5
14(page-merge-seq) sort chunk 6
15(page-merge-seq) sort chunk 7
16(page-merge-seq) sort chunk 8
17(page-merge-seq) sort chunk 9
18(page-merge-seq) sort chunk 10
19(page-merge-seq) sort chunk 11
20(page-merge-seq) sort chunk 12
21(page-merge-seq) sort chunk 13
22(page-merge-seq) sort chunk 14
23(page-merge-seq) sort chunk 15
24(page-merge-seq) merge
25(page-merge-seq) verify
26(page-merge-seq) success, buf_idx=1,032,192
27(page-merge-seq) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-merge-stk.c b/pintos-progos/tests/vm/page-merge-stk.c
new file mode 100644
index 0000000..5eb1069
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-stk.c
@@ -0,0 +1,8 @@
1#include "tests/main.h"
2#include "tests/vm/parallel-merge.h"
3
4void
5test_main (void)
6{
7 parallel_merge ("child-qsort", 72);
8}
diff --git a/pintos-progos/tests/vm/page-merge-stk.ck b/pintos-progos/tests/vm/page-merge-stk.ck
new file mode 100644
index 0000000..c5bc1ae
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-stk.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-stk) begin
7(page-merge-stk) init
8(page-merge-stk) sort chunk 0
9(page-merge-stk) sort chunk 1
10(page-merge-stk) sort chunk 2
11(page-merge-stk) sort chunk 3
12(page-merge-stk) sort chunk 4
13(page-merge-stk) sort chunk 5
14(page-merge-stk) sort chunk 6
15(page-merge-stk) sort chunk 7
16(page-merge-stk) wait for child 0
17(page-merge-stk) wait for child 1
18(page-merge-stk) wait for child 2
19(page-merge-stk) wait for child 3
20(page-merge-stk) wait for child 4
21(page-merge-stk) wait for child 5
22(page-merge-stk) wait for child 6
23(page-merge-stk) wait for child 7
24(page-merge-stk) merge
25(page-merge-stk) verify
26(page-merge-stk) success, buf_idx=1,048,576
27(page-merge-stk) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-parallel.c b/pintos-progos/tests/vm/page-parallel.c
new file mode 100644
index 0000000..9d619e0
--- /dev/null
+++ b/pintos-progos/tests/vm/page-parallel.c
@@ -0,0 +1,21 @@
1/* Runs 4 child-linear processes at once. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7#define CHILD_CNT 4
8
9void
10test_main (void)
11{
12 pid_t children[CHILD_CNT];
13 int i;
14
15 for (i = 0; i < CHILD_CNT; i++)
16 CHECK ((children[i] = exec ("child-linear")) != -1,
17 "exec \"child-linear\"");
18
19 for (i = 0; i < CHILD_CNT; i++)
20 CHECK (wait (children[i]) == 0x42, "wait for child %d", i);
21}
diff --git a/pintos-progos/tests/vm/page-parallel.ck b/pintos-progos/tests/vm/page-parallel.ck
new file mode 100644
index 0000000..90c14ef
--- /dev/null
+++ b/pintos-progos/tests/vm/page-parallel.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-parallel) begin
7(page-parallel) exec "child-linear"
8(page-parallel) exec "child-linear"
9(page-parallel) exec "child-linear"
10(page-parallel) exec "child-linear"
11(page-parallel) wait for child 0
12(page-parallel) wait for child 1
13(page-parallel) wait for child 2
14(page-parallel) wait for child 3
15(page-parallel) end
16EOF
17pass;
diff --git a/pintos-progos/tests/vm/page-shuffle.c b/pintos-progos/tests/vm/page-shuffle.c
new file mode 100644
index 0000000..095a9da
--- /dev/null
+++ b/pintos-progos/tests/vm/page-shuffle.c
@@ -0,0 +1,30 @@
1/* Shuffles a 128 kB data buffer 10 times, printing the checksum
2 after each time. */
3
4#include <stdbool.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10#define SIZE (128 * 1024)
11
12static char buf[SIZE];
13
14void
15test_main (void)
16{
17 size_t i;
18
19 /* Initialize. */
20 for (i = 0; i < sizeof buf; i++)
21 buf[i] = i * 257;
22 msg ("init: cksum=%lu", cksum (buf, sizeof buf));
23
24 /* Shuffle repeatedly. */
25 for (i = 0; i < 10; i++)
26 {
27 shuffle (buf, sizeof buf, 1);
28 msg ("shuffle %zu: cksum=%lu", i, cksum (buf, sizeof buf));
29 }
30}
diff --git a/pintos-progos/tests/vm/page-shuffle.ck b/pintos-progos/tests/vm/page-shuffle.ck
new file mode 100644
index 0000000..6447d38
--- /dev/null
+++ b/pintos-progos/tests/vm/page-shuffle.ck
@@ -0,0 +1,44 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::cksum;
6use tests::lib;
7
8my ($init, @shuffle);
9if (1) {
10 # Use precalculated values.
11 $init = 3115322833;
12 @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467,
13 3022003332, 3614934266, 2704001777, 735775156, 1864109763);
14} else {
15 # Recalculate values.
16 my ($buf) = "";
17 for my $i (0...128 * 1024 - 1) {
18 $buf .= chr (($i * 257) & 0xff);
19 }
20 $init = cksum ($buf);
21
22 random_init (0);
23 for my $i (1...10) {
24 $buf = shuffle ($buf, length ($buf), 1);
25 push (@shuffle, cksum ($buf));
26 }
27}
28
29check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]);
30(page-shuffle) begin
31(page-shuffle) init: cksum=$init
32(page-shuffle) shuffle 0: cksum=$shuffle[0]
33(page-shuffle) shuffle 1: cksum=$shuffle[1]
34(page-shuffle) shuffle 2: cksum=$shuffle[2]
35(page-shuffle) shuffle 3: cksum=$shuffle[3]
36(page-shuffle) shuffle 4: cksum=$shuffle[4]
37(page-shuffle) shuffle 5: cksum=$shuffle[5]
38(page-shuffle) shuffle 6: cksum=$shuffle[6]
39(page-shuffle) shuffle 7: cksum=$shuffle[7]
40(page-shuffle) shuffle 8: cksum=$shuffle[8]
41(page-shuffle) shuffle 9: cksum=$shuffle[9]
42(page-shuffle) end
43EOF
44pass;
diff --git a/pintos-progos/tests/vm/parallel-merge.c b/pintos-progos/tests/vm/parallel-merge.c
new file mode 100644
index 0000000..cc09bb1
--- /dev/null
+++ b/pintos-progos/tests/vm/parallel-merge.c
@@ -0,0 +1,149 @@
1/* Generates about 1 MB of random data that is then divided into
2 16 chunks. A separate subprocess sorts each chunk; the
3 subprocesses run in parallel. Then we merge the chunks and
4 verify that the result is what it should be. */
5
6#include "tests/vm/parallel-merge.h"
7#include <stdio.h>
8#include <syscall.h>
9#include "tests/arc4.h"
10#include "tests/lib.h"
11#include "tests/main.h"
12
13#define CHUNK_SIZE (128 * 1024)
14#define CHUNK_CNT 8 /* Number of chunks. */
15#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */
16
17unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
18size_t histogram[256];
19
20/* Initialize buf1 with random data,
21 then count the number of instances of each value within it. */
22static void
23init (void)
24{
25 struct arc4 arc4;
26 size_t i;
27
28 msg ("init");
29
30 arc4_init (&arc4, "foobar", 6);
31 arc4_crypt (&arc4, buf1, sizeof buf1);
32 for (i = 0; i < sizeof buf1; i++)
33 histogram[buf1[i]]++;
34}
35
36/* Sort each chunk of buf1 using SUBPROCESS,
37 which is expected to return EXIT_STATUS. */
38static void
39sort_chunks (const char *subprocess, int exit_status)
40{
41 pid_t children[CHUNK_CNT];
42 size_t i;
43
44 for (i = 0; i < CHUNK_CNT; i++)
45 {
46 char fn[128];
47 char cmd[128];
48 int handle;
49
50 msg ("sort chunk %zu", i);
51
52 /* Write this chunk to a file. */
53 snprintf (fn, sizeof fn, "buf%zu", i);
54 create (fn, CHUNK_SIZE);
55 quiet = true;
56 CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
57 write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
58 close (handle);
59
60 /* Sort with subprocess. */
61 snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn);
62 CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd);
63 quiet = false;
64 }
65
66 for (i = 0; i < CHUNK_CNT; i++)
67 {
68 char fn[128];
69 int handle;
70
71 CHECK (wait (children[i]) == exit_status, "wait for child %zu", i);
72
73 /* Read chunk back from file. */
74 quiet = true;
75 snprintf (fn, sizeof fn, "buf%zu", i);
76 CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
77 read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
78 close (handle);
79 quiet = false;
80 }
81}
82
83/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
84static void
85merge (void)
86{
87 unsigned char *mp[CHUNK_CNT];
88 size_t mp_left;
89 unsigned char *op;
90 size_t i;
91
92 msg ("merge");
93
94 /* Initialize merge pointers. */
95 mp_left = CHUNK_CNT;
96 for (i = 0; i < CHUNK_CNT; i++)
97 mp[i] = buf1 + CHUNK_SIZE * i;
98
99 /* Merge. */
100 op = buf2;
101 while (mp_left > 0)
102 {
103 /* Find smallest value. */
104 size_t min = 0;
105 for (i = 1; i < mp_left; i++)
106 if (*mp[i] < *mp[min])
107 min = i;
108
109 /* Append value to buf2. */
110 *op++ = *mp[min];
111
112 /* Advance merge pointer.
113 Delete this chunk from the set if it's emptied. */
114 if ((++mp[min] - buf1) % CHUNK_SIZE == 0)
115 mp[min] = mp[--mp_left];
116 }
117}
118
119static void
120verify (void)
121{
122 size_t buf_idx;
123 size_t hist_idx;
124
125 msg ("verify");
126
127 buf_idx = 0;
128 for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
129 hist_idx++)
130 {
131 while (histogram[hist_idx]-- > 0)
132 {
133 if (buf2[buf_idx] != hist_idx)
134 fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
135 buf_idx++;
136 }
137 }
138
139 msg ("success, buf_idx=%'zu", buf_idx);
140}
141
142void
143parallel_merge (const char *child_name, int exit_status)
144{
145 init ();
146 sort_chunks (child_name, exit_status);
147 merge ();
148 verify ();
149}
diff --git a/pintos-progos/tests/vm/parallel-merge.h b/pintos-progos/tests/vm/parallel-merge.h
new file mode 100644
index 0000000..a6b6431
--- /dev/null
+++ b/pintos-progos/tests/vm/parallel-merge.h
@@ -0,0 +1,6 @@
1#ifndef TESTS_VM_PARALLEL_MERGE
2#define TESTS_VM_PARALLEL_MERGE 1
3
4void parallel_merge (const char *child_name, int exit_status);
5
6#endif /* tests/vm/parallel-merge.h */
diff --git a/pintos-progos/tests/vm/process_death.pm b/pintos-progos/tests/vm/process_death.pm
new file mode 100644
index 0000000..52039a1
--- /dev/null
+++ b/pintos-progos/tests/vm/process_death.pm
@@ -0,0 +1,22 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5
6sub check_process_death {
7 my ($proc_name) = @_;
8 our ($test);
9 my (@output) = read_text_file ("$test.output");
10
11 common_checks ("run", @output);
12 @output = get_core_output ("run", @output);
13 fail "First line of output is not `($proc_name) begin' message.\n"
14 if $output[0] ne "($proc_name) begin";
15 fail "Output missing '$proc_name: exit(-1)' message.\n"
16 if !grep ("$proc_name: exit(-1)" eq $_, @output);
17 fail "Output contains '($proc_name) end' message.\n"
18 if grep (/\($proc_name\) end/, @output);
19 pass;
20}
21
221;
diff --git a/pintos-progos/tests/vm/pt-bad-addr.c b/pintos-progos/tests/vm/pt-bad-addr.c
new file mode 100644
index 0000000..3ca4084
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-addr.c
@@ -0,0 +1,11 @@
1/* Accesses a bad address.
2 The process must be terminated with -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 fail ("bad addr read as %d", *(int *) 0x04000000);
11}
diff --git a/pintos-progos/tests/vm/pt-bad-addr.ck b/pintos-progos/tests/vm/pt-bad-addr.ck
new file mode 100644
index 0000000..09ea039
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-addr.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::vm::process_death;
6
7check_process_death ('pt-bad-addr');
diff --git a/pintos-progos/tests/vm/pt-bad-read.c b/pintos-progos/tests/vm/pt-bad-read.c
new file mode 100644
index 0000000..ee791ff
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-read.c
@@ -0,0 +1,16 @@
1/* Reads from a file into a bad address.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12
13 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
14 read (handle, (char *) &handle - 4096, 1);
15 fail ("survived reading data into bad address");
16}
diff --git a/pintos-progos/tests/vm/pt-bad-read.ck b/pintos-progos/tests/vm/pt-bad-read.ck
new file mode 100644
index 0000000..1f96bb4
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-read.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(pt-bad-read) begin
7(pt-bad-read) open "sample.txt"
8pt-bad-read: exit(-1)
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.c b/pintos-progos/tests/vm/pt-big-stk-obj.c
new file mode 100644
index 0000000..6b630ec
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-big-stk-obj.c
@@ -0,0 +1,20 @@
1/* Allocates and writes to a 64 kB object on the stack.
2 This must succeed. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char stk_obj[65536];
14 struct arc4 arc4;
15
16 arc4_init (&arc4, "foobar", 6);
17 memset (stk_obj, 0, sizeof stk_obj);
18 arc4_crypt (&arc4, stk_obj, sizeof stk_obj);
19 msg ("cksum: %lu", cksum (stk_obj, sizeof stk_obj));
20}
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.ck b/pintos-progos/tests/vm/pt-big-stk-obj.ck
new file mode 100644
index 0000000..eb5853a
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-big-stk-obj.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-big-stk-obj) begin
7(pt-big-stk-obj) cksum: 3256410166
8(pt-big-stk-obj) end
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/pt-grow-bad.c b/pintos-progos/tests/vm/pt-grow-bad.c
new file mode 100644
index 0000000..d4beba2
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-bad.c
@@ -0,0 +1,14 @@
1/* Read from an address 4,096 bytes below the stack pointer.
2 The process must be terminated with -1 exit code. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 asm volatile ("movl -4096(%esp), %eax");
14}
diff --git a/pintos-progos/tests/vm/pt-grow-bad.ck b/pintos-progos/tests/vm/pt-grow-bad.ck
new file mode 100644
index 0000000..4c0ab8a
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-bad.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(pt-grow-bad) begin
7pt-grow-bad: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.c b/pintos-progos/tests/vm/pt-grow-pusha.c
new file mode 100644
index 0000000..f9762a5
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-pusha.c
@@ -0,0 +1,20 @@
1/* Expand the stack by 32 bytes all at once using the PUSHA
2 instruction.
3 This must succeed. */
4
5#include <string.h>
6#include "tests/arc4.h"
7#include "tests/cksum.h"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 asm volatile
15 ("movl %%esp, %%eax;" /* Save a copy of the stack pointer. */
16 "andl $0xfffff000, %%esp;" /* Move stack pointer to bottom of page. */
17 "pushal;" /* Push 32 bytes on stack at once. */
18 "movl %%eax, %%esp" /* Restore copied stack pointer. */
19 : : : "eax"); /* Tell GCC we destroyed eax. */
20}
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.ck b/pintos-progos/tests/vm/pt-grow-pusha.ck
new file mode 100644
index 0000000..5000966
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-pusha.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-grow-pusha) begin
7(pt-grow-pusha) end
8EOF
9pass;
diff --git a/pintos-progos/tests/vm/pt-grow-stack.c b/pintos-progos/tests/vm/pt-grow-stack.c
new file mode 100644
index 0000000..0997a00
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stack.c
@@ -0,0 +1,20 @@
1/* Demonstrate that the stack can grow.
2 This must succeed. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char stack_obj[4096];
14 struct arc4 arc4;
15
16 arc4_init (&arc4, "foobar", 6);
17 memset (stack_obj, 0, sizeof stack_obj);
18 arc4_crypt (&arc4, stack_obj, sizeof stack_obj);
19 msg ("cksum: %lu", cksum (stack_obj, sizeof stack_obj));
20}
diff --git a/pintos-progos/tests/vm/pt-grow-stack.ck b/pintos-progos/tests/vm/pt-grow-stack.ck
new file mode 100644
index 0000000..1e669db
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stack.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-grow-stack) begin
7(pt-grow-stack) cksum: 3424492700
8(pt-grow-stack) end
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.c b/pintos-progos/tests/vm/pt-grow-stk-sc.c
new file mode 100644
index 0000000..3efbb5f
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stk-sc.c
@@ -0,0 +1,32 @@
1/* This test checks that the stack is properly extended even if
2 the first access to a stack location occurs inside a system
3 call.
4
5 From Godmar Back. */
6
7#include <string.h>
8#include <syscall.h>
9#include "tests/vm/sample.inc"
10#include "tests/lib.h"
11#include "tests/main.h"
12
13void
14test_main (void)
15{
16 int handle;
17 int slen = strlen (sample);
18 char buf2[65536];
19
20 /* Write file via write(). */
21 CHECK (create ("sample.txt", slen), "create \"sample.txt\"");
22 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
23 CHECK (write (handle, sample, slen) == slen, "write \"sample.txt\"");
24 close (handle);
25
26 /* Read back via read(). */
27 CHECK ((handle = open ("sample.txt")) > 1, "2nd open \"sample.txt\"");
28 CHECK (read (handle, buf2 + 32768, slen) == slen, "read \"sample.txt\"");
29
30 CHECK (!memcmp (sample, buf2 + 32768, slen), "compare written data against read data");
31 close (handle);
32}
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.ck b/pintos-progos/tests/vm/pt-grow-stk-sc.ck
new file mode 100644
index 0000000..23d3b02
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stk-sc.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-grow-stk-sc) begin
7(pt-grow-stk-sc) create "sample.txt"
8(pt-grow-stk-sc) open "sample.txt"
9(pt-grow-stk-sc) write "sample.txt"
10(pt-grow-stk-sc) 2nd open "sample.txt"
11(pt-grow-stk-sc) read "sample.txt"
12(pt-grow-stk-sc) compare written data against read data
13(pt-grow-stk-sc) end
14EOF
15pass;
diff --git a/pintos-progos/tests/vm/pt-write-code-2.c b/pintos-progos/tests/vm/pt-write-code-2.c
new file mode 100644
index 0000000..83bcc2c
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code-2.c
@@ -0,0 +1,15 @@
1/* Try to write to the code segment using a system call.
2 The process must be terminated with -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13 read (handle, (void *) test_main, 1);
14 fail ("survived reading data into code segment");
15}
diff --git a/pintos-progos/tests/vm/pt-write-code.c b/pintos-progos/tests/vm/pt-write-code.c
new file mode 100644
index 0000000..5072cec
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code.c
@@ -0,0 +1,12 @@
1/* Try to write to the code segment.
2 The process must be terminated with -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 *(int *) test_main = 0;
11 fail ("writing the code segment succeeded");
12}
diff --git a/pintos-progos/tests/vm/pt-write-code.ck b/pintos-progos/tests/vm/pt-write-code.ck
new file mode 100644
index 0000000..65610fb
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::vm::process_death;
6
7check_process_death ('pt-write-code');
diff --git a/pintos-progos/tests/vm/pt-write-code2.ck b/pintos-progos/tests/vm/pt-write-code2.ck
new file mode 100644
index 0000000..69ffc77
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code2.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(pt-write-code2) begin
7(pt-write-code2) open "sample.txt"
8pt-write-code2: exit(-1)
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/qsort.c b/pintos-progos/tests/vm/qsort.c
new file mode 100644
index 0000000..922572c
--- /dev/null
+++ b/pintos-progos/tests/vm/qsort.c
@@ -0,0 +1,136 @@
1#include "tests/vm/qsort.h"
2#include <stdbool.h>
3#include <debug.h>
4#include <random.h>
5
6/* Picks a pivot for the quicksort from the SIZE bytes in BUF. */
7static unsigned char
8pick_pivot (unsigned char *buf, size_t size)
9{
10 ASSERT (size >= 1);
11 return buf[random_ulong () % size];
12}
13
14/* Checks whether the SIZE bytes in ARRAY are divided into an
15 initial LEFT_SIZE elements all less than PIVOT followed by
16 SIZE - LEFT_SIZE elements all greater than or equal to
17 PIVOT. */
18static bool
19is_partitioned (const unsigned char *array, size_t size,
20 unsigned char pivot, size_t left_size)
21{
22 size_t i;
23
24 for (i = 0; i < left_size; i++)
25 if (array[i] >= pivot)
26 return false;
27
28 for (; i < size; i++)
29 if (array[i] < pivot)
30 return false;
31
32 return true;
33}
34
35/* Swaps the bytes at *A and *B. */
36static void
37swap (unsigned char *a, unsigned char *b)
38{
39 unsigned char t = *a;
40 *a = *b;
41 *b = t;
42}
43
44/* Partitions ARRAY in-place in an initial run of bytes all less
45 than PIVOT, followed by a run of bytes all greater than or
46 equal to PIVOT. Returns the length of the initial run. */
47static size_t
48partition (unsigned char *array, size_t size, int pivot)
49{
50 size_t left_size = size;
51 unsigned char *first = array;
52 unsigned char *last = first + left_size;
53
54 for (;;)
55 {
56 /* Move FIRST forward to point to first element greater than
57 PIVOT. */
58 for (;;)
59 {
60 if (first == last)
61 {
62 ASSERT (is_partitioned (array, size, pivot, left_size));
63 return left_size;
64 }
65 else if (*first >= pivot)
66 break;
67
68 first++;
69 }
70 left_size--;
71
72 /* Move LAST backward to point to last element no bigger
73 than PIVOT. */
74 for (;;)
75 {
76 last--;
77
78 if (first == last)
79 {
80 ASSERT (is_partitioned (array, size, pivot, left_size));
81 return left_size;
82 }
83 else if (*last < pivot)
84 break;
85 else
86 left_size--;
87 }
88
89 /* By swapping FIRST and LAST we extend the starting and
90 ending sequences that pass and fail, respectively,
91 PREDICATE. */
92 swap (first, last);
93 first++;
94 }
95}
96
97/* Returns true if the SIZE bytes in BUF are in nondecreasing
98 order, false otherwise. */
99static bool
100is_sorted (const unsigned char *buf, size_t size)
101{
102 size_t i;
103
104 for (i = 1; i < size; i++)
105 if (buf[i - 1] > buf[i])
106 return false;
107
108 return true;
109}
110
111/* Sorts the SIZE bytes in BUF into nondecreasing order, using
112 the quick-sort algorithm. */
113void
114qsort_bytes (unsigned char *buf, size_t size)
115{
116 if (!is_sorted (buf, size))
117 {
118 int pivot = pick_pivot (buf, size);
119
120 unsigned char *left_half = buf;
121 size_t left_size = partition (buf, size, pivot);
122 unsigned char *right_half = left_half + left_size;
123 size_t right_size = size - left_size;
124
125 if (left_size <= right_size)
126 {
127 qsort_bytes (left_half, left_size);
128 qsort_bytes (right_half, right_size);
129 }
130 else
131 {
132 qsort_bytes (right_half, right_size);
133 qsort_bytes (left_half, left_size);
134 }
135 }
136}
diff --git a/pintos-progos/tests/vm/qsort.h b/pintos-progos/tests/vm/qsort.h
new file mode 100644
index 0000000..61b65f3
--- /dev/null
+++ b/pintos-progos/tests/vm/qsort.h
@@ -0,0 +1,8 @@
1#ifndef TESTS_VM_QSORT_H
2#define TESTS_VM_QSORT_H 1
3
4#include <stddef.h>
5
6void qsort_bytes (unsigned char *buf, size_t size);
7
8#endif /* tests/vm/qsort.h */
diff --git a/pintos-progos/tests/vm/sample.inc b/pintos-progos/tests/vm/sample.inc
new file mode 100644
index 0000000..a60a139
--- /dev/null
+++ b/pintos-progos/tests/vm/sample.inc
@@ -0,0 +1,19 @@
1char sample[] = {
2 "=== ALL USERS PLEASE NOTE ========================\n"
3 "\n"
4 "CAR and CDR now return extra values.\n"
5 "\n"
6 "The function CAR now returns two values. Since it has to go to the\n"
7 "trouble to figure out if the object is carcdr-able anyway, we figured\n"
8 "you might as well get both halves at once. For example, the following\n"
9 "code shows how to destructure a cons (SOME-CONS) into its two slots\n"
10 "(THE-CAR and THE-CDR):\n"
11 "\n"
12 " (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)\n"
13 "\n"
14 "For symmetry with CAR, CDR returns a second value which is the CAR of\n"
15 "the object. In a related change, the functions MAKE-ARRAY and CONS\n"
16 "have been fixed so they don't allocate any storage except on the\n"
17 "stack. This should hopefully help people who don't like using the\n"
18 "garbage collector because it cold boots the machine so often.\n"
19};
diff --git a/pintos-progos/tests/vm/sample.txt b/pintos-progos/tests/vm/sample.txt
new file mode 100644
index 0000000..c446830
--- /dev/null
+++ b/pintos-progos/tests/vm/sample.txt
@@ -0,0 +1,17 @@
1=== ALL USERS PLEASE NOTE ========================
2
3CAR and CDR now return extra values.
4
5The function CAR now returns two values. Since it has to go to the
6trouble to figure out if the object is carcdr-able anyway, we figured
7you might as well get both halves at once. For example, the following
8code shows how to destructure a cons (SOME-CONS) into its two slots
9(THE-CAR and THE-CDR):
10
11 (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)
12
13For symmetry with CAR, CDR returns a second value which is the CAR of
14the object. In a related change, the functions MAKE-ARRAY and CONS
15have been fixed so they don't allocate any storage except on the
16stack. This should hopefully help people who don't like using the
17garbage collector because it cold boots the machine so often.