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 &quote(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]

Reply via email to