blob: 85889559bcb5bad2f41bfe6020c88f7f45485ee3 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include "./pn_test.hpp"
#include <proton/object.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using Catch::Matchers::Equals;
static char mem;
static void *END = &mem;
static pn_list_t *build_list(size_t capacity, ...) {
pn_list_t *result = pn_list(PN_OBJECT, capacity);
va_list ap;
va_start(ap, capacity);
while (true) {
void *arg = va_arg(ap, void *);
if (arg == END) {
break;
}
pn_list_add(result, arg);
pn_class_decref(PN_OBJECT, arg);
}
va_end(ap);
return result;
}
static pn_map_t *build_map(float load_factor, size_t capacity, ...) {
pn_map_t *result = pn_map(PN_OBJECT, PN_OBJECT, capacity, load_factor);
va_list ap;
void *prev = NULL;
va_start(ap, capacity);
int count = 0;
while (true) {
void *arg = va_arg(ap, void *);
bool last = arg == END;
if (arg == END) {
arg = NULL;
}
if (count % 2) {
pn_map_put(result, prev, arg);
pn_class_decref(PN_OBJECT, prev);
pn_class_decref(PN_OBJECT, arg);
} else {
prev = arg;
}
if (last) {
break;
}
count++;
}
va_end(ap);
return result;
}
static void noop(void *o) {}
static uintptr_t zero(void *o) { return 0; }
static intptr_t delta(void *a, void *b) { return (uintptr_t)b - (uintptr_t)a; }
#define CID_noop CID_pn_object
#define noop_initialize noop
#define noop_finalize noop
#define noop_hashcode zero
#define noop_compare delta
#define noop_inspect NULL
static const pn_class_t noop_class = PN_CLASS(noop);
static void test_class(const pn_class_t *clazz, size_t size) {
INFO("class=" << pn_class_name(clazz) << " size=" << size);
void *a = pn_class_new(clazz, size);
void *b = pn_class_new(clazz, size);
CHECK(!pn_class_equals(clazz, a, b));
CHECK(pn_class_equals(clazz, a, a));
CHECK(pn_class_equals(clazz, b, b));
CHECK(!pn_class_equals(clazz, a, NULL));
CHECK(!pn_class_equals(clazz, NULL, a));
int rca = pn_class_refcount(clazz, a);
int rcb = pn_class_refcount(clazz, b);
CHECK((rca == -1 || rca == 1));
CHECK((rcb == -1 || rcb == 1));
pn_class_incref(clazz, a);
rca = pn_class_refcount(clazz, a);
CHECK((rca == -1 || rca == 2));
pn_class_decref(clazz, a);
rca = pn_class_refcount(clazz, a);
CHECK((rca == -1 || rca == 1));
pn_class_free(clazz, a);
pn_class_free(clazz, b);
}
TEST_CASE("object_class") {
test_class(PN_OBJECT, 0);
test_class(PN_VOID, 5);
test_class(&noop_class, 128);
}
static void test_new(size_t size, const pn_class_t *clazz) {
INFO("class=" << pn_class_name(clazz) << " size=" << size);
void *obj = pn_class_new(clazz, size);
REQUIRE(obj);
CHECK(pn_class_refcount(PN_OBJECT, obj) == 1);
CHECK(pn_class(obj) == clazz);
char *bytes = (char *)obj;
for (size_t i = 0; i < size; i++) {
// touch everything for valgrind
bytes[i] = i;
}
pn_free(obj);
}
TEST_CASE("object_class new") {
test_new(0, PN_OBJECT);
test_new(5, PN_OBJECT);
test_new(128, &noop_class);
}
static void finalizer(void *object) {
int **called = (int **)object;
(**called)++;
}
#define CID_finalizer CID_pn_object
#define finalizer_initialize NULL
#define finalizer_finalize finalizer
#define finalizer_hashcode NULL
#define finalizer_compare NULL
#define finalizer_inspect NULL
TEST_CASE("object_finalize") {
static pn_class_t clazz = PN_CLASS(finalizer);
int **obj = (int **)pn_class_new(&clazz, sizeof(int *));
REQUIRE(obj);
int called = 0;
*obj = &called;
pn_free(obj);
CHECK(called == 1);
}
TEST_CASE("object_free") {
// just to make sure it doesn't seg fault or anything
pn_free(NULL);
}
static uintptr_t hashcode(void *obj) { return (uintptr_t)obj; }
#define CID_hashcode CID_pn_object
#define hashcode_initialize NULL
#define hashcode_finalize NULL
#define hashcode_compare NULL
#define hashcode_hashcode hashcode
#define hashcode_inspect NULL
TEST_CASE("object_hashcode") {
static pn_class_t clazz = PN_CLASS(hashcode);
void *obj = pn_class_new(&clazz, 0);
REQUIRE(obj);
CHECK(pn_hashcode(obj) == (uintptr_t)obj);
CHECK(pn_hashcode(NULL) == 0);
pn_free(obj);
}
#define CID_compare CID_pn_object
#define compare_initialize NULL
#define compare_finalize NULL
#define compare_compare delta
#define compare_hashcode NULL
#define compare_inspect NULL
TEST_CASE("object_compare") {
static pn_class_t clazz = PN_CLASS(compare);
void *a = pn_class_new(&clazz, 0);
REQUIRE(a);
void *b = pn_class_new(&clazz, 0);
REQUIRE(b);
CHECK(pn_compare(a, b));
CHECK(!pn_equals(a, b));
CHECK(!pn_compare(a, a));
CHECK(pn_equals(a, a));
CHECK(!pn_compare(b, b));
CHECK(pn_equals(b, b));
CHECK(pn_compare(a, b) == (intptr_t)((uintptr_t)b - (uintptr_t)a));
CHECK(pn_compare(NULL, b));
CHECK(!pn_equals(NULL, b));
CHECK(pn_compare(a, NULL));
CHECK(!pn_equals(a, NULL));
CHECK(!pn_compare(NULL, NULL));
CHECK(pn_equals(NULL, NULL));
pn_free(a);
pn_free(b);
}
TEST_CASE("object_refcounting") {
int refs = 3;
void *obj = pn_class_new(PN_OBJECT, 0);
CHECK(pn_refcount(obj) == 1);
for (int i = 0; i < refs; i++) {
pn_incref(obj);
CHECK(pn_refcount(obj) == i + 2);
}
CHECK(pn_refcount(obj) == refs + 1);
for (int i = 0; i < refs; i++) {
pn_decref(obj);
CHECK(pn_refcount(obj) == refs - i);
}
CHECK(pn_refcount(obj) == 1);
pn_free(obj);
}
TEST_CASE("list") {
pn_list_t *list = pn_list(PN_WEAKREF, 0);
CHECK(pn_list_size(list) == 0);
CHECK(!pn_list_add(list, (void *)0));
CHECK(!pn_list_add(list, (void *)1));
CHECK(!pn_list_add(list, (void *)2));
CHECK(!pn_list_add(list, (void *)3));
CHECK(pn_list_get(list, 0) == (void *)0);
CHECK(pn_list_get(list, 1) == (void *)1);
CHECK(pn_list_get(list, 2) == (void *)2);
CHECK(pn_list_get(list, 3) == (void *)3);
CHECK(pn_list_size(list) == 4);
pn_list_del(list, 1, 2);
CHECK(pn_list_size(list) == 2);
CHECK(pn_list_get(list, 0) == (void *)0);
CHECK(pn_list_get(list, 1) == (void *)3);
pn_decref(list);
}
TEST_CASE("list_refcount") {
void *one = pn_class_new(PN_OBJECT, 0);
void *two = pn_class_new(PN_OBJECT, 0);
void *three = pn_class_new(PN_OBJECT, 0);
void *four = pn_class_new(PN_OBJECT, 0);
pn_list_t *list = pn_list(PN_OBJECT, 0);
CHECK(!pn_list_add(list, one));
CHECK(!pn_list_add(list, two));
CHECK(!pn_list_add(list, three));
CHECK(!pn_list_add(list, four));
CHECK(pn_list_get(list, 0) == one);
CHECK(pn_list_get(list, 1) == two);
CHECK(pn_list_get(list, 2) == three);
CHECK(pn_list_get(list, 3) == four);
CHECK(pn_list_size(list) == 4);
CHECK(pn_refcount(one) == 2);
CHECK(pn_refcount(two) == 2);
CHECK(pn_refcount(three) == 2);
CHECK(pn_refcount(four) == 2);
pn_list_del(list, 1, 2);
CHECK(pn_list_size(list) == 2);
CHECK(pn_refcount(one) == 2);
CHECK(pn_refcount(two) == 1);
CHECK(pn_refcount(three) == 1);
CHECK(pn_refcount(four) == 2);
CHECK(pn_list_get(list, 0) == one);
CHECK(pn_list_get(list, 1) == four);
CHECK(!pn_list_add(list, one));
CHECK(pn_list_size(list) == 3);
CHECK(pn_refcount(one) == 3);
pn_decref(list);
CHECK(pn_refcount(one) == 1);
CHECK(pn_refcount(two) == 1);
CHECK(pn_refcount(three) == 1);
CHECK(pn_refcount(four) == 1);
pn_decref(one);
pn_decref(two);
pn_decref(three);
pn_decref(four);
}
#define check_list_index(list, value, idx) \
CHECK(pn_list_index(list, value) == idx)
TEST_CASE("list_index") {
pn_list_t *l = pn_list(PN_WEAKREF, 0);
void *one = pn_string("one");
void *two = pn_string("two");
void *three = pn_string("three");
void *dup1 = pn_string("dup");
void *dup2 = pn_string("dup");
void *last = pn_string("last");
pn_list_add(l, one);
pn_list_add(l, two);
pn_list_add(l, three);
pn_list_add(l, dup1);
pn_list_add(l, dup2);
pn_list_add(l, last);
check_list_index(l, one, 0);
check_list_index(l, two, 1);
check_list_index(l, three, 2);
check_list_index(l, dup1, 3);
check_list_index(l, dup2, 3);
check_list_index(l, last, 5);
void *nonexistent = pn_string("nonexistent");
check_list_index(l, nonexistent, -1);
pn_free(l);
pn_free(one);
pn_free(two);
pn_free(three);
pn_free(dup1);
pn_free(dup2);
pn_free(last);
pn_free(nonexistent);
}
TEST_CASE("list_build") {
pn_list_t *l = build_list(0, pn_string("one"), pn_string("two"),
pn_string("three"), END);
REQUIRE(pn_list_size(l) == 3);
CHECK_THAT(pn_string_get((pn_string_t *)pn_list_get(l, 0)), Equals("one"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_list_get(l, 1)), Equals("two"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_list_get(l, 2)), Equals("three"));
pn_free(l);
}
TEST_CASE("map_build") {
pn_map_t *m = build_map(0.75, 0, pn_string("key"), pn_string("value"),
pn_string("key2"), pn_string("value2"), END);
CHECK(pn_map_size(m) == 2);
pn_string_t *key = pn_string(NULL);
pn_string_set(key, "key");
CHECK_THAT(pn_string_get((pn_string_t *)pn_map_get(m, key)), Equals("value"));
pn_string_set(key, "key2");
CHECK_THAT(pn_string_get((pn_string_t *)pn_map_get(m, key)),
Equals("value2"));
pn_free(m);
pn_free(key);
}
TEST_CASE("map_build_odd") {
pn_map_t *m =
build_map(0.75, 0, pn_string("key"), pn_string("value"),
pn_string("key2"), pn_string("value2"), pn_string("key3"), END);
CHECK(pn_map_size(m) == 3);
pn_string_t *key = pn_string(NULL);
pn_string_set(key, "key");
CHECK_THAT(pn_string_get((pn_string_t *)pn_map_get(m, key)), Equals("value"));
pn_string_set(key, "key2");
CHECK_THAT(pn_string_get((pn_string_t *)pn_map_get(m, key)),
Equals("value2"));
pn_string_set(key, "key3");
CHECK(pn_map_get(m, key) == NULL);
pn_free(m);
pn_free(key);
}
TEST_CASE("map") {
void *one = pn_class_new(PN_OBJECT, 0);
void *two = pn_class_new(PN_OBJECT, 0);
void *three = pn_class_new(PN_OBJECT, 0);
pn_map_t *map = pn_map(PN_OBJECT, PN_OBJECT, 4, 0.75);
CHECK(pn_map_size(map) == 0);
pn_string_t *key = pn_string("key");
pn_string_t *dup = pn_string("key");
pn_string_t *key1 = pn_string("key1");
pn_string_t *key2 = pn_string("key2");
CHECK(!pn_map_put(map, key, one));
CHECK(pn_map_size(map) == 1);
CHECK(!pn_map_put(map, key1, two));
CHECK(pn_map_size(map) == 2);
CHECK(!pn_map_put(map, key2, three));
CHECK(pn_map_size(map) == 3);
CHECK(pn_map_get(map, dup) == one);
CHECK(!pn_map_put(map, dup, one));
CHECK(pn_map_size(map) == 3);
CHECK(!pn_map_put(map, dup, two));
CHECK(pn_map_size(map) == 3);
CHECK(pn_map_get(map, dup) == two);
CHECK(pn_refcount(key) == 2);
CHECK(pn_refcount(dup) == 1);
CHECK(pn_refcount(key1) == 2);
CHECK(pn_refcount(key2) == 2);
CHECK(pn_refcount(one) == 1);
CHECK(pn_refcount(two) == 3);
CHECK(pn_refcount(three) == 2);
pn_map_del(map, key1);
CHECK(pn_map_size(map) == 2);
CHECK(pn_refcount(key) == 2);
CHECK(pn_refcount(dup) == 1);
CHECK(pn_refcount(key1) == 1);
CHECK(pn_refcount(key2) == 2);
CHECK(pn_refcount(one) == 1);
CHECK(pn_refcount(two) == 2);
CHECK(pn_refcount(three) == 2);
pn_decref(one);
pn_decref(two);
pn_decref(three);
pn_decref(key);
pn_decref(dup);
pn_decref(key1);
pn_decref(key2);
pn_decref(map);
}
TEST_CASE("hash") {
void *one = pn_class_new(PN_OBJECT, 0);
void *two = pn_class_new(PN_OBJECT, 0);
void *three = pn_class_new(PN_OBJECT, 0);
pn_hash_t *hash = pn_hash(PN_OBJECT, 4, 0.75);
pn_hash_put(hash, 0, NULL);
pn_hash_put(hash, 1, one);
pn_hash_put(hash, 2, two);
pn_hash_put(hash, 3, three);
pn_hash_put(hash, 4, one);
pn_hash_put(hash, 5, two);
pn_hash_put(hash, 6, three);
pn_hash_put(hash, 7, one);
pn_hash_put(hash, 8, two);
pn_hash_put(hash, 9, three);
pn_hash_put(hash, 10, one);
pn_hash_put(hash, 11, two);
pn_hash_put(hash, 12, three);
pn_hash_put(hash, 18, one);
CHECK(pn_hash_get(hash, 2) == two);
CHECK(pn_hash_get(hash, 5) == two);
CHECK(pn_hash_get(hash, 18) == one);
CHECK(pn_hash_get(hash, 0) == NULL);
CHECK(pn_hash_size(hash) == 14);
pn_hash_del(hash, 5);
CHECK(pn_hash_get(hash, 5) == NULL);
CHECK(pn_hash_size(hash) == 13);
pn_hash_del(hash, 18);
CHECK(pn_hash_get(hash, 18) == NULL);
CHECK(pn_hash_size(hash) == 12);
pn_decref(hash);
pn_decref(one);
pn_decref(two);
pn_decref(three);
}
// collider class: all objects have same hash, no two objects compare equal
static intptr_t collider_compare(void *a, void *b) {
if (a == b) return 0;
return (a > b) ? 1 : -1;
}
static uintptr_t collider_hashcode(void *obj) { return 23; }
#define CID_collider CID_pn_object
#define collider_initialize NULL
#define collider_finalize NULL
#define collider_inspect NULL
TEST_CASE("map_links") {
const pn_class_t collider_clazz = PN_CLASS(collider);
void *keys[3];
for (int i = 0; i < 3; i++) keys[i] = pn_class_new(&collider_clazz, 0);
// test deleting a head, middle link, tail
for (int delete_idx = 0; delete_idx < 3; delete_idx++) {
pn_map_t *map = pn_map(PN_WEAKREF, PN_WEAKREF, 0, 0.75);
// create a chain of entries that have same head (from identical key
// hashcode)
for (int i = 0; i < 3; i++) {
pn_map_put(map, keys[i], keys[i]);
}
pn_map_del(map, keys[delete_idx]);
for (int i = 0; i < 3; i++) {
void *value = (i == delete_idx) ? NULL : keys[i];
CHECK(pn_map_get(map, keys[i]) == value);
}
pn_free(map);
}
for (int i = 0; i < 3; i++) pn_free(keys[i]);
}
static void test_string(const char *value) {
size_t size = value ? strlen(value) : 0;
pn_string_t *str = pn_string(value);
CHECK_THAT(value, Equals(pn_string_get(str)));
CHECK(size == pn_string_size(str));
pn_string_t *strn = pn_stringn(value, size);
CHECK_THAT(value, Equals(pn_string_get(strn)));
CHECK(size == pn_string_size(strn));
pn_string_t *strset = pn_string(NULL);
pn_string_set(strset, value);
CHECK_THAT(value, Equals(pn_string_get(strset)));
CHECK(size == pn_string_size(strset));
pn_string_t *strsetn = pn_string(NULL);
pn_string_setn(strsetn, value, size);
CHECK_THAT(value, Equals(pn_string_get(strsetn)));
CHECK(size == pn_string_size(strsetn));
CHECK(pn_hashcode(str) == pn_hashcode(strn));
CHECK(pn_hashcode(str) == pn_hashcode(strset));
CHECK(pn_hashcode(str) == pn_hashcode(strsetn));
CHECK(!pn_compare(str, str));
CHECK(!pn_compare(str, strn));
CHECK(!pn_compare(str, strset));
CHECK(!pn_compare(str, strsetn));
pn_free(str);
pn_free(strn);
pn_free(strset);
pn_free(strsetn);
}
TEST_CASE("string_null") { test_string(NULL); }
TEST_CASE("string_empty") { test_string(""); }
TEST_CASE("string_simple") { test_string("this is a test"); }
TEST_CASE("string_long") {
test_string(
"012345678910111213151617181920212223242526272829303132333435363"
"738394041424344454647484950515253545556575859606162636465666768");
}
TEST_CASE("string embedded null") {
const char value[] = "this has an embedded \000 in it";
size_t size = sizeof(value);
pn_string_t *strn = pn_stringn(value, size);
CHECK_THAT(value, Equals(pn_string_get(strn)));
CHECK(pn_string_size(strn) == size);
pn_string_t *strsetn = pn_string(NULL);
pn_string_setn(strsetn, value, size);
CHECK_THAT(value, Equals(pn_string_get(strsetn)));
CHECK(pn_string_size(strsetn) == size);
CHECK(pn_hashcode(strn) == pn_hashcode(strsetn));
CHECK(!pn_compare(strn, strsetn));
pn_free(strn);
pn_free(strsetn);
}
TEST_CASE("string_format") {
pn_string_t *str = pn_string("");
CHECK(str);
int err = pn_string_format(str, "%s",
"this is a string that should be long "
"enough to force growth but just in case we'll "
"tack this other really long string on for the "
"heck of it");
CHECK(err == 0);
pn_free(str);
}
TEST_CASE("string_addf") {
pn_string_t *str = pn_string("hello ");
CHECK(str);
int err = pn_string_addf(str, "%s",
"this is a string that should be long "
"enough to force growth but just in case we'll "
"tack this other really long string on for the "
"heck of it");
CHECK(err == 0);
pn_free(str);
}
TEST_CASE("map_iteration") {
int n = 5;
pn_list_t *pairs = pn_list(PN_OBJECT, 2 * n);
for (int i = 0; i < n; i++) {
void *key = pn_class_new(PN_OBJECT, 0);
void *value = pn_class_new(PN_OBJECT, 0);
pn_list_add(pairs, key);
pn_list_add(pairs, value);
pn_decref(key);
pn_decref(value);
}
pn_map_t *map = pn_map(PN_OBJECT, PN_OBJECT, 0, 0.75);
CHECK(pn_map_head(map) == 0);
for (int i = 0; i < n; i++) {
pn_map_put(map, pn_list_get(pairs, 2 * i), pn_list_get(pairs, 2 * i + 1));
}
for (pn_handle_t entry = pn_map_head(map); entry;
entry = pn_map_next(map, entry)) {
void *key = pn_map_key(map, entry);
void *value = pn_map_value(map, entry);
ssize_t idx = pn_list_index(pairs, key);
CHECK(idx >= 0);
CHECK(pn_list_get(pairs, idx) == key);
CHECK(pn_list_get(pairs, idx + 1) == value);
pn_list_del(pairs, idx, 2);
}
CHECK(pn_list_size(pairs) == 0);
pn_decref(map);
pn_decref(pairs);
}
#define test_inspect(o, expected) \
do { \
pn_string_t *dst = pn_string(NULL); \
pn_inspect(o, dst); \
CHECK_THAT(expected, Equals(pn_string_get(dst))); \
pn_free(dst); \
} while (0)
TEST_CASE("list_inspect") {
pn_list_t *l = build_list(0, END);
test_inspect(l, "[]");
pn_free(l);
l = build_list(0, pn_string("one"), END);
test_inspect(l, "[\"one\"]");
pn_free(l);
l = build_list(0, pn_string("one"), pn_string("two"), END);
test_inspect(l, "[\"one\", \"two\"]");
pn_free(l);
l = build_list(0, pn_string("one"), pn_string("two"), pn_string("three"),
END);
test_inspect(l, "[\"one\", \"two\", \"three\"]");
pn_free(l);
}
TEST_CASE("map_inspect") {
// note that when there is more than one entry in a map, the order
// of the entries is dependent on the hashes involved, it will be
// deterministic though
pn_map_t *m = build_map(0.75, 0, END);
test_inspect(m, "{}");
pn_free(m);
m = build_map(0.75, 0, pn_string("key"), pn_string("value"), END);
test_inspect(m, "{\"key\": \"value\"}");
pn_free(m);
m = build_map(0.75, 0, pn_string("k1"), pn_string("v1"), pn_string("k2"),
pn_string("v2"), END);
test_inspect(m, "{\"k1\": \"v1\", \"k2\": \"v2\"}");
pn_free(m);
m = build_map(0.75, 0, pn_string("k1"), pn_string("v1"), pn_string("k2"),
pn_string("v2"), pn_string("k3"), pn_string("v3"), END);
test_inspect(m, "{\"k3\": \"v3\", \"k1\": \"v1\", \"k2\": \"v2\"}");
pn_free(m);
}
TEST_CASE("map_coalesced_chain") {
pn_hash_t *map = pn_hash(PN_OBJECT, 16, 0.75);
pn_string_t *values[9] = {pn_string("a"), pn_string("b"), pn_string("c"),
pn_string("d"), pn_string("e"), pn_string("f"),
pn_string("g"), pn_string("h"), pn_string("i")};
// add some items:
pn_hash_put(map, 1, values[0]);
pn_hash_put(map, 2, values[1]);
pn_hash_put(map, 3, values[2]);
// use up all non-addressable elements:
pn_hash_put(map, 14, values[3]);
pn_hash_put(map, 15, values[4]);
pn_hash_put(map, 16, values[5]);
// use an addressable element for a key that doesn't map to it:
pn_hash_put(map, 4, values[6]);
pn_hash_put(map, 17, values[7]);
CHECK(pn_hash_size(map) == 8);
// free up one non-addressable entry:
pn_hash_del(map, 16);
CHECK(pn_hash_get(map, 16) == NULL);
CHECK(pn_hash_size(map) == 7);
// add a key whose addressable slot is already taken (by 17),
// generating a coalesced chain:
pn_hash_put(map, 12, values[8]);
// remove an entry from the coalesced chain:
pn_hash_del(map, 4);
CHECK(pn_hash_get(map, 4) == NULL);
// test lookup of all entries:
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 1)), Equals("a"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 2)), Equals("b"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 3)), Equals("c"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 14)), Equals("d"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 15)), Equals("e"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 17)), Equals("h"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 12)), Equals("i"));
CHECK(pn_hash_size(map) == 7);
// cleanup:
for (pn_handle_t i = pn_hash_head(map); i; i = pn_hash_head(map)) {
pn_hash_del(map, pn_hash_key(map, i));
}
CHECK(pn_hash_size(map) == 0);
for (size_t i = 0; i < 9; ++i) {
pn_free(values[i]);
}
pn_free(map);
}
TEST_CASE("map_coalesced_chain2") {
pn_hash_t *map = pn_hash(PN_OBJECT, 16, 0.75);
pn_string_t *values[10] = {pn_string("a"), pn_string("b"), pn_string("c"),
pn_string("d"), pn_string("e"), pn_string("f"),
pn_string("g"), pn_string("h"), pn_string("i"),
pn_string("j")};
// add some items:
pn_hash_put(map, 1, values[0]); // a
pn_hash_put(map, 2, values[1]); // b
pn_hash_put(map, 3, values[2]); // c
// use up all non-addressable elements:
pn_hash_put(map, 14, values[3]); // d
pn_hash_put(map, 15, values[4]); // e
pn_hash_put(map, 16, values[5]); // f
// take slot from addressable region
pn_hash_put(map, 29, values[6]); // g, goes into slot 12
// free up one non-addressable entry:
pn_hash_del(map, 14);
CHECK(pn_hash_get(map, 14) == NULL);
// add a key whose addressable slot is already taken (by 29),
// generating a coalesced chain:
pn_hash_put(map, 12, values[7]); // h
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 12)), Equals("h"));
// delete from tail of coalesced chain:
pn_hash_del(map, 12);
CHECK(pn_hash_get(map, 12) == NULL);
// extend chain into cellar again, then coalesce again extending back
// into addressable region
pn_hash_put(map, 42, values[8]); // i
pn_hash_put(map, 25, values[9]); // j
// delete entry from coalesced chain, where next element in chain is
// in cellar:
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 29)), Equals("g"));
pn_hash_del(map, 29);
// test lookup of all entries:
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 1)), Equals("a"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 2)), Equals("b"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 3)), Equals("c"));
// d was deleted
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 15)), Equals("e"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 16)), Equals("f"));
// g was deleted, h was deleted
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 42)), Equals("i"));
CHECK_THAT(pn_string_get((pn_string_t *)pn_hash_get(map, 25)), Equals("j"));
CHECK(pn_hash_size(map) == 7);
// cleanup:
for (pn_handle_t i = pn_hash_head(map); i; i = pn_hash_head(map)) {
pn_hash_del(map, pn_hash_key(map, i));
}
CHECK(pn_hash_size(map) == 0);
for (size_t i = 0; i < 10; ++i) {
pn_free(values[i]);
}
pn_free(map);
}
TEST_CASE("list_compare") {
pn_list_t *a = pn_list(PN_OBJECT, 0);
pn_list_t *b = pn_list(PN_OBJECT, 0);
CHECK(pn_equals(a, b));
void *one = pn_class_new(PN_OBJECT, 0);
void *two = pn_class_new(PN_OBJECT, 0);
void *three = pn_class_new(PN_OBJECT, 0);
pn_list_add(a, one);
CHECK(!pn_equals(a, b));
pn_list_add(b, one);
CHECK(pn_equals(a, b));
pn_list_add(b, two);
CHECK(!pn_equals(a, b));
pn_list_add(a, two);
CHECK(pn_equals(a, b));
pn_list_add(a, three);
CHECK(!pn_equals(a, b));
pn_list_add(b, three);
CHECK(pn_equals(a, b));
pn_free(a);
pn_free(b);
pn_free(one);
pn_free(two);
pn_free(three);
}
typedef struct {
pn_list_t *list;
size_t index;
} pn_it_state_t;
static void *pn_it_next(void *state) {
pn_it_state_t *it = (pn_it_state_t *)state;
if (it->index < pn_list_size(it->list)) {
return pn_list_get(it->list, it->index++);
} else {
return NULL;
}
}
TEST_CASE("list_iterator") {
pn_list_t *list = build_list(0, pn_string("one"), pn_string("two"),
pn_string("three"), pn_string("four"), END);
pn_iterator_t *it = pn_iterator();
pn_it_state_t *state =
(pn_it_state_t *)pn_iterator_start(it, pn_it_next, sizeof(pn_it_state_t));
state->list = list;
state->index = 0;
void *obj;
int index = 0;
while ((obj = pn_iterator_next(it))) {
CHECK(obj == pn_list_get(list, index));
++index;
}
CHECK(index == 4);
pn_free(list);
pn_free(it);
}
TEST_CASE("list_heap") {
int size = 64;
pn_list_t *list = pn_list(PN_VOID, 0);
intptr_t min = 0;
intptr_t max = 0;
for (int i = 0; i < size; i++) {
intptr_t r = rand();
if (i == 0) {
min = r;
max = r;
} else {
if (r < min) {
min = r;
}
if (r > max) {
max = r;
}
}
pn_list_minpush(list, (void *)r);
}
intptr_t prev = (intptr_t)pn_list_minpop(list);
CHECK(prev == min);
CHECK(pn_list_size(list) == (size_t)(size - 1));
int count = 0;
while (pn_list_size(list)) {
intptr_t r = (intptr_t)pn_list_minpop(list);
CHECK(r >= prev);
prev = r;
count++;
}
CHECK(count == size - 1);
CHECK(prev == max);
pn_free(list);
}