http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/event_test.cpp ---------------------------------------------------------------------- diff --git a/c/tests/event_test.cpp b/c/tests/event_test.cpp new file mode 100644 index 0000000..0a93b5c --- /dev/null +++ b/c/tests/event_test.cpp @@ -0,0 +1,105 @@ +/* + * + * 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/event.h> +#include <proton/object.h> + +TEST_CASE("event_collector") { + pn_collector_t *collector = pn_collector(); + REQUIRE(collector); + pn_free(collector); +} + +#define SETUP_COLLECTOR \ + void *obj = pn_class_new(PN_OBJECT, 0); \ + pn_collector_t *collector = pn_collector(); \ + REQUIRE(collector); \ + pn_event_t *event = \ + pn_collector_put(collector, PN_OBJECT, obj, (pn_event_type_t)0); \ + pn_decref(obj); + +TEST_CASE("event_collector_put") { + SETUP_COLLECTOR; + REQUIRE(event); + REQUIRE(pn_event_context(event) == obj); + pn_free(collector); +} + +TEST_CASE("event_collector_peek") { + SETUP_COLLECTOR; + pn_event_t *head = pn_collector_peek(collector); + REQUIRE(head == event); + pn_free(collector); +} + +TEST_CASE("event_collector_pop") { + SETUP_COLLECTOR; + pn_event_t *head = pn_collector_peek(collector); + REQUIRE(head == event); + pn_collector_pop(collector); + head = pn_collector_peek(collector); + REQUIRE(!head); + pn_free(collector); +} + +TEST_CASE("event_collector_pool") { + SETUP_COLLECTOR; + pn_event_t *head = pn_collector_peek(collector); + REQUIRE(head == event); + pn_collector_pop(collector); + head = pn_collector_peek(collector); + REQUIRE(!head); + void *obj2 = pn_class_new(PN_OBJECT, 0); + pn_event_t *event2 = + pn_collector_put(collector, PN_OBJECT, obj2, (pn_event_type_t)0); + pn_decref(obj2); + REQUIRE(event == event2); + pn_free(collector); +} + +void test_event_incref(bool eventfirst) { + INFO("eventfirst = " << eventfirst); + SETUP_COLLECTOR; + pn_event_t *head = pn_collector_peek(collector); + REQUIRE(head == event); + pn_incref(head); + pn_collector_pop(collector); + REQUIRE(!pn_collector_peek(collector)); + void *obj2 = pn_class_new(PN_OBJECT, 0); + pn_event_t *event2 = + pn_collector_put(collector, PN_OBJECT, obj2, (pn_event_type_t)0); + pn_decref(obj2); + REQUIRE(head != event2); + if (eventfirst) { + pn_decref(head); + pn_free(collector); + } else { + pn_free(collector); + pn_decref(head); + } +} + +TEST_CASE("event_incref") { + test_event_incref(true); + test_event_incref(false); +}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/message.c ---------------------------------------------------------------------- diff --git a/c/tests/message.c b/c/tests/message.c deleted file mode 100644 index 7a6dc4f..0000000 --- a/c/tests/message.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * 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 "test_tools.h" - -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <proton/error.h> -#include <proton/message.h> - -#define assert(E) ((E) ? 0 : (abort(), 0)) - -static void test_overflow_error(test_t *t) -{ - pn_message_t *message = pn_message(); - char buf[6]; - size_t size = 6; - - int err = pn_message_encode(message, buf, &size); - TEST_INT_EQUAL(t,PN_OVERFLOW, err); - TEST_INT_EQUAL(t, 0, pn_message_errno(message)); - pn_message_free(message); -} - -static void recode(pn_message_t *dst, pn_message_t *src) { - pn_rwbytes_t buf = { 0 }; - int size = pn_message_encode2(src, &buf); - assert(size > 0); - pn_message_decode(dst, buf.start, size); - free(buf.start); -} - -static void test_inferred(test_t *t) { - pn_message_t *src = pn_message(); - pn_message_t *dst = pn_message(); - - TEST_CHECK(t, !pn_message_is_inferred(src)); - pn_data_put_binary(pn_message_body(src), PN_BYTES_LITERAL("hello")); - recode(dst, src); - TEST_CHECK(t, !pn_message_is_inferred(dst)); - pn_message_set_inferred(src, true); - recode(dst, src); - TEST_CHECK(t, pn_message_is_inferred(dst)); - - pn_message_clear(src); - TEST_CHECK(t, !pn_message_is_inferred(src)); - pn_data_put_list(pn_message_body(src)); - pn_data_enter(pn_message_body(src)); - pn_data_put_binary(pn_message_body(src), PN_BYTES_LITERAL("hello")); - recode(dst, src); - TEST_CHECK(t, !pn_message_is_inferred(dst)); - pn_message_set_inferred(src, true); - recode(dst, src); - TEST_CHECK(t, pn_message_is_inferred(dst)); - - pn_message_clear(src); - TEST_CHECK(t, !pn_message_is_inferred(src)); - pn_data_put_string(pn_message_body(src), PN_BYTES_LITERAL("hello")); - recode(dst, src); - TEST_CHECK(t, !pn_message_is_inferred(dst)); - pn_message_set_inferred(src, true); - recode(dst, src); - /* A value section is never considered to be inferred */ - TEST_CHECK(t, !pn_message_is_inferred(dst)); - - pn_message_free(src); - pn_message_free(dst); -} - -int main(int argc, char **argv) -{ - int failed = 0; - RUN_ARGV_TEST(failed, t, test_overflow_error(&t)); - RUN_ARGV_TEST(failed, t, test_inferred(&t)); - return 0; -} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/message_test.cpp ---------------------------------------------------------------------- diff --git a/c/tests/message_test.cpp b/c/tests/message_test.cpp new file mode 100644 index 0000000..5d76abe --- /dev/null +++ b/c/tests/message_test.cpp @@ -0,0 +1,84 @@ +/* + * + * 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/error.h> +#include <proton/message.h> +#include <stdarg.h> + +using namespace pn_test; + +TEST_CASE("message_overflow_error") { + pn_message_t *message = pn_message(); + char buf[6]; + size_t size = 6; + + int err = pn_message_encode(message, buf, &size); + CHECK(PN_OVERFLOW == err); + CHECK(0 == pn_message_errno(message)); + pn_message_free(message); +} + +static void recode(pn_message_t *dst, pn_message_t *src) { + pn_rwbytes_t buf = {0}; + int size = pn_message_encode2(src, &buf); + REQUIRE(size > 0); + pn_message_decode(dst, buf.start, size); + free(buf.start); +} + +TEST_CASE("message_inferred") { + pn_message_t *src = pn_message(); + pn_message_t *dst = pn_message(); + + CHECK(!pn_message_is_inferred(src)); + pn_data_put_binary(pn_message_body(src), pn_bytes("hello")); + recode(dst, src); + CHECK(!pn_message_is_inferred(dst)); + pn_message_set_inferred(src, true); + recode(dst, src); + CHECK(pn_message_is_inferred(dst)); + + pn_message_clear(src); + CHECK(!pn_message_is_inferred(src)); + pn_data_put_list(pn_message_body(src)); + pn_data_enter(pn_message_body(src)); + pn_data_put_binary(pn_message_body(src), pn_bytes("hello")); + recode(dst, src); + CHECK(!pn_message_is_inferred(dst)); + pn_message_set_inferred(src, true); + recode(dst, src); + CHECK(pn_message_is_inferred(dst)); + + pn_message_clear(src); + CHECK(!pn_message_is_inferred(src)); + pn_data_put_string(pn_message_body(src), pn_bytes("hello")); + recode(dst, src); + CHECK(!pn_message_is_inferred(dst)); + pn_message_set_inferred(src, true); + recode(dst, src); + /* A value section is never considered to be inferred */ + CHECK(!pn_message_is_inferred(dst)); + + pn_message_free(src); + pn_message_free(dst); +} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/object.c ---------------------------------------------------------------------- diff --git a/c/tests/object.c b/c/tests/object.c deleted file mode 100644 index 8a1d00e..0000000 --- a/c/tests/object.c +++ /dev/null @@ -1,1115 +0,0 @@ -/* - * - * 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 <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <proton/object.h> - -#define assert(E) ((E) ? 0 : (abort(), 0)) - -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) -{ - void *a = pn_class_new(clazz, size); - void *b = pn_class_new(clazz, size); - - assert(!pn_class_equals(clazz, a, b)); - assert(pn_class_equals(clazz, a, a)); - assert(pn_class_equals(clazz, b, b)); - assert(!pn_class_equals(clazz, a, NULL)); - assert(!pn_class_equals(clazz, NULL, a)); - - int rca = pn_class_refcount(clazz, a); - int rcb = pn_class_refcount(clazz, b); - - assert(rca == -1 || rca == 1); - assert(rcb == -1 || rcb == 1); - - pn_class_incref(clazz, a); - - rca = pn_class_refcount(clazz, a); - assert(rca == -1 || rca == 2); - - pn_class_decref(clazz, a); - - rca = pn_class_refcount(clazz, a); - assert(rca == -1 || rca == 1); - - pn_class_free(clazz, a); - pn_class_free(clazz, b); -} - -static void test_new(size_t size, const pn_class_t *clazz) -{ - void *obj = pn_class_new(clazz, size); - assert(obj); - assert(pn_class_refcount(PN_OBJECT, obj) == 1); - assert(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); -} - -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 - -static void test_finalize(void) -{ - static pn_class_t clazz = PN_CLASS(finalizer); - - int **obj = (int **) pn_class_new(&clazz, sizeof(int *)); - assert(obj); - - int called = 0; - *obj = &called; - pn_free(obj); - - assert(called == 1); -} - -static void test_free(void) -{ - // 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 - -static void test_hashcode(void) -{ - static pn_class_t clazz = PN_CLASS(hashcode); - void *obj = pn_class_new(&clazz, 0); - assert(obj); - assert(pn_hashcode(obj) == (uintptr_t) obj); - assert(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 - -static void test_compare(void) -{ - static pn_class_t clazz = PN_CLASS(compare); - - void *a = pn_class_new(&clazz, 0); - assert(a); - void *b = pn_class_new(&clazz, 0); - assert(b); - - assert(pn_compare(a, b)); - assert(!pn_equals(a, b)); - assert(!pn_compare(a, a)); - assert(pn_equals(a, a)); - assert(!pn_compare(b, b)); - assert(pn_equals(b, b)); - assert(pn_compare(a, b) == (intptr_t) ((uintptr_t) b - (uintptr_t) a)); - - assert(pn_compare(NULL, b)); - assert(!pn_equals(NULL, b)); - - assert(pn_compare(a, NULL)); - assert(!pn_equals(a, NULL)); - - assert(!pn_compare(NULL, NULL)); - assert(pn_equals(NULL, NULL)); - - pn_free(a); - pn_free(b); -} - -static void test_refcounting(int refs) -{ - void *obj = pn_class_new(PN_OBJECT, 0); - - assert(pn_refcount(obj) == 1); - - for (int i = 0; i < refs; i++) { - pn_incref(obj); - assert(pn_refcount(obj) == i + 2); - } - - assert(pn_refcount(obj) == refs + 1); - - for (int i = 0; i < refs; i++) { - pn_decref(obj); - assert(pn_refcount(obj) == refs - i); - } - - assert(pn_refcount(obj) == 1); - - pn_free(obj); -} - -static void test_list(size_t capacity) -{ - pn_list_t *list = pn_list(PN_WEAKREF, 0); - assert(pn_list_size(list) == 0); - assert(!pn_list_add(list, (void *) 0)); - assert(!pn_list_add(list, (void *) 1)); - assert(!pn_list_add(list, (void *) 2)); - assert(!pn_list_add(list, (void *) 3)); - assert(pn_list_get(list, 0) == (void *) 0); - assert(pn_list_get(list, 1) == (void *) 1); - assert(pn_list_get(list, 2) == (void *) 2); - assert(pn_list_get(list, 3) == (void *) 3); - assert(pn_list_size(list) == 4); - pn_list_del(list, 1, 2); - assert(pn_list_size(list) == 2); - assert(pn_list_get(list, 0) == (void *) 0); - assert(pn_list_get(list, 1) == (void *) 3); - pn_decref(list); -} - -static void test_list_refcount(size_t capacity) -{ - 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); - assert(!pn_list_add(list, one)); - assert(!pn_list_add(list, two)); - assert(!pn_list_add(list, three)); - assert(!pn_list_add(list, four)); - assert(pn_list_get(list, 0) == one); - assert(pn_list_get(list, 1) == two); - assert(pn_list_get(list, 2) == three); - assert(pn_list_get(list, 3) == four); - assert(pn_list_size(list) == 4); - - assert(pn_refcount(one) == 2); - assert(pn_refcount(two) == 2); - assert(pn_refcount(three) == 2); - assert(pn_refcount(four) == 2); - - pn_list_del(list, 1, 2); - assert(pn_list_size(list) == 2); - - assert(pn_refcount(one) == 2); - assert(pn_refcount(two) == 1); - assert(pn_refcount(three) == 1); - assert(pn_refcount(four) == 2); - - assert(pn_list_get(list, 0) == one); - assert(pn_list_get(list, 1) == four); - - assert(!pn_list_add(list, one)); - - assert(pn_list_size(list) == 3); - assert(pn_refcount(one) == 3); - - pn_decref(list); - - assert(pn_refcount(one) == 1); - assert(pn_refcount(two) == 1); - assert(pn_refcount(three) == 1); - assert(pn_refcount(four) == 1); - - pn_decref(one); - pn_decref(two); - pn_decref(three); - pn_decref(four); -} - -static void check_list_index(pn_list_t *list, void *value, ssize_t idx) -{ - assert(pn_list_index(list, value) == idx); -} - -static void test_list_index(void) -{ - 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); -} - -static bool pn_strequals(const char *a, const char *b) -{ - return !strcmp(a, b); -} - -static void test_build_list(void) -{ - pn_list_t *l = build_list(0, - pn_string("one"), - pn_string("two"), - pn_string("three"), - END); - - assert(pn_list_size(l) == 3); - - assert(pn_strequals(pn_string_get((pn_string_t *) pn_list_get(l, 0)), - "one")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_list_get(l, 1)), - "two")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_list_get(l, 2)), - "three")); - - pn_free(l); -} - -static void test_build_map(void) -{ - pn_map_t *m = build_map(0.75, 0, - pn_string("key"), - pn_string("value"), - pn_string("key2"), - pn_string("value2"), - END); - - assert(pn_map_size(m) == 2); - - pn_string_t *key = pn_string(NULL); - - pn_string_set(key, "key"); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_map_get(m, key)), - "value")); - pn_string_set(key, "key2"); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_map_get(m, key)), - "value2")); - - pn_free(m); - pn_free(key); -} - -static void test_build_map_odd(void) -{ - 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); - - assert(pn_map_size(m) == 3); - - pn_string_t *key = pn_string(NULL); - - pn_string_set(key, "key"); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_map_get(m, key)), - "value")); - pn_string_set(key, "key2"); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_map_get(m, key)), - "value2")); - pn_string_set(key, "key3"); - assert(pn_map_get(m, key) == NULL); - - pn_free(m); - pn_free(key); -} - -static void test_map(void) -{ - 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); - assert(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"); - - assert(!pn_map_put(map, key, one)); - assert(pn_map_size(map) == 1); - assert(!pn_map_put(map, key1, two)); - assert(pn_map_size(map) == 2); - assert(!pn_map_put(map, key2, three)); - assert(pn_map_size(map) == 3); - - assert(pn_map_get(map, dup) == one); - - assert(!pn_map_put(map, dup, one)); - assert(pn_map_size(map) == 3); - - assert(!pn_map_put(map, dup, two)); - assert(pn_map_size(map) == 3); - assert(pn_map_get(map, dup) == two); - - assert(pn_refcount(key) == 2); - assert(pn_refcount(dup) == 1); - assert(pn_refcount(key1) == 2); - assert(pn_refcount(key2) == 2); - - assert(pn_refcount(one) == 1); - assert(pn_refcount(two) == 3); - assert(pn_refcount(three) == 2); - - pn_map_del(map, key1); - assert(pn_map_size(map) == 2); - - assert(pn_refcount(key) == 2); - assert(pn_refcount(dup) == 1); - assert(pn_refcount(key1) == 1); - assert(pn_refcount(key2) == 2); - - assert(pn_refcount(one) == 1); - assert(pn_refcount(two) == 2); - assert(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); -} - -static void test_hash(void) -{ - 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); - - assert(pn_hash_get(hash, 2) == two); - assert(pn_hash_get(hash, 5) == two); - assert(pn_hash_get(hash, 18) == one); - assert(pn_hash_get(hash, 0) == NULL); - - assert(pn_hash_size(hash) == 14); - - pn_hash_del(hash, 5); - assert(pn_hash_get(hash, 5) == NULL); - assert(pn_hash_size(hash) == 13); - pn_hash_del(hash, 18); - assert(pn_hash_get(hash, 18) == NULL); - assert(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 - -static void test_map_links(void) -{ - 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]; - assert (pn_map_get(map, keys[i]) == value); - } - pn_free(map); - } - for (int i = 0; i < 3; i++) - pn_free(keys[i]); -} - - -static bool equals(const char *a, const char *b) -{ - if (a == NULL && b == NULL) { - return true; - } - - if (a == NULL || b == NULL) { - return false; - } - - return !strcmp(a, b); -} - -static void test_string(const char *value) -{ - size_t size = value ? strlen(value) : 0; - - pn_string_t *str = pn_string(value); - assert(equals(pn_string_get(str), value)); - assert(pn_string_size(str) == size); - - pn_string_t *strn = pn_stringn(value, size); - assert(equals(pn_string_get(strn), value)); - assert(pn_string_size(strn) == size); - - pn_string_t *strset = pn_string(NULL); - pn_string_set(strset, value); - assert(equals(pn_string_get(strset), value)); - assert(pn_string_size(strset) == size); - - pn_string_t *strsetn = pn_string(NULL); - pn_string_setn(strsetn, value, size); - assert(equals(pn_string_get(strsetn), value)); - assert(pn_string_size(strsetn) == size); - - assert(pn_hashcode(str) == pn_hashcode(strn)); - assert(pn_hashcode(str) == pn_hashcode(strset)); - assert(pn_hashcode(str) == pn_hashcode(strsetn)); - - assert(!pn_compare(str, str)); - assert(!pn_compare(str, strn)); - assert(!pn_compare(str, strset)); - assert(!pn_compare(str, strsetn)); - - pn_free(str); - pn_free(strn); - pn_free(strset); - pn_free(strsetn); -} - -static void test_stringn(const char *value, size_t size) -{ - pn_string_t *strn = pn_stringn(value, size); - assert(equals(pn_string_get(strn), value)); - assert(pn_string_size(strn) == size); - - pn_string_t *strsetn = pn_string(NULL); - pn_string_setn(strsetn, value, size); - assert(equals(pn_string_get(strsetn), value)); - assert(pn_string_size(strsetn) == size); - - assert(pn_hashcode(strn) == pn_hashcode(strsetn)); - assert(!pn_compare(strn, strsetn)); - - pn_free(strn); - pn_free(strsetn); -} - -static void test_string_format(void) -{ - pn_string_t *str = pn_string(""); - assert(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"); - assert(err == 0); - pn_free(str); -} - -static void test_string_addf(void) -{ - pn_string_t *str = pn_string("hello "); - assert(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"); - assert(err == 0); - pn_free(str); -} - -static void test_map_iteration(int n) -{ - 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); - - assert(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); - assert(idx >= 0); - - assert(pn_list_get(pairs, idx) == key); - assert(pn_list_get(pairs, idx + 1) == value); - - pn_list_del(pairs, idx, 2); - } - - assert(pn_list_size(pairs) == 0); - - pn_decref(map); - pn_decref(pairs); -} - -void test_inspect(void *o, const char *expected) -{ - pn_string_t *dst = pn_string(NULL); - pn_inspect(o, dst); - assert(pn_strequals(pn_string_get(dst), expected)); - pn_free(dst); -} - -void test_list_inspect(void) -{ - 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); -} - -void test_map_inspect(void) -{ - // 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); -} - -void test_map_coalesced_chain(void) -{ - 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]); - assert(pn_hash_size(map) == 8); - - //free up one non-addressable entry: - pn_hash_del(map, 16); - assert(pn_hash_get(map, 16) == NULL); - assert(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); - assert(pn_hash_get(map, 4) == NULL); - - //test lookup of all entries: - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 1)), "a")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 2)), "b")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 3)), "c")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 14)), "d")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 15)), "e")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 17)), "h")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 12)), "i")); - assert(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)); - } - assert(pn_hash_size(map) == 0); - - for (size_t i = 0; i < 9; ++i) { - pn_free(values[i]); - } - pn_free(map); -} - -void test_map_coalesced_chain2(void) -{ - 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); - assert(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 - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 12)), "h")); - //delete from tail of coalesced chain: - pn_hash_del(map, 12); - assert(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: - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 29)), "g")); - pn_hash_del(map, 29); - - //test lookup of all entries: - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 1)), "a")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 2)), "b")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 3)), "c")); - //d was deleted - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 15)), "e")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 16)), "f")); - //g was deleted, h was deleted - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 42)), "i")); - assert(pn_strequals(pn_string_get((pn_string_t *) pn_hash_get(map, 25)), "j")); - assert(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)); - } - assert(pn_hash_size(map) == 0); - - for (size_t i = 0; i < 10; ++i) { - pn_free(values[i]); - } - pn_free(map); -} - -void test_list_compare(void) -{ - pn_list_t *a = pn_list(PN_OBJECT, 0); - pn_list_t *b = pn_list(PN_OBJECT, 0); - - assert(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); - assert(!pn_equals(a, b)); - pn_list_add(b, one); - assert(pn_equals(a, b)); - - pn_list_add(b, two); - assert(!pn_equals(a, b)); - pn_list_add(a, two); - assert(pn_equals(a, b)); - - pn_list_add(a, three); - assert(!pn_equals(a, b)); - pn_list_add(b, three); - assert(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; - } -} - -void test_iterator(void) -{ - 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))) { - assert(obj == pn_list_get(list, index)); - ++index; - } - assert(index == 4); - - pn_free(list); - pn_free(it); -} - -void test_heap(int seed, int size) -{ - srand(seed); - 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); - assert(prev == min); - assert(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); - assert(r >= prev); - prev = r; - count++; - } - assert(count == size - 1); - assert(prev == max); - - pn_free(list); -} - -int main(int argc, char **argv) -{ - for (size_t i = 0; i < 128; i++) { - test_class(PN_OBJECT, i); - test_class(PN_VOID, i); - test_class(&noop_class, i); - } - - for (size_t i = 0; i < 128; i++) { - test_new(i, PN_OBJECT); - test_new(i, &noop_class); - } - - test_finalize(); - test_free(); - test_hashcode(); - test_compare(); - - for (int i = 0; i < 1024; i++) { - test_refcounting(i); - } - - for (size_t i = 0; i < 4; i++) { - test_list(i); - } - - for (size_t i = 0; i < 4; i++) { - test_list_refcount(i); - } - - test_list_index(); - - test_map(); - test_map_links(); - - test_hash(); - - test_string(NULL); - test_string(""); - test_string("this is a test"); - test_string("012345678910111213151617181920212223242526272829303132333435363" - "738394041424344454647484950515253545556575859606162636465666768"); - test_string("this has an embedded \000 in it"); - test_stringn("this has an embedded \000 in it", 28); - - test_string_format(); - test_string_addf(); - - test_build_list(); - test_build_map(); - test_build_map_odd(); - - for (int i = 0; i < 64; i++) - { - test_map_iteration(i); - } - - test_list_inspect(); - test_map_inspect(); - test_list_compare(); - test_iterator(); - for (int seed = 0; seed < 64; seed++) { - for (int size = 1; size <= 64; size++) { - test_heap(seed, size); - } - } - - test_map_coalesced_chain(); - test_map_coalesced_chain2(); - - return 0; -} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/object_test.cpp ---------------------------------------------------------------------- diff --git a/c/tests/object_test.cpp b/c/tests/object_test.cpp new file mode 100644 index 0000000..8588955 --- /dev/null +++ b/c/tests/object_test.cpp @@ -0,0 +1,969 @@ +/* + * + * 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); +} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/parse-url.c ---------------------------------------------------------------------- diff --git a/c/tests/parse-url.c b/c/tests/parse-url.c deleted file mode 100644 index ea5abc3..0000000 --- a/c/tests/parse-url.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * - * 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. - * - */ - -#define PN_USE_DEPRECATED_API 1 - -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "proton/type_compat.h" -#include "proton/error.h" -#include "proton/url.h" - -static bool verify(const char* what, const char* want, const char* got) { - bool eq = (want == got || (want && got && strcmp(want, got) == 0)); - if (!eq) printf(" %s: '%s' != '%s'\n", what, want, got); - return eq; -} - -static bool test(const char* url, const char* scheme, const char* user, const char* pass, const char* host, const char* port, const char*path) -{ - pn_url_t *purl = pn_url_parse(url); - bool ok = - verify("scheme", scheme, pn_url_get_scheme(purl)) && - verify("user", user, pn_url_get_username(purl)) && - verify("pass", pass, pn_url_get_password(purl)) && - verify("host", host, pn_url_get_host(purl)) && - verify("port", port, pn_url_get_port(purl)) && - verify("path", path, pn_url_get_path(purl)); - pn_url_free(purl); - return ok; -} - -// Run test and additionally verify the round trip of parse and stringify -// matches original string. -static bool testrt(const char* url, const char* scheme, const char* user, const char* pass, const char* host, const char* port, const char*path) -{ - bool ok = test(url, scheme, user, pass, host, port, path); - pn_url_t *purl = pn_url_parse(url); - ok = ok && verify("url", url, pn_url_str(purl)); - pn_url_free(purl); - return ok; -} - -#define TEST(EXPR) \ - do { if (!(EXPR)) { printf("%s:%d: %s\n\n", __FILE__, __LINE__, #EXPR); failed++; } } while(0) - -int main(int argc, char **argv) -{ - int failed = 0; - TEST(testrt("/Foo.bar:90087@somewhere", 0, 0, 0, 0, 0, "Foo.bar:90087@somewhere")); - TEST(testrt("host", 0, 0, 0, "host", 0, 0)); - TEST(testrt("host:423", 0, 0, 0, "host", "423", 0)); - TEST(testrt("user@host", 0, "user", 0, "host", 0, 0)); - - // Can't round-trip passwords with ':', not strictly legal but the parser allows it. - TEST(test("user:1243^&^:pw@host:423", 0, "user", "1243^&^:pw", "host", "423", 0)); - TEST(test("user:1243^&^:pw@host:423/Foo.bar:90087", 0, "user", "1243^&^:pw", "host", "423", "Foo.bar:90087")); - TEST(test("user:1243^&^:pw@host:423/Foo.bar:90087@somewhere", 0, "user", "1243^&^:pw", "host", "423", "Foo.bar:90087@somewhere")); - - TEST(testrt("[::1]", 0, 0, 0, "::1", 0, 0)); - TEST(testrt("[::1]:amqp", 0, 0, 0, "::1", "amqp", 0)); - TEST(testrt("user@[::1]", 0, "user", 0, "::1", 0, 0)); - TEST(testrt("user@[::1]:amqp", 0, "user", 0, "::1", "amqp", 0)); - - // Can't round-trip passwords with ':', not strictly legal but the parser allows it. - TEST(test("user:1243^&^:pw@[::1]:amqp", 0, "user", "1243^&^:pw", "::1", "amqp", 0)); - TEST(test("user:1243^&^:pw@[::1]:amqp/Foo.bar:90087", 0, "user", "1243^&^:pw", "::1", "amqp", "Foo.bar:90087")); - TEST(test("user:1243^&^:pw@[::1:amqp/Foo.bar:90087", 0, "user", "1243^&^:pw", "[::1", "amqp", "Foo.bar:90087")); - TEST(test("user:1243^&^:pw@::1]:amqp/Foo.bar:90087", 0, "user", "1243^&^:pw", "::1]", "amqp", "Foo.bar:90087")); - - TEST(testrt("amqp://user@[::1]", "amqp", "user", 0, "::1", 0, 0)); - TEST(testrt("amqp://user@[::1]:amqp", "amqp", "user", 0, "::1", "amqp", 0)); - TEST(testrt("amqp://user@[1234:52:0:1260:f2de:f1ff:fe59:8f87]:amqp", "amqp", "user", 0, "1234:52:0:1260:f2de:f1ff:fe59:8f87", "amqp", 0)); - - // Can't round-trip passwords with ':', not strictly legal but the parser allows it. - TEST(test("amqp://user:1243^&^:pw@[::1]:amqp", "amqp", "user", "1243^&^:pw", "::1", "amqp", 0)); - TEST(test("amqp://user:1243^&^:pw@[::1]:amqp/Foo.bar:90087", "amqp", "user", "1243^&^:pw", "::1", "amqp", "Foo.bar:90087")); - - TEST(testrt("amqp://host", "amqp", 0, 0, "host", 0, 0)); - TEST(testrt("amqp://user@host", "amqp", "user", 0, "host", 0, 0)); - TEST(testrt("amqp://user@host/path:%", "amqp", "user", 0, "host", 0, "path:%")); - TEST(testrt("amqp://user@host:5674/path:%", "amqp", "user", 0, "host", "5674", "path:%")); - TEST(testrt("amqp://user@host/path:%", "amqp", "user", 0, "host", 0, "path:%")); - TEST(testrt("amqp://bigbird@host/queue@host", "amqp", "bigbird", 0, "host", 0, "queue@host")); - TEST(testrt("amqp://host/queue@host", "amqp", 0, 0, "host", 0, "queue@host")); - TEST(testrt("amqp://host:9765/queue@host", "amqp", 0, 0, "host", "9765", "queue@host")); - TEST(test("user:pass%2fword@host", 0, "user", "pass/word", "host", 0, 0)); - TEST(testrt("user:pass%2Fword@host", 0, "user", "pass/word", "host", 0, 0)); - // Can't round-trip passwords with lowercase hex encoding - TEST(test("us%2fer:password@host", 0, "us/er", "password", "host", 0, 0)); - TEST(testrt("us%2Fer:password@host", 0, "us/er", "password", "host", 0, 0)); - // Can't round-trip passwords with lowercase hex encoding - TEST(test("user:pass%2fword%@host", 0, "user", "pass/word%", "host", 0, 0)); - TEST(testrt("localhost/temp-queue://ID:ganymede-36663-1408448359876-2:123:0", 0, 0, 0, "localhost", 0, "temp-queue://ID:ganymede-36663-1408448359876-2:123:0")); - TEST(testrt("/temp-queue://ID:ganymede-36663-1408448359876-2:123:0", 0, 0, 0, 0, 0, "temp-queue://ID:ganymede-36663-1408448359876-2:123:0")); - TEST(testrt("amqp://localhost/temp-queue://ID:ganymede-36663-1408448359876-2:123:0", "amqp", 0, 0, "localhost", 0, "temp-queue://ID:ganymede-36663-1408448359876-2:123:0")); - // PROTON-995 - TEST(testrt("amqps://%40user%2F%3A:%40pass%[email protected]/some_topic", - "amqps", "@user/:", "@pass/:", "example.net", 0, "some_topic")); - TEST(testrt("amqps://user%2F%3A=:pass%2F%[email protected]/some_topic", - "amqps", "user/:=", "pass/:=", "example.net", 0, "some_topic")); - // Really perverse url - TEST(testrt("://:@://:", "", "", "", 0, "", "/:")); - return failed; -} - -#undef PN_USE_DEPRECATED_API http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/pn_test.cpp ---------------------------------------------------------------------- diff --git a/c/tests/pn_test.cpp b/c/tests/pn_test.cpp new file mode 100644 index 0000000..e683898 --- /dev/null +++ b/c/tests/pn_test.cpp @@ -0,0 +1,217 @@ +/* + * 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/condition.h> +#include <proton/connection.h> +#include <proton/delivery.h> +#include <proton/event.h> +#include <proton/link.h> +#include <proton/message.h> +#include <proton/netaddr.h> +#include <proton/object.h> +#include <proton/transport.h> + +std::ostream &operator<<(std::ostream &o, pn_event_type_t et) { + return o << pn_event_type_name(et); +} + +inline std::ostream "e(std::ostream &o, const char *s) { + return s ? (o << '"' << s << '"') : (o << "null"); +} + +std::ostream &operator<<(std::ostream &o, const pn_condition_t &const_cond) { + pn_condition_t *cond = const_cast<pn_condition_t *>(&const_cond); + o << "pn_condition{"; + if (pn_condition_is_set(cond)) { + quote(o, pn_condition_get_name(cond)) << ", "; + quote(o, pn_condition_get_description(cond)); + } + o << "}"; + return o; +} + +std::ostream &operator<<(std::ostream &o, const pn_error_t &const_err) { + pn_error_t *err = const_cast<pn_error_t *>(&const_err); + o << "pn_error{" << pn_code(pn_error_code(err)) << ", "; + return quote(o, pn_error_text(err)) << "}"; +} + +namespace pn_test { + +std::string inspect(void *obj) { + auto_free<pn_string_t, pn_string_free> s(pn_string(NULL)); + pn_inspect(obj, s); + return pn_string_get(s); +} + +etypes make_etypes_(int first, ...) { + etypes v; + va_list ap; + va_start(ap, first); + for (int i = first; i >= 0; i = va_arg(ap, int)) { + v.push_back(static_cast<pn_event_type_t>(i)); + } + va_end(ap); + return v; +} + +std::ostream &operator<<(std::ostream &o, const etypes &et) { + return o << Catch::toString(static_cast<std::vector<pn_event_type_t> >(et)); +} + +pn_bytes_t pn_bytes(const std::string &s) { + return ::pn_bytes(s.size(), s.data()); +} + +void rwbytes_ensure(pn_rwbytes_t *buf, size_t size) { + if (buf->start == NULL || buf->size < size) { + buf->start = (char *)realloc(buf->start, size); + buf->size = size; + } +} + +void message_decode(pn_message_t *m, pn_delivery_t *d, pn_rwbytes_t *buf) { + ssize_t size = pn_delivery_pending(d); + rwbytes_ensure(buf, size); + ssize_t result = pn_link_recv(pn_delivery_link(d), buf->start, size); + REQUIRE(size == result); + pn_message_clear(m); + if (pn_message_decode(m, buf->start, size)) FAIL(pn_message_error(m)); +} + +handler::handler() + : last_condition(pn_condition()), connection(), session(), link(), sender(), + receiver(), delivery(), message() {} + +bool handler::dispatch(pn_event_t *e) { + log.push_back(pn_event_type(e)); + if (pn_event_condition(e)) { + pn_condition_copy(last_condition, pn_event_condition(e)); + } else { + pn_condition_clear(last_condition); + } + return handle(e); +} + +etypes handler::log_clear() { + etypes ret; + std::swap(ret, log); + return ret; +} + +pn_event_type_t handler::log_last() { + pn_event_type_t et = log.empty() ? PN_EVENT_NONE : log.back(); + log.clear(); + return et; +} + +driver::driver(struct handler &h) : handler(h) { + pn_connection_driver_init(this, NULL, NULL); +} +driver::~driver() { pn_connection_driver_destroy(this); } + +pn_event_type_t driver::run(pn_event_type_t stop) { + pn_event_t *e = NULL; + while ((e = pn_connection_driver_next_event(this))) { + pn_event_type_t et = pn_event_type(e); + if (handler.dispatch(e) || et == stop) return et; + } + return PN_EVENT_NONE; +} + +driver_pair::driver_pair(handler &ch, handler &sh) : client(ch), server(sh) { + pn_transport_set_server(server.transport); +} + +size_t driver::read(pn_connection_driver_t &src) { + pn_bytes_t wb = pn_connection_driver_write_buffer(&src); + pn_rwbytes_t rb = pn_connection_driver_read_buffer(this); + size_t size = rb.size < wb.size ? rb.size : wb.size; + if (size) { + std::copy(wb.start, wb.start + size, rb.start); + pn_connection_driver_write_done(&src, size); + pn_connection_driver_read_done(this, size); + } + return size; +} + +pn_event_type_t driver_pair::run() { + pn_connection_open(client.connection); // Make sure it is open + size_t n = 0; + do { + pn_event_type_t et = PN_EVENT_NONE; + if ((et = client.run())) return et; + if ((et = server.run())) return et; + n = client.read(server) + server.read(client); + } while (n || pn_connection_driver_has_event(&client) || + pn_connection_driver_has_event(&server)); + return PN_EVENT_NONE; +} + +std::string cond_empty::describe() const { return "is empty"; } + +bool cond_empty::match(const pn_condition_t &cond) const { + return !pn_condition_is_set(const_cast<pn_condition_t *>(&cond)); +} + +cond_matches::cond_matches(const std::string &name, const std::string &desc) + : name_(name), desc_(desc) {} + +std::string cond_matches::describe() const { + std::ostringstream o; + o << "matches " << Catch::toString(name_); + if (!desc_.empty()) o << ", " + Catch::toString(desc_); + return o.str(); +} + +bool cond_matches::match(const pn_condition_t &const_cond) const { + pn_condition_t *cond = const_cast<pn_condition_t *>(&const_cond); + const char *name = pn_condition_get_name(cond); + const char *desc = pn_condition_get_description(cond); + return pn_condition_is_set(cond) && name && name_ == name && + (desc_.empty() || (desc && Catch::contains(desc, desc_))); +} + +std::string error_empty::describe() const { return "is empty"; } + +bool error_empty::match(const pn_error_t &err) const { + return !pn_error_code(const_cast<pn_error_t *>(&err)); +} + +error_matches::error_matches(int code, const std::string &desc) + : code_(code), desc_(desc) {} + +std::string error_matches::describe() const { + std::ostringstream o; + o << "matches " << pn_code(code_); + if (!desc_.empty()) o << ", " + Catch::toString(desc_); + return o.str(); +} + +bool error_matches::match(const pn_error_t &const_err) const { + pn_error_t *err = const_cast<pn_error_t *>(&const_err); + int code = pn_error_code(err); + const char *desc = pn_error_text(err); + return code_ == code && + (desc_.empty() || (desc && Catch::contains(desc, desc_))); +} + +} // namespace pn_test http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/pn_test.hpp ---------------------------------------------------------------------- diff --git a/c/tests/pn_test.hpp b/c/tests/pn_test.hpp new file mode 100644 index 0000000..bbfa3d5 --- /dev/null +++ b/c/tests/pn_test.hpp @@ -0,0 +1,193 @@ +#ifndef TESTS_PN_TEST_HPP +#define TESTS_PN_TEST_HPP +/* + * 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. + */ + +/// @file +/// +/// Wrappers and Catch2 extensions to simplify testing the proton-C library. + +#include <catch_extra.hpp> + +#include <proton/condition.h> +#include <proton/connection_driver.h> +#include <proton/event.h> +#include <proton/message.h> + +#include <iosfwd> +#include <string> +#include <vector> + +// String form of C pn_ types used in tests, must be in global C namespace +// Note objects are passed by reference, not pointer. +std::ostream &operator<<(std::ostream &, pn_event_type_t); +std::ostream &operator<<(std::ostream &, const pn_condition_t &); +std::ostream &operator<<(std::ostream &, const pn_error_t &); + +namespace pn_test { + +// Holder for T*, calls function Free() in dtor. Not copyable. +template <class T, void (*Free)(T *)> class auto_free { + T *ptr_; + auto_free &operator=(auto_free &x); + auto_free(auto_free &x); + +public: + auto_free(T *p = 0) : ptr_(p) {} + ~auto_free() { Free(ptr_); } + T *get() const { return ptr_; } + operator T *() const { return ptr_; } +}; + +// pn_free() works for some, but not all pn_xxx_t* types. +// Add typed pn_string_free() so we can be consistent and safe. +inline void pn_string_free(pn_string_t *s) { pn_free(s); } + +// Call pn_inspect(), return std::string +std::string inspect(void *); + +// List of pn_event_type_t +typedef std::vector<pn_event_type_t> etypes; +std::ostream &operator<<(std::ostream &o, const etypes &et); + +// Workaround for lack of list initializers in C++03. +// Use ETYPES macro, don't call make_etypes_ directly +etypes make_etypes_(int first, ...); +#define ETYPES(...) (make_etypes_(__VA_ARGS__, -1)) + +/// Make a pn_bytes_t from a std::string +pn_bytes_t pn_bytes(const std::string &s); + +/// Ensure buf has at least size bytes, use realloc if need be +void rwbytes_ensure(pn_rwbytes_t *buf, size_t size); + +/// Decode message from delivery into buf, expand buf as needed. +void message_decode(pn_message_t *m, pn_delivery_t *d, pn_rwbytes_t *buf); + +// A test handler that logs the type of each event handled, and has +// slots to store all of the basic proton types for ad-hoc use in +// tests. Subclass and override the handle() method. +struct handler { + etypes log; // Log of events + auto_free<pn_condition_t, pn_condition_free> + last_condition; // Condition of last event + + // Slots to save proton objects for use outside the handler. + pn_listener_t *listener; + pn_connection_t *connection; + pn_session_t *session; + pn_link_t *link; + pn_link_t *sender; + pn_link_t *receiver; + pn_delivery_t *delivery; + pn_message_t *message; + + handler(); + + /// dispatch an event: log its type then call handle() + /// Returns the value of handle() + bool dispatch(pn_event_t *e); + + // Return the current log contents, clear the log. + etypes log_clear(); + + // Return the last event in the log, clear the log. + pn_event_type_t log_last(); + +protected: + // Override this function to handle events. + // + // Return true to stop dispatching and return control to the test function, + // false to continue processing. + virtual bool handle(pn_event_t *e) { return false; } +}; + +// A pn_connection_driver_t that dispatches to a pn_test::handler +// +// driver::run() dispatches events to the handler, but returns if the handler +// returns true, or if a specific event type is handled. Test functions can +// alternate between letting the handler run and checking state or calling +// functions on proton objects in the test function directly. Handlers can +// automate uninteresting work, the test function can make checks that are +// clearly located in the flow of the test logic. +struct driver : public ::pn_connection_driver_t { + struct handler &handler; + + driver(struct handler &h); + ~driver(); + + // Dispatch events till a handler returns true, the `stop` event is handled, + // or there are no more events + // Returns the last event handled or PN_EVENT_NONE if none were. + pn_event_type_t run(pn_event_type_t stop = PN_EVENT_NONE); + + // Transfer available data from src write buffer to this read-buffer and + // update both drivers. Return size of data transferred. + size_t read(pn_connection_driver_t &src); +}; + +// A client/server pair drivers. run() simulates a connection in memory. +struct driver_pair { + driver client, server; + + // Associate handlers with drivers. Sets server.transport to server mode + driver_pair(handler &ch, handler &sh); + + // Run the drivers until a handle returns true or there is nothing left to + // do. Opens the client.connection() if not already open. + // Return the last event handled or PN_EVENT_NONE + pn_event_type_t run(); +}; + +// Matches for use with Catch macros CHECK_THAT and REQUIRE_THAT. +// Check pn_condition_t and pn_error_t, failed checks report code, name, +// description etc. + +struct cond_empty : public Catch::MatcherBase<pn_condition_t> { + std::string describe() const CATCH_OVERRIDE; + bool match(const pn_condition_t &cond) const CATCH_OVERRIDE; +}; + +class cond_matches : public Catch::MatcherBase<pn_condition_t> { + std::string name_, desc_; + +public: + cond_matches(const std::string &name, const std::string &desc_contains = ""); + std::string describe() const CATCH_OVERRIDE; + bool match(const pn_condition_t &cond) const CATCH_OVERRIDE; +}; + +struct error_empty : public Catch::MatcherBase<pn_error_t> { + std::string describe() const CATCH_OVERRIDE; + bool match(const pn_error_t &) const CATCH_OVERRIDE; +}; + +class error_matches : public Catch::MatcherBase<pn_error_t> { + int code_; + std::string desc_; + +public: + error_matches(int code, const std::string &desc_contains = ""); + std::string describe() const CATCH_OVERRIDE; + bool match(const pn_error_t &) const CATCH_OVERRIDE; +}; + +} // namespace pn_test + +#endif // TESTS_PN_TEST_HPP --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
