myxce/src/lib/testsuite/test_xstring.c

225 lines
5.8 KiB
C

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unity/unity.h>
#include "utils.h"
#include "xstring.h"
/* ------------------------------------------------------------------ */
/* Concurrent-test helpers */
/* ------------------------------------------------------------------ */
#define NTHREADS (8)
/* Thread argument for the same-key test */
typedef struct {
const char* key;
char* result;
} same_key_arg_t;
static void* thread_make_same_key(void* arg)
{
same_key_arg_t* a = (same_key_arg_t*)arg;
a->result = makeString((char*)a->key);
return a->result;
}
/* Thread argument for the distinct-key test */
typedef struct {
char key[16];
char* result;
} distinct_key_arg_t;
static void* thread_make_distinct_key(void* arg)
{
distinct_key_arg_t* a = (distinct_key_arg_t*)arg;
a->result = makeString(a->key);
return a->result;
}
/* ------------------------------------------------------------------ */
/* Concurrent tests */
/* ------------------------------------------------------------------ */
/*
* test_xstring_concurrent_same_key
*
* Spawn NTHREADS threads, each racing to intern the same key.
* With the TOCTOU fix the lock is held from key_find through key_add,
* so every thread must receive the identical interned pointer.
* Without the fix, two threads could both miss key_find, both call
* makeInstance, and one would leak — and the pointers would differ.
*/
static void test_xstring_concurrent_same_key(void)
{
pthread_t tids[NTHREADS];
same_key_arg_t args[NTHREADS];
int rc;
int i;
rc = xstring_table_init(139);
TEST_ASSERT_TRUE(rc >= 0);
for (i = 0; i < NTHREADS; i++) {
args[i].key = "concurrent_key";
args[i].result = NULL;
rc = pthread_create(&tids[i], NULL, thread_make_same_key, &args[i]);
TEST_ASSERT_TRUE(rc == 0);
}
for (i = 0; i < NTHREADS; i++) {
void* retval = NULL;
rc = pthread_join(tids[i], &retval);
TEST_ASSERT_TRUE(rc == 0);
}
/* All threads must have received a non-NULL pointer */
for (i = 0; i < NTHREADS; i++) {
TEST_ASSERT_TRUE(args[i].result != NULL);
}
/* All pointers must be the same interned object */
for (i = 1; i < NTHREADS; i++) {
TEST_ASSERT_EQUAL_PTR(args[0].result, args[i].result);
}
/*
* makeString increments the refcount on a cache hit. Each thread
* got one reference; drop NTHREADS - 1 extras so xstring_table_fini
* can release the final one cleanly.
*/
for (i = 1; i < NTHREADS; i++) {
dereference((var)args[i].result);
}
xstring_table_fini();
}
/*
* test_xstring_concurrent_distinct_keys
*
* Spawn NTHREADS threads, each interning a unique key.
* Every returned pointer must be non-NULL and distinct from all others,
* confirming that concurrent inserts of different keys do not collide.
*/
static void test_xstring_concurrent_distinct_keys(void)
{
pthread_t tids[NTHREADS];
distinct_key_arg_t args[NTHREADS];
int rc;
int i;
int j;
rc = xstring_table_init(139);
TEST_ASSERT_TRUE(rc >= 0);
for (i = 0; i < NTHREADS; i++) {
snprintf(args[i].key, sizeof(args[i].key), "key_%d", i);
args[i].result = NULL;
rc = pthread_create(&tids[i], NULL, thread_make_distinct_key, &args[i]);
TEST_ASSERT_TRUE(rc == 0);
}
for (i = 0; i < NTHREADS; i++) {
void* retval = NULL;
rc = pthread_join(tids[i], &retval);
TEST_ASSERT_TRUE(rc == 0);
}
/* Every pointer must be non-NULL */
for (i = 0; i < NTHREADS; i++) {
TEST_ASSERT_TRUE(args[i].result != NULL);
}
/* Every pointer must be distinct from all others */
for (i = 0; i < NTHREADS; i++) {
for (j = i + 1; j < NTHREADS; j++) {
TEST_ASSERT_NOT_EQUAL(args[i].result, args[j].result);
}
}
/* Release each thread's reference */
for (i = 0; i < NTHREADS; i++) {
dereference((var)args[i].result);
}
xstring_table_fini();
}
static void test_xstring_create(void)
{
char* s = "Hello, world";
char* d = NULL;
int rc;
rc = xstring_table_init(139);
TEST_ASSERT_TRUE(rc >= 0);
d = makeString(s);
TEST_ASSERT_TRUE(d != NULL);
TEST_ASSERT_TRUE(strcmp(d, s) == 0);
TEST_ASSERT_TRUE(length(d) == strlen(s));
TEST_ASSERT_TRUE(hash(d) == hash_string(s));
xstring_table_fini();
}
static void test_xstring_share(void)
{
char* s = "Hello, world";
char* d = NULL;
char* d1 = NULL;
int rc;
rc = xstring_table_init(139);
TEST_ASSERT_TRUE(rc >= 0);
d = makeString(s);
TEST_ASSERT_TRUE(d != NULL);
TEST_ASSERT_TRUE(strcmp(d, s) == 0);
TEST_ASSERT_TRUE(length(d) == strlen(s));
TEST_ASSERT_TRUE(hash(d) == hash_string(s));
d1 = makeString(s);
TEST_ASSERT_TRUE(d1 != NULL);
TEST_ASSERT_TRUE(d == d1);
xstring_table_fini();
}
static void test_xstring_deref(void)
{
char* s = "Hello, world";
char* d = NULL;
int rc;
rc = xstring_table_init(139);
TEST_ASSERT_TRUE(rc >= 0);
d = makeString(s);
TEST_ASSERT_TRUE(d != NULL);
TEST_ASSERT_TRUE(strcmp(d, s) == 0);
TEST_ASSERT_TRUE(length(d) == strlen(s));
TEST_ASSERT_TRUE(hash(d) == hash_string(s));
dereference((var)d);
xstring_table_fini();
}
void test_xstrings(void)
{
printf("\n>> xstring test\n\n");
RUN_TEST(test_xstring_create);
RUN_TEST(test_xstring_deref);
RUN_TEST(test_xstring_share);
RUN_TEST(test_xstring_concurrent_same_key);
RUN_TEST(test_xstring_concurrent_distinct_keys);
return;
}