/* * * 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 #include #include #include #include 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); }