diff options
Diffstat (limited to 'lab04/tests/test.c')
| -rw-r--r-- | lab04/tests/test.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/lab04/tests/test.c b/lab04/tests/test.c new file mode 100644 index 0000000..c10b130 --- /dev/null +++ b/lab04/tests/test.c @@ -0,0 +1,497 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#include "container_of.h" +#include "expression.h" +#include "list.h" +#include "polynomial.h" +#include "vec.h" + +#include <math.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#define CHECK(condition) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, " failed: %s:%d: %s\n", \ + __FILE__, __LINE__, #condition); \ + return false; \ + } \ + } while (0) + +static int compare_int(const void *lhs, const void *rhs) +{ + int left = *(const int *)lhs; + int right = *(const int *)rhs; + return (left > right) - (left < right); +} + +static bool test_vec_lifecycle_and_access(void) +{ + struct vec vector; + vec_init(&vector, int); + CHECK(vec_empty(&vector)); + CHECK(vec_elem_size(&vector) == sizeof(int)); + + int values[] = { 10, 20, 30 }; + for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { + CHECK(vec_push_back(&vector, &values[i])); + } + CHECK(vec_size(&vector) == 3); + CHECK(vec_capacity(&vector) >= 3); + CHECK(*(int *)vec_front(&vector) == 10); + CHECK(*(int *)vec_back(&vector) == 30); + CHECK(*(int *)vec_at_checked(&vector, 1) == 20); + CHECK(vec_at_checked(&vector, 3) == NULL); + + vec_destroy(&vector); + CHECK(vector.data == NULL); + CHECK(vector.size == 0); + CHECK(vector.capacity == 0); + CHECK(vector.elem_size == 0); + return true; +} + +static bool test_vec_insert_erase_and_assign(void) +{ + struct vec vector; + vec_init(&vector, int); + int initial[] = { 1, 3 }; + CHECK(vec_append(&vector, initial, 2)); + + int two = 2; + CHECK(vec_insert(&vector, 1, &two)); + int tail[] = { 4, 5 }; + CHECK(vec_insert_range(&vector, vector.size, tail, 2)); + CHECK(vector.size == 5); + CHECK(*(int *)vec_at(&vector, 0) == 1); + CHECK(*(int *)vec_at(&vector, 4) == 5); + + vec_erase(&vector, 1); + CHECK(*(int *)vec_at(&vector, 1) == 3); + vec_erase_n(&vector, 1, 2); + CHECK(vector.size == 2); + CHECK(*(int *)vec_at(&vector, 0) == 1); + CHECK(*(int *)vec_at(&vector, 1) == 5); + + int replacement[] = { 7, 8, 9 }; + CHECK(vec_assign_range(&vector, replacement, 3)); + CHECK(vector.size == 3); + int repeated = 4; + CHECK(vec_assign_n(&vector, 2, &repeated)); + CHECK(vector.size == 2); + CHECK(*(int *)vec_at(&vector, 0) == 4); + CHECK(*(int *)vec_at(&vector, 1) == 4); + + vec_destroy(&vector); + return true; +} + +static bool test_vec_resize_capacity_and_swap(void) +{ + struct vec vector; + vec_init(&vector, int); + CHECK(vec_reserve(&vector, 16)); + CHECK(vector.capacity >= 16); + CHECK(vec_resize_zero(&vector, 4)); + for (size_t i = 0; i < vector.size; ++i) { + CHECK(*(int *)vec_at(&vector, i) == 0); + } + + int value = 11; + CHECK(vec_assign_n(&vector, 1, &value)); + CHECK(vec_resize(&vector, 4)); + CHECK(*(int *)vec_at(&vector, 0) == 11); + CHECK(vec_resize(&vector, 1)); + CHECK(vec_shrink_to_fit(&vector)); + CHECK(vector.capacity == vector.size); + + struct vec other; + vec_init(&other, int); + int other_value = 22; + CHECK(vec_push_back(&other, &other_value)); + vec_swap(&vector, &other); + CHECK(*(int *)vec_front(&vector) == 22); + CHECK(*(int *)vec_front(&other) == 11); + CHECK(!vec_reserve(&vector, SIZE_MAX)); + + vec_destroy(&vector); + vec_destroy(&other); + return true; +} + +static bool test_vec_iterators_and_comparison(void) +{ + struct vec vector; + vec_init(&vector, int); + int values[] = { 1, 2, 3 }; + CHECK(vec_append(&vector, values, 3)); + + int *begin = vec_begin_as(&vector, int); + int *end = vec_end_as(&vector, int); + CHECK(vec_iter_distance(begin, end) == 3); + CHECK(vec_iter_distance((int *)NULL, (int *)NULL) == 0); + CHECK(*begin == 1); + CHECK(*(end - 1) == 3); + + struct vec_reverse_iterator reverse = vec_rbegin(&vector); + struct vec_reverse_iterator reverse_end = vec_rend(&vector); + CHECK(vec_reverse_distance(reverse, reverse_end) == 3); + CHECK(*(int *)vec_reverse_deref(reverse) == 3); + vec_reverse_next(&reverse); + CHECK(*(int *)vec_reverse_deref(reverse) == 2); + vec_reverse_next(&reverse); + CHECK(*(int *)vec_reverse_deref(reverse) == 1); + + struct vec same; + vec_init(&same, int); + CHECK(vec_append(&same, values, 3)); + CHECK(vec_equal(&vector, &same, compare_int)); + CHECK(vec_compare(&vector, &same, compare_int) == 0); + vec_pop_back(&same); + CHECK(vec_compare(&same, &vector, compare_int) < 0); + + vec_destroy(&vector); + vec_destroy(&same); + return true; +} + +struct list_test_item { + int value; + struct list_head link; +}; + +static bool test_list_and_container_of(void) +{ + LIST_HEAD(head); + struct list_test_item first = { .value = 1 }; + struct list_test_item second = { .value = 2 }; + struct list_test_item third = { .value = 3 }; + INIT_LIST_HEAD(&first.link); + INIT_LIST_HEAD(&second.link); + INIT_LIST_HEAD(&third.link); + + list_add(&first.link, &head); + list_add_tail(&second.link, &head); + list_add_tail(&third.link, &head); + CHECK(!list_empty(&head)); + CHECK(list_is_first(&first.link, &head)); + CHECK(list_is_last(&third.link, &head)); + CHECK(list_entry(head.next, struct list_test_item, link)->value == 1); + CHECK(list_entry(head.prev, struct list_test_item, link)->value == 3); + + struct list_test_item *cursor; + int expected = 1; + list_for_each_entry(cursor, &head, link) { + CHECK(cursor->value == expected); + ++expected; + } + list_move_tail(&first.link, &head); + CHECK(list_entry(head.prev, struct list_test_item, link)->value == 1); + list_del(&second.link); + CHECK(list_entry(head.next, struct list_test_item, link)->value == 3); + CHECK(list_entry_is_head(list_entry(head.next, struct list_test_item, link), + &head, link) == false); + return true; +} + +static bool test_poly_vec_normalization(void) +{ + struct poly_vec poly; + poly_vec_init(&poly); + CHECK(poly_vec_add_term(&poly, 3.0, 2)); + CHECK(poly_vec_add_term(&poly, 1.0, 5)); + CHECK(poly_vec_add_term(&poly, 2.0, 2)); + CHECK(poly_vec_size(&poly) == 2); + CHECK(poly_vec_at_const(&poly, 0)->exp == 5); + CHECK(poly_vec_at_const(&poly, 0)->coef == 1.0); + CHECK(poly_vec_at_const(&poly, 1)->exp == 2); + CHECK(poly_vec_at_const(&poly, 1)->coef == 5.0); + CHECK(poly_vec_set_term(&poly, 0.0, 5)); + CHECK(!poly_vec_get_term(&poly, 5, &(double){ 0.0 })); + CHECK(poly_vec_remove_term(&poly, 2)); + CHECK(poly_vec_empty(&poly)); + poly_vec_destroy(&poly); + return true; +} + +static bool test_poly_vec_add_sub_and_alias(void) +{ + struct poly_vec a; + struct poly_vec b; + struct poly_vec result; + poly_vec_init(&a); + poly_vec_init(&b); + poly_vec_init(&result); + CHECK(poly_vec_add_term(&a, 3.0, 2)); + CHECK(poly_vec_add_term(&a, 2.0, 1)); + CHECK(poly_vec_add_term(&a, 1.0, 0)); + CHECK(poly_vec_add_term(&b, 1.0, 2)); + CHECK(poly_vec_add_term(&b, -2.0, 1)); + CHECK(poly_vec_add_term(&b, 4.0, 0)); + + CHECK(poly_vec_add(&result, &a, &b)); + CHECK(poly_vec_equal(&result, &b, 0.0) == false); + CHECK(poly_vec_get_term(&result, 2, &(double){ 0.0 })); + CHECK(result.terms.size == 2); + CHECK(poly_vec_at_const(&result, 0)->coef == 4.0); + CHECK(poly_vec_at_const(&result, 1)->exp == 0); + CHECK(poly_vec_at_const(&result, 1)->coef == 5.0); + + CHECK(poly_vec_sub(&result, &a, &b)); + CHECK(poly_vec_at_const(&result, 0)->coef == 2.0); + CHECK(poly_vec_at_const(&result, 1)->coef == 4.0); + CHECK(poly_vec_at_const(&result, 2)->coef == -3.0); + + CHECK(poly_vec_add(&a, &a, &b)); + CHECK(a.terms.size == 2); + CHECK(poly_vec_at_const(&a, 0)->coef == 4.0); + CHECK(poly_vec_at_const(&a, 1)->coef == 5.0); + + poly_vec_destroy(&a); + poly_vec_destroy(&b); + poly_vec_destroy(&result); + return true; +} + +static bool test_poly_vec_mul_derivative_and_eval(void) +{ + struct poly_vec x_plus_one; + struct poly_vec x_minus_one; + struct poly_vec result; + struct poly_vec derivative; + poly_vec_init(&x_plus_one); + poly_vec_init(&x_minus_one); + poly_vec_init(&result); + poly_vec_init(&derivative); + CHECK(poly_vec_add_term(&x_plus_one, 1.0, 1)); + CHECK(poly_vec_add_term(&x_plus_one, 1.0, 0)); + CHECK(poly_vec_add_term(&x_minus_one, 1.0, 1)); + CHECK(poly_vec_add_term(&x_minus_one, -1.0, 0)); + CHECK(poly_vec_mul(&result, &x_plus_one, &x_minus_one)); + CHECK(result.terms.size == 2); + CHECK(poly_vec_at_const(&result, 0)->exp == 2); + CHECK(poly_vec_at_const(&result, 0)->coef == 1.0); + CHECK(poly_vec_at_const(&result, 1)->exp == 0); + CHECK(poly_vec_at_const(&result, 1)->coef == -1.0); + CHECK(poly_vec_derivative(&derivative, &result, 1)); + CHECK(derivative.terms.size == 1); + CHECK(poly_vec_at_const(&derivative, 0)->coef == 2.0); + CHECK(poly_vec_eval(&result, 3.0) == 8.0); + + poly_vec_destroy(&x_plus_one); + poly_vec_destroy(&x_minus_one); + poly_vec_destroy(&result); + poly_vec_destroy(&derivative); + return true; +} + +static bool test_poly_list_normalization_and_conversion(void) +{ + struct poly_list list; + struct poly_vec vector; + struct poly_list copy; + poly_list_init(&list); + poly_vec_init(&vector); + poly_list_init(©); + CHECK(poly_list_add_term(&list, 2.0, 1)); + CHECK(poly_list_add_term(&list, 5.0, 4)); + CHECK(poly_list_add_term(&list, 3.0, 1)); + CHECK(poly_list_size(&list) == 2); + CHECK(poly_list_get_term(&list, 1, &(double){ 0.0 })); + CHECK(list_entry(list.head.next, struct poly_node, link)->term.exp == 4); + CHECK(poly_vec_from_list(&vector, &list)); + CHECK(poly_vec_size(&vector) == 2); + CHECK(poly_vec_at_const(&vector, 0)->exp == 4); + CHECK(poly_vec_at_const(&vector, 1)->coef == 5.0); + CHECK(poly_list_from_vec(©, &vector)); + CHECK(poly_list_equal(&list, ©, 0.0)); + CHECK(poly_list_remove_term(&list, 4)); + CHECK(poly_list_size(&list) == 1); + + poly_list_destroy(&list); + poly_vec_destroy(&vector); + poly_list_destroy(©); + return true; +} + +static bool test_poly_list_arithmetic_and_alias(void) +{ + struct poly_list a; + struct poly_list b; + struct poly_list result; + struct poly_list derivative; + poly_list_init(&a); + poly_list_init(&b); + poly_list_init(&result); + poly_list_init(&derivative); + CHECK(poly_list_add_term(&a, 3.0, 2)); + CHECK(poly_list_add_term(&a, 1.0, 0)); + CHECK(poly_list_add_term(&b, 1.0, 2)); + CHECK(poly_list_add_term(&b, -1.0, 0)); + CHECK(poly_list_add(&result, &a, &b)); + double coefficient; + CHECK(poly_list_get_term(&result, 2, &coefficient)); + CHECK(coefficient == 4.0); + CHECK(!poly_list_get_term(&result, 0, &coefficient)); + CHECK(poly_list_sub(&result, &a, &b)); + CHECK(poly_list_get_term(&result, 0, &coefficient)); + CHECK(coefficient == 2.0); + CHECK(poly_list_derivative(&derivative, &a, 1)); + CHECK(poly_list_get_term(&derivative, 1, &coefficient)); + CHECK(coefficient == 6.0); + CHECK(poly_list_eval(&a, 2.0) == 13.0); + CHECK(poly_list_add(&a, &a, &b)); + CHECK(poly_list_get_term(&a, 2, &coefficient)); + CHECK(coefficient == 4.0); + + poly_list_destroy(&a); + poly_list_destroy(&b); + poly_list_destroy(&result); + poly_list_destroy(&derivative); + return true; +} + +static bool test_expression_nested_precedence_and_variables(void) +{ + struct expression_context context; + struct expression_error error; + double result; + expression_init(&context); + CHECK(expression_collect_variables(&context, + "-(A + 2.5) * (B - (-C + 4)) / 2", &error)); + CHECK(context.variables.size == 3); + CHECK(expression_set_variable(&context, "A", 3.0)); + CHECK(expression_set_variable(&context, "B", 10.0)); + CHECK(expression_set_variable(&context, "C", 2.0)); + CHECK(expression_eval(&context, + "-(A + 2.5) * (B - (-C + 4)) / 2", &result, &error)); + CHECK(fabs(result + 22.0) < 1e-12); + expression_destroy(&context); + return true; +} + +static bool test_expression_variable_dedup_and_update(void) +{ + struct expression_context context; + struct expression_error error; + double result; + expression_init(&context); + CHECK(expression_collect_variables(&context, + "A1 + A1 * B2 - (B2 / 2) + C", &error)); + CHECK(context.variables.size == 3); + CHECK(expression_set_variable(&context, "A1", 4.0)); + CHECK(expression_set_variable(&context, "B2", 6.0)); + CHECK(expression_set_variable(&context, "C", 1.0)); + CHECK(expression_eval(&context, + "A1 + A1 * B2 - (B2 / 2) + C", &result, &error)); + CHECK(fabs(result - 26.0) < 1e-12); + CHECK(expression_set_variable(&context, "B2", 10.0)); + CHECK(expression_eval(&context, + "A1 + A1 * B2 - (B2 / 2) + C", &result, &error)); + CHECK(result == 40.0); + expression_destroy(&context); + return true; +} + +static bool test_expression_unary_and_decimal_values(void) +{ + struct expression_context context; + struct expression_error error; + double result; + expression_init(&context); + CHECK(expression_collect_variables(&context, + "--5 + -(+2.5) * (3 - -1) + .5", &error)); + CHECK(expression_eval(&context, + "--5 + -(+2.5) * (3 - -1) + .5", &result, &error)); + CHECK(fabs(result + 4.5) < 1e-12); + expression_destroy(&context); + return true; +} + +static bool test_expression_deep_parentheses_and_associativity(void) +{ + struct expression_context context; + struct expression_error error; + double result; + expression_init(&context); + CHECK(expression_collect_variables(&context, + "1 - 2 - 3 + 4 * (5 + (6 - (7 - 8)))", &error)); + CHECK(expression_eval(&context, + "1 - 2 - 3 + 4 * (5 + (6 - (7 - 8)))", &result, &error)); + CHECK(fabs(result - 44.0) < 1e-12); + expression_destroy(&context); + return true; +} + +static bool test_expression_error_paths(void) +{ + struct expression_context context; + struct expression_error error; + double result; + expression_init(&context); + CHECK(expression_collect_variables(&context, "X + 1", &error)); + CHECK(!expression_eval(&context, "X + 1", &result, &error)); + CHECK(error.code == EXPRESSION_UNSET_VARIABLE); + CHECK(expression_set_variable(&context, "X", 2.0)); + CHECK(!expression_eval(&context, "X / (3 - 3)", &result, &error)); + CHECK(error.code == EXPRESSION_DIVISION_BY_ZERO); + CHECK(!expression_eval(&context, "X + (2 * 3", &result, &error)); + CHECK(error.code == EXPRESSION_PAREN_ERROR); + CHECK(!expression_collect_variables(&context, "X @ 2", &error)); + CHECK(error.code == EXPRESSION_SYNTAX_ERROR); + expression_destroy(&context); + return true; +} + +struct test_case { + const char *name; + bool (*function)(void); +}; + +int main(void) +{ + const struct test_case tests[] = { + { "vec lifecycle and access", test_vec_lifecycle_and_access }, + { "vec insert, erase and assign", test_vec_insert_erase_and_assign }, + { "vec resize, capacity and swap", test_vec_resize_capacity_and_swap }, + { "vec iterators and comparison", test_vec_iterators_and_comparison }, + { "list and container_of", test_list_and_container_of }, + { "poly_vec normalization", test_poly_vec_normalization }, + { "poly_vec add, sub and alias", test_poly_vec_add_sub_and_alias }, + { "poly_vec multiply, derivative and eval", + test_poly_vec_mul_derivative_and_eval }, + { "poly_list normalization and conversion", + test_poly_list_normalization_and_conversion }, + { "poly_list arithmetic and alias", + test_poly_list_arithmetic_and_alias }, + { "expression nested precedence and variables", + test_expression_nested_precedence_and_variables }, + { "expression variable dedup and update", + test_expression_variable_dedup_and_update }, + { "expression unary and decimal values", + test_expression_unary_and_decimal_values }, + { "expression deep parentheses and associativity", + test_expression_deep_parentheses_and_associativity }, + { "expression error paths", test_expression_error_paths }, + }; + size_t failures = 0; + + for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) { + printf("[%zu/%zu] %s\n", i + 1, + sizeof(tests) / sizeof(tests[0]), tests[i].name); + if (tests[i].function()) { + puts(" passed"); + } else { + ++failures; + puts(" failed"); + } + } + printf("\n%zu/%zu tests passed\n", + sizeof(tests) / sizeof(tests[0]) - failures, + sizeof(tests) / sizeof(tests[0])); + return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} |
