diff options
| author | Ethan Werner <mail@ethanwerner.com> | 2026-09-19 06:09:54 +0000 |
|---|---|---|
| committer | Ethan Werner <mail@ethanwerner.com> | 2026-09-19 06:09:54 +0000 |
| commit | bf19b9938508f70011395f4dc283ca758ec6a8d3 (patch) | |
| tree | 46dc4c442ed8d8d43040433c6a4794e76f8e618a /tests/test_pushdown.c | |
| parent | 96ecf29e8be977f51f351308ef2c7c492bb829aa (diff) | |
Diffstat (limited to 'tests/test_pushdown.c')
| -rw-r--r-- | tests/test_pushdown.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/tests/test_pushdown.c b/tests/test_pushdown.c new file mode 100644 index 0000000..7e2e18a --- /dev/null +++ b/tests/test_pushdown.c @@ -0,0 +1,442 @@ +// Tests for pushdown.h - pushdown automaton instruction execution +#include <stdio.h> +typedef double stack_value_t; +#define STACK_IMPLEMENTATION +#include "../include/stack.h" + +typedef double pushdown_value_t; +#define PUSHDOWN_IMPLEMENTATION +#include "../include/pushdown.h" + +#include <math.h> + +#define EPS 1e-9 + +#define ASSERT(cond, msg) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "FAIL: %s (test_pushdown.c:%d)\n", msg, __LINE__); \ + failures++; \ + } \ + } while (0) + +#define ASSERT_FLOAT_EQ(a, b, msg) \ + do { \ + if (fabs((a) - (b)) > EPS) { \ + fprintf(stderr, "FAIL: %s (got %f, expected %f) (test_pushdown.c:%d)\n", \ + msg, (double)(a), (double)(b), __LINE__); \ + failures++; \ + } \ + } while (0) + +static int failures = 0; + +void setup_stacks(stack_t *input, stack_t *output) { + *input = (stack_t){.data = NULL, .size = 0, .capacity = 16}; + *output = (stack_t){.data = NULL, .size = 0, .capacity = 16}; + stack_init(input); + stack_init(output); +} + +void teardown_stacks(stack_t *input, stack_t *output) { + stack_free(input); + stack_free(output); +} + +void test_push() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 5); + pushdown_instruction_t ops[] = {PUSH, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "push size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 5, "push value"); + + teardown_stacks(&input, &output); +} + +void test_pop() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&output, 5); + pushdown_instruction_t ops[] = {POP, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 0, "pop size"); + + teardown_stacks(&input, &output); +} + +void test_skip() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 5); + stack_push(&input, 10); + pushdown_instruction_t ops[] = {SKIP, PUSH, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "skip+push size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 5, "skip top(10), push next(5)"); + + teardown_stacks(&input, &output); +} + +void test_add() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 3); + stack_push(&input, 4); + pushdown_instruction_t ops[] = {PUSH, PUSH, ADD, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "add size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 7, "add value"); + + teardown_stacks(&input, &output); +} + +void test_sub() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 10); + stack_push(&input, 3); + pushdown_instruction_t ops[] = {PUSH, PUSH, SUB, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "sub size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 7, "sub value"); + + teardown_stacks(&input, &output); +} + +void test_mul() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 3); + stack_push(&input, 4); + pushdown_instruction_t ops[] = {PUSH, PUSH, MUL, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "mul size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 12, "mul value"); + + teardown_stacks(&input, &output); +} + +void test_div() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 10); + stack_push(&input, 2); + pushdown_instruction_t ops[] = {PUSH, PUSH, DIV, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "div size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 5, "div value"); + + teardown_stacks(&input, &output); +} + +void test_div_zero() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 0); + stack_push(&input, 10); + pushdown_instruction_t ops[] = {PUSH, PUSH, DIV, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "div zero size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 0, "div zero result is 0"); + + teardown_stacks(&input, &output); +} + +void test_recip() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 4); + pushdown_instruction_t ops[] = {PUSH, RECIP, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "recip size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 0.25, "recip value"); + + teardown_stacks(&input, &output); +} + +void test_inc() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 5); + pushdown_instruction_t ops[] = {PUSH, INC, END}; + pushdown_run(&output, &input, ops); + + ASSERT_FLOAT_EQ(stack_peek(&output), 6, "inc value"); + + teardown_stacks(&input, &output); +} + +void test_dec() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 5); + pushdown_instruction_t ops[] = {PUSH, DEC, END}; + pushdown_run(&output, &input, ops); + + ASSERT_FLOAT_EQ(stack_peek(&output), 4, "dec value"); + + teardown_stacks(&input, &output); +} + +void test_sign() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, -5); + pushdown_instruction_t ops[] = {PUSH, SIGN, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), -1, "sign negative"); + teardown_stacks(&input, &output); + + setup_stacks(&input, &output); + stack_push(&input, 5); + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 1, "sign positive"); + teardown_stacks(&input, &output); + + setup_stacks(&input, &output); + stack_push(&input, 0); + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 0, "sign zero"); + teardown_stacks(&input, &output); +} + +void test_neg() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 5); + pushdown_instruction_t ops[] = {PUSH, NEG, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), -5, "neg value"); + + teardown_stacks(&input, &output); +} + +void test_sin() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 0); + pushdown_instruction_t ops[] = {PUSH, SIN, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 0, "sin(0)"); + + teardown_stacks(&input, &output); +} + +void test_sqrt() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 9); + pushdown_instruction_t ops[] = {PUSH, SQRT, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 3, "sqrt(9)"); + + teardown_stacks(&input, &output); +} + +void test_abs() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, -5); + pushdown_instruction_t ops[] = {PUSH, ABS, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 5, "abs(-5)"); + + teardown_stacks(&input, &output); +} + +void test_floor() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 3.7); + pushdown_instruction_t ops[] = {PUSH, FLOOR, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 3, "floor(3.7)"); + + teardown_stacks(&input, &output); +} + +void test_ceil() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 3.2); + pushdown_instruction_t ops[] = {PUSH, CEIL, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 4, "ceil(3.2)"); + + teardown_stacks(&input, &output); +} + +void test_copy() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 42); + pushdown_instruction_t ops[] = {PUSH, COPY, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 2, "copy size"); + + teardown_stacks(&input, &output); +} + +void test_noop() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 5); + pushdown_instruction_t ops[] = {NOOP, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 0, "noop no change"); + ASSERT(input.size == 1, "noop input unchanged"); + + teardown_stacks(&input, &output); +} + +void test_sqrt_negative() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, -1); + pushdown_instruction_t ops[] = {PUSH, SQRT, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "sqrt negative pushes 0"); + ASSERT_FLOAT_EQ(stack_peek(&output), 0, "sqrt negative result is 0"); + + teardown_stacks(&input, &output); +} + +void test_pow() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 2); + stack_push(&input, 3); + pushdown_instruction_t ops[] = {PUSH, PUSH, POW, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 1, "pow size"); + ASSERT_FLOAT_EQ(stack_peek(&output), 8, "pow value 2^3"); + + teardown_stacks(&input, &output); +} + +void test_log() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 1); + pushdown_instruction_t ops[] = {PUSH, LOG, END}; + pushdown_run(&output, &input, ops); + ASSERT_FLOAT_EQ(stack_peek(&output), 0, "log(1)"); + + teardown_stacks(&input, &output); +} + +void test_complex_program() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 3); + stack_push(&input, 4); + pushdown_instruction_t ops[] = {PUSH, PUSH, MUL, INC, END}; + pushdown_run(&output, &input, ops); + + ASSERT_FLOAT_EQ(stack_peek(&output), 13, "3*4+1"); + + teardown_stacks(&input, &output); +} + +void test_minmax() { + stack_t input, output; + setup_stacks(&input, &output); + + stack_push(&input, 3); + stack_push(&input, 7); + pushdown_instruction_t ops[] = {PUSH, PUSH, MINMAX, END}; + pushdown_run(&output, &input, ops); + + ASSERT(output.size == 2, "minmax size is 2"); + ASSERT_FLOAT_EQ(output.data[0], 3, "minmax min value"); + ASSERT_FLOAT_EQ(output.data[1], 7, "minmax max value"); + + teardown_stacks(&input, &output); +} + +void test_validate_minmax() { + pushdown_instruction_t valid[] = {PUSH, MINMAX, END}; + ASSERT(pushdown_validate(valid), "validate accepts MINMAX"); + + pushdown_instruction_t invalid[] = {PUSH, (pushdown_instruction_t)999, END}; + ASSERT(!pushdown_validate(invalid), "validate rejects out-of-range"); +} + +void test_instruction_count_bounds() { + pushdown_instruction_t ops[] = {PUSH, ADD, (pushdown_instruction_t)999, END}; + ASSERT(pushdown_instruction_count(ops) == 2, "stops at invalid instruction"); + + ASSERT(pushdown_instruction_count(NULL) == 0, "NULL returns 0"); +} + +int main() { + test_push(); + test_pop(); + test_skip(); + test_add(); + test_sub(); + test_mul(); + test_div(); + test_div_zero(); + test_recip(); + test_inc(); + test_dec(); + test_sign(); + test_neg(); + test_sin(); + test_sqrt(); + test_abs(); + test_floor(); + test_ceil(); + test_copy(); + test_noop(); + test_sqrt_negative(); + test_pow(); + test_log(); + test_complex_program(); + test_minmax(); + test_validate_minmax(); + test_instruction_count_bounds(); + + printf("test_pushdown: %d failures\n", failures); + return failures; +} |