1#include "vm.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7static void require(int condition, const char *message) {
8 if (!condition) {
9 fprintf(stderr, "FAIL: %s\n", message);
10 exit(1);
11 }
12}
13
14static void run_example(const char *path, const char *expected) {
15 Assembler assembler;
16 Program program;
17 VM vm;
18 VMStatus status;
19
20 assembler_init(&assembler);
21 require(assembler_assemble_file(&assembler, path, &program), assembler.error);
22 vm_init(&vm);
23 status = vm_run(&vm, &program);
24 if (status != VM_OK) {
25 fprintf(stderr, "%s: %s (%s)\n", path, vm_status_name(status), vm.error);
26 }
27 require(status == VM_OK, "example VM run failed");
28 require(strcmp(vm.output, expected) == 0, "example output mismatch");
29}
30
31static VMStatus run_program(const Program *program) {
32 VM vm;
33 vm_init(&vm);
34 return vm_run(&vm, program);
35}
36
37static void expect_status(const Program *program, VMStatus expected, const char *name) {
38 VMStatus actual = run_program(program);
39 if (actual != expected) {
40 fprintf(stderr, "%s: expected %s, got %s\n", name, vm_status_name(expected), vm_status_name(actual));
41 }
42 require(actual == expected, name);
43}
44
45static void expect_asm_fail(const char *source, const char *name) {
46 Assembler assembler;
47 Program program;
48 assembler_init(&assembler);
49 require(!assembler_assemble_string(&assembler, source, &program), name);
50}
51
52static void negative_vm_tests(void) {
53 Program underflow = {{OP_POP, OP_HALT}, 2};
54 Program call_underflow = {{OP_RET, OP_HALT}, 2};
55 Program div_zero = {{OP_PUSH, 1, OP_PUSH, 0, OP_DIV, OP_HALT}, 6};
56 Program bad_opcode = {{999, OP_HALT}, 2};
57 Program bad_jump = {{OP_JMP, 999, OP_HALT}, 3};
58 Program bad_local = {{OP_LOAD, 16, OP_HALT}, 3};
59 Program no_halt = {{OP_PUSH, 1}, 2};
60 Program output_overflow = {{OP_PUSH, 123456789, OP_PRINT, OP_JMP, 0}, 5};
61 Program call_overflow = {{OP_CALL, 0, OP_HALT}, 3};
62 Program stack_overflow;
63 size_t i;
64
65 stack_overflow.count = 0;
66 for (i = 0; i < 1025; i++) {
67 stack_overflow.code[stack_overflow.count++] = OP_PUSH;
68 stack_overflow.code[stack_overflow.count++] = (int)i;
69 }
70 stack_overflow.code[stack_overflow.count++] = OP_HALT;
71
72 expect_status(&underflow, VM_ERR_STACK_UNDERFLOW, "stack underflow");
73 expect_status(&stack_overflow, VM_ERR_STACK_OVERFLOW, "stack overflow");
74 expect_status(&call_overflow, VM_ERR_CALL_STACK_OVERFLOW, "call stack overflow");
75 expect_status(&call_underflow, VM_ERR_CALL_STACK_UNDERFLOW, "call stack underflow");
76 expect_status(&div_zero, VM_ERR_DIV_ZERO, "division by zero");
77 expect_status(&bad_opcode, VM_ERR_BAD_OPCODE, "bad opcode");
78 expect_status(&bad_jump, VM_ERR_BAD_JUMP, "bad jump");
79 expect_status(&bad_local, VM_ERR_BAD_LOCAL, "bad local");
80 expect_status(&no_halt, VM_ERR_NO_HALT, "no halt");
81 expect_status(&output_overflow, VM_ERR_OUTPUT_OVERFLOW, "output overflow");
82}
83
84static void negative_assembler_tests(void) {
85 expect_asm_fail("NOPE\n", "unknown mnemonic");
86 expect_asm_fail("PUSH\n", "missing operand");
87 expect_asm_fail("POP 1\n", "extra operand");
88 expect_asm_fail("bad-label:\n", "invalid label");
89 expect_asm_fail("x:\nx:\n", "duplicate label");
90 expect_asm_fail("JMP missing\n", "unknown label operand");
91 expect_asm_fail("LOAD 16\n", "bad local operand");
92}
93
94int main(void) {
95 run_example("examples/fib.vasm", "6765\n");
96 run_example("examples/fact.vasm", "3628800\n");
97 run_example("examples/primes.vasm", "2\n3\n5\n7\n11\n13\n17\n19\n23\n29\n31\n37\n41\n43\n47\n");
98 negative_vm_tests();
99 negative_assembler_tests();
100 puts("all tests passed");
101 return 0;
102}
103
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.