Added Hash Table Tests
This commit is contained in:
parent
63541aac85
commit
95dba7e478
|
|
@ -39,6 +39,7 @@ typedef struct {
|
|||
|
||||
TestsReport run_string_tests();
|
||||
TestsReport run_lexer_tests();
|
||||
TestsReport run_hash_table_tests();
|
||||
TestsReport run_extra_tests();
|
||||
|
||||
#endif // SLS_TESTS_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,383 @@
|
|||
// Kyler Olsen
|
||||
// YREA SLS
|
||||
// Hash Table Tests
|
||||
// November 2025
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sls/string.h"
|
||||
#include "sls/hash_table.h"
|
||||
#include "tests/tests.h"
|
||||
|
||||
static const size_t NUM_HASH_TABLE_TESTS = 12;
|
||||
|
||||
static TestResult pass_hash_table_test(SlsStr test_name) {
|
||||
TestResult result = {.name = test_name, .status = TEST_NOT_IMPLEMENTED};
|
||||
result.status = TEST_PASS;
|
||||
return result;
|
||||
}
|
||||
|
||||
static TestResult fail_hash_table_test(SlsStr test_name, SlsStr message) {
|
||||
TestResult result = { .name = test_name, .status = TEST_NOT_IMPLEMENTED };
|
||||
result.status = TEST_ERROR;
|
||||
result.message = sls_str_cpy(message);
|
||||
|
||||
if (result.message.str == NULL) {
|
||||
result.error = (SlsError){ SLS_STR("Out Of Memory Error."), 1 };
|
||||
result.status = TEST_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Test init_hash_table and del_hash_table
|
||||
static TestResult test_init_and_delete() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_init_and_delete"), SLS_STR("Initialization failed"));
|
||||
|
||||
if (ht->buckets_count < 10) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_init_and_delete"), SLS_STR("Bucket count less than minimum"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_init_and_delete"));
|
||||
}
|
||||
|
||||
// Test basic put and get operations
|
||||
static TestResult test_put_and_get() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Initialization failed"));
|
||||
|
||||
int value1 = 42;
|
||||
int value2 = 84;
|
||||
SlsStr key1 = SLS_STR("key1");
|
||||
SlsStr key2 = SLS_STR("key2");
|
||||
|
||||
if (!hash_table_put(ht, key1, &value1)) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Put operation failed"));
|
||||
}
|
||||
|
||||
if (!hash_table_put(ht, key2, &value2)) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Second put operation failed"));
|
||||
}
|
||||
|
||||
int *retrieved1 = (int*)hash_table_get(ht, key1, NULL);
|
||||
int *retrieved2 = (int*)hash_table_get(ht, key2, NULL);
|
||||
|
||||
if (!retrieved1 || *retrieved1 != 42) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Get operation failed for key1"));
|
||||
}
|
||||
|
||||
if (!retrieved2 || *retrieved2 != 84) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Get operation failed for key2"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_put_and_get"));
|
||||
}
|
||||
|
||||
// Test get with non-existent key returns default
|
||||
static TestResult test_get_nonexistent_key() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_get_nonexistent_key"), SLS_STR("Initialization failed"));
|
||||
|
||||
int default_value = -1;
|
||||
SlsStr key = SLS_STR("nonexistent");
|
||||
|
||||
int *result = (int*)hash_table_get(ht, key, &default_value);
|
||||
if (result != &default_value) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_get_nonexistent_key"), SLS_STR("Did not return default value"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_get_nonexistent_key"));
|
||||
}
|
||||
|
||||
// Test updating existing key
|
||||
static TestResult test_update_existing_key() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_update_existing_key"), SLS_STR("Initialization failed"));
|
||||
|
||||
int value1 = 100;
|
||||
int value2 = 200;
|
||||
SlsStr key = SLS_STR("update_key");
|
||||
|
||||
hash_table_put(ht, key, &value1);
|
||||
hash_table_put(ht, key, &value2);
|
||||
|
||||
int *result = (int*)hash_table_get(ht, key, NULL);
|
||||
if (!result || *result != 200) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_update_existing_key"), SLS_STR("Update failed"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_update_existing_key"));
|
||||
}
|
||||
|
||||
// Test delete operation
|
||||
static TestResult test_delete_key() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_delete_key"), SLS_STR("Initialization failed"));
|
||||
|
||||
int value = 42;
|
||||
SlsStr key = SLS_STR("delete_key");
|
||||
|
||||
hash_table_put(ht, key, &value);
|
||||
|
||||
if (!hash_table_del(ht, key)) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_delete_key"), SLS_STR("Delete returned FALSE"));
|
||||
}
|
||||
|
||||
int default_val = -1;
|
||||
int *result = (int*)hash_table_get(ht, key, &default_val);
|
||||
if (result != &default_val) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_delete_key"), SLS_STR("Key still exists after delete"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_delete_key"));
|
||||
}
|
||||
|
||||
// Test delete non-existent key returns FALSE
|
||||
static TestResult test_delete_nonexistent_key() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_delete_nonexistent_key"), SLS_STR("Initialization failed"));
|
||||
|
||||
SlsStr key = SLS_STR("nonexistent");
|
||||
|
||||
if (hash_table_del(ht, key)) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_delete_nonexistent_key"), SLS_STR("Delete returned TRUE for nonexistent key"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_delete_nonexistent_key"));
|
||||
}
|
||||
|
||||
// Test collision handling (multiple keys in same bucket)
|
||||
static TestResult test_collision_handling() {
|
||||
HashTable *ht = init_hash_table(1); // Force collisions with single bucket
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_collision_handling"), SLS_STR("Initialization failed"));
|
||||
|
||||
int val1 = 1, val2 = 2, val3 = 3;
|
||||
SlsStr key1 = SLS_STR("key1");
|
||||
SlsStr key2 = SLS_STR("key2");
|
||||
SlsStr key3 = SLS_STR("key3");
|
||||
|
||||
hash_table_put(ht, key1, &val1);
|
||||
hash_table_put(ht, key2, &val2);
|
||||
hash_table_put(ht, key3, &val3);
|
||||
|
||||
int *r1 = (int*)hash_table_get(ht, key1, NULL);
|
||||
int *r2 = (int*)hash_table_get(ht, key2, NULL);
|
||||
int *r3 = (int*)hash_table_get(ht, key3, NULL);
|
||||
|
||||
if (!r1 || *r1 != 1 || !r2 || *r2 != 2 || !r3 || *r3 != 3) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_collision_handling"), SLS_STR("Collision handling failed"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_collision_handling"));
|
||||
}
|
||||
|
||||
// Test empty string key
|
||||
static TestResult test_empty_key() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_empty_key"), SLS_STR("Initialization failed"));
|
||||
|
||||
int value = 99;
|
||||
SlsStr key = SLS_STR("");
|
||||
|
||||
if (!hash_table_put(ht, key, &value)) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_empty_key"), SLS_STR("Put with empty key failed"));
|
||||
}
|
||||
|
||||
int *result = (int*)hash_table_get(ht, key, NULL);
|
||||
if (!result || *result != 99) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_empty_key"), SLS_STR("Get with empty key failed"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_empty_key"));
|
||||
}
|
||||
|
||||
// Test long key
|
||||
static TestResult test_long_key() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_long_key"), SLS_STR("Initialization failed"));
|
||||
|
||||
char long_key_str[256];
|
||||
for (size_t i = 0; i < 255; i++) long_key_str[i] = 'A';
|
||||
long_key_str[255] = '\0';
|
||||
|
||||
SlsStr key = sls_str_malloc(long_key_str, 255);
|
||||
int value = 777;
|
||||
|
||||
if (!hash_table_put(ht, key, &value)) {
|
||||
sls_str_free(&key);
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_long_key"), SLS_STR("Put with long key failed"));
|
||||
}
|
||||
|
||||
int *result = (int*)hash_table_get(ht, key, NULL);
|
||||
if (!result || *result != 777) {
|
||||
sls_str_free(&key);
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_long_key"), SLS_STR("Get with long key failed"));
|
||||
}
|
||||
|
||||
sls_str_free(&key);
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_long_key"));
|
||||
}
|
||||
|
||||
// Test multiple operations sequence
|
||||
static TestResult test_multiple_operations() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Initialization failed"));
|
||||
|
||||
int vals[5] = {10, 20, 30, 40, 50};
|
||||
SlsStr keys[5] = {SLS_STR("k1"), SLS_STR("k2"), SLS_STR("k3"), SLS_STR("k4"), SLS_STR("k5")};
|
||||
|
||||
// Insert all
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (!hash_table_put(ht, keys[i], &vals[i])) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Insert failed"));
|
||||
}
|
||||
}
|
||||
|
||||
// Delete some
|
||||
hash_table_del(ht, keys[1]);
|
||||
hash_table_del(ht, keys[3]);
|
||||
|
||||
// Verify remaining
|
||||
int *r0 = (int*)hash_table_get(ht, keys[0], NULL);
|
||||
int *r2 = (int*)hash_table_get(ht, keys[2], NULL);
|
||||
int *r4 = (int*)hash_table_get(ht, keys[4], NULL);
|
||||
|
||||
if (!r0 || *r0 != 10 || !r2 || *r2 != 30 || !r4 || *r4 != 50) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Get after delete failed"));
|
||||
}
|
||||
|
||||
// Verify deleted
|
||||
int def = -1;
|
||||
int *r1 = (int*)hash_table_get(ht, keys[1], &def);
|
||||
int *r3 = (int*)hash_table_get(ht, keys[3], &def);
|
||||
|
||||
if (r1 != &def || r3 != &def) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Deleted keys still present"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_multiple_operations"));
|
||||
}
|
||||
|
||||
// Test storing NULL pointers
|
||||
static TestResult test_null_value() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_null_value"), SLS_STR("Initialization failed"));
|
||||
|
||||
SlsStr key = SLS_STR("null_key");
|
||||
|
||||
if (!hash_table_put(ht, key, NULL)) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_null_value"), SLS_STR("Put with NULL value failed"));
|
||||
}
|
||||
|
||||
int default_val = -1;
|
||||
void *result = hash_table_get(ht, key, &default_val);
|
||||
|
||||
// Should retrieve NULL, not default
|
||||
if (result == &default_val) {
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_null_value"), SLS_STR("NULL value not stored correctly"));
|
||||
}
|
||||
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_null_value"));
|
||||
}
|
||||
|
||||
// Test large number of entries
|
||||
static TestResult test_large_capacity() {
|
||||
HashTable *ht = init_hash_table(10);
|
||||
if (!ht) return fail_hash_table_test(SLS_STR("test_large_capacity"), SLS_STR("Initialization failed"));
|
||||
|
||||
const size_t COUNT = 100;
|
||||
int *values = malloc(sizeof(int) * COUNT);
|
||||
SlsStr *keys = malloc(sizeof(SlsStr) * COUNT);
|
||||
|
||||
for (size_t i = 0; i < COUNT; i++) {
|
||||
values[i] = (int)i;
|
||||
char key_buf[32];
|
||||
snprintf(key_buf, 32, "key_%zu", i);
|
||||
keys[i] = sls_str_malloc(key_buf, strlen(key_buf));
|
||||
|
||||
if (!hash_table_put(ht, keys[i], &values[i])) {
|
||||
for (size_t j = 0; j <= i; j++) sls_str_free(&keys[j]);
|
||||
free(keys);
|
||||
free(values);
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_large_capacity"), SLS_STR("Put failed in bulk insert"));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all entries
|
||||
for (size_t i = 0; i < COUNT; i++) {
|
||||
int *result = (int*)hash_table_get(ht, keys[i], NULL);
|
||||
if (!result || *result != (int)i) {
|
||||
for (size_t j = 0; j < COUNT; j++) sls_str_free(&keys[j]);
|
||||
free(keys);
|
||||
free(values);
|
||||
del_hash_table(ht);
|
||||
return fail_hash_table_test(SLS_STR("test_large_capacity"), SLS_STR("Get failed in bulk verify"));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < COUNT; i++) sls_str_free(&keys[i]);
|
||||
free(keys);
|
||||
free(values);
|
||||
del_hash_table(ht);
|
||||
return pass_hash_table_test(SLS_STR("test_large_capacity"));
|
||||
}
|
||||
|
||||
// Run all hash table tests
|
||||
TestsReport run_hash_table_tests() {
|
||||
TestsReport report = {
|
||||
.section = SLS_STR("hash_table_tests"),
|
||||
.count = NUM_HASH_TABLE_TESTS,
|
||||
.tests = malloc(sizeof(TestResult) * NUM_HASH_TABLE_TESTS)
|
||||
};
|
||||
|
||||
size_t i = 0;
|
||||
report.tests[i++] = test_init_and_delete();
|
||||
report.tests[i++] = test_put_and_get();
|
||||
report.tests[i++] = test_get_nonexistent_key();
|
||||
report.tests[i++] = test_update_existing_key();
|
||||
report.tests[i++] = test_delete_key();
|
||||
report.tests[i++] = test_delete_nonexistent_key();
|
||||
report.tests[i++] = test_collision_handling();
|
||||
report.tests[i++] = test_empty_key();
|
||||
report.tests[i++] = test_long_key();
|
||||
report.tests[i++] = test_multiple_operations();
|
||||
report.tests[i++] = test_null_value();
|
||||
report.tests[i++] = test_large_capacity();
|
||||
|
||||
return report;
|
||||
}
|
||||
|
|
@ -85,6 +85,8 @@ int main(void) {
|
|||
test_report(run_string_tests(), &counts);
|
||||
printf(" ========== Lexer Tests ==========\n");
|
||||
test_report(run_lexer_tests(), &counts);
|
||||
printf(" ========== Hash Table Tests ==========\n");
|
||||
test_report(run_hash_table_tests(), &counts);
|
||||
printf(" ========== Extra Tests ==========\n");
|
||||
test_report(run_extra_tests(), &counts);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue