# Automated test results

Generated by `npm test` (`node tests/run.js --md`) on 2026-10-21 with Node v26.7.0.

**Result: 96 / 96 passed** · 38 of them are edge cases · total 18.0 ms

| Group | Passed |
|---|---|
| Configuration & geometry | 20 / 20 |
| Address & trace parsing | 11 / 11 |
| Translation arithmetic | 12 / 12 |
| TLB replacement policies | 19 / 19 |
| Page faults & demand paging | 10 / 10 |
| Protection & dirty bits | 5 / 5 |
| Processes, ASIDs & context switches | 5 / 5 |
| Statistics & effective access time | 8 / 8 |
| Workload generators & utilities | 6 / 6 |

## Configuration & geometry

| # | Test case | Type | Result |
|---|---|---|---|
| 1 | default configuration is valid | Functional | PASS |
| 2 | default geometry: 8 offset bits, 8 VPN bits, 256 pages, 16 frames | Functional | PASS |
| 3 | rejects address widths outside 4–32 bits | Edge | PASS |
| 4 | rejects a page size that is not a power of two | Edge | PASS |
| 5 | rejects page size ≥ virtual space (no page-number bits left) | Edge | PASS |
| 6 | rejects page size larger than physical memory | Edge | PASS |
| 7 | rejects geometries with too many page-table entries | Edge | PASS |
| 8 | accepts the largest supported geometry (32-bit VA, 4 KB pages) | Edge | PASS |
| 9 | accepts the smallest geometry (4-bit VA/PA, 2-byte pages) | Edge | PASS |
| 10 | rejects TLB sizes of 0 and 65 | Edge | PASS |
| 11 | rejects associativity that does not divide the TLB size | Edge | PASS |
| 12 | rejects a non-power-of-two number of sets (6 entries / 2 ways = 3 sets) | Edge | PASS |
| 13 | allows a non-power-of-two fully associative TLB (3 entries) | Functional | PASS |
| 14 | set-associative geometry: 8 entries, 2-way → 4 sets, 2 index bits, 6 tag bits | Functional | PASS |
| 15 | splits 10 VPN bits over 3 page-table levels as [4,3,3] | Functional | PASS |
| 16 | rejects more page-table levels than VPN bits | Edge | PASS |
| 17 | rejects negative or non-finite timings | Edge | PASS |
| 18 | normalises lower-case policy names and numeric strings | Functional | PASS |
| 19 | rejects unknown policies, mappings and modes | Edge | PASS |
| 20 | MMU constructor throws RangeError on an invalid configuration | Edge | PASS |

## Address & trace parsing

| # | Test case | Type | Result |
|---|---|---|---|
| 21 | parses decimal, hex, binary and octal literals | Functional | PASS |
| 22 | accepts digit-group underscores (0x1_2C, 1_000) | Functional | PASS |
| 23 | rejects negative, fractional, empty and garbage input | Edge | PASS |
| 24 | rejects numbers beyond Number.MAX_SAFE_INTEGER | Edge | PASS |
| 25 | parses a mixed trace with processes, writes and comments | Functional | PASS |
| 26 | reports the exact token that is out of range | Edge | PASS |
| 27 | rejects a process id that does not exist | Edge | PASS |
| 28 | rejects an empty or comment-only trace | Edge | PASS |
| 29 | rejects malformed access modes like 300:X | Edge | PASS |
| 30 | caps traces at 20,000 accesses | Edge | PASS |
| 31 | formatOp round-trips through parseTrace | Functional | PASS |

## Translation arithmetic

| # | Test case | Type | Result |
|---|---|---|---|
| 32 | VA 300 (256-B pages): page 1, offset 44, PA 300; miss then hit | Functional | PASS |
| 33 | VA 0 maps to PA 0 (first byte of the first page) | Edge | PASS |
| 34 | last mapped byte 4095 → page 15, offset 255, PA 4095 | Edge | PASS |
| 35 | first unmapped byte 4096 → page fault, no frame, no PA | Edge | PASS |
| 36 | maximum address 65535 is accepted (page 255, offset 255) | Edge | PASS |
| 37 | out-of-range and non-integer addresses return an error and change nothing | Edge | PASS |
| 38 | rejects an access from a process that does not exist | Edge | PASS |
| 39 | sparse mapping: page 2 lives in frame 1, page 1 faults | Functional | PASS |
| 40 | property: offset preserved and PA < 2^paBits for 2,000 random addresses | Functional | PASS |
| 41 | 32-bit virtual address 0xFFFFFFFF splits correctly (no 32-bit overflow) | Edge | PASS |
| 42 | multi-level indices: VPN 0b1011_011_101 → L1=11, L2=3, L3=5 | Functional | PASS |
| 43 | set-associative decomposition: tag and index rebuild the VPN | Functional | PASS |

## TLB replacement policies

| # | Test case | Type | Result |
|---|---|---|---|
| 44 | FIFO evicts the oldest insertion even if it was just used | Functional | PASS |
| 45 | LRU evicts the least recently used entry | Functional | PASS |
| 46 | LFU evicts the least frequently used entry | Functional | PASS |
| 47 | CLOCK on Belady's string with 3 entries: 9 misses, hits at accesses 8, 9, 12 | Functional | PASS |
| 48 | CLOCK second chance: a re-referenced entry survives where FIFO evicts it | Functional | PASS |
| 49 | Belady's anomaly: FIFO gets 9 misses with 3 entries but 10 with 4 | Functional | PASS |
| 50 | compare() flags the FIFO anomaly and none for LRU/OPT | Functional | PASS |
| 51 | compare() isolates the TLB: a 4-frame demand-paging machine still shows the FIFO anomaly | Functional | PASS |
| 52 | LRU on Belady's string: 10 misses (3 entries), 8 misses (4 entries) | Functional | PASS |
| 53 | OPT on Belady's string: 7 misses (3 entries), 6 misses (4 entries) | Functional | PASS |
| 54 | OPT never misses more than FIFO/LRU/LFU/CLOCK/RANDOM (50 random traces) | Functional | PASS |
| 55 | LRU is a stack algorithm: misses never increase with TLB size | Functional | PASS |
| 56 | RANDOM replacement is reproducible for a given seed | Functional | PASS |
| 57 | OPT through access() without future knowledge is refused | Edge | PASS |
| 58 | 1-entry TLB: alternating pages always miss | Edge | PASS |
| 59 | cyclic loop over N+1 pages defeats LRU and FIFO (0 hits) but not OPT | Functional | PASS |
| 60 | direct-mapped TLB: pages 0 and 4 conflict in set 0 while page 1 stays cached | Functional | PASS |
| 61 | 2-way set-associative: each entry lands in set VPN mod sets | Functional | PASS |
| 62 | TLB never exceeds capacity and holds no duplicates (invariants, 500 accesses) | Functional | PASS |

## Page faults & demand paging

| # | Test case | Type | Result |
|---|---|---|---|
| 63 | report mode: fault produces no PA, no TLB fill, no memory access | Functional | PASS |
| 64 | demand mode: first touch faults into frame 0, repeat is a TLB hit | Functional | PASS |
| 65 | frames full → FIFO evicts the oldest page, invalidates its PTE and shoots down its TLB entry | Functional | PASS |
| 66 | frame LRU keeps the recently used page that FIFO would evict | Functional | PASS |
| 67 | frame CLOCK gives a referenced page a second chance (FIFO does not) | Functional | PASS |
| 68 | evicting a dirty page counts one write-back; clean pages none | Functional | PASS |
| 69 | a reloaded page starts clean (dirty bit cleared on page-in) | Edge | PASS |
| 70 | single-frame memory: every new page evicts the previous one | Edge | PASS |
| 71 | fault cost = t + k·m + S + m | Functional | PASS |
| 72 | invariants hold over 3,000 random multi-process demand-paging accesses | Functional | PASS |

## Protection & dirty bits

| # | Test case | Type | Result |
|---|---|---|---|
| 73 | write to a read-only page raises PROT with no PA and leaves it clean | Functional | PASS |
| 74 | reads from a read-only page still succeed | Functional | PASS |
| 75 | a protection fault is also raised on a TLB hit (cached permission bits) | Functional | PASS |
| 76 | changing permissions shoots down the stale TLB entry | Functional | PASS |
| 77 | a write sets the dirty bit in both the PTE and the TLB entry | Functional | PASS |

## Processes, ASIDs & context switches

| # | Test case | Type | Result |
|---|---|---|---|
| 78 | sequential mapping isolates processes: P0 and P1 page 0 get different frames | Functional | PASS |
| 79 | ASID mode: entries survive a context switch (switch back → HIT) | Functional | PASS |
| 80 | flush mode: every switch empties the TLB (switch back → MISS) | Functional | PASS |
| 81 | same VPN in two processes never hits the other process’s entry | Functional | PASS |
| 82 | no context switch is counted when the same process keeps running | Edge | PASS |

## Statistics & effective access time

| # | Test case | Type | Result |
|---|---|---|---|
| 83 | textbook EAT: h=0.8, t=10, m=100 → 130 ns | Functional | PASS |
| 84 | two-level paging with faults: h=.95, t=20, m=80, k=2, p=.1, S=5000 → 608 ns | Functional | PASS |
| 85 | EAT boundaries: h=1 → t+m, h=0 → t+2m | Edge | PASS |
| 86 | EAT rejects h outside [0,1], negative times and non-numbers | Edge | PASS |
| 87 | measured EAT of a run equals the analytic formula at the observed hit ratio | Functional | PASS |
| 88 | all ratios are 0 (not NaN) before any access | Edge | PASS |
| 89 | hits + misses = accesses and walks = misses on a mixed run | Functional | PASS |
| 90 | k-level walk reads k page-table entries per miss | Functional | PASS |

## Workload generators & utilities

| # | Test case | Type | Result |
|---|---|---|---|
| 91 | every generator yields in-range, parseable accesses (default and tiny geometry) | Edge | PASS |
| 92 | generators are deterministic for a given seed | Functional | PASS |
| 93 | nextUseIndex points at the next access to the same page | Functional | PASS |
| 94 | binary/hex helpers pad to the field width | Functional | PASS |
| 95 | simulate() throws with the failing access index on bad input | Edge | PASS |
| 96 | random initial mapping is reproducible and internally consistent | Functional | PASS |
