libvirt/tests/virhashtest.c
Michal Privoznik 7c9a2d88cd virutil: Move string related functions to virstring.c
The source code base needs to be adapted as well. Some files
include virutil.h just for the string related functions (here,
the include is substituted to match the new file), some include
virutil.h without any need (here, the include is removed), and
some require both.
2013-05-02 16:56:55 +02:00

704 lines
18 KiB
C

#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 "viralloc.h"
#include "virlog.h"
#include "virstring.h"
#define testError(...) \
do { \
char *str; \
if (virAsprintf(&str, __VA_ARGS__) == 0) { \
fprintf(stderr, "%s", str); \
VIR_FREE(str); \
} \
/* 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()) {
VIR_WARN("hash grown from %zd to %zd",
(size_t)oldsize, (size_t)virHashTableSize(hash));
}
}
for (i = 0; i < ARRAY_CARDINALITY(uuids); i++) {
if (!virHashLookup(hash, uuids[i])) {
if (virTestGetVerbose()) {
VIR_WARN("\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 %zu instead of %zu elements\n",
(size_t)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 %zu\n",
count, (size_t)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)