// Tests for stack.h - stack push, pop, peek, and resizing operations #include #include typedef double stack_value_t; #define STACK_IMPLEMENTATION #include "../include/stack.h" #define EPS 1e-9 #define ASSERT(cond, msg) \ do { \ if (!(cond)) { \ fprintf(stderr, "FAIL: %s (test_stack.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_stack.c:%d)\n", \ msg, (double)(a), (double)(b), __LINE__); \ failures++; \ } \ } while (0) static int failures = 0; void test_init_and_free() { stack_t s = {.capacity = 10}; stack_init(&s); ASSERT(s.size == 0, "init size should be 0"); ASSERT(s.capacity == 10, "init capacity should be 10"); ASSERT(s.data != NULL, "init data should not be NULL"); stack_free(&s); } void test_default_capacity() { stack_t s = {0}; stack_init(&s); ASSERT(s.capacity == 16, "default capacity should be 16"); stack_free(&s); } void test_push_pop() { stack_t s = {0}; stack_init(&s); for (int i = 0; i < 5; i++) { stack_push(&s, (stack_value_t)(i * 10)); } ASSERT(s.size == 5, "size after 5 pushes"); ASSERT_FLOAT_EQ(stack_pop(&s), 40, "pop last pushed"); ASSERT_FLOAT_EQ(stack_pop(&s), 30, "pop second last"); ASSERT(s.size == 3, "size after 2 pops"); stack_free(&s); } void test_peek() { stack_t s = {0}; stack_init(&s); stack_push(&s, 42); ASSERT_FLOAT_EQ(stack_peek(&s), 42, "peek returns top"); ASSERT(s.size == 1, "peek does not change size"); stack_free(&s); } void test_peek_empty() { stack_t s = {0}; stack_init(&s); ASSERT_FLOAT_EQ(stack_peek(&s), 0, "peek empty returns ZERO"); stack_free(&s); } void test_pop_empty() { stack_t s = {0}; stack_init(&s); ASSERT_FLOAT_EQ(stack_pop(&s), 0, "pop empty returns ZERO"); stack_free(&s); } void test_grow() { stack_t s = {.capacity = 2}; stack_init(&s); for (int i = 0; i < 10; i++) { stack_push(&s, (stack_value_t)i); } ASSERT(s.size == 10, "size after growing"); ASSERT(s.capacity >= 10, "capacity grew"); for (int i = 9; i >= 0; i--) { ASSERT_FLOAT_EQ(stack_pop(&s), (stack_value_t)i, "values preserved after grow"); } stack_free(&s); } void test_clear() { stack_t s = {0}; stack_init(&s); stack_push(&s, 1); stack_push(&s, 2); stack_push(&s, 3); stack_clear(&s); ASSERT_FLOAT_EQ(s.data[0], 0, "clear zeroed data[0]"); ASSERT_FLOAT_EQ(s.data[1], 0, "clear zeroed data[1]"); ASSERT_FLOAT_EQ(s.data[2], 0, "clear zeroed data[2]"); stack_free(&s); } void test_from_array() { stack_value_t arr[] = {1, 2, 3, 4, 5}; stack_t s = {0}; stack_from_array(&s, arr, 5); ASSERT(s.size == 5, "from_array size"); ASSERT(s.capacity == 5, "from_array capacity"); for (int i = 4; i >= 0; i--) { ASSERT_FLOAT_EQ(stack_pop(&s), (stack_value_t)(i + 1), "from_array values"); } stack_free(&s); } void test_empty() { stack_t s = {0}; stack_init(&s); ASSERT(stack_empty(&s) == 1, "empty stack returns true"); stack_push(&s, 42); ASSERT(stack_empty(&s) == 0, "non-empty stack returns false"); stack_free(&s); } void test_from_array_single() { stack_value_t arr[] = {42}; stack_t s = {0}; stack_from_array(&s, arr, 1); ASSERT(s.size == 1, "from_array single size"); ASSERT(s.capacity == 1, "from_array single capacity"); ASSERT_FLOAT_EQ(stack_pop(&s), 42, "from_array single value"); stack_free(&s); } void test_multiple_free() { stack_t s = {0}; stack_init(&s); stack_free(&s); ASSERT(s.data == NULL, "data is NULL after free"); ASSERT(s.size == 0, "size is 0 after free"); ASSERT(s.capacity == 0, "capacity is 0 after free"); stack_free(&s); ASSERT(s.data == NULL, "second free is safe"); } void test_pop_ok() { stack_t s = {0}; stack_init(&s); stack_value_t out = 0; ASSERT(!stack_pop_ok(&s, &out), "pop_ok on empty returns 0"); stack_push(&s, 42); ASSERT(stack_pop_ok(&s, &out), "pop_ok on non-empty returns 1"); ASSERT_FLOAT_EQ(out, 42, "pop_ok value"); ASSERT(s.size == 0, "pop_ok decrements size"); stack_free(&s); } void test_peek_ok() { stack_t s = {0}; stack_init(&s); stack_value_t out = 0; ASSERT(!stack_peek_ok(&s, &out), "peek_ok on empty returns 0"); stack_push(&s, 99); ASSERT(stack_peek_ok(&s, &out), "peek_ok on non-empty returns 1"); ASSERT_FLOAT_EQ(out, 99, "peek_ok value"); ASSERT(s.size == 1, "peek_ok does not change size"); stack_free(&s); } void test_pop_ok_null_out() { stack_t s = {0}; stack_init(&s); stack_push(&s, 1); ASSERT(!stack_pop_ok(&s, NULL), "pop_ok with NULL out returns 0"); stack_free(&s); } void test_peek_ok_null_out() { stack_t s = {0}; stack_init(&s); stack_push(&s, 1); ASSERT(!stack_peek_ok(&s, NULL), "peek_ok with NULL out returns 0"); stack_free(&s); } int main() { test_init_and_free(); test_default_capacity(); test_push_pop(); test_peek(); test_peek_empty(); test_pop_empty(); test_pop_ok(); test_peek_ok(); test_pop_ok_null_out(); test_peek_ok_null_out(); test_grow(); test_clear(); test_from_array(); test_from_array_single(); test_empty(); test_multiple_free(); printf("test_stack: %d failures\n", failures); return failures; }