From 87807516500ea3a963308135c8561f3a5832348c Mon Sep 17 00:00:00 2001 From: Mosder Date: Mon, 31 Aug 2026 16:20:32 +0200 Subject: feat: added tests for hashmap --- include/moslib/ds/hashmap.h | 18 +++++-- src/ds/hashmap.c | 9 ++++ tests/ds/hashmap_tests.c | 125 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 6 deletions(-) diff --git a/include/moslib/ds/hashmap.h b/include/moslib/ds/hashmap.h index 0e040c3..a05fa67 100644 --- a/include/moslib/ds/hashmap.h +++ b/include/moslib/ds/hashmap.h @@ -10,6 +10,8 @@ // key; // val; // }; +// can use: +// HM_DEF(name, tk, tv) -> expands to: typedef struct {tk key; tv val;} name // // Struct needs to have these two values. Ig it could have more and it would work if you just use // the entry functions, but would have uninitialized values for hm_put as that one will only @@ -45,6 +47,7 @@ #ifndef MOS_FORCE_PREFIXES +#define HM_DEF MOS_HM_DEF #define hm_new mos_hm_new #define hm_put mos_hm_put #define hm_put_e mos_hm_put_e @@ -62,6 +65,11 @@ typedef struct { int (*eq)(void *key1, void *key2); } MosHmInitArgs; +// clang-format off +// Macro to define Entry struct easier +#define MOS_HM_DEF(name, key_t, val_t) typedef struct {key_t key; val_t val;} name +// clang-format on + // Create a new hashmap // // Optional arguments: @@ -80,7 +88,7 @@ typedef struct { // // Returns: // pointer to the new hashmap -#define mos_hm_new(...) mos_hm_new_fn((MosHmInitArgs){.key = DEFAULT, __VA_ARGS__}) +#define mos_hm_new(...) mos_hm_new_fn((MosHmInitArgs){__VA_ARGS__}) // Put given value with given key in the hashmap // If entry of the same key exists - overwrites it @@ -120,7 +128,7 @@ typedef struct { // // Returns: // value of the entry with given key or 0 if no such entry exist -#define mos_hm_get(hm, key) +#define mos_hm_get(hm, key) 0 // Get a pointer to an entry with specific key in hashmap // @@ -133,7 +141,7 @@ typedef struct { // // Returns: // pointer to the entry or NULL if no entry with such key exists -#define mos_hm_get_e(hm, key) +#define mos_hm_get_e(hm, key) NULL // Get a pointer to the first entry in the hashmap // @@ -143,7 +151,7 @@ typedef struct { // // Returns: // pointer to the first entry or NULL if hashmap is empty -#define mos_hm_first(hm) +#define mos_hm_first(hm) NULL // Get the pointer to the next entry in the hashmap from the pointer to the current one // If pointer is NULL - gets the first entry from the hashmap @@ -157,7 +165,7 @@ typedef struct { // // Returns: // pointer to the next entry or NULL if there's no more entries -#define mos_hm_next(hm, curr) +#define mos_hm_next(hm, curr) NULL // Free the hashmap // diff --git a/src/ds/hashmap.c b/src/ds/hashmap.c index 2c5784a..b0e13ac 100644 --- a/src/ds/hashmap.c +++ b/src/ds/hashmap.c @@ -1,2 +1,11 @@ #define MOS_FORCE_PREFIXES #include "moslib/ds/hashmap.h" + +void *mos_hm_new_fn(MosHmInitArgs args) { + (void)args; + return NULL; +} + +void mos_hm_free(void *hm) { + (void)hm; +} diff --git a/tests/ds/hashmap_tests.c b/tests/ds/hashmap_tests.c index 83efe03..49e4261 100644 --- a/tests/ds/hashmap_tests.c +++ b/tests/ds/hashmap_tests.c @@ -1,7 +1,130 @@ #include "hashmap_tests.h" #include "moslib/ds/hashmap.h" +#include +#include + +#include "moslib/ds/array.h" +#include "moslib/string.h" + +Test put_get() { + HM_DEF(E, float, int); + E *hm = NULL; + + hm_put(hm, 0.0f, 69); + hm_put(hm, 1.0f, 420); + test_assert(hm_get(hm, 0.0f) == 69, "hm_get got value not from hm_put"); + test_assert(hm_get(hm, 1.0f) == 420, "hm_get got value not from hm_put"); + + hm_put(hm, 0.0f, 2137); + test_assert(hm_get(hm, 0.0f) == 2137, "hm_put didn't update value properly"); + + // a lot of puts and gets to test expansion of hashmap + for (int i = 2; i < 100; i++) { + hm_put(hm, (float)i, i); + } + int flag = 1; + for (int i = 2; i < 100; i++) { + if (hm_get(hm, (float)i) != i) + flag = 0; + } + test_assert(flag, "hm_put / hm_get failed when expansion was needed"); + + hm_free(hm); +} + +Test put_e_get_e() { + HM_DEF(E, float, int); + E *hm = NULL; + + hm_put_e(hm, (E){0.0f, 69}); + hm_put_e(hm, (E){1.0f, 420}); + E e = {0.0f, 69}; + test_assert(memcmp(&e, hm_get_e(hm, 0.0f), sizeof(E)) == 0, "hm_get_e got entry not from hm_put_e"); + e = (E){1.0f, 420}; + test_assert(memcmp(&e, hm_get_e(hm, 1.0f), sizeof(E)) == 0, "hm_get_e got entry not from hm_put_e"); + + e = (E){1.0f, 2137}; + hm_put_e(hm, e); + test_assert(memcmp(&e, hm_get_e(hm, 1.0f), sizeof(E)) == 0, "hm_put_e didn't update entry properly"); + + hm_free(hm); +} + +Test str_key() { + HM_DEF(E, char *, int); + E *hm = hm_new(.key = STR); + + char *str = "key"; + char *str2 = strdup(str); + hm_put(hm, str, 69); + hm_put(hm, "hey", 420); + test_assert(hm_get(hm, str2) == 69, "hm_get got value not from hm_put"); + test_assert(hm_get(hm, "hey") == 420, "hm_get got value not from hm_put"); + + hm_put(hm, str2, 2137); + test_assert(hm_get(hm, str) == 2137, "hm_put didn't update value properly"); + + free(str2); + hm_free(hm); +} + +Test ss_key() { + HM_DEF(E, StringSlice, int); + E *hm = hm_new(.key = SS); + + char *str = "key"; + char *str2 = strdup(str); + StringSlice ss = ss_from_str(str); + StringSlice ss2 = ss_from_str(str2); + hm_put(hm, ss, 69); + hm_put(hm, SS_LIT("hey"), 420); + test_assert(hm_get(hm, ss2) == 69, "hm_get got value not from hm_put"); + test_assert(hm_get(hm, SS_LIT("hey")) == 420, "hm_get got value not from hm_put"); + + hm_put(hm, ss2, 2137); + test_assert(hm_get(hm, ss) == 2137, "hm_put didn't update value properly"); + + free(str2); + hm_free(hm); +} + +Test looping() { + HM_DEF(E, int, int); + E *hm = NULL; + + hm_put(hm, 1, 10); + hm_put(hm, 2, 20); + hm_put(hm, 3, 30); + + E *allowed = NULL; + E items[] = {{1, 10}, {2, 20}, {3, 30}}; + arr_append_n(allowed, items, 3); + + test_assert(memcmp(hm_first(hm), hm_next(hm, NULL), sizeof(E)), "hm_next(hm, NULL) != hm_first(hm)"); + E *e = NULL; + while ((e = hm_next(hm, e))) { + int flag = 0; + for (size_t i = 0; i < arr_len(allowed); i++) { + if (memcmp(e, allowed + i, sizeof(E)) == 0) { + flag = 1; + arr_del(allowed, i); + break; + } + } + test_assert(flag, "hm_next produced an entry that wasn't put in the hashmap"); + } + test_assert(arr_len(allowed) == 0, "hm_next didn't produce every entry from hashmap when it looped over it"); + + arr_free(allowed); + hm_free(hm); +} + void ds_hashmap_tests(Tester *tester) { TestGroup *group = add_test_group(tester, "moslib/ds/hashmap.h"); - (void)group; + add_test(group, put_get); + add_test(group, put_e_get_e); + add_test(group, str_key); + add_test(group, ss_key); + add_test(group, looping); } -- cgit v1.2.3