provided code
This commit is contained in:
1713
src/tests/Algorithm/Diff.pm
Normal file
1713
src/tests/Algorithm/Diff.pm
Normal file
File diff suppressed because it is too large
Load Diff
75
src/tests/Make.tests
Normal file
75
src/tests/Make.tests
Normal file
@@ -0,0 +1,75 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS))
|
||||
|
||||
PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS))
|
||||
TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS))
|
||||
EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES))
|
||||
|
||||
OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES))
|
||||
ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES))
|
||||
RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES))
|
||||
|
||||
ifdef PROGS
|
||||
include ../../Makefile.userprog
|
||||
endif
|
||||
|
||||
TIMEOUT = 60
|
||||
|
||||
clean::
|
||||
rm -f $(OUTPUTS) $(ERRORS) $(RESULTS)
|
||||
|
||||
grade:: results
|
||||
$(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@
|
||||
|
||||
check:: results
|
||||
@cat $<
|
||||
@COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \
|
||||
FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \
|
||||
if [ $$FAILURES = 0 ]; then \
|
||||
echo "All $$COUNT tests passed!"; \
|
||||
else \
|
||||
echo "Warning: $$FAILURES of $$COUNT tests failed!"; \
|
||||
fi
|
||||
|
||||
results: $(RESULTS)
|
||||
@for d in $(TESTS) $(EXTRA_GRADES); do \
|
||||
if echo PASS | cmp -s $$d.result -; then \
|
||||
echo "pass $$d"; \
|
||||
else \
|
||||
echo "FAIL $$d"; \
|
||||
fi; \
|
||||
done > $@
|
||||
|
||||
outputs:: $(OUTPUTS)
|
||||
|
||||
$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog)))
|
||||
$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES)))
|
||||
$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test)))
|
||||
|
||||
# Prevent an environment variable VERBOSE from surprising us.
|
||||
VERBOSE =
|
||||
|
||||
TESTCMD = pintos -v -k -T $(TIMEOUT)
|
||||
TESTCMD += $(SIMULATOR)
|
||||
TESTCMD += $(PINTOSOPTS)
|
||||
ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
|
||||
TESTCMD += $(FILESYSSOURCE)
|
||||
TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
|
||||
endif
|
||||
ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
|
||||
TESTCMD += --swap-size=8
|
||||
endif
|
||||
TESTCMD += -- -q
|
||||
TESTCMD += $(KERNELFLAGS)
|
||||
ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
|
||||
TESTCMD += -f
|
||||
endif
|
||||
TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F))
|
||||
TESTCMD += < /dev/null
|
||||
TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output
|
||||
%.output: kernel.bin loader.bin
|
||||
$(TESTCMD)
|
||||
|
||||
%.result: %.ck %.output
|
||||
perl -I$(SRCDIR) $< $* $@
|
||||
53
src/tests/arc4.c
Normal file
53
src/tests/arc4.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <stdint.h>
|
||||
#include "tests/arc4.h"
|
||||
|
||||
/* Swap bytes. */
|
||||
static inline void
|
||||
swap_byte (uint8_t *a, uint8_t *b)
|
||||
{
|
||||
uint8_t t = *a;
|
||||
*a = *b;
|
||||
*b = t;
|
||||
}
|
||||
|
||||
void
|
||||
arc4_init (struct arc4 *arc4, const void *key_, size_t size)
|
||||
{
|
||||
const uint8_t *key = key_;
|
||||
size_t key_idx;
|
||||
uint8_t *s;
|
||||
int i, j;
|
||||
|
||||
s = arc4->s;
|
||||
arc4->i = arc4->j = 0;
|
||||
for (i = 0; i < 256; i++)
|
||||
s[i] = (uint8_t) i;
|
||||
for (key_idx = 0, i = j = 0; i < 256; i++)
|
||||
{
|
||||
j = (j + s[i] + key[key_idx]) & 255;
|
||||
swap_byte (s + i, s + j);
|
||||
if (++key_idx >= size)
|
||||
key_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
arc4_crypt (struct arc4 *arc4, void *buf_, size_t size)
|
||||
{
|
||||
uint8_t *buf = buf_;
|
||||
uint8_t *s;
|
||||
uint8_t i, j;
|
||||
|
||||
s = arc4->s;
|
||||
i = arc4->i;
|
||||
j = arc4->j;
|
||||
while (size-- > 0)
|
||||
{
|
||||
i = (uint8_t) (i + 1);
|
||||
j = (uint8_t) (j + s[i]);
|
||||
swap_byte (s + i, s + j);
|
||||
*buf++ ^= s[(s[i] + s[j]) & 255];
|
||||
}
|
||||
arc4->i = i;
|
||||
arc4->j = j;
|
||||
}
|
||||
17
src/tests/arc4.h
Normal file
17
src/tests/arc4.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef TESTS_ARC4_H
|
||||
#define TESTS_ARC4_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Alleged RC4 algorithm encryption state. */
|
||||
struct arc4
|
||||
{
|
||||
uint8_t s[256];
|
||||
uint8_t i, j;
|
||||
};
|
||||
|
||||
void arc4_init (struct arc4 *, const void *, size_t);
|
||||
void arc4_crypt (struct arc4 *, void *, size_t);
|
||||
|
||||
#endif /* tests/arc4.h */
|
||||
29
src/tests/arc4.pm
Normal file
29
src/tests/arc4.pm
Normal file
@@ -0,0 +1,29 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub arc4_init {
|
||||
my ($key) = @_;
|
||||
my (@s) = 0...255;
|
||||
my ($j) = 0;
|
||||
for my $i (0...255) {
|
||||
$j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff;
|
||||
@s[$i, $j] = @s[$j, $i];
|
||||
}
|
||||
return (0, 0, @s);
|
||||
}
|
||||
|
||||
sub arc4_crypt {
|
||||
my ($arc4, $buf) = @_;
|
||||
my ($i, $j, @s) = @$arc4;
|
||||
my ($out) = "";
|
||||
for my $c (split (//, $buf)) {
|
||||
$i = ($i + 1) & 0xff;
|
||||
$j = ($j + $s[$i]) & 0xff;
|
||||
@s[$i, $j] = @s[$j, $i];
|
||||
$out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]);
|
||||
}
|
||||
@$arc4 = ($i, $j, @s);
|
||||
return $out;
|
||||
}
|
||||
|
||||
1;
|
||||
92
src/tests/cksum.c
Normal file
92
src/tests/cksum.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/* crctab[] and cksum() are from the `cksum' entry in SUSv3. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tests/cksum.h"
|
||||
|
||||
static unsigned long crctab[] = {
|
||||
0x00000000,
|
||||
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
|
||||
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
|
||||
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
|
||||
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
|
||||
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
|
||||
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
|
||||
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
|
||||
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
|
||||
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
|
||||
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
|
||||
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
|
||||
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
|
||||
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
|
||||
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
|
||||
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
|
||||
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
|
||||
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
|
||||
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
|
||||
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
|
||||
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
|
||||
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
|
||||
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
|
||||
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
|
||||
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
|
||||
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
|
||||
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
|
||||
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
|
||||
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
|
||||
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
|
||||
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
|
||||
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
|
||||
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
|
||||
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
|
||||
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
|
||||
/* This is the algorithm used by the Posix `cksum' utility. */
|
||||
unsigned long
|
||||
cksum (const void *b_, size_t n)
|
||||
{
|
||||
const unsigned char *b = b_;
|
||||
uint32_t s = 0;
|
||||
size_t i;
|
||||
for (i = n; i > 0; --i)
|
||||
{
|
||||
unsigned char c = *b++;
|
||||
s = (s << 8) ^ crctab[(s >> 24) ^ c];
|
||||
}
|
||||
while (n != 0)
|
||||
{
|
||||
unsigned char c = (unsigned char) n;
|
||||
n >>= 8;
|
||||
s = (s << 8) ^ crctab[(s >> 24) ^ c];
|
||||
}
|
||||
return ~s;
|
||||
}
|
||||
|
||||
#ifdef STANDALONE_TEST
|
||||
#include <stdio.h>
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
char buf[65536];
|
||||
int n = fread (buf, 1, sizeof buf, stdin);
|
||||
printf ("%lu\n", cksum (buf, n));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
8
src/tests/cksum.h
Normal file
8
src/tests/cksum.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef TESTS_CKSUM_H
|
||||
#define TESTS_CKSUM_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
unsigned long cksum(const void *, size_t);
|
||||
|
||||
#endif /* tests/cksum.h */
|
||||
87
src/tests/cksum.pm
Normal file
87
src/tests/cksum.pm
Normal file
@@ -0,0 +1,87 @@
|
||||
# From the `cksum' entry in SUSv3.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my (@crctab) =
|
||||
(0x00000000,
|
||||
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
|
||||
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
|
||||
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
|
||||
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
|
||||
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
|
||||
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
|
||||
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
|
||||
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
|
||||
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
|
||||
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
|
||||
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
|
||||
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
|
||||
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
|
||||
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
|
||||
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
|
||||
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
|
||||
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
|
||||
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
|
||||
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
|
||||
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
|
||||
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
|
||||
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
|
||||
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
|
||||
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
|
||||
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
|
||||
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
|
||||
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
|
||||
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
|
||||
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
|
||||
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
|
||||
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
|
||||
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
|
||||
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
|
||||
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4);
|
||||
|
||||
sub cksum {
|
||||
my ($b) = @_;
|
||||
my ($n) = length ($b);
|
||||
my ($s) = 0;
|
||||
for my $i (0...$n - 1) {
|
||||
my ($c) = ord (substr ($b, $i, 1));
|
||||
$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
|
||||
$s &= 0xffff_ffff;
|
||||
}
|
||||
while ($n != 0) {
|
||||
my ($c) = $n & 0xff;
|
||||
$n >>= 8;
|
||||
$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
|
||||
$s &= 0xffff_ffff;
|
||||
}
|
||||
return ~$s & 0xffff_ffff;
|
||||
}
|
||||
|
||||
sub cksum_file {
|
||||
my ($file) = @_;
|
||||
open (FILE, '<', $file) or die "$file: open: $!\n";
|
||||
my ($data);
|
||||
sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n";
|
||||
close (FILE);
|
||||
return cksum ($data);
|
||||
}
|
||||
|
||||
1;
|
||||
4
src/tests/devices/Grading
Normal file
4
src/tests/devices/Grading
Normal file
@@ -0,0 +1,4 @@
|
||||
# Percentage of the testing point total designated for each set of tests.
|
||||
|
||||
50.0% tests/devices/Rubric.alarmfunc
|
||||
50.0% tests/devices/Rubric.alarmrobust
|
||||
48
src/tests/devices/Make.tests
Normal file
48
src/tests/devices/Make.tests
Normal file
@@ -0,0 +1,48 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
# Test names.
|
||||
tests/devices_TESTS = $(addprefix tests/devices/,alarm-single \
|
||||
alarm-multiple alarm-simultaneous alarm-no-busy-wait alarm-one \
|
||||
alarm-zero alarm-negative)
|
||||
|
||||
# Sources for tests.
|
||||
tests/devices_SRC = tests/devices/tests.c
|
||||
tests/devices_SRC += tests/devices/alarm-wait.c
|
||||
tests/devices_SRC += tests/devices/alarm-simultaneous.c
|
||||
tests/devices_SRC += tests/devices/alarm-no-busy-wait.c
|
||||
tests/devices_SRC += tests/devices/alarm-one.c
|
||||
tests/devices_SRC += tests/devices/alarm-zero.c
|
||||
tests/devices_SRC += tests/devices/alarm-negative.c
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4
src/tests/devices/Rubric.alarmfunc
Normal file
4
src/tests/devices/Rubric.alarmfunc
Normal file
@@ -0,0 +1,4 @@
|
||||
Functionality of alarm clock:
|
||||
10 alarm-no-busy-wait
|
||||
5 alarm-single
|
||||
5 alarm-multiple
|
||||
5
src/tests/devices/Rubric.alarmrobust
Normal file
5
src/tests/devices/Rubric.alarmrobust
Normal file
@@ -0,0 +1,5 @@
|
||||
Robustness of alarm clock:
|
||||
10 alarm-simultaneous
|
||||
5 alarm-one
|
||||
5 alarm-zero
|
||||
5 alarm-negative
|
||||
4
src/tests/devices/alarm-multiple.ck
Normal file
4
src/tests/devices/alarm-multiple.ck
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- perl -*-
|
||||
use tests::tests;
|
||||
use tests::devices::alarm;
|
||||
check_alarm (7);
|
||||
15
src/tests/devices/alarm-negative.c
Normal file
15
src/tests/devices/alarm-negative.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Tests timer_sleep(-100). Only requirement is that it not crash. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/devices/tests.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
void
|
||||
test_alarm_negative (void)
|
||||
{
|
||||
timer_sleep (-100);
|
||||
pass ();
|
||||
}
|
||||
10
src/tests/devices/alarm-negative.ck
Normal file
10
src/tests/devices/alarm-negative.ck
Normal file
@@ -0,0 +1,10 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected ([<<'EOF']);
|
||||
(alarm-negative) begin
|
||||
(alarm-negative) PASS
|
||||
(alarm-negative) end
|
||||
EOF
|
||||
pass;
|
||||
131
src/tests/devices/alarm-no-busy-wait.c
Normal file
131
src/tests/devices/alarm-no-busy-wait.c
Normal file
@@ -0,0 +1,131 @@
|
||||
/* Creates 5 threads, each of which sleeps for 20 ticks.
|
||||
Checks to ensure that the threads are genuinely sleeping (not busy-waiting)
|
||||
by inspecting the ready_list half-way though the sleep time.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/devices/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static void test_sleep (int thread_cnt, int iterations);
|
||||
|
||||
void
|
||||
test_alarm_no_busy_wait (void)
|
||||
{
|
||||
test_sleep (5, 1);
|
||||
}
|
||||
|
||||
/* Information about the test. */
|
||||
struct sleep_test
|
||||
{
|
||||
int64_t start; /* Current time at start of test. */
|
||||
int iterations; /* Number of iterations per thread. */
|
||||
|
||||
/* Output. */
|
||||
struct lock output_lock; /* Lock protecting output buffer. */
|
||||
int *output_pos; /* Current position in output buffer. */
|
||||
};
|
||||
|
||||
/* Information about an individual thread in the test. */
|
||||
struct sleep_thread
|
||||
{
|
||||
struct sleep_test *test; /* Info shared between all threads. */
|
||||
int id; /* Sleeper ID. */
|
||||
int duration; /* Number of ticks to sleep. */
|
||||
int iterations; /* Iterations counted so far. */
|
||||
};
|
||||
|
||||
static void sleeper (void *);
|
||||
|
||||
/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
|
||||
static void
|
||||
test_sleep (int thread_cnt, int iterations)
|
||||
{
|
||||
struct sleep_test test;
|
||||
struct sleep_thread *threads;
|
||||
int *output;
|
||||
int i;
|
||||
|
||||
/* This test does not work with the MLFQS. */
|
||||
ASSERT (!thread_mlfqs);
|
||||
|
||||
msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
|
||||
msg ("Each thread sleeps for 20 ticks at a time,");
|
||||
msg ("Test is successful if the threads are not running");
|
||||
msg ("when they are supposed to be asleep.");
|
||||
|
||||
/* Allocate memory. */
|
||||
threads = malloc (sizeof *threads * (int)(thread_cnt));
|
||||
output = malloc (sizeof *output * (int)(iterations * thread_cnt * 2));
|
||||
if (threads == NULL || output == NULL)
|
||||
PANIC ("couldn't allocate memory for test");
|
||||
|
||||
/* Initialize test. */
|
||||
test.start = timer_ticks () + 100;
|
||||
test.iterations = iterations;
|
||||
lock_init (&test.output_lock);
|
||||
test.output_pos = output;
|
||||
|
||||
/* Start threads. */
|
||||
ASSERT (output != NULL);
|
||||
for (i = 0; i < thread_cnt; i++)
|
||||
{
|
||||
struct sleep_thread *t = threads + i;
|
||||
char name[16];
|
||||
|
||||
t->test = &test;
|
||||
t->id = i;
|
||||
t->duration = 20;
|
||||
t->iterations = 0;
|
||||
|
||||
snprintf (name, sizeof name, "thread %d", i);
|
||||
thread_create (name, PRI_DEFAULT, sleeper, t);
|
||||
}
|
||||
|
||||
/* yield the CPU so that the new threads have sufficient time to go to sleep */
|
||||
timer_sleep(10);
|
||||
|
||||
/* now check that all of the threads are indeed asleep */
|
||||
|
||||
/* Acquire the output lock so we cannot race with the sleeping threads */
|
||||
lock_acquire (&test.output_lock);
|
||||
|
||||
/* Inspect and print the number of threads on the ready list . */
|
||||
size_t num_ready_threads = threads_ready();
|
||||
msg("%d threads on the ready list", num_ready_threads);
|
||||
|
||||
if (num_ready_threads > 0)
|
||||
fail ("too many threads on the ready_list (they should all be asleep)!");
|
||||
|
||||
pass ();
|
||||
lock_release (&test.output_lock);
|
||||
|
||||
/* Wait long enough for all the threads to finish. */
|
||||
timer_sleep (100 + thread_cnt * iterations * 20 + 100);
|
||||
|
||||
/* Clean up after ourselves */
|
||||
free (output);
|
||||
free (threads);
|
||||
}
|
||||
|
||||
/* Sleeper thread. */
|
||||
static void
|
||||
sleeper (void *t_)
|
||||
{
|
||||
struct sleep_thread *t = t_;
|
||||
struct sleep_test *test = t->test;
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= test->iterations; i++)
|
||||
{
|
||||
int64_t sleep_until = test->start + i * t->duration;
|
||||
timer_sleep (sleep_until - timer_ticks ());
|
||||
lock_acquire (&test->output_lock);
|
||||
*test->output_pos++ = t->id;
|
||||
lock_release (&test->output_lock);
|
||||
}
|
||||
}
|
||||
15
src/tests/devices/alarm-no-busy-wait.ck
Normal file
15
src/tests/devices/alarm-no-busy-wait.ck
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected ([<<'EOF']);
|
||||
(alarm-no-busy-wait) begin
|
||||
(alarm-no-busy-wait) Creating 5 threads to sleep 1 times each.
|
||||
(alarm-no-busy-wait) Each thread sleeps for 20 ticks at a time,
|
||||
(alarm-no-busy-wait) Test is successful if the threads are not running
|
||||
(alarm-no-busy-wait) when they are supposed to be asleep.
|
||||
(alarm-no-busy-wait) 0 threads on the ready list
|
||||
(alarm-no-busy-wait) PASS
|
||||
(alarm-no-busy-wait) end
|
||||
EOF
|
||||
pass;
|
||||
18
src/tests/devices/alarm-one.c
Normal file
18
src/tests/devices/alarm-one.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Tests timer_sleep(1), which should return shortly after called.
|
||||
This test can expose a race-condition if the kernel attempts to unblock
|
||||
the thread before it has been blocked.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/devices/tests.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
void
|
||||
test_alarm_one (void)
|
||||
{
|
||||
timer_sleep (1);
|
||||
pass ();
|
||||
}
|
||||
10
src/tests/devices/alarm-one.ck
Normal file
10
src/tests/devices/alarm-one.ck
Normal file
@@ -0,0 +1,10 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected ([<<'EOF']);
|
||||
(alarm-one) begin
|
||||
(alarm-one) PASS
|
||||
(alarm-one) end
|
||||
EOF
|
||||
pass;
|
||||
94
src/tests/devices/alarm-simultaneous.c
Normal file
94
src/tests/devices/alarm-simultaneous.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/* Creates N threads, each of which sleeps a different, fixed
|
||||
duration, M times. Records the wake-up order and verifies
|
||||
that it is valid. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/devices/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static void test_sleep (int thread_cnt, int iterations);
|
||||
|
||||
void
|
||||
test_alarm_simultaneous (void)
|
||||
{
|
||||
test_sleep (3, 5);
|
||||
}
|
||||
|
||||
/* Information about the test. */
|
||||
struct sleep_test
|
||||
{
|
||||
int64_t start; /* Current time at start of test. */
|
||||
int iterations; /* Number of iterations per thread. */
|
||||
int *output_pos; /* Current position in output buffer. */
|
||||
};
|
||||
|
||||
static void sleeper (void *);
|
||||
|
||||
/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
|
||||
static void
|
||||
test_sleep (int thread_cnt, int iterations)
|
||||
{
|
||||
struct sleep_test test;
|
||||
int *output;
|
||||
int i;
|
||||
|
||||
/* This test does not work with the MLFQS. */
|
||||
ASSERT (!thread_mlfqs);
|
||||
|
||||
msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
|
||||
msg ("Each thread sleeps 10 ticks each time.");
|
||||
msg ("Within an iteration, all threads should wake up on the same tick.");
|
||||
|
||||
/* Allocate memory. */
|
||||
output = malloc (sizeof *output * (int)(iterations * thread_cnt * 2));
|
||||
if (output == NULL)
|
||||
PANIC ("couldn't allocate memory for test");
|
||||
|
||||
/* Initialize test. */
|
||||
test.start = timer_ticks () + 100;
|
||||
test.iterations = iterations;
|
||||
test.output_pos = output;
|
||||
|
||||
/* Start threads. */
|
||||
ASSERT (output != NULL);
|
||||
for (i = 0; i < thread_cnt; i++)
|
||||
{
|
||||
char name[16];
|
||||
snprintf (name, sizeof name, "thread %d", i);
|
||||
thread_create (name, PRI_DEFAULT, sleeper, &test);
|
||||
}
|
||||
|
||||
/* Wait long enough for all the threads to finish. */
|
||||
timer_sleep (100 + iterations * 10 + 100);
|
||||
|
||||
/* Print completion order. */
|
||||
msg ("iteration 0, thread 0: woke up after %d ticks", output[0]);
|
||||
for (i = 1; i < test.output_pos - output; i++)
|
||||
msg ("iteration %d, thread %d: woke up %d ticks later",
|
||||
i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]);
|
||||
|
||||
free (output);
|
||||
}
|
||||
|
||||
/* Sleeper thread. */
|
||||
static void
|
||||
sleeper (void *test_)
|
||||
{
|
||||
struct sleep_test *test = test_;
|
||||
int i;
|
||||
|
||||
/* Make sure we're at the beginning of a timer tick. */
|
||||
timer_sleep (1);
|
||||
|
||||
for (i = 1; i <= test->iterations; i++)
|
||||
{
|
||||
int64_t sleep_until = test->start + i * 10;
|
||||
timer_sleep (sleep_until - timer_ticks ());
|
||||
*test->output_pos++ = (int)(timer_ticks () - test->start);
|
||||
thread_yield ();
|
||||
}
|
||||
}
|
||||
27
src/tests/devices/alarm-simultaneous.ck
Normal file
27
src/tests/devices/alarm-simultaneous.ck
Normal file
@@ -0,0 +1,27 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected ([<<'EOF']);
|
||||
(alarm-simultaneous) begin
|
||||
(alarm-simultaneous) Creating 3 threads to sleep 5 times each.
|
||||
(alarm-simultaneous) Each thread sleeps 10 ticks each time.
|
||||
(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick.
|
||||
(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks
|
||||
(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later
|
||||
(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later
|
||||
(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later
|
||||
(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later
|
||||
(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later
|
||||
(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later
|
||||
(alarm-simultaneous) end
|
||||
EOF
|
||||
pass;
|
||||
4
src/tests/devices/alarm-single.ck
Normal file
4
src/tests/devices/alarm-single.ck
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- perl -*-
|
||||
use tests::tests;
|
||||
use tests::devices::alarm;
|
||||
check_alarm (1);
|
||||
152
src/tests/devices/alarm-wait.c
Normal file
152
src/tests/devices/alarm-wait.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/* Creates N threads, each of which sleeps a different, fixed
|
||||
duration, M times. Records the wake-up order and verifies
|
||||
that it is valid. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/devices/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static void test_sleep (int thread_cnt, int iterations);
|
||||
|
||||
void
|
||||
test_alarm_single (void)
|
||||
{
|
||||
test_sleep (5, 1);
|
||||
}
|
||||
|
||||
void
|
||||
test_alarm_multiple (void)
|
||||
{
|
||||
test_sleep (5, 7);
|
||||
}
|
||||
|
||||
/* Information about the test. */
|
||||
struct sleep_test
|
||||
{
|
||||
int64_t start; /* Current time at start of test. */
|
||||
int iterations; /* Number of iterations per thread. */
|
||||
|
||||
/* Output. */
|
||||
struct lock output_lock; /* Lock protecting output buffer. */
|
||||
int *output_pos; /* Current position in output buffer. */
|
||||
};
|
||||
|
||||
/* Information about an individual thread in the test. */
|
||||
struct sleep_thread
|
||||
{
|
||||
struct sleep_test *test; /* Info shared between all threads. */
|
||||
int id; /* Sleeper ID. */
|
||||
int duration; /* Number of ticks to sleep. */
|
||||
int iterations; /* Iterations counted so far. */
|
||||
};
|
||||
|
||||
static void sleeper (void *);
|
||||
|
||||
/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
|
||||
static void
|
||||
test_sleep (int thread_cnt, int iterations)
|
||||
{
|
||||
struct sleep_test test;
|
||||
struct sleep_thread *threads;
|
||||
int *output, *op;
|
||||
int product;
|
||||
int i;
|
||||
|
||||
/* This test does not work with the MLFQS. */
|
||||
ASSERT (!thread_mlfqs);
|
||||
|
||||
msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
|
||||
msg ("Thread 0 sleeps 10 ticks each time,");
|
||||
msg ("thread 1 sleeps 20 ticks each time, and so on.");
|
||||
msg ("If successful, product of iteration count and");
|
||||
msg ("sleep duration will appear in nondescending order.");
|
||||
|
||||
/* Allocate memory. */
|
||||
threads = malloc (sizeof *threads * (int)(thread_cnt));
|
||||
output = malloc (sizeof *output * (int)(iterations * thread_cnt * 2));
|
||||
if (threads == NULL || output == NULL)
|
||||
PANIC ("couldn't allocate memory for test");
|
||||
|
||||
/* Initialize test. */
|
||||
test.start = timer_ticks () + 100;
|
||||
test.iterations = iterations;
|
||||
lock_init (&test.output_lock);
|
||||
test.output_pos = output;
|
||||
|
||||
/* Start threads. */
|
||||
ASSERT (output != NULL);
|
||||
for (i = 0; i < thread_cnt; i++)
|
||||
{
|
||||
struct sleep_thread *t = threads + i;
|
||||
char name[16];
|
||||
|
||||
t->test = &test;
|
||||
t->id = i;
|
||||
t->duration = (i + 1) * 10;
|
||||
t->iterations = 0;
|
||||
|
||||
snprintf (name, sizeof name, "thread %d", i);
|
||||
thread_create (name, PRI_DEFAULT, sleeper, t);
|
||||
}
|
||||
|
||||
/* Wait long enough for all the threads to finish. */
|
||||
timer_sleep (100 + thread_cnt * iterations * 10 + 100);
|
||||
|
||||
/* Acquire the output lock in case some rogue thread is still
|
||||
running. */
|
||||
lock_acquire (&test.output_lock);
|
||||
|
||||
/* Print completion order. */
|
||||
product = 0;
|
||||
for (op = output; op < test.output_pos; op++)
|
||||
{
|
||||
struct sleep_thread *t;
|
||||
int new_prod;
|
||||
|
||||
ASSERT (*op >= 0 && *op < thread_cnt);
|
||||
t = threads + *op;
|
||||
|
||||
new_prod = ++t->iterations * t->duration;
|
||||
|
||||
msg ("thread %d: duration=%d, iteration=%d, product=%d",
|
||||
t->id, t->duration, t->iterations, new_prod);
|
||||
|
||||
if (new_prod >= product)
|
||||
product = new_prod;
|
||||
else
|
||||
fail ("thread %d woke up out of order (%d > %d)!",
|
||||
t->id, product, new_prod);
|
||||
}
|
||||
|
||||
/* Verify that we had the proper number of wakeups. */
|
||||
for (i = 0; i < thread_cnt; i++)
|
||||
if (threads[i].iterations != iterations)
|
||||
fail ("thread %d woke up %d times instead of %d",
|
||||
i, threads[i].iterations, iterations);
|
||||
|
||||
lock_release (&test.output_lock);
|
||||
free (output);
|
||||
free (threads);
|
||||
}
|
||||
|
||||
/* Sleeper thread. */
|
||||
static void
|
||||
sleeper (void *t_)
|
||||
{
|
||||
struct sleep_thread *t = t_;
|
||||
struct sleep_test *test = t->test;
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= test->iterations; i++)
|
||||
{
|
||||
int64_t sleep_until = test->start + i * t->duration;
|
||||
timer_sleep (sleep_until - timer_ticks ());
|
||||
lock_acquire (&test->output_lock);
|
||||
*test->output_pos++ = t->id;
|
||||
lock_release (&test->output_lock);
|
||||
}
|
||||
}
|
||||
15
src/tests/devices/alarm-zero.c
Normal file
15
src/tests/devices/alarm-zero.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Tests timer_sleep(0), which should return immediately. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/devices/tests.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
void
|
||||
test_alarm_zero (void)
|
||||
{
|
||||
timer_sleep (0);
|
||||
pass ();
|
||||
}
|
||||
10
src/tests/devices/alarm-zero.ck
Normal file
10
src/tests/devices/alarm-zero.ck
Normal file
@@ -0,0 +1,10 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected ([<<'EOF']);
|
||||
(alarm-zero) begin
|
||||
(alarm-zero) PASS
|
||||
(alarm-zero) end
|
||||
EOF
|
||||
pass;
|
||||
32
src/tests/devices/alarm.pm
Normal file
32
src/tests/devices/alarm.pm
Normal file
@@ -0,0 +1,32 @@
|
||||
sub check_alarm {
|
||||
my ($iterations) = @_;
|
||||
our ($test);
|
||||
|
||||
@output = read_text_file ("$test.output");
|
||||
common_checks ("run", @output);
|
||||
|
||||
my (@products);
|
||||
for (my ($i) = 0; $i < $iterations; $i++) {
|
||||
for (my ($t) = 0; $t < 5; $t++) {
|
||||
push (@products, ($i + 1) * ($t + 1) * 10);
|
||||
}
|
||||
}
|
||||
@products = sort {$a <=> $b} @products;
|
||||
|
||||
local ($_);
|
||||
foreach (@output) {
|
||||
fail $_ if /out of order/i;
|
||||
|
||||
my ($p) = /product=(\d+)$/;
|
||||
next if !defined $p;
|
||||
|
||||
my ($q) = shift (@products);
|
||||
fail "Too many wakeups.\n" if !defined $q;
|
||||
fail "Out of order wakeups ($p vs. $q).\n" if $p != $q;
|
||||
}
|
||||
fail scalar (@products) . " fewer wakeups than expected.\n"
|
||||
if @products != 0;
|
||||
pass;
|
||||
}
|
||||
|
||||
1;
|
||||
117
src/tests/devices/tests.c
Normal file
117
src/tests/devices/tests.c
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "tests/devices/tests.h"
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct test
|
||||
{
|
||||
const char *name;
|
||||
test_func *function;
|
||||
};
|
||||
|
||||
#ifndef THREADS
|
||||
static const struct test tests[] =
|
||||
{
|
||||
{"alarm-single", test_alarm_single},
|
||||
{"alarm-multiple", test_alarm_multiple},
|
||||
{"alarm-simultaneous", test_alarm_simultaneous},
|
||||
{"alarm-no-busy-wait", test_alarm_no_busy_wait},
|
||||
{"alarm-one", test_alarm_one},
|
||||
{"alarm-zero", test_alarm_zero},
|
||||
{"alarm-negative", test_alarm_negative}
|
||||
};
|
||||
#else
|
||||
static const struct test tests[] =
|
||||
{
|
||||
{"alarm-single", test_alarm_single},
|
||||
{"alarm-multiple", test_alarm_multiple},
|
||||
{"alarm-simultaneous", test_alarm_simultaneous},
|
||||
{"alarm-no-busy-wait", test_alarm_no_busy_wait},
|
||||
{"alarm-one", test_alarm_one},
|
||||
{"alarm-zero", test_alarm_zero},
|
||||
{"alarm-negative", test_alarm_negative},
|
||||
{"alarm-priority", test_alarm_priority},
|
||||
{"priority-change", test_priority_change},
|
||||
{"priority-donate-one", test_priority_donate_one},
|
||||
{"priority-donate-multiple", test_priority_donate_multiple},
|
||||
{"priority-donate-multiple2", test_priority_donate_multiple2},
|
||||
{"priority-donate-nest", test_priority_donate_nest},
|
||||
{"priority-donate-sema", test_priority_donate_sema},
|
||||
{"priority-donate-lower", test_priority_donate_lower},
|
||||
{"priority-donate-chain", test_priority_donate_chain},
|
||||
{"priority-preservation", test_priority_preservation},
|
||||
{"priority-fifo", test_priority_fifo},
|
||||
{"priority-preempt", test_priority_preempt},
|
||||
{"priority-sema", test_priority_sema},
|
||||
{"priority-condvar", test_priority_condvar},
|
||||
{"mlfqs-load-1", test_mlfqs_load_1},
|
||||
{"mlfqs-load-60", test_mlfqs_load_60},
|
||||
{"mlfqs-load-avg", test_mlfqs_load_avg},
|
||||
{"mlfqs-recent-1", test_mlfqs_recent_1},
|
||||
{"mlfqs-fair-2", test_mlfqs_fair_2},
|
||||
{"mlfqs-fair-20", test_mlfqs_fair_20},
|
||||
{"mlfqs-nice-2", test_mlfqs_nice_2},
|
||||
{"mlfqs-nice-10", test_mlfqs_nice_10},
|
||||
{"mlfqs-block", test_mlfqs_block},
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char *test_name;
|
||||
|
||||
/* Runs the test named NAME. */
|
||||
void
|
||||
run_test (const char *name)
|
||||
{
|
||||
const struct test *t;
|
||||
|
||||
for (t = tests; t < tests + sizeof tests / sizeof *tests; t++)
|
||||
if (!strcmp (name, t->name))
|
||||
{
|
||||
test_name = name;
|
||||
msg ("begin");
|
||||
t->function ();
|
||||
msg ("end");
|
||||
return;
|
||||
}
|
||||
PANIC ("no test named \"%s\"", name);
|
||||
}
|
||||
|
||||
/* Prints FORMAT as if with printf(),
|
||||
prefixing the output by the name of the test
|
||||
and following it with a new-line character. */
|
||||
void
|
||||
msg (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
printf ("(%s) ", test_name);
|
||||
va_start (args, format);
|
||||
vprintf (format, args);
|
||||
va_end (args);
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
/* Prints failure message FORMAT as if with printf(),
|
||||
prefixing the output by the name of the test and FAIL:
|
||||
and following it with a new-line character,
|
||||
and then panics the kernel. */
|
||||
void
|
||||
fail (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
printf ("(%s) FAIL: ", test_name);
|
||||
va_start (args, format);
|
||||
vprintf (format, args);
|
||||
va_end (args);
|
||||
putchar ('\n');
|
||||
|
||||
PANIC ("test failed");
|
||||
}
|
||||
|
||||
/* Prints a message indicating the current test passed. */
|
||||
void
|
||||
pass (void)
|
||||
{
|
||||
printf ("(%s) PASS\n", test_name);
|
||||
}
|
||||
47
src/tests/devices/tests.h
Normal file
47
src/tests/devices/tests.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef TESTS_DEVICES_TESTS_H
|
||||
#define TESTS_DEVICES_TESTS_H
|
||||
|
||||
void run_test (const char *);
|
||||
|
||||
typedef void test_func (void);
|
||||
|
||||
extern test_func test_alarm_single;
|
||||
extern test_func test_alarm_multiple;
|
||||
extern test_func test_alarm_simultaneous;
|
||||
extern test_func test_alarm_no_busy_wait;
|
||||
extern test_func test_alarm_one;
|
||||
extern test_func test_alarm_zero;
|
||||
extern test_func test_alarm_negative;
|
||||
|
||||
#ifdef THREADS
|
||||
extern test_func test_alarm_priority;
|
||||
extern test_func test_priority_change;
|
||||
extern test_func test_priority_donate_one;
|
||||
extern test_func test_priority_donate_multiple;
|
||||
extern test_func test_priority_donate_multiple2;
|
||||
extern test_func test_priority_donate_sema;
|
||||
extern test_func test_priority_donate_nest;
|
||||
extern test_func test_priority_donate_lower;
|
||||
extern test_func test_priority_donate_chain;
|
||||
extern test_func test_priority_preservation;
|
||||
extern test_func test_priority_fifo;
|
||||
extern test_func test_priority_preempt;
|
||||
extern test_func test_priority_sema;
|
||||
extern test_func test_priority_condvar;
|
||||
extern test_func test_mlfqs_load_1;
|
||||
extern test_func test_mlfqs_load_60;
|
||||
extern test_func test_mlfqs_load_avg;
|
||||
extern test_func test_mlfqs_recent_1;
|
||||
extern test_func test_mlfqs_fair_2;
|
||||
extern test_func test_mlfqs_fair_20;
|
||||
extern test_func test_mlfqs_nice_2;
|
||||
extern test_func test_mlfqs_nice_10;
|
||||
extern test_func test_mlfqs_block;
|
||||
#endif
|
||||
|
||||
void msg (const char *, ...);
|
||||
void fail (const char *, ...);
|
||||
void pass (void);
|
||||
|
||||
#endif /* tests/devices/tests.h */
|
||||
|
||||
18
src/tests/filesys/Grading.no-vm
Normal file
18
src/tests/filesys/Grading.no-vm
Normal file
@@ -0,0 +1,18 @@
|
||||
# Percentage of the testing point total designated for each set of
|
||||
# tests.
|
||||
|
||||
# This task is primarily about implementing the file system, but
|
||||
# all the previous functionality should work too. It's not too easy
|
||||
# to screw it up, thus the emphasis.
|
||||
|
||||
# 65% for extended file system features.
|
||||
30% tests/filesys/extended/Rubric.functionality
|
||||
15% tests/filesys/extended/Rubric.robustness
|
||||
20% tests/filesys/extended/Rubric.persistence
|
||||
|
||||
# 20% to not break the provided file system features.
|
||||
20% tests/filesys/base/Rubric
|
||||
|
||||
# 15% for the rest.
|
||||
10% tests/userprog/Rubric.functionality
|
||||
5% tests/userprog/Rubric.robustness
|
||||
22
src/tests/filesys/Grading.with-vm
Normal file
22
src/tests/filesys/Grading.with-vm
Normal file
@@ -0,0 +1,22 @@
|
||||
# Percentage of the testing point total designated for each set of
|
||||
# tests.
|
||||
|
||||
# This task is primarily about implementing the file system, but
|
||||
# all the previous functionality should work too. It's not too easy
|
||||
# to screw it up, thus the emphasis.
|
||||
|
||||
# 65% for extended file system features.
|
||||
30% tests/filesys/extended/Rubric.functionality
|
||||
15% tests/filesys/extended/Rubric.robustness
|
||||
20% tests/filesys/extended/Rubric.persistence
|
||||
|
||||
# 20% to not break the provided file system features.
|
||||
20% tests/filesys/base/Rubric
|
||||
|
||||
# 15% for the rest.
|
||||
10% tests/userprog/Rubric.functionality
|
||||
5% tests/userprog/Rubric.robustness
|
||||
|
||||
# Up to 10% bonus for working VM functionality.
|
||||
8% tests/vm/Rubric.functionality
|
||||
2% tests/vm/Rubric.robustness
|
||||
18
src/tests/filesys/base/Make.tests
Normal file
18
src/tests/filesys/base/Make.tests
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \
|
||||
lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \
|
||||
sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write)
|
||||
|
||||
tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \
|
||||
tests/filesys/base/,child-syn-read child-syn-wrt)
|
||||
|
||||
$(foreach prog,$(tests/filesys/base_PROGS), \
|
||||
$(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
|
||||
$(foreach prog,$(tests/filesys/base_TESTS), \
|
||||
$(eval $(prog)_SRC += tests/main.c))
|
||||
|
||||
tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read
|
||||
tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt
|
||||
|
||||
tests/filesys/base/syn-read.output: TIMEOUT = 300
|
||||
19
src/tests/filesys/base/Rubric
Normal file
19
src/tests/filesys/base/Rubric
Normal file
@@ -0,0 +1,19 @@
|
||||
Functionality of base file system:
|
||||
- Test basic support for small files.
|
||||
1 sm-create
|
||||
2 sm-full
|
||||
2 sm-random
|
||||
2 sm-seq-block
|
||||
3 sm-seq-random
|
||||
|
||||
- Test basic support for large files.
|
||||
1 lg-create
|
||||
2 lg-full
|
||||
2 lg-random
|
||||
2 lg-seq-block
|
||||
3 lg-seq-random
|
||||
|
||||
- Test synchronized multiprogram access to files.
|
||||
4 syn-read
|
||||
4 syn-write
|
||||
2 syn-remove
|
||||
44
src/tests/filesys/base/child-syn-read.c
Normal file
44
src/tests/filesys/base/child-syn-read.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/* Child process for syn-read test.
|
||||
Reads the contents of a test file a byte at a time, in the
|
||||
hope that this will take long enough that we can get a
|
||||
significant amount of contention in the kernel file system
|
||||
code. */
|
||||
|
||||
#include <random.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
#include "tests/lib.h"
|
||||
#include "tests/filesys/base/syn-read.h"
|
||||
|
||||
const char *test_name = "child-syn-read";
|
||||
|
||||
static char buf[BUF_SIZE];
|
||||
|
||||
int
|
||||
main (int argc, const char *argv[])
|
||||
{
|
||||
int child_idx;
|
||||
int fd;
|
||||
size_t i;
|
||||
|
||||
quiet = true;
|
||||
|
||||
CHECK (argc == 2, "argc must be 2, actually %d", argc);
|
||||
child_idx = atoi (argv[1]);
|
||||
|
||||
random_init (0);
|
||||
random_bytes (buf, sizeof buf);
|
||||
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
|
||||
for (i = 0; i < sizeof buf; i++)
|
||||
{
|
||||
char c;
|
||||
CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name);
|
||||
compare_bytes (&c, buf + i, 1, i, file_name);
|
||||
}
|
||||
close (fd);
|
||||
|
||||
return child_idx;
|
||||
}
|
||||
|
||||
35
src/tests/filesys/base/child-syn-wrt.c
Normal file
35
src/tests/filesys/base/child-syn-wrt.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Child process for syn-read test.
|
||||
Writes into part of a test file. Other processes will be
|
||||
writing into other parts at the same time. */
|
||||
|
||||
#include <random.h>
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
#include "tests/lib.h"
|
||||
#include "tests/filesys/base/syn-write.h"
|
||||
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int child_idx;
|
||||
int fd;
|
||||
|
||||
quiet = true;
|
||||
|
||||
CHECK (argc == 2, "argc must be 2, actually %d", argc);
|
||||
child_idx = atoi (argv[1]);
|
||||
|
||||
random_init (0);
|
||||
random_bytes (buf, sizeof buf);
|
||||
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
|
||||
seek (fd, CHUNK_SIZE * (unsigned) child_idx);
|
||||
CHECK (write (fd, buf + CHUNK_SIZE * (unsigned) child_idx, CHUNK_SIZE) > 0,
|
||||
"write \"%s\"", file_name);
|
||||
msg ("close \"%s\"", file_name);
|
||||
close (fd);
|
||||
|
||||
return child_idx;
|
||||
}
|
||||
20
src/tests/filesys/base/full.inc
Normal file
20
src/tests/filesys/base/full.inc
Normal file
@@ -0,0 +1,20 @@
|
||||
/* -*- c -*- */
|
||||
|
||||
#include "tests/filesys/seq-test.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
static char buf[TEST_SIZE];
|
||||
|
||||
static size_t
|
||||
return_test_size (void)
|
||||
{
|
||||
return TEST_SIZE;
|
||||
}
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
seq_test ("quux",
|
||||
buf, sizeof buf, sizeof buf,
|
||||
return_test_size, NULL);
|
||||
}
|
||||
5
src/tests/filesys/base/lg-create.c
Normal file
5
src/tests/filesys/base/lg-create.c
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Tests that create properly zeros out the contents of a fairly
|
||||
large file. */
|
||||
|
||||
#define TEST_SIZE 75678
|
||||
#include "tests/filesys/create.inc"
|
||||
13
src/tests/filesys/base/lg-create.ck
Normal file
13
src/tests/filesys/base/lg-create.ck
Normal file
@@ -0,0 +1,13 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(lg-create) begin
|
||||
(lg-create) create "blargle"
|
||||
(lg-create) open "blargle" for verification
|
||||
(lg-create) verified contents of "blargle"
|
||||
(lg-create) close "blargle"
|
||||
(lg-create) end
|
||||
EOF
|
||||
pass;
|
||||
6
src/tests/filesys/base/lg-full.c
Normal file
6
src/tests/filesys/base/lg-full.c
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Writes out the contents of a fairly large file all at once,
|
||||
and then reads it back to make sure that it was written
|
||||
properly. */
|
||||
|
||||
#define TEST_SIZE 75678
|
||||
#include "tests/filesys/base/full.inc"
|
||||
16
src/tests/filesys/base/lg-full.ck
Normal file
16
src/tests/filesys/base/lg-full.ck
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(lg-full) begin
|
||||
(lg-full) create "quux"
|
||||
(lg-full) open "quux"
|
||||
(lg-full) writing "quux"
|
||||
(lg-full) close "quux"
|
||||
(lg-full) open "quux" for verification
|
||||
(lg-full) verified contents of "quux"
|
||||
(lg-full) close "quux"
|
||||
(lg-full) end
|
||||
EOF
|
||||
pass;
|
||||
7
src/tests/filesys/base/lg-random.c
Normal file
7
src/tests/filesys/base/lg-random.c
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Writes out the content of a fairly large file in random order,
|
||||
then reads it back in random order to verify that it was
|
||||
written properly. */
|
||||
|
||||
#define BLOCK_SIZE 512U
|
||||
#define TEST_SIZE (512 * 150)
|
||||
#include "tests/filesys/base/random.inc"
|
||||
14
src/tests/filesys/base/lg-random.ck
Normal file
14
src/tests/filesys/base/lg-random.ck
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(lg-random) begin
|
||||
(lg-random) create "bazzle"
|
||||
(lg-random) open "bazzle"
|
||||
(lg-random) write "bazzle" in random order
|
||||
(lg-random) read "bazzle" in random order
|
||||
(lg-random) close "bazzle"
|
||||
(lg-random) end
|
||||
EOF
|
||||
pass;
|
||||
7
src/tests/filesys/base/lg-seq-block.c
Normal file
7
src/tests/filesys/base/lg-seq-block.c
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Writes out a fairly large file sequentially, one fixed-size
|
||||
block at a time, then reads it back to verify that it was
|
||||
written properly. */
|
||||
|
||||
#define TEST_SIZE 75678
|
||||
#define BLOCK_SIZE 513
|
||||
#include "tests/filesys/base/seq-block.inc"
|
||||
16
src/tests/filesys/base/lg-seq-block.ck
Normal file
16
src/tests/filesys/base/lg-seq-block.ck
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(lg-seq-block) begin
|
||||
(lg-seq-block) create "noodle"
|
||||
(lg-seq-block) open "noodle"
|
||||
(lg-seq-block) writing "noodle"
|
||||
(lg-seq-block) close "noodle"
|
||||
(lg-seq-block) open "noodle" for verification
|
||||
(lg-seq-block) verified contents of "noodle"
|
||||
(lg-seq-block) close "noodle"
|
||||
(lg-seq-block) end
|
||||
EOF
|
||||
pass;
|
||||
6
src/tests/filesys/base/lg-seq-random.c
Normal file
6
src/tests/filesys/base/lg-seq-random.c
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Writes out a fairly large file sequentially, one random-sized
|
||||
block at a time, then reads it back to verify that it was
|
||||
written properly. */
|
||||
|
||||
#define TEST_SIZE 75678
|
||||
#include "tests/filesys/base/seq-random.inc"
|
||||
16
src/tests/filesys/base/lg-seq-random.ck
Normal file
16
src/tests/filesys/base/lg-seq-random.ck
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(lg-seq-random) begin
|
||||
(lg-seq-random) create "nibble"
|
||||
(lg-seq-random) open "nibble"
|
||||
(lg-seq-random) writing "nibble"
|
||||
(lg-seq-random) close "nibble"
|
||||
(lg-seq-random) open "nibble" for verification
|
||||
(lg-seq-random) verified contents of "nibble"
|
||||
(lg-seq-random) close "nibble"
|
||||
(lg-seq-random) end
|
||||
EOF
|
||||
pass;
|
||||
59
src/tests/filesys/base/random.inc
Normal file
59
src/tests/filesys/base/random.inc
Normal file
@@ -0,0 +1,59 @@
|
||||
/* -*- c -*- */
|
||||
|
||||
#include <random.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
#include "tests/lib.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
#if TEST_SIZE % BLOCK_SIZE != 0
|
||||
#error TEST_SIZE must be a multiple of BLOCK_SIZE
|
||||
#endif
|
||||
|
||||
#define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE)
|
||||
|
||||
char buf[TEST_SIZE];
|
||||
int order[BLOCK_CNT];
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
const char *file_name = "bazzle";
|
||||
int fd;
|
||||
size_t i;
|
||||
|
||||
random_init (57);
|
||||
random_bytes (buf, sizeof buf);
|
||||
|
||||
for (i = 0; i < BLOCK_CNT; i++)
|
||||
order[i] = (int) i;
|
||||
|
||||
CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
|
||||
|
||||
msg ("write \"%s\" in random order", file_name);
|
||||
shuffle (order, BLOCK_CNT, sizeof *order);
|
||||
for (i = 0; i < BLOCK_CNT; i++)
|
||||
{
|
||||
size_t ofs = BLOCK_SIZE * (unsigned) order[i];
|
||||
seek (fd, ofs);
|
||||
if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE)
|
||||
fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
|
||||
}
|
||||
|
||||
msg ("read \"%s\" in random order", file_name);
|
||||
shuffle (order, BLOCK_CNT, sizeof *order);
|
||||
for (i = 0; i < BLOCK_CNT; i++)
|
||||
{
|
||||
char block[BLOCK_SIZE];
|
||||
size_t ofs = BLOCK_SIZE * (unsigned) order[i];
|
||||
seek (fd, ofs);
|
||||
if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE)
|
||||
fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
|
||||
compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name);
|
||||
}
|
||||
|
||||
msg ("close \"%s\"", file_name);
|
||||
close (fd);
|
||||
}
|
||||
20
src/tests/filesys/base/seq-block.inc
Normal file
20
src/tests/filesys/base/seq-block.inc
Normal file
@@ -0,0 +1,20 @@
|
||||
/* -*- c -*- */
|
||||
|
||||
#include "tests/filesys/seq-test.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
static char buf[TEST_SIZE];
|
||||
|
||||
static size_t
|
||||
return_block_size (void)
|
||||
{
|
||||
return BLOCK_SIZE;
|
||||
}
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
seq_test ("noodle",
|
||||
buf, sizeof buf, sizeof buf,
|
||||
return_block_size, NULL);
|
||||
}
|
||||
22
src/tests/filesys/base/seq-random.inc
Normal file
22
src/tests/filesys/base/seq-random.inc
Normal file
@@ -0,0 +1,22 @@
|
||||
/* -*- c -*- */
|
||||
|
||||
#include <random.h>
|
||||
#include "tests/filesys/seq-test.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
static char buf[TEST_SIZE];
|
||||
|
||||
static size_t
|
||||
return_random (void)
|
||||
{
|
||||
return random_ulong () % 1031 + 1;
|
||||
}
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
random_init ( (unsigned) -1);
|
||||
seq_test ("nibble",
|
||||
buf, sizeof buf, sizeof buf,
|
||||
return_random, NULL);
|
||||
}
|
||||
5
src/tests/filesys/base/sm-create.c
Normal file
5
src/tests/filesys/base/sm-create.c
Normal file
@@ -0,0 +1,5 @@
|
||||
/* Tests that create properly zeros out the contents of a fairly
|
||||
small file. */
|
||||
|
||||
#define TEST_SIZE 5678
|
||||
#include "tests/filesys/create.inc"
|
||||
13
src/tests/filesys/base/sm-create.ck
Normal file
13
src/tests/filesys/base/sm-create.ck
Normal file
@@ -0,0 +1,13 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(sm-create) begin
|
||||
(sm-create) create "blargle"
|
||||
(sm-create) open "blargle" for verification
|
||||
(sm-create) verified contents of "blargle"
|
||||
(sm-create) close "blargle"
|
||||
(sm-create) end
|
||||
EOF
|
||||
pass;
|
||||
6
src/tests/filesys/base/sm-full.c
Normal file
6
src/tests/filesys/base/sm-full.c
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Writes out the contents of a fairly small file all at once,
|
||||
and then reads it back to make sure that it was written
|
||||
properly. */
|
||||
|
||||
#define TEST_SIZE 5678
|
||||
#include "tests/filesys/base/full.inc"
|
||||
16
src/tests/filesys/base/sm-full.ck
Normal file
16
src/tests/filesys/base/sm-full.ck
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(sm-full) begin
|
||||
(sm-full) create "quux"
|
||||
(sm-full) open "quux"
|
||||
(sm-full) writing "quux"
|
||||
(sm-full) close "quux"
|
||||
(sm-full) open "quux" for verification
|
||||
(sm-full) verified contents of "quux"
|
||||
(sm-full) close "quux"
|
||||
(sm-full) end
|
||||
EOF
|
||||
pass;
|
||||
7
src/tests/filesys/base/sm-random.c
Normal file
7
src/tests/filesys/base/sm-random.c
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Writes out the content of a fairly small file in random order,
|
||||
then reads it back in random order to verify that it was
|
||||
written properly. */
|
||||
|
||||
#define BLOCK_SIZE 13
|
||||
#define TEST_SIZE (13 * 123)
|
||||
#include "tests/filesys/base/random.inc"
|
||||
14
src/tests/filesys/base/sm-random.ck
Normal file
14
src/tests/filesys/base/sm-random.ck
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(sm-random) begin
|
||||
(sm-random) create "bazzle"
|
||||
(sm-random) open "bazzle"
|
||||
(sm-random) write "bazzle" in random order
|
||||
(sm-random) read "bazzle" in random order
|
||||
(sm-random) close "bazzle"
|
||||
(sm-random) end
|
||||
EOF
|
||||
pass;
|
||||
7
src/tests/filesys/base/sm-seq-block.c
Normal file
7
src/tests/filesys/base/sm-seq-block.c
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Writes out a fairly small file sequentially, one fixed-size
|
||||
block at a time, then reads it back to verify that it was
|
||||
written properly. */
|
||||
|
||||
#define TEST_SIZE 5678
|
||||
#define BLOCK_SIZE 513
|
||||
#include "tests/filesys/base/seq-block.inc"
|
||||
16
src/tests/filesys/base/sm-seq-block.ck
Normal file
16
src/tests/filesys/base/sm-seq-block.ck
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(sm-seq-block) begin
|
||||
(sm-seq-block) create "noodle"
|
||||
(sm-seq-block) open "noodle"
|
||||
(sm-seq-block) writing "noodle"
|
||||
(sm-seq-block) close "noodle"
|
||||
(sm-seq-block) open "noodle" for verification
|
||||
(sm-seq-block) verified contents of "noodle"
|
||||
(sm-seq-block) close "noodle"
|
||||
(sm-seq-block) end
|
||||
EOF
|
||||
pass;
|
||||
6
src/tests/filesys/base/sm-seq-random.c
Normal file
6
src/tests/filesys/base/sm-seq-random.c
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Writes out a fairly large file sequentially, one random-sized
|
||||
block at a time, then reads it back to verify that it was
|
||||
written properly. */
|
||||
|
||||
#define TEST_SIZE 5678
|
||||
#include "tests/filesys/base/seq-random.inc"
|
||||
16
src/tests/filesys/base/sm-seq-random.ck
Normal file
16
src/tests/filesys/base/sm-seq-random.ck
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(sm-seq-random) begin
|
||||
(sm-seq-random) create "nibble"
|
||||
(sm-seq-random) open "nibble"
|
||||
(sm-seq-random) writing "nibble"
|
||||
(sm-seq-random) close "nibble"
|
||||
(sm-seq-random) open "nibble" for verification
|
||||
(sm-seq-random) verified contents of "nibble"
|
||||
(sm-seq-random) close "nibble"
|
||||
(sm-seq-random) end
|
||||
EOF
|
||||
pass;
|
||||
31
src/tests/filesys/base/syn-read.c
Normal file
31
src/tests/filesys/base/syn-read.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Spawns 10 child processes, all of which read from the same
|
||||
file and make sure that the contents are what they should
|
||||
be. */
|
||||
|
||||
#include <random.h>
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
#include "tests/lib.h"
|
||||
#include "tests/main.h"
|
||||
#include "tests/filesys/base/syn-read.h"
|
||||
|
||||
static char buf[BUF_SIZE];
|
||||
|
||||
#define CHILD_CNT 10
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
pid_t children[CHILD_CNT];
|
||||
int fd;
|
||||
|
||||
CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name);
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
|
||||
random_bytes (buf, sizeof buf);
|
||||
CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name);
|
||||
msg ("close \"%s\"", file_name);
|
||||
close (fd);
|
||||
|
||||
exec_children ("child-syn-read", children, CHILD_CNT);
|
||||
wait_children (children, CHILD_CNT);
|
||||
}
|
||||
33
src/tests/filesys/base/syn-read.ck
Normal file
33
src/tests/filesys/base/syn-read.ck
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(syn-read) begin
|
||||
(syn-read) create "data"
|
||||
(syn-read) open "data"
|
||||
(syn-read) write "data"
|
||||
(syn-read) close "data"
|
||||
(syn-read) exec child 1 of 10: "child-syn-read 0"
|
||||
(syn-read) exec child 2 of 10: "child-syn-read 1"
|
||||
(syn-read) exec child 3 of 10: "child-syn-read 2"
|
||||
(syn-read) exec child 4 of 10: "child-syn-read 3"
|
||||
(syn-read) exec child 5 of 10: "child-syn-read 4"
|
||||
(syn-read) exec child 6 of 10: "child-syn-read 5"
|
||||
(syn-read) exec child 7 of 10: "child-syn-read 6"
|
||||
(syn-read) exec child 8 of 10: "child-syn-read 7"
|
||||
(syn-read) exec child 9 of 10: "child-syn-read 8"
|
||||
(syn-read) exec child 10 of 10: "child-syn-read 9"
|
||||
(syn-read) wait for child 1 of 10 returned 0 (expected 0)
|
||||
(syn-read) wait for child 2 of 10 returned 1 (expected 1)
|
||||
(syn-read) wait for child 3 of 10 returned 2 (expected 2)
|
||||
(syn-read) wait for child 4 of 10 returned 3 (expected 3)
|
||||
(syn-read) wait for child 5 of 10 returned 4 (expected 4)
|
||||
(syn-read) wait for child 6 of 10 returned 5 (expected 5)
|
||||
(syn-read) wait for child 7 of 10 returned 6 (expected 6)
|
||||
(syn-read) wait for child 8 of 10 returned 7 (expected 7)
|
||||
(syn-read) wait for child 9 of 10 returned 8 (expected 8)
|
||||
(syn-read) wait for child 10 of 10 returned 9 (expected 9)
|
||||
(syn-read) end
|
||||
EOF
|
||||
pass;
|
||||
7
src/tests/filesys/base/syn-read.h
Normal file
7
src/tests/filesys/base/syn-read.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef TESTS_FILESYS_BASE_SYN_READ_H
|
||||
#define TESTS_FILESYS_BASE_SYN_READ_H
|
||||
|
||||
#define BUF_SIZE 1024
|
||||
static const char file_name[] = "data";
|
||||
|
||||
#endif /* tests/filesys/base/syn-read.h */
|
||||
30
src/tests/filesys/base/syn-remove.c
Normal file
30
src/tests/filesys/base/syn-remove.c
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Verifies that a deleted file may still be written to and read
|
||||
from. */
|
||||
|
||||
#include <random.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
#include "tests/lib.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
char buf1[1234];
|
||||
char buf2[1234];
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
const char *file_name = "deleteme";
|
||||
int fd;
|
||||
|
||||
CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
|
||||
CHECK (remove (file_name), "remove \"%s\"", file_name);
|
||||
random_bytes (buf1, sizeof buf1);
|
||||
CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name);
|
||||
msg ("seek \"%s\" to 0", file_name);
|
||||
seek (fd, 0);
|
||||
CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name);
|
||||
compare_bytes (buf2, buf1, sizeof buf1, 0, file_name);
|
||||
msg ("close \"%s\"", file_name);
|
||||
close (fd);
|
||||
}
|
||||
16
src/tests/filesys/base/syn-remove.ck
Normal file
16
src/tests/filesys/base/syn-remove.ck
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(syn-remove) begin
|
||||
(syn-remove) create "deleteme"
|
||||
(syn-remove) open "deleteme"
|
||||
(syn-remove) remove "deleteme"
|
||||
(syn-remove) write "deleteme"
|
||||
(syn-remove) seek "deleteme" to 0
|
||||
(syn-remove) read "deleteme"
|
||||
(syn-remove) close "deleteme"
|
||||
(syn-remove) end
|
||||
EOF
|
||||
pass;
|
||||
31
src/tests/filesys/base/syn-write.c
Normal file
31
src/tests/filesys/base/syn-write.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Spawns several child processes to write out different parts of
|
||||
the contents of a file and waits for them to finish. Then
|
||||
reads back the file and verifies its contents. */
|
||||
|
||||
#include <random.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
#include "tests/filesys/base/syn-write.h"
|
||||
#include "tests/lib.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
char buf1[BUF_SIZE];
|
||||
char buf2[BUF_SIZE];
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
pid_t children[CHILD_CNT];
|
||||
int fd;
|
||||
|
||||
CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
|
||||
|
||||
exec_children ("child-syn-wrt", children, CHILD_CNT);
|
||||
wait_children (children, CHILD_CNT);
|
||||
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
|
||||
CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name);
|
||||
random_bytes (buf2, sizeof buf2);
|
||||
compare_bytes (buf1, buf2, sizeof buf1, 0, file_name);
|
||||
}
|
||||
32
src/tests/filesys/base/syn-write.ck
Normal file
32
src/tests/filesys/base/syn-write.ck
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
|
||||
(syn-write) begin
|
||||
(syn-write) create "stuff"
|
||||
(syn-write) exec child 1 of 10: "child-syn-wrt 0"
|
||||
(syn-write) exec child 2 of 10: "child-syn-wrt 1"
|
||||
(syn-write) exec child 3 of 10: "child-syn-wrt 2"
|
||||
(syn-write) exec child 4 of 10: "child-syn-wrt 3"
|
||||
(syn-write) exec child 5 of 10: "child-syn-wrt 4"
|
||||
(syn-write) exec child 6 of 10: "child-syn-wrt 5"
|
||||
(syn-write) exec child 7 of 10: "child-syn-wrt 6"
|
||||
(syn-write) exec child 8 of 10: "child-syn-wrt 7"
|
||||
(syn-write) exec child 9 of 10: "child-syn-wrt 8"
|
||||
(syn-write) exec child 10 of 10: "child-syn-wrt 9"
|
||||
(syn-write) wait for child 1 of 10 returned 0 (expected 0)
|
||||
(syn-write) wait for child 2 of 10 returned 1 (expected 1)
|
||||
(syn-write) wait for child 3 of 10 returned 2 (expected 2)
|
||||
(syn-write) wait for child 4 of 10 returned 3 (expected 3)
|
||||
(syn-write) wait for child 5 of 10 returned 4 (expected 4)
|
||||
(syn-write) wait for child 6 of 10 returned 5 (expected 5)
|
||||
(syn-write) wait for child 7 of 10 returned 6 (expected 6)
|
||||
(syn-write) wait for child 8 of 10 returned 7 (expected 7)
|
||||
(syn-write) wait for child 9 of 10 returned 8 (expected 8)
|
||||
(syn-write) wait for child 10 of 10 returned 9 (expected 9)
|
||||
(syn-write) open "stuff"
|
||||
(syn-write) read "stuff"
|
||||
(syn-write) end
|
||||
EOF
|
||||
pass;
|
||||
9
src/tests/filesys/base/syn-write.h
Normal file
9
src/tests/filesys/base/syn-write.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef TESTS_FILESYS_BASE_SYN_WRITE_H
|
||||
#define TESTS_FILESYS_BASE_SYN_WRITE_H
|
||||
|
||||
#define CHILD_CNT 10
|
||||
#define CHUNK_SIZE 512U
|
||||
#define BUF_SIZE (CHILD_CNT * CHUNK_SIZE)
|
||||
static const char file_name[] = "stuff";
|
||||
|
||||
#endif /* tests/filesys/base/syn-write.h */
|
||||
15
src/tests/filesys/create.inc
Normal file
15
src/tests/filesys/create.inc
Normal file
@@ -0,0 +1,15 @@
|
||||
/* -*- c -*- */
|
||||
|
||||
#include <syscall.h>
|
||||
#include "tests/lib.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
static char buf[TEST_SIZE];
|
||||
|
||||
void
|
||||
test_main (void)
|
||||
{
|
||||
const char *file_name = "blargle";
|
||||
CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
|
||||
check_file (file_name, buf, TEST_SIZE);
|
||||
}
|
||||
37
src/tests/filesys/seq-test.c
Normal file
37
src/tests/filesys/seq-test.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "tests/filesys/seq-test.h"
|
||||
#include <random.h>
|
||||
#include <syscall.h>
|
||||
#include "tests/lib.h"
|
||||
|
||||
void
|
||||
seq_test (const char *file_name, void *buf, size_t size, size_t initial_size,
|
||||
size_t (*block_size_func) (void),
|
||||
void (*check_func) (int fd, long ofs))
|
||||
{
|
||||
size_t ofs;
|
||||
int fd;
|
||||
|
||||
random_bytes (buf, size);
|
||||
CHECK (create (file_name, initial_size), "create \"%s\"", file_name);
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
|
||||
|
||||
ofs = 0;
|
||||
msg ("writing \"%s\"", file_name);
|
||||
while (ofs < size)
|
||||
{
|
||||
size_t block_size = block_size_func ();
|
||||
if (block_size > size - ofs)
|
||||
block_size = size - ofs;
|
||||
|
||||
if (write (fd, buf + ofs, block_size) != (int) block_size)
|
||||
fail ("write %zu bytes at offset %zu in \"%s\" failed",
|
||||
block_size, ofs, file_name);
|
||||
|
||||
ofs += block_size;
|
||||
if (check_func != NULL)
|
||||
check_func (fd, (long) ofs);
|
||||
}
|
||||
msg ("close \"%s\"", file_name);
|
||||
close (fd);
|
||||
check_file (file_name, buf, size);
|
||||
}
|
||||
11
src/tests/filesys/seq-test.h
Normal file
11
src/tests/filesys/seq-test.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef TESTS_FILESYS_SEQ_TEST_H
|
||||
#define TESTS_FILESYS_SEQ_TEST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void seq_test (const char *file_name,
|
||||
void *buf, size_t size, size_t initial_size,
|
||||
size_t (*block_size_func) (void),
|
||||
void (*check_func) (int fd, long ofs));
|
||||
|
||||
#endif /* tests/filesys/seq-test.h */
|
||||
174
src/tests/internal/list.c
Normal file
174
src/tests/internal/list.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/* Test program for lib/kernel/list.c.
|
||||
|
||||
Attempts to test the list functionality that is not
|
||||
sufficiently tested elsewhere in PintOS.
|
||||
|
||||
This is not a test we will run on your submitted tasks.
|
||||
It is here for completeness.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
#include <debug.h>
|
||||
#include <list.h>
|
||||
#include <random.h>
|
||||
#include <stdio.h>
|
||||
#include "threads/test.h"
|
||||
|
||||
/* Maximum number of elements in a linked list that we will
|
||||
test. */
|
||||
#define MAX_SIZE 64
|
||||
|
||||
/* A linked list element. */
|
||||
struct value
|
||||
{
|
||||
struct list_elem elem; /* List element. */
|
||||
int value; /* Item value. */
|
||||
};
|
||||
|
||||
static void shuffle (struct value[], size_t);
|
||||
static bool value_less (const struct list_elem *, const struct list_elem *,
|
||||
void *);
|
||||
static void verify_list_fwd (struct list *, int size);
|
||||
static void verify_list_bkwd (struct list *, int size);
|
||||
|
||||
/* Test the linked list implementation. */
|
||||
void
|
||||
test (void)
|
||||
{
|
||||
int size;
|
||||
|
||||
printf ("testing various size lists:");
|
||||
for (size = 0; size < MAX_SIZE; size++)
|
||||
{
|
||||
int repeat;
|
||||
|
||||
printf (" %d", size);
|
||||
for (repeat = 0; repeat < 10; repeat++)
|
||||
{
|
||||
static struct value values[MAX_SIZE * 4];
|
||||
struct list list;
|
||||
struct list_elem *e;
|
||||
int i, ofs;
|
||||
|
||||
/* Put values 0...SIZE in random order in VALUES. */
|
||||
for (i = 0; i < size; i++)
|
||||
values[i].value = i;
|
||||
shuffle (values, size);
|
||||
|
||||
/* Assemble list. */
|
||||
list_init (&list);
|
||||
for (i = 0; i < size; i++)
|
||||
list_push_back (&list, &values[i].elem);
|
||||
|
||||
/* Verify correct minimum and maximum elements. */
|
||||
e = list_min (&list, value_less, NULL);
|
||||
ASSERT (size ? list_entry (e, struct value, elem)->value == 0
|
||||
: e == list_begin (&list));
|
||||
e = list_max (&list, value_less, NULL);
|
||||
ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1
|
||||
: e == list_begin (&list));
|
||||
|
||||
/* Sort and verify list. */
|
||||
list_sort (&list, value_less, NULL);
|
||||
verify_list_fwd (&list, size);
|
||||
|
||||
/* Reverse and verify list. */
|
||||
list_reverse (&list);
|
||||
verify_list_bkwd (&list, size);
|
||||
|
||||
/* Shuffle, insert using list_insert_ordered(),
|
||||
and verify ordering. */
|
||||
shuffle (values, size);
|
||||
list_init (&list);
|
||||
for (i = 0; i < size; i++)
|
||||
list_insert_ordered (&list, &values[i].elem,
|
||||
value_less, NULL);
|
||||
verify_list_fwd (&list, size);
|
||||
|
||||
/* Duplicate some items, uniquify, and verify. */
|
||||
ofs = size;
|
||||
for (e = list_begin (&list); e != list_end (&list);
|
||||
e = list_next (e))
|
||||
{
|
||||
struct value *v = list_entry (e, struct value, elem);
|
||||
int copies = random_ulong () % 4;
|
||||
while (copies-- > 0)
|
||||
{
|
||||
values[ofs].value = v->value;
|
||||
list_insert (e, &values[ofs++].elem);
|
||||
}
|
||||
}
|
||||
ASSERT ((size_t) ofs < sizeof values / sizeof *values);
|
||||
list_unique (&list, NULL, value_less, NULL);
|
||||
verify_list_fwd (&list, size);
|
||||
}
|
||||
}
|
||||
|
||||
printf (" done\n");
|
||||
printf ("list: PASS\n");
|
||||
}
|
||||
|
||||
/* Shuffles the CNT elements in ARRAY into random order. */
|
||||
static void
|
||||
shuffle (struct value *array, size_t cnt)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
size_t j = i + random_ulong () % (cnt - i);
|
||||
struct value t = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if value A is less than value B, false
|
||||
otherwise. */
|
||||
static bool
|
||||
value_less (const struct list_elem *a_, const struct list_elem *b_,
|
||||
void *aux UNUSED)
|
||||
{
|
||||
const struct value *a = list_entry (a_, struct value, elem);
|
||||
const struct value *b = list_entry (b_, struct value, elem);
|
||||
|
||||
return a->value < b->value;
|
||||
}
|
||||
|
||||
/* Verifies that LIST contains the values 0...SIZE when traversed
|
||||
in forward order. */
|
||||
static void
|
||||
verify_list_fwd (struct list *list, int size)
|
||||
{
|
||||
struct list_elem *e;
|
||||
int i;
|
||||
|
||||
for (i = 0, e = list_begin (list);
|
||||
i < size && e != list_end (list);
|
||||
i++, e = list_next (e))
|
||||
{
|
||||
struct value *v = list_entry (e, struct value, elem);
|
||||
ASSERT (i == v->value);
|
||||
}
|
||||
ASSERT (i == size);
|
||||
ASSERT (e == list_end (list));
|
||||
}
|
||||
|
||||
/* Verifies that LIST contains the values 0...SIZE when traversed
|
||||
in reverse order. */
|
||||
static void
|
||||
verify_list_bkwd (struct list *list, int size)
|
||||
{
|
||||
struct list_elem *e;
|
||||
int i;
|
||||
|
||||
for (i = 0, e = list_rbegin (list);
|
||||
i < size && e != list_rend (list);
|
||||
i++, e = list_prev (e))
|
||||
{
|
||||
struct value *v = list_entry (e, struct value, elem);
|
||||
ASSERT (i == v->value);
|
||||
}
|
||||
ASSERT (i == size);
|
||||
ASSERT (e == list_rend (list));
|
||||
}
|
||||
208
src/tests/internal/stdio.c
Normal file
208
src/tests/internal/stdio.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/* Test program for printf() in lib/stdio.c.
|
||||
|
||||
Attempts to test printf() functionality that is not
|
||||
sufficiently tested elsewhere in PintOS.
|
||||
|
||||
This is not a test we will run on your submitted tasks.
|
||||
It is here for completeness.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "threads/test.h"
|
||||
|
||||
/* Number of failures so far. */
|
||||
static int failure_cnt;
|
||||
|
||||
static void
|
||||
checkf (const char *expect, const char *format, ...)
|
||||
{
|
||||
char output[128];
|
||||
va_list args;
|
||||
|
||||
printf ("\"%s\" -> \"%s\": ", format, expect);
|
||||
|
||||
va_start (args, format);
|
||||
vsnprintf (output, sizeof output, format, args);
|
||||
va_end (args);
|
||||
|
||||
if (strcmp (expect, output))
|
||||
{
|
||||
printf ("\nFAIL: actual output \"%s\"\n", output);
|
||||
failure_cnt++;
|
||||
}
|
||||
else
|
||||
printf ("okay\n");
|
||||
}
|
||||
|
||||
/* Test printf() implementation. */
|
||||
void
|
||||
test (void)
|
||||
{
|
||||
printf ("Testing formats:");
|
||||
|
||||
/* Check that commas show up in the right places, for positive
|
||||
numbers. */
|
||||
checkf ("1", "%'d", 1);
|
||||
checkf ("12", "%'d", 12);
|
||||
checkf ("123", "%'d", 123);
|
||||
checkf ("1,234", "%'d", 1234);
|
||||
checkf ("12,345", "%'d", 12345);
|
||||
checkf ("123,456", "%'ld", 123456L);
|
||||
checkf ("1,234,567", "%'ld", 1234567L);
|
||||
checkf ("12,345,678", "%'ld", 12345678L);
|
||||
checkf ("123,456,789", "%'ld", 123456789L);
|
||||
checkf ("1,234,567,890", "%'ld", 1234567890L);
|
||||
checkf ("12,345,678,901", "%'lld", 12345678901LL);
|
||||
checkf ("123,456,789,012", "%'lld", 123456789012LL);
|
||||
checkf ("1,234,567,890,123", "%'lld", 1234567890123LL);
|
||||
checkf ("12,345,678,901,234", "%'lld", 12345678901234LL);
|
||||
checkf ("123,456,789,012,345", "%'lld", 123456789012345LL);
|
||||
checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL);
|
||||
checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL);
|
||||
checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL);
|
||||
checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL);
|
||||
|
||||
/* Check that commas show up in the right places, for positive
|
||||
numbers. */
|
||||
checkf ("-1", "%'d", -1);
|
||||
checkf ("-12", "%'d", -12);
|
||||
checkf ("-123", "%'d", -123);
|
||||
checkf ("-1,234", "%'d", -1234);
|
||||
checkf ("-12,345", "%'d", -12345);
|
||||
checkf ("-123,456", "%'ld", -123456L);
|
||||
checkf ("-1,234,567", "%'ld", -1234567L);
|
||||
checkf ("-12,345,678", "%'ld", -12345678L);
|
||||
checkf ("-123,456,789", "%'ld", -123456789L);
|
||||
checkf ("-1,234,567,890", "%'ld", -1234567890L);
|
||||
checkf ("-12,345,678,901", "%'lld", -12345678901LL);
|
||||
checkf ("-123,456,789,012", "%'lld", -123456789012LL);
|
||||
checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL);
|
||||
checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL);
|
||||
checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL);
|
||||
checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL);
|
||||
checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL);
|
||||
checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL);
|
||||
checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL);
|
||||
|
||||
/* Check signed integer conversions. */
|
||||
checkf (" 0", "%5d", 0);
|
||||
checkf ("0 ", "%-5d", 0);
|
||||
checkf (" +0", "%+5d", 0);
|
||||
checkf ("+0 ", "%+-5d", 0);
|
||||
checkf (" 0", "% 5d", 0);
|
||||
checkf ("00000", "%05d", 0);
|
||||
checkf (" ", "%5.0d", 0);
|
||||
checkf (" 00", "%5.2d", 0);
|
||||
checkf ("0", "%d", 0);
|
||||
|
||||
checkf (" 1", "%5d", 1);
|
||||
checkf ("1 ", "%-5d", 1);
|
||||
checkf (" +1", "%+5d", 1);
|
||||
checkf ("+1 ", "%+-5d", 1);
|
||||
checkf (" 1", "% 5d", 1);
|
||||
checkf ("00001", "%05d", 1);
|
||||
checkf (" 1", "%5.0d", 1);
|
||||
checkf (" 01", "%5.2d", 1);
|
||||
checkf ("1", "%d", 1);
|
||||
|
||||
checkf (" -1", "%5d", -1);
|
||||
checkf ("-1 ", "%-5d", -1);
|
||||
checkf (" -1", "%+5d", -1);
|
||||
checkf ("-1 ", "%+-5d", -1);
|
||||
checkf (" -1", "% 5d", -1);
|
||||
checkf ("-0001", "%05d", -1);
|
||||
checkf (" -1", "%5.0d", -1);
|
||||
checkf (" -01", "%5.2d", -1);
|
||||
checkf ("-1", "%d", -1);
|
||||
|
||||
checkf ("12345", "%5d", 12345);
|
||||
checkf ("12345", "%-5d", 12345);
|
||||
checkf ("+12345", "%+5d", 12345);
|
||||
checkf ("+12345", "%+-5d", 12345);
|
||||
checkf (" 12345", "% 5d", 12345);
|
||||
checkf ("12345", "%05d", 12345);
|
||||
checkf ("12345", "%5.0d", 12345);
|
||||
checkf ("12345", "%5.2d", 12345);
|
||||
checkf ("12345", "%d", 12345);
|
||||
|
||||
checkf ("123456", "%5d", 123456);
|
||||
checkf ("123456", "%-5d", 123456);
|
||||
checkf ("+123456", "%+5d", 123456);
|
||||
checkf ("+123456", "%+-5d", 123456);
|
||||
checkf (" 123456", "% 5d", 123456);
|
||||
checkf ("123456", "%05d", 123456);
|
||||
checkf ("123456", "%5.0d", 123456);
|
||||
checkf ("123456", "%5.2d", 123456);
|
||||
checkf ("123456", "%d", 123456);
|
||||
|
||||
/* Check unsigned integer conversions. */
|
||||
checkf (" 0", "%5u", 0);
|
||||
checkf (" 0", "%5o", 0);
|
||||
checkf (" 0", "%5x", 0);
|
||||
checkf (" 0", "%5X", 0);
|
||||
checkf (" 0", "%#5o", 0);
|
||||
checkf (" 0", "%#5x", 0);
|
||||
checkf (" 0", "%#5X", 0);
|
||||
checkf (" 00000000", "%#10.8x", 0);
|
||||
|
||||
checkf (" 1", "%5u", 1);
|
||||
checkf (" 1", "%5o", 1);
|
||||
checkf (" 1", "%5x", 1);
|
||||
checkf (" 1", "%5X", 1);
|
||||
checkf (" 01", "%#5o", 1);
|
||||
checkf (" 0x1", "%#5x", 1);
|
||||
checkf (" 0X1", "%#5X", 1);
|
||||
checkf ("0x00000001", "%#10.8x", 1);
|
||||
|
||||
checkf ("123456", "%5u", 123456);
|
||||
checkf ("361100", "%5o", 123456);
|
||||
checkf ("1e240", "%5x", 123456);
|
||||
checkf ("1E240", "%5X", 123456);
|
||||
checkf ("0361100", "%#5o", 123456);
|
||||
checkf ("0x1e240", "%#5x", 123456);
|
||||
checkf ("0X1E240", "%#5X", 123456);
|
||||
checkf ("0x0001e240", "%#10.8x", 123456);
|
||||
|
||||
/* Character and string conversions. */
|
||||
checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r');
|
||||
checkf (" left-right ", "%6s%s%-7s", "left", "-", "right");
|
||||
checkf ("trim", "%.4s", "trimoff");
|
||||
checkf ("%%", "%%%%");
|
||||
|
||||
/* From Cristian Cadar's automatic test case generator. */
|
||||
checkf (" abcdefgh", "%9s", "abcdefgh");
|
||||
checkf ("36657730000", "%- o", (unsigned) 036657730000);
|
||||
checkf ("4139757568", "%- u", (unsigned) 4139757568UL);
|
||||
checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000);
|
||||
checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000);
|
||||
checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL);
|
||||
checkf ("-155209728", "%-zi", (size_t) -155209728);
|
||||
checkf ("-155209728", "%-zd", (size_t) -155209728);
|
||||
checkf ("036657730000", "%+#o", (unsigned) 036657730000);
|
||||
checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000);
|
||||
checkf ("-155209728", "% zi", (size_t) -155209728);
|
||||
checkf ("-155209728", "% zd", (size_t) -155209728);
|
||||
checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL);
|
||||
checkf ("036657730000", "% #o", (unsigned) 036657730000);
|
||||
checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000);
|
||||
checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000);
|
||||
checkf ("-155209728", "%#zd", (size_t) -155209728);
|
||||
checkf ("-155209728", "%0zi", (size_t) -155209728);
|
||||
checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL);
|
||||
checkf ("-155,209,728", "%-'d", -155209728);
|
||||
checkf ("-155209728", "%.zi", (size_t) -155209728);
|
||||
checkf ("-155209728", "%zi", (size_t) -155209728);
|
||||
checkf ("-155209728", "%zd", (size_t) -155209728);
|
||||
checkf ("-155209728", "%+zi", (size_t) -155209728);
|
||||
|
||||
if (failure_cnt == 0)
|
||||
printf ("\nstdio: PASS\n");
|
||||
else
|
||||
printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt);
|
||||
}
|
||||
114
src/tests/internal/stdlib.c
Normal file
114
src/tests/internal/stdlib.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/* Test program for sorting and searching in lib/stdlib.c.
|
||||
|
||||
Attempts to test the sorting and searching functionality that
|
||||
is not sufficiently tested elsewhere in PintOS.
|
||||
|
||||
This is not a test we will run on your submitted tasks.
|
||||
It is here for completeness.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
#include <debug.h>
|
||||
#include <limits.h>
|
||||
#include <random.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "threads/test.h"
|
||||
|
||||
/* Maximum number of elements in an array that we will test. */
|
||||
#define MAX_CNT 4096
|
||||
|
||||
static void shuffle (int[], size_t);
|
||||
static int compare_ints (const void *, const void *);
|
||||
static void verify_order (const int[], size_t);
|
||||
static void verify_bsearch (const int[], size_t);
|
||||
|
||||
/* Test sorting and searching implementations. */
|
||||
void
|
||||
test (void)
|
||||
{
|
||||
int cnt;
|
||||
|
||||
printf ("testing various size arrays:");
|
||||
for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1)
|
||||
{
|
||||
int repeat;
|
||||
|
||||
printf (" %zu", cnt);
|
||||
for (repeat = 0; repeat < 10; repeat++)
|
||||
{
|
||||
static int values[MAX_CNT];
|
||||
int i;
|
||||
|
||||
/* Put values 0...CNT in random order in VALUES. */
|
||||
for (i = 0; i < cnt; i++)
|
||||
values[i] = i;
|
||||
shuffle (values, cnt);
|
||||
|
||||
/* Sort VALUES, then verify ordering. */
|
||||
qsort (values, cnt, sizeof *values, compare_ints);
|
||||
verify_order (values, cnt);
|
||||
verify_bsearch (values, cnt);
|
||||
}
|
||||
}
|
||||
|
||||
printf (" done\n");
|
||||
printf ("stdlib: PASS\n");
|
||||
}
|
||||
|
||||
/* Shuffles the CNT elements in ARRAY into random order. */
|
||||
static void
|
||||
shuffle (int *array, size_t cnt)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
size_t j = i + random_ulong () % (cnt - i);
|
||||
int t = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns 1 if *A is greater than *B,
|
||||
0 if *A equals *B,
|
||||
-1 if *A is less than *B. */
|
||||
static int
|
||||
compare_ints (const void *a_, const void *b_)
|
||||
{
|
||||
const int *a = a_;
|
||||
const int *b = b_;
|
||||
|
||||
return *a < *b ? -1 : *a > *b;
|
||||
}
|
||||
|
||||
/* Verifies that ARRAY contains the CNT ints 0...CNT-1. */
|
||||
static void
|
||||
verify_order (const int *array, size_t cnt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; (size_t) i < cnt; i++)
|
||||
ASSERT (array[i] == i);
|
||||
}
|
||||
|
||||
/* Checks that bsearch() works properly in ARRAY. ARRAY must
|
||||
contain the values 0...CNT-1. */
|
||||
static void
|
||||
verify_bsearch (const int *array, size_t cnt)
|
||||
{
|
||||
int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2};
|
||||
int i;
|
||||
|
||||
/* Check that all the values in the array are found properly. */
|
||||
for (i = 0; (size_t) i < cnt; i++)
|
||||
ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints)
|
||||
== array + i);
|
||||
|
||||
/* Check that some values not in the array are not found. */
|
||||
not_in_array[0] = cnt;
|
||||
for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++)
|
||||
ASSERT (bsearch (¬_in_array[i], array, cnt, sizeof *array, compare_ints)
|
||||
== NULL);
|
||||
}
|
||||
196
src/tests/lib.c
Normal file
196
src/tests/lib.c
Normal file
@@ -0,0 +1,196 @@
|
||||
#include "tests/lib.h"
|
||||
#include <random.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
const char *test_name;
|
||||
bool quiet = false;
|
||||
|
||||
static void
|
||||
vmsg (const char *format, va_list args, const char *suffix)
|
||||
{
|
||||
/* We go to some trouble to stuff the entire message into a
|
||||
single buffer and output it in a single system call, because
|
||||
that'll (typically) ensure that it gets sent to the console
|
||||
atomically. Otherwise kernel messages like "foo: exit(0)"
|
||||
can end up being interleaved if we're unlucky. */
|
||||
static char buf[1024];
|
||||
|
||||
snprintf (buf, sizeof buf, "(%s) ", test_name);
|
||||
vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args);
|
||||
strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf));
|
||||
write (STDOUT_FILENO, buf, strlen (buf));
|
||||
}
|
||||
|
||||
void
|
||||
msg (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (quiet)
|
||||
return;
|
||||
va_start (args, format);
|
||||
vmsg (format, args, "\n");
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
void
|
||||
fail (const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start (args, format);
|
||||
vmsg (format, args, ": FAILED\n");
|
||||
va_end (args);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
static void
|
||||
swap (void *a_, void *b_, size_t size)
|
||||
{
|
||||
uint8_t *a = a_;
|
||||
uint8_t *b = b_;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
uint8_t t = a[i];
|
||||
a[i] = b[i];
|
||||
b[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shuffle (void *buf_, size_t cnt, size_t size)
|
||||
{
|
||||
char *buf = buf_;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
size_t j = i + random_ulong () % (cnt - i);
|
||||
swap (buf + i * size, buf + j * size, size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
exec_children (const char *child_name, pid_t pids[], size_t child_cnt)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < child_cnt; i++)
|
||||
{
|
||||
char cmd_line[128];
|
||||
snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i);
|
||||
CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR,
|
||||
"exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wait_children (pid_t pids[], size_t child_cnt)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < child_cnt; i++)
|
||||
{
|
||||
int status = wait (pids[i]);
|
||||
CHECK (status == (int) i,
|
||||
"wait for child %zu of %zu returned %d (expected %zu)",
|
||||
i + 1, child_cnt, status, i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
check_file_handle (int fd,
|
||||
const char *file_name, const void *buf_, size_t size)
|
||||
{
|
||||
const char *buf = buf_;
|
||||
size_t ofs = 0;
|
||||
size_t file_size;
|
||||
|
||||
/* Warn about file of wrong size. Don't fail yet because we
|
||||
may still be able to get more information by reading the
|
||||
file. */
|
||||
file_size = (size_t) filesize (fd);
|
||||
if (file_size != size)
|
||||
msg ("size of %s (%zu) differs from expected (%zu)",
|
||||
file_name, file_size, size);
|
||||
|
||||
/* Read the file block-by-block, comparing data as we go. */
|
||||
while (ofs < size)
|
||||
{
|
||||
char block[512];
|
||||
size_t block_size, ret_val;
|
||||
|
||||
block_size = size - ofs;
|
||||
if (block_size > sizeof block)
|
||||
block_size = sizeof block;
|
||||
|
||||
ret_val = (size_t) read (fd, block, block_size);
|
||||
if (ret_val != block_size)
|
||||
fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu",
|
||||
block_size, ofs, file_name, ret_val);
|
||||
|
||||
compare_bytes (block, buf + ofs, block_size, ofs, file_name);
|
||||
ofs += block_size;
|
||||
}
|
||||
|
||||
/* Now fail due to wrong file size. */
|
||||
if (file_size != size)
|
||||
fail ("size of %s (%zu) differs from expected (%zu)",
|
||||
file_name, file_size, size);
|
||||
|
||||
msg ("verified contents of \"%s\"", file_name);
|
||||
}
|
||||
|
||||
void
|
||||
check_file (const char *file_name, const void *buf, size_t size)
|
||||
{
|
||||
int fd;
|
||||
|
||||
CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification",
|
||||
file_name);
|
||||
check_file_handle (fd, file_name, buf, size);
|
||||
msg ("close \"%s\"", file_name);
|
||||
close (fd);
|
||||
}
|
||||
|
||||
void
|
||||
compare_bytes (const void *read_data_, const void *expected_data_, size_t size,
|
||||
size_t ofs, const char *file_name)
|
||||
{
|
||||
const uint8_t *read_data = read_data_;
|
||||
const uint8_t *expected_data = expected_data_;
|
||||
size_t i, j;
|
||||
size_t show_cnt;
|
||||
|
||||
if (!memcmp (read_data, expected_data, size))
|
||||
return;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (read_data[i] != expected_data[i])
|
||||
break;
|
||||
for (j = i + 1; j < size; j++)
|
||||
if (read_data[j] == expected_data[j])
|
||||
break;
|
||||
|
||||
quiet = false;
|
||||
msg ("%zu bytes read starting at offset %zu in \"%s\" differ "
|
||||
"from expected.", j - i, ofs + i, file_name);
|
||||
show_cnt = j - i;
|
||||
if (j - i > 64)
|
||||
{
|
||||
show_cnt = 64;
|
||||
msg ("Showing first differing %zu bytes.", show_cnt);
|
||||
}
|
||||
msg ("Data actually read:");
|
||||
hex_dump (ofs + i, read_data + i, show_cnt, true);
|
||||
msg ("Expected data:");
|
||||
hex_dump (ofs + i, expected_data + i, show_cnt, true);
|
||||
fail ("%zu bytes read starting at offset %zu in \"%s\" differ "
|
||||
"from expected", j - i, ofs + i, file_name);
|
||||
}
|
||||
50
src/tests/lib.h
Normal file
50
src/tests/lib.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef TESTS_LIB_H
|
||||
#define TESTS_LIB_H
|
||||
|
||||
#include <debug.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <syscall.h>
|
||||
|
||||
extern const char *test_name;
|
||||
extern bool quiet;
|
||||
|
||||
void msg (const char *, ...) PRINTF_FORMAT (1, 2);
|
||||
void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN;
|
||||
|
||||
/* Takes an expression to test for SUCCESS and a message, which
|
||||
may include printf-style arguments. Logs the message, then
|
||||
tests the expression. If it is zero, indicating failure,
|
||||
emits the message as a failure.
|
||||
|
||||
Somewhat tricky to use:
|
||||
|
||||
- SUCCESS must not have side effects that affect the
|
||||
message, because that will cause the original message and
|
||||
the failure message to differ.
|
||||
|
||||
- The message must not have side effects of its own, because
|
||||
it will be printed twice on failure, or zero times on
|
||||
success if quiet is set. */
|
||||
#define CHECK(SUCCESS, ...) \
|
||||
do \
|
||||
{ \
|
||||
msg (__VA_ARGS__); \
|
||||
if (!(SUCCESS)) \
|
||||
fail (__VA_ARGS__); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
void shuffle (void *, size_t cnt, size_t size);
|
||||
|
||||
void exec_children (const char *child_name, pid_t pids[], size_t child_cnt);
|
||||
void wait_children (pid_t pids[], size_t child_cnt);
|
||||
|
||||
void check_file_handle (int fd, const char *file_name,
|
||||
const void *buf_, size_t filesize);
|
||||
void check_file (const char *file_name, const void *buf, size_t filesize);
|
||||
|
||||
void compare_bytes (const void *read_data, const void *expected_data,
|
||||
size_t size, size_t ofs, const char *file_name);
|
||||
|
||||
#endif /* test/lib.h */
|
||||
19
src/tests/lib.pm
Normal file
19
src/tests/lib.pm
Normal file
@@ -0,0 +1,19 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use tests::random;
|
||||
|
||||
sub shuffle {
|
||||
my ($in, $cnt, $sz) = @_;
|
||||
$cnt * $sz == length $in or die;
|
||||
my (@a) = 0...$cnt - 1;
|
||||
for my $i (0...$cnt - 1) {
|
||||
my ($j) = $i + random_ulong () % ($cnt - $i);
|
||||
@a[$i, $j] = @a[$j, $i];
|
||||
}
|
||||
my ($out) = "";
|
||||
$out .= substr ($in, $_ * $sz, $sz) foreach @a;
|
||||
return $out;
|
||||
}
|
||||
|
||||
1;
|
||||
15
src/tests/main.c
Normal file
15
src/tests/main.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <random.h>
|
||||
#include "tests/lib.h"
|
||||
#include "tests/main.h"
|
||||
|
||||
int
|
||||
main (int argc UNUSED, char *argv[])
|
||||
{
|
||||
test_name = argv[0];
|
||||
|
||||
msg ("begin");
|
||||
random_init (0);
|
||||
test_main ();
|
||||
msg ("end");
|
||||
return 0;
|
||||
}
|
||||
6
src/tests/main.h
Normal file
6
src/tests/main.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef TESTS_MAIN_H
|
||||
#define TESTS_MAIN_H
|
||||
|
||||
void test_main (void);
|
||||
|
||||
#endif /* tests/main.h */
|
||||
152
src/tests/make-grade
Executable file
152
src/tests/make-grade
Executable file
@@ -0,0 +1,152 @@
|
||||
#! /usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
@ARGV == 3 || die;
|
||||
my ($src_dir, $results_file, $grading_file) = @ARGV;
|
||||
|
||||
# Read pass/file verdicts from $results_file.
|
||||
open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n";
|
||||
my (%verdicts, %verdict_counts);
|
||||
while (<RESULTS>) {
|
||||
my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die;
|
||||
$verdicts{$test} = $verdict eq 'pass';
|
||||
}
|
||||
close RESULTS;
|
||||
|
||||
my (@failures);
|
||||
my (@overall, @rubrics, @summary);
|
||||
my ($pct_actual, $pct_possible) = (0, 0);
|
||||
|
||||
# Read grading file.
|
||||
my (@items);
|
||||
open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n";
|
||||
while (<GRADING>) {
|
||||
s/#.*//;
|
||||
next if /^\s*$/;
|
||||
my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die;
|
||||
my ($dir) = $rubric_suffix =~ /^(.*)\//;
|
||||
my ($rubric_file) = "$src_dir/$rubric_suffix";
|
||||
open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n";
|
||||
|
||||
# Rubric file must begin with title line.
|
||||
my $title = <RUBRIC>;
|
||||
chomp $title;
|
||||
$title =~ s/:$// or die;
|
||||
$title .= " ($rubric_suffix):";
|
||||
push (@rubrics, $title);
|
||||
|
||||
my ($score, $possible) = (0, 0);
|
||||
my ($cnt, $passed) = (0, 0);
|
||||
my ($was_score) = 0;
|
||||
while (<RUBRIC>) {
|
||||
chomp;
|
||||
push (@rubrics, "\t$_"), next if /^-/;
|
||||
push (@rubrics, ""), next if /^\s*$/;
|
||||
my ($poss, $name) = /^(\d+)\t(.*)$/ or die;
|
||||
my ($test) = "$dir/$name";
|
||||
my ($points) = 0;
|
||||
if (!defined $verdicts{$test}) {
|
||||
push (@overall, "warning: $test not tested, assuming failure");
|
||||
} elsif ($verdicts{$test}) {
|
||||
$points = $poss;
|
||||
$passed++;
|
||||
}
|
||||
push (@failures, $test) if !$points;
|
||||
$verdict_counts{$test}++;
|
||||
push (@rubrics, sprintf ("\t%4s%2d/%2d %s",
|
||||
$points ? '' : '**', $points, $poss, $test));
|
||||
$score += $points;
|
||||
$possible += $poss;
|
||||
$cnt++;
|
||||
}
|
||||
close (RUBRIC);
|
||||
|
||||
push (@rubrics, "");
|
||||
push (@rubrics, "\t- Section summary.");
|
||||
push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
|
||||
'', $passed, $cnt, 'tests passed'));
|
||||
push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
|
||||
'', $score, $possible, 'points subtotal'));
|
||||
push (@rubrics, '');
|
||||
|
||||
my ($pct) = ($score / $possible) * $max_pct;
|
||||
push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%",
|
||||
$rubric_suffix,
|
||||
$score, $possible,
|
||||
$pct, $max_pct));
|
||||
$pct_actual += $pct;
|
||||
$pct_possible += $max_pct;
|
||||
}
|
||||
close GRADING;
|
||||
|
||||
my ($sum_line)
|
||||
= "--------------------------------------------- --- --- ------ ------";
|
||||
unshift (@summary,
|
||||
"SUMMARY BY TEST SET",
|
||||
'',
|
||||
sprintf ("%-45s %3s %3s %6s %6s",
|
||||
"Test Set", "Pts", "Max", "% Ttl", "% Max"),
|
||||
$sum_line);
|
||||
push (@summary,
|
||||
$sum_line,
|
||||
sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%",
|
||||
'Total', '', '', $pct_actual, $pct_possible));
|
||||
|
||||
unshift (@rubrics,
|
||||
"SUMMARY OF INDIVIDUAL TESTS",
|
||||
'');
|
||||
|
||||
foreach my $name (keys (%verdicts)) {
|
||||
my ($count) = $verdict_counts{$name};
|
||||
if (!defined ($count) || $count != 1) {
|
||||
if (!defined ($count) || !$count) {
|
||||
push (@overall, "warning: test $name doesn't count for grading");
|
||||
} else {
|
||||
push (@overall,
|
||||
"warning: test $name counted $count times in grading");
|
||||
}
|
||||
}
|
||||
}
|
||||
push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual));
|
||||
if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) {
|
||||
push (@overall, "ALL TESTED PASSED -- PERFECT SCORE");
|
||||
}
|
||||
|
||||
my (@divider) = ('', '- ' x 38, '');
|
||||
|
||||
print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics);
|
||||
|
||||
for my $test (@failures) {
|
||||
print map ("$_\n", @divider);
|
||||
print "DETAILS OF $test FAILURE:\n\n";
|
||||
|
||||
if (open (RESULT, '<', "$test.result")) {
|
||||
my $first_line = <RESULT>;
|
||||
my ($cnt) = 0;
|
||||
while (<RESULT>) {
|
||||
print;
|
||||
$cnt++;
|
||||
}
|
||||
close (RESULT);
|
||||
}
|
||||
|
||||
if (open (OUTPUT, '<', "$test.output")) {
|
||||
print "\nOUTPUT FROM $test:\n\n";
|
||||
|
||||
my ($panics, $boots) = (0, 0);
|
||||
while (<OUTPUT>) {
|
||||
if (/PANIC/ && ++$panics > 2) {
|
||||
print "[...details of additional panic(s) omitted...]\n";
|
||||
last;
|
||||
}
|
||||
print;
|
||||
if (/PintOS booting/ && ++$boots > 1) {
|
||||
print "[...details of reboot(s) omitted...]\n";
|
||||
last;
|
||||
}
|
||||
}
|
||||
close (OUTPUT);
|
||||
}
|
||||
}
|
||||
27
src/tests/random.pm
Normal file
27
src/tests/random.pm
Normal file
@@ -0,0 +1,27 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use tests::arc4;
|
||||
|
||||
my (@arc4);
|
||||
|
||||
sub random_init {
|
||||
if (@arc4 == 0) {
|
||||
my ($seed) = @_;
|
||||
$seed = 0 if !defined $seed;
|
||||
@arc4 = arc4_init (pack ("V", $seed));
|
||||
}
|
||||
}
|
||||
|
||||
sub random_bytes {
|
||||
random_init ();
|
||||
my ($n) = @_;
|
||||
return arc4_crypt (\@arc4, "\0" x $n);
|
||||
}
|
||||
|
||||
sub random_ulong {
|
||||
random_init ();
|
||||
return unpack ("V", random_bytes (4));
|
||||
}
|
||||
|
||||
1;
|
||||
650
src/tests/tests.pm
Normal file
650
src/tests/tests.pm
Normal file
@@ -0,0 +1,650 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::Algorithm::Diff;
|
||||
use File::Temp 'tempfile';
|
||||
use Fcntl qw(SEEK_SET SEEK_CUR);
|
||||
|
||||
sub fail;
|
||||
sub pass;
|
||||
|
||||
die if @ARGV != 2;
|
||||
our ($test, $src_dir) = @ARGV;
|
||||
|
||||
my ($msg_file) = tempfile ();
|
||||
select ($msg_file);
|
||||
|
||||
our (@prereq_tests) = ();
|
||||
if ($test =~ /^(.*)-persistence$/) {
|
||||
push (@prereq_tests, $1);
|
||||
}
|
||||
for my $prereq_test (@prereq_tests) {
|
||||
my (@result) = read_text_file ("$prereq_test.result");
|
||||
fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS';
|
||||
}
|
||||
|
||||
|
||||
# Generic testing.
|
||||
|
||||
sub check_expected {
|
||||
my ($expected) = pop @_;
|
||||
my (@options) = @_;
|
||||
my (@output) = read_text_file ("$test.output");
|
||||
common_checks ("run", @output);
|
||||
compare_output ("run", @options, \@output, $expected);
|
||||
}
|
||||
|
||||
sub common_checks {
|
||||
my ($run, @output) = @_;
|
||||
|
||||
fail "\u$run produced no output at all\n" if @output == 0;
|
||||
|
||||
check_for_panic ($run, @output);
|
||||
check_for_keyword ($run, "FAIL", @output);
|
||||
check_for_triple_fault ($run, @output);
|
||||
check_for_keyword ($run, "TIMEOUT", @output);
|
||||
|
||||
fail "\u$run didn't start up properly: no \"PintOS booting\" message\n"
|
||||
if !grep (/PintOS booting with.*kB RAM\.\.\./, @output);
|
||||
fail "\u$run didn't start up properly: no \"Boot complete\" message\n"
|
||||
if !grep (/Boot complete/, @output);
|
||||
fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n"
|
||||
if !grep (/Timer: \d+ ticks/, @output);
|
||||
fail "\u$run didn't shut down properly: no \"Powering off\" message\n"
|
||||
if !grep (/Powering off/, @output);
|
||||
}
|
||||
|
||||
sub check_for_panic {
|
||||
my ($run, @output) = @_;
|
||||
|
||||
my ($panic) = grep (/PANIC/, @output);
|
||||
return unless defined $panic;
|
||||
|
||||
print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")),
|
||||
"\n";
|
||||
|
||||
my (@stack_line) = grep (/Call stack:/, @output);
|
||||
if (@stack_line != 0) {
|
||||
my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/;
|
||||
|
||||
# Find a user program to translate user virtual addresses.
|
||||
my ($userprog) = "";
|
||||
$userprog = "$test"
|
||||
if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test;
|
||||
|
||||
# Get and print the backtrace.
|
||||
my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`);
|
||||
print "Call stack:$addrs\n";
|
||||
print "Translation of call stack:\n";
|
||||
print $trace;
|
||||
|
||||
# Print disclaimer.
|
||||
if ($userprog ne '' && index ($trace, $userprog) >= 0) {
|
||||
print <<EOF;
|
||||
Translations of user virtual addresses above are based on a guess at
|
||||
the binary to use. If this guess is incorrect, then those
|
||||
translations will be misleading.
|
||||
EOF
|
||||
}
|
||||
}
|
||||
|
||||
if ($panic =~ /sec_no \< d-\>capacity/) {
|
||||
print <<EOF;
|
||||
\nThis assertion commonly fails when accessing a file via an inode that
|
||||
has been closed and freed. Freeing an inode clears all its sector
|
||||
indexes to 0xcccccccc, which is not a valid sector number for disks
|
||||
smaller than about 1.6 TB.
|
||||
EOF
|
||||
}
|
||||
|
||||
fail;
|
||||
}
|
||||
|
||||
sub check_for_keyword {
|
||||
my ($run, $keyword, @output) = @_;
|
||||
|
||||
my ($kw_line) = grep (/$keyword/, @output);
|
||||
return unless defined $kw_line;
|
||||
|
||||
# Most output lines are prefixed by (test-name). Eliminate this
|
||||
# from our message for brevity.
|
||||
$kw_line =~ s/^\([^\)]+\)\s+//;
|
||||
print "$run: $kw_line\n";
|
||||
|
||||
fail;
|
||||
}
|
||||
|
||||
sub check_for_triple_fault {
|
||||
my ($run, @output) = @_;
|
||||
|
||||
my ($reboots) = grep (/PintOS booting/, @output) - 1;
|
||||
return unless $reboots > 0;
|
||||
|
||||
print <<EOF;
|
||||
\u$run spontaneously rebooted $reboots times.
|
||||
This is most often caused by unhandled page faults.
|
||||
Read the Triple Faults section in the Debugging chapter
|
||||
of the PintOS manual for more information.
|
||||
EOF
|
||||
|
||||
fail;
|
||||
}
|
||||
|
||||
# Get @output without header or trailer.
|
||||
sub get_core_output {
|
||||
my ($run, @output) = @_;
|
||||
my ($p);
|
||||
|
||||
my ($process);
|
||||
my ($start);
|
||||
for my $i (0...$#_) {
|
||||
$start = $i + 1, last
|
||||
if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/;
|
||||
}
|
||||
|
||||
my ($end);
|
||||
for my $i ($start...$#output) {
|
||||
$end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/;
|
||||
}
|
||||
|
||||
fail "\u$run didn't start a thread or process\n" if !defined $start;
|
||||
fail "\u$run started '$process' but it never finished\n" if !defined $end;
|
||||
|
||||
return @output[$start...$end];
|
||||
}
|
||||
|
||||
sub compare_output {
|
||||
my ($run) = shift @_;
|
||||
my ($expected) = pop @_;
|
||||
my ($output) = pop @_;
|
||||
my (%options) = @_;
|
||||
|
||||
my (@output) = get_core_output ($run, @$output);
|
||||
fail "\u$run didn't produce any output" if !@output;
|
||||
|
||||
my $ignore_exit_codes = exists $options{IGNORE_EXIT_CODES};
|
||||
if ($ignore_exit_codes) {
|
||||
delete $options{IGNORE_EXIT_CODES};
|
||||
@output = grep (!/^[a-zA-Z0-9-_]+: exit\(\-?\d+\)$/, @output);
|
||||
}
|
||||
my $ignore_user_faults = exists $options{IGNORE_USER_FAULTS};
|
||||
if ($ignore_user_faults) {
|
||||
delete $options{IGNORE_USER_FAULTS};
|
||||
@output = grep (!/^Page fault at.*in user context\.$/
|
||||
&& !/: dying due to interrupt 0x0e \(.*\).$/
|
||||
&& !/^Interrupt 0x0e \(.*\) at eip=/
|
||||
&& !/^ cr2=.* error=.*/
|
||||
&& !/^ eax=.* ebx=.* ecx=.* edx=.*/
|
||||
&& !/^ esi=.* edi=.* esp=.* ebp=.*/
|
||||
&& !/^ cs=.* ds=.* es=.* ss=.*/, @output);
|
||||
}
|
||||
my $ignore_kernel_faults = exists $options{IGNORE_KERNEL_FAULTS};
|
||||
if ($ignore_kernel_faults) {
|
||||
delete $options{IGNORE_KERNEL_FAULTS};
|
||||
@output = grep (!/^Page fault at.*in kernel context\.$/
|
||||
&& !/: dying due to interrupt 0x0e \(.*\).$/
|
||||
&& !/^Interrupt 0x0e \(.*\) at eip=/
|
||||
&& !/^ cr2=.* error=.*/
|
||||
&& !/^ eax=.* ebx=.* ecx=.* edx=.*/
|
||||
&& !/^ esi=.* edi=.* esp=.* ebp=.*/
|
||||
&& !/^ cs=.* ds=.* es=.* ss=.*/, @output);
|
||||
}
|
||||
my $ignore_div0_faults = exists $options{IGNORE_DIV0_FAULTS};
|
||||
if ($ignore_div0_faults) {
|
||||
delete $options{IGNORE_DIV0_FAULTS};
|
||||
@output = grep (!/: dying due to interrupt 0000 \(.*\).$/
|
||||
&& !/^Interrupt 0000 \(.*\) at eip=/
|
||||
&& !/^ cr2=.* error=.*/
|
||||
&& !/^ eax=.* ebx=.* ecx=.* edx=.*/
|
||||
&& !/^ esi=.* edi=.* esp=.* ebp=.*/
|
||||
&& !/^ cs=.* ds=.* es=.* ss=.*/, @output);
|
||||
}
|
||||
die "unknown option " . (keys (%options))[0] . "\n" if %options;
|
||||
|
||||
my ($msg);
|
||||
|
||||
# Compare actual output against each allowed output.
|
||||
if (ref ($expected) eq 'ARRAY') {
|
||||
my ($i) = 0;
|
||||
$expected = {map ((++$i => $_), @$expected)};
|
||||
}
|
||||
foreach my $key (keys %$expected) {
|
||||
my (@expected) = split ("\n", $expected->{$key});
|
||||
|
||||
$msg .= "Acceptable output:\n";
|
||||
$msg .= join ('', map (" $_\n", @expected));
|
||||
|
||||
# Check whether actual and expected match.
|
||||
# If it's a perfect match, we're done.
|
||||
if ($#output == $#expected) {
|
||||
my ($eq) = 1;
|
||||
for (my ($i) = 0; $i <= $#expected; $i++) {
|
||||
$eq = 0 if $output[$i] ne $expected[$i];
|
||||
}
|
||||
return $key if $eq;
|
||||
}
|
||||
|
||||
# They differ. Output a diff.
|
||||
my (@diff) = "";
|
||||
my ($d) = Algorithm::Diff->new (\@expected, \@output);
|
||||
while ($d->Next ()) {
|
||||
my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
|
||||
if ($d->Same ()) {
|
||||
push (@diff, map (" $_\n", $d->Items (1)));
|
||||
} else {
|
||||
push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1);
|
||||
push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2);
|
||||
}
|
||||
}
|
||||
|
||||
$msg .= "Differences in `diff -u' format:\n";
|
||||
$msg .= join ('', @diff);
|
||||
}
|
||||
|
||||
# Failed to match. Report failure.
|
||||
$msg .= "\n(Process exit codes are excluded for matching purposes.)\n"
|
||||
if $ignore_exit_codes;
|
||||
$msg .= "\n(User fault messages are excluded for matching purposes.)\n"
|
||||
if $ignore_user_faults;
|
||||
$msg .= "\n(Kernel fault messages are excluded for matching purposes.)\n"
|
||||
if $ignore_kernel_faults;
|
||||
$msg .= "\n(Divide Error messages are excluded for matching purposes.)\n"
|
||||
if $ignore_div0_faults;
|
||||
fail "Test output failed to match any acceptable form.\n\n$msg";
|
||||
}
|
||||
|
||||
# File system extraction.
|
||||
|
||||
# check_archive (\%CONTENTS)
|
||||
#
|
||||
# Checks that the extracted file system's contents match \%CONTENTS.
|
||||
# Each key in the hash is a file name. Each value may be:
|
||||
#
|
||||
# - $FILE: Name of a host file containing the expected contents.
|
||||
#
|
||||
# - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE
|
||||
# comprising the $LENGTH bytes starting at $OFFSET.
|
||||
#
|
||||
# - [$CONTENTS]: The literal expected file contents, as a string.
|
||||
#
|
||||
# - {SUBDIR}: A subdirectory, in the same form described here,
|
||||
# recursively.
|
||||
sub check_archive {
|
||||
my ($expected_hier) = @_;
|
||||
|
||||
my (@output) = read_text_file ("$test.output");
|
||||
common_checks ("file system extraction run", @output);
|
||||
|
||||
@output = get_core_output ("file system extraction run", @output);
|
||||
@output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output);
|
||||
fail join ("\n", "Error extracting file system:", @output) if @output;
|
||||
|
||||
my ($test_base_name) = $test;
|
||||
$test_base_name =~ s%.*/%%;
|
||||
$test_base_name =~ s%-persistence$%%;
|
||||
$expected_hier->{$test_base_name} = $prereq_tests[0];
|
||||
$expected_hier->{'tar'} = 'tests/filesys/extended/tar';
|
||||
|
||||
my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, ""));
|
||||
my (%actual) = read_tar ("$prereq_tests[0].tar");
|
||||
|
||||
my ($errors) = 0;
|
||||
foreach my $name (sort keys %expected) {
|
||||
if (exists $actual{$name}) {
|
||||
if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) {
|
||||
print "$name is a directory but should be an ordinary file.\n";
|
||||
$errors++;
|
||||
} elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) {
|
||||
print "$name is an ordinary file but should be a directory.\n";
|
||||
$errors++;
|
||||
}
|
||||
} else {
|
||||
print "$name is missing from the file system.\n";
|
||||
$errors++;
|
||||
}
|
||||
}
|
||||
foreach my $name (sort keys %actual) {
|
||||
if (!exists $expected{$name}) {
|
||||
if ($name =~ /^[[:print:]]+$/) {
|
||||
print "$name exists in the file system but it should not.\n";
|
||||
} else {
|
||||
my ($esc_name) = $name;
|
||||
$esc_name =~ s/[^[:print:]]/./g;
|
||||
print <<EOF;
|
||||
$esc_name exists in the file system but should not. (The name
|
||||
of this file contains unusual characters that were printed as `.'.)
|
||||
EOF
|
||||
}
|
||||
$errors++;
|
||||
}
|
||||
}
|
||||
if ($errors) {
|
||||
print "\nActual contents of file system:\n";
|
||||
print_fs (%actual);
|
||||
print "\nExpected contents of file system:\n";
|
||||
print_fs (%expected);
|
||||
} else {
|
||||
foreach my $name (sort keys %expected) {
|
||||
if (!is_dir ($expected{$name})) {
|
||||
my ($exp_file, $exp_length) = open_file ($expected{$name});
|
||||
my ($act_file, $act_length) = open_file ($actual{$name});
|
||||
$errors += !compare_files ($exp_file, $exp_length,
|
||||
$act_file, $act_length, $name,
|
||||
!$errors);
|
||||
close ($exp_file);
|
||||
close ($act_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
fail "Extracted file system contents are not correct.\n" if $errors;
|
||||
}
|
||||
|
||||
# open_file ([$FILE, $OFFSET, $LENGTH])
|
||||
# open_file ([$CONTENTS])
|
||||
#
|
||||
# Opens a file for the contents passed in, which must be in one of
|
||||
# the two above forms that correspond to check_archive() arguments.
|
||||
#
|
||||
# Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and
|
||||
# $LENGTH is the number of bytes in the file's content.
|
||||
sub open_file {
|
||||
my ($value) = @_;
|
||||
die if ref ($value) ne 'ARRAY';
|
||||
|
||||
my ($file) = tempfile ();
|
||||
my ($length);
|
||||
if (@$value == 1) {
|
||||
$length = length ($value->[0]);
|
||||
$file = tempfile ();
|
||||
syswrite ($file, $value->[0]) == $length
|
||||
or die "writing temporary file: $!\n";
|
||||
sysseek ($file, 0, SEEK_SET);
|
||||
} elsif (@$value == 3) {
|
||||
$length = $value->[2];
|
||||
open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n";
|
||||
die "$value->[0]: file is smaller than expected\n"
|
||||
if -s $file < $value->[1] + $length;
|
||||
sysseek ($file, $value->[1], SEEK_SET);
|
||||
} else {
|
||||
die;
|
||||
}
|
||||
return ($file, $length);
|
||||
}
|
||||
|
||||
# compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE)
|
||||
#
|
||||
# Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B.
|
||||
# ($A and $B are handles.)
|
||||
# If their contents differ, prints a brief message describing
|
||||
# the differences, using $NAME to identify the file.
|
||||
# The message contains more detail if $VERBOSE is nonzero.
|
||||
# Returns 1 if the contents are identical, 0 otherwise.
|
||||
sub compare_files {
|
||||
my ($a, $a_size, $b, $b_size, $name, $verbose) = @_;
|
||||
my ($ofs) = 0;
|
||||
select(STDOUT);
|
||||
for (;;) {
|
||||
my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size;
|
||||
my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size;
|
||||
my ($a_data, $b_data);
|
||||
if (!defined (sysread ($a, $a_data, $a_amt))
|
||||
|| !defined (sysread ($b, $b_data, $b_amt))) {
|
||||
die "reading $name: $!\n";
|
||||
}
|
||||
|
||||
my ($a_len) = length $a_data;
|
||||
my ($b_len) = length $b_data;
|
||||
last if $a_len == 0 && $b_len == 0;
|
||||
|
||||
if ($a_data ne $b_data) {
|
||||
my ($min_len) = $a_len < $b_len ? $a_len : $b_len;
|
||||
my ($diff_ofs);
|
||||
for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) {
|
||||
last if (substr ($a_data, $diff_ofs, 1)
|
||||
ne substr ($b_data, $diff_ofs, 1));
|
||||
}
|
||||
|
||||
printf "\nFile $name differs from expected "
|
||||
. "starting at offset 0x%x.\n", $ofs + $diff_ofs;
|
||||
if ($verbose ) {
|
||||
print "Expected contents:\n";
|
||||
hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs);
|
||||
print "Actual contents:\n";
|
||||
hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
$ofs += $a_len;
|
||||
$a_size -= $a_len;
|
||||
$b_size -= $b_len;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# hex_dump ($DATA, $OFS)
|
||||
#
|
||||
# Prints $DATA in hex and text formats.
|
||||
# The first byte of $DATA corresponds to logical offset $OFS
|
||||
# in whatever file the data comes from.
|
||||
sub hex_dump {
|
||||
my ($data, $ofs) = @_;
|
||||
|
||||
if ($data eq '') {
|
||||
printf " (File ends at offset %08x.)\n", $ofs;
|
||||
return;
|
||||
}
|
||||
|
||||
my ($per_line) = 16;
|
||||
while ((my $size = length ($data)) > 0) {
|
||||
my ($start) = $ofs % $per_line;
|
||||
my ($end) = $per_line;
|
||||
$end = $start + $size if $end - $start > $size;
|
||||
my ($n) = $end - $start;
|
||||
|
||||
printf "0x%08x ", int ($ofs / $per_line) * $per_line;
|
||||
|
||||
# Hex version.
|
||||
print " " x $start;
|
||||
for my $i ($start...$end - 1) {
|
||||
printf "%02x", ord (substr ($data, $i - $start, 1));
|
||||
print $i == $per_line / 2 - 1 ? '-' : ' ';
|
||||
}
|
||||
print " " x ($per_line - $end);
|
||||
|
||||
# Character version.
|
||||
my ($esc_data) = substr ($data, 0, $n);
|
||||
$esc_data =~ s/[^[:print:]]/./g;
|
||||
print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|";
|
||||
|
||||
print "\n";
|
||||
|
||||
$data = substr ($data, $n);
|
||||
$ofs += $n;
|
||||
}
|
||||
}
|
||||
|
||||
# print_fs (%FS)
|
||||
#
|
||||
# Prints a list of files in %FS, which must be a file system
|
||||
# as flattened by flatten_hierarchy() and normalized by
|
||||
# normalize_fs().
|
||||
sub print_fs {
|
||||
my (%fs) = @_;
|
||||
foreach my $name (sort keys %fs) {
|
||||
my ($esc_name) = $name;
|
||||
$esc_name =~ s/[^[:print:]]/./g;
|
||||
print "$esc_name: ";
|
||||
if (!is_dir ($fs{$name})) {
|
||||
print +file_size ($fs{$name}), "-byte file";
|
||||
} else {
|
||||
print "directory";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
print "(empty)\n" if !@_;
|
||||
}
|
||||
|
||||
# normalize_fs (%FS)
|
||||
#
|
||||
# Takes a file system as flattened by flatten_hierarchy().
|
||||
# Returns a similar file system in which values of the form $FILE
|
||||
# are replaced by those of the form [$FILE, $OFFSET, $LENGTH].
|
||||
sub normalize_fs {
|
||||
my (%fs) = @_;
|
||||
foreach my $name (keys %fs) {
|
||||
my ($value) = $fs{$name};
|
||||
next if is_dir ($value) || ref ($value) ne '';
|
||||
die "can't open $value\n" if !stat $value;
|
||||
$fs{$name} = [$value, 0, -s _];
|
||||
}
|
||||
return %fs;
|
||||
}
|
||||
|
||||
# is_dir ($VALUE)
|
||||
#
|
||||
# Takes a value like one in the hash returned by flatten_hierarchy()
|
||||
# and returns 1 if it represents a directory, 0 otherwise.
|
||||
sub is_dir {
|
||||
my ($value) = @_;
|
||||
return ref ($value) eq '' && $value eq 'directory';
|
||||
}
|
||||
|
||||
# file_size ($VALUE)
|
||||
#
|
||||
# Takes a value like one in the hash returned by flatten_hierarchy()
|
||||
# and returns the size of the file it represents.
|
||||
sub file_size {
|
||||
my ($value) = @_;
|
||||
die if is_dir ($value);
|
||||
die if ref ($value) ne 'ARRAY';
|
||||
return @$value > 1 ? $value->[2] : length ($value->[0]);
|
||||
}
|
||||
|
||||
# flatten_hierarchy ($HIER_FS, $PREFIX)
|
||||
#
|
||||
# Takes a file system in the format expected by check_archive() and
|
||||
# returns a "flattened" version in which file names include all parent
|
||||
# directory names and the value of directories is just "directory".
|
||||
sub flatten_hierarchy {
|
||||
my (%hier_fs) = %{$_[0]};
|
||||
my ($prefix) = $_[1];
|
||||
my (%flat_fs);
|
||||
for my $name (keys %hier_fs) {
|
||||
my ($value) = $hier_fs{$name};
|
||||
if (ref $value eq 'HASH') {
|
||||
%flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/"));
|
||||
$flat_fs{"$prefix$name"} = 'directory';
|
||||
} else {
|
||||
$flat_fs{"$prefix$name"} = $value;
|
||||
}
|
||||
}
|
||||
return %flat_fs;
|
||||
}
|
||||
|
||||
# read_tar ($ARCHIVE)
|
||||
#
|
||||
# Reads the ustar-format tar file in $ARCHIVE
|
||||
# and returns a flattened file system for it.
|
||||
sub read_tar {
|
||||
my ($archive) = @_;
|
||||
my (%content);
|
||||
open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n";
|
||||
for (;;) {
|
||||
my ($header);
|
||||
if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) {
|
||||
fail "$archive: unexpected end of file\n" if $retval >= 0;
|
||||
fail "$archive: read: $!\n";
|
||||
}
|
||||
|
||||
last if $header eq "\0" x 512;
|
||||
|
||||
# Verify magic numbers.
|
||||
if (substr ($header, 257, 6) ne "ustar\0"
|
||||
|| substr ($header, 263, 2) ne '00') {
|
||||
fail "$archive: corrupt ustar header\n";
|
||||
}
|
||||
|
||||
# Verify checksum.
|
||||
my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8)));
|
||||
my ($correct_chksum) = unpack ("%32a*", $header);
|
||||
fail "$archive: bad header checksum\n" if $chksum != $correct_chksum;
|
||||
|
||||
# Get file name.
|
||||
my ($name) = unpack ("Z100", $header);
|
||||
my ($prefix) = unpack ("Z*", substr ($header, 345));
|
||||
$name = "$prefix/$name" if $prefix ne '';
|
||||
fail "$archive: contains file with empty name" if $name eq '';
|
||||
|
||||
# Get type.
|
||||
my ($typeflag) = substr ($header, 156, 1);
|
||||
$typeflag = '0' if $typeflag eq "\0";
|
||||
fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/;
|
||||
|
||||
# Get size.
|
||||
my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
|
||||
fail "bad size $size\n" if $size < 0;
|
||||
$size = 0 if $typeflag eq '5';
|
||||
|
||||
# Store content.
|
||||
$name =~ s%^(/|\./|\.\./)*%%; # Strip leading "/", "./", "../".
|
||||
$name = '' if $name eq '.' || $name eq '..';
|
||||
if (exists $content{$name}) {
|
||||
fail "$archive: contains multiple entries for $name\n";
|
||||
}
|
||||
if ($typeflag eq '5') {
|
||||
$content{$name} = 'directory' if $name ne '';
|
||||
} else {
|
||||
fail "$archive: contains file with empty name\n" if $name eq '';
|
||||
my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR);
|
||||
$content{$name} = [$archive, $position, $size];
|
||||
sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
close (ARCHIVE);
|
||||
return %content;
|
||||
}
|
||||
|
||||
# Utilities.
|
||||
|
||||
sub fail {
|
||||
finish ("FAIL", @_);
|
||||
}
|
||||
|
||||
sub pass {
|
||||
finish ("PASS", @_);
|
||||
}
|
||||
|
||||
sub finish {
|
||||
my ($verdict, @messages) = @_;
|
||||
|
||||
seek ($msg_file, 0, 0);
|
||||
push (@messages, <$msg_file>);
|
||||
close ($msg_file);
|
||||
chomp (@messages);
|
||||
|
||||
my ($result_fn) = "$test.result";
|
||||
open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n";
|
||||
print RESULT "$verdict\n";
|
||||
print RESULT "$_\n" foreach @messages;
|
||||
close (RESULT);
|
||||
|
||||
if ($verdict eq 'PASS') {
|
||||
print STDOUT "pass $test\n";
|
||||
} else {
|
||||
print STDOUT "FAIL $test\n";
|
||||
}
|
||||
print STDOUT "$_\n" foreach @messages;
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub read_text_file {
|
||||
my ($file_name) = @_;
|
||||
open (FILE, '<', $file_name) or die "$file_name: open: $!\n";
|
||||
my (@content) = <FILE>;
|
||||
chomp (@content);
|
||||
close (FILE);
|
||||
return @content;
|
||||
}
|
||||
|
||||
1;
|
||||
7
src/tests/threads/Grading
Normal file
7
src/tests/threads/Grading
Normal file
@@ -0,0 +1,7 @@
|
||||
# Percentage of the testing point total designated for each set of tests.
|
||||
|
||||
5.0% tests/devices/Rubric.alarmfunc
|
||||
5.0% tests/devices/Rubric.alarmrobust
|
||||
45.0% tests/threads/Rubric.priority
|
||||
0.0% tests/threads/Rubric.priorityCR
|
||||
45.0% tests/threads/Rubric.mlfqs
|
||||
49
src/tests/threads/Make.tests
Normal file
49
src/tests/threads/Make.tests
Normal file
@@ -0,0 +1,49 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
# Test names.
|
||||
tests/threads_TESTS = $(addprefix tests/threads/, \
|
||||
alarm-priority priority-change priority-donate-one \
|
||||
priority-donate-multiple priority-donate-multiple2 \
|
||||
priority-donate-nest priority-donate-sema priority-donate-lower \
|
||||
priority-fifo priority-preempt priority-sema priority-condvar \
|
||||
priority-donate-chain priority-preservation \
|
||||
mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2 \
|
||||
mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block)
|
||||
|
||||
# Sources for tests.
|
||||
tests/threads_SRC = tests/threads/tests.c
|
||||
tests/threads_SRC += tests/threads/alarm-priority.c
|
||||
tests/threads_SRC += tests/threads/priority-change.c
|
||||
tests/threads_SRC += tests/threads/priority-donate-one.c
|
||||
tests/threads_SRC += tests/threads/priority-donate-multiple.c
|
||||
tests/threads_SRC += tests/threads/priority-donate-multiple2.c
|
||||
tests/threads_SRC += tests/threads/priority-donate-nest.c
|
||||
tests/threads_SRC += tests/threads/priority-donate-sema.c
|
||||
tests/threads_SRC += tests/threads/priority-donate-lower.c
|
||||
tests/threads_SRC += tests/threads/priority-fifo.c
|
||||
tests/threads_SRC += tests/threads/priority-preempt.c
|
||||
tests/threads_SRC += tests/threads/priority-sema.c
|
||||
tests/threads_SRC += tests/threads/priority-condvar.c
|
||||
tests/threads_SRC += tests/threads/priority-donate-chain.c
|
||||
tests/threads_SRC += tests/threads/priority-preservation.c
|
||||
tests/threads_SRC += tests/threads/mlfqs-load-1.c
|
||||
tests/threads_SRC += tests/threads/mlfqs-load-60.c
|
||||
tests/threads_SRC += tests/threads/mlfqs-load-avg.c
|
||||
tests/threads_SRC += tests/threads/mlfqs-recent-1.c
|
||||
tests/threads_SRC += tests/threads/mlfqs-fair.c
|
||||
tests/threads_SRC += tests/threads/mlfqs-block.c
|
||||
|
||||
MLFQS_OUTPUTS = \
|
||||
tests/threads/mlfqs-load-1.output \
|
||||
tests/threads/mlfqs-load-60.output \
|
||||
tests/threads/mlfqs-load-avg.output \
|
||||
tests/threads/mlfqs-recent-1.output \
|
||||
tests/threads/mlfqs-fair-2.output \
|
||||
tests/threads/mlfqs-fair-20.output \
|
||||
tests/threads/mlfqs-nice-2.output \
|
||||
tests/threads/mlfqs-nice-10.output \
|
||||
tests/threads/mlfqs-block.output
|
||||
|
||||
$(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs
|
||||
$(MLFQS_OUTPUTS): TIMEOUT = 480
|
||||
|
||||
14
src/tests/threads/Rubric.mlfqs
Normal file
14
src/tests/threads/Rubric.mlfqs
Normal file
@@ -0,0 +1,14 @@
|
||||
Functionality of advanced scheduler:
|
||||
5 mlfqs-load-1
|
||||
5 mlfqs-load-60
|
||||
5 mlfqs-load-avg
|
||||
|
||||
5 mlfqs-recent-1
|
||||
|
||||
5 mlfqs-fair-2
|
||||
5 mlfqs-fair-20
|
||||
|
||||
5 mlfqs-nice-2
|
||||
5 mlfqs-nice-10
|
||||
|
||||
5 mlfqs-block
|
||||
16
src/tests/threads/Rubric.priority
Normal file
16
src/tests/threads/Rubric.priority
Normal file
@@ -0,0 +1,16 @@
|
||||
Functionality of priority scheduler:
|
||||
5 alarm-priority
|
||||
5 priority-change
|
||||
5 priority-preempt
|
||||
|
||||
5 priority-fifo
|
||||
5 priority-sema
|
||||
5 priority-condvar
|
||||
|
||||
5 priority-donate-one
|
||||
5 priority-donate-multiple
|
||||
5 priority-donate-multiple2
|
||||
5 priority-donate-nest
|
||||
10 priority-donate-chain
|
||||
5 priority-donate-sema
|
||||
5 priority-donate-lower
|
||||
2
src/tests/threads/Rubric.priorityCR
Normal file
2
src/tests/threads/Rubric.priorityCR
Normal file
@@ -0,0 +1,2 @@
|
||||
Full correctness of priority scheduler:
|
||||
10 priority-preservation
|
||||
58
src/tests/threads/alarm-priority.c
Normal file
58
src/tests/threads/alarm-priority.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Checks that when the alarm clock wakes up threads, the
|
||||
higher-priority threads run first. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/threads/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static thread_func alarm_priority_thread;
|
||||
static int64_t wake_time;
|
||||
static struct semaphore wait_sema;
|
||||
|
||||
void
|
||||
test_alarm_priority (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* This test does not work with the MLFQS. */
|
||||
ASSERT (!thread_mlfqs);
|
||||
|
||||
wake_time = timer_ticks () + 5 * TIMER_FREQ;
|
||||
sema_init (&wait_sema, 0);
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
int priority = PRI_DEFAULT - (i + 5) % 10 - 1;
|
||||
char name[16];
|
||||
snprintf (name, sizeof name, "priority %d", priority);
|
||||
thread_create (name, priority, alarm_priority_thread, NULL);
|
||||
}
|
||||
|
||||
thread_set_priority (PRI_MIN);
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
sema_down (&wait_sema);
|
||||
}
|
||||
|
||||
static void
|
||||
alarm_priority_thread (void *aux UNUSED)
|
||||
{
|
||||
/* Busy-wait until the current time changes. */
|
||||
int64_t start_time = timer_ticks ();
|
||||
while (timer_elapsed (start_time) == 0)
|
||||
continue;
|
||||
|
||||
/* Now we know we're at the very beginning of a timer tick, so
|
||||
we can call timer_sleep() without worrying about races
|
||||
between checking the time and a timer interrupt. */
|
||||
timer_sleep (wake_time - timer_ticks ());
|
||||
|
||||
/* Print a message on wake-up. */
|
||||
msg ("Thread %s woke up.", thread_name ());
|
||||
|
||||
sema_up (&wait_sema);
|
||||
}
|
||||
19
src/tests/threads/alarm-priority.ck
Normal file
19
src/tests/threads/alarm-priority.ck
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected ([<<'EOF']);
|
||||
(alarm-priority) begin
|
||||
(alarm-priority) Thread priority 30 woke up.
|
||||
(alarm-priority) Thread priority 29 woke up.
|
||||
(alarm-priority) Thread priority 28 woke up.
|
||||
(alarm-priority) Thread priority 27 woke up.
|
||||
(alarm-priority) Thread priority 26 woke up.
|
||||
(alarm-priority) Thread priority 25 woke up.
|
||||
(alarm-priority) Thread priority 24 woke up.
|
||||
(alarm-priority) Thread priority 23 woke up.
|
||||
(alarm-priority) Thread priority 22 woke up.
|
||||
(alarm-priority) Thread priority 21 woke up.
|
||||
(alarm-priority) end
|
||||
EOF
|
||||
pass;
|
||||
64
src/tests/threads/mlfqs-block.c
Normal file
64
src/tests/threads/mlfqs-block.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/* Checks that recent_cpu and priorities are updated for blocked
|
||||
threads.
|
||||
|
||||
The main thread sleeps for 25 seconds, spins for 5 seconds,
|
||||
then releases a lock. The "block" thread spins for 20 seconds
|
||||
then attempts to acquire the lock, which will block for 10
|
||||
seconds (until the main thread releases it). If recent_cpu
|
||||
decays properly while the "block" thread sleeps, then the
|
||||
block thread should be immediately scheduled when the main
|
||||
thread releases the lock. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/threads/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static void block_thread (void *lock_);
|
||||
|
||||
void
|
||||
test_mlfqs_block (void)
|
||||
{
|
||||
int64_t start_time;
|
||||
struct lock lock;
|
||||
|
||||
ASSERT (thread_mlfqs);
|
||||
|
||||
msg ("Main thread acquiring lock.");
|
||||
lock_init (&lock);
|
||||
lock_acquire (&lock);
|
||||
|
||||
msg ("Main thread creating block thread, sleeping 25 seconds...");
|
||||
thread_create ("block", PRI_DEFAULT, block_thread, &lock);
|
||||
timer_sleep (25 * TIMER_FREQ);
|
||||
|
||||
msg ("Main thread spinning for 5 seconds...");
|
||||
start_time = timer_ticks ();
|
||||
while (timer_elapsed (start_time) < 5 * TIMER_FREQ)
|
||||
continue;
|
||||
|
||||
msg ("Main thread releasing lock.");
|
||||
lock_release (&lock);
|
||||
|
||||
msg ("Block thread should have already acquired lock.");
|
||||
}
|
||||
|
||||
static void
|
||||
block_thread (void *lock_)
|
||||
{
|
||||
struct lock *lock = lock_;
|
||||
int64_t start_time;
|
||||
|
||||
msg ("Block thread spinning for 20 seconds...");
|
||||
start_time = timer_ticks ();
|
||||
while (timer_elapsed (start_time) < 20 * TIMER_FREQ)
|
||||
continue;
|
||||
|
||||
msg ("Block thread acquiring lock...");
|
||||
lock_acquire (lock);
|
||||
|
||||
msg ("...got it.");
|
||||
}
|
||||
17
src/tests/threads/mlfqs-block.ck
Normal file
17
src/tests/threads/mlfqs-block.ck
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
check_expected ([<<'EOF']);
|
||||
(mlfqs-block) begin
|
||||
(mlfqs-block) Main thread acquiring lock.
|
||||
(mlfqs-block) Main thread creating block thread, sleeping 25 seconds...
|
||||
(mlfqs-block) Block thread spinning for 20 seconds...
|
||||
(mlfqs-block) Block thread acquiring lock...
|
||||
(mlfqs-block) Main thread spinning for 5 seconds...
|
||||
(mlfqs-block) Main thread releasing lock.
|
||||
(mlfqs-block) ...got it.
|
||||
(mlfqs-block) Block thread should have already acquired lock.
|
||||
(mlfqs-block) end
|
||||
EOF
|
||||
pass;
|
||||
7
src/tests/threads/mlfqs-fair-2.ck
Normal file
7
src/tests/threads/mlfqs-fair-2.ck
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
use tests::threads::mlfqs;
|
||||
|
||||
check_mlfqs_fair ([0, 0], 50);
|
||||
7
src/tests/threads/mlfqs-fair-20.ck
Normal file
7
src/tests/threads/mlfqs-fair-20.ck
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
use tests::threads::mlfqs;
|
||||
|
||||
check_mlfqs_fair ([(0) x 20], 20);
|
||||
124
src/tests/threads/mlfqs-fair.c
Normal file
124
src/tests/threads/mlfqs-fair.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/* Measures the correctness of the "nice" implementation.
|
||||
|
||||
The "fair" tests run either 2 or 20 threads all niced to 0.
|
||||
The threads should all receive approximately the same number
|
||||
of ticks. Each test runs for 30 seconds, so the ticks should
|
||||
also sum to approximately 30 * 100 == 3000 ticks.
|
||||
|
||||
The mlfqs-nice-2 test runs 2 threads, one with nice 0, the
|
||||
other with nice 5, which should receive 1,904 and 1,096 ticks,
|
||||
respectively, over 30 seconds.
|
||||
|
||||
The mlfqs-nice-10 test runs 10 threads with nice 0 through 9.
|
||||
They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40,
|
||||
and 8 ticks, respectively, over 30 seconds.
|
||||
|
||||
(The above are computed via simulation in mlfqs.pm.) */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "tests/threads/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/palloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step);
|
||||
|
||||
void
|
||||
test_mlfqs_fair_2 (void)
|
||||
{
|
||||
test_mlfqs_fair (2, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_mlfqs_fair_20 (void)
|
||||
{
|
||||
test_mlfqs_fair (20, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_mlfqs_nice_2 (void)
|
||||
{
|
||||
test_mlfqs_fair (2, 0, 5);
|
||||
}
|
||||
|
||||
void
|
||||
test_mlfqs_nice_10 (void)
|
||||
{
|
||||
test_mlfqs_fair (10, 0, 1);
|
||||
}
|
||||
|
||||
#define MAX_THREAD_CNT 20
|
||||
|
||||
struct thread_info
|
||||
{
|
||||
int64_t start_time;
|
||||
int tick_count;
|
||||
int nice;
|
||||
};
|
||||
|
||||
static void load_thread (void *aux);
|
||||
|
||||
static void
|
||||
test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step)
|
||||
{
|
||||
struct thread_info info[MAX_THREAD_CNT];
|
||||
int64_t start_time;
|
||||
int nice;
|
||||
int i;
|
||||
|
||||
ASSERT (thread_mlfqs);
|
||||
ASSERT (thread_cnt <= MAX_THREAD_CNT);
|
||||
ASSERT (nice_min >= -10);
|
||||
ASSERT (nice_step >= 0);
|
||||
ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20);
|
||||
|
||||
thread_set_nice (-20);
|
||||
|
||||
start_time = timer_ticks ();
|
||||
msg ("Starting %d threads...", thread_cnt);
|
||||
nice = nice_min;
|
||||
for (i = 0; i < thread_cnt; i++)
|
||||
{
|
||||
struct thread_info *ti = &info[i];
|
||||
char name[16];
|
||||
|
||||
ti->start_time = start_time;
|
||||
ti->tick_count = 0;
|
||||
ti->nice = nice;
|
||||
|
||||
snprintf(name, sizeof name, "load %d", i);
|
||||
thread_create (name, PRI_DEFAULT, load_thread, ti);
|
||||
|
||||
nice += nice_step;
|
||||
}
|
||||
msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time));
|
||||
|
||||
msg ("Sleeping 40 seconds to let threads run, please wait...");
|
||||
timer_sleep (40 * TIMER_FREQ);
|
||||
|
||||
for (i = 0; i < thread_cnt; i++)
|
||||
msg ("Thread %d received %d ticks.", i, info[i].tick_count);
|
||||
}
|
||||
|
||||
static void
|
||||
load_thread (void *ti_)
|
||||
{
|
||||
struct thread_info *ti = ti_;
|
||||
int64_t sleep_time = 5 * TIMER_FREQ;
|
||||
int64_t spin_time = sleep_time + 30 * TIMER_FREQ;
|
||||
int64_t last_time = 0;
|
||||
|
||||
thread_set_nice (ti->nice);
|
||||
timer_sleep (sleep_time - timer_elapsed (ti->start_time));
|
||||
while (timer_elapsed (ti->start_time) < spin_time)
|
||||
{
|
||||
int64_t cur_time = timer_ticks ();
|
||||
if (cur_time != last_time)
|
||||
ti->tick_count++;
|
||||
last_time = cur_time;
|
||||
}
|
||||
}
|
||||
60
src/tests/threads/mlfqs-load-1.c
Normal file
60
src/tests/threads/mlfqs-load-1.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/* Verifies that a single busy thread raises the load average to
|
||||
0.5 in 38 to 45 seconds. The expected time is 42 seconds, as
|
||||
you can verify:
|
||||
perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"'
|
||||
|
||||
Then, verifies that 10 seconds of inactivity drop the load
|
||||
average back below 0.5 again. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/threads/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
void
|
||||
test_mlfqs_load_1 (void)
|
||||
{
|
||||
int64_t start_time;
|
||||
int elapsed;
|
||||
int load_avg;
|
||||
|
||||
ASSERT (thread_mlfqs);
|
||||
|
||||
msg ("spinning for up to 45 seconds, please wait...");
|
||||
|
||||
start_time = timer_ticks ();
|
||||
for (;;)
|
||||
{
|
||||
load_avg = thread_get_load_avg ();
|
||||
ASSERT (load_avg >= 0);
|
||||
elapsed = (int)(timer_elapsed (start_time) / TIMER_FREQ);
|
||||
if (load_avg > 100)
|
||||
fail ("load average is %d.%02d "
|
||||
"but should be between 0 and 1 (after %d seconds)",
|
||||
load_avg / 100, load_avg % 100, elapsed);
|
||||
else if (load_avg > 50)
|
||||
break;
|
||||
else if (elapsed > 45)
|
||||
fail ("load average stayed below 0.5 for more than 45 seconds");
|
||||
}
|
||||
|
||||
if (elapsed < 38)
|
||||
fail ("load average took only %d seconds to rise above 0.5", elapsed);
|
||||
msg ("load average rose to 0.5 after %d seconds", elapsed);
|
||||
|
||||
msg ("sleeping for another 10 seconds, please wait...");
|
||||
timer_sleep (TIMER_FREQ * 10);
|
||||
|
||||
load_avg = thread_get_load_avg ();
|
||||
if (load_avg < 0)
|
||||
fail ("load average fell below 0");
|
||||
if (load_avg > 50)
|
||||
fail ("load average stayed above 0.5 for more than 10 seconds");
|
||||
msg ("load average fell back below 0.5 (to %d.%02d)",
|
||||
load_avg / 100, load_avg % 100);
|
||||
|
||||
pass ();
|
||||
}
|
||||
15
src/tests/threads/mlfqs-load-1.ck
Normal file
15
src/tests/threads/mlfqs-load-1.ck
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
|
||||
our ($test);
|
||||
my (@output) = read_text_file ("$test.output");
|
||||
|
||||
common_checks ("run", @output);
|
||||
|
||||
@output = get_core_output ("run", @output);
|
||||
fail "missing PASS in output"
|
||||
unless grep ($_ eq '(mlfqs-load-1) PASS', @output);
|
||||
|
||||
pass;
|
||||
160
src/tests/threads/mlfqs-load-60.c
Normal file
160
src/tests/threads/mlfqs-load-60.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/* Starts 60 threads that each sleep for 10 seconds, then spin in
|
||||
a tight loop for 60 seconds, and sleep for another 60 seconds.
|
||||
Every 2 seconds after the initial sleep, the main thread
|
||||
prints the load average.
|
||||
|
||||
The expected output is this (some margin of error is allowed):
|
||||
|
||||
After 0 seconds, load average=1.00.
|
||||
After 2 seconds, load average=2.95.
|
||||
After 4 seconds, load average=4.84.
|
||||
After 6 seconds, load average=6.66.
|
||||
After 8 seconds, load average=8.42.
|
||||
After 10 seconds, load average=10.13.
|
||||
After 12 seconds, load average=11.78.
|
||||
After 14 seconds, load average=13.37.
|
||||
After 16 seconds, load average=14.91.
|
||||
After 18 seconds, load average=16.40.
|
||||
After 20 seconds, load average=17.84.
|
||||
After 22 seconds, load average=19.24.
|
||||
After 24 seconds, load average=20.58.
|
||||
After 26 seconds, load average=21.89.
|
||||
After 28 seconds, load average=23.15.
|
||||
After 30 seconds, load average=24.37.
|
||||
After 32 seconds, load average=25.54.
|
||||
After 34 seconds, load average=26.68.
|
||||
After 36 seconds, load average=27.78.
|
||||
After 38 seconds, load average=28.85.
|
||||
After 40 seconds, load average=29.88.
|
||||
After 42 seconds, load average=30.87.
|
||||
After 44 seconds, load average=31.84.
|
||||
After 46 seconds, load average=32.77.
|
||||
After 48 seconds, load average=33.67.
|
||||
After 50 seconds, load average=34.54.
|
||||
After 52 seconds, load average=35.38.
|
||||
After 54 seconds, load average=36.19.
|
||||
After 56 seconds, load average=36.98.
|
||||
After 58 seconds, load average=37.74.
|
||||
After 60 seconds, load average=37.48.
|
||||
After 62 seconds, load average=36.24.
|
||||
After 64 seconds, load average=35.04.
|
||||
After 66 seconds, load average=33.88.
|
||||
After 68 seconds, load average=32.76.
|
||||
After 70 seconds, load average=31.68.
|
||||
After 72 seconds, load average=30.63.
|
||||
After 74 seconds, load average=29.62.
|
||||
After 76 seconds, load average=28.64.
|
||||
After 78 seconds, load average=27.69.
|
||||
After 80 seconds, load average=26.78.
|
||||
After 82 seconds, load average=25.89.
|
||||
After 84 seconds, load average=25.04.
|
||||
After 86 seconds, load average=24.21.
|
||||
After 88 seconds, load average=23.41.
|
||||
After 90 seconds, load average=22.64.
|
||||
After 92 seconds, load average=21.89.
|
||||
After 94 seconds, load average=21.16.
|
||||
After 96 seconds, load average=20.46.
|
||||
After 98 seconds, load average=19.79.
|
||||
After 100 seconds, load average=19.13.
|
||||
After 102 seconds, load average=18.50.
|
||||
After 104 seconds, load average=17.89.
|
||||
After 106 seconds, load average=17.30.
|
||||
After 108 seconds, load average=16.73.
|
||||
After 110 seconds, load average=16.17.
|
||||
After 112 seconds, load average=15.64.
|
||||
After 114 seconds, load average=15.12.
|
||||
After 116 seconds, load average=14.62.
|
||||
After 118 seconds, load average=14.14.
|
||||
After 120 seconds, load average=13.67.
|
||||
After 122 seconds, load average=13.22.
|
||||
After 124 seconds, load average=12.78.
|
||||
After 126 seconds, load average=12.36.
|
||||
After 128 seconds, load average=11.95.
|
||||
After 130 seconds, load average=11.56.
|
||||
After 132 seconds, load average=11.17.
|
||||
After 134 seconds, load average=10.80.
|
||||
After 136 seconds, load average=10.45.
|
||||
After 138 seconds, load average=10.10.
|
||||
After 140 seconds, load average=9.77.
|
||||
After 142 seconds, load average=9.45.
|
||||
After 144 seconds, load average=9.13.
|
||||
After 146 seconds, load average=8.83.
|
||||
After 148 seconds, load average=8.54.
|
||||
After 150 seconds, load average=8.26.
|
||||
After 152 seconds, load average=7.98.
|
||||
After 154 seconds, load average=7.72.
|
||||
After 156 seconds, load average=7.47.
|
||||
After 158 seconds, load average=7.22.
|
||||
After 160 seconds, load average=6.98.
|
||||
After 162 seconds, load average=6.75.
|
||||
After 164 seconds, load average=6.53.
|
||||
After 166 seconds, load average=6.31.
|
||||
After 168 seconds, load average=6.10.
|
||||
After 170 seconds, load average=5.90.
|
||||
After 172 seconds, load average=5.70.
|
||||
After 174 seconds, load average=5.52.
|
||||
After 176 seconds, load average=5.33.
|
||||
After 178 seconds, load average=5.16.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/threads/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static int64_t start_time;
|
||||
|
||||
static void load_thread (void *aux);
|
||||
|
||||
#define THREAD_CNT 60
|
||||
|
||||
void
|
||||
test_mlfqs_load_60 (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
ASSERT (thread_mlfqs);
|
||||
|
||||
start_time = timer_ticks ();
|
||||
|
||||
/* align start_time to TIMER_FREQ to ensure sleeping threads wake up at the
|
||||
tick on which the load average is recalculated */
|
||||
start_time = ((start_time / TIMER_FREQ) + 1) * TIMER_FREQ;
|
||||
|
||||
msg ("Starting %d niced load threads...", THREAD_CNT);
|
||||
for (i = 0; i < THREAD_CNT; i++)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof name, "load %d", i);
|
||||
thread_create (name, PRI_DEFAULT, load_thread, NULL);
|
||||
}
|
||||
msg ("Starting threads took %d seconds.",
|
||||
timer_elapsed (start_time) / TIMER_FREQ);
|
||||
|
||||
for (i = 0; i < 90; i++)
|
||||
{
|
||||
int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
|
||||
int load_avg;
|
||||
timer_sleep (sleep_until - timer_ticks ());
|
||||
load_avg = thread_get_load_avg ();
|
||||
msg ("After %d seconds, load average=%d.%02d.",
|
||||
i * 2, load_avg / 100, load_avg % 100);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
load_thread (void *aux UNUSED)
|
||||
{
|
||||
int64_t sleep_time = 10 * TIMER_FREQ;
|
||||
int64_t spin_time = sleep_time + 60 * TIMER_FREQ;
|
||||
int64_t exit_time = spin_time + 60 * TIMER_FREQ;
|
||||
|
||||
thread_set_nice (20);
|
||||
timer_sleep (sleep_time - timer_elapsed (start_time));
|
||||
while (timer_elapsed (start_time) < spin_time)
|
||||
continue;
|
||||
timer_sleep (exit_time - timer_elapsed (start_time));
|
||||
}
|
||||
36
src/tests/threads/mlfqs-load-60.ck
Normal file
36
src/tests/threads/mlfqs-load-60.ck
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
use tests::threads::mlfqs;
|
||||
|
||||
our ($test);
|
||||
|
||||
my (@output) = read_text_file ("$test.output");
|
||||
common_checks ("run", @output);
|
||||
@output = get_core_output ("run", @output);
|
||||
|
||||
# Get actual values.
|
||||
local ($_);
|
||||
my (@actual);
|
||||
foreach (@output) {
|
||||
my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
|
||||
or next;
|
||||
$actual[$t] = $load_avg;
|
||||
}
|
||||
|
||||
# Calculate expected values.
|
||||
my ($load_avg) = 0;
|
||||
my ($recent) = 0;
|
||||
my (@expected);
|
||||
for (my ($t) = 0; $t < 180; $t++) {
|
||||
my ($ready) = $t < 60 ? 60 : 0;
|
||||
$load_avg = (59/60) * $load_avg + (1/60) * $ready;
|
||||
$expected[$t] = $load_avg;
|
||||
}
|
||||
|
||||
mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2],
|
||||
"Some load average values were missing or "
|
||||
. "differed from those expected "
|
||||
. "by more than 3.5.");
|
||||
pass;
|
||||
167
src/tests/threads/mlfqs-load-avg.c
Normal file
167
src/tests/threads/mlfqs-load-avg.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/* Starts 60 threads numbered 0 through 59. Thread #i sleeps for
|
||||
(10+i) seconds, then spins in a loop for 60 seconds, then
|
||||
sleeps until a total of 120 seconds have passed. Every 2
|
||||
seconds, starting 10 seconds in, the main thread prints the
|
||||
load average.
|
||||
|
||||
The expected output is listed below. Some margin of error is
|
||||
allowed.
|
||||
|
||||
If your implementation fails this test but passes most other
|
||||
tests, then consider whether you are doing too much work in
|
||||
the timer interrupt. If the timer interrupt handler takes too
|
||||
long, then the test's main thread will not have enough time to
|
||||
do its own work (printing a message) and go back to sleep
|
||||
before the next tick arrives. Then the main thread will be
|
||||
ready, instead of sleeping, when the tick arrives,
|
||||
artificially driving up the load average.
|
||||
|
||||
After 0 seconds, load average=0.00.
|
||||
After 2 seconds, load average=0.05.
|
||||
After 4 seconds, load average=0.16.
|
||||
After 6 seconds, load average=0.34.
|
||||
After 8 seconds, load average=0.58.
|
||||
After 10 seconds, load average=0.87.
|
||||
After 12 seconds, load average=1.22.
|
||||
After 14 seconds, load average=1.63.
|
||||
After 16 seconds, load average=2.09.
|
||||
After 18 seconds, load average=2.60.
|
||||
After 20 seconds, load average=3.16.
|
||||
After 22 seconds, load average=3.76.
|
||||
After 24 seconds, load average=4.42.
|
||||
After 26 seconds, load average=5.11.
|
||||
After 28 seconds, load average=5.85.
|
||||
After 30 seconds, load average=6.63.
|
||||
After 32 seconds, load average=7.46.
|
||||
After 34 seconds, load average=8.32.
|
||||
After 36 seconds, load average=9.22.
|
||||
After 38 seconds, load average=10.15.
|
||||
After 40 seconds, load average=11.12.
|
||||
After 42 seconds, load average=12.13.
|
||||
After 44 seconds, load average=13.16.
|
||||
After 46 seconds, load average=14.23.
|
||||
After 48 seconds, load average=15.33.
|
||||
After 50 seconds, load average=16.46.
|
||||
After 52 seconds, load average=17.62.
|
||||
After 54 seconds, load average=18.81.
|
||||
After 56 seconds, load average=20.02.
|
||||
After 58 seconds, load average=21.26.
|
||||
After 60 seconds, load average=22.52.
|
||||
After 62 seconds, load average=23.71.
|
||||
After 64 seconds, load average=24.80.
|
||||
After 66 seconds, load average=25.78.
|
||||
After 68 seconds, load average=26.66.
|
||||
After 70 seconds, load average=27.45.
|
||||
After 72 seconds, load average=28.14.
|
||||
After 74 seconds, load average=28.75.
|
||||
After 76 seconds, load average=29.27.
|
||||
After 78 seconds, load average=29.71.
|
||||
After 80 seconds, load average=30.06.
|
||||
After 82 seconds, load average=30.34.
|
||||
After 84 seconds, load average=30.55.
|
||||
After 86 seconds, load average=30.68.
|
||||
After 88 seconds, load average=30.74.
|
||||
After 90 seconds, load average=30.73.
|
||||
After 92 seconds, load average=30.66.
|
||||
After 94 seconds, load average=30.52.
|
||||
After 96 seconds, load average=30.32.
|
||||
After 98 seconds, load average=30.06.
|
||||
After 100 seconds, load average=29.74.
|
||||
After 102 seconds, load average=29.37.
|
||||
After 104 seconds, load average=28.95.
|
||||
After 106 seconds, load average=28.47.
|
||||
After 108 seconds, load average=27.94.
|
||||
After 110 seconds, load average=27.36.
|
||||
After 112 seconds, load average=26.74.
|
||||
After 114 seconds, load average=26.07.
|
||||
After 116 seconds, load average=25.36.
|
||||
After 118 seconds, load average=24.60.
|
||||
After 120 seconds, load average=23.81.
|
||||
After 122 seconds, load average=23.02.
|
||||
After 124 seconds, load average=22.26.
|
||||
After 126 seconds, load average=21.52.
|
||||
After 128 seconds, load average=20.81.
|
||||
After 130 seconds, load average=20.12.
|
||||
After 132 seconds, load average=19.46.
|
||||
After 134 seconds, load average=18.81.
|
||||
After 136 seconds, load average=18.19.
|
||||
After 138 seconds, load average=17.59.
|
||||
After 140 seconds, load average=17.01.
|
||||
After 142 seconds, load average=16.45.
|
||||
After 144 seconds, load average=15.90.
|
||||
After 146 seconds, load average=15.38.
|
||||
After 148 seconds, load average=14.87.
|
||||
After 150 seconds, load average=14.38.
|
||||
After 152 seconds, load average=13.90.
|
||||
After 154 seconds, load average=13.44.
|
||||
After 156 seconds, load average=13.00.
|
||||
After 158 seconds, load average=12.57.
|
||||
After 160 seconds, load average=12.15.
|
||||
After 162 seconds, load average=11.75.
|
||||
After 164 seconds, load average=11.36.
|
||||
After 166 seconds, load average=10.99.
|
||||
After 168 seconds, load average=10.62.
|
||||
After 170 seconds, load average=10.27.
|
||||
After 172 seconds, load average=9.93.
|
||||
After 174 seconds, load average=9.61.
|
||||
After 176 seconds, load average=9.29.
|
||||
After 178 seconds, load average=8.98.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/threads/tests.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
static int64_t start_time;
|
||||
|
||||
static void load_thread (void *seq_no);
|
||||
|
||||
#define THREAD_CNT 60
|
||||
|
||||
void
|
||||
test_mlfqs_load_avg (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
ASSERT (thread_mlfqs);
|
||||
|
||||
start_time = timer_ticks ();
|
||||
msg ("Starting %d load threads...", THREAD_CNT);
|
||||
for (i = 0; i < THREAD_CNT; i++)
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof name, "load %d", i);
|
||||
thread_create (name, PRI_DEFAULT, load_thread, (void *) i);
|
||||
}
|
||||
msg ("Starting threads took %d seconds.",
|
||||
timer_elapsed (start_time) / TIMER_FREQ);
|
||||
thread_set_nice (-20);
|
||||
|
||||
for (i = 0; i < 90; i++)
|
||||
{
|
||||
int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
|
||||
int load_avg;
|
||||
timer_sleep (sleep_until - timer_ticks ());
|
||||
load_avg = thread_get_load_avg ();
|
||||
msg ("After %d seconds, load average=%d.%02d.",
|
||||
i * 2, load_avg / 100, load_avg % 100);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
load_thread (void *seq_no_)
|
||||
{
|
||||
int seq_no = (int) seq_no_;
|
||||
int sleep_time = TIMER_FREQ * (10 + seq_no);
|
||||
int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT;
|
||||
int exit_time = TIMER_FREQ * (THREAD_CNT * 2);
|
||||
|
||||
timer_sleep (sleep_time - timer_elapsed (start_time));
|
||||
while (timer_elapsed (start_time) < spin_time)
|
||||
continue;
|
||||
timer_sleep (exit_time - timer_elapsed (start_time));
|
||||
}
|
||||
36
src/tests/threads/mlfqs-load-avg.ck
Normal file
36
src/tests/threads/mlfqs-load-avg.ck
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
use tests::threads::mlfqs;
|
||||
|
||||
our ($test);
|
||||
my (@output) = read_text_file ("$test.output");
|
||||
|
||||
common_checks ("run", @output);
|
||||
@output = get_core_output ("run", @output);
|
||||
|
||||
# Get actual values.
|
||||
local ($_);
|
||||
my (@actual);
|
||||
foreach (@output) {
|
||||
my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
|
||||
or next;
|
||||
$actual[$t] = $load_avg;
|
||||
}
|
||||
|
||||
# Calculate expected values.
|
||||
my ($load_avg) = 0;
|
||||
my ($recent) = 0;
|
||||
my (@expected);
|
||||
for (my ($t) = 0; $t < 180; $t++) {
|
||||
my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0;
|
||||
$load_avg = (59/60) * $load_avg + (1/60) * $ready;
|
||||
$expected[$t] = $load_avg;
|
||||
}
|
||||
|
||||
mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
|
||||
"Some load average values were missing or "
|
||||
. "differed from those expected "
|
||||
. "by more than 2.5.");
|
||||
pass;
|
||||
7
src/tests/threads/mlfqs-nice-10.ck
Normal file
7
src/tests/threads/mlfqs-nice-10.ck
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
use tests::threads::mlfqs;
|
||||
|
||||
check_mlfqs_fair ([0...9], 25);
|
||||
7
src/tests/threads/mlfqs-nice-2.ck
Normal file
7
src/tests/threads/mlfqs-nice-2.ck
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- perl -*-
|
||||
use strict;
|
||||
use warnings;
|
||||
use tests::tests;
|
||||
use tests::threads::mlfqs;
|
||||
|
||||
check_mlfqs_fair ([0, 5], 50);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user