test: added test framework for testing trie
parent
2d7cce138d
commit
ccbc55350c
|
@ -5,11 +5,15 @@ set(CMAKE_C_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
include_directories(crow/include include)
|
include_directories(crow/include include)
|
||||||
|
|
||||||
add_subdirectory(crow)
|
add_subdirectory(crow)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL Release)
|
if(CMAKE_BUILD_TYPE MATCHES "^Test")
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(test)
|
||||||
|
else()
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL Release)
|
||||||
add_compile_options(-O3 -flto)
|
add_compile_options(-O3 -flto)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(lander src/main.cpp src/trie.c)
|
add_executable(lander src/main.cpp src/trie/trie.c)
|
||||||
|
endif()
|
||||||
|
|
15
Makefile
15
Makefile
|
@ -14,13 +14,22 @@ all: build
|
||||||
.PHONY: cmake
|
.PHONY: cmake
|
||||||
cmake: $(BUILD_DIR)/Debug/Makefile
|
cmake: $(BUILD_DIR)/Debug/Makefile
|
||||||
$(BUILD_DIR)/Debug/Makefile: CMakeLists.txt
|
$(BUILD_DIR)/Debug/Makefile: CMakeLists.txt
|
||||||
@ cmake -B'$(BUILD_DIR)/Debug' -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .
|
@ cmake -B'$(BUILD_DIR)/Debug' -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .
|
||||||
@ ln -sf '$(BUILD_DIR)/Debug/compile_commands.json' compile_commands.json
|
@ ln -sf '$(BUILD_DIR)/Debug/compile_commands.json' compile_commands.json
|
||||||
|
|
||||||
|
.PHONY: cmake-test
|
||||||
|
cmake-test: $(BUILD_DIR)/Test/Makefile
|
||||||
|
$(BUILD_DIR)/Test/Makefile: CMakeLists.txt
|
||||||
|
@ cmake -B'$(BUILD_DIR)/Test' -DCMAKE_BUILD_TYPE=Test .
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: cmake
|
build: cmake
|
||||||
@ make -C '$(BUILD_DIR)/Debug'
|
@ make -C '$(BUILD_DIR)/Debug'
|
||||||
|
|
||||||
|
.PHONY: build-test
|
||||||
|
build-test: cmake-test
|
||||||
|
@ make -C '$(BUILD_DIR)/Test'
|
||||||
|
|
||||||
.PHONY: cmake-release
|
.PHONY: cmake-release
|
||||||
cmake-release: $(BUILD_DIR)/Release/Makefile
|
cmake-release: $(BUILD_DIR)/Release/Makefile
|
||||||
$(BUILD_DIR)/Release/Makefile: CMakeLists.txt
|
$(BUILD_DIR)/Release/Makefile: CMakeLists.txt
|
||||||
|
@ -38,6 +47,10 @@ run: build
|
||||||
gdb: build
|
gdb: build
|
||||||
@ LANDER_DATA_DIR=data LANDER_BASE_URL=http://localhost:18080/ LANDER_API_KEY=test gdb --args ./build/Debug/lander
|
@ LANDER_DATA_DIR=data LANDER_BASE_URL=http://localhost:18080/ LANDER_API_KEY=test gdb --args ./build/Debug/lander
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: build-test
|
||||||
|
@ $(MAKE) -C '$(BUILD_DIR)/Test' test ARGS=-j$(CORES) CTEST_OUTPUT_ON_FAILURE=1
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@ rm -rf '$(BUILD_DIR)' compile_commands.json
|
@ rm -rf '$(BUILD_DIR)' compile_commands.json
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
add_compile_options(-Wno-incompatible-pointer-types)
|
||||||
|
|
||||||
|
add_executable(test_trie_fuzzy test_trie_fuzzy.c ../src/trie/trie.c)
|
||||||
|
add_test(NAME test_trie_fuzzy COMMAND test_trie_fuzzy)
|
|
@ -0,0 +1,199 @@
|
||||||
|
#ifndef AD3_FUZZYTEST
|
||||||
|
#define AD3_FUZZYTEST
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct fuzzyconfig {
|
||||||
|
int seed;
|
||||||
|
int word_length;
|
||||||
|
int word_count;
|
||||||
|
} FuzzyConfig;
|
||||||
|
|
||||||
|
void random_clean_string(char* s, int len) {
|
||||||
|
char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,?";
|
||||||
|
int charset_len = strlen(charset);
|
||||||
|
|
||||||
|
// len - 1 ensures that we can still set the null byte for the final byte
|
||||||
|
int actual_len = rand() % (len - 1);
|
||||||
|
int key;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < actual_len; i++) {
|
||||||
|
key = rand() % charset_len;
|
||||||
|
s[i] = charset[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
s[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void random_string(char* s, int len) {
|
||||||
|
// len - 1 ensures that we can still set the null byte for the final byte
|
||||||
|
int val;
|
||||||
|
|
||||||
|
for (int i = 0; i < len - 1; i++) {
|
||||||
|
val = rand();
|
||||||
|
s[i] = ((char *) &val)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just in case no null characters were created
|
||||||
|
s[len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void random_string_matrix(char** s, int count, int len) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
random_string(s[i], len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char** init_string_matrix(int count, int len) {
|
||||||
|
char** matrix = malloc(count * sizeof(char*));
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
matrix[i] = calloc(len, sizeof(char));
|
||||||
|
}
|
||||||
|
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a given trie implementation using randomly generated strings generated
|
||||||
|
* using a given seed.
|
||||||
|
*
|
||||||
|
* @param seed seed to use for generating random strings
|
||||||
|
* @param count how many strings to test with
|
||||||
|
* @param len maximum length of each string
|
||||||
|
* @param init_func function to creat a new trie of the wanted type
|
||||||
|
* @param free_func function to free the given trie
|
||||||
|
* @param add_func function to add a string to the given trie
|
||||||
|
* @param remove_func function to remove a string from the given trie
|
||||||
|
* @param size_func function to get the size of the given trie
|
||||||
|
* @return exit code describing failures, if any
|
||||||
|
*/
|
||||||
|
int fuzzy_test_trie_seed(FuzzyConfig conf, void* (*init_func) (), void (*free_func) (void*), bool (*add_func) (void*, char*), bool (*remove_func) (void*, char*), size_t (*size_func) (void*)) {
|
||||||
|
srand(conf.seed);
|
||||||
|
|
||||||
|
char** matrix = init_string_matrix(conf.word_count, conf.word_length);
|
||||||
|
random_string_matrix(matrix, conf.word_count, conf.word_length);
|
||||||
|
bool* contains = calloc(conf.word_count, sizeof(bool));
|
||||||
|
|
||||||
|
// It's possible that the string matrix contains duplicate strings
|
||||||
|
bool** contains_dedupped = calloc(conf.word_count, sizeof(bool*));
|
||||||
|
|
||||||
|
for (int i = 0; i < conf.word_count; i++) {
|
||||||
|
if (contains_dedupped[i] == NULL) {
|
||||||
|
contains_dedupped[i] = contains + i;
|
||||||
|
|
||||||
|
for (int j = i + 1; j < conf.word_count; j++) {
|
||||||
|
if (strcmp(matrix[i], matrix[j]) == 0) {
|
||||||
|
contains_dedupped[j] = contains + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We keep track of the size as well so that we can check whether this is
|
||||||
|
// also correct
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
void* ct = init_func();
|
||||||
|
|
||||||
|
bool changed;
|
||||||
|
|
||||||
|
// 0: success
|
||||||
|
// 1: invalid add
|
||||||
|
// 2: invalid remove
|
||||||
|
// 3: bad size after adds
|
||||||
|
// 4: bad size after removes
|
||||||
|
int exit_code = 0;
|
||||||
|
|
||||||
|
// Add all strings to trie, checking for duplicates
|
||||||
|
for (int i = 0; i < conf.word_count; i++) {
|
||||||
|
changed = add_func(ct, matrix[i]);
|
||||||
|
|
||||||
|
// if changed is false, *contains_dedupped[i] should be true, as changed
|
||||||
|
// can only be false if the string is already contained in the trie. if
|
||||||
|
// changed is true, *contains_dedupped[i] should be false, as the string
|
||||||
|
// cannot be in the trie yet.
|
||||||
|
if (changed == *contains_dedupped[i]) {
|
||||||
|
exit_code = 1;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*contains_dedupped[i]) {
|
||||||
|
*contains_dedupped[i] = true;
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure size is correct
|
||||||
|
if (size_func(ct) != size) {
|
||||||
|
printf("%i %i\n", size_func(ct), size);
|
||||||
|
exit_code = 3;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all strings again, again taking duplicates into consideration
|
||||||
|
for (int i = 0; i < conf.word_count; i++) {
|
||||||
|
changed = remove_func(ct, matrix[i]);
|
||||||
|
|
||||||
|
// The string shouldn't be in the trie, yet another add operation
|
||||||
|
// says it added it as well
|
||||||
|
if (changed != *contains_dedupped[i]) {
|
||||||
|
exit_code = 2;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*contains_dedupped[i]) {
|
||||||
|
*contains_dedupped[i] = false;
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, check that the trie is completely empty
|
||||||
|
if (size_func(ct) != 0) {
|
||||||
|
exit_code = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
END:
|
||||||
|
free_func(ct);
|
||||||
|
|
||||||
|
// Even testing functions should properly free memory
|
||||||
|
free(contains);
|
||||||
|
free(contains_dedupped);
|
||||||
|
|
||||||
|
for (int i = 0; i < conf.word_count; i++) {
|
||||||
|
free(matrix[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(matrix);
|
||||||
|
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as fuzzy_test_trie_seed, except that the seed is randomly generated.
|
||||||
|
*
|
||||||
|
* @param count how many strings to test with
|
||||||
|
* @param len maximum length of each string
|
||||||
|
* @param init_func function to creat a new trie of the wanted type
|
||||||
|
* @param free_func function to free the given trie
|
||||||
|
* @param add_func function to add a string to the given trie
|
||||||
|
* @param remove_func function to remove a string from the given trie
|
||||||
|
* @param size_func function to get the size of the given trie
|
||||||
|
* @return the generated seed if the test wasn't successful, -1 otherwise.
|
||||||
|
*/
|
||||||
|
/* int fuzzy_test_trie(int count, int len, void* (*init_func) (), void (*free_func) (void*), bool (*add_func) (void*, char*), bool (*remove_func) (void*, char*), int (*size_func) (void*)) { */
|
||||||
|
/* int seed = rand(); */
|
||||||
|
/* bool succeeded = fuzzy_test_trie_seed(seed, count, len, init_func, free_func, add_func, remove_func, size_func); */
|
||||||
|
|
||||||
|
/* if (!succeeded) { */
|
||||||
|
/* return seed; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* return -1; */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,36 @@
|
||||||
|
#include "test.h"
|
||||||
|
#include "trie.h"
|
||||||
|
#include "fuzzy.h"
|
||||||
|
|
||||||
|
void test_fuzzy() {
|
||||||
|
// Randomize seed
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
FuzzyConfig config;
|
||||||
|
int counter = 0;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
for (int len = 1; len < 25; len += 5) {
|
||||||
|
for (int count = 10; count <= 500; count += 10) {
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
config.seed = rand();
|
||||||
|
config.word_length = len;
|
||||||
|
config.word_count = count;
|
||||||
|
|
||||||
|
res = fuzzy_test_trie_seed(config, trie_init,
|
||||||
|
trie_free, trie_add,
|
||||||
|
trie_remove, trie_size);
|
||||||
|
TEST_CHECK_(res == 0,
|
||||||
|
"Failed config, seed = %i, len = %i, count = %i, code = %i", config.seed, config.word_length, config.word_count, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_MSG("fuzzy tests done = %i", counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_LIST = {
|
||||||
|
{ "customtrie fuzzy", test_fuzzy },
|
||||||
|
{ NULL, NULL}
|
||||||
|
};
|
Loading…
Reference in New Issue