#include "../src/vector.c" #include #include #include #include #include #include #include #include "../src/testing.h" static int test_vector_new_with(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_new_with()"); { testing("allocating struct Vector with small capacity"); const size_t capacity = 1U; const size_t max_capacity = 2U; const size_t multiplier = 3U; const size_t size = 4U; if (vector_new_with( capacity, max_capacity, multiplier, size, &v )) { logerr("vector_new_with()"); goto out; } assert(v->capacity == capacity); assert(v->count == 0U); assert(v->max_capacity == max_capacity); assert(v->multiplier == multiplier); assert(v->value_size == size); vector_free(&v); test_ok(); } { testing("we can't ask for a capacity beyond max_capacity"); const size_t max_capacity = 2U; assert(v == NULL); assert(vector_new_with( max_capacity + 1, max_capacity, 1U, 1U, &v ) == EOVERFLOW); assert(v == NULL); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static int test_vector_new(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_new()"); { testing("simple allocation of int[]"); const size_t size = sizeof(int); if (vector_new(size, &v)) { logerr("vector_new()"); goto out; } assert(v->capacity == VECTOR_DEFAULT_CAPACITY); assert(v->count == 0); assert(v->max_capacity == VECTOR_MAX_CAPACITY); assert(v->multiplier == GROWTH_MULTIPLIER); assert(v->value_size == size); vector_free(&v); test_ok(); } { testing("simple allocation of custom struct"); struct Custom { int a; char b; void *c; } custom; const size_t size = sizeof(custom); if (vector_new(size, &v)) { logerr("vector_new()"); goto out; } assert(v->capacity == VECTOR_DEFAULT_CAPACITY); assert(v->count == 0); assert(v->max_capacity == VECTOR_MAX_CAPACITY); assert(v->multiplier == GROWTH_MULTIPLIER); assert(v->value_size == size); vector_free(&v); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static int test_vector_free(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_free()"); { testing("*v becomes NULL again after vector_free(&v)"); assert(v == NULL); if (vector_new(sizeof(char), &v)) { logerr("vector_new()"); goto out; } assert(v != NULL); vector_free(&v); assert(v == NULL); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static int test_vector_capacity(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_capacity()"); { testing("we get whatever the capacity is"); if (vector_new(sizeof(long), &v)) { logerr("vector_new()"); goto out; } assert(vector_capacity(v) == VECTOR_DEFAULT_CAPACITY); assert(v->capacity == VECTOR_DEFAULT_CAPACITY); printf("\n"); const long value = 123; for (size_t i = 0U; i < VECTOR_DEFAULT_CAPACITY; i++) { assert(vector_push_back(v, &value) == 0); } const size_t new_capacity = next_capacity(v); if (vector_push_back(v, &value)) { logerr("vector_push_back()"); goto out; } assert(vector_capacity(v) == new_capacity); assert(v->capacity == new_capacity); assert(vector_capacity(v) != VECTOR_DEFAULT_CAPACITY); vector_free(&v); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static int test_vector_count(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_count()"); { testing("we get whatever the count is"); if (vector_new(sizeof(unsigned long), &v)) { logerr("vector_new()"); goto out; } assert(vector_count(v) == 0); assert(v->count == 0); const unsigned long value = 123; assert(vector_push_back(v, &value) == 0); assert(vector_count(v) == 1); assert(v->count == 1); vector_free(&v); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static void test_next_capacity(void) { size_t capacity; test_start("next_capacity()"); { testing("noop multipliers"); capacity = next_capacity(&(struct Vector) { .capacity = 1U, .multiplier = 1U, .max_capacity = 0U, }); assert(capacity == 1U); capacity = next_capacity(&(struct Vector) { .capacity = SIZE_MAX, .multiplier = 1U, .max_capacity = 0U, }); assert(capacity == SIZE_MAX); capacity = next_capacity(&(struct Vector) { .capacity = 1U, .multiplier = SIZE_MAX, .max_capacity = 0U, }); assert(capacity == SIZE_MAX); test_ok(); } { testing("small value"); capacity = next_capacity(&(struct Vector) { .capacity = 10U, .multiplier = 10U, .max_capacity = 0U, }); assert(capacity == 100U); test_ok(); } { testing("big value"); capacity = next_capacity(&(struct Vector) { .capacity = SIZE_MAX / 4U, .multiplier = 4U, .max_capacity = 0U, }); assert(capacity == SIZE_MAX - 3U); test_ok(); } { testing("big value with overflow"); capacity = next_capacity(&(struct Vector) { .capacity = SIZE_MAX / 2U, .multiplier = 3U, .max_capacity = 0U, }); assert(capacity == 0U); test_ok(); } } static void test_next_size(void) { test_start("next_size()"); { testing("same as multiplication"); // FIXME: test_ok(); } } static int test_vector_nth(void) { int rc = -1; const struct Vector *v = NULL; const int first = 123; const int second = 321; const int third = 555; const int values[] = { first, second, third }; const size_t values_len = sizeof(values) / sizeof(values[0]); test_start("vector_nth()"); { testing("nth with growth"); if (vector_new(sizeof(int), &v)) { logerr("vector_new_with()"); goto out; } assert(v->capacity >= values_len); for (unsigned int i = 0; i < values_len; i++) { assert(vector_push_back(v, &values[i]) == 0); } int nth; assert(vector_nth(v, 0U, (void *)&nth) == 0); assert(nth == 123); assert(vector_nth(v, 1U, (void *)&nth) == 0); assert(nth == 321); assert(vector_nth(v, 2U, (void *)&nth) == 0); assert(nth == 555); vector_free(&v); test_ok(); } { testing("nth out of bounds errors"); if (vector_new(sizeof(int), &v)) { logerr("vector_new_with()"); goto out; } assert(v->capacity >= values_len); for (unsigned int i = 0; i < values_len; i++) { assert(vector_push_back(v, &values[i]) == 0); } int nth = 222; assert(vector_nth(v, 4U, (void *)&nth) != 0); assert(nth == 222); vector_free(&v); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static int test_vector_push_back(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_push_back()"); { testing("a single push back overwrites existing garbage"); if (vector_new(sizeof(long long), &v)) { logerr("vector_new()"); goto out; } const long long before = 123; const long long after = 321; v->values[0] = (void *)before; assert(vector_push_back(v, &after) == 0); assert(v->values[0] == (void *)after); vector_free(&v); test_ok(); } { testing("push back beyond capacity causes reallocation"); const unsigned long data = 2222U; if (vector_new(sizeof(unsigned long), &v)) { logerr("vector_new()"); goto out; } assert(v->capacity == VECTOR_DEFAULT_CAPACITY); assert(vector_count(v) == 0); for (unsigned int i = 0; i < v->capacity; i++) { assert(vector_push_back(v, &data) == 0); } assert(v->values[0] == (void *)data); const size_t capacity = v->capacity; assert(capacity == vector_count(v)); if (vector_push_back(v, &data)) { logerr("vector_push_back()"); goto out; } const size_t new_capacity = capacity * v->multiplier; assert(v->capacity == new_capacity); assert(vector_count(v) == capacity + 1U); assert(vector_push_back(v, &data) == 0); assert(v->capacity == new_capacity); assert(v->count == capacity + 2U); vector_free(&v); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static int test_vector_pop_back(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_pop_back()"); { testing("an empty vector underflows"); if (vector_new(sizeof(int), &v)) { logerr("vector_new()"); goto out; } int val = 222; assert(vector_pop_back(v, (void *)&val) != 0); assert(val == 222); vector_free(&v); test_ok(); } { testing("a pop never shrinks a vector back"); const unsigned long long data = 1234U; unsigned long long val = 0U; if (vector_new(sizeof(data), &v)) { logerr("vector_new()"); goto out; } const size_t capacity = v->capacity; for (unsigned int i = 0U; i < capacity; i++) { assert(vector_push_back(v, &data) == 0); } assert(v->values[0U] == (void *)data); assert(v->values[capacity - 1U] == (void *)data); assert(capacity == vector_count(v)); if (vector_push_back(v, &data)) { logerr("vector_push_back()"); goto out; } const size_t new_capacity = capacity * v->multiplier; assert(v->capacity == new_capacity); assert(vector_count(v) == capacity + 1U); for (unsigned int i = 0U; i < capacity + 1U; i++) { val = 0U; assert(vector_pop_back(v, (void *)&val) == 0); assert(val == 1234U); } assert(vector_pop_back(v, (void *)&val) != 0); vector_free(&v); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } static int test_vector_contains(void) { int rc = -1; const struct Vector *v = NULL; test_start("vector_contains()"); { testing("empty vector"); if (vector_new(sizeof(int), &v)) { logerr("vector_new()"); goto out; } const int value = 1; for (size_t i = 0; i < 100; i++) { assert(!vector_contains(v, &value)); } vector_free(&v); test_ok(); } { testing("small vectors"); if (vector_new(sizeof(size_t), &v)) { logerr("vector_new()"); goto out; } for (size_t i = 0; i < 100U; i++) { if (vector_push_back(v, &i)) { logerr("vector_push_back()"); goto out; } } const size_t value = 100U; for (size_t i = 0; i < vector_count(v); i++) { assert(!vector_contains(v, &value)); } for (size_t i = 0; i < vector_count(v); i++) { assert(vector_contains(v, &i)); } vector_free(&v); test_ok(); } rc = 0; out: if (v != NULL) { vector_free(&v); } return rc; } int main(void) { int rc = EXIT_FAILURE; if (test_vector_new_with()) { logerr("test_vector_new_with()"); goto out; } if (test_vector_new()) { logerr("test_vector_new()"); goto out; } if (test_vector_free()) { logerr("test_vector_free()"); goto out; } if (test_vector_capacity()) { logerr("test_vector_capacity()"); goto out; } if (test_vector_count()) { logerr("test_vector_count()"); goto out; } test_next_capacity(); test_next_size(); if (test_vector_nth()) { logerr("test_vector_nth()"); goto out; } if (test_vector_push_back()) { logerr("test_vector_push_back()"); goto out; } if (test_vector_pop_back()) { logerr("test_vector_pop_back()"); goto out; } if (test_vector_contains()) { logerr("test_vector_contains()"); goto out; } rc = EXIT_SUCCESS; out: return rc; }