diff --git a/Makefile b/Makefile index 5cee03b..6f61b53 100644 --- a/Makefile +++ b/Makefile @@ -8,26 +8,29 @@ OBJ_DIR = obj TESTS_SRC = tests TARGET = usage -TEST_TARGET = test_vector +TEST_V_TARGET = test_vector +TEST_M_TARGET = test_map LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o PROG_OBJS = $(OBJ_DIR)/usage.o -TESTS_OBJS = $(OBJ_DIR)/test_vector.o .PHONY: all clean -all: $(TARGET) $(TEST_TARGET) +all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TARGET): $(PROG_OBJS) $(LIB_OBJS) $(CC) $(CFLAGS) -o $@ $^ -$(TEST_TARGET): $(TESTS_OBJS) $(OBJ_DIR)/vector.o +$(TEST_V_TARGET): $(OBJ_DIR)/test_vector.o $(OBJ_DIR)/vector.o + $(CC) $(CFLAGS) -o $@ $^ + +$(TEST_M_TARGET): $(OBJ_DIR)/test_map.o $(OBJ_DIR)/map.o $(CC) $(CFLAGS) -o $@ $^ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c -o $@ $< -$(OBJ_DIR)/%.o: %.c | $(OBJ_DIR) +$(OBJ_DIR)/usage.o: usage.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c -o $@ $< $(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR) @@ -37,4 +40,4 @@ $(OBJ_DIR): mkdir -p $(OBJ_DIR) clean: - rm -rf $(OBJ_DIR) $(TARGET) $(TEST_TARGET) + rm -rf $(OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) diff --git a/tests/test_map.c b/tests/test_map.c new file mode 100644 index 0000000..fa4b3e7 --- /dev/null +++ b/tests/test_map.c @@ -0,0 +1,340 @@ +/* + * Unit tests for Map data type + */ + +#define TEST(NAME) do { \ + printf("Running test_%s...", #NAME); \ + test_##NAME(); \ + printf(" PASSED\n"); \ +} while(0) + +#include +#include +#include + +#include "../src/map.h" + +// Create a new map +void test_map_new() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + assert(res.value.map != NULL); + assert(map_size(res.value.map) == 0); + assert(map_capacity(res.value.map) > 0); + + map_destroy(res.value.map); +} + +// Add elements to map +void test_map_add() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + int x = 42, y = 84; + + map_result_t x_res = map_add(map, "key1", &x); + assert(x_res.status == MAP_OK); + assert(map_size(map) == 1); + + map_result_t y_res = map_add(map, "key2", &y); + assert(y_res.status == MAP_OK); + assert(map_size(map) == 2); + + map_destroy(map); +} + +// Add multiple elements to the map +void test_map_add_multiple() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + const int x = 0xB00B5; + const char *y = "Hello"; + + map_result_t add_res = map_add(map, "x", (void*)&x); + assert(add_res.status == MAP_OK); + + add_res = map_add(map, "y", (void*)y); + assert(add_res.status == MAP_OK); + + assert(map_size(map) == 2); + + map_destroy(map); +} + +// Get map element +void test_map_get() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + int val = 123; + map_add(map, "test", &val); + + map_result_t get_res = map_get(map, "test"); + assert(get_res.status == MAP_OK); + assert(*(int*)get_res.value.element == 123); + + map_destroy(map); +} + +// Get non-existing key from map +void test_map_get_invalid() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + map_result_t get_res = map_get(map, "boom"); + assert(get_res.status == MAP_ERR_NOT_FOUND); + + map_destroy(map); +} + +// Map with heterogeneous types +void test_map_mixed() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + const int x = 0xB00B5; + const char *y = "Hello"; + + map_add(map, "x", (void*)&x); + map_add(map, "y", (void*)y); + + map_result_t get_res = map_get(map, "x"); + assert(get_res.status == MAP_OK); + + const int *val_x = (const int*)get_res.value.element; + assert(*val_x == 0xB00B5); + + get_res = map_get(map, "y"); + assert(get_res.status == MAP_OK); + + const char *val_y = (const char*)get_res.value.element; + assert(!strcmp(val_y, "Hello")); + + map_destroy(map); +} + +// Update existing map key +void test_map_update() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + const int x = 100, y = 200; + + map_add(map, "key", (void*)&x); + + map_result_t set_res = map_add(map, "key", (void*)&y); + assert(set_res.status == MAP_OK); + + map_result_t get_res = map_get(map, "key"); + const int *val = (const int*)get_res.value.element; + + assert(*val == 200); + assert(map_size(map) == 1); + + map_destroy(map); +} + +// Remove an element from map +void test_map_remove() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + const int x = 10, y = 20; + + map_add(map, "x", (void*)&x); + map_add(map, "y", (void*)&y); + + assert(map_size(map) == 2); + + map_result_t rm_res = map_remove(map, "x"); + assert(rm_res.status == MAP_OK); + assert(map_size(map) == 1); + + // Check whether the 'x' and 'y' keysx is still there + map_result_t get_res = map_get(map, "x"); + assert(get_res.status != MAP_OK); + + get_res = map_get(map, "y"); + assert(get_res.status == MAP_OK); + + map_destroy(map); +} + +// Remove non-existing key from map +void test_map_remove_invalid() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + map_result_t rm_res = map_remove(map, "boom"); + assert(rm_res.status != MAP_OK); + + map_destroy(map); +} + +// Clear the map +void test_map_clear() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + int x = 10, y = 20, z = 30; + + map_add(map, "x", (void*)&x); + map_add(map, "y", (void*)&y); + map_add(map, "z", (void*)&z); + + assert(map_size(map) == 3); + + map_result_t clear_res = map_clear(map); + assert(clear_res.status == MAP_OK); + assert(map_size(map) == 0); + + map_destroy(map); +} + +// Clear empty map +void test_map_clear_empty() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + map_result_t clear_res = map_clear(map); + assert(clear_res.status == MAP_OK); + assert(map_size(map) == 0); + + map_destroy(map); +} + +// Multiple operations in sequence (add, update, delete and clear) +void test_map_sequence() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + const int x = 0xB00B5; + const char *y = "Hello"; + + // Add + map_add(map, "x", (void*)&x); + map_add(map, "y", (void*)&y); + + assert(map_size(map) == 2); + + // Update + const int new_x = 0xC0FFEE; + map_add(map, "x", (void*)&new_x); + + map_result_t get_res = map_get(map, "x"); + assert(*(const int*)get_res.value.element == 0xC0FFEE); + + // Delete + map_remove(map, "y"); + assert(map_size(map) == 1); + + // Clear + map_clear(map); + assert(map_size(map) == 0); + + map_destroy(map); +} + +// Test map with product data types +typedef struct { + char name[256]; + char surname[256]; + short age; +} Person; + +void test_map_struct() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + Person bob = { "Bob", "Miller", 23 }; + Person alice = { "Alice", "Davis", 21 }; + + map_add(map, "af94rt", &bob); + map_add(map, "b910o5", &alice); + + map_result_t get_res = map_get(map, "af94rt"); + assert(get_res.status == MAP_OK); + + Person *retr = (Person*)get_res.value.element; + assert(!strcmp(retr->name, "Bob")); + assert(!strcmp(retr->surname, "Miller")); + assert(retr->age == 23); + + get_res = map_get(map, "b910o5"); + assert(get_res.status == MAP_OK); + + retr = (Person*)get_res.value.element; + assert(!strcmp(retr->name, "Alice")); + assert(!strcmp(retr->surname, "Davis")); + assert(retr->age == 21); + + map_destroy(map); +} + +// Test map capacity tracking +void test_map_cap() { + map_result_t res = map_new(); + + assert(res.status == MAP_OK); + map_t *map = res.value.map; + + for (int i = 0; i < 10; i++) { + char key[16]; + snprintf(key, sizeof(key), "key%d", i); + map_add(map, key, &i); + } + + assert(map_size(map) == 10); + assert(map_capacity(map) >= 10); + + map_destroy(map); +} + +int main(void) { + printf("=== Running Map unit tests ===\n\n"); + + TEST(map_new); + TEST(map_add); + TEST(map_add_multiple); + TEST(map_get); + TEST(map_get_invalid); + TEST(map_mixed); + TEST(map_update); + TEST(map_remove); + TEST(map_remove_invalid); + TEST(map_clear); + TEST(map_clear_empty); + TEST(map_sequence); + TEST(map_struct); + TEST(map_cap); + + printf("\n=== All tests passed! ===\n"); + + return 0; +} diff --git a/tests/test_vector.c b/tests/test_vector.c index cf33d3c..0017bfa 100644 --- a/tests/test_vector.c +++ b/tests/test_vector.c @@ -1,5 +1,5 @@ /* - * Unit tests for Vector + * Unit tests for Vector data type */ #define TEST(NAME) do { \ @@ -48,7 +48,7 @@ void test_vector_push() { vector_result_t y_res = vector_push(v, &y); assert(y_res.status == VECTOR_OK); - assert(vector_size(v) == 1); + assert(vector_size(v) == 2); vector_destroy(v); } @@ -274,6 +274,7 @@ int main(void) { TEST(vector_new); TEST(vector_new_zcap); + TEST(vector_push); TEST(vector_push_realloc); TEST(vector_get); TEST(vector_get_ofb); diff --git a/usage.c b/usage.c index 49de956..d204cbf 100644 --- a/usage.c +++ b/usage.c @@ -1,4 +1,20 @@ -#define SEP(SIZE) { for (size_t i = 0; i < SIZE; i++) { printf("="); }; puts("\n"); } +/* + * Sample usage of the Datum library. + * + * This program is a complete example on how to use Datum + * with *verbose* error checking. For a more minimal usage, you may want to ignore + * return messages/codes and get straight to the actual result. See the early + * part of the README.md file for such example (use it at your own risk). + * + * Developed by Marco Cetica (c) 2025, + * + */ +#define SEP(SIZE) do { \ + for (size_t i = 0; i < SIZE; i++) { \ + printf("="); \ + }; \ + puts("\n"); \ +} while(0) #include @@ -14,7 +30,7 @@ int main(void) { st = vector_usage(); if (st) { return st; } - SEP(50) + SEP(50); st = map_usage(); if (st) { return st; }