diff options
Diffstat (limited to 'tests/virhashtest.c')
-rw-r--r-- | tests/virhashtest.c | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/tests/virhashtest.c b/tests/virhashtest.c new file mode 100644 index 000000000..ba0cf02ff --- /dev/null +++ b/tests/virhashtest.c @@ -0,0 +1,698 @@ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "internal.h" +#include "virhash.h" +#include "virhashdata.h" +#include "testutils.h" +#include "memory.h" + + +#define testError(...) \ + do { \ + fprintf(stderr, __VA_ARGS__); \ + /* Pad to line up with test name ... in virTestRun */ \ + fprintf(stderr, "%74s", "... "); \ + } while (0) + + +static virHashTablePtr +testHashInit(int size) +{ + virHashTablePtr hash; + ssize_t i; + + if (!(hash = virHashCreate(size, NULL))) + return NULL; + + /* entires are added in reverse order so that they will be linked in + * collision list in the same order as in the uuids array + */ + for (i = ARRAY_CARDINALITY(uuids) - 1; i >= 0; i--) { + ssize_t oldsize = virHashTableSize(hash); + if (virHashAddEntry(hash, uuids[i], (void *) uuids[i]) < 0) { + virHashFree(hash); + return NULL; + } + + if (virHashTableSize(hash) != oldsize && virTestGetDebug()) { + fprintf(stderr, "\nhash grown from %zd to %zd", + oldsize, virHashTableSize(hash)); + } + } + + for (i = 0; i < ARRAY_CARDINALITY(uuids); i++) { + if (!virHashLookup(hash, uuids[i])) { + if (virTestGetVerbose()) { + fprintf(stderr, "\nentry \"%s\" could not be found\n", + uuids[i]); + } + virHashFree(hash); + return NULL; + } + } + + if (size && size != virHashTableSize(hash) && virTestGetDebug()) + fprintf(stderr, "\n"); + + return hash; +} + +static void +testHashCheckForEachCount(void *payload ATTRIBUTE_UNUSED, + const void *name ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ +} + +static int +testHashCheckCount(virHashTablePtr hash, size_t count) +{ + ssize_t iter_count = 0; + + if (virHashSize(hash) != count) { + testError("\nhash contains %zd instead of %zu elements\n", + virHashSize(hash), count); + return -1; + } + + iter_count = virHashForEach(hash, testHashCheckForEachCount, NULL); + if (count != iter_count) { + testError("\nhash claims to have %zu elements but iteration finds %zd\n", + count, iter_count); + return -1; + } + + return 0; +} + + +struct testInfo { + void *data; + size_t count; +}; + + +static int +testHashGrow(const void *data) +{ + const struct testInfo *info = data; + virHashTablePtr hash; + int ret = -1; + + if (!(hash = testHashInit(info->count))) + return -1; + + if (testHashCheckCount(hash, ARRAY_CARDINALITY(uuids)) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +static int +testHashUpdate(const void *data ATTRIBUTE_UNUSED) +{ + int count = ARRAY_CARDINALITY(uuids) + ARRAY_CARDINALITY(uuids_new); + virHashTablePtr hash; + int i; + int ret = -1; + + if (!(hash = testHashInit(0))) + return -1; + + for (i = 0; i < ARRAY_CARDINALITY(uuids_subset); i++) { + if (virHashUpdateEntry(hash, uuids_subset[i], (void *) 1) < 0) { + if (virTestGetVerbose()) { + fprintf(stderr, "\nentry \"%s\" could not be updated\n", + uuids_subset[i]); + } + goto cleanup; + } + } + + for (i = 0; i < ARRAY_CARDINALITY(uuids_new); i++) { + if (virHashUpdateEntry(hash, uuids_new[i], (void *) 1) < 0) { + if (virTestGetVerbose()) { + fprintf(stderr, "\nnew entry \"%s\" could not be updated\n", + uuids_new[i]); + } + goto cleanup; + } + } + + if (testHashCheckCount(hash, count) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +static int +testHashRemove(const void *data ATTRIBUTE_UNUSED) +{ + int count = ARRAY_CARDINALITY(uuids) - ARRAY_CARDINALITY(uuids_subset); + virHashTablePtr hash; + int i; + int ret = -1; + + if (!(hash = testHashInit(0))) + return -1; + + for (i = 0; i < ARRAY_CARDINALITY(uuids_subset); i++) { + if (virHashRemoveEntry(hash, uuids_subset[i]) < 0) { + if (virTestGetVerbose()) { + fprintf(stderr, "\nentry \"%s\" could not be removed\n", + uuids_subset[i]); + } + goto cleanup; + } + } + + if (testHashCheckCount(hash, count) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +const int testHashCountRemoveForEachSome = + ARRAY_CARDINALITY(uuids) - ARRAY_CARDINALITY(uuids_subset); + +static void +testHashRemoveForEachSome(void *payload ATTRIBUTE_UNUSED, + const void *name, + void *data) +{ + virHashTablePtr hash = data; + int i; + + for (i = 0; i < ARRAY_CARDINALITY(uuids_subset); i++) { + if (STREQ(uuids_subset[i], name)) { + if (virHashRemoveEntry(hash, name) < 0 && virTestGetVerbose()) { + fprintf(stderr, "\nentry \"%s\" could not be removed", + uuids_subset[i]); + } + break; + } + } +} + + +const int testHashCountRemoveForEachAll = 0; + +static void +testHashRemoveForEachAll(void *payload ATTRIBUTE_UNUSED, + const void *name, + void *data) +{ + virHashTablePtr hash = data; + + virHashRemoveEntry(hash, name); +} + + +const int testHashCountRemoveForEachForbidden = ARRAY_CARDINALITY(uuids); + +static void +testHashRemoveForEachForbidden(void *payload ATTRIBUTE_UNUSED, + const void *name, + void *data) +{ + virHashTablePtr hash = data; + int i; + + for (i = 0; i < ARRAY_CARDINALITY(uuids_subset); i++) { + if (STREQ(uuids_subset[i], name)) { + int next = (i + 1) % ARRAY_CARDINALITY(uuids_subset); + + if (virHashRemoveEntry(hash, uuids_subset[next]) == 0 && + virTestGetVerbose()) { + fprintf(stderr, + "\nentry \"%s\" should not be allowed to be removed", + uuids_subset[next]); + } + break; + } + } +} + + +static int +testHashRemoveForEach(const void *data) +{ + const struct testInfo *info = data; + virHashTablePtr hash; + int count; + int ret = -1; + + if (!(hash = testHashInit(0))) + return -1; + + count = virHashForEach(hash, (virHashIterator) info->data, hash); + + if (count != ARRAY_CARDINALITY(uuids)) { + if (virTestGetVerbose()) { + testError("\nvirHashForEach didn't go through all entries," + " %d != %zu\n", + count, ARRAY_CARDINALITY(uuids)); + } + goto cleanup; + } + + if (testHashCheckCount(hash, info->count) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +static int +testHashSteal(const void *data ATTRIBUTE_UNUSED) +{ + int count = ARRAY_CARDINALITY(uuids) - ARRAY_CARDINALITY(uuids_subset); + virHashTablePtr hash; + int i; + int ret = -1; + + if (!(hash = testHashInit(0))) + return -1; + + for (i = 0; i < ARRAY_CARDINALITY(uuids_subset); i++) { + if (!virHashSteal(hash, uuids_subset[i])) { + if (virTestGetVerbose()) { + fprintf(stderr, "\nentry \"%s\" could not be stolen\n", + uuids_subset[i]); + } + goto cleanup; + } + } + + if (testHashCheckCount(hash, count) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +static void +testHashIter(void *payload ATTRIBUTE_UNUSED, + const void *name ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + return; +} + +static void +testHashForEachIter(void *payload ATTRIBUTE_UNUSED, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + virHashTablePtr hash = data; + + if (virHashAddEntry(hash, uuids_new[0], NULL) == 0 && + virTestGetVerbose()) { + fprintf(stderr, "\nadding entries in ForEach should be forbidden"); + } + + if (virHashUpdateEntry(hash, uuids_new[0], NULL) == 0 && + virTestGetVerbose()) { + fprintf(stderr, "\nupdating entries in ForEach should be forbidden"); + } + + if (virHashSteal(hash, uuids_new[0]) != NULL && + virTestGetVerbose()) { + fprintf(stderr, "\nstealing entries in ForEach should be forbidden"); + } + + if (virHashSteal(hash, uuids_new[0]) != NULL && + virTestGetVerbose()) { + fprintf(stderr, "\nstealing entries in ForEach should be forbidden"); + } + + if (virHashForEach(hash, testHashIter, NULL) >= 0 && + virTestGetVerbose()) { + fprintf(stderr, "\niterating through hash in ForEach" + " should be forbidden"); + } +} + +static int +testHashForEach(const void *data ATTRIBUTE_UNUSED) +{ + virHashTablePtr hash; + int count; + int ret = -1; + + if (!(hash = testHashInit(0))) + return -1; + + count = virHashForEach(hash, testHashForEachIter, hash); + + if (count != ARRAY_CARDINALITY(uuids)) { + if (virTestGetVerbose()) { + testError("\nvirHashForEach didn't go through all entries," + " %d != %zu\n", + count, ARRAY_CARDINALITY(uuids)); + } + goto cleanup; + } + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +static int +testHashRemoveSetIter(const void *payload ATTRIBUTE_UNUSED, + const void *name, + const void *data) +{ + int *count = (int *) data; + bool rem = false; + int i; + + for (i = 0; i < ARRAY_CARDINALITY(uuids_subset); i++) { + if (STREQ(uuids_subset[i], name)) { + rem = true; + break; + } + } + + if (rem || rand() % 2) { + (*count)++; + return 1; + } else { + return 0; + } +} + +static int +testHashRemoveSet(const void *data ATTRIBUTE_UNUSED) +{ + virHashTablePtr hash; + int count = 0; + int rcount; + int ret = -1; + + if (!(hash = testHashInit(0))) + return -1; + + /* seed the generator so that rand() provides reproducible sequence */ + srand(9000); + + rcount = virHashRemoveSet(hash, testHashRemoveSetIter, &count); + + if (count != rcount) { + if (virTestGetVerbose()) { + testError("\nvirHashRemoveSet didn't remove expected number of" + " entries, %d != %u\n", + rcount, count); + } + goto cleanup; + } + + if (testHashCheckCount(hash, ARRAY_CARDINALITY(uuids) - count) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +const int testSearchIndex = ARRAY_CARDINALITY(uuids_subset) / 2; + +static int +testHashSearchIter(const void *payload ATTRIBUTE_UNUSED, + const void *name, + const void *data ATTRIBUTE_UNUSED) +{ + return STREQ(uuids_subset[testSearchIndex], name); +} + +static int +testHashSearch(const void *data ATTRIBUTE_UNUSED) +{ + virHashTablePtr hash; + void *entry; + int ret = -1; + + if (!(hash = testHashInit(0))) + return -1; + + entry = virHashSearch(hash, testHashSearchIter, NULL); + + if (!entry || STRNEQ(uuids_subset[testSearchIndex], entry)) { + if (virTestGetVerbose()) { + testError("\nvirHashSearch didn't find entry '%s'\n", + uuids_subset[testSearchIndex]); + } + goto cleanup; + } + + if (testHashCheckCount(hash, ARRAY_CARDINALITY(uuids)) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virHashFree(hash); + return ret; +} + + +static int +testHashGetItemsCompKey(const virHashKeyValuePairPtr a, + const virHashKeyValuePairPtr b) +{ + return strcmp (a->key, b->key); +} + +static int +testHashGetItemsCompValue(const virHashKeyValuePairPtr a, + const virHashKeyValuePairPtr b) +{ + return strcmp (a->value, b->value); +} + +static int +testHashGetItems(const void *data ATTRIBUTE_UNUSED) +{ + virHashTablePtr hash; + virHashKeyValuePairPtr array = NULL; + int ret = -1; + char keya[] = "a"; + char keyb[] = "b"; + char keyc[] = "c"; + char value1[] = "1"; + char value2[] = "2"; + char value3[] = "3"; + + if (!(hash = virHashCreate(0, NULL)) || + virHashAddEntry(hash, keya, value3) < 0 || + virHashAddEntry(hash, keyc, value1) < 0 || + virHashAddEntry(hash, keyb, value2) < 0) { + if (virTestGetVerbose()) { + testError("\nfailed to create hash"); + } + goto cleanup; + } + + if (!(array = virHashGetItems(hash, NULL)) || + array[3].key || array[3].value) { + if (virTestGetVerbose()) { + testError("\nfailed to get items with NULL sort"); + } + goto cleanup; + } + VIR_FREE(array); + + if (!(array = virHashGetItems(hash, testHashGetItemsCompKey)) || + STRNEQ(array[0].key, "a") || + STRNEQ(array[0].value, "3") || + STRNEQ(array[1].key, "b") || + STRNEQ(array[1].value, "2") || + STRNEQ(array[2].key, "c") || + STRNEQ(array[2].value, "1") || + array[3].key || array[3].value) { + if (virTestGetVerbose()) { + testError("\nfailed to get items with key sort"); + } + goto cleanup; + } + VIR_FREE(array); + + if (!(array = virHashGetItems(hash, testHashGetItemsCompValue)) || + STRNEQ(array[0].key, "c") || + STRNEQ(array[0].value, "1") || + STRNEQ(array[1].key, "b") || + STRNEQ(array[1].value, "2") || + STRNEQ(array[2].key, "a") || + STRNEQ(array[2].value, "3") || + array[3].key || array[3].value) { + if (virTestGetVerbose()) { + testError("\nfailed to get items with value sort"); + } + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(array); + virHashFree(hash); + return ret; +} + +static int +testHashEqualCompValue(const void *value1, const void *value2) +{ + return c_strcasecmp(value1, value2); +} + +static int +testHashEqual(const void *data ATTRIBUTE_UNUSED) +{ + virHashTablePtr hash1, hash2 = NULL; + int ret = -1; + char keya[] = "a"; + char keyb[] = "b"; + char keyc[] = "c"; + char value1_l[] = "m"; + char value2_l[] = "n"; + char value3_l[] = "o"; + char value1_u[] = "M"; + char value2_u[] = "N"; + char value3_u[] = "O"; + char value4_u[] = "P"; + + if (!(hash1 = virHashCreate(0, NULL)) || + !(hash2 = virHashCreate(0, NULL)) || + virHashAddEntry(hash1, keya, value1_l) < 0 || + virHashAddEntry(hash1, keyb, value2_l) < 0 || + virHashAddEntry(hash1, keyc, value3_l) < 0 || + virHashAddEntry(hash2, keya, value1_u) < 0 || + virHashAddEntry(hash2, keyb, value2_u) < 0) { + if (virTestGetVerbose()) { + testError("\nfailed to create hashes"); + } + goto cleanup; + } + + if (virHashEqual(hash1, hash2, testHashEqualCompValue)) { + if (virTestGetVerbose()) { + testError("\nfailed equal test for different number of elements"); + } + goto cleanup; + } + + if (virHashAddEntry(hash2, keyc, value4_u) < 0) { + if (virTestGetVerbose()) { + testError("\nfailed to add element to hash2"); + } + goto cleanup; + } + + if (virHashEqual(hash1, hash2, testHashEqualCompValue)) { + if (virTestGetVerbose()) { + testError("\nfailed equal test for same number of elements"); + } + goto cleanup; + } + + if (virHashUpdateEntry(hash2, keyc, value3_u) < 0) { + if (virTestGetVerbose()) { + testError("\nfailed to update element in hash2"); + } + goto cleanup; + } + + if (!virHashEqual(hash1, hash2, testHashEqualCompValue)) { + if (virTestGetVerbose()) { + testError("\nfailed equal test for equal hash tables"); + } + goto cleanup; + } + + ret = 0; + +cleanup: + virHashFree(hash1); + virHashFree(hash2); + return ret; +} + + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST_FULL(name, cmd, data, count) \ + do { \ + struct testInfo info = { data, count }; \ + if (virtTestRun(name, 1, testHash ## cmd, &info) < 0) \ + ret = -1; \ + } while (0) + +#define DO_TEST_DATA(name, cmd, data) \ + DO_TEST_FULL(name "(" #data ")", \ + cmd, \ + testHash ## cmd ## data, \ + testHashCount ## cmd ## data) + +#define DO_TEST_COUNT(name, cmd, count) \ + DO_TEST_FULL(name "(" #count ")", cmd, NULL, count) + +#define DO_TEST(name, cmd) \ + DO_TEST_FULL(name, cmd, NULL, -1) + + DO_TEST_COUNT("Grow", Grow, 1); + DO_TEST_COUNT("Grow", Grow, 10); + DO_TEST_COUNT("Grow", Grow, 42); + DO_TEST("Update", Update); + DO_TEST("Remove", Remove); + DO_TEST_DATA("Remove in ForEach", RemoveForEach, Some); + DO_TEST_DATA("Remove in ForEach", RemoveForEach, All); + DO_TEST_DATA("Remove in ForEach", RemoveForEach, Forbidden); + DO_TEST("Steal", Steal); + DO_TEST("Forbidden ops in ForEach", ForEach); + DO_TEST("RemoveSet", RemoveSet); + DO_TEST("Search", Search); + DO_TEST("GetItems", GetItems); + DO_TEST("Equal", Equal); + + return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain) |