You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2018/11/19 22:53:31 UTC

[1/6] qpid-proton git commit: PROTON-1887: [c] Convert C tests to use Catch2 harness.

Repository: qpid-proton
Updated Branches:
  refs/heads/master f53c7683d -> 0bdba37da


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/tests/include/catch_extra.hpp
----------------------------------------------------------------------
diff --git a/tests/include/catch_extra.hpp b/tests/include/catch_extra.hpp
new file mode 100644
index 0000000..14e2a2f
--- /dev/null
+++ b/tests/include/catch_extra.hpp
@@ -0,0 +1,107 @@
+#ifndef CATCH_EXTRA_HPP
+#define CATCH_EXTRA_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.
+ */
+
+// Extensions to the Catch2 framework
+
+#include <catch.hpp>
+
+namespace Catch {
+namespace Matchers {
+
+// Matcher for NUL terminated C-strings.
+
+/* Example:
+const char *x, *y
+
+CHECK(x == y); // POINTER equality, probably not what you want.
+
+using Catch::Matchers::Equals;
+CHECK_THAT(x, Equals(y)); // String equality, like strcmp()
+*/
+
+namespace CString {
+
+struct CStringMatcherBase : MatcherBase<const char *> {
+  const std::string m_operation;
+  const char *m_comparator;
+
+  CStringMatcherBase(const std::string &operation, const char *comparator)
+      : m_operation(operation), m_comparator(comparator) {}
+  std::string describe() const CATCH_OVERRIDE {
+    return m_operation + ": " + Catch::toString(m_comparator);
+  }
+};
+
+struct EqualsMatcher : CStringMatcherBase {
+  EqualsMatcher(const char *comparator)
+      : CStringMatcherBase("equals", comparator) {}
+  bool match(const char *source) const CATCH_OVERRIDE {
+    // match if both null or both non-null and equal
+    return source == m_comparator ||
+           (source && m_comparator &&
+            std::string(source) == std::string(m_comparator));
+  }
+};
+
+struct ContainsMatcher : CStringMatcherBase {
+  ContainsMatcher(const char *comparator)
+      : CStringMatcherBase("contains", comparator) {}
+  bool match(const char *source) const CATCH_OVERRIDE {
+    return source && m_comparator && Catch::contains(source, m_comparator);
+  }
+};
+
+struct StartsWithMatcher : CStringMatcherBase {
+  StartsWithMatcher(const char *comparator)
+      : CStringMatcherBase("starts with", comparator) {}
+  bool match(const char *source) const CATCH_OVERRIDE {
+    return source && m_comparator && Catch::startsWith(source, m_comparator);
+  }
+};
+
+struct EndsWithMatcher : CStringMatcherBase {
+  EndsWithMatcher(const char *comparator)
+      : CStringMatcherBase("ends with", comparator) {}
+  bool match(const char *source) const CATCH_OVERRIDE {
+    return source && m_comparator && Catch::endsWith(source, m_comparator);
+  }
+};
+
+} // namespace CString
+
+inline CString::EqualsMatcher Equals(const char *str) {
+  return CString::EqualsMatcher(str);
+}
+inline CString::ContainsMatcher Contains(const char *str) {
+  return CString::ContainsMatcher(str);
+}
+inline CString::StartsWithMatcher StartsWith(const char *str) {
+  return CString::StartsWithMatcher(str);
+}
+inline CString::EndsWithMatcher EndsWith(const char *str) {
+  return CString::EndsWithMatcher(str);
+}
+
+} // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_EXTRA_HPP


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[4/6] qpid-proton git commit: PROTON-1887: [c] Convert C tests to use Catch2 harness.

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/pn_test_proactor.cpp
----------------------------------------------------------------------
diff --git a/c/tests/pn_test_proactor.cpp b/c/tests/pn_test_proactor.cpp
new file mode 100644
index 0000000..0c06f72
--- /dev/null
+++ b/c/tests/pn_test_proactor.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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_proactor.hpp"
+#include "./thread.h"
+
+#include <proton/condition.h>
+#include <proton/connection.h>
+#include <proton/delivery.h>
+#include <proton/event.h>
+#include <proton/link.h>
+#include <proton/listener.h>
+#include <proton/message.h>
+#include <proton/netaddr.h>
+#include <proton/object.h>
+#include <proton/proactor.h>
+#include <proton/transport.h>
+
+namespace pn_test {
+
+std::string listening_port(pn_listener_t *l) {
+  const pn_netaddr_t *na = pn_listener_addr(l);
+  char port[PN_MAX_ADDR];
+  pn_netaddr_host_port(na, NULL, 0, port, sizeof(port));
+  return port;
+}
+
+proactor::proactor(struct handler *h)
+    : auto_free<pn_proactor_t, pn_proactor_free>(pn_proactor()), handler(h) {}
+
+bool proactor::dispatch(pn_event_t *e) {
+  void *ctx = NULL;
+  if (pn_event_listener(e))
+    ctx = pn_listener_get_context(pn_event_listener(e));
+  else if (pn_event_connection(e))
+    ctx = pn_connection_get_context(pn_event_connection(e));
+  struct handler *h = ctx ? reinterpret_cast<struct handler *>(ctx) : handler;
+  bool ret = h ? h->dispatch(e) : false;
+  //  pn_test::handler doesn't know about listeners so save listener condition
+  //  here.
+  if (pn_event_listener(e)) {
+    pn_condition_copy(h->last_condition,
+                      pn_listener_condition(pn_event_listener(e)));
+  }
+  return ret;
+}
+
+// RAII for event batches
+class auto_batch {
+  pn_proactor_t *p_;
+  pn_event_batch_t *b_;
+
+public:
+  auto_batch(pn_proactor_t *p, pn_event_batch_t *b) : p_(p), b_(b) {}
+  ~auto_batch() {
+    if (b_) pn_proactor_done(p_, b_);
+  }
+  pn_event_t *next() { return b_ ? pn_event_batch_next(b_) : NULL; }
+  pn_event_batch_t *get() { return b_; }
+};
+
+pn_event_type_t proactor::run(pn_event_type_t stop) {
+  // This will hang in the underlying poll if test expectations are never met.
+  // Not ideal, but easier to debug than race conditions caused by buggy
+  // test-harness code that attempts to detect the problem and recover early.
+  // The larger test or CI harness will kill us after some time limit
+  while (true) {
+    auto_batch b(*this, pn_proactor_wait(*this));
+    if (b.get()) {
+      pn_event_t *e;
+      while ((e = b.next())) {
+        pn_event_type_t et = pn_event_type(e);
+        if (dispatch(e) || et == stop) return et;
+      }
+    }
+  }
+}
+
+pn_event_type_t proactor::flush(pn_event_type_t stop) {
+  auto_batch b(*this, pn_proactor_get(*this));
+  if (b.get()) {
+    pn_event_t *e;
+    while ((e = b.next())) {
+      pn_event_type_t et = pn_event_type(e);
+      if (dispatch(e) || et == stop) return et;
+    }
+  }
+  return PN_EVENT_NONE;
+}
+
+pn_event_type_t proactor::corun(proactor &other, pn_event_type_t stop) {
+  // We can't wait() on either proactor as it might be idle until
+  // something happens on the other, so spin between the two for a limited
+  // number of attempts that should be large enough if the test is going to
+  // past.
+  int spin_limit = 1000;
+  while (spin_limit > 0) {
+    pn_event_type_t et = flush(stop);
+    if (et) return et;
+    other.flush();
+    --spin_limit;
+  }
+  return PN_EVENT_NONE;
+}
+
+pn_event_type_t proactor::wait_next() {
+  // pn_proactor_wait() should never return an empty batch, so we shouldn't need
+  // a loop here. Due to bug https://issues.apache.org/jira/browse/PROTON-1964
+  // we need to re-wait if we get an empty batch.
+  //
+  // To reproduce PROTON-1964 remove the loop below and run
+  // TEST_CASE("proactor_proton_1586") from proactor_test.cpp
+  //
+  // You will pn_proactor_wait() return a non-NULL batch, but the
+  // first call to pn_event_batch_next() returns a NULL event.
+  //
+  while (true) {
+    auto_batch b(*this, pn_proactor_wait(*this));
+    pn_event_t *e = b.next();
+    if (e) {
+      dispatch(e);
+      return pn_event_type(e);
+    } // Try again on an empty batch.
+  }
+}
+
+pn_listener_t *proactor::listen(const std::string &addr,
+                                struct handler *handler) {
+  pn_listener_t *l = pn_listener();
+  pn_listener_set_context(l, handler);
+  pn_proactor_listen(*this, l, addr.c_str(), 4);
+  return l;
+}
+
+pn_connection_t *proactor::connect(const std::string &addr, struct handler *h,
+                                   pn_connection_t *c) {
+  if (!c) c = pn_connection();
+  if (h) pn_connection_set_context(c, h);
+  pn_proactor_connect2(*this, c, NULL, addr.c_str());
+  return c;
+}
+
+} // namespace pn_test

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/pn_test_proactor.hpp
----------------------------------------------------------------------
diff --git a/c/tests/pn_test_proactor.hpp b/c/tests/pn_test_proactor.hpp
new file mode 100644
index 0000000..3703910
--- /dev/null
+++ b/c/tests/pn_test_proactor.hpp
@@ -0,0 +1,120 @@
+#ifndef TESTS_PN_TEST_PROACTOR_HPP
+#define TESTS_PN_TEST_PROACTOR_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
+///
+/// Wrapper for driving proactor tests.
+
+#include "./pn_test.hpp"
+#include <proton/proactor.h>
+
+namespace pn_test {
+
+// Get the listening port, l must be open.
+std::string listening_port(pn_listener_t *l);
+
+// Test proactor with an optional global handler.
+// For connection and listener events, if pn_*_get_context() is non-NULL
+// then it is cast to a handler and used instead of the global one.
+struct proactor : auto_free<pn_proactor_t, pn_proactor_free> {
+  struct handler *handler;
+
+  proactor(struct handler *h = 0);
+
+  // Listen on addr using optional listener handler lh
+  pn_listener_t *listen(const std::string &addr = ":0", struct handler *lh = 0);
+
+  // Connect to addr use optional handler, and optionally providing the
+  // connection object.
+  pn_connection_t *connect(const std::string &addr, struct handler *h = 0,
+                           pn_connection_t *c = 0);
+
+  // Connect to listenr's address useing optional handler.
+  pn_connection_t *connect(pn_listener_t *l, struct handler *h = 0,
+                           pn_connection_t *c = 0) {
+    return connect(":" + pn_test::listening_port(l), h, c);
+  }
+
+  // Accept a connection, associate with optional connection handler.
+  pn_connection_t *accept(pn_listener_t *l, struct handler *h = 0);
+
+  // Wait for events and dispatch them until:
+  // * A handler returns true.
+  // * The `stop` event type is handled.
+  // Return the event-type of the last event handled or PN_EVENT_NONE
+  // if something went wrong.
+  pn_event_type_t run(pn_event_type_t stop = PN_EVENT_NONE);
+
+  // Dispatch immediately-available events until:
+  // * A handler returns true.
+  // * The `stop` event type is handled.
+  // * All available events are flushed.
+  //
+  // Return PN_EVENT_NONE if all events were flushed, the event-type of the last
+  // event handled otherwise.
+  pn_event_type_t flush(pn_event_type_t stop = PN_EVENT_NONE);
+
+  // Alternate flushing this proactor and `other` until
+  // * A handler on this proactor returns true.
+  // * The `stop` event type is handled by this proactor.
+  // Return the event-type of the last event handled or PN_EVENT_NONE
+  // if something went wrong.
+  pn_event_type_t corun(proactor &other, pn_event_type_t stop = PN_EVENT_NONE);
+
+  // Wait for and handle a single event, return it's type.
+  pn_event_type_t wait_next();
+
+private:
+  bool dispatch(pn_event_t *e);
+};
+
+// CHECK/REQUIRE macros to run a proactor up to an expected event and
+// include the last condition in the error message if the expected event is not
+// returned.
+
+#define CHECK_RUN(P, E)                                                        \
+  CHECKED_IF((E) == (P).run(E)) {}                                             \
+  else if ((P).handler) {                                                      \
+    FAIL_CHECK(*(P).handler->last_condition);                                  \
+  }
+
+#define REQUIRE_RUN(P, E)                                                      \
+  CHECKED_IF((E) == (P).run(E)) {}                                             \
+  else if ((P).handler) {                                                      \
+    FAIL(*(P).handler->last_condition);                                        \
+  }
+
+#define CHECK_CORUN(P, O, E)                                                   \
+  CHECKED_IF((E) == (P).corun(O, E)) {}                                        \
+  else if ((P).handler) {                                                      \
+    FAIL_CHECK(*(P).handler->last_condition);                                  \
+  }
+
+#define REQUIRE_CORUN(P, E)                                                    \
+  CHECKED_IF((E) == (P).corun(O, E)) {}                                        \
+  else if ((P).handler_) {                                                     \
+    FAIL(*(P).handler_->last_condition);                                       \
+  }
+
+} // namespace pn_test
+
+#endif // TESTS_PN_TEST_PROACTOR_HPP

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/pn_test_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/pn_test_test.cpp b/c/tests/pn_test_test.cpp
new file mode 100644
index 0000000..1d51a43
--- /dev/null
+++ b/c/tests/pn_test_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+// Tests for the test framework
+
+#include "./pn_test.hpp"
+
+#include <proton/condition.h>
+#include <proton/error.h>
+#include <proton/event.h>
+
+using namespace pn_test;
+
+TEST_CASE("test_stringify") {
+  std::ostringstream o;
+  SECTION("event_type") {
+    o << PN_CONNECTION_INIT;
+    CHECK(o.str() == "PN_CONNECTION_INIT");
+    CHECK(Catch::toString(PN_CONNECTION_INIT) == "PN_CONNECTION_INIT");
+  }
+  SECTION("condition") {
+    pn_condition_t *c = pn_condition();
+    SECTION("empty") {
+      o << *c;
+      CHECK(o.str() == "pn_condition{}");
+      CHECK_THAT(*c, cond_empty());
+    }
+    SECTION("name-desc") {
+      pn_condition_set_name(c, "foo");
+      pn_condition_set_description(c, "bar");
+      o << *c;
+      CHECK(o.str() == "pn_condition{\"foo\", \"bar\"}");
+      CHECK_THAT(*c, cond_matches("foo", "bar"));
+      CHECK_THAT(*c, !cond_empty());
+    }
+    SECTION("desc-only") {
+      pn_condition_set_name(c, "foo");
+      o << *c;
+      CHECK(o.str() == "pn_condition{\"foo\", null}");
+      CHECK_THAT(*c, cond_matches("foo"));
+    }
+    pn_condition_free(c);
+  }
+  SECTION("error") {
+    pn_error_t *err = pn_error();
+    pn_error_format(err, PN_EOS, "foo");
+    o << *err;
+    pn_error_free(err);
+    CHECK(o.str() == "pn_error{PN_EOS, \"foo\"}");
+  }
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/proactor.c
----------------------------------------------------------------------
diff --git a/c/tests/proactor.c b/c/tests/proactor.c
deleted file mode 100644
index 9078ffc..0000000
--- a/c/tests/proactor.c
+++ /dev/null
@@ -1,1107 +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 "test_handler.h"
-#include "test_config.h"
-#include "../src/proactor/proactor-internal.h"
-
-#include <proton/condition.h>
-#include <proton/connection.h>
-#include <proton/event.h>
-#include <proton/listener.h>
-#include <proton/session.h>
-#include <proton/netaddr.h>
-#include <proton/proactor.h>
-#include <proton/ssl.h>
-#include <proton/transport.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define ARRAYLEN(A) (sizeof(A)/sizeof((A)[0]))
-
-/* Proactor and handler that take part in a test */
-typedef struct test_proactor_t {
-  test_handler_t handler;
-  pn_proactor_t *proactor;
-} test_proactor_t;
-
-static test_proactor_t test_proactor(test_t *t, test_handler_fn f) {
-  test_proactor_t tp;
-  test_handler_init(&tp.handler, t, f);
-  tp.proactor = pn_proactor();
-  TEST_ASSERT(tp.proactor);
-  return tp;
-}
-
-static void test_proactor_destroy(test_proactor_t *tp) {
-  pn_proactor_free(tp->proactor);
-}
-
-/* Set this to a pn_condition() to save condition data */
-pn_condition_t *last_condition = NULL;
-
-static void save_condition(pn_event_t *e) {
-  if (last_condition) {
-    pn_condition_t *cond = NULL;
-    if (pn_event_listener(e)) {
-      cond = pn_listener_condition(pn_event_listener(e));
-    } else {
-      cond = pn_event_condition(e);
-    }
-    if (cond) {
-      pn_condition_copy(last_condition, cond);
-    } else {
-      pn_condition_clear(last_condition);
-    }
-  }
-}
-
-/* Process events on a proactor array until a handler returns an event, or
- * all proactors return NULL
- */
-static pn_event_type_t test_proactors_get(test_proactor_t *tps, size_t n) {
-  if (last_condition) pn_condition_clear(last_condition);
-  while (true) {
-    bool busy = false;
-    for (test_proactor_t *tp = tps; tp < tps + n; ++tp) {
-      pn_event_batch_t *eb =  pn_proactor_get(tp->proactor);
-      if (eb) {
-        busy = true;
-        pn_event_type_t ret = PN_EVENT_NONE;
-        for (pn_event_t* e = pn_event_batch_next(eb); e; e = pn_event_batch_next(eb)) {
-          test_handler_log(&tp->handler, e);
-          save_condition(e);
-          ret = tp->handler.f(&tp->handler, e);
-          if (ret) break;
-        }
-        pn_proactor_done(tp->proactor, eb);
-        if (ret) return ret;
-      }
-    }
-    if (!busy) {
-      return PN_EVENT_NONE;
-    }
-  }
-}
-
-/* Run an array of proactors till a handler returns an event. */
-static pn_event_type_t test_proactors_run(test_proactor_t *tps, size_t n) {
-  pn_event_type_t e;
-  while ((e = test_proactors_get(tps, n)) == PN_EVENT_NONE)
-         ;
-  return e;
-}
-
-/* Run an array of proactors till a handler returns the desired event. */
-void test_proactors_run_until(test_proactor_t *tps, size_t n, pn_event_type_t want) {
-  while (test_proactors_get(tps, n) != want)
-         ;
-}
-
-/* Drain and discard outstanding events from an array of proactors */
-static void test_proactors_drain(test_proactor_t *tps, size_t n) {
-  while (test_proactors_get(tps, n))
-         ;
-}
-
-
-#define TEST_PROACTORS_GET(A) test_proactors_get((A), ARRAYLEN(A))
-#define TEST_PROACTORS_RUN(A) test_proactors_run((A), ARRAYLEN(A))
-#define TEST_PROACTORS_RUN_UNTIL(A, WANT) test_proactors_run_until((A), ARRAYLEN(A), WANT)
-#define TEST_PROACTORS_DRAIN(A) test_proactors_drain((A), ARRAYLEN(A))
-
-#define TEST_PROACTORS_DESTROY(A) do {           \
-    for (size_t i = 0; i < ARRAYLEN(A); ++i)       \
-      test_proactor_destroy((A)+i);             \
-  } while (0)
-
-
-#define MAX_STR 256
-struct addrinfo {
-  char host[MAX_STR];
-  char port[MAX_STR];
-  char connect[MAX_STR];
-  char host_port[MAX_STR];
-};
-
-struct addrinfo listener_info(pn_listener_t *l) {
-  struct addrinfo ai = {{0}};
-  const pn_netaddr_t *na = pn_listener_addr(l);
-  TEST_ASSERT(0 == pn_netaddr_host_port(na, ai.host, sizeof(ai.host), ai.port, sizeof(ai.port)));
-  for (na = pn_netaddr_next(na); na; na = pn_netaddr_next(na)) { /* Check that ports are consistent */
-    char port[MAX_STR];
-    TEST_ASSERT(0 == pn_netaddr_host_port(na, NULL, 0, port, sizeof(port)));
-    TEST_ASSERTF(0 == strcmp(port, ai.port), "%s !=  %s", port, ai.port);
-  }
-  (void)pn_proactor_addr(ai.connect, sizeof(ai.connect), "", ai.port); /* Address for connecting */
-  (void)pn_netaddr_str(na, ai.host_port, sizeof(ai.host_port)); /* host:port listening address */
-  return ai;
-}
-
-/* Return a pn_listener_t*, raise errors if not successful */
-pn_listener_t *test_listen(test_proactor_t *tp, const char *host) {
-  char addr[1024];
-  pn_listener_t *l = pn_listener();
-  (void)pn_proactor_addr(addr, sizeof(addr), host, "0");
-  pn_proactor_listen(tp->proactor, l, addr, 4);
-  TEST_ETYPE_EQUAL(tp->handler.t, PN_LISTENER_OPEN, test_proactors_run(tp, 1));
-  TEST_COND_EMPTY(tp->handler.t, last_condition);
-  return l;
-}
-
-
-/* Wait for the next single event, return its type */
-static pn_event_type_t wait_next(pn_proactor_t *proactor) {
-  pn_event_batch_t *events = pn_proactor_wait(proactor);
-  pn_event_type_t etype = pn_event_type(pn_event_batch_next(events));
-  pn_proactor_done(proactor, events);
-  return etype;
-}
-
-/* Test that interrupt and timeout events cause pn_proactor_wait() to return. */
-static void test_interrupt_timeout(test_t *t) {
-  pn_proactor_t *p = pn_proactor();
-  TEST_CHECK(t, pn_proactor_get(p) == NULL); /* idle */
-  pn_proactor_interrupt(p);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INTERRUPT, wait_next(p));
-  TEST_CHECK(t, pn_proactor_get(p) == NULL); /* idle */
-
-  /* Set an immediate timeout */
-  pn_proactor_set_timeout(p, 0);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_TIMEOUT, wait_next(p));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, wait_next(p)); /* Inactive because timeout expired */
-
-  /* Set a (very short) timeout */
-  pn_proactor_set_timeout(p, 1);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_TIMEOUT, wait_next(p));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, wait_next(p));
-
-  /* Set and cancel a timeout, make sure we don't get the timeout event */
-  pn_proactor_set_timeout(p, 10000000);
-  pn_proactor_cancel_timeout(p);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, wait_next(p));
-  TEST_CHECK(t, pn_proactor_get(p) == NULL); /* idle */
-
-  pn_proactor_free(p);
-}
-
-/* Save the last connection accepted by the common_handler */
-pn_connection_t *last_accepted = NULL;
-
-/* Common handler for simple client/server interactions,  */
-static pn_event_type_t common_handler(test_handler_t *th, pn_event_t *e) {
-  pn_connection_t *c = pn_event_connection(e);
-  pn_listener_t *l = pn_event_listener(e);
-
-  switch (pn_event_type(e)) {
-
-    /* Stop on these events */
-   case PN_TRANSPORT_ERROR:
-   case PN_TRANSPORT_CLOSED:
-   case PN_PROACTOR_INACTIVE:
-   case PN_PROACTOR_TIMEOUT:
-   case PN_LISTENER_OPEN:
-    return pn_event_type(e);
-
-   case PN_LISTENER_ACCEPT:
-    last_accepted = pn_connection();
-    pn_listener_accept2(l, last_accepted, NULL);
-    pn_listener_close(l);       /* Only accept one connection */
-    return PN_EVENT_NONE;
-
-   case PN_CONNECTION_REMOTE_OPEN:
-    pn_connection_open(c);      /* Return the open (no-op if already open) */
-    return PN_EVENT_NONE;
-
-   case PN_SESSION_REMOTE_OPEN:
-    pn_session_open(pn_event_session(e));
-    return PN_EVENT_NONE;
-
-   case PN_LINK_REMOTE_OPEN:
-    pn_link_open(pn_event_link(e));
-    return PN_EVENT_NONE;
-
-   case PN_CONNECTION_REMOTE_CLOSE:
-    pn_connection_close(c);     /* Return the close */
-    return PN_EVENT_NONE;
-
-    /* Ignore these events */
-   case PN_CONNECTION_BOUND:
-   case PN_CONNECTION_INIT:
-   case PN_CONNECTION_LOCAL_CLOSE:
-   case PN_CONNECTION_LOCAL_OPEN:
-   case PN_LINK_INIT:
-   case PN_LINK_LOCAL_OPEN:
-   case PN_LISTENER_CLOSE:
-   case PN_SESSION_INIT:
-   case PN_SESSION_LOCAL_OPEN:
-   case PN_TRANSPORT:
-   case PN_TRANSPORT_HEAD_CLOSED:
-   case PN_TRANSPORT_TAIL_CLOSED:
-    return PN_EVENT_NONE;
-
-   default:
-    TEST_ERRORF(th->t, "unexpected event %s", pn_event_type_name(pn_event_type(e)));
-    return PN_EVENT_NONE;                   /* Fail the test but keep going */
-  }
-}
-
-/* Like common_handler but does not auto-close the listener after one accept,
-   and returns on LISTENER_CLOSE
-*/
-static pn_event_type_t listen_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_LISTENER_ACCEPT:
-    /* No automatic listener close/free for tests that accept multiple connections */
-    last_accepted = pn_connection();
-    pn_listener_accept2(pn_event_listener(e), last_accepted, NULL);
-    /* No automatic close */
-    return PN_EVENT_NONE;
-
-   case PN_LISTENER_CLOSE:
-    return PN_LISTENER_CLOSE;
-
-   default:
-    return common_handler(th, e);
-  }
-}
-
-/* close a connection when it is remote open */
-static pn_event_type_t open_close_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_REMOTE_OPEN:
-    pn_connection_close(pn_event_connection(e));
-    return PN_EVENT_NONE;          /* common_handler will finish on TRANSPORT_CLOSED */
-   default:
-    return common_handler(th, e);
-  }
-}
-
-/* Test simple client/server connection with 2 proactors */
-static void test_client_server(test_t *t) {
-  test_proactor_t tps[] ={ test_proactor(t, open_close_handler), test_proactor(t, common_handler) };
-  pn_listener_t *l = test_listen(&tps[1], "");
-  /* Connect and wait for close at both ends */
-  pn_proactor_connect2(tps[0].proactor, NULL, NULL, listener_info(l).connect);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);  
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Return on connection open, close and return on wake */
-static pn_event_type_t open_wake_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_REMOTE_OPEN:
-    return pn_event_type(e);
-   case PN_CONNECTION_WAKE:
-    pn_connection_close(pn_event_connection(e));
-    return pn_event_type(e);
-   default:
-    return common_handler(th, e);
-  }
-}
-
-/* Test waking up a connection that is idle */
-static void test_connection_wake(test_t *t) {
-  test_proactor_t tps[] =  { test_proactor(t, open_wake_handler), test_proactor(t,  listen_handler) };
-  pn_proactor_t *client = tps[0].proactor;
-  pn_listener_t *l = test_listen(&tps[1], "");
-
-  pn_connection_t *c = pn_connection();
-  pn_incref(c);                 /* Keep a reference for wake() after free */
-  pn_proactor_connect2(client, c, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  TEST_CHECK(t, pn_proactor_get(client) == NULL); /* Should be idle */
-  pn_connection_wake(c);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_WAKE, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps)); /* Both ends */
-  /* The pn_connection_t is still valid so wake is legal but a no-op */
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_EVENT_NONE, TEST_PROACTORS_GET(tps)); /* No more wake */
-
-  /* Verify we don't get a wake after close even if they happen together */
-  pn_connection_t *c2 = pn_connection();
-  pn_proactor_connect2(client, c2, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  pn_connection_wake(c2);
-  pn_proactor_disconnect(client, NULL);
-  pn_connection_wake(c2);
-
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, test_proactors_run(&tps[0], 1));
-  TEST_CONDITION(t, "amqp:connection:framing-error", "connection aborted", last_condition);
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, test_proactors_run(&tps[0], 1));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, test_proactors_run(&tps[0], 1));
-  TEST_ETYPE_EQUAL(t, PN_EVENT_NONE, test_proactors_get(&tps[0], 1)); /* No late wake */
-
-  TEST_PROACTORS_DESTROY(tps);
-  /* The pn_connection_t is still valid so wake is legal but a no-op */
-  pn_connection_wake(c);
-  pn_decref(c);
-}
-
-/* Close the transport to abort a connection, i.e. close the socket without an AMQP close */
-static pn_event_type_t listen_abort_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_REMOTE_OPEN:
-    /* Close the transport - abruptly closes the socket */
-    pn_transport_close_tail(pn_connection_transport(pn_event_connection(e)));
-    pn_transport_close_head(pn_connection_transport(pn_event_connection(e)));
-    return PN_EVENT_NONE;
-
-   default:
-    /* Don't auto-close the listener to keep the event sequences simple */
-    return listen_handler(th, e);
-  }
-}
-
-/* Verify that pn_transport_close_head/tail aborts a connection without an AMQP protocol close */
-static void test_abort(test_t *t) {
-  test_proactor_t tps[] = { test_proactor(t, open_close_handler), test_proactor(t, listen_abort_handler) };
-  pn_proactor_t *client = tps[0].proactor;
-  pn_listener_t *l = test_listen(&tps[1], "");
-  pn_proactor_connect2(client, NULL, NULL, listener_info(l).connect);
-
-  /* server transport closes */
-  if (TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps))) {
-    TEST_CONDITION(t, "amqp:connection:framing-error", "abort", last_condition);
-    TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  }
-  /* client transport closes */
-  if (TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps))) {
-    TEST_CONDITION(t, "amqp:connection:framing-error", "abort", last_condition);
-    TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  }
-
-  pn_listener_close(l);
-
-  while (TEST_PROACTORS_RUN(tps) != PN_PROACTOR_INACTIVE) {}
-  while (TEST_PROACTORS_RUN(tps) != PN_PROACTOR_INACTIVE) {}
-
-  /* Verify expected event sequences, no unexpected events */
-  TEST_HANDLER_EXPECT(
-    &tps[0].handler,
-    PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN, PN_CONNECTION_BOUND,
-    PN_TRANSPORT_TAIL_CLOSED, PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_CLOSED,
-    PN_PROACTOR_INACTIVE,
-    0);
-
-  TEST_HANDLER_EXPECT(
-    &tps[1].handler,
-    PN_LISTENER_OPEN, PN_LISTENER_ACCEPT,
-    PN_CONNECTION_INIT, PN_CONNECTION_BOUND, PN_CONNECTION_REMOTE_OPEN,
-    PN_TRANSPORT_TAIL_CLOSED, PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_CLOSED,
-    PN_LISTENER_CLOSE,
-    PN_PROACTOR_INACTIVE,
-    0);
-
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Refuse a connection: abort before the AMQP open sequence begins. */
-static pn_event_type_t listen_refuse_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-
-   case PN_CONNECTION_BOUND:
-    /* Close the transport - abruptly closes the socket */
-    pn_transport_close_tail(pn_connection_transport(pn_event_connection(e)));
-    pn_transport_close_head(pn_connection_transport(pn_event_connection(e)));
-    return PN_EVENT_NONE;
-
-   default:
-    /* Don't auto-close the listener to keep the event sequences simple */
-    return listen_handler(th, e);
-  }
-}
-
-/* Verify that pn_transport_close_head/tail aborts a connection without an AMQP protocol close */
-static void test_refuse(test_t *t) {
-  test_proactor_t tps[] = { test_proactor(t, open_close_handler), test_proactor(t, listen_refuse_handler) };
-  pn_proactor_t *client = tps[0].proactor;
-  pn_listener_t *l = test_listen(&tps[1], "");
-  pn_proactor_connect2(client, NULL, NULL, listener_info(l).connect);
-
-  /* client transport closes */
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps)); /* client */
-  TEST_COND_NAME(t, "amqp:connection:framing-error", last_condition);
-
-  pn_listener_close(l);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_PROACTOR_INACTIVE);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_PROACTOR_INACTIVE);
-
-  /* Verify expected event sequences, no unexpected events */
-  TEST_HANDLER_EXPECT(
-    &tps[0].handler,
-    PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN, PN_CONNECTION_BOUND,
-    PN_TRANSPORT_TAIL_CLOSED, PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_CLOSED,
-    PN_PROACTOR_INACTIVE,
-    0);
-
-  TEST_HANDLER_EXPECT(
-    &tps[1].handler,
-    PN_LISTENER_OPEN, PN_LISTENER_ACCEPT,
-    PN_CONNECTION_INIT, PN_CONNECTION_BOUND,
-    PN_TRANSPORT_TAIL_CLOSED, PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_CLOSED,
-    PN_LISTENER_CLOSE,
-    PN_PROACTOR_INACTIVE,
-    0);
-
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Test that INACTIVE event is generated when last connections/listeners closes. */
-static void test_inactive(test_t *t) {
-  test_proactor_t tps[] =  { test_proactor(t, open_wake_handler), test_proactor(t, listen_handler) };
-  pn_proactor_t *client = tps[0].proactor, *server = tps[1].proactor;
-
-  /* Listen, connect, disconnect */
-  pn_listener_t *l = test_listen(&tps[1], "");
-  pn_connection_t *c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  pn_connection_wake(c);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_WAKE, TEST_PROACTORS_RUN(tps));
-  /* Expect TRANSPORT_CLOSED from client and server, INACTIVE from client */
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  /* Immediate timer generates INACTIVE on client (no connections) */
-  pn_proactor_set_timeout(client, 0);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_TIMEOUT, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  /* Connect, set-timer, disconnect */
-  pn_proactor_set_timeout(client, 1000000);
-  c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  pn_connection_wake(c);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_WAKE, TEST_PROACTORS_RUN(tps));
-  /* Expect TRANSPORT_CLOSED from client and server */
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  /* No INACTIVE till timer is cancelled */
-  TEST_CHECK(t, pn_proactor_get(server) == NULL);
-  pn_proactor_cancel_timeout(client);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  /* Server won't be INACTIVE until listener is closed */
-  TEST_CHECK(t, pn_proactor_get(server) == NULL);
-  pn_listener_close(l);
-  TEST_ETYPE_EQUAL(t, PN_LISTENER_CLOSE, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Tests for error handling */
-static void test_errors(test_t *t) {
-  test_proactor_t tps[] =  { test_proactor(t, open_wake_handler), test_proactor(t, listen_handler) };
-  pn_proactor_t *client = tps[0].proactor, *server = tps[1].proactor;
-
-  /* Invalid connect/listen service name */
-  pn_connection_t *c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, "127.0.0.1:xxx");
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps));
-  TEST_COND_DESC(t, "xxx", last_condition);
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  pn_proactor_listen(server, pn_listener(), "127.0.0.1:xxx", 1);
-  TEST_PROACTORS_RUN(tps);
-  TEST_HANDLER_EXPECT(&tps[1].handler, PN_LISTENER_CLOSE, 0); /* CLOSE only, no OPEN */
-  TEST_COND_DESC(t, "xxx", last_condition);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  /* Invalid connect/listen host name */
-  c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, "nosuch.example.com:");
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps));
-  TEST_COND_DESC(t, "nosuch", last_condition);
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  test_handler_clear(&tps[1].handler, 0);
-  pn_proactor_listen(server, pn_listener(), "nosuch.example.com:", 1);
-  TEST_PROACTORS_RUN(tps);
-  TEST_HANDLER_EXPECT(&tps[1].handler, PN_LISTENER_CLOSE, 0); /* CLOSE only, no OPEN */
-  TEST_COND_DESC(t, "nosuch", last_condition);
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  /* Listen on a port already in use */
-  pn_listener_t *l = pn_listener();
-  pn_proactor_listen(server, l, ":0", 1);
-  TEST_ETYPE_EQUAL(t, PN_LISTENER_OPEN, TEST_PROACTORS_RUN(tps));
-  test_handler_clear(&tps[1].handler, 0);
-  struct addrinfo laddr = listener_info(l);
-  pn_proactor_listen(server, pn_listener(), laddr.connect, 1); /* Busy */
-  TEST_PROACTORS_RUN(tps);
-  TEST_HANDLER_EXPECT(&tps[1].handler, PN_LISTENER_CLOSE, 0); /* CLOSE only, no OPEN */
-  TEST_COND_NAME(t, "proton:io", last_condition);
-  pn_listener_close(l);
-  TEST_ETYPE_EQUAL(t, PN_LISTENER_CLOSE, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  /* Connect with no listener */
-  c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, laddr.connect);
-  if (TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps))) {
-    TEST_COND_DESC(t, "refused", last_condition);
-    TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-    TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-  }
-
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Closing the connection during PN_TRANSPORT_ERROR should be a no-op
- * Regression test for: https://issues.apache.org/jira/browse/PROTON-1586
- */
-static pn_event_type_t transport_close_connection_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_TRANSPORT_ERROR:
-    pn_connection_close(pn_event_connection(e));
-    break;
-   default:
-    return open_wake_handler(th, e);
-  }
-  return PN_EVENT_NONE;
-}
-
-/* Closing the connection during PN_TRANSPORT_ERROR due to connection failure should be a no-op
- * Regression test for: https://issues.apache.org/jira/browse/PROTON-1586
- */
-static void test_proton_1586(test_t *t) {
-  test_proactor_t tps[] =  { test_proactor(t, transport_close_connection_handler) };
-  pn_proactor_connect2(tps[0].proactor, NULL, NULL, ":yyy");
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));
-  TEST_COND_DESC(t, ":yyy", last_condition);
-  test_handler_clear(&tps[0].handler, 0); /* Clear events */
-  /* There should be no events generated after PN_TRANSPORT_CLOSED */
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-  TEST_HANDLER_EXPECT(&tps[0].handler,PN_PROACTOR_INACTIVE, 0);
-
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Test that we can control listen/select on ipv6/v4 and listen on both by default */
-static void test_ipv4_ipv6(test_t *t) {
-  test_proactor_t tps[] ={ test_proactor(t, open_close_handler), test_proactor(t, listen_handler) };
-  pn_proactor_t *client = tps[0].proactor, *server = tps[1].proactor;
-
-  /* Listen on all interfaces for IPv4 only. */
-  pn_listener_t *l4 = test_listen(&tps[1], "0.0.0.0");
-  TEST_PROACTORS_DRAIN(tps);
-
-  /* Empty address listens on both IPv4 and IPv6 on all interfaces */
-  pn_listener_t *l = test_listen(&tps[1], "");
-  TEST_PROACTORS_DRAIN(tps);
-
-#define EXPECT_CONNECT(LISTENER, HOST) do {                             \
-    char addr[1024];                                                    \
-    pn_proactor_addr(addr, sizeof(addr), HOST, listener_info(LISTENER).port); \
-    pn_proactor_connect2(client, NULL, NULL, addr);                     \
-    TEST_ETYPE_EQUAL(t, PN_TRANSPORT_CLOSED, TEST_PROACTORS_RUN(tps));  \
-    TEST_COND_EMPTY(t, last_condition);                                 \
-    TEST_PROACTORS_DRAIN(tps);                                          \
-  } while(0)
-
-  EXPECT_CONNECT(l4, "127.0.0.1"); /* v4->v4 */
-  EXPECT_CONNECT(l4, "");          /* local->v4*/
-
-  EXPECT_CONNECT(l, "127.0.0.1"); /* v4->all */
-  EXPECT_CONNECT(l, "");          /* local->all */
-
-  /* Listen on ipv6 loopback, if it fails skip ipv6 tests.
-
-     NOTE: Don't use the unspecified address "::" here - ipv6-disabled platforms
-     may allow listening on "::" without complaining. However they won't have a
-     local ipv6 loopback configured, so "::1" will force an error.
-  */
-  TEST_PROACTORS_DRAIN(tps);
-  pn_listener_t *l6 = pn_listener();
-  pn_proactor_listen(server, l6, "::1:0", 4);
-  pn_event_type_t e = TEST_PROACTORS_RUN(tps);
-  if (e == PN_LISTENER_OPEN && !pn_condition_is_set(last_condition)) {
-    TEST_PROACTORS_DRAIN(tps);
-
-    EXPECT_CONNECT(l6, "::1"); /* v6->v6 */
-    EXPECT_CONNECT(l6, "");    /* local->v6 */
-    EXPECT_CONNECT(l, "::1");  /* v6->all */
-
-    pn_listener_close(l6);
-  } else  {
-    const char *d = pn_condition_get_description(last_condition);
-    TEST_LOGF(t, "skip IPv6 tests: %s %s", pn_event_type_name(e), d ? d : "no condition");
-  }
-
-  pn_listener_close(l);
-  pn_listener_close(l4);
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Make sure we clean up released connections and open sockets correctly */
-static void test_release_free(test_t *t) {
-  test_proactor_t tps[] = { test_proactor(t, open_wake_handler), test_proactor(t, listen_handler) };
-  pn_proactor_t *client = tps[0].proactor;
-  pn_listener_t *l = test_listen(&tps[1], "");
-
-  /* leave one connection to the proactor  */
-  pn_proactor_connect2(client, NULL, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-
-  /* release c1 and free immediately */
-  pn_connection_t *c1 = pn_connection();
-  pn_proactor_connect2(client, c1, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  pn_proactor_release_connection(c1); /* We free but socket should still be cleaned up */
-  pn_connection_free(c1);
-  TEST_CHECK(t, pn_proactor_get(client) == NULL); /* Should be idle */
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);
-
-  /* release c2 and but don't free till after proactor free */
-  pn_connection_t *c2 = pn_connection();
-  pn_proactor_connect2(client, c2, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  pn_proactor_release_connection(c2);
-  TEST_CHECK(t, pn_proactor_get(client) == NULL); /* Should be idle */
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);
-
-  TEST_PROACTORS_DESTROY(tps);
-  pn_connection_free(c2);
-
-  /* Check freeing a listener or connection that was never given to a proactor */
-  pn_listener_free(pn_listener());
-  pn_connection_free(pn_connection());
-}
-
-#define SSL_FILE(NAME) CMAKE_CURRENT_SOURCE_DIR "/ssl-certs/" NAME
-#define SSL_PW "tserverpw"
-/* Windows vs. OpenSSL certificates */
-#if defined(_WIN32)
-#  define CERTIFICATE(NAME) SSL_FILE(NAME "-certificate.p12")
-#  define SET_CREDENTIALS(DOMAIN, NAME)                                 \
-  pn_ssl_domain_set_credentials(DOMAIN, SSL_FILE(NAME "-full.p12"), "", SSL_PW)
-#else
-#  define CERTIFICATE(NAME) SSL_FILE(NAME "-certificate.pem")
-#  define SET_CREDENTIALS(DOMAIN, NAME)                                 \
-  pn_ssl_domain_set_credentials(DOMAIN, CERTIFICATE(NAME), SSL_FILE(NAME "-private-key.pem"), SSL_PW)
-#endif
-
-static pn_event_type_t ssl_handler(test_handler_t *h, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-
-   case PN_CONNECTION_BOUND:
-    TEST_CHECK(h->t, 0 == pn_ssl_init(pn_ssl(pn_event_transport(e)), h->ssl_domain, NULL));
-    return PN_EVENT_NONE;
-
-   case PN_CONNECTION_REMOTE_OPEN: {
-     pn_ssl_t *ssl = pn_ssl(pn_event_transport(e));
-     TEST_CHECK(h->t, ssl);
-     char protocol[256];
-     TEST_CHECK(h->t, pn_ssl_get_protocol_name(ssl, protocol, sizeof(protocol)));
-     TEST_STR_IN(h->t, "TLS", protocol);
-     return PN_CONNECTION_REMOTE_OPEN;
-   }
-   default:
-    return PN_EVENT_NONE;
-  }
-}
-
-static pn_event_type_t ssl_server_handler(test_handler_t *h, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_BOUND:
-    return ssl_handler(h, e);
-   case PN_CONNECTION_REMOTE_OPEN: {
-     pn_event_type_t et = ssl_handler(h, e);
-     pn_connection_open(pn_event_connection(e));
-     return et;
-   }
-   default:
-    return listen_handler(h, e);
-  }
-}
-
-static pn_event_type_t ssl_client_handler(test_handler_t *h, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_BOUND:
-    return ssl_handler(h, e);
-   case PN_CONNECTION_REMOTE_OPEN: {
-     pn_event_type_t et = ssl_handler(h, e);
-     pn_connection_close(pn_event_connection(e));
-     return et;
-   }
-    break;
-   default:
-    return common_handler(h, e);
-  }
-}
-
-/* Test various SSL connections between proactors*/
-static void test_ssl(test_t *t) {
-  if (!pn_ssl_present()) {
-    TEST_LOGF(t, "Skip SSL test, no support");
-    return;
-  }
-
-  test_proactor_t tps[] ={ test_proactor(t, ssl_client_handler), test_proactor(t, ssl_server_handler) };
-  test_proactor_t *client = &tps[0], *server = &tps[1];
-  pn_ssl_domain_t *cd = client->handler.ssl_domain = pn_ssl_domain(PN_SSL_MODE_CLIENT);
-  pn_ssl_domain_t *sd =  server->handler.ssl_domain = pn_ssl_domain(PN_SSL_MODE_SERVER);
-  TEST_CHECK(t, 0 == SET_CREDENTIALS(sd, "tserver"));
-  pn_listener_t *l = test_listen(server, "");
-
-  /* Basic SSL connection */
-  pn_proactor_connect2(client->proactor, NULL, NULL, listener_info(l).connect);
-  /* Open ok at both ends */
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  TEST_COND_EMPTY(t, last_condition);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  TEST_COND_EMPTY(t, last_condition);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);
-
-  /* Verify peer with good hostname */
-  TEST_INT_EQUAL(t, 0, pn_ssl_domain_set_trusted_ca_db(cd, CERTIFICATE("tserver")));
-  TEST_INT_EQUAL(t, 0, pn_ssl_domain_set_peer_authentication(cd, PN_SSL_VERIFY_PEER_NAME, NULL));
-  pn_connection_t *c = pn_connection();
-  pn_connection_set_hostname(c, "test_server");
-  pn_proactor_connect2(client->proactor, c, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  TEST_COND_EMPTY(t, last_condition);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  TEST_COND_EMPTY(t, last_condition);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_TRANSPORT_CLOSED);
-
-  /* Verify peer with bad hostname */
-  c = pn_connection();
-  pn_connection_set_hostname(c, "wrongname");
-  pn_proactor_connect2(client->proactor, c, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps));
-  TEST_CONDITION(t, "amqp:connection:framing-error", "SSL", last_condition);
-  TEST_PROACTORS_DRAIN(tps);
-
-  pn_ssl_domain_free(cd);
-  pn_ssl_domain_free(sd);
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-static void test_proactor_addr(test_t *t) {
-  /* Test the address formatter */
-  char addr[PN_MAX_ADDR];
-  pn_proactor_addr(addr, sizeof(addr), "foo", "bar");
-  TEST_STR_EQUAL(t, "foo:bar", addr);
-  pn_proactor_addr(addr, sizeof(addr), "foo", "");
-  TEST_STR_EQUAL(t, "foo:", addr);
-  pn_proactor_addr(addr, sizeof(addr), "foo", NULL);
-  TEST_STR_EQUAL(t, "foo:", addr);
-  pn_proactor_addr(addr, sizeof(addr), "", "bar");
-  TEST_STR_EQUAL(t, ":bar", addr);
-  pn_proactor_addr(addr, sizeof(addr), NULL, "bar");
-  TEST_STR_EQUAL(t, ":bar", addr);
-  pn_proactor_addr(addr, sizeof(addr), "1:2:3:4", "5");
-  TEST_STR_EQUAL(t, "1:2:3:4:5", addr);
-  pn_proactor_addr(addr, sizeof(addr), "1:2:3:4", "");
-  TEST_STR_EQUAL(t, "1:2:3:4:", addr);
-  pn_proactor_addr(addr, sizeof(addr), "1:2:3:4", NULL);
-  TEST_STR_EQUAL(t, "1:2:3:4:", addr);
-}
-
-static void test_parse_addr(test_t *t) {
-  char buf[1024];
-  const char *host, *port;
-
-  TEST_CHECK(t, 0 == pni_parse_addr("foo:bar", buf, sizeof(buf), &host, &port));
-  TEST_STR_EQUAL(t, "foo", host);
-  TEST_STR_EQUAL(t, "bar", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr("foo:", buf, sizeof(buf), &host, &port));
-  TEST_STR_EQUAL(t, "foo", host);
-  TEST_STR_EQUAL(t, "5672", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr(":bar", buf, sizeof(buf), &host, &port));
-  TEST_CHECKF(t, NULL == host, "expected null, got: %s", host);
-  TEST_STR_EQUAL(t, "bar", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr(":", buf, sizeof(buf), &host, &port));
-  TEST_CHECKF(t, NULL == host, "expected null, got: %s", host);
-  TEST_STR_EQUAL(t, "5672", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr(":amqps", buf, sizeof(buf), &host, &port));
-  TEST_STR_EQUAL(t, "5671", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr(":amqp", buf, sizeof(buf), &host, &port));
-  TEST_STR_EQUAL(t, "5672", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr("::1:2:3", buf, sizeof(buf), &host, &port));
-  TEST_STR_EQUAL(t, "::1:2", host);
-  TEST_STR_EQUAL(t, "3", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr(":::", buf, sizeof(buf), &host, &port));
-  TEST_STR_EQUAL(t, "::", host);
-  TEST_STR_EQUAL(t, "5672", port);
-
-  TEST_CHECK(t, 0 == pni_parse_addr("", buf, sizeof(buf), &host, &port));
-  TEST_CHECKF(t, NULL == host, "expected null, got: %s", host);
-  TEST_STR_EQUAL(t, "5672", port);
-}
-
-/* Test pn_proactor_addr functions */
-
-static void test_netaddr(test_t *t) {
-  test_proactor_t tps[] ={ test_proactor(t, open_wake_handler), test_proactor(t, listen_handler) };
-  pn_proactor_t *client = tps[0].proactor;
-  /* Use IPv4 to get consistent results all platforms */
-  pn_listener_t *l = test_listen(&tps[1], "127.0.0.1");
-  pn_connection_t *c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, listener_info(l).connect);
-  if (!TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps))) {
-    TEST_COND_EMPTY(t, last_condition); /* Show the last condition */
-    return;                     /* don't continue if connection is closed */
-  }
-
-  /* client remote, client local, server remote and server local address strings */
-  char cr[1024], cl[1024], sr[1024], sl[1024];
-
-  pn_transport_t *ct = pn_connection_transport(c);
-  const pn_netaddr_t *na = pn_transport_remote_addr(ct);
-  pn_netaddr_str(na, cr, sizeof(cr));
-  TEST_STR_IN(t, listener_info(l).port, cr); /* remote address has listening port */
-
-  pn_connection_t *s = last_accepted; /* server side of the connection */
-
-  pn_transport_t *st = pn_connection_transport(s);
-  if (!TEST_CHECK(t, st)) return;
-  pn_netaddr_str(pn_transport_local_addr(st), sl, sizeof(sl));
-  TEST_STR_EQUAL(t, cr, sl);  /* client remote == server local */
-
-  pn_netaddr_str(pn_transport_local_addr(ct), cl, sizeof(cl));
-  pn_netaddr_str(pn_transport_remote_addr(st), sr, sizeof(sr));
-  TEST_STR_EQUAL(t, cl, sr);    /* client local == server remote */
-
-  char host[MAX_STR] = "";
-  char serv[MAX_STR] = "";
-  int err = pn_netaddr_host_port(na, host, sizeof(host), serv, sizeof(serv));
-  TEST_CHECK(t, 0 == err);
-  TEST_STR_EQUAL(t, "127.0.0.1", host);
-  TEST_STR_EQUAL(t, listener_info(l).port, serv);
-
-  /* Make sure you can use NULL, 0 to get length of address string without a crash */
-  size_t len = pn_netaddr_str(pn_transport_local_addr(ct), NULL, 0);
-  TEST_CHECKF(t, strlen(cl) == len, "%d != %d", strlen(cl), len);
-
-  TEST_PROACTORS_DRAIN(tps);
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-/* Test pn_proactor_disconnect */
-static void test_disconnect(test_t *t) {
-  test_proactor_t tps[] ={ test_proactor(t, open_wake_handler), test_proactor(t, listen_handler) };
-  pn_proactor_t *client = tps[0].proactor, *server = tps[1].proactor;
-
-  /* Start two listeners */
-  pn_listener_t *l = test_listen(&tps[1], "");
-  pn_listener_t *l2 = test_listen(&tps[1], "");
-
-  /* Only wait for one connection to remote-open before disconnect */
-  pn_connection_t *c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, listener_info(l).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  pn_connection_t *c2 = pn_connection();
-  pn_proactor_connect2(client, c2, NULL, listener_info(l2).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  TEST_PROACTORS_DRAIN(tps);
-
-  /* Disconnect the client proactor */
-  pn_condition_t *cond = pn_condition();
-  pn_condition_set_name(cond, "test-name");
-  pn_condition_set_description(cond, "test-description");
-  pn_proactor_disconnect(client, cond);
-  /* Verify expected client side first */
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, test_proactors_run(&tps[0], 1));
-  TEST_CONDITION(t, "test-name", "test-description", last_condition);
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, test_proactors_run(&tps[0], 1));
-  TEST_CONDITION(t, "test-name", "test-description", last_condition);
-  test_proactors_run_until(&tps[0], 1, PN_PROACTOR_INACTIVE);
-
-  /* Now check server sees the disconnects */
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps));
-  TEST_CONDITION(t, "amqp:connection:framing-error", "connection aborted", last_condition);
-  TEST_ETYPE_EQUAL(t, PN_TRANSPORT_ERROR, TEST_PROACTORS_RUN(tps));
-  TEST_CONDITION(t, "amqp:connection:framing-error", "connection aborted", last_condition);
-  TEST_PROACTORS_DRAIN(tps);
-
-  /* Now disconnect the server end (the listeners) */
-  pn_proactor_disconnect(server, cond);
-  pn_condition_free(cond);
-
-  TEST_ETYPE_EQUAL(t, PN_LISTENER_CLOSE, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_LISTENER_CLOSE, TEST_PROACTORS_RUN(tps));
-  TEST_ETYPE_EQUAL(t, PN_PROACTOR_INACTIVE, TEST_PROACTORS_RUN(tps));
-
-  /* Make sure the proactors are still functional */
-  pn_listener_t *l3 = test_listen(&tps[1], "");
-  pn_proactor_connect2(client, NULL, NULL, listener_info(l3).connect);
-  TEST_ETYPE_EQUAL(t, PN_CONNECTION_REMOTE_OPEN, TEST_PROACTORS_RUN(tps));
-  pn_proactor_disconnect(client, NULL);
-
-  TEST_PROACTORS_DRAIN(tps);
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-struct message_stream_context {
-  pn_link_t *sender;
-  pn_delivery_t *dlv;
-  pn_rwbytes_t send_buf, recv_buf;
-  ssize_t size, sent, received;
-  bool complete;
-};
-
-#define FRAME 512                   /* Smallest legal frame */
-#define CHUNK (FRAME + FRAME/2)     /* Chunk overflows frame */
-#define BODY (CHUNK*3 + CHUNK/2)    /* Body doesn't fit into chunks */
-
-static pn_event_type_t message_stream_handler(test_handler_t *th, pn_event_t *e) {
-  struct message_stream_context *ctx = (struct message_stream_context*)th->context;
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_BOUND:
-    pn_transport_set_max_frame(pn_event_transport(e), FRAME);
-    return PN_EVENT_NONE;
-
-   case PN_SESSION_INIT:
-    pn_session_set_incoming_capacity(pn_event_session(e), FRAME); /* Single frame incoming */
-    pn_session_set_outgoing_window(pn_event_session(e), 1);       /* Single frame outgoing */
-    return PN_EVENT_NONE;
-
-   case PN_LINK_REMOTE_OPEN:
-    common_handler(th, e);
-    if (pn_link_is_receiver(pn_event_link(e))) {
-      pn_link_flow(pn_event_link(e), 1);
-    } else {
-      ctx->sender = pn_event_link(e);
-    }
-    return PN_EVENT_NONE;
-
-   case PN_LINK_FLOW:           /* Start a delivery */
-    if (pn_link_is_sender(pn_event_link(e)) && !ctx->dlv) {
-      ctx->dlv = pn_delivery(pn_event_link(e), pn_dtag("x", 1));
-    }
-    return PN_LINK_FLOW;
-
-   case PN_CONNECTION_WAKE: {     /* Send a chunk */
-     ssize_t remains = ctx->size - ctx->sent;
-     ssize_t n = (CHUNK < remains) ? CHUNK : remains;
-     TEST_CHECK(th->t, n == pn_link_send(ctx->sender, ctx->send_buf.start + ctx->sent, n));
-     ctx->sent += n;
-     if (ctx->sent == ctx->size) {
-       TEST_CHECK(th->t, pn_link_advance(ctx->sender));
-     }
-     return PN_CONNECTION_WAKE;
-   }
-
-   case PN_DELIVERY: {          /* Receive a delivery - smaller than a chunk? */
-     pn_delivery_t *dlv = pn_event_delivery(e);
-     if (pn_delivery_readable(dlv)) {
-       ssize_t n = pn_delivery_pending(dlv);
-       rwbytes_ensure(&ctx->recv_buf, ctx->received + n);
-       TEST_ASSERT(n == pn_link_recv(pn_event_link(e), ctx->recv_buf.start + ctx->received, n));
-       ctx->received += n;
-     }
-     ctx->complete = !pn_delivery_partial(dlv);
-     return PN_DELIVERY;
-   }
-
-   default:
-    return common_handler(th, e);
-  }
-}
-
-/* Test sending/receiving a message in chunks */
-static void test_message_stream(test_t *t) {
-  test_proactor_t tps[] ={
-    test_proactor(t, message_stream_handler),
-    test_proactor(t, message_stream_handler)
-  };
-  pn_proactor_t *client = tps[0].proactor;
-  pn_listener_t *l = test_listen(&tps[1], "");
-  struct message_stream_context ctx = { 0 };
-  tps[0].handler.context = &ctx;
-  tps[1].handler.context = &ctx;
-
-  /* Encode a large (not very) message to send in chunks */
-  char *body = (char*)malloc(BODY);
-  memset(body, 'x', BODY);
-  pn_message_t *m = pn_message();
-  pn_data_put_binary(pn_message_body(m), pn_bytes(BODY, body));
-  free(body);
-  ctx.size = message_encode(m, &ctx.send_buf);
-  pn_message_free(m);
-
-  pn_connection_t *c = pn_connection();
-  pn_proactor_connect2(client, c, NULL, listener_info(l).connect);
-  pn_session_t *ssn = pn_session(c);
-  pn_session_open(ssn);
-  pn_link_t *snd = pn_sender(ssn, "x");
-  pn_link_open(snd);
-  TEST_PROACTORS_RUN_UNTIL(tps, PN_LINK_FLOW);
-
-  /* Send and receive the message in chunks */
-  do {
-    pn_connection_wake(c);      /* Initiate send/receive of one chunk */
-    do {                        /* May be multiple receives for one send */
-      TEST_PROACTORS_RUN_UNTIL(tps, PN_DELIVERY);
-    } while (ctx.received < ctx.sent);
-  } while (!ctx.complete);
-  TEST_CHECK(t, ctx.received == ctx.size);
-  TEST_CHECK(t, ctx.sent == ctx.size);
-  TEST_CHECK(t, !memcmp(ctx.send_buf.start, ctx.recv_buf.start, ctx.size));
-
-  free(ctx.send_buf.start);
-  free(ctx.recv_buf.start);
-  TEST_PROACTORS_DESTROY(tps);
-}
-
-int main(int argc, char **argv) {
-  int failed = 0;
-  last_condition = pn_condition();
-  RUN_ARGV_TEST(failed, t, test_inactive(&t));
-  RUN_ARGV_TEST(failed, t, test_interrupt_timeout(&t));
-  RUN_ARGV_TEST(failed, t, test_errors(&t));
-  RUN_ARGV_TEST(failed, t, test_proton_1586(&t));
-  RUN_ARGV_TEST(failed, t, test_client_server(&t));
-  RUN_ARGV_TEST(failed, t, test_connection_wake(&t));
-  RUN_ARGV_TEST(failed, t, test_ipv4_ipv6(&t));
-  RUN_ARGV_TEST(failed, t, test_release_free(&t));
-#if !defined(_WIN32)
-  RUN_ARGV_TEST(failed, t, test_ssl(&t));
-#endif
-  RUN_ARGV_TEST(failed, t, test_proactor_addr(&t));
-  RUN_ARGV_TEST(failed, t, test_parse_addr(&t));
-  RUN_ARGV_TEST(failed, t, test_netaddr(&t));
-  RUN_ARGV_TEST(failed, t, test_disconnect(&t));
-  RUN_ARGV_TEST(failed, t, test_abort(&t));
-  RUN_ARGV_TEST(failed, t, test_refuse(&t));
-  RUN_ARGV_TEST(failed, t, test_message_stream(&t));
-  pn_condition_free(last_condition);
-  return failed;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/proactor_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/proactor_test.cpp b/c/tests/proactor_test.cpp
new file mode 100644
index 0000000..971c231
--- /dev/null
+++ b/c/tests/proactor_test.cpp
@@ -0,0 +1,831 @@
+/*
+ * 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 "../src/proactor/proactor-internal.h"
+#include "./pn_test_proactor.hpp"
+#include "./test_config.h"
+
+#include <proton/condition.h>
+#include <proton/connection.h>
+#include <proton/delivery.h>
+#include <proton/event.h>
+#include <proton/link.h>
+#include <proton/listener.h>
+#include <proton/netaddr.h>
+#include <proton/proactor.h>
+#include <proton/session.h>
+#include <proton/ssl.h>
+#include <proton/transport.h>
+
+#include <string.h>
+
+#include <iostream>
+
+using namespace pn_test;
+using Catch::Matchers::Contains;
+using Catch::Matchers::Equals;
+
+/* Test that interrupt and timeout events cause pn_proactor_wait() to return. */
+TEST_CASE("proactor_interrupt_timeout") {
+  proactor p;
+
+  CHECK(pn_proactor_get(p) == NULL); /* idle */
+  pn_proactor_interrupt(p);
+  CHECK(PN_PROACTOR_INTERRUPT == p.wait_next());
+  CHECK(pn_proactor_get(p) == NULL); /* idle */
+
+  /* Set an immediate timeout */
+  pn_proactor_set_timeout(p, 0);
+  CHECK(PN_PROACTOR_TIMEOUT == p.wait_next());
+  CHECK(PN_PROACTOR_INACTIVE == p.wait_next());
+
+  /* Set a (very short) timeout */
+  pn_proactor_set_timeout(p, 1);
+  CHECK(PN_PROACTOR_TIMEOUT == p.wait_next());
+  CHECK(PN_PROACTOR_INACTIVE == p.wait_next());
+
+  /* Set and cancel a timeout, make sure we don't get the timeout event */
+  pn_proactor_set_timeout(p, 10000000);
+  pn_proactor_cancel_timeout(p);
+  CHECK(PN_PROACTOR_INACTIVE == p.wait_next());
+  CHECK(pn_proactor_get(p) == NULL); /* idle */
+}
+
+namespace {
+
+class common_handler : public handler {
+  handler *accept_; // Handler for accepted connections
+
+public:
+  common_handler(handler *accept = 0) : accept_(accept) {}
+
+  bool handle(pn_event_t *e) CATCH_OVERRIDE {
+    switch (pn_event_type(e)) {
+      /* Always stop on these noteworthy events */
+    case PN_TRANSPORT_ERROR:
+    case PN_LISTENER_OPEN:
+    case PN_LISTENER_CLOSE:
+    case PN_PROACTOR_INACTIVE:
+      return true;
+
+    case PN_LISTENER_ACCEPT:
+      listener = pn_event_listener(e);
+      connection = pn_connection();
+      if (accept_) pn_connection_set_context(connection, accept_);
+      pn_listener_accept2(listener, connection, NULL);
+      return false;
+
+      // Return remote opens
+    case PN_CONNECTION_REMOTE_OPEN:
+      pn_connection_open(pn_event_connection(e));
+      return false;
+    case PN_SESSION_REMOTE_OPEN:
+      pn_session_open(pn_event_session(e));
+      return false;
+    case PN_LINK_REMOTE_OPEN:
+      pn_link_open(pn_event_link(e));
+      return false;
+
+      // Return remote closes
+    case PN_CONNECTION_REMOTE_CLOSE:
+      pn_connection_close(pn_event_connection(e));
+      return false;
+    case PN_SESSION_REMOTE_CLOSE:
+      pn_session_close(pn_event_session(e));
+      return false;
+    case PN_LINK_REMOTE_CLOSE:
+      pn_link_close(pn_event_link(e));
+      return false;
+
+    default:
+      return false;
+    }
+  }
+};
+
+/* close a connection when it is remote open */
+struct close_on_open_handler : public common_handler {
+  bool handle(pn_event_t *e) CATCH_OVERRIDE {
+    switch (pn_event_type(e)) {
+    case PN_CONNECTION_REMOTE_OPEN:
+      pn_connection_close(pn_event_connection(e));
+      return false;
+    default:
+      return common_handler::handle(e);
+    }
+  }
+};
+
+} // namespace
+
+/* Test simple client/server connection that opens and closes */
+TEST_CASE("proactor_connect") {
+  close_on_open_handler h;
+  proactor p(&h);
+  /* Connect and wait for close at both ends */
+  pn_listener_t *l = p.listen(":0", &h);
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  p.connect(l);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+}
+
+namespace {
+/* Return on connection open, close and return on wake */
+struct close_on_wake_handler : public common_handler {
+  bool handle(pn_event_t *e) CATCH_OVERRIDE {
+    switch (pn_event_type(e)) {
+    case PN_CONNECTION_WAKE:
+      pn_connection_close(pn_event_connection(e));
+      return true;
+    default:
+      return common_handler::handle(e);
+    }
+  }
+};
+} // namespace
+
+// Test waking up a connection that is idle
+TEST_CASE("proactor_connection_wake") {
+  common_handler h;
+  proactor p(&h);
+  close_on_wake_handler wh;
+  pn_listener_t *l = p.listen(":0");
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  pn_connection_t *c = p.connect(l, &wh);
+  pn_incref(c); /* Keep a reference for wake() after free */
+
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  CHECK(!pn_proactor_get(p)); /* Should be idle */
+  pn_connection_wake(c);
+  REQUIRE_RUN(p, PN_CONNECTION_WAKE);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED); /* Both ends */
+
+  /* Verify we don't get a wake after close even if they happen together */
+  pn_connection_t *c2 = p.connect(l, &wh);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  pn_connection_wake(c2);
+  pn_proactor_disconnect(p, NULL);
+  pn_connection_wake(c2);
+
+  for (pn_event_type_t et = p.run(); et != PN_PROACTOR_INACTIVE; et = p.run()) {
+    switch (et) {
+    case PN_TRANSPORT_ERROR:
+    case PN_TRANSPORT_CLOSED:
+    case PN_LISTENER_CLOSE:
+      break; // expected
+    default:
+      FAIL("Unexpected event type: " << et);
+    }
+  }
+  // The pn_connection_t is still valid so wake is legal but a no-op.
+  // Make sure there's no memory error.
+  pn_connection_wake(c);
+  pn_decref(c);
+}
+
+namespace {
+struct abort_handler : public common_handler {
+  bool handle(pn_event_t *e) {
+    switch (pn_event_type(e)) {
+    case PN_CONNECTION_REMOTE_OPEN:
+      /* Close the transport - abruptly closes the socket */
+      pn_transport_close_tail(pn_connection_transport(pn_event_connection(e)));
+      pn_transport_close_head(pn_connection_transport(pn_event_connection(e)));
+      return false;
+
+    default:
+      return common_handler::handle(e);
+    }
+  }
+};
+} // namespace
+
+/* Verify that pn_transport_close_head/tail aborts a connection without an AMQP
+ * protocol close */
+TEST_CASE("proactor_abort") {
+  abort_handler sh; // Handle listener and server side of connection
+  proactor p(&sh);
+  pn_listener_t *l = p.listen();
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  common_handler ch; // Handle client side of connection
+  pn_connection_t *c = p.connect(l, &ch);
+
+  /* server transport closes */
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*sh.last_condition,
+             cond_matches("amqp:connection:framing-error", "abort"));
+
+  /* client transport closes */
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*ch.last_condition,
+             cond_matches("amqp:connection:framing-error", "abort"));
+  pn_listener_close(l);
+  REQUIRE_RUN(p, PN_LISTENER_CLOSE);
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+
+  /* Verify expected event sequences, no unexpected events */
+  CHECK_THAT(ETYPES(PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN,
+                    PN_CONNECTION_BOUND, PN_TRANSPORT_TAIL_CLOSED,
+                    PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED,
+                    PN_TRANSPORT_CLOSED),
+             Equals(ch.log_clear()));
+  CHECK_THAT(ETYPES(PN_LISTENER_OPEN, PN_LISTENER_ACCEPT, PN_CONNECTION_INIT,
+                    PN_CONNECTION_BOUND, PN_CONNECTION_REMOTE_OPEN,
+                    PN_TRANSPORT_TAIL_CLOSED, PN_TRANSPORT_ERROR,
+                    PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_CLOSED,
+                    PN_LISTENER_CLOSE, PN_PROACTOR_INACTIVE),
+             Equals(sh.log_clear()));
+}
+
+/* Test that INACTIVE event is generated when last connections/listeners closes.
+ */
+TEST_CASE("proactor_inactive") {
+  close_on_wake_handler h;
+  proactor p(&h);
+
+  /* Listen, connect, disconnect */
+  pn_listener_t *l = p.listen();
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  pn_connection_t *c = p.connect(l, &h);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  pn_connection_wake(c);
+  REQUIRE_RUN(p, PN_CONNECTION_WAKE);
+  /* Expect TRANSPORT_CLOSED both ends */
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  pn_listener_close(l);
+  REQUIRE_RUN(p, PN_LISTENER_CLOSE);
+  /* Immediate timer generates INACTIVE (no connections) */
+  pn_proactor_set_timeout(p, 0);
+  REQUIRE_RUN(p, PN_PROACTOR_TIMEOUT);
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+
+  /* Connect, set-timer, disconnect */
+  l = p.listen();
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  c = p.connect(l, &h);
+  pn_proactor_set_timeout(p, 1000000);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  pn_connection_wake(c);
+  REQUIRE_RUN(p, PN_CONNECTION_WAKE);
+  /* Expect TRANSPORT_CLOSED from client and server */
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  pn_listener_close(l);
+  REQUIRE_RUN(p, PN_LISTENER_CLOSE);
+  /* No INACTIVE till timer is cancelled */
+  CHECK(!pn_proactor_get(p)); // idle
+  pn_proactor_cancel_timeout(p);
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+}
+
+/* Tests for error handling */
+TEST_CASE("proactor_errors") {
+  close_on_wake_handler h;
+  proactor p(&h);
+  /* Invalid connect/listen service name */
+  p.connect("127.0.0.1:xxx");
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*h.last_condition, cond_matches("proton:io", "xxx"));
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+
+  pn_listener_t *l = pn_listener();
+  pn_proactor_listen(p, l, "127.0.0.1:xxx", 1);
+  REQUIRE_RUN(p, PN_LISTENER_CLOSE);
+  CHECK_THAT(*h.last_condition, cond_matches("proton:io", "xxx"));
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+
+  /* Invalid connect/listen host name */
+  p.connect("nosuch.example.com:");
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*h.last_condition, cond_matches("proton:io", "nosuch"));
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+
+  pn_proactor_listen(p, pn_listener(), "nosuch.example.com:", 1);
+  REQUIRE_RUN(p, PN_LISTENER_CLOSE);
+  CHECK_THAT(*h.last_condition, cond_matches("proton:io", "nosuch"));
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+
+  /* Listen on a port already in use */
+  l = p.listen(":0");
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  std::string laddr = ":" + listening_port(l);
+  p.listen(laddr);
+  REQUIRE_RUN(p, PN_LISTENER_CLOSE);
+  CHECK_THAT(*h.last_condition, cond_matches("proton:io"));
+
+  pn_listener_close(l);
+  REQUIRE_RUN(p, PN_LISTENER_CLOSE);
+  REQUIRE_RUN(p, PN_PROACTOR_INACTIVE);
+
+  /* Connect with no listener */
+  p.connect(laddr);
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*h.last_condition, cond_matches("proton:io", "refused"));
+}
+
+namespace {
+/* Closing the connection during PN_TRANSPORT_ERROR should be a no-op
+ * Regression test for: https://issues.apache.org/jira/browse/PROTON-1586
+ */
+struct transport_close_connection_handler : public common_handler {
+  bool handle(pn_event_t *e) {
+    switch (pn_event_type(e)) {
+    case PN_TRANSPORT_ERROR:
+      pn_connection_close(pn_event_connection(e));
+      break;
+    default:
+      return common_handler::handle(e);
+    }
+    return PN_EVENT_NONE;
+  }
+};
+} // namespace
+
+/* Closing the connection during PN_TRANSPORT_ERROR due to connection failure
+ * should be a no-op. Regression test for:
+ * https://issues.apache.org/jira/browse/PROTON-1586
+ */
+TEST_CASE("proactor_proton_1586") {
+  transport_close_connection_handler h;
+  proactor p(&h);
+  p.connect(":yyy");
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  CHECK_THAT(*h.last_condition, cond_matches("proton:io", ":yyy"));
+
+  // No events expected after PN_TRANSPORT_CLOSED, proactor is inactive.
+  CHECK(PN_PROACTOR_INACTIVE == p.wait_next());
+}
+
+/* Test that we can control listen/select on ipv6/v4 and listen on both by
+ * default */
+TEST_CASE("proactor_ipv4_ipv6") {
+  close_on_open_handler h;
+  proactor p(&h);
+
+  /* Listen on all interfaces for IPv4 only. */
+  pn_listener_t *l4 = p.listen("0.0.0.0");
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  /* Empty address listens on both IPv4 and IPv6 on all interfaces */
+  pn_listener_t *l = p.listen(":0");
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+
+#define EXPECT_CONNECT(LISTENER, HOST)                                         \
+  do {                                                                         \
+    p.connect(std::string(HOST) + ":" + listening_port(LISTENER));             \
+    REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);                                       \
+    CHECK_THAT(*h.last_condition, cond_empty());                               \
+  } while (0)
+
+  EXPECT_CONNECT(l4, "127.0.0.1"); /* v4->v4 */
+  EXPECT_CONNECT(l4, "");          /* local->v4*/
+
+  EXPECT_CONNECT(l, "127.0.0.1"); /* v4->all */
+  EXPECT_CONNECT(l, "");          /* local->all */
+
+  /* Listen on ipv6 loopback, if it fails skip ipv6 tests.
+
+     NOTE: Don't use the unspecified address "::" here - ipv6-disabled platforms
+     may allow listening on "::" without complaining. However they won't have a
+     local ipv6 loopback configured, so "::1" will force an error.
+  */
+  pn_listener_t *l6 = pn_listener();
+  pn_proactor_listen(p, l6, "::1:0", 4);
+  pn_event_type_t e = p.run();
+  if (e == PN_LISTENER_OPEN && !pn_condition_is_set(h.last_condition)) {
+    EXPECT_CONNECT(l6, "::1"); /* v6->v6 */
+    EXPECT_CONNECT(l6, "");    /* local->v6 */
+    EXPECT_CONNECT(l, "::1");  /* v6->all */
+    pn_listener_close(l6);
+  } else {
+    WARN("skip IPv6 tests: %s %s" << e << *h.last_condition);
+  }
+
+  pn_listener_close(l);
+  pn_listener_close(l4);
+}
+
+/* Make sure we clean up released connections and open sockets
+ * correctly */
+TEST_CASE("proactor_release_free") {
+  common_handler h;
+  proactor p(&h);
+
+  pn_listener_t *l = p.listen(":0");
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  /* leave one connection to the proactor  */
+  pn_connection_t *c = p.connect(l);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+
+  {
+    /* release c1 and free immediately */
+    auto_free<pn_connection_t, pn_connection_free> c1(p.connect(l));
+    REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+    REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+    pn_proactor_release_connection(c1);
+  }
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+
+  /* release c2 and but don't free till after proactor free */
+  auto_free<pn_connection_t, pn_connection_free> c2(p.connect(l));
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  pn_proactor_release_connection(c2);
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+
+  // OK to free a listener/connection that was never used by a
+  // proactor.
+  pn_listener_free(pn_listener());
+  pn_connection_free(pn_connection());
+}
+
+#define SSL_FILE(NAME) CMAKE_CURRENT_SOURCE_DIR "/ssl-certs/" NAME
+#define SSL_PW "tserverpw"
+/* Windows vs. OpenSSL certificates */
+#if defined(_WIN32)
+#define CERTIFICATE(NAME) SSL_FILE(NAME "-certificate.p12")
+#define SET_CREDENTIALS(DOMAIN, NAME)                                          \
+  pn_ssl_domain_set_credentials(DOMAIN, SSL_FILE(NAME "-full.p12"), "", SSL_PW)
+#else
+#define CERTIFICATE(NAME) SSL_FILE(NAME "-certificate.pem")
+#define SET_CREDENTIALS(DOMAIN, NAME)                                          \
+  pn_ssl_domain_set_credentials(DOMAIN, CERTIFICATE(NAME),                     \
+                                SSL_FILE(NAME "-private-key.pem"), SSL_PW)
+#endif
+
+namespace {
+
+struct ssl_handler : public common_handler {
+  auto_free<pn_ssl_domain_t, pn_ssl_domain_free> ssl_domain;
+
+  ssl_handler(pn_ssl_domain_t *d) : ssl_domain(d) {}
+
+  bool handle(pn_event_t *e) {
+    switch (pn_event_type(e)) {
+
+    case PN_CONNECTION_BOUND:
+      CHECK(0 == pn_ssl_init(pn_ssl(pn_event_transport(e)), ssl_domain, NULL));
+      return false;
+
+    case PN_CONNECTION_REMOTE_OPEN: {
+      pn_ssl_t *ssl = pn_ssl(pn_event_transport(e));
+      CHECK(ssl);
+      char protocol[256];
+      CHECK(pn_ssl_get_protocol_name(ssl, protocol, sizeof(protocol)));
+      CHECK_THAT(protocol, Contains("TLS"));
+      pn_connection_t *c = pn_event_connection(e);
+      if (pn_connection_state(c) & PN_LOCAL_ACTIVE) {
+        pn_connection_close(c); // Client closes on completion.
+      } else {
+        pn_connection_open(c); // Server returns the OPEN
+      }
+      return true;
+    }
+    default:
+      return common_handler::handle(e);
+    }
+  }
+};
+
+} // namespace
+
+/* Test various SSL connections between proactors*/
+TEST_CASE("proactor_ssl") {
+  if (!pn_ssl_present()) {
+    WARN("Skip SSL tests, not available");
+    return;
+  }
+
+  ssl_handler client(pn_ssl_domain(PN_SSL_MODE_CLIENT));
+  ssl_handler server(pn_ssl_domain(PN_SSL_MODE_SERVER));
+  CHECK(0 == SET_CREDENTIALS(server.ssl_domain, "tserver"));
+  proactor p;
+  common_handler listener(&server); // Use server for accepted connections
+  pn_listener_t *l = p.listen(":0", &listener);
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+
+  /* Basic SSL connection */
+  p.connect(l, &client);
+  /* Open ok at both ends */
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  CHECK_THAT(*server.last_condition, cond_empty());
+  CHECK_THAT(*client.last_condition, cond_empty());
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+
+  /* Verify peer with good hostname */
+  pn_ssl_domain_t *cd = client.ssl_domain;
+  REQUIRE(0 == pn_ssl_domain_set_trusted_ca_db(cd, CERTIFICATE("tserver")));
+  REQUIRE(0 == pn_ssl_domain_set_peer_authentication(
+                   cd, PN_SSL_VERIFY_PEER_NAME, NULL));
+  pn_connection_t *c = pn_connection();
+  pn_connection_set_hostname(c, "test_server");
+  p.connect(l, &client, c);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  CHECK_THAT(*server.last_condition, cond_empty());
+  CHECK_THAT(*client.last_condition, cond_empty());
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+  REQUIRE_RUN(p, PN_TRANSPORT_CLOSED);
+
+  /* Verify peer with bad hostname */
+  c = pn_connection();
+  pn_connection_set_hostname(c, "wrongname");
+  p.connect(l, &client, c);
+  REQUIRE_RUN(p, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*client.last_condition,
+             cond_matches("amqp:connection:framing-error", "SSL"));
+}
+
+TEST_CASE("proactor_addr") {
+  /* Test the address formatter */
+  char addr[PN_MAX_ADDR];
+  pn_proactor_addr(addr, sizeof(addr), "foo", "bar");
+  CHECK_THAT("foo:bar", Equals(addr));
+  pn_proactor_addr(addr, sizeof(addr), "foo", "");
+  CHECK_THAT("foo:", Equals(addr));
+  pn_proactor_addr(addr, sizeof(addr), "foo", NULL);
+  CHECK_THAT("foo:", Equals(addr));
+  pn_proactor_addr(addr, sizeof(addr), "", "bar");
+  CHECK_THAT(":bar", Equals(addr));
+  pn_proactor_addr(addr, sizeof(addr), NULL, "bar");
+  CHECK_THAT(":bar", Equals(addr));
+  pn_proactor_addr(addr, sizeof(addr), "1:2:3:4", "5");
+  CHECK_THAT("1:2:3:4:5", Equals(addr));
+  pn_proactor_addr(addr, sizeof(addr), "1:2:3:4", "");
+  CHECK_THAT("1:2:3:4:", Equals(addr));
+  pn_proactor_addr(addr, sizeof(addr), "1:2:3:4", NULL);
+  CHECK_THAT("1:2:3:4:", Equals(addr));
+}
+
+/* Test pn_proactor_addr functions */
+
+TEST_CASE("proactor_netaddr") {
+  common_handler h;
+  proactor p(&h);
+  /* Use IPv4 to get consistent results all platforms */
+  pn_listener_t *l = p.listen("127.0.0.1");
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+  pn_connection_t *c = p.connect(l);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+  REQUIRE_RUN(p, PN_CONNECTION_REMOTE_OPEN);
+
+  // client remote, client local, server remote and server
+  // local address strings
+  char cr[1024], cl[1024], sr[1024], sl[1024];
+
+  pn_transport_t *ct = pn_connection_transport(c);
+  const pn_netaddr_t *na = pn_transport_remote_addr(ct);
+  pn_netaddr_str(na, cr, sizeof(cr));
+  CHECK_THAT(cr, Contains(listening_port(l)));
+
+  pn_connection_t *s = h.connection; /* server side of the connection */
+
+  pn_transport_t *st = pn_connection_transport(s);
+  pn_netaddr_str(pn_transport_local_addr(st), sl, sizeof(sl));
+  CHECK_THAT(cr, Equals(sl)); /* client remote == server local */
+
+  pn_netaddr_str(pn_transport_local_addr(ct), cl, sizeof(cl));
+  pn_netaddr_str(pn_transport_remote_addr(st), sr, sizeof(sr));
+  CHECK_THAT(cl, Equals(sr)); /* client local == server remote */
+
+  char host[PN_MAX_ADDR] = "";
+  char serv[PN_MAX_ADDR] = "";
+  CHECK(0 == pn_netaddr_host_port(na, host, sizeof(host), serv, sizeof(serv)));
+  CHECK_THAT("127.0.0.1", Equals(host));
+  CHECK(listening_port(l) == serv);
+
+  /* Make sure you can use NULL, 0 to get length of address
+   * string without a crash */
+  size_t len = pn_netaddr_str(pn_transport_local_addr(ct), NULL, 0);
+  CHECK(strlen(cl) == len);
+}
+
+TEST_CASE("proactor_parse_addr") {
+  char buf[1024];
+  const char *host, *port;
+
+  CHECK(0 == pni_parse_addr("foo:bar", buf, sizeof(buf), &host, &port));
+  CHECK_THAT("foo", Equals(host));
+  CHECK_THAT("bar", Equals(port));
+
+  CHECK(0 == pni_parse_addr("foo:", buf, sizeof(buf), &host, &port));
+  CHECK_THAT("foo", Equals(host));
+  CHECK_THAT("5672", Equals(port));
+
+  CHECK(0 == pni_parse_addr(":bar", buf, sizeof(buf), &host, &port));
+  CHECK(NULL == host);
+  CHECK_THAT("bar", Equals(port));
+
+  CHECK(0 == pni_parse_addr(":", buf, sizeof(buf), &host, &port));
+  CHECK(NULL == host);
+  CHECK_THAT("5672", Equals(port));
+
+  CHECK(0 == pni_parse_addr(":amqps", buf, sizeof(buf), &host, &port));
+  CHECK_THAT("5671", Equals(port));
+
+  CHECK(0 == pni_parse_addr(":amqp", buf, sizeof(buf), &host, &port));
+  CHECK_THAT("5672", Equals(port));
+
+  CHECK(0 == pni_parse_addr("::1:2:3", buf, sizeof(buf), &host, &port));
+  CHECK_THAT("::1:2", Equals(host));
+  CHECK_THAT("3", Equals(port));
+
+  CHECK(0 == pni_parse_addr(":::", buf, sizeof(buf), &host, &port));
+  CHECK_THAT("::", Equals(host));
+  CHECK_THAT("5672", Equals(port));
+
+  CHECK(0 == pni_parse_addr("", buf, sizeof(buf), &host, &port));
+  CHECK(NULL == host);
+  CHECK_THAT("5672", Equals(port));
+}
+
+/* Test pn_proactor_disconnect */
+TEST_CASE("proactor_disconnect") {
+  common_handler ch, sh;
+  proactor client(&ch), server(&sh);
+
+  // Start two listeners on the server
+  pn_listener_t *l = server.listen(":0");
+  REQUIRE_RUN(server, PN_LISTENER_OPEN);
+  pn_listener_t *l2 = server.listen(":0");
+  REQUIRE_RUN(server, PN_LISTENER_OPEN);
+
+  // Two connections from client
+  pn_connection_t *c = client.connect(l);
+  CHECK_CORUN(client, server, PN_CONNECTION_REMOTE_OPEN);
+  pn_connection_t *c2 = client.connect(l);
+  CHECK_CORUN(client, server, PN_CONNECTION_REMOTE_OPEN);
+
+  /* Disconnect the client proactor */
+  auto_free<pn_condition_t, pn_condition_free> cond(pn_condition());
+  pn_condition_format(cond, "test-name", "test-description");
+  pn_proactor_disconnect(client, cond);
+
+  /* Verify expected client side first */
+  CHECK_CORUN(client, server, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*client.handler->last_condition,
+             cond_matches("test-name", "test-description"));
+  CHECK_CORUN(client, server, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*client.handler->last_condition,
+             cond_matches("test-name", "test-description"));
+  REQUIRE_RUN(client, PN_PROACTOR_INACTIVE);
+
+  /* Now check server sees the disconnects */
+  CHECK_CORUN(server, client, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*server.handler->last_condition,
+             cond_matches("amqp:connection:framing-error", "aborted"));
+  CHECK_CORUN(server, client, PN_TRANSPORT_ERROR);
+  CHECK_THAT(*server.handler->last_condition,
+             cond_matches("amqp:connection:framing-error", "aborted"));
+
+  /* Now disconnect the server end (the listeners) */
+  pn_proactor_disconnect(server, NULL);
+  REQUIRE_RUN(server, PN_LISTENER_CLOSE);
+  REQUIRE_RUN(server, PN_LISTENER_CLOSE);
+  REQUIRE_RUN(server, PN_PROACTOR_INACTIVE);
+
+  /* Make sure the proactors are still functional */
+  pn_listener_t *l3 = server.listen(":0");
+  REQUIRE_RUN(server, PN_LISTENER_OPEN);
+  client.connect(l3);
+  CHECK_CORUN(client, server, PN_CONNECTION_REMOTE_OPEN);
+}
+
+namespace {
+const size_t FRAME = 512;                    /* Smallest legal frame */
+const ssize_t CHUNK = (FRAME + FRAME / 2);   /* Chunk overflows frame */
+const size_t BODY = (CHUNK * 3 + CHUNK / 2); /* Body doesn't fit into chunks */
+} // namespace
+
+struct message_stream_handler : public common_handler {
+  pn_link_t *sender;
+  pn_delivery_t *dlv;
+  pn_rwbytes_t send_buf, recv_buf;
+  ssize_t size, sent, received;
+  bool complete;
+
+  message_stream_handler()
+      : sender(), dlv(), send_buf(), recv_buf(), size(), sent(), received(),
+        complete() {}
+
+  bool handle(pn_event_t *e) {
+    switch (pn_event_type(e)) {
+    case PN_CONNECTION_BOUND:
+      pn_transport_set_max_frame(pn_event_transport(e), FRAME);
+      return false;
+
+    case PN_SESSION_INIT:
+      pn_session_set_incoming_capacity(pn_event_session(e),
+                                       FRAME); /* Single frame incoming */
+      pn_session_set_outgoing_window(pn_event_session(e),
+                                     1); /* Single frame outgoing */
+      return false;
+
+    case PN_LINK_REMOTE_OPEN:
+      common_handler::handle(e);
+      if (pn_link_is_receiver(pn_event_link(e))) {
+        pn_link_flow(pn_event_link(e), 1);
+      } else {
+        sender = pn_event_link(e);
+      }
+      return false;
+
+    case PN_LINK_FLOW: /* Start a delivery */
+      if (pn_link_is_sender(pn_event_link(e)) && !dlv) {
+        dlv = pn_delivery(pn_event_link(e), pn_dtag("x", 1));
+      }
+      return false;
+
+    case PN_CONNECTION_WAKE: { /* Send a chunk */
+      ssize_t remains = size - sent;
+      ssize_t n = (CHUNK < remains) ? CHUNK : remains;
+      CHECK(n == pn_link_send(sender, send_buf.start + sent, n));
+      sent += n;
+      if (sent == size) {
+        CHECK(pn_link_advance(sender));
+      }
+      return false;
+    }
+
+    case PN_DELIVERY: { /* Receive a delivery - smaller than a
+                           chunk? */
+      pn_delivery_t *dlv = pn_event_delivery(e);
+      if (pn_delivery_readable(dlv)) {
+        ssize_t n = pn_delivery_pending(dlv);
+        rwbytes_ensure(&recv_buf, received + n);
+        REQUIRE(n ==
+                pn_link_recv(pn_event_link(e), recv_buf.start + received, n));
+        received += n;
+      }
+      complete = !pn_delivery_partial(dlv);
+      return true;
+    }
+    default:
+      return common_handler::handle(e);
+    }
+  }
+};
+
+/* Test sending/receiving a message in chunks */
+TEST_CASE("proactor_message_stream") {
+  message_stream_handler h;
+  proactor p(&h);
+
+  pn_listener_t *l = p.listen(":0");
+  REQUIRE_RUN(p, PN_LISTENER_OPEN);
+
+  /* Encode a large (not very) message to send in chunks */
+  auto_free<pn_message_t, pn_message_free> m(pn_message());
+  pn_data_put_binary(pn_message_body(m), pn_bytes(std::string(BODY, 'x')));
+  h.size = pn_message_encode2(m, &h.send_buf);
+
+  pn_connection_t *c = p.connect(l);
+  pn_session_t *ssn = pn_session(c);
+  pn_session_open(ssn);
+  pn_link_t *snd = pn_sender(ssn, "x");
+  pn_link_open(snd);
+  REQUIRE_RUN(p, PN_LINK_FLOW);
+
+  /* Send and receive the message in chunks */
+  do {
+    pn_connection_wake(c); /* Initiate send/receive of one chunk */
+    do {                   /* May be multiple receives for one send */
+      REQUIRE_RUN(p, PN_DELIVERY);
+    } while (h.received < h.sent);
+  } while (!h.complete);
+  CHECK(h.received == h.size);
+  CHECK(h.sent == h.size);
+  CHECK(!memcmp(h.send_buf.start, h.recv_buf.start, h.size));
+
+  free(h.send_buf.start);
+  free(h.recv_buf.start);
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[2/6] qpid-proton git commit: PROTON-1887: [c] Convert C tests to use Catch2 harness.

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/tests/include/catch.hpp
----------------------------------------------------------------------
diff --git a/tests/include/catch.hpp b/tests/include/catch.hpp
new file mode 100644
index 0000000..f619f23
--- /dev/null
+++ b/tests/include/catch.hpp
@@ -0,0 +1,11685 @@
+/*
+ *  Catch v1.12.1
+ *  Generated: 2018-03-02 21:17:41.036711
+ *  ----------------------------------------------------------
+ *  This file has been merged from multiple headers. Please don't edit it directly
+ *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ *  Distributed under the Boost Software License, Version 1.0. (See accompanying
+ *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#ifdef __clang__
+#    pragma clang system_header
+#elif defined __GNUC__
+#    pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#ifdef __clang__
+#   ifdef __ICC // icpc defines the __clang__ macro
+#       pragma warning(push)
+#       pragma warning(disable: 161 1682)
+#   else // __ICC
+#       pragma clang diagnostic ignored "-Wglobal-constructors"
+#       pragma clang diagnostic ignored "-Wvariadic-macros"
+#       pragma clang diagnostic ignored "-Wc99-extensions"
+#       pragma clang diagnostic ignored "-Wunused-variable"
+#       pragma clang diagnostic push
+#       pragma clang diagnostic ignored "-Wpadded"
+#       pragma clang diagnostic ignored "-Wc++98-compat"
+#       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#       pragma clang diagnostic ignored "-Wswitch-enum"
+#       pragma clang diagnostic ignored "-Wcovered-switch-default"
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic ignored "-Wvariadic-macros"
+#    pragma GCC diagnostic ignored "-Wunused-variable"
+#    pragma GCC diagnostic ignored "-Wparentheses"
+
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wpadded"
+#endif
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+#  define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+#  ifndef CLARA_CONFIG_MAIN
+#    define CLARA_CONFIG_MAIN_NOT_DEFINED
+#    define CLARA_CONFIG_MAIN
+#  endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
+// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+
+#ifdef __cplusplus
+
+#  if __cplusplus >= 201103L
+#    define CATCH_CPP11_OR_GREATER
+#  endif
+
+#  if __cplusplus >= 201402L
+#    define CATCH_CPP14_OR_GREATER
+#  endif
+
+#endif
+
+#ifdef __clang__
+
+#  if __has_feature(cxx_nullptr)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  if __has_feature(cxx_noexcept)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#   if defined(CATCH_CPP11_OR_GREATER)
+#       define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+            _Pragma( "clang diagnostic push" ) \
+            _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+            _Pragma( "clang diagnostic pop" )
+
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic push" ) \
+            _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic pop" )
+#   endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__)
+
+#   if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#   endif
+
+#endif
+
+#ifdef __OS400__
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#       define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+#   define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#   if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#       define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+
+#if (_MSC_VER >= 1600)
+#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+    ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+    ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+    ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+
+#endif
+
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+    ( defined __GNUC__  && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \
+    ( defined __clang__ && __clang_major__ >= 3 )
+
+// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former,
+// because __COUNTER__ is not properly handled by it.
+// This does not affect compilation
+#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L )
+    #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(CATCH_CPP11_OR_GREATER)
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#    define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#    define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#    define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+#    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+#  endif
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#  endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
+#   define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#  endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
+#  define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+# endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_IS_ENUM
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_TUPLE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+#   define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_UNIQUE_PTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+#   define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_SHUFFLE
+#endif
+# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
+#  define CATCH_CONFIG_CPP11_TYPE_TRAITS
+# endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+#   define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#   define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#   define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+#   define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+#  define CATCH_NOEXCEPT noexcept
+#  define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+#  define CATCH_NOEXCEPT throw()
+#  define CATCH_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+#   define CATCH_NULL nullptr
+#else
+#   define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+#   define CATCH_OVERRIDE override
+#else
+#   define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+#   define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+    struct IConfig;
+
+    struct CaseSensitive { enum Choice {
+        Yes,
+        No
+    }; };
+
+    class NonCopyable {
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        NonCopyable( NonCopyable const& )              = delete;
+        NonCopyable( NonCopyable && )                  = delete;
+        NonCopyable& operator = ( NonCopyable const& ) = delete;
+        NonCopyable& operator = ( NonCopyable && )     = delete;
+#else
+        NonCopyable( NonCopyable const& info );
+        NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+    protected:
+        NonCopyable() {}
+        virtual ~NonCopyable();
+    };
+
+    class SafeBool {
+    public:
+        typedef void (SafeBool::*type)() const;
+
+        static type makeSafe( bool value ) {
+            return value ? &SafeBool::trueValue : 0;
+        }
+    private:
+        void trueValue() const {}
+    };
+
+    template<typename ContainerT>
+    void deleteAll( ContainerT& container ) {
+        typename ContainerT::const_iterator it = container.begin();
+        typename ContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete *it;
+    }
+    template<typename AssociativeContainerT>
+    void deleteAllValues( AssociativeContainerT& container ) {
+        typename AssociativeContainerT::const_iterator it = container.begin();
+        typename AssociativeContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete it->second;
+    }
+
+    bool startsWith( std::string const& s, std::string const& prefix );
+    bool startsWith( std::string const& s, char prefix );
+    bool endsWith( std::string const& s, std::string const& suffix );
+    bool endsWith( std::string const& s, char suffix );
+    bool contains( std::string const& s, std::string const& infix );
+    void toLowerInPlace( std::string& s );
+    std::string toLower( std::string const& s );
+    std::string trim( std::string const& str );
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+    struct pluralise {
+        pluralise( std::size_t count, std::string const& label );
+
+        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+        std::size_t m_count;
+        std::string m_label;
+    };
+
+    struct SourceLineInfo {
+
+        SourceLineInfo();
+        SourceLineInfo( char const* _file, std::size_t _line );
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        SourceLineInfo(SourceLineInfo const& other)          = default;
+        SourceLineInfo( SourceLineInfo && )                  = default;
+        SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+        SourceLineInfo& operator = ( SourceLineInfo && )     = default;
+#  endif
+        bool empty() const;
+        bool operator == ( SourceLineInfo const& other ) const;
+        bool operator < ( SourceLineInfo const& other ) const;
+
+        char const* file;
+        std::size_t line;
+    };
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+    // This is just here to avoid compiler warnings with macro constants and boolean literals
+    inline bool isTrue( bool value ){ return value; }
+    inline bool alwaysTrue() { return true; }
+    inline bool alwaysFalse() { return false; }
+
+    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+    void seedRng( IConfig const& config );
+    unsigned int rngSeed();
+
+    // Use this in variadic streaming macros to allow
+    //    >> +StreamEndStop
+    // as well as
+    //    >> stuff +StreamEndStop
+    struct StreamEndStop {
+        std::string operator+() {
+            return std::string();
+        }
+    };
+    template<typename T>
+    T const& operator + ( T const& value, StreamEndStop ) {
+        return value;
+    }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+namespace Catch {
+
+    class NotImplementedException : public std::exception
+    {
+    public:
+        NotImplementedException( SourceLineInfo const& lineInfo );
+
+        virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+        virtual const char* what() const CATCH_NOEXCEPT;
+
+    private:
+        std::string m_what;
+        SourceLineInfo m_lineInfo;
+    };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct IGeneratorInfo {
+        virtual ~IGeneratorInfo();
+        virtual bool moveNext() = 0;
+        virtual std::size_t getCurrentIndex() const = 0;
+    };
+
+    struct IGeneratorsForTest {
+        virtual ~IGeneratorsForTest();
+
+        virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+        virtual bool moveNext() = 0;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    // An intrusive reference counting smart pointer.
+    // T must implement addRef() and release() methods
+    // typically implementing the IShared interface
+    template<typename T>
+    class Ptr {
+    public:
+        Ptr() : m_p( CATCH_NULL ){}
+        Ptr( T* p ) : m_p( p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        Ptr( Ptr const& other ) : m_p( other.m_p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        ~Ptr(){
+            if( m_p )
+                m_p->release();
+        }
+        void reset() {
+            if( m_p )
+                m_p->release();
+            m_p = CATCH_NULL;
+        }
+        Ptr& operator = ( T* p ){
+            Ptr temp( p );
+            swap( temp );
+            return *this;
+        }
+        Ptr& operator = ( Ptr const& other ){
+            Ptr temp( other );
+            swap( temp );
+            return *this;
+        }
+        void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+        T* get() const{ return m_p; }
+        T& operator*() const { return *m_p; }
+        T* operator->() const { return m_p; }
+        bool operator !() const { return m_p == CATCH_NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
+
+    private:
+        T* m_p;
+    };
+
+    struct IShared : NonCopyable {
+        virtual ~IShared();
+        virtual void addRef() const = 0;
+        virtual void release() const = 0;
+    };
+
+    template<typename T = IShared>
+    struct SharedImpl : T {
+
+        SharedImpl() : m_rc( 0 ){}
+
+        virtual void addRef() const {
+            ++m_rc;
+        }
+        virtual void release() const {
+            if( --m_rc == 0 )
+                delete this;
+        }
+
+        mutable unsigned int m_rc;
+    };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+    class TestCase;
+    class Stream;
+    struct IResultCapture;
+    struct IRunner;
+    struct IGeneratorsForTest;
+    struct IConfig;
+
+    struct IContext
+    {
+        virtual ~IContext();
+
+        virtual IResultCapture* getResultCapture() = 0;
+        virtual IRunner* getRunner() = 0;
+        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+        virtual bool advanceGeneratorsForCurrentTest() = 0;
+        virtual Ptr<IConfig const> getConfig() const = 0;
+    };
+
+    struct IMutableContext : IContext
+    {
+        virtual ~IMutableContext();
+        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+        virtual void setRunner( IRunner* runner ) = 0;
+        virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+    };
+
+    IContext& getCurrentContext();
+    IMutableContext& getCurrentMutableContext();
+    void cleanUpContext();
+    Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec;
+
+    struct ITestCase : IShared {
+        virtual void invoke () const = 0;
+    protected:
+        virtual ~ITestCase();
+    };
+
+    class TestCase;
+    struct IConfig;
+
+    struct ITestCaseRegistry {
+        virtual ~ITestCaseRegistry();
+        virtual std::vector<TestCase> const& getAllTests() const = 0;
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+    };
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+    MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+    virtual void invoke() const {
+        C obj;
+        (obj.*m_method)();
+    }
+
+private:
+    virtual ~MethodTestCase() {}
+
+    void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+    NameAndDesc( const char* _name = "", const char* _description= "" )
+    : name( _name ), description( _description )
+    {}
+
+    const char* name;
+    const char* description;
+};
+
+void registerTestCase
+    (   ITestCase* testCase,
+        char const* className,
+        NameAndDesc const& nameAndDesc,
+        SourceLineInfo const& lineInfo );
+
+struct AutoReg {
+
+    AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc );
+
+    template<typename C>
+    AutoReg
+        (   void (C::*method)(),
+            char const* className,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        registerTestCase
+            (   new MethodTestCase<C>( method ),
+                className,
+                nameAndDesc,
+                lineInfo );
+    }
+
+    ~AutoReg();
+
+private:
+    AutoReg( AutoReg const& );
+    void operator= ( AutoReg const& );
+};
+
+void registerTestCaseFunction
+    (   TestFunction function,
+        SourceLineInfo const& lineInfo,
+        NameAndDesc const& nameAndDesc );
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+        static void TestName(); \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( ... ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ \
+            struct TestName : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \
+        } \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        void TestName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#else
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+        static void TestName(); \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ \
+            struct TestCaseName : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \
+        } \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        void TestCaseName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+    // ResultWas::OfType enum
+    struct ResultWas { enum OfType {
+        Unknown = -1,
+        Ok = 0,
+        Info = 1,
+        Warning = 2,
+
+        FailureBit = 0x10,
+
+        ExpressionFailed = FailureBit | 1,
+        ExplicitFailure = FailureBit | 2,
+
+        Exception = 0x100 | FailureBit,
+
+        ThrewException = Exception | 1,
+        DidntThrowException = Exception | 2,
+
+        FatalErrorCondition = 0x200 | FailureBit
+
+    }; };
+
+    inline bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    inline bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    // ResultDisposition::Flags enum
+    struct ResultDisposition { enum Flags {
+        Normal = 0x01,
+
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        SuppressFail = 0x08         // Failures are reported but do not fail the test
+    }; };
+
+    inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+    }
+
+    inline bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+    inline bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; }
+    inline bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct DecomposedExpression
+    {
+        virtual ~DecomposedExpression() {}
+        virtual bool isBinaryExpression() const {
+            return false;
+        }
+        virtual void reconstructExpression( std::string& dest ) const = 0;
+
+        // Only simple binary comparisons can be decomposed.
+        // If more complex check is required then wrap sub-expressions in parentheses.
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+
+    private:
+        DecomposedExpression& operator = (DecomposedExpression const&);
+    };
+
+    struct AssertionInfo
+    {
+        AssertionInfo();
+        AssertionInfo(  char const * _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        char const * _capturedExpression,
+                        ResultDisposition::Flags _resultDisposition,
+                        char const * _secondArg = "");
+
+        char const * macroName;
+        SourceLineInfo lineInfo;
+        char const * capturedExpression;
+        ResultDisposition::Flags resultDisposition;
+        char const * secondArg;
+    };
+
+    struct AssertionResultData
+    {
+        AssertionResultData() : decomposedExpression( CATCH_NULL )
+                              , resultType( ResultWas::Unknown )
+                              , negated( false )
+                              , parenthesized( false ) {}
+
+        void negate( bool parenthesize ) {
+            negated = !negated;
+            parenthesized = parenthesize;
+            if( resultType == ResultWas::Ok )
+                resultType = ResultWas::ExpressionFailed;
+            else if( resultType == ResultWas::ExpressionFailed )
+                resultType = ResultWas::Ok;
+        }
+
+        std::string const& reconstructExpression() const {
+            if( decomposedExpression != CATCH_NULL ) {
+                decomposedExpression->reconstructExpression( reconstructedExpression );
+                if( parenthesized ) {
+                    reconstructedExpression.insert( 0, 1, '(' );
+                    reconstructedExpression.append( 1, ')' );
+                }
+                if( negated ) {
+                    reconstructedExpression.insert( 0, 1, '!' );
+                }
+                decomposedExpression = CATCH_NULL;
+            }
+            return reconstructedExpression;
+        }
+
+        mutable DecomposedExpression const* decomposedExpression;
+        mutable std::string reconstructedExpression;
+        std::string message;
+        ResultWas::OfType resultType;
+        bool negated;
+        bool parenthesized;
+    };
+
+    class AssertionResult {
+    public:
+        AssertionResult();
+        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+        ~AssertionResult();
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+         AssertionResult( AssertionResult const& )              = default;
+         AssertionResult( AssertionResult && )                  = default;
+         AssertionResult& operator = ( AssertionResult const& ) = default;
+         AssertionResult& operator = ( AssertionResult && )     = default;
+#  endif
+
+        bool isOk() const;
+        bool succeeded() const;
+        ResultWas::OfType getResultType() const;
+        bool hasExpression() const;
+        bool hasMessage() const;
+        std::string getExpression() const;
+        std::string getExpressionInMacro() const;
+        bool hasExpandedExpression() const;
+        std::string getExpandedExpression() const;
+        std::string getMessage() const;
+        SourceLineInfo getSourceInfo() const;
+        std::string getTestMacroName() const;
+        void discardDecomposedExpression() const;
+        void expandDecomposedExpression() const;
+
+    protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+        template<typename ArgT> struct MatchAllOf;
+        template<typename ArgT> struct MatchAnyOf;
+        template<typename ArgT> struct MatchNotOf;
+
+        class MatcherUntypedBase {
+        public:
+            std::string toString() const {
+                if( m_cachedToString.empty() )
+                    m_cachedToString = describe();
+                return m_cachedToString;
+            }
+
+        protected:
+            virtual ~MatcherUntypedBase();
+            virtual std::string describe() const = 0;
+            mutable std::string m_cachedToString;
+        private:
+            MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
+        };
+
+        template<typename ObjectT>
+        struct MatcherMethod {
+            virtual bool match( ObjectT const& arg ) const = 0;
+        };
+        template<typename PtrT>
+        struct MatcherMethod<PtrT*> {
+            virtual bool match( PtrT* arg ) const = 0;
+        };
+
+        template<typename ObjectT, typename ComparatorT = ObjectT>
+        struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> {
+
+            MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
+            MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
+            MatchNotOf<ComparatorT> operator ! () const;
+        };
+
+        template<typename ArgT>
+        struct MatchAllOf : MatcherBase<ArgT> {
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (!m_matchers[i]->match(arg))
+                        return false;
+                }
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        description += " and ";
+                    description += m_matchers[i]->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
+                return *this;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+        template<typename ArgT>
+        struct MatchAnyOf : MatcherBase<ArgT> {
+
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (m_matchers[i]->match(arg))
+                        return true;
+                }
+                return false;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        description += " or ";
+                    description += m_matchers[i]->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
+                return *this;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+
+        template<typename ArgT>
+        struct MatchNotOf : MatcherBase<ArgT> {
+
+            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                return !m_underlyingMatcher.match( arg );
+            }
+
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "not " + m_underlyingMatcher.toString();
+            }
+            MatcherBase<ArgT> const& m_underlyingMatcher;
+        };
+
+        template<typename ObjectT, typename ComparatorT>
+        MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
+            return MatchAllOf<ComparatorT>() && *this && other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
+            return MatchAnyOf<ComparatorT>() || *this || other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
+            return MatchNotOf<ComparatorT>( *this );
+        }
+
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    // - deprecated: prefer ||, && and !
+    template<typename T>
+    Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
+        return Impl::MatchNotOf<T>( underlyingMatcher );
+    }
+    template<typename T>
+    Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2;
+    }
+    template<typename T>
+    Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2 && m3;
+    }
+    template<typename T>
+    Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2;
+    }
+    template<typename T>
+    Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str(std::string());
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder : public DecomposedExpression {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition,
+                        char const* secondArg = "" );
+        ~ResultBuilder();
+
+        template<typename T>
+        ExpressionLhs<T const&> operator <= ( T const& operand );
+        ExpressionLhs<bool> operator <= ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            stream().oss << value;
+            return *this;
+        }
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+
+        void endExpression( DecomposedExpression const& expr );
+
+        virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
+
+        AssertionResult build() const;
+        AssertionResult build( DecomposedExpression const& expr ) const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void captureExpectedException( std::string const& expectedMessage );
+        void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
+        void handleResult( AssertionResult const& result );
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+        template<typename ArgT, typename MatcherT>
+        void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
+
+        void setExceptionGuard();
+        void unsetExceptionGuard();
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+
+        CopyableStream &stream()
+        {
+            if(!m_usedStream)
+            {
+                m_usedStream = true;
+                m_stream().oss.str("");
+            }
+            return m_stream();
+        }
+
+        static CopyableStream &m_stream()
+        {
+            static CopyableStream s;
+            return s;
+        }
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+        bool m_guardException;
+        bool m_usedStream;
+    };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4018) // more "signed/unsigned mismatch"
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+    enum Operator {
+        IsEqualTo,
+        IsNotEqualTo,
+        IsLessThan,
+        IsGreaterThan,
+        IsLessThanOrEqualTo,
+        IsGreaterThanOrEqualTo
+    };
+
+    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
+    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
+    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
+    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
+    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
+    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
+    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+    template<typename T>
+    T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+    // So the compare overloads can be operator agnostic we convey the operator as a template
+    // enum, which is used to specialise an Evaluator for doing the comparison.
+    template<typename T1, typename T2, Operator Op>
+    struct Evaluator{};
+
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs) {
+            return bool( opCast( lhs ) ==  opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsNotEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) != opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) < opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) > opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) >= opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) <= opCast( rhs ) );
+        }
+    };
+
+    template<Operator Op, typename T1, typename T2>
+    bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // This level of indirection allows us to specialise for integer types
+    // to avoid signed/ unsigned warnings
+
+    // "base" overload
+    template<Operator Op, typename T1, typename T2>
+    bool compare( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // unsigned X to int
+    template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+
+    // unsigned X to long
+    template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+
+    // int to unsigned X
+    template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+
+    // long to unsigned X
+    template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // pointer to long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+    // pointer to int (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+    // long long to unsigned X
+    template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // unsigned long long to X
+    template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+
+    // pointer to long long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    // pointer to nullptr_t (when comparing against nullptr)
+    template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
+    }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+    [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring );
+    std::string toString( NSString * CATCH_ARC_STRONG & nsstring );
+    std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+    extern const std::string unprintableString;
+
+ #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK)
+    struct BorgType {
+        template<typename T> BorgType( T const& );
+    };
+
+    struct TrueType { char sizer[1]; };
+    struct FalseType { char sizer[2]; };
+
+    TrueType& testStreamable( std::ostream& );
+    FalseType testStreamable( FalseType );
+
+    FalseType operator<<( std::ostream const&, BorgType const& );
+
+    template<typename T>
+    struct IsStreamInsertable {
+        static std::ostream &s;
+        static T  const&t;
+        enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+    };
+#else
+    template<typename T>
+    class IsStreamInsertable {
+        template<typename SS, typename TT>
+        static auto test(int)
+        -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
+
+        template<typename, typename>
+        static auto test(...) -> std::false_type;
+
+    public:
+        static const bool value = decltype(test<std::ostream,const T&>(0))::value;
+    };
+#endif
+
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+    template<typename T,
+             bool IsEnum = std::is_enum<T>::value
+             >
+    struct EnumStringMaker
+    {
+        static std::string convert( T const& ) { return unprintableString; }
+    };
+
+    template<typename T>
+    struct EnumStringMaker<T,true>
+    {
+        static std::string convert( T const& v )
+        {
+            return ::Catch::toString(
+                static_cast<typename std::underlying_type<T>::type>(v)
+                );
+        }
+    };
+#endif
+    template<bool C>
+    struct StringMakerBase {
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+        template<typename T>
+        static std::string convert( T const& v )
+        {
+            return EnumStringMaker<T>::convert( v );
+        }
+#else
+        template<typename T>
+        static std::string convert( T const& ) { return unprintableString; }
+#endif
+    };
+
+    template<>
+    struct StringMakerBase<true> {
+        template<typename T>
+        static std::string convert( T const& _value ) {
+            std::ostringstream oss;
+            oss << _value;
+            return oss.str();
+        }
+    };
+
+    std::string rawMemoryToString( const void *object, std::size_t size );
+
+    template<typename T>
+    std::string rawMemoryToString( const T& object ) {
+      return rawMemoryToString( &object, sizeof(object) );
+    }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+    Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+    template<typename U>
+    static std::string convert( U* p ) {
+        if( !p )
+            return "NULL";
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+    static std::string convert( R C::* p ) {
+        if( !p )
+            return "NULL";
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template<typename T, typename Allocator>
+//struct StringMaker<std::vector<T, Allocator> > {
+//    static std::string convert( std::vector<T,Allocator> const& v ) {
+//        return Detail::rangeToString( v.begin(), v.end() );
+//    }
+//};
+
+template<typename T, typename Allocator>
+std::string toString( std::vector<T,Allocator> const& v ) {
+    return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+
+// toString for tuples
+namespace TupleDetail {
+  template<
+      typename Tuple,
+      std::size_t N = 0,
+      bool = (N < std::tuple_size<Tuple>::value)
+      >
+  struct ElementPrinter {
+      static void print( const Tuple& tuple, std::ostream& os )
+      {
+          os << ( N ? ", " : " " )
+             << Catch::toString(std::get<N>(tuple));
+          ElementPrinter<Tuple,N+1>::print(tuple,os);
+      }
+  };
+
+  template<
+      typename Tuple,
+      std::size_t N
+      >
+  struct ElementPrinter<Tuple,N,false> {
+      static void print( const Tuple&, std::ostream& ) {}
+  };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+    static std::string convert( const std::tuple<Types...>& tuple )
+    {
+        std::ostringstream os;
+        os << '{';
+        TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
+        os << " }";
+        return os.str();
+    }
+};
+#endif // CATCH_CONFIG_CPP11_TUPLE
+
+namespace Detail {
+    template<typename T>
+    std::string makeString( T const& value ) {
+        return StringMaker<T>::convert( value );
+    }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+    return StringMaker<T>::convert( value );
+}
+
+    namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last ) {
+        std::ostringstream oss;
+        oss << "{ ";
+        if( first != last ) {
+            oss << Catch::toString( *first );
+            for( ++first ; first != last ; ++first )
+                oss << ", " << Catch::toString( *first );
+        }
+        oss << " }";
+        return oss.str();
+    }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression;
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression;
+
+// Wraps the LHS of an expression and overloads comparison operators
+// for also capturing those and RHS (if any)
+template<typename T>
+class ExpressionLhs : public DecomposedExpression {
+public:
+    ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+
+    ExpressionLhs& operator = ( const ExpressionLhs& );
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
+    operator == ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
+    operator != ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsLessThan, RhsT const&>
+    operator < ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThan>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
+    operator > ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThan>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
+    operator <= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
+    operator >= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+    }
+
+    BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    void endExpression() {
+        m_truthy = m_lhs ? true : false;
+        m_rb
+            .setResultType( m_truthy )
+            .endExpression( *this );
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        dest = Catch::toString( m_lhs );
+    }
+
+private:
+    template<Internal::Operator Op, typename RhsT>
+    BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
+        return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
+    }
+
+    template<Internal::Operator Op>
+    BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
+        return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
+    }
+
+private:
+    ResultBuilder& m_rb;
+    T m_lhs;
+    bool m_truthy;
+};
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression : public DecomposedExpression {
+public:
+    BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
+        : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+
+    BinaryExpression& operator = ( BinaryExpression& );
+
+    void endExpression() const {
+        m_rb
+            .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
+            .endExpression( *this );
+    }
+
+    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+        return true;
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        std::string lhs = Catch::toString( m_lhs );
+        std::string rhs = Catch::toString( m_rhs );
+        char delim = lhs.size() + rhs.size() < 40 &&
+                     lhs.find('\n') == std::string::npos &&
+                     rhs.find('\n') == std::string::npos ? ' ' : '\n';
+        dest.reserve( 7 + lhs.size() + rhs.size() );
+                   // 2 for spaces around operator
+                   // 2 for operator
+                   // 2 for parentheses (conditionally added later)
+                   // 1 for negation (conditionally added later)
+        dest = lhs;
+        dest += delim;
+        dest += Internal::OperatorTraits<Op>::getName();
+        dest += delim;
+        dest += rhs;
+    }
+
+private:
+    ResultBuilder& m_rb;
+    LhsT m_lhs;
+    RhsT m_rhs;
+};
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression : public DecomposedExpression {
+public:
+    MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
+        : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+
+    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+        return true;
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        std::string matcherAsString = m_matcher.toString();
+        dest = Catch::toString( m_arg );
+        dest += ' ';
+        if( matcherAsString == Detail::unprintableString )
+            dest += m_matcherString;
+        else
+            dest += matcherAsString;
+    }
+
+private:
+    ArgT m_arg;
+    MatcherT m_matcher;
+    char const* m_matcherString;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+    template<typename T>
+    ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
+        return ExpressionLhs<T const&>( *this, operand );
+    }
+
+    inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
+        return ExpressionLhs<bool>( *this, value );
+    }
+
+    template<typename ArgT, typename MatcherT>
+    void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
+                                             char const* matcherString ) {
+        MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
+        setResultType( matcher.match( arg ) );
+        endExpression( expr );
+    }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct MessageInfo {
+        MessageInfo(    std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        std::string message;
+        unsigned int sequence;
+
+        bool operator == ( MessageInfo const& other ) const {
+            return sequence == other.sequence;
+        }
+        bool operator < ( MessageInfo const& other ) const {
+            return sequence < other.sequence;
+        }
+    private:
+        static unsigned int globalCount;
+    };
+
+    struct MessageBuilder {
+        MessageBuilder( std::string const& macroName,
+                        SourceLineInfo const& lineInfo,
+                        ResultWas::OfType type )
+        : m_info( macroName, lineInfo, type )
+        {}
+
+        template<typename T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+        std::ostringstream m_stream;
+    };
+
+    class ScopedMessage {
+    public:
+        ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage const& other );
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    class AssertionResult;
+    struct AssertionInfo;
+    struct SectionInfo;
+    struct SectionEndInfo;
+    struct MessageInfo;
+    class ScopedMessageBuilder;
+    struct Counts;
+
+    struct IResultCapture {
+
+        virtual ~IResultCapture();
+
+        virtual void assertionEnded( AssertionResult const& result ) = 0;
+        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
+                                        Counts& assertions ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+        virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+        virtual std::string getCurrentTestName() const = 0;
+        virtual const AssertionResult* getLastResult() const = 0;
+
+        virtual void exceptionEarlyReported() = 0;
+
+        virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+
+        virtual bool lastAssertionPassed() = 0;
+        virtual void assertionPassed() = 0;
+        virtual void assertionRun() = 0;
+    };
+
+    IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#  define CATCH_PLATFORM_MAC
+#elif  defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#  define CATCH_PLATFORM_IPHONE
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+#  define CATCH_PLATFORM_LINUX
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#  define CATCH_PLATFORM_WINDOWS
+#  if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+#    define CATCH_DEFINES_NOMINMAX
+#  endif
+#  if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+#    define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+#  endif
+#endif
+
+#include <string>
+
+namespace Catch{
+
+    bool isDebuggerActive();
+    void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    // The following code snippet based on:
+    // http://cocoawithlove.com/2008/03/break-into-debugger.html
+    #if defined(__ppc64__) || defined(__ppc__)
+        #define CATCH_TRAP() \
+                __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+                : : : "memory","r0","r3","r4" ) /* NOLINT */
+    #else
+        #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ )
+    #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+    // If we can use inline assembler, do it because this allows us to break
+    // directly at the location of the failing check instead of breaking inside
+    // raise() called from it, i.e. one stack frame below.
+    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
+    #else // Fall back to the generic way.
+        #include <signal.h>
+
+        #define CATCH_TRAP() raise(SIGTRAP)
+    #endif
+#elif defined(_MSC_VER)
+    #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifdef CATCH_TRAP
+    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+#else
+    #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+    class TestCase;
+
+    struct IRunner {
+        virtual ~IRunner();
+        virtual bool aborting() const = 0;
+    };
+}
+
+#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
+# define CATCH_INTERNAL_STRINGIFY(expr) #expr
+#else
+# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
+#endif
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+// This can potentially cause false negative, if the test code catches
+// the exception before it propagates back up to the runner.
+#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \
+        __catchResult.setExceptionGuard(); \
+        CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+        ( __catchResult <= expr ).endExpression(); \
+        CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+        __catchResult.unsetExceptionGuard(); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        __catchResult.setExceptionGuard(); \
+        __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \
+        __catchResult.unsetExceptionGuard(); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+#else
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+    resultBuilder.react();
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \
+        try { \
+            CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            ( __catchResult <= expr ).endExpression(); \
+            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+    // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+    if( Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+    if( !Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \
+        try { \
+            static_cast<void>(expr); \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                static_cast<void>(expr); \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( ... ) { \
+                __catchResult.captureExpectedException( matcher ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                static_cast<void>(expr); \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( exceptionType ) { \
+                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            } \
+            catch( ... ) { \
+                __catchResult.useActiveException( resultDisposition ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#else
+    #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << log + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( macroName, log ) \
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        try { \
+            __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \
+        } catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+    struct Counts {
+        Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+        Counts operator - ( Counts const& other ) const {
+            Counts diff;
+            diff.passed = passed - other.passed;
+            diff.failed = failed - other.failed;
+            diff.failedButOk = failedButOk - other.failedButOk;
+            return diff;
+        }
+        Counts& operator += ( Counts const& other ) {
+            passed += other.passed;
+            failed += other.failed;
+            failedButOk += other.failedButOk;
+            return *this;
+        }
+
+        std::size_t total() const {
+            return passed + failed + failedButOk;
+        }
+        bool allPassed() const {
+            return failed == 0 && failedButOk == 0;
+        }
+        bool allOk() const {
+            return failed == 0;
+        }
+
+        std::size_t passed;
+        std::size_t failed;
+        std::size_t failedButOk;
+    };
+
+    struct Totals {
+
+        Totals operator - ( Totals const& other ) const {
+            Totals diff;
+            diff.assertions = assertions - other.assertions;
+            diff.testCases = testCases - other.testCases;
+            return diff;
+        }
+
+        Totals delta( Totals const& prevTotals ) const {
+            Totals diff = *this - prevTotals;
+            if( diff.assertions.failed > 0 )
+                ++diff.testCases.failed;
+            else if( diff.assertions.failedButOk > 0 )
+                ++diff.testCases.failedButOk;
+            else
+                ++diff.testCases.passed;
+            return diff;
+        }
+
+        Totals& operator += ( Totals const& other ) {
+            assertions += other.assertions;
+            testCases += other.testCases;
+            return *this;
+        }
+
+        Counts assertions;
+        Counts testCases;
+    };
+}
+
+#include <string>
+
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+        : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+        {}
+
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef _MSC_VER
+
+namespace Catch {
+    typedef unsigned long long UInt64;
+}
+#else
+#include <stdint.h>
+namespace Catch {
+    typedef uint64_t UInt64;
+}
+#endif
+
+namespace Catch {
+    class Timer {
+    public:
+        Timer() : m_ticks( 0 ) {}
+        void start();
+        unsigned int getElapsedMicroseconds() const;
+        unsigned int getElapsedMilliseconds() const;
+        double getElapsedSeconds() const;
+
+    private:
+        UInt64 m_ticks;
+    };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+    class Section : NonCopyable {
+    public:
+        Section( SectionInfo const& info );
+        ~Section();
+
+        // This indicates whether the section should be executed or not
+        operator bool() const;
+
+    private:
+        SectionInfo m_info;
+
+        std::string m_name;
+        Counts m_assertions;
+        bool m_sectionIncluded;
+        Timer m_timer;
+    };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_SECTION( ... ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+    #define INTERNAL_CATCH_SECTION( name, desc ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: inte

<TRUNCATED>

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[6/6] qpid-proton git commit: PROTON-1887: [c] Convert C tests to use Catch2 harness.

Posted by ac...@apache.org.
PROTON-1887: [c] Convert C tests to use Catch2 harness.

Convert c/tests from home-baked test macros to the Catch2 test harness.
We are using version 1.x until we can move on from supporting C++03.
https://github.com/catchorg/Catch2/tree/Catch1.x

Added files:

test/include/
- catch.hpp - the Catch2 version 1.x framework in a single header.
- catch_extra.hpp - added Catch::Matcher support for C-string comparisons

c/tests/
- pn_test.hpp/.cpp - tools for testing the core library
  - auto_free<> - scoped freeing of pn_xxx_t*
  - matchers to check pn_condition/error_t with useful output
  - driver class to pump a pn_connection_driver_t and check events
    - trivial in-memory copy transport, no networking needed.
- pn_test_proactor.hpp/cpp - tools for testing the proactor library
  - proactor to pump a pn_proactor and check events.
  - same handler as driver above.

Renamed tests files: c/tests/*.c => c/tests/*_test.cpp

Tests are now linked into 3 executables:

- c-core-test - tests for libqpid-proton-core API
- c-extra-test - tests for the "extra" deprecated API in libqpid-proton
- c-proactor-test - tests for the proactor API in libqpid-proton-proactor

Individual tests or subsets can be run via command line arguments, run with
"-h" for a summary, or see https://github.com/catchorg/Catch2/blob/Catch1.x/docs/command-line.md

NOTE: Catch2 allows multiple "tags" to be associated with each test to easily
define different test sets. We're not using this feature yet, we could use it to
identify a fast self-test set, a long-running stress test set etc.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/0bdba37d
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/0bdba37d
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/0bdba37d

Branch: refs/heads/master
Commit: 0bdba37da9fbce7db4db106c21c1393ef6f1b433
Parents: f53c768
Author: Alan Conway <ac...@redhat.com>
Authored: Wed Nov 14 17:12:51 2018 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Mon Nov 19 17:46:12 2018 -0500

----------------------------------------------------------------------
 c/src/proactor/proactor-internal.h |    12 +-
 c/tests/CMakeLists.txt             |    78 +-
 c/tests/condition.c                |    56 -
 c/tests/condition_test.cpp         |    50 +
 c/tests/connection_driver.c        |   591 --
 c/tests/connection_driver_test.cpp |   567 ++
 c/tests/data.c                     |   104 -
 c/tests/data_test.cpp              |    92 +
 c/tests/engine.c                   |   369 -
 c/tests/engine_test.cpp            |   318 +
 c/tests/event.c                    |   109 -
 c/tests/event_test.cpp             |   105 +
 c/tests/message.c                  |    96 -
 c/tests/message_test.cpp           |    84 +
 c/tests/object.c                   |  1115 ---
 c/tests/object_test.cpp            |   969 +++
 c/tests/parse-url.c                |   127 -
 c/tests/pn_test.cpp                |   217 +
 c/tests/pn_test.hpp                |   193 +
 c/tests/pn_test_proactor.cpp       |   159 +
 c/tests/pn_test_proactor.hpp       |   120 +
 c/tests/pn_test_test.cpp           |    67 +
 c/tests/proactor.c                 |  1107 ---
 c/tests/proactor_test.cpp          |   831 +++
 c/tests/reactor.c                  |   578 --
 c/tests/refcount.c                 |   393 -
 c/tests/refcount_test.cpp          |   371 +
 c/tests/ssl.c                      |    71 -
 c/tests/ssl_test.cpp               |    60 +
 c/tests/test_handler.h             |   184 -
 c/tests/test_main.cpp              |    46 +
 c/tests/test_tools.h               |   262 -
 c/tests/url_test.cpp               |   137 +
 cpp/CMakeLists.txt                 |    20 +-
 cpp/src/cpp-test.cpp               |    21 +
 cpp/src/url_test.cpp               |   124 +-
 tests/include/catch.hpp            | 11685 ++++++++++++++++++++++++++++++
 tests/include/catch_extra.hpp      |   107 +
 38 files changed, 16325 insertions(+), 5270 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/src/proactor/proactor-internal.h
----------------------------------------------------------------------
diff --git a/c/src/proactor/proactor-internal.h b/c/src/proactor/proactor-internal.h
index 67c0bf6..cc5f2cb 100644
--- a/c/src/proactor/proactor-internal.h
+++ b/c/src/proactor/proactor-internal.h
@@ -20,9 +20,13 @@
  * under the License.
  */
 
-#include <proton/type_compat.h>
-#include <proton/import_export.h>
 #include <proton/condition.h>
+#include <proton/import_export.h>
+#include <proton/type_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 /* NOTE PNP_EXTERN is for use by proton-internal tests  */
 
@@ -48,4 +52,8 @@ extern const char *PNI_IO_CONDITION;
 void pni_proactor_set_cond(
   pn_condition_t *cond, const char *what, const char *host, const char *port, const char *msg);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif  /*!PROACTOR_PROACTOR_INTERNAL_H*/

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/c/tests/CMakeLists.txt b/c/tests/CMakeLists.txt
index a4bfb67..f6957d4 100644
--- a/c/tests/CMakeLists.txt
+++ b/c/tests/CMakeLists.txt
@@ -17,51 +17,61 @@
 # under the License.
 #
 
-add_definitions(${COMPILE_WARNING_FLAGS} ${COMPILE_PLATFORM_FLAGS})
-
 configure_file(test_config.h.in test_config.h)
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/tests/include)
+if (WIN32)
+  set(test_env ${PN_ENV_SCRIPT} -- "PATH=$<TARGET_FILE_DIR:qpid-proton-core>")
+else()
+  set(platform_test_src ssl_test.cpp)
+endif()
 
-set(test_env ${PN_ENV_SCRIPT} -- "PATH=$<TARGET_FILE_DIR:qpid-proton-core>")
+# NOTE: C library tests are written in C++ using the Catch2 framework.
+# The tests are more concise than with a native C framework and we can
+# use the same framework for C++ tests.
+# See https://github.com/catchorg/Catch2/blob/Catch1.x/docs/tutorial.md for more.
+# We will use the Catch2 1.x branch for as long as we must support C++03
+#
+# ${CMAKE_SOURCE_DIR}/include/tests/catch_extra.cpp has some general extensions to catch.
+# ./pn_test.hpp contains proton-C specific code to help with testing.
+#
+# Tests for each library are combined in a single executable.
 
-# Add test without any qpid-proton lib: allows choice of
-# qpid-proton-core or qpid-proton
-macro (pn_add_c_test_nolib test)
-  add_executable (${test} ${ARGN})
-  target_link_libraries (${test} ${PLATFORM_LIBS})
-  if (BUILD_WITH_CXX)
-    set_source_files_properties (${ARGN} PROPERTIES LANGUAGE CXX)
-  endif (BUILD_WITH_CXX)
-  add_test(NAME ${test} COMMAND ${test_env} ${TEST_EXE_PREFIX_CMD} $<TARGET_FILE:${test}>)
-endmacro (pn_add_c_test_nolib)
+macro(add_c_test exe)
+  add_executable(${exe} test_main.cpp pn_test.cpp ${ARGN})
+  set_target_properties(${exe} PROPERTIES
+    COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_FLAGS}")
+  add_test(NAME ${exe} COMMAND ${test_env} ${TEST_EXE_PREFIX_CMD} $<TARGET_FILE:${exe}>)
+endmacro()
 
-# Add test with qpid-proton-core linked
-macro (pn_add_c_test test)
-  pn_add_c_test_nolib (${test} ${ARGN})
-  target_link_libraries (${test} qpid-proton-core ${PLATFORM_LIBS})
-endmacro (pn_add_c_test)
+## Tests that depend only on qpid-proton-core
+add_c_test(c-core-test
+  pn_test_test.cpp
+  object_test.cpp
+  condition_test.cpp
+  connection_driver_test.cpp
+  data_test.cpp
+  engine_test.cpp
+  refcount_test.cpp
+  ${platform_test_src})
 
-pn_add_c_test (c-object-tests object.c)
-pn_add_c_test (c-message-tests message.c)
-pn_add_c_test (c-engine-tests engine.c)
-pn_add_c_test (c-refcount-tests refcount.c)
-pn_add_c_test (c-event-tests event.c)
-pn_add_c_test (c-data-tests data.c)
-pn_add_c_test (c-condition-tests condition.c)
-pn_add_c_test (c-connection-driver-tests connection_driver.c)
-pn_add_c_test (c-ssl-tests ssl.c)
+target_link_libraries(c-core-test qpid-proton-core ${PLATFORM_LIBS})
 
-pn_add_c_test_nolib (c-parse-url-tests parse-url.c)
-target_link_libraries (c-parse-url-tests qpid-proton)
+## Tests for the deprecated "extra" part of the qpid-proton library.
+add_c_test(c-extra-test url_test.cpp)
+target_link_libraries(c-extra-test qpid-proton ${PLATFORM_LIBS})
 
 if(HAS_PROACTOR)
-  pn_add_c_test (c-proactor-tests proactor.c)
-  target_link_libraries (c-proactor-tests qpid-proton-proactor)
+  # Tests for qpid-proton-proactor
+  add_c_test(c-proactor-test pn_test_proactor.cpp proactor_test.cpp)
+  target_link_libraries(c-proactor-test qpid-proton-core qpid-proton-proactor ${PLATFORM_LIBS})
 
-  # TODO Enable by default when races and xcode problems are cleared up
+  # Thread race test.
+  #
+  # TODO aconway 2018-11-14: enable by default when races and xcode
+  # problems are cleared up
   option(THREADERCISER "Run the threaderciser concurrency tests" OFF)
   if (THREADERCISER)
-    pn_add_c_test(c-threaderciser threaderciser.c)
+    add_c_test(c-threaderciser threaderciser.c)
     target_link_libraries (c-threaderciser qpid-proton-proactor)
     find_library(Pthread_LIBRARY pthread)
     if (Pthread_LIBRARY)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/condition.c
----------------------------------------------------------------------
diff --git a/c/tests/condition.c b/c/tests/condition.c
deleted file mode 100644
index 5ecc263..0000000
--- a/c/tests/condition.c
+++ /dev/null
@@ -1,56 +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 <proton/condition.h>
-#include <proton/connection.h>
-#include <stdio.h>
-#include <string.h>
-
-static int fail = 0;
-
-#define TEST_ASSERT(B)                                  \
-    if(!(B)) {                                          \
-        ++fail;                                         \
-        printf("%s:%d %s\n", __FILE__, __LINE__ , #B); \
-    }
-
-int main(int argc, char **argv) {
-    pn_connection_t *c = pn_connection();
-    pn_condition_t *cond = pn_connection_condition(c);
-
-    // Verify empty
-    TEST_ASSERT(!pn_condition_is_set(cond));
-    TEST_ASSERT(!pn_condition_get_name(cond));
-    TEST_ASSERT(!pn_condition_get_description(cond));
-
-    // Format a condition
-    pn_condition_format(cond, "foo", "hello %d", 42);
-    TEST_ASSERT(pn_condition_is_set(cond));
-    TEST_ASSERT(strcmp("foo", pn_condition_get_name(cond)) == 0);
-    TEST_ASSERT(strcmp("hello 42", pn_condition_get_description(cond)) == 0);
-
-    // Clear and verify empty
-    pn_condition_clear(cond);
-    TEST_ASSERT(!pn_condition_is_set(cond));
-    TEST_ASSERT(!pn_condition_get_name(cond));
-    TEST_ASSERT(!pn_condition_get_description(cond));
-
-    pn_connection_free(c);
-    return fail;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/condition_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/condition_test.cpp b/c/tests/condition_test.cpp
new file mode 100644
index 0000000..89e7648
--- /dev/null
+++ b/c/tests/condition_test.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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>
+
+using Catch::Matchers::Equals;
+using namespace pn_test;
+
+TEST_CASE("condition") {
+  auto_free<pn_connection_t, pn_connection_free> c(pn_connection());
+  REQUIRE(c);
+  pn_condition_t *cond = pn_connection_condition(c);
+  REQUIRE(cond);
+
+  // Verify empty
+  CHECK(!pn_condition_is_set(cond));
+  CHECK(!pn_condition_get_name(cond));
+  CHECK(!pn_condition_get_description(cond));
+
+  // Format a condition
+  pn_condition_format(cond, "foo", "hello %d", 42);
+  CHECK(pn_condition_is_set(cond));
+  CHECK_THAT("foo", Equals(pn_condition_get_name(cond)));
+  CHECK_THAT("hello 42", Equals(pn_condition_get_description(cond)));
+
+  // Clear and verify empty
+  pn_condition_clear(cond);
+  CHECK(!pn_condition_is_set(cond));
+  CHECK(!pn_condition_get_name(cond));
+  CHECK(!pn_condition_get_description(cond));
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/connection_driver.c
----------------------------------------------------------------------
diff --git a/c/tests/connection_driver.c b/c/tests/connection_driver.c
deleted file mode 100644
index c22c2d9..0000000
--- a/c/tests/connection_driver.c
+++ /dev/null
@@ -1,591 +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_handler.h"
-#include <proton/codec.h>
-#include <proton/connection_driver.h>
-#include <proton/connection.h>
-#include <proton/delivery.h>
-#include <proton/message.h>
-#include <proton/session.h>
-#include <proton/link.h>
-
-/* Handler that replies to REMOTE_OPEN, stores the opened object on the handler */
-static pn_event_type_t open_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_REMOTE_OPEN:
-    th->connection = pn_event_connection(e);
-    pn_connection_open(th->connection);
-    break;
-   case PN_SESSION_REMOTE_OPEN:
-    th->session =  pn_event_session(e);
-    pn_session_open(th->session);
-    break;
-   case PN_LINK_REMOTE_OPEN:
-    th->link = pn_event_link(e);
-    pn_link_open(th->link);
-    break;
-   default:
-    break;
-  }
-  return PN_EVENT_NONE;
-}
-
-/* Like open_handler but also reply to REMOTE_CLOSE */
-static pn_event_type_t open_close_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_REMOTE_CLOSE:
-    pn_connection_open(pn_event_connection(e));
-    break;
-   case PN_SESSION_REMOTE_CLOSE:
-    pn_session_open(pn_event_session(e));
-    break;
-   case PN_LINK_REMOTE_CLOSE:
-    pn_link_close(pn_event_link(e));
-    break;
-   default:
-    return open_handler(th, e);
-  }
-  return PN_EVENT_NONE;
-}
-
-/* Handler that returns control on PN_DELIVERY and stores the delivery on the handler */
-static pn_event_type_t delivery_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_DELIVERY: {
-     th->delivery = pn_event_delivery(e);
-    return PN_DELIVERY;
-   }
-   default:
-    return open_handler(th, e);
-  }
-}
-
-/* Blow-by-blow event verification of a single message transfer */
-static void test_message_transfer(test_t *t) {
-  test_connection_driver_t client, server;
-  test_connection_driver_init(&client, t, open_handler, NULL);
-  test_connection_driver_init(&server, t, delivery_handler, NULL);
-  pn_transport_set_server(server.driver.transport);
-
-  pn_connection_open(client.driver.connection);
-  pn_session_t *ssn = pn_session(client.driver.connection);
-  pn_session_open(ssn);
-  pn_link_t *snd = pn_sender(ssn, "x");
-  pn_link_open(snd);
-  test_connection_drivers_run(&client, &server);
-
-  TEST_HANDLER_EXPECT(
-    &client.handler,
-    PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN,
-    PN_SESSION_INIT, PN_SESSION_LOCAL_OPEN,
-    PN_LINK_INIT, PN_LINK_LOCAL_OPEN,
-    PN_CONNECTION_BOUND, PN_CONNECTION_REMOTE_OPEN, PN_SESSION_REMOTE_OPEN, PN_LINK_REMOTE_OPEN,
-    0);
-
-  TEST_HANDLER_EXPECT(
-    &server.handler,
-    PN_CONNECTION_INIT, PN_CONNECTION_BOUND, PN_CONNECTION_REMOTE_OPEN,
-    PN_SESSION_INIT, PN_SESSION_REMOTE_OPEN,
-    PN_LINK_INIT, PN_LINK_REMOTE_OPEN,
-    PN_CONNECTION_LOCAL_OPEN, PN_TRANSPORT,
-    PN_SESSION_LOCAL_OPEN, PN_TRANSPORT,
-    PN_LINK_LOCAL_OPEN, PN_TRANSPORT,
-    0);
-
-  pn_link_t *rcv = server.handler.link;
-  TEST_CHECK(t, rcv);
-  TEST_CHECK(t, pn_link_is_receiver(rcv));
-  pn_link_flow(rcv, 1);
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT(&client.handler, PN_LINK_FLOW, 0);
-
-  /* Encode and send a message */
-  pn_message_t *m = pn_message();
-  pn_data_put_string(pn_message_body(m), pn_bytes(4, "abc")); /* Include trailing NULL */
-  pn_delivery(snd, PN_BYTES_LITERAL(x));
-  pn_message_send(m, snd, NULL);
-  pn_message_free(m);
-
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT(&server.handler, PN_TRANSPORT, PN_DELIVERY, 0);
-
-  /* Receive and decode the message */
-  pn_delivery_t *dlv = server.handler.delivery;
-  TEST_ASSERT(dlv);
-  pn_message_t *m2 = pn_message();
-  pn_rwbytes_t buf2 = { 0 };
-  message_decode(m2, dlv, &buf2);
-  pn_data_t *body = pn_message_body(m2);
-  pn_data_rewind(body);
-  TEST_CHECK(t, pn_data_next(body));
-  TEST_CHECK(t, PN_STRING == pn_data_type(body));
-  TEST_CHECK(t, 4 == pn_data_get_string(pn_message_body(m2)).size);
-  TEST_STR_EQUAL(t, "abc", pn_data_get_string(pn_message_body(m2)).start);
-  pn_message_free(m2);
-
-  free(buf2.start);
-  test_connection_driver_destroy(&client);
-  test_connection_driver_destroy(&server);
-}
-
-/* Handler that opens a connection and sender link */
-pn_event_type_t send_client_handler(test_handler_t *th, pn_event_t *e) {
-  switch (pn_event_type(e)) {
-   case PN_CONNECTION_LOCAL_OPEN: {
-    pn_connection_open(pn_event_connection(e));
-    pn_session_t *ssn = pn_session(pn_event_connection(e));
-    pn_session_open(ssn);
-    pn_link_t *snd = pn_sender(ssn, "x");
-    pn_link_open(snd);
-    break;
-   }
-   case PN_LINK_REMOTE_OPEN: {
-    th->link = pn_event_link(e);
-    return PN_LINK_REMOTE_OPEN;
-   }
-   default:
-    break;
-  }
-  return PN_EVENT_NONE;
-}
-
-/* Send a message in pieces, ensure each can be received before the next is sent */
-static void test_message_stream(test_t *t) {
-  /* Set up the link, give credit, start the delivery */
-  test_connection_driver_t client, server;
-  test_connection_driver_init(&client, t, send_client_handler, NULL);
-  test_connection_driver_init(&server, t, delivery_handler, NULL);
-  pn_transport_set_server(server.driver.transport);
-
-  pn_connection_open(client.driver.connection);
-  test_connection_drivers_run(&client, &server);
-  pn_link_t *rcv = server.handler.link;
-  pn_link_t *snd = client.handler.link;
-  pn_link_flow(rcv, 1);
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT_LAST(&client.handler, PN_LINK_FLOW);
-  TEST_HANDLER_EXPECT_LAST(&server.handler, PN_TRANSPORT);
-
-  /* Encode a large (not very) message to send in chunks */
-  pn_message_t *m = pn_message();
-  char body[1024] = { 0 };
-  pn_data_put_binary(pn_message_body(m), pn_bytes(sizeof(body), body));
-  pn_rwbytes_t buf = { 0 };
-  ssize_t size = message_encode(m, &buf);
-
-  /* Send and receive the message in chunks */
-  static const ssize_t CHUNK = 100;
-  pn_delivery(snd, PN_BYTES_LITERAL(x));
-  pn_rwbytes_t buf2 = { 0 };
-  ssize_t received = 0;
-  for (ssize_t i = 0; i < size; i += CHUNK) {
-    /* Send a chunk */
-    ssize_t c = (i+CHUNK < size) ? CHUNK : size - i;
-    TEST_CHECK(t, c == pn_link_send(snd, buf.start + i, c));
-    test_connection_drivers_run(&client, &server);
-    TEST_HANDLER_EXPECT_LAST(&server.handler, PN_DELIVERY);
-    /* Receive a chunk */
-    pn_delivery_t *dlv = server.handler.delivery;
-    pn_link_t *l = pn_delivery_link(dlv);
-    ssize_t dsize = pn_delivery_pending(dlv);
-    rwbytes_ensure(&buf2, received+dsize);
-    TEST_ASSERT(dsize == pn_link_recv(l, buf2.start + received, dsize));
-    received += dsize;
-  }
-  TEST_CHECK(t, pn_link_advance(snd));
-  TEST_CHECK(t, received == size);
-  TEST_CHECK(t, !memcmp(buf.start, buf2.start, size));
-
-  pn_message_free(m);
-  free(buf.start);
-  free(buf2.start);
-  test_connection_driver_destroy(&client);
-  test_connection_driver_destroy(&server);
-}
-
-// Test aborting a delivery
-static void test_message_abort(test_t *t) {
-  /* Set up the link, give credit, start the delivery */
-  test_connection_driver_t client, server;
-  test_connection_driver_init(&client, t, send_client_handler, NULL);
-  test_connection_driver_init(&server, t, delivery_handler, NULL);
-  pn_transport_set_server(server.driver.transport);
-  pn_connection_open(client.driver.connection);
-
-  test_connection_drivers_run(&client, &server);
-  pn_link_t *rcv = server.handler.link;
-  pn_link_t *snd = client.handler.link;
-  char data[100] = {0};          /* Dummy data to send. */
-  char rbuf[sizeof(data)] = {0}; /* Read buffer for incoming data. */
-
-  /* Send 2 frames with data */
-  pn_link_flow(rcv, 1);
-  TEST_INT_EQUAL(t, 1, pn_link_credit(rcv));
-  test_connection_drivers_run(&client, &server);
-  TEST_INT_EQUAL(t, 1, pn_link_credit(snd));
-  pn_delivery_t *sd = pn_delivery(snd, PN_BYTES_LITERAL(1)); /* Sender delivery */
-  for (size_t i = 0; i < 2; ++i) {
-    TEST_INT_EQUAL(t, sizeof(data), pn_link_send(snd, data, sizeof(data)));
-    test_connection_drivers_run(&client, &server);
-    TEST_HANDLER_EXPECT_LAST(&server.handler, PN_DELIVERY);
-    pn_delivery_t *rd = server.handler.delivery;
-    TEST_CHECK(t, !pn_delivery_aborted(rd));
-    TEST_CHECK(t, pn_delivery_partial(rd));
-    TEST_INT_EQUAL(t, 1, pn_link_credit(rcv));
-    TEST_INT_EQUAL(t, sizeof(data), pn_delivery_pending(rd));
-    TEST_INT_EQUAL(t, sizeof(rbuf), pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
-    TEST_INT_EQUAL(t, 0, pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
-    TEST_INT_EQUAL(t, 1, pn_link_credit(rcv));
-  }
-  TEST_INT_EQUAL(t, 1, pn_link_credit(snd));
-  /* Abort the delivery */
-  pn_delivery_abort(sd);
-  TEST_INT_EQUAL(t, 0, pn_link_credit(snd));
-  TEST_CHECK(t, pn_link_current(snd) != sd); /* Settled */
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT_LAST(&server.handler, PN_DELIVERY);
-  TEST_INT_EQUAL(t, 0, pn_link_credit(snd));
-
-  /* Receive the aborted=true frame, should be empty */
-  pn_delivery_t *rd = server.handler.delivery;
-  TEST_CHECK(t, pn_delivery_aborted(rd));
-  TEST_CHECK(t, !pn_delivery_partial(rd)); /* Aborted deliveries are never partial */
-  TEST_CHECK(t, pn_delivery_settled(rd)); /* Aborted deliveries are remote settled */
-  TEST_INT_EQUAL(t, 1, pn_delivery_pending(rd));
-  TEST_INT_EQUAL(t, PN_ABORTED, pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
-  pn_delivery_settle(rd);       /* Must be settled locally to free it */
-
-  TEST_INT_EQUAL(t, 0, pn_link_credit(snd));
-  TEST_INT_EQUAL(t, 0, pn_link_credit(rcv));
-
-  /* Abort a delivery before any data has been framed, should be dropped. */
-  pn_link_flow(rcv, 1);
-  TEST_INT_EQUAL(t, 1, pn_link_credit(rcv));
-  test_connection_drivers_run(&client, &server);
-  test_handler_clear(&client.handler, 0);
-  test_handler_clear(&server.handler, 0);
-
-  sd = pn_delivery(snd, PN_BYTES_LITERAL(x));
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_send(snd, data, sizeof(data)));
-  pn_delivery_abort(sd);
-  TEST_CHECK(t, pn_link_current(snd) != sd); /* Settled, possibly freed */
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT(&server.handler, 0); /* Expect no delivery at the server */
-  /* Client gets transport/flow after abort to ensure other messages are sent */
-  TEST_HANDLER_EXPECT(&client.handler, PN_TRANSPORT, PN_LINK_FLOW, 0);
-  /* Aborted delivery consumes no credit */
-  TEST_INT_EQUAL(t, 1, pn_link_credit(rcv));
-  TEST_INT_EQUAL(t, 1, pn_link_credit(snd));
-
-  test_connection_driver_destroy(&client);
-  test_connection_driver_destroy(&server);
-}
-
-
-int send_receive_message(test_t *t, const char* tag,
-                         test_connection_driver_t *src, test_connection_driver_t *dst)
-{
-  int errors = t->errors;
-  char data[100] = {0};          /* Dummy data to send. */
-  strncpy(data, tag, sizeof(data)-1);
-  data[99] = 0; /* Ensure terminated as we strcmp this later*/
-
-  if (!TEST_CHECK(t, pn_link_credit(src->handler.link))) return 1;
-
-  pn_delivery_t *sd = pn_delivery(src->handler.link, pn_dtag(tag, strlen(tag)));
-  dst->handler.delivery = NULL;
-  TEST_CHECK(t, pn_delivery_current(sd));
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_send(src->handler.link, data, sizeof(data)));
-  pn_delivery_settle(sd);
-  test_connection_drivers_run(src, dst);
-  pn_delivery_t *rd = dst->handler.delivery;
-  dst->handler.delivery = NULL;
-  if (!TEST_CHECK(t, rd)) return 1;
-
-  TEST_CHECK(t, pn_delivery_current(rd));
-  char rbuf[sizeof(data)] = {0}; /* Read buffer for incoming data. */
-  TEST_INT_EQUAL(t, sizeof(rbuf), pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
-  TEST_STR_EQUAL(t, data, rbuf);
-  pn_delivery_settle(rd);
-  return t->errors > errors;
-}
-
-#define SEND_RECEIVE_MESSAGE(T, TAG, SRC, DST)                  \
-  TEST_INT_EQUAL(T, 0, send_receive_message(T, TAG, SRC, DST))
-
-// Test mixing aborted and good deliveries, make sure credit is correct.
-static void test_message_abort_mixed(test_t *t) {
-  /* Set up the link, give credit, start the delivery */
-  test_connection_driver_t client, server;
-  test_connection_driver_init(&client, t, send_client_handler, NULL);
-  test_connection_driver_init(&server, t, delivery_handler, NULL);
-  pn_transport_set_server(server.driver.transport);
-  pn_connection_open(client.driver.connection);
-
-  test_connection_drivers_run(&client, &server);
-  pn_link_t *rcv = server.handler.link;
-  pn_link_t *snd = client.handler.link;
-  char data[100] = {0};          /* Dummy data to send. */
-  char rbuf[sizeof(data)] = {0}; /* Read buffer for incoming data. */
-
-  /* We will send 3 good messages, interleaved with aborted ones */
-  pn_link_flow(rcv, 5);
-  test_connection_drivers_run(&client, &server);
-  SEND_RECEIVE_MESSAGE(t, "one", &client, &server);
-  TEST_INT_EQUAL(t, 4, pn_link_credit(snd));
-  TEST_INT_EQUAL(t, 4, pn_link_credit(rcv));
-  pn_delivery_t *sd, *rd;
-
-  /* Send a frame, then an abort */
-  sd = pn_delivery(snd, PN_BYTES_LITERAL("x1"));
-  server.handler.delivery = NULL;
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_send(snd, data, sizeof(data)));
-  TEST_INT_EQUAL(t, 4, pn_link_credit(snd)); /* Nothing sent yet */
-  test_connection_drivers_run(&client, &server);
-  rd = server.handler.delivery;
-  if (!TEST_CHECK(t, rd)) goto cleanup;
-  TEST_INT_EQUAL(t, sizeof(rbuf), pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
-
-  pn_delivery_abort(sd);
-  test_connection_drivers_run(&client, &server);
-  TEST_CHECK(t, pn_delivery_aborted(rd));
-  pn_delivery_settle(rd);
-  /* Abort after sending data consumes credit */
-  TEST_INT_EQUAL(t, 3, pn_link_credit(snd));
-  TEST_INT_EQUAL(t, 3, pn_link_credit(rcv));
-
-  SEND_RECEIVE_MESSAGE(t, "two", &client, &server);
-  TEST_INT_EQUAL(t, 2, pn_link_credit(snd));
-  TEST_INT_EQUAL(t, 2, pn_link_credit(rcv));
-
-  /* Abort a delivery before any data has been framed, should be dropped. */
-  test_handler_clear(&server.handler, 0);
-  sd = pn_delivery(snd, PN_BYTES_LITERAL(4));
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_send(snd, data, sizeof(data)));
-  pn_delivery_abort(sd);
-  TEST_CHECK(t, pn_link_current(snd) != sd); /* Advanced */
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT(&server.handler, PN_TRANSPORT, 0);
-  /* Aborting wit no frames sent should leave credit untouched */
-  TEST_INT_EQUAL(t, 2, pn_link_credit(snd));
-  TEST_INT_EQUAL(t, 2, pn_link_credit(rcv));
-
-  SEND_RECEIVE_MESSAGE(t, "three", &client, &server);
-  TEST_INT_EQUAL(t, 1, pn_link_credit(rcv));
-  TEST_INT_EQUAL(t, 1, pn_link_credit(snd));
-
- cleanup:
-  test_connection_driver_destroy(&client);
-  test_connection_driver_destroy(&server);
-}
-
-/* Set capacity and max frame, send a single message */
-static void set_capacity_and_max_frame(
-  size_t capacity, size_t max_frame,
-  test_connection_driver_t *client, test_connection_driver_t *server,
-  const char* data)
-{
-  pn_transport_set_max_frame(client->driver.transport, max_frame);
-  pn_connection_open(client->driver.connection);
-  pn_session_t *ssn = pn_session(client->driver.connection);
-  pn_session_set_incoming_capacity(ssn, capacity);
-  pn_session_open(ssn);
-  pn_link_t *snd = pn_sender(ssn, "x");
-  pn_link_open(snd);
-  test_connection_drivers_run(client, server);
-  pn_link_flow(server->handler.link, 1);
-  test_connection_drivers_run(client, server);
-  if (pn_transport_closed(client->driver.transport) ||
-      pn_transport_closed(server->driver.transport))
-    return;
-  /* Send a message */
-  pn_message_t *m = pn_message();
-  pn_message_set_address(m, data);
-  pn_delivery(snd, PN_BYTES_LITERAL(x));
-  pn_message_send(m, snd, NULL);
-  pn_message_free(m);
-  test_connection_drivers_run(client, server);
-}
-
-/* Test different settings for max-frame, outgoing-window, incoming-capacity */
-static void test_session_flow_control(test_t *t) {
-  test_connection_driver_t client, server;
-  test_connection_drivers_init(t, &client, open_handler, &server, delivery_handler);
-  pn_message_t *m = pn_message();
-  pn_rwbytes_t buf= {0};
-
-  /* Capacity equal to frame size OK */
-  set_capacity_and_max_frame(1234, 1234, &client, &server, "foo");
-  pn_delivery_t *dlv = server.handler.delivery;
-  if (TEST_CHECK(t, dlv)) {
-    message_decode(m, dlv, &buf);
-    TEST_STR_EQUAL(t, "foo", pn_message_get_address(m));
-  }
-
-  /* Capacity bigger than frame size OK */
-  set_capacity_and_max_frame(12345, 1234, &client, &server, "foo");
-  dlv = server.handler.delivery;
-  if (TEST_CHECK(t, dlv)) {
-    message_decode(m, dlv, &buf);
-    TEST_STR_EQUAL(t, "foo", pn_message_get_address(m));
-  }
-
-  /* Capacity smaller than frame size is an error */
-  set_capacity_and_max_frame(1234, 12345, &client, &server, "foo");
-  TEST_COND_NAME(t, "amqp:internal-error", pn_transport_condition(client.driver.transport));
-  TEST_COND_DESC(t, "session capacity 1234 is less than frame size 12345", pn_transport_condition(client.driver.transport));
-
-  pn_message_free(m);
-  free(buf.start);
-  test_connection_drivers_destroy(&client, &server);
-}
-
-/* Regression test for https://issues.apache.org/jira/browse/PROTON-1832.
-   Make sure we error on attempt to re-attach an already-attached link name.
-   No crash or memory error.
-*/
-static void test_duplicate_link_server(test_t *t) {
-  test_connection_driver_t client, server;
-  test_connection_drivers_init(t, &client, open_close_handler, &server, open_close_handler);
-  pn_connection_open(client.driver.connection);
-  pn_session_t *ssn = pn_session(client.driver.connection);
-  pn_session_open(ssn);
-
-  /* Set up link "x" */
-  pn_link_t *x = pn_sender(ssn, "xxx");
-  pn_link_open(x);
-  test_connection_drivers_run(&client, &server);
-  test_handler_clear(&client.handler, 0);
-  test_handler_clear(&server.handler, 0);
-  /* Free (but don't close) the link and open a new one to generate the invalid double-attach */
-  pn_link_free(x);
-  pn_link_open(pn_sender(ssn, "xxx"));
-  test_connection_drivers_run(&client, &server);
-
-  TEST_COND_NAME(t, "amqp:invalid-field", pn_transport_condition(server.driver.transport));
-  TEST_COND_DESC(t, "xxx", pn_transport_condition(server.driver.transport));
-
-  TEST_COND_NAME(t, "amqp:invalid-field", pn_connection_remote_condition(client.driver.connection));
-  TEST_COND_DESC(t, "xxx", pn_connection_remote_condition(client.driver.connection));
-
-  /* Freeing the link at this point is allowed but caused a crash in transport_unbind with the bug */
-  pn_link_free(server.handler.link);
-  test_connection_drivers_destroy(&client, &server);
-}
-
-/* Reproducer test for https://issues.apache.org/jira/browse/PROTON-1832.
-   Make sure the client does not generate an illegal "attach; attach; detach" sequence
-   from a legal "pn_link_open(); pn_link_close(); pn_link_open()" sequence
-*/
-static void test_duplicate_link_client(test_t *t) {
-  /* This test will fail till PROTON-1832 is fully fixed */
-  t->inverted = true;
-  /* Set up the initial link */
-  test_connection_driver_t client, server;
-  test_connection_drivers_init(t, &client, open_close_handler, &server, open_close_handler);
-  pn_connection_open(client.driver.connection);
-  pn_session_t *ssn = pn_session(client.driver.connection);
-  pn_session_open(ssn);
-  pn_link_t *x = pn_sender(ssn, "x");
-  pn_link_open(x);
-  test_connection_drivers_run(&client, &server);
-  test_handler_clear(&client.handler, 0);
-  test_handler_clear(&server.handler, 0);
-
-  /* Close the link and open a new link with same name in the same batch of events. */
-  pn_link_close(x);
-  pn_link_open(pn_sender(ssn, "x"));
-  test_connection_drivers_run(&client, &server);
-
-  TEST_HANDLER_EXPECT(&server.handler,
-                      PN_LINK_REMOTE_CLOSE, PN_LINK_LOCAL_CLOSE, PN_TRANSPORT,
-                      PN_LINK_INIT, PN_LINK_REMOTE_OPEN, PN_LINK_LOCAL_OPEN, PN_TRANSPORT,
-                      0);
-  TEST_COND_EMPTY(t, pn_transport_condition(server.driver.transport));
-
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT(&client.handler,
-                      PN_LINK_LOCAL_CLOSE, PN_TRANSPORT, PN_LINK_REMOTE_CLOSE,
-                      PN_LINK_INIT, PN_LINK_LOCAL_OPEN, PN_TRANSPORT, PN_LINK_REMOTE_OPEN,
-                      0);
-  TEST_COND_EMPTY(t, pn_connection_remote_condition(client.driver.connection));
-  test_connection_drivers_destroy(&client, &server);
-}
-
-/* Settling an incomplete delivery should not cause an error */
-static void test_settle_incomplete_receiver(test_t *t) {
-  /* Inverted: this test will fail till PROTON-1914 is fixed */
-  t->inverted = true;
-
-  /* Set up the link, give credit, start the delivery */
-  test_connection_driver_t client, server;
-  test_connection_driver_init(&client, t, send_client_handler, NULL);
-  test_connection_driver_init(&server, t, delivery_handler, NULL);
-  pn_transport_set_server(server.driver.transport);
-  pn_connection_open(client.driver.connection);
-
-  test_connection_drivers_run(&client, &server);
-  pn_link_t *rcv = server.handler.link;
-  pn_link_t *snd = client.handler.link;
-  char data[100] = {0};          /* Dummy data to send. */
-  char rbuf[sizeof(data)] = {0}; /* Read buffer for incoming data. */
-  pn_link_flow(rcv, 1);
-  pn_delivery(snd, PN_BYTES_LITERAL(1)); /* Prepare to send */
-  test_connection_drivers_run(&client, &server);
-
-  /* Send/receive a frame */
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_send(snd, data, sizeof(data)));
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT_LAST(&server.handler, PN_DELIVERY);
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_recv(rcv, rbuf, sizeof(data)));
-  test_connection_drivers_run(&client, &server);
-
-  /* Settle the receiver's delivery */
-  pn_delivery_settle(pn_link_current(rcv));
-  test_connection_drivers_run(&client, &server);
-  TEST_COND_EMPTY(t, pn_connection_remote_condition(client.driver.connection));
-  TEST_COND_EMPTY(t, pn_connection_condition(server.driver.connection));
-
-  /* Send/receive a frame, should not cause error */
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_send(snd, data, sizeof(data)));
-  test_connection_drivers_run(&client, &server);
-  TEST_HANDLER_EXPECT_LAST(&server.handler, PN_DELIVERY);
-  TEST_INT_EQUAL(t, sizeof(data), pn_link_recv(rcv, rbuf, sizeof(data)));
-  test_connection_drivers_run(&client, &server);
-  TEST_COND_EMPTY(t, pn_connection_remote_condition(client.driver.connection));
-  TEST_COND_EMPTY(t, pn_connection_condition(server.driver.connection));
-
-  test_connection_driver_destroy(&client);
-  test_connection_driver_destroy(&server);
-}
-
-int main(int argc, char **argv) {
-  int failed = 0;
-  RUN_ARGV_TEST(failed, t, test_message_transfer(&t));
-  RUN_ARGV_TEST(failed, t, test_message_stream(&t));
-  RUN_ARGV_TEST(failed, t, test_message_abort(&t));
-  RUN_ARGV_TEST(failed, t, test_message_abort_mixed(&t));
-  RUN_ARGV_TEST(failed, t, test_session_flow_control(&t));
-  RUN_ARGV_TEST(failed, t, test_duplicate_link_server(&t));
-  RUN_ARGV_TEST(failed, t, test_duplicate_link_client(&t));
-  RUN_ARGV_TEST(failed, t, test_settle_incomplete_receiver(&t));
-  return failed;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/connection_driver_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/connection_driver_test.cpp b/c/tests/connection_driver_test.cpp
new file mode 100644
index 0000000..175a77d
--- /dev/null
+++ b/c/tests/connection_driver_test.cpp
@@ -0,0 +1,567 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ h * 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/codec.h>
+#include <proton/connection.h>
+#include <proton/connection_driver.h>
+#include <proton/delivery.h>
+#include <proton/link.h>
+#include <proton/message.h>
+#include <proton/session.h>
+#include <proton/transport.h>
+
+#include <string.h>
+
+using Catch::Matchers::EndsWith;
+using Catch::Matchers::Equals;
+using namespace pn_test;
+
+namespace {
+
+/* Handler that replies to REMOTE_OPEN, stores the opened object on the handler
+ */
+struct open_handler : pn_test::handler {
+  bool handle(pn_event_t *e) CATCH_OVERRIDE {
+    switch (pn_event_type(e)) {
+    case PN_CONNECTION_REMOTE_OPEN:
+      connection = pn_event_connection(e);
+      pn_connection_open(connection);
+      break;
+    case PN_SESSION_REMOTE_OPEN:
+      session = pn_event_session(e);
+      pn_session_open(session);
+      break;
+    case PN_LINK_REMOTE_OPEN:
+      link = pn_event_link(e);
+      pn_link_open(link);
+      break;
+    default:
+      break;
+    }
+    return false;
+  }
+};
+
+/* Like open_handler but also reply to REMOTE_CLOSE */
+struct open_close_handler : public open_handler {
+  bool handle(pn_event_t *e) CATCH_OVERRIDE {
+    switch (pn_event_type(e)) {
+    case PN_CONNECTION_REMOTE_CLOSE:
+      pn_connection_open(pn_event_connection(e));
+      break;
+    case PN_SESSION_REMOTE_CLOSE:
+      pn_session_open(pn_event_session(e));
+      break;
+    case PN_LINK_REMOTE_CLOSE:
+      pn_link_close(pn_event_link(e));
+      break;
+    default:
+      return open_handler::handle(e);
+    }
+    return false;
+  }
+};
+
+/* open_handler that returns control on PN_DELIVERY and stores the delivery */
+struct delivery_handler : public open_handler {
+  bool handle(pn_event_t *e) {
+    switch (pn_event_type(e)) {
+    case PN_DELIVERY: {
+      delivery = pn_event_delivery(e);
+      return true;
+    }
+    default:
+      return open_handler::handle(e);
+    }
+  }
+};
+
+} // namespace
+
+/* Blow-by-blow event verification of a single message transfer */
+TEST_CASE("driver_message_transfer") {
+  open_handler client;
+  delivery_handler server;
+  pn_test::driver_pair d(client, server);
+
+  pn_connection_open(d.client.connection);
+  pn_session_t *ssn = pn_session(d.client.connection);
+  pn_session_open(ssn);
+  pn_link_t *snd = pn_sender(ssn, "x");
+  pn_link_open(snd);
+  d.run();
+
+  CHECK_THAT(ETYPES(PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN,
+                    PN_SESSION_INIT, PN_SESSION_LOCAL_OPEN, PN_LINK_INIT,
+                    PN_LINK_LOCAL_OPEN, PN_CONNECTION_BOUND,
+                    PN_CONNECTION_REMOTE_OPEN, PN_SESSION_REMOTE_OPEN,
+                    PN_LINK_REMOTE_OPEN),
+             Equals(client.log_clear()));
+
+  CHECK_THAT(ETYPES(PN_CONNECTION_INIT, PN_CONNECTION_BOUND,
+                    PN_CONNECTION_REMOTE_OPEN, PN_SESSION_INIT,
+                    PN_SESSION_REMOTE_OPEN, PN_LINK_INIT, PN_LINK_REMOTE_OPEN,
+                    PN_CONNECTION_LOCAL_OPEN, PN_TRANSPORT,
+                    PN_SESSION_LOCAL_OPEN, PN_TRANSPORT, PN_LINK_LOCAL_OPEN,
+                    PN_TRANSPORT),
+             Equals(server.log_clear()));
+
+  pn_link_t *rcv = server.link;
+  REQUIRE(rcv);
+  REQUIRE(pn_link_is_receiver(rcv));
+  pn_link_flow(rcv, 1);
+  d.run();
+  CHECK_THAT(ETYPES(PN_LINK_FLOW), Equals(client.log_clear()));
+
+  /* Encode and send a message */
+  auto_free<pn_message_t, pn_message_free> m(pn_message());
+  pn_data_put_string(pn_message_body(m),
+                     pn_bytes("abc")); /* Include trailing NULL */
+  pn_delivery(snd, pn_bytes("x"));
+  pn_message_send(m, snd, NULL);
+
+  d.run();
+  CHECK_THAT(ETYPES(PN_TRANSPORT, PN_DELIVERY), Equals(server.log_clear()));
+
+  /* Receive and decode the message */
+  pn_delivery_t *dlv = server.delivery;
+  REQUIRE(dlv);
+  auto_free<pn_message_t, pn_message_free> m2(pn_message());
+  pn_rwbytes_t buf2 = {0};
+  message_decode(m2, dlv, &buf2);
+  pn_data_t *body = pn_message_body(m2);
+  pn_data_rewind(body);
+  CHECK(pn_data_next(body));
+  CHECK(PN_STRING == pn_data_type(body));
+  CHECK(3 == pn_data_get_string(pn_message_body(m2)).size);
+  CHECK_THAT("abc", Equals(pn_data_get_string(pn_message_body(m2)).start));
+
+  free(buf2.start);
+}
+
+namespace {
+/* Handler that opens a connection and sender link */
+struct send_client_handler : public pn_test::handler {
+  bool handle(pn_event_t *e) {
+    switch (pn_event_type(e)) {
+    case PN_CONNECTION_LOCAL_OPEN: {
+      pn_connection_open(pn_event_connection(e));
+      pn_session_t *ssn = pn_session(pn_event_connection(e));
+      pn_session_open(ssn);
+      pn_link_t *snd = pn_sender(ssn, "x");
+      pn_link_open(snd);
+      break;
+    }
+    case PN_LINK_REMOTE_OPEN: {
+      link = pn_event_link(e);
+      return true;
+    }
+    default:
+      break;
+    }
+    return false;
+  }
+};
+} // namespace
+
+/* Send a message in pieces, ensure each can be received before the next is sent
+ */
+TEST_CASE("driver_message_stream") {
+  send_client_handler client;
+  delivery_handler server;
+  pn_test::driver_pair d(client, server);
+
+  d.run();
+  pn_link_t *rcv = server.link;
+  pn_link_t *snd = client.link;
+  pn_link_flow(rcv, 1);
+  d.run();
+  CHECK(PN_LINK_FLOW == client.log_last());
+  CHECK(PN_TRANSPORT == server.log_last());
+
+  /* Encode a large (not very) message to send in chunks */
+  auto_free<pn_message_t, pn_message_free> m(pn_message());
+  char body[1024] = {0};
+  pn_data_put_binary(pn_message_body(m), pn_bytes(sizeof(body), body));
+  pn_rwbytes_t buf = {0};
+  ssize_t size = pn_message_encode2(m, &buf);
+
+  /* Send and receive the message in chunks */
+  static const ssize_t CHUNK = 100;
+  pn_delivery(snd, pn_bytes("x"));
+  pn_rwbytes_t buf2 = {0};
+  ssize_t received = 0;
+  for (ssize_t i = 0; i < size; i += CHUNK) {
+    /* Send a chunk */
+    ssize_t c = (i + CHUNK < size) ? CHUNK : size - i;
+    CHECK(c == pn_link_send(snd, buf.start + i, c));
+    d.run();
+    CHECK_THAT(ETYPES(PN_DELIVERY), Equals(server.log_clear()));
+    /* Receive a chunk */
+    pn_delivery_t *dlv = server.delivery;
+    pn_link_t *l = pn_delivery_link(dlv);
+    ssize_t dsize = pn_delivery_pending(dlv);
+    rwbytes_ensure(&buf2, received + dsize);
+    REQUIRE(dsize == pn_link_recv(l, buf2.start + received, dsize));
+    received += dsize;
+  }
+  CHECK(pn_link_advance(snd));
+  CHECK(received == size);
+  CHECK(!memcmp(buf.start, buf2.start, size));
+
+  free(buf.start);
+  free(buf2.start);
+}
+
+// Test aborting a delivery
+TEST_CASE("driver_message_abort") {
+  send_client_handler client;
+  delivery_handler server;
+  pn_test::driver_pair d(client, server);
+
+  d.run();
+  pn_link_t *rcv = server.link;
+  pn_link_t *snd = client.link;
+  char data[100] = {0};          /* Dummy data to send. */
+  char rbuf[sizeof(data)] = {0}; /* Read buffer for incoming data. */
+
+  /* Send 2 frames with data */
+  pn_link_flow(rcv, 1);
+  CHECK(1 == pn_link_credit(rcv));
+  d.run();
+  CHECK(1 == pn_link_credit(snd));
+  pn_delivery_t *sd = pn_delivery(snd, pn_bytes("1")); /* Sender delivery */
+  for (size_t i = 0; i < 2; ++i) {
+    CHECK(sizeof(data) == pn_link_send(snd, data, sizeof(data)));
+    d.run();
+    CHECK(server.log_last() == PN_DELIVERY);
+    pn_delivery_t *rd = server.delivery;
+    CHECK(!pn_delivery_aborted(rd));
+    CHECK(pn_delivery_partial(rd));
+    CHECK(1 == pn_link_credit(rcv));
+    CHECK(sizeof(data) == pn_delivery_pending(rd));
+    CHECK(sizeof(rbuf) ==
+          pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
+    CHECK(0 == pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
+    CHECK(1 == pn_link_credit(rcv));
+  }
+  CHECK(1 == pn_link_credit(snd));
+  /* Abort the delivery */
+  pn_delivery_abort(sd);
+  CHECK(0 == pn_link_credit(snd));
+  CHECK(pn_link_current(snd) != sd); /* Settled */
+  d.run();
+  CHECK(PN_DELIVERY == server.log_last());
+  CHECK(0 == pn_link_credit(snd));
+
+  /* Receive the aborted=true frame, should be empty */
+  pn_delivery_t *rd = server.delivery;
+  CHECK(pn_delivery_aborted(rd));
+  CHECK(!pn_delivery_partial(rd)); /* Aborted deliveries are never partial */
+  CHECK(pn_delivery_settled(rd));  /* Aborted deliveries are remote settled */
+  CHECK(1 == pn_delivery_pending(rd));
+  CHECK(PN_ABORTED == pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
+  pn_delivery_settle(rd); /* Must be settled locally to free it */
+
+  CHECK(0 == pn_link_credit(snd));
+  CHECK(0 == pn_link_credit(rcv));
+
+  /* Abort a delivery before any data has been framed, should be dropped. */
+  pn_link_flow(rcv, 1);
+  CHECK(1 == pn_link_credit(rcv));
+  d.run();
+  client.log_clear();
+  server.log_clear();
+
+  sd = pn_delivery(snd, pn_bytes("x"));
+  CHECK(sizeof(data) == pn_link_send(snd, data, sizeof(data)));
+  pn_delivery_abort(sd);
+  CHECK(pn_link_current(snd) != sd); /* Settled, possibly freed */
+  CHECK_FALSE(d.run());
+  CHECK_THAT(server.log_clear(), Equals(etypes()));
+  /* Client gets transport/flow after abort to ensure other messages are sent */
+  CHECK_THAT(ETYPES(PN_TRANSPORT, PN_LINK_FLOW), Equals(client.log_clear()));
+  /* Aborted delivery consumes no credit */
+  CHECK(1 == pn_link_credit(rcv));
+  CHECK(1 == pn_link_credit(snd));
+}
+
+void send_receive_message(const std::string &tag, pn_test::driver_pair &d) {
+  pn_link_t *l = d.client.handler.link;
+  CHECKED_IF(pn_link_credit(l) > 0) {
+    pn_delivery_t *sd = pn_delivery(l, pn_dtag(tag.data(), tag.size()));
+    d.server.handler.delivery = NULL;
+    CHECK(pn_delivery_current(sd));
+    CHECK(tag.size() == pn_link_send(l, tag.data(), tag.size()));
+    pn_delivery_settle(sd);
+    d.run();
+    pn_delivery_t *rd = d.server.handler.delivery;
+    d.server.handler.delivery = NULL;
+    CHECKED_IF(rd) {
+      CHECK(pn_delivery_current(rd));
+      std::string rbuf(tag.size() * 2, 'x');
+      CHECK(tag.size() ==
+            pn_link_recv(pn_delivery_link(rd), &rbuf[0], rbuf.size()));
+      rbuf.resize(tag.size());
+      CHECK(tag == rbuf);
+    }
+    pn_delivery_settle(rd);
+  }
+}
+
+#define SEND_RECEIVE_MESSAGE(TAG, DP)                                          \
+  do {                                                                         \
+    INFO("in send_receive_message: " << TAG);                                  \
+    send_receive_message(TAG, DP);                                             \
+  } while (0)
+
+// Test mixing aborted and good deliveries, make sure credit is correct.
+TEST_CASE("driver_message_abort_mixed") {
+  send_client_handler client;
+  delivery_handler server;
+  pn_test::driver_pair d(client, server);
+
+  d.run();
+  pn_link_t *rcv = server.link;
+  pn_link_t *snd = client.link;
+  char data[100] = {0};          /* Dummy data to send. */
+  char rbuf[sizeof(data)] = {0}; /* Read buffer for incoming data. */
+
+  /* We will send 3 good messages, interleaved with aborted ones */
+  pn_link_flow(rcv, 5);
+  d.run();
+  SEND_RECEIVE_MESSAGE("one", d);
+  CHECK(4 == pn_link_credit(snd));
+  CHECK(4 == pn_link_credit(rcv));
+  pn_delivery_t *sd, *rd;
+
+  /* Send a frame, then an abort */
+  sd = pn_delivery(snd, pn_bytes("x1"));
+  server.delivery = NULL;
+  CHECK(sizeof(data) == pn_link_send(snd, data, sizeof(data)));
+  CHECK(4 == pn_link_credit(snd)); /* Nothing sent yet */
+  d.run();
+  rd = server.delivery;
+  REQUIRE(rd);
+  CHECK(sizeof(rbuf) == pn_link_recv(pn_delivery_link(rd), rbuf, sizeof(rbuf)));
+
+  pn_delivery_abort(sd);
+  d.run();
+  CHECK(pn_delivery_aborted(rd));
+  pn_delivery_settle(rd);
+  /* Abort after sending data consumes credit */
+  CHECK(3 == pn_link_credit(snd));
+  CHECK(3 == pn_link_credit(rcv));
+
+  SEND_RECEIVE_MESSAGE("two", d);
+  CHECK(2 == pn_link_credit(snd));
+  CHECK(2 == pn_link_credit(rcv));
+
+  /* Abort a delivery before any data has been framed, should be dropped. */
+  server.log.clear();
+  sd = pn_delivery(snd, pn_bytes("4"));
+  CHECK(sizeof(data) == pn_link_send(snd, data, sizeof(data)));
+  pn_delivery_abort(sd);
+  CHECK(pn_link_current(snd) != sd); /* Advanced */
+  d.run();
+  CHECK_THAT(ETYPES(PN_TRANSPORT), Equals(server.log_clear()));
+  /* Aborting wit no frames sent should leave credit untouched */
+  CHECK(2 == pn_link_credit(snd));
+  CHECK(2 == pn_link_credit(rcv));
+
+  SEND_RECEIVE_MESSAGE("three", d);
+  CHECK(1 == pn_link_credit(rcv));
+  CHECK(1 == pn_link_credit(snd));
+}
+
+/* Set capacity and max frame, send a single message */
+static void set_capacity_and_max_frame(size_t capacity, size_t max_frame,
+                                       pn_test::driver_pair &d,
+                                       const char *data) {
+  pn_transport_set_max_frame(d.client.transport, max_frame);
+  pn_connection_open(d.client.connection);
+  pn_session_t *ssn = pn_session(d.client.connection);
+  pn_session_set_incoming_capacity(ssn, capacity);
+  pn_session_open(ssn);
+  pn_link_t *snd = pn_sender(ssn, "x");
+  pn_link_open(snd);
+  d.run();
+  pn_link_flow(d.server.handler.link, 1);
+  d.run();
+  if (pn_transport_closed(d.client.transport) ||
+      pn_transport_closed(d.server.transport))
+    return;
+  /* Send a message */
+  auto_free<pn_message_t, pn_message_free> m(pn_message());
+  pn_message_set_address(m, data);
+  pn_delivery(snd, pn_bytes("x"));
+  pn_message_send(m, snd, NULL);
+  d.run();
+}
+
+/* Test different settings for max-frame, outgoing-window, incoming-capacity */
+TEST_CASE("driver_session_flow_control") {
+  open_handler client;
+  delivery_handler server;
+  pn_test::driver_pair d(client, server);
+
+  auto_free<pn_message_t, pn_message_free> m(pn_message());
+  pn_rwbytes_t buf = {0};
+
+  /* Capacity equal to frame size OK */
+  set_capacity_and_max_frame(1234, 1234, d, "foo");
+  pn_delivery_t *dlv = server.delivery;
+  CHECKED_IF(dlv) {
+    message_decode(m, dlv, &buf);
+    CHECK_THAT("foo", Equals(pn_message_get_address(m)));
+  }
+
+  /* Capacity bigger than frame size OK */
+  set_capacity_and_max_frame(12345, 1234, d, "foo");
+  dlv = server.delivery;
+  CHECKED_IF(dlv) {
+    message_decode(m, dlv, &buf);
+    CHECK_THAT("foo", Equals(pn_message_get_address(m)));
+  }
+
+  /* Capacity smaller than frame size is an error */
+  set_capacity_and_max_frame(1234, 12345, d, "foo");
+  CHECK_THAT(
+      *client.last_condition,
+      cond_matches("amqp:internal-error",
+                   "session capacity 1234 is less than frame size 12345"));
+  free(buf.start);
+}
+
+/* Regression test for https://issues.apache.org/jira/browse/PROTON-1832.
+   Make sure we error on attempt to re-attach an already-attached link name.
+   No crash or memory error.
+*/
+TEST_CASE("driver_duplicate_link_server") {
+  open_close_handler client, server;
+  pn_test::driver_pair d(client, server);
+
+  pn_connection_open(d.client.connection);
+  pn_session_t *ssn = pn_session(d.client.connection);
+  pn_session_open(ssn);
+
+  /* Set up link "x" */
+  auto_free<pn_link_t, pn_link_free> x(pn_sender(ssn, "xxx"));
+  pn_link_open(x);
+  d.run();
+  client.log.clear();
+  server.log.clear();
+  /* Free (but don't close) the link and open a new one to generate the invalid
+   * double-attach */
+  pn_link_open(pn_sender(ssn, "xxx"));
+  d.run();
+
+  CHECK_THAT(*pn_transport_condition(d.server.transport),
+             cond_matches("amqp:invalid-field", "xxx"));
+  CHECK_THAT(*pn_connection_remote_condition(d.client.connection),
+             cond_matches("amqp:invalid-field", "xxx"));
+
+  /* Freeing the link at this point is allowed but caused a crash in
+   * transport_unbind with the bug */
+}
+
+/* Reproducer test for https://issues.apache.org/jira/browse/PROTON-1832.
+   Make sure the client does not generate an illegal "attach; attach; detach"
+   sequence from a legal "pn_link_open(); pn_link_close(); pn_link_open()"
+   sequence
+
+   This test is expected to fail till PROTON-1832 is fully fixed
+*/
+TEST_CASE("driver_duplicate_link_client", "[!hide][!shouldfail]") {
+  /* Set up the initial link */
+  open_close_handler client, server;
+  pn_test::driver_pair d(client, server);
+
+  pn_session_t *ssn = pn_session(d.client.connection);
+  pn_session_open(ssn);
+  pn_link_t *x = pn_sender(ssn, "x");
+  pn_link_open(x);
+  d.run();
+  client.log.clear();
+  server.log.clear();
+
+  /* Close the link and open a new link with same name in the same batch of
+   * events. */
+  pn_link_close(x);
+  pn_link_open(pn_sender(ssn, "x"));
+  d.run();
+
+  CHECK_THAT(ETYPES(PN_LINK_REMOTE_CLOSE, PN_LINK_LOCAL_CLOSE, PN_TRANSPORT,
+                    PN_LINK_INIT, PN_LINK_REMOTE_OPEN, PN_LINK_LOCAL_OPEN,
+                    PN_TRANSPORT),
+             Equals(server.log_clear()));
+  CHECK_THAT(*pn_transport_condition(d.server.transport), cond_empty());
+
+  d.run();
+  CHECK_THAT(ETYPES(PN_LINK_LOCAL_CLOSE, PN_TRANSPORT, PN_LINK_REMOTE_CLOSE,
+                    PN_LINK_INIT, PN_LINK_LOCAL_OPEN, PN_TRANSPORT,
+                    PN_LINK_REMOTE_OPEN),
+             Equals(client.log_clear()));
+  CHECK_THAT(*pn_connection_remote_condition(d.client.connection),
+             cond_empty());
+}
+
+/* Settling an incomplete delivery should not cause an error
+   This test will fail till PROTON-1914 is fixed
+*/
+TEST_CASE("driver_settle_incomplete_receiver", "[!hide][!shouldfail]") {
+  send_client_handler client;
+  delivery_handler server;
+  pn_test::driver_pair d(client, server);
+
+  d.run();
+  pn_link_t *rcv = server.link;
+  pn_link_t *snd = client.link;
+  char data[100] = {0};          /* Dummy data to send. */
+  char rbuf[sizeof(data)] = {0}; /* Read buffer for incoming data. */
+  pn_link_flow(rcv, 1);
+  pn_delivery(snd, pn_bytes("1")); /* Prepare to send */
+  d.run();
+
+  /* Send/receive a frame */
+  CHECK(sizeof(data) == pn_link_send(snd, data, sizeof(data)));
+  d.run();
+  CHECK_THAT(ETYPES(PN_DELIVERY), Equals(server.log_clear()));
+  CHECK(sizeof(data) == pn_link_recv(rcv, rbuf, sizeof(data)));
+  d.run();
+
+  /* Settle the receiver's delivery */
+  pn_delivery_settle(pn_link_current(rcv));
+  d.run();
+  CHECK_THAT(*pn_connection_remote_condition(d.client.connection),
+             cond_empty());
+  CHECK_THAT(*pn_connection_condition(d.server.connection), cond_empty());
+
+  /* Send/receive a frame, should not cause error */
+  CHECK(sizeof(data) == pn_link_send(snd, data, sizeof(data)));
+  d.run();
+  CHECK_THAT(ETYPES(PN_DELIVERY), Equals(server.log_clear()));
+  CHECK(sizeof(data) == pn_link_recv(rcv, rbuf, sizeof(data)));
+  d.run();
+  CHECK_THAT(*pn_connection_remote_condition(d.client.connection),
+             cond_empty());
+  CHECK_THAT(*pn_connection_condition(d.server.connection), cond_empty());
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/data.c
----------------------------------------------------------------------
diff --git a/c/tests/data.c b/c/tests/data.c
deleted file mode 100644
index 8f8030f..0000000
--- a/c/tests/data.c
+++ /dev/null
@@ -1,104 +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.
- *
- */
-
-#undef NDEBUG                   /* Make sure that assert() is enabled even in a release build. */
-
-#include "test_tools.h"
-#include "core/data.h"
-
-#include <proton/codec.h>
-#include <assert.h>
-#include <stdio.h>
-
-// Make sure we can grow the capacity of a pn_data_t all the way to the max and we stop there.
-static void test_grow(void)
-{
-  pn_data_t* data = pn_data(0);
-  while (pn_data_size(data) < PNI_NID_MAX) {
-    int code = pn_data_put_int(data, 1);
-    if (code) fprintf(stderr, "%d: %s", code, pn_error_text(pn_data_error(data)));
-    assert(code == 0);
-  }
-  assert(pn_data_size(data) == PNI_NID_MAX);
-  int code = pn_data_put_int(data, 1);
-  if (code != PN_OUT_OF_MEMORY)
-    fprintf(stderr, "expected PN_OUT_OF_MEMORY, got  %s\n", pn_code(code));
-  assert(code == PN_OUT_OF_MEMORY);
-  assert(pn_data_size(data) == PNI_NID_MAX);
-  pn_data_free(data);
-}
-
-static void test_multiple(test_t *t) {
-  pn_data_t *data = pn_data(1);
-  pn_data_t *src = pn_data(1);
-
-  /* NULL data pointer */
-  pn_data_fill(data, "M", NULL);
-  TEST_INSPECT(t, "null", data);
-
-  /* Empty data object */
-  pn_data_clear(data);
-  pn_data_fill(data, "M", src);
-  TEST_INSPECT(t, "null", data);
-
-  /* Empty array */
-  pn_data_clear(data);
-  pn_data_clear(src);
-  pn_data_put_array(src, false, PN_SYMBOL);
-  pn_data_fill(data, "M", src);
-  TEST_INSPECT(t, "null", data);
-
-  /* Single-element array */
-  pn_data_clear(data);
-  pn_data_clear(src);
-  pn_data_put_array(src, false, PN_SYMBOL);
-  pn_data_enter(src);
-  pn_data_put_symbol(src, PN_BYTES_LITERAL(foo));
-  pn_data_fill(data, "M", src);
-  TEST_INSPECT(t, ":foo", data);
-
-  /* Multi-element array */
-  pn_data_clear(data);
-  pn_data_clear(src);
-  pn_data_put_array(src, false, PN_SYMBOL);
-  pn_data_enter(src);
-  pn_data_put_symbol(src, PN_BYTES_LITERAL(foo));
-  pn_data_put_symbol(src, PN_BYTES_LITERAL(bar));
-  pn_data_fill(data, "M", src);
-  TEST_INSPECT(t, "@PN_SYMBOL[:foo, :bar]", data);
-
-  /* Non-array */
-  pn_data_clear(data);
-  pn_data_clear(src);
-  pn_data_put_symbol(src, PN_BYTES_LITERAL(baz));
-  pn_data_fill(data, "M", src);
-  TEST_INSPECT(t, ":baz", data);
-
-  pn_data_free(data);
-  pn_data_free(src);
-}
-
-int main(int argc, char **argv) {
-  int failed = 0;
-  test_grow();
-  RUN_ARGV_TEST(failed, t, test_multiple(&t));
-  return failed;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/data_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/data_test.cpp b/c/tests/data_test.cpp
new file mode 100644
index 0000000..73fcc1f
--- /dev/null
+++ b/c/tests/data_test.cpp
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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 "core/data.h"
+
+#include <proton/codec.h>
+#include <proton/error.h>
+
+using namespace pn_test;
+
+// Make sure we can grow the capacity of a pn_data_t all the way to the max and
+// we stop there.
+TEST_CASE("data_grow") {
+  auto_free<pn_data_t, pn_data_free> data(pn_data(0));
+  int code = 0;
+  while (pn_data_size(data) < PNI_NID_MAX && !code) {
+    code = pn_data_put_int(data, 1);
+  }
+  CHECK_THAT(*pn_data_error(data), error_empty());
+  CHECK(pn_data_size(data) == PNI_NID_MAX);
+  code = pn_data_put_int(data, 1);
+  INFO(pn_code(code));
+  CHECK(code == PN_OUT_OF_MEMORY);
+  CHECK(pn_data_size(data) == PNI_NID_MAX);
+}
+
+TEST_CASE("data_multiple") {
+  auto_free<pn_data_t, pn_data_free> data(pn_data(1));
+  auto_free<pn_data_t, pn_data_free> src(pn_data(1));
+
+  /* NULL data pointer */
+  pn_data_fill(data, "M", NULL);
+  CHECK("null" == inspect(data));
+
+  /* Empty data object */
+  pn_data_clear(data);
+  pn_data_fill(data, "M", src.get());
+  CHECK("null" == inspect(data));
+
+  /* Empty array */
+  pn_data_clear(data);
+  pn_data_clear(src);
+  pn_data_put_array(src, false, PN_SYMBOL);
+  pn_data_fill(data, "M", src.get());
+  CHECK("null" == inspect(data));
+
+  /* Single-element array */
+  pn_data_clear(data);
+  pn_data_clear(src);
+  pn_data_put_array(src, false, PN_SYMBOL);
+  pn_data_enter(src);
+  pn_data_put_symbol(src, pn_bytes("foo"));
+  pn_data_fill(data, "M", src.get());
+  CHECK(":foo" == inspect(data));
+
+  /* Multi-element array */
+  pn_data_clear(data);
+  pn_data_clear(src);
+  pn_data_put_array(src, false, PN_SYMBOL);
+  pn_data_enter(src);
+  pn_data_put_symbol(src, pn_bytes("foo"));
+  pn_data_put_symbol(src, pn_bytes("bar"));
+  pn_data_fill(data, "M", src.get());
+  CHECK("@PN_SYMBOL[:foo, :bar]" == inspect(data));
+
+  /* Non-array */
+  pn_data_clear(data);
+  pn_data_clear(src);
+  pn_data_put_symbol(src, pn_bytes("baz"));
+  pn_data_fill(data, "M", src.get());
+  CHECK(":baz" == inspect(data));
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/engine.c
----------------------------------------------------------------------
diff --git a/c/tests/engine.c b/c/tests/engine.c
deleted file mode 100644
index 41d17a0..0000000
--- a/c/tests/engine.c
+++ /dev/null
@@ -1,369 +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/engine.h>
-
-// never remove 'assert()'
-#undef NDEBUG
-#include <assert.h>
-
-// push data from one transport to another
-static int xfer(pn_transport_t *src, pn_transport_t *dest)
-{
-    ssize_t out = pn_transport_pending(src);
-    if (out > 0) {
-        ssize_t in = pn_transport_capacity(dest);
-        if (in > 0) {
-            size_t count = (size_t)((out < in) ? out : in);
-            pn_transport_push(dest,
-                              pn_transport_head(src),
-                              count);
-            pn_transport_pop(src, count);
-            return (int)count;
-        }
-    }
-    return 0;
-}
-
-// transfer all available data between two transports
-static int pump(pn_transport_t *t1, pn_transport_t *t2)
-{
-    int total = 0;
-    int work;
-    do {
-        work = xfer(t1, t2) + xfer(t2, t1);
-        total += work;
-    } while (work);
-    return total;
-}
-
-// handle state changes of the endpoints
-static void process_endpoints(pn_connection_t *conn)
-{
-    pn_session_t *ssn = pn_session_head(conn, PN_LOCAL_UNINIT);
-    while (ssn) {
-        //fprintf(stderr, "Opening session %p\n", (void*)ssn);
-        pn_session_open(ssn);
-        ssn = pn_session_next(ssn, PN_LOCAL_UNINIT);
-    }
-
-    pn_link_t *link = pn_link_head(conn, PN_LOCAL_UNINIT);
-    while (link) {
-        //fprintf(stderr, "Opening link %p\n", (void*)link);
-        pn_link_open(link);
-        link = pn_link_next(link, PN_LOCAL_UNINIT);
-    }
-
-    link = pn_link_head(conn, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
-    while (link) {
-        //fprintf(stderr, "Closing link %p\n", (void*)link);
-        pn_link_close(link);
-        link = pn_link_next(link, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
-    }
-
-    ssn = pn_session_head(conn, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
-    while (ssn) {
-        //fprintf(stderr, "Closing session %p\n", (void*)ssn);
-        pn_session_close(ssn);
-        ssn = pn_session_next(ssn, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
-    }
-}
-
-// bring up a session and a link between the two connections
-static void test_setup(pn_connection_t *c1, pn_transport_t *t1,
-                       pn_connection_t *c2, pn_transport_t *t2)
-{
-    pn_connection_open(c1);
-    pn_connection_open(c2);
-
-    pn_session_t *s1 = pn_session(c1);
-    pn_session_open(s1);
-
-    pn_link_t *tx = pn_sender(s1, "sender");
-    pn_link_open(tx);
-
-    while (pump(t1, t2)) {
-        process_endpoints(c1);
-        process_endpoints(c2);
-    }
-
-    // session and link should be up, c2 should have a receiver link:
-
-    assert(pn_session_state( s1 ) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(pn_link_state( tx ) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-
-    pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(rx && pn_link_is_receiver(rx));
-}
-
-// test that free'ing the connection should free all contained
-// resources (session, links, deliveries)
-int test_free_connection(int argc, char **argv)
-{
-    fprintf(stdout, "test_free_connection\n");
-    pn_connection_t *c1 = pn_connection();
-    pn_transport_t  *t1 = pn_transport();
-    pn_transport_bind(t1, c1);
-
-    pn_connection_t *c2 = pn_connection();
-    pn_transport_t  *t2 = pn_transport();
-    pn_transport_set_server(t2);
-    pn_transport_bind(t2, c2);
-
-    //pn_transport_trace(t1, PN_TRACE_FRM);
-    test_setup(c1, t1,
-               c2, t2);
-
-    pn_link_t *tx = pn_link_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(tx);
-    pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(rx);
-
-    // transfer some data across the link:
-    pn_link_flow(rx, 10);
-    pn_delivery_t *d1 = pn_delivery(tx, pn_dtag("tag-1", 6));
-    while (pump(t1, t2)) {
-        process_endpoints(c1);
-        process_endpoints(c2);
-    }
-    assert(pn_delivery_writable(d1));
-    pn_link_send(tx, "ABC", 4);
-    pn_link_advance(tx);
-
-    // now free the connection, but keep processing the transport
-    process_endpoints(c1);
-    pn_connection_free(c1);
-    while (pump(t1, t2)) {
-        process_endpoints(c2);
-    }
-
-    // delivery should have transfered:
-    assert(pn_link_current(rx) &&
-           pn_delivery_readable(pn_link_current(rx)));
-
-    pn_transport_unbind(t1);
-    pn_transport_free(t1);
-
-    pn_connection_free(c2);
-    pn_transport_unbind(t2);
-    pn_transport_free(t2);
-
-    return 0;
-}
-
-int test_free_session(int argc, char **argv)
-{
-    fprintf(stdout, "test_free_session\n");
-    pn_connection_t *c1 = pn_connection();
-    pn_transport_t  *t1 = pn_transport();
-    pn_transport_bind(t1, c1);
-
-    pn_connection_t *c2 = pn_connection();
-    pn_transport_t  *t2 = pn_transport();
-    pn_transport_set_server(t2);
-    pn_transport_bind(t2, c2);
-
-    //pn_transport_trace(t1, PN_TRACE_FRM);
-    test_setup(c1, t1,
-               c2, t2);
-
-    pn_session_t *ssn = pn_session_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(ssn);
-    pn_link_t *tx = pn_link_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(tx);
-    pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(rx);
-
-    // prepare for transfer: request some credit
-    pn_link_flow(rx, 10);
-    pn_delivery_t *d1 = pn_delivery(tx, pn_dtag("tag-1", 6));
-    while (pump(t1, t2)) {
-        process_endpoints(c1);
-        process_endpoints(c2);
-    }
-    assert(pn_delivery_writable(d1));
-
-    // send some data, but also close the session:
-    pn_link_send(tx, "ABC", 4);
-    pn_link_advance(tx);
-
-    pn_session_close(ssn);
-    pn_session_free(ssn);
-
-    while (pump(t1, t2)) {
-        process_endpoints(c1);
-        process_endpoints(c2);
-    }
-
-    // delivery should have transfered:
-    assert(pn_link_current(rx));
-    assert(pn_delivery_readable(pn_link_current(rx)));
-
-    // c2's session should see the close:
-    pn_session_t *ssn2 = pn_session_head(c2, 0);
-    assert(ssn2 && pn_session_state(ssn2) == (PN_LOCAL_CLOSED | PN_REMOTE_CLOSED));
-
-    pn_transport_unbind(t1);
-    pn_transport_free(t1);
-    pn_connection_free(c1);
-
-    pn_transport_unbind(t2);
-    pn_transport_free(t2);
-    pn_connection_free(c2);
-
-    return 0;
-}
-
-int test_free_link(int argc, char **argv)
-{
-    fprintf(stdout, "test_free_link\n");
-    pn_connection_t *c1 = pn_connection();
-    pn_transport_t  *t1 = pn_transport();
-    pn_transport_bind(t1, c1);
-
-    pn_connection_t *c2 = pn_connection();
-    pn_transport_t  *t2 = pn_transport();
-    pn_transport_set_server(t2);
-    pn_transport_bind(t2, c2);
-
-    //pn_transport_trace(t1, PN_TRACE_FRM);
-    test_setup(c1, t1,
-               c2, t2);
-
-    pn_link_t *tx = pn_link_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(tx);
-    pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(rx);
-
-    // prepare for transfer: request some credit
-    pn_link_flow(rx, 10);
-    pn_delivery_t *d1 = pn_delivery(tx, pn_dtag("tag-1", 6));
-    while (pump(t1, t2)) {
-        process_endpoints(c1);
-        process_endpoints(c2);
-    }
-    assert(pn_delivery_writable(d1));
-
-    // send some data, then close and destroy the link:
-    pn_link_send(tx, "ABC", 4);
-    pn_link_advance(tx);
-
-    pn_link_close(tx);
-    pn_link_free(tx);
-
-    while (pump(t1, t2)) {
-        process_endpoints(c1);
-        process_endpoints(c2);
-    }
-
-    // the data transfer will complete and the link close
-    // should have been sent to the peer
-    assert(pn_link_current(rx));
-    assert(pn_link_state(rx) == (PN_LOCAL_CLOSED | PN_REMOTE_CLOSED));
-
-    pn_transport_unbind(t1);
-    pn_transport_free(t1);
-    pn_connection_free(c1);
-
-    pn_transport_unbind(t2);
-    pn_transport_free(t2);
-    pn_connection_free(c2);
-
-    return 0;
-}
-
-// regression test fo PROTON-1466 - confusion between links with prefix names
-static int test_link_name_prefix(int argc, char **argv)
-{
-    fprintf(stdout, "test_link_name_prefix\n");
-    pn_connection_t *c1 = pn_connection();
-    pn_transport_t  *t1 = pn_transport();
-    pn_transport_bind(t1, c1);
-
-    pn_connection_t *c2 = pn_connection();
-    pn_transport_t  *t2 = pn_transport();
-    pn_transport_set_server(t2);
-    pn_transport_bind(t2, c2);
-
-    pn_connection_open(c1);
-    pn_connection_open(c2);
-
-    pn_session_t *s1 = pn_session(c1);
-    pn_session_open(s1);
-
-    pn_link_t *l = pn_receiver(s1, "l");
-    pn_link_open(l);
-    pn_link_t *lll = pn_receiver(s1, "lll");
-    pn_link_open(lll);
-    pn_link_t *ll = pn_receiver(s1, "ll");
-    pn_link_open(ll);
-
-    while (pump(t1, t2)) {
-        process_endpoints(c1);
-        process_endpoints(c2);
-    }
-
-    // session and link should be up, c2 should have a receiver link:
-    assert(pn_session_state( s1 ) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(pn_link_state( l ) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(pn_link_state( lll ) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(pn_link_state( ll ) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-
-    pn_link_t *r = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(!strcmp(pn_link_name(r), "l"));
-    r = pn_link_next(r, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(!strcmp(pn_link_name(r), "lll"));
-    r = pn_link_next(r, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
-    assert(!strcmp(pn_link_name(r), "ll"));
-
-    pn_transport_unbind(t1);
-    pn_transport_free(t1);
-    pn_connection_free(c1);
-
-    pn_transport_unbind(t2);
-    pn_transport_free(t2);
-    pn_connection_free(c2);
-
-    return 0;
-}
-
-typedef int (*test_ptr_t)(int argc, char **argv);
-
-test_ptr_t tests[] = {test_free_connection,
-                      test_free_session,
-                      test_free_link,
-                      test_link_name_prefix,
-                      NULL};
-
-int main(int argc, char **argv)
-{
-    test_ptr_t *test = tests;
-    while (*test) {
-        int rc = (*test++)(argc, argv);
-        if (rc)
-            return rc;
-    }
-    return 0;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/engine_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/engine_test.cpp b/c/tests/engine_test.cpp
new file mode 100644
index 0000000..fa6e23f
--- /dev/null
+++ b/c/tests/engine_test.cpp
@@ -0,0 +1,318 @@
+/*
+ *
+ * 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/engine.h>
+
+// push data from one transport to another
+static int xfer(pn_transport_t *src, pn_transport_t *dest) {
+  ssize_t out = pn_transport_pending(src);
+  if (out > 0) {
+    ssize_t in = pn_transport_capacity(dest);
+    if (in > 0) {
+      size_t count = (size_t)((out < in) ? out : in);
+      pn_transport_push(dest, pn_transport_head(src), count);
+      pn_transport_pop(src, count);
+      return (int)count;
+    }
+  }
+  return 0;
+}
+
+// transfer all available data between two transports
+static int pump(pn_transport_t *t1, pn_transport_t *t2) {
+  int total = 0;
+  int work;
+  do {
+    work = xfer(t1, t2) + xfer(t2, t1);
+    total += work;
+  } while (work);
+  return total;
+}
+
+// handle state changes of the endpoints
+static void process_endpoints(pn_connection_t *conn) {
+  pn_session_t *ssn = pn_session_head(conn, PN_LOCAL_UNINIT);
+  while (ssn) {
+    // fprintf(stderr, "Opening session %p\n", (void*)ssn);
+    pn_session_open(ssn);
+    ssn = pn_session_next(ssn, PN_LOCAL_UNINIT);
+  }
+
+  pn_link_t *link = pn_link_head(conn, PN_LOCAL_UNINIT);
+  while (link) {
+    pn_link_open(link);
+    link = pn_link_next(link, PN_LOCAL_UNINIT);
+  }
+
+  link = pn_link_head(conn, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
+  while (link) {
+    pn_link_close(link);
+    link = pn_link_next(link, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
+  }
+
+  ssn = pn_session_head(conn, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
+  while (ssn) {
+    pn_session_close(ssn);
+    ssn = pn_session_next(ssn, PN_LOCAL_ACTIVE | PN_REMOTE_CLOSED);
+  }
+}
+
+// bring up a session and a link between the two connections
+static void test_setup(pn_connection_t *c1, pn_transport_t *t1,
+                       pn_connection_t *c2, pn_transport_t *t2) {
+  pn_connection_open(c1);
+  pn_connection_open(c2);
+
+  pn_session_t *s1 = pn_session(c1);
+  pn_session_open(s1);
+
+  pn_link_t *tx = pn_sender(s1, "sender");
+  pn_link_open(tx);
+
+  while (pump(t1, t2)) {
+    process_endpoints(c1);
+    process_endpoints(c2);
+  }
+
+  // session and link should be up, c2 should have a receiver link:
+
+  REQUIRE(pn_session_state(s1) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(pn_link_state(tx) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+
+  pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(rx);
+  REQUIRE(pn_link_is_receiver(rx));
+}
+
+// test that free'ing the connection should free all contained
+// resources (session, links, deliveries)
+TEST_CASE("engine_free_connection") {
+  pn_connection_t *c1 = pn_connection();
+  pn_transport_t *t1 = pn_transport();
+  pn_transport_bind(t1, c1);
+
+  pn_connection_t *c2 = pn_connection();
+  pn_transport_t *t2 = pn_transport();
+  pn_transport_set_server(t2);
+  pn_transport_bind(t2, c2);
+
+  // pn_transport_trace(t1, PN_TRACE_FRM);
+  test_setup(c1, t1, c2, t2);
+
+  pn_link_t *tx = pn_link_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(tx);
+  pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(rx);
+
+  // transfer some data across the link:
+  pn_link_flow(rx, 10);
+  pn_delivery_t *d1 = pn_delivery(tx, pn_dtag("tag-1", 6));
+  while (pump(t1, t2)) {
+    process_endpoints(c1);
+    process_endpoints(c2);
+  }
+  REQUIRE(pn_delivery_writable(d1));
+  pn_link_send(tx, "ABC", 4);
+  pn_link_advance(tx);
+
+  // now free the connection, but keep processing the transport
+  process_endpoints(c1);
+  pn_connection_free(c1);
+  while (pump(t1, t2)) {
+    process_endpoints(c2);
+  }
+
+  // delivery should have transfered:
+  REQUIRE(pn_link_current(rx));
+  REQUIRE(pn_delivery_readable(pn_link_current(rx)));
+
+  pn_transport_unbind(t1);
+  pn_transport_free(t1);
+
+  pn_connection_free(c2);
+  pn_transport_unbind(t2);
+  pn_transport_free(t2);
+}
+
+TEST_CASE("engine_free_session") {
+  pn_connection_t *c1 = pn_connection();
+  pn_transport_t *t1 = pn_transport();
+  pn_transport_bind(t1, c1);
+
+  pn_connection_t *c2 = pn_connection();
+  pn_transport_t *t2 = pn_transport();
+  pn_transport_set_server(t2);
+  pn_transport_bind(t2, c2);
+
+  // pn_transport_trace(t1, PN_TRACE_FRM);
+  test_setup(c1, t1, c2, t2);
+
+  pn_session_t *ssn = pn_session_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(ssn);
+  pn_link_t *tx = pn_link_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(tx);
+  pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(rx);
+
+  // prepare for transfer: request some credit
+  pn_link_flow(rx, 10);
+  pn_delivery_t *d1 = pn_delivery(tx, pn_dtag("tag-1", 6));
+  while (pump(t1, t2)) {
+    process_endpoints(c1);
+    process_endpoints(c2);
+  }
+  REQUIRE(pn_delivery_writable(d1));
+
+  // send some data, but also close the session:
+  pn_link_send(tx, "ABC", 4);
+  pn_link_advance(tx);
+
+  pn_session_close(ssn);
+  pn_session_free(ssn);
+
+  while (pump(t1, t2)) {
+    process_endpoints(c1);
+    process_endpoints(c2);
+  }
+
+  // delivery should have transfered:
+  REQUIRE(pn_link_current(rx));
+  REQUIRE(pn_delivery_readable(pn_link_current(rx)));
+
+  // c2's session should see the close:
+  pn_session_t *ssn2 = pn_session_head(c2, 0);
+  REQUIRE(ssn2);
+  REQUIRE(pn_session_state(ssn2) == (PN_LOCAL_CLOSED | PN_REMOTE_CLOSED));
+
+  pn_transport_unbind(t1);
+  pn_transport_free(t1);
+  pn_connection_free(c1);
+
+  pn_transport_unbind(t2);
+  pn_transport_free(t2);
+  pn_connection_free(c2);
+}
+
+TEST_CASE("engine_free_link)") {
+  pn_connection_t *c1 = pn_connection();
+  pn_transport_t *t1 = pn_transport();
+  pn_transport_bind(t1, c1);
+
+  pn_connection_t *c2 = pn_connection();
+  pn_transport_t *t2 = pn_transport();
+  pn_transport_set_server(t2);
+  pn_transport_bind(t2, c2);
+
+  // pn_transport_trace(t1, PN_TRACE_FRM);
+  test_setup(c1, t1, c2, t2);
+
+  pn_link_t *tx = pn_link_head(c1, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(tx);
+  pn_link_t *rx = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(rx);
+
+  // prepare for transfer: request some credit
+  pn_link_flow(rx, 10);
+  pn_delivery_t *d1 = pn_delivery(tx, pn_dtag("tag-1", 6));
+  while (pump(t1, t2)) {
+    process_endpoints(c1);
+    process_endpoints(c2);
+  }
+  REQUIRE(pn_delivery_writable(d1));
+
+  // send some data, then close and destroy the link:
+  pn_link_send(tx, "ABC", 4);
+  pn_link_advance(tx);
+
+  pn_link_close(tx);
+  pn_link_free(tx);
+
+  while (pump(t1, t2)) {
+    process_endpoints(c1);
+    process_endpoints(c2);
+  }
+
+  // the data transfer will complete and the link close
+  // should have been sent to the peer
+  REQUIRE(pn_link_current(rx));
+  REQUIRE(pn_link_state(rx) == (PN_LOCAL_CLOSED | PN_REMOTE_CLOSED));
+
+  pn_transport_unbind(t1);
+  pn_transport_free(t1);
+  pn_connection_free(c1);
+
+  pn_transport_unbind(t2);
+  pn_transport_free(t2);
+  pn_connection_free(c2);
+}
+
+// regression test fo PROTON-1466 - confusion between links with prefix names
+TEST_CASE("engine_link_name_prefix)") {
+  pn_connection_t *c1 = pn_connection();
+  pn_transport_t *t1 = pn_transport();
+  pn_transport_bind(t1, c1);
+
+  pn_connection_t *c2 = pn_connection();
+  pn_transport_t *t2 = pn_transport();
+  pn_transport_set_server(t2);
+  pn_transport_bind(t2, c2);
+
+  pn_connection_open(c1);
+  pn_connection_open(c2);
+
+  pn_session_t *s1 = pn_session(c1);
+  pn_session_open(s1);
+
+  pn_link_t *l = pn_receiver(s1, "l");
+  pn_link_open(l);
+  pn_link_t *lll = pn_receiver(s1, "lll");
+  pn_link_open(lll);
+  pn_link_t *ll = pn_receiver(s1, "ll");
+  pn_link_open(ll);
+
+  while (pump(t1, t2)) {
+    process_endpoints(c1);
+    process_endpoints(c2);
+  }
+
+  // session and link should be up, c2 should have a receiver link:
+  REQUIRE(pn_session_state(s1) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(pn_link_state(l) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(pn_link_state(lll) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(pn_link_state(ll) == (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+
+  pn_link_t *r = pn_link_head(c2, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(std::string("l") == pn_link_name(r));
+  r = pn_link_next(r, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(std::string("lll") == pn_link_name(r));
+  r = pn_link_next(r, (PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE));
+  REQUIRE(std::string("ll") == pn_link_name(r));
+
+  pn_transport_unbind(t1);
+  pn_transport_free(t1);
+  pn_connection_free(c1);
+
+  pn_transport_unbind(t2);
+  pn_transport_free(t2);
+  pn_connection_free(c2);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/event.c
----------------------------------------------------------------------
diff --git a/c/tests/event.c b/c/tests/event.c
deleted file mode 100644
index 6746c3b..0000000
--- a/c/tests/event.c
+++ /dev/null
@@ -1,109 +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 <proton/object.h>
-#include <proton/event.h>
-#include <stdlib.h>
-
-#define assert(E) ((E) ? 0 : (abort(), 0))
-
-static void test_collector(void) {
-  pn_collector_t *collector = pn_collector();
-  assert(collector);
-  pn_free(collector);
-}
-
-#define SETUP_COLLECTOR \
-  void *obj = pn_class_new(PN_OBJECT, 0); \
-  pn_collector_t *collector = pn_collector(); \
-  assert(collector); \
-  pn_event_t *event = pn_collector_put(collector, PN_OBJECT, obj, (pn_event_type_t) 0); \
-  pn_decref(obj); \
-
-static void test_collector_put(void) {
-  SETUP_COLLECTOR;
-  assert(event);
-  assert(pn_event_context(event) == obj);
-  pn_free(collector);
-}
-
-static void test_collector_peek(void) {
-  SETUP_COLLECTOR;
-  pn_event_t *head = pn_collector_peek(collector);
-  assert(head == event);
-  pn_free(collector);
-}
-
-static void test_collector_pop(void) {
-  SETUP_COLLECTOR;
-  pn_event_t *head = pn_collector_peek(collector);
-  assert(head == event);
-  pn_collector_pop(collector);
-  head = pn_collector_peek(collector);
-  assert(!head);
-  pn_free(collector);
-}
-
-static void test_collector_pool(void) {
-  SETUP_COLLECTOR;
-  pn_event_t *head = pn_collector_peek(collector);
-  assert(head == event);
-  pn_collector_pop(collector);
-  head = pn_collector_peek(collector);
-  assert(!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);
-  assert(event == event2);
-  pn_free(collector);
-}
-
-static void test_event_incref(bool eventfirst) {
-  SETUP_COLLECTOR;
-  pn_event_t *head = pn_collector_peek(collector);
-  assert(head == event);
-  pn_incref(head);
-  pn_collector_pop(collector);
-  assert(!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);
-  assert(head != event2);
-  if (eventfirst) {
-    pn_decref(head);
-    pn_free(collector);
-  } else {
-    pn_free(collector);
-    pn_decref(head);
-  }
-}
-
-int main(int argc, char **argv)
-{
-  test_collector();
-  test_collector_put();
-  test_collector_peek();
-  test_collector_pop();
-  test_collector_pool();
-  test_event_incref(true);
-  test_event_incref(false);
-  return 0;
-}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[5/6] qpid-proton git commit: PROTON-1887: [c] Convert C tests to use Catch2 harness.

Posted by ac...@apache.org.
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%2F%3A@example.net/some_topic",
-              "amqps", "@user/:", "@pass/:", "example.net", 0, "some_topic"));
-  TEST(testrt("amqps://user%2F%3A=:pass%2F%3A=@example.net/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: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[3/6] qpid-proton git commit: PROTON-1887: [c] Convert C tests to use Catch2 harness.

Posted by ac...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/reactor.c
----------------------------------------------------------------------
diff --git a/c/tests/reactor.c b/c/tests/reactor.c
deleted file mode 100644
index 900d6a3..0000000
--- a/c/tests/reactor.c
+++ /dev/null
@@ -1,578 +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 <proton/reactor.h>
-#include <proton/handlers.h>
-#include <proton/event.h>
-#include <proton/connection.h>
-#include <proton/session.h>
-#include <proton/link.h>
-#include <proton/delivery.h>
-#include <proton/url.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define assert(E) ((E) ? 0 : (abort(), 0))
-
-
-static void test_reactor(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_free(reactor);
-}
-
-static void test_reactor_free(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_reactor_free(reactor);
-}
-
-static void test_reactor_run(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  // run should exit if there is nothing left to do
-  pn_reactor_run(reactor);
-  pn_free(reactor);
-}
-
-static void test_reactor_run_free(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  // run should exit if there is nothing left to do
-  pn_reactor_run(reactor);
-  pn_reactor_free(reactor);
-}
-
-typedef struct {
-  pn_reactor_t *reactor;
-  pn_list_t *events;
-} pni_test_handler_t;
-
-pni_test_handler_t *thmem(pn_handler_t *handler) {
-  return (pni_test_handler_t *) pn_handler_mem(handler);
-}
-
-void test_dispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-  pni_test_handler_t *th = thmem(handler);
-  pn_reactor_t *reactor = pn_event_reactor(event);
-  assert(reactor == th->reactor);
-  pn_list_add(th->events, (void *) type);
-}
-
-pn_handler_t *test_handler(pn_reactor_t *reactor, pn_list_t *events) {
-  pn_handler_t *handler = pn_handler_new(test_dispatch, sizeof(pni_test_handler_t), NULL);
-  thmem(handler)->reactor = reactor;
-  thmem(handler)->events = events;
-  return handler;
-}
-
-
-void root_dispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-  pni_test_handler_t *th = thmem(handler);
-  pn_reactor_t *reactor = pn_event_reactor(event);
-  assert(reactor == th->reactor);
-  pn_list_add(th->events, pn_event_root(event));
-}
-
-pn_handler_t *test_root(pn_reactor_t *reactor, pn_list_t *events) {
-  pn_handler_t *handler = pn_handler_new(root_dispatch, sizeof(pni_test_handler_t), NULL);
-  thmem(handler)->reactor = reactor;
-  thmem(handler)->events = events;
-  return handler;
-}
-
-#define END PN_EVENT_NONE
-
-void expect(pn_list_t *events, ...) {
-  va_list ap;
-
-  va_start(ap, events);
-  size_t idx = 0;
-  while (true) {
-    pn_event_type_t expected = (pn_event_type_t) va_arg(ap, int);
-    if (expected == END) {
-      assert(idx == pn_list_size(events));
-      break;
-    }
-    assert(idx < pn_list_size(events));
-    pn_event_type_t actual = (pn_event_type_t)(size_t) pn_list_get(events, idx++);
-    assert(expected == actual);
-  }
-  va_end(ap);
-}
-
-static void test_reactor_handler(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_handler_t *handler = pn_reactor_get_handler(reactor);
-  assert(handler);
-  pn_list_t *events = pn_list(PN_VOID, 0);
-  pn_handler_t *th = test_handler(reactor, events);
-  pn_handler_add(handler, th);
-  pn_decref(th);
-  pn_free(reactor);
-  expect(events, END);
-  pn_free(events);
-}
-
-static void test_reactor_handler_free(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_handler_t *handler = pn_reactor_get_handler(reactor);
-  assert(handler);
-  pn_list_t *events = pn_list(PN_VOID, 0);
-  pn_handler_add(handler, test_handler(reactor, events));
-  pn_reactor_free(reactor);
-  expect(events, END);
-  pn_free(events);
-}
-
-static void test_reactor_handler_run(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_handler_t *handler = pn_reactor_get_handler(reactor);
-  assert(handler);
-  pn_list_t *events = pn_list(PN_VOID, 0);
-  pn_handler_t *th = test_handler(reactor, events);
-  pn_handler_add(handler, th);
-  pn_reactor_run(reactor);
-  expect(events, PN_REACTOR_INIT, PN_SELECTABLE_INIT, PN_SELECTABLE_UPDATED, PN_SELECTABLE_FINAL, PN_REACTOR_FINAL, END);
-  pn_free(reactor);
-  pn_free(th);
-  pn_free(events);
-}
-
-static void test_reactor_handler_run_free(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_handler_t *handler = pn_reactor_get_handler(reactor);
-  assert(handler);
-  pn_list_t *events = pn_list(PN_VOID, 0);
-  pn_handler_add(handler, test_handler(reactor, events));
-  pn_reactor_run(reactor);
-  expect(events, PN_REACTOR_INIT, PN_SELECTABLE_INIT, PN_SELECTABLE_UPDATED, PN_SELECTABLE_FINAL, PN_REACTOR_FINAL, END);
-  pn_reactor_free(reactor);
-  pn_free(events);
-}
-
-static void test_reactor_event_root(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_handler_t *handler = pn_reactor_get_handler(reactor);
-  assert(handler);
-  pn_list_t *roots = pn_list(PN_VOID, 0);
-  pn_handler_t *th = test_root(reactor, roots);
-  pn_handler_add(handler, th);
-  pn_reactor_run(reactor);
-  expect(roots, handler, handler, handler, handler, handler, END);
-  pn_free(reactor);
-  pn_free(th);
-  pn_free(roots);
-}
-
-static void test_reactor_connection(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_list_t *cevents = pn_list(PN_VOID, 0);
-  pn_handler_t *tch = test_handler(reactor, cevents);
-  pn_connection_t *connection = pn_reactor_connection(reactor, tch);
-  assert(connection);
-  pn_reactor_set_connection_host(reactor, connection, "127.0.0.1", "5672");
-  pn_url_t *url = pn_url_parse(pn_reactor_get_connection_address(reactor, connection));
-  assert(strcmp(pn_url_get_host(url), "127.0.0.1") == 0);
-  assert(strcmp(pn_url_get_port(url), "5672") == 0);
-  pn_decref(url);
-  pn_handler_t *root = pn_reactor_get_handler(reactor);
-  pn_list_t *revents = pn_list(PN_VOID, 0);
-  pn_handler_add(root, test_handler(reactor, revents));
-  pn_reactor_run(reactor);
-  expect(revents, PN_REACTOR_INIT, PN_SELECTABLE_INIT, PN_SELECTABLE_UPDATED, PN_SELECTABLE_FINAL, PN_REACTOR_FINAL,
-         END);
-  expect(cevents, PN_CONNECTION_INIT, END);
-  pn_reactor_free(reactor);
-  pn_handler_free(tch);
-  pn_free(cevents);
-  pn_free(revents);
-}
-
-static void test_reactor_connection_factory(void)
-{
-  pn_reactor_t *reactor = pn_reactor();
-  pn_connection_t *conn;
-  const char *addr;
-  // use host as connection hostname default
-  conn = pn_reactor_connection_to_host(reactor, "a.test.com", "5678", NULL);
-  pn_connection_set_hostname(conn, "virt.host");
-  addr = pn_reactor_get_connection_address(reactor, conn);
-  assert(addr && strcmp(addr, "a.test.com:5678") == 0);
-  assert(strcmp(pn_connection_get_hostname(conn), "virt.host") == 0);
-  // verify the host address can be changed:
-  pn_reactor_set_connection_host(reactor, conn, "a.different.com", "9999");
-  addr = pn_reactor_get_connection_address(reactor, conn);
-  assert(addr && strcmp(addr, "a.different.com:9999") == 0);
-  assert(strcmp(pn_connection_get_hostname(conn), "virt.host") == 0);
-  pn_reactor_free(reactor);
-}
-
-static void test_reactor_acceptor(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_acceptor_t *acceptor = pn_reactor_acceptor(reactor, "0.0.0.0", "5678", NULL);
-  assert(acceptor);
-  pn_reactor_free(reactor);
-}
-
-pn_acceptor_t **tram(pn_handler_t *h) {
-  return (pn_acceptor_t **) pn_handler_mem(h);
-}
-
-static void tra_dispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-  switch (type) {
-  case PN_REACTOR_INIT:
-    {
-      pn_acceptor_t *acceptor = *tram(handler);
-      pn_acceptor_close(acceptor);
-    }
-    break;
-  default:
-    break;
-  }
-}
-
-static pn_handler_t *tra_handler(pn_acceptor_t *acceptor) {
-  pn_handler_t *handler = pn_handler_new(tra_dispatch, sizeof(pn_acceptor_t *), NULL);
-  *tram(handler) = acceptor;
-  return handler;
-}
-
-static void test_reactor_acceptor_run(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_handler_t *root = pn_reactor_get_handler(reactor);
-  assert(root);
-  pn_acceptor_t *acceptor = pn_reactor_acceptor(reactor, "0.0.0.0", "5678", NULL);
-  assert(acceptor);
-  pn_handler_add(root, tra_handler(acceptor));
-  pn_reactor_run(reactor);
-  pn_reactor_free(reactor);
-}
-
-typedef struct {
-  pn_reactor_t *reactor;
-  pn_acceptor_t *acceptor;
-  pn_list_t *events;
-} server_t;
-
-static server_t *smem(pn_handler_t *handler) {
-  return (server_t *) pn_handler_mem(handler);
-}
-
-static void server_dispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-  server_t *srv = smem(handler);
-  pn_list_add(srv->events, (void *) pn_event_type(event));
-  switch (type) {
-  case PN_CONNECTION_REMOTE_OPEN:
-    pn_connection_open(pn_event_connection(event));
-    break;
-  case PN_CONNECTION_REMOTE_CLOSE:
-    pn_acceptor_close(srv->acceptor);
-    pn_connection_close(pn_event_connection(event));
-    pn_connection_release(pn_event_connection(event));
-    break;
-  default:
-    break;
-  }
-}
-
-typedef struct {
-  pn_list_t *events;
-} client_t;
-
-static client_t *cmem(pn_handler_t *handler) {
-  return (client_t *) pn_handler_mem(handler);
-}
-
-static void client_dispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-  client_t *cli = cmem(handler);
-  pn_list_add(cli->events, (void *) type);
-  pn_connection_t *conn = pn_event_connection(event);
-  switch (pn_event_type(event)) {
-  case PN_CONNECTION_INIT:
-    pn_connection_set_hostname(conn, "some.org");
-    pn_connection_open(conn);
-    break;
-  case PN_CONNECTION_REMOTE_OPEN:
-    pn_connection_close(conn);
-    break;
-  case PN_CONNECTION_REMOTE_CLOSE:
-    pn_connection_release(conn);
-    break;
-  default:
-    break;
-  }
-}
-
-static void test_reactor_connect(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  pn_handler_t *sh = pn_handler_new(server_dispatch, sizeof(server_t), NULL);
-  server_t *srv = smem(sh);
-  pn_acceptor_t *acceptor = pn_reactor_acceptor(reactor, "0.0.0.0", "5678", sh);
-  srv->reactor = reactor;
-  srv->acceptor = acceptor;
-  srv->events = pn_list(PN_VOID, 0);
-  pn_handler_t *ch = pn_handler_new(client_dispatch, sizeof(client_t), NULL);
-  client_t *cli = cmem(ch);
-  cli->events = pn_list(PN_VOID, 0);
-  pn_connection_t *conn = pn_reactor_connection_to_host(reactor,
-                                                        "127.0.0.1",
-                                                        "5678",
-                                                        ch);
-  assert(conn);
-  pn_url_t *url = pn_url_parse(pn_reactor_get_connection_address(reactor, conn));
-  assert(strcmp(pn_url_get_host(url), "127.0.0.1") == 0);
-  assert(strcmp(pn_url_get_port(url), "5678") == 0);
-  pn_decref(url);
-  pn_reactor_run(reactor);
-  expect(srv->events, PN_CONNECTION_INIT, PN_CONNECTION_BOUND,
-         PN_CONNECTION_REMOTE_OPEN,
-         PN_CONNECTION_LOCAL_OPEN, PN_TRANSPORT,
-         PN_CONNECTION_REMOTE_CLOSE, PN_TRANSPORT_TAIL_CLOSED,
-         PN_CONNECTION_LOCAL_CLOSE, PN_TRANSPORT,
-         PN_TRANSPORT_HEAD_CLOSED, PN_TRANSPORT_CLOSED,
-         PN_CONNECTION_UNBOUND, PN_CONNECTION_FINAL, END);
-  pn_free(srv->events);
-  pn_decref(sh);
-  expect(cli->events, PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN,
-         PN_CONNECTION_BOUND,
-         PN_CONNECTION_REMOTE_OPEN, PN_CONNECTION_LOCAL_CLOSE,
-         PN_TRANSPORT, PN_TRANSPORT_HEAD_CLOSED,
-         PN_CONNECTION_REMOTE_CLOSE, PN_TRANSPORT_TAIL_CLOSED,
-         PN_TRANSPORT_CLOSED, PN_CONNECTION_UNBOUND,
-         PN_CONNECTION_FINAL, END);
-  pn_free(cli->events);
-  pn_decref(ch);
-  pn_reactor_free(reactor);
-}
-
-static void test_reactor_bad_domain(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  assert(reactor);
-  pn_handler_t *ch = pn_handler_new(client_dispatch, sizeof(client_t), NULL);
-  client_t *cli = cmem(ch);
-  cli->events = pn_list(PN_VOID, 0);
-  pn_connection_t *connection = pn_reactor_connection_to_host(reactor, "somebogusdomain", "5672", ch);
-  assert(connection);
-  pn_reactor_run(reactor);
-
-  expect(cli->events, PN_CONNECTION_INIT, PN_CONNECTION_LOCAL_OPEN,
-         PN_CONNECTION_BOUND, PN_TRANSPORT_TAIL_CLOSED,
-         PN_TRANSPORT_ERROR, PN_TRANSPORT_HEAD_CLOSED,
-         PN_TRANSPORT_CLOSED, PN_CONNECTION_UNBOUND,
-         END);
-
-  pn_free(cli->events);
-  pn_decref(ch);
-  pn_reactor_free(reactor);
-}
-
-typedef struct {
-  int received;
-} sink_t;
-
-static sink_t *sink(pn_handler_t *handler) {
-  return (sink_t *) pn_handler_mem(handler);
-}
-
-void sink_dispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-  sink_t *snk = sink(handler);
-  pn_delivery_t *dlv = pn_event_delivery(event);
-  switch (type) {
-  case PN_DELIVERY:
-    if (!pn_delivery_partial(dlv)) {
-      pn_delivery_settle(dlv);
-      snk->received++;
-    }
-    break;
-  default:
-    break;
-  }
-}
-
-typedef struct {
-  int remaining;
-} source_t;
-
-static source_t *source(pn_handler_t *handler) {
-  return (source_t *) pn_handler_mem(handler);
-}
-
-
-void source_dispatch(pn_handler_t *handler, pn_event_t *event, pn_event_type_t type) {
-  source_t *src = source(handler);
-  pn_connection_t *conn = pn_event_connection(event);
-  switch (type) {
-  case PN_CONNECTION_INIT:
-    {
-      pn_session_t *ssn = pn_session(conn);
-      pn_link_t *snd = pn_sender(ssn, "sender");
-      pn_connection_open(conn);
-      pn_session_open(ssn);
-      pn_link_open(snd);
-    }
-    break;
-  case PN_LINK_FLOW:
-    {
-      pn_link_t *link = pn_event_link(event);
-      while (pn_link_credit(link) > 0 && src->remaining > 0) {
-        pn_delivery_t *dlv = pn_delivery(link, pn_dtag("", 0));
-        assert(dlv);
-        pn_delivery_settle(dlv);
-        src->remaining--;
-      }
-
-      if (!src->remaining) {
-        pn_connection_close(conn);
-      }
-    }
-    break;
-  case PN_CONNECTION_REMOTE_CLOSE:
-    pn_connection_release(conn);
-    break;
-  default:
-    break;
-  }
-}
-
-static void test_reactor_transfer(int count, int window) {
-  pn_reactor_t *reactor = pn_reactor();
-
-  pn_handler_t *sh = pn_handler_new(server_dispatch, sizeof(server_t), NULL);
-  server_t *srv = smem(sh);
-  pn_acceptor_t *acceptor = pn_reactor_acceptor(reactor, "0.0.0.0", "5678", sh);
-  srv->reactor = reactor;
-  srv->acceptor = acceptor;
-  srv->events = pn_list(PN_VOID, 0);
-  pn_handler_add(sh, pn_handshaker());
-  // XXX: a window of 1 doesn't work unless the flowcontroller is
-  // added after the thing that settles the delivery
-  pn_handler_add(sh, pn_flowcontroller(window));
-  pn_handler_t *snk = pn_handler_new(sink_dispatch, sizeof(sink_t), NULL);
-  sink(snk)->received = 0;
-  pn_handler_add(sh, snk);
-
-  pn_handler_t *ch = pn_handler_new(source_dispatch, sizeof(source_t), NULL);
-  source_t *src = source(ch);
-  src->remaining = count;
-  pn_connection_t *conn = NULL;
-  // Using the connection's hostname to set the connection address is
-  // deprecated. Once support is dropped the conditional code can be removed:
-  #if 0
-  conn = pn_reactor_connection(reactor, ch);
-  assert(conn);
-  pn_reactor_connection_set_address(reactor, conn, "127.0.0.1", "5678");
-  #else
-  // This is deprecated:
-  conn = pn_reactor_connection(reactor, ch);
-  pn_connection_set_hostname(conn, "127.0.0.1:5678");
-  #endif
-
-  pn_reactor_run(reactor);
-
-  assert(sink(snk)->received == count);
-
-  pn_free(srv->events);
-  pn_reactor_free(reactor);
-  pn_handler_free(sh);
-  pn_handler_free(ch);
-}
-
-static void test_reactor_schedule(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  pn_handler_t *root = pn_reactor_get_handler(reactor);
-  pn_list_t *events = pn_list(PN_VOID, 0);
-  pn_handler_add(root, test_handler(reactor, events));
-  pn_reactor_schedule(reactor, 0, NULL);
-  pn_reactor_run(reactor);
-  pn_reactor_free(reactor);
-  expect(events, PN_REACTOR_INIT, PN_SELECTABLE_INIT, PN_SELECTABLE_UPDATED, PN_REACTOR_QUIESCED,
-         PN_TIMER_TASK, PN_SELECTABLE_UPDATED, PN_SELECTABLE_FINAL, PN_REACTOR_FINAL, END);
-  pn_free(events);
-}
-
-static void test_reactor_schedule_handler(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  pn_handler_t *root = pn_reactor_get_handler(reactor);
-  pn_list_t *events = pn_list(PN_VOID, 0);
-  pn_list_t *tevents = pn_list(PN_VOID, 0);
-  pn_handler_add(root, test_handler(reactor, events));
-  pn_handler_t *th = test_handler(reactor, tevents);
-  pn_reactor_schedule(reactor, 0, th);
-  pn_reactor_run(reactor);
-  pn_reactor_free(reactor);
-  pn_handler_free(th);
-  expect(events, PN_REACTOR_INIT, PN_SELECTABLE_INIT, PN_SELECTABLE_UPDATED, PN_REACTOR_QUIESCED, PN_SELECTABLE_UPDATED,
-         PN_SELECTABLE_FINAL, PN_REACTOR_FINAL, END);
-  expect(tevents, PN_TIMER_TASK, END);
-  pn_free(events);
-  pn_free(tevents);
-}
-
-static void test_reactor_schedule_cancel(void) {
-  pn_reactor_t *reactor = pn_reactor();
-  pn_handler_t *root = pn_reactor_get_handler(reactor);
-  pn_list_t *events = pn_list(PN_VOID, 0);
-  pn_handler_add(root, test_handler(reactor, events));
-  pn_task_t *task = pn_reactor_schedule(reactor, 0, NULL);
-  pn_task_cancel(task);
-  pn_reactor_run(reactor);
-  pn_reactor_free(reactor);
-  expect(events, PN_REACTOR_INIT, PN_SELECTABLE_INIT, PN_SELECTABLE_UPDATED,
-         PN_SELECTABLE_FINAL, PN_REACTOR_FINAL, END);
-  pn_free(events);
-}
-
-int main(int argc, char **argv)
-{
-  test_reactor_event_root();
-  test_reactor();
-  test_reactor_free();
-  test_reactor_run();
-  test_reactor_run_free();
-  test_reactor_handler();
-  test_reactor_handler_free();
-  test_reactor_handler_run();
-  test_reactor_handler_run_free();
-  test_reactor_connection();
-  test_reactor_connection_factory();
-  test_reactor_bad_domain();
-  test_reactor_acceptor();
-  test_reactor_acceptor_run();
-  test_reactor_connect();
-  for (int i = 0; i < 64; i++) {
-    test_reactor_transfer(i, 2);
-  }
-  test_reactor_transfer(1024, 64);
-  test_reactor_transfer(4*1024, 1024);
-  test_reactor_schedule();
-  test_reactor_schedule_handler();
-  test_reactor_schedule_cancel();
-  return 0;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/refcount.c
----------------------------------------------------------------------
diff --git a/c/tests/refcount.c b/c/tests/refcount.c
deleted file mode 100644
index 487f115..0000000
--- a/c/tests/refcount.c
+++ /dev/null
@@ -1,393 +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 <proton/connection.h>
-#include <proton/event.h>
-#include <proton/session.h>
-#include <proton/link.h>
-#include <proton/delivery.h>
-#include <proton/transport.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define assert(E) ((E) ? 0 : (abort(), 0))
-
-/**
- * The decref order tests validate that whenever the last pointer to a
- * child object, e.g. a session or a link, is about to go away, the
- * parent object takes ownership of that reference if the child object
- * has not been freed, this avoids reference cycles but allows
- * navigation from parents to children.
- **/
-
-#define SETUP_CSL                               \
-  pn_connection_t *conn = pn_connection();      \
-  pn_session_t *ssn = pn_session(conn);         \
-  pn_incref(ssn);                               \
-  pn_link_t *lnk = pn_sender(ssn, "sender");    \
-  pn_incref(lnk);                               \
-                                                \
-  assert(pn_refcount(conn) == 2);               \
-  assert(pn_refcount(ssn) == 2);                \
-  assert(pn_refcount(lnk) == 1);
-
-static void test_decref_order_csl(void) {
-  SETUP_CSL;
-
-  pn_decref(conn);
-  assert(pn_refcount(conn) == 1); // session keeps alive
-  pn_decref(ssn);
-  assert(pn_refcount(ssn) == 1); // link keeps alive
-  pn_decref(lnk);
-  // all gone now (requires valgrind to validate)
-}
-
-static void test_decref_order_cls(void) {
-  SETUP_CSL;
-
-  pn_decref(conn);
-  assert(pn_refcount(conn) == 1); // session keeps alive
-  pn_decref(lnk);
-  assert(pn_refcount(lnk) == 1); // session takes over ownership
-  pn_decref(ssn);
-  // all gone now (requires valgrind to validate)
-}
-
-static void test_decref_order_lcs(void) {
-  SETUP_CSL;
-
-  pn_decref(lnk);
-  assert(pn_refcount(lnk) == 1); // session takes over ownership
-  pn_decref(conn);
-  assert(pn_refcount(conn) == 1); // session keeps alive
-  pn_decref(ssn);
-  // all gone now (requires valgrind to validate)
-}
-
-static void test_decref_order_scl(void) {
-  SETUP_CSL;
-
-  pn_decref(ssn);
-  assert(pn_refcount(ssn) == 1); // link keeps alive
-  pn_decref(conn);
-  assert(pn_refcount(conn) == 1); // session keeps alive
-  pn_decref(lnk);
-  // all gone now (requires valgrind to validate)
-}
-
-static void test_decref_order_slc(void) {
-  SETUP_CSL;
-
-  pn_decref(ssn);
-  assert(pn_refcount(ssn) == 1); // link keeps alive
-  pn_decref(lnk);
-  assert(pn_refcount(ssn) == 1); // connection takes over ownership
-  assert(pn_refcount(lnk) == 1); // session takes over ownership
-  pn_decref(conn);
-  // all gone now (requires valgrind to validate)
-}
-
-static void test_decref_order_lsc(void) {
-  SETUP_CSL;
-
-  pn_decref(lnk);
-  assert(pn_refcount(lnk) == 1); // session takes over ownership
-  assert(pn_refcount(ssn) == 1);
-  pn_decref(ssn);
-  assert(pn_refcount(lnk) == 1);
-  assert(pn_refcount(ssn) == 1); // connection takes over ownership
-  pn_decref(conn);
-  // all gone now (requires valgrind to validate)
-}
-
-/**
- * The incref order tests verify that once ownership of the last
- * pointer to a child is taken over by a parent, it is reassigned when
- * the child is increfed.
- **/
-
-#define SETUP_INCREF_ORDER                      \
-  SETUP_CSL;                                    \
-  pn_decref(lnk);                               \
-  pn_decref(ssn);                               \
-  assert(pn_refcount(lnk) == 1);                \
-  assert(pn_refcount(ssn) == 1);                \
-  assert(pn_refcount(conn) == 1);
-
-static void test_incref_order_sl(void) {
-  SETUP_INCREF_ORDER;
-
-  pn_incref(ssn);
-  assert(pn_refcount(conn) == 2);
-  assert(pn_refcount(ssn) == 1);
-  assert(pn_refcount(lnk) == 1);
-  pn_incref(lnk);
-  assert(pn_refcount(conn) == 2);
-  assert(pn_refcount(ssn) == 2);
-  assert(pn_refcount(lnk) == 1);
-
-  pn_decref(conn);
-  pn_decref(ssn);
-  pn_decref(lnk);
-}
-
-static void test_incref_order_ls(void) {
-  SETUP_INCREF_ORDER;
-
-  pn_incref(lnk);
-  assert(pn_refcount(conn) == 2);
-  assert(pn_refcount(ssn) == 1);
-  assert(pn_refcount(lnk) == 1);
-  pn_incref(ssn);
-  assert(pn_refcount(conn) == 2);
-  assert(pn_refcount(ssn) == 2);
-  assert(pn_refcount(lnk) == 1);
-
-  pn_decref(conn);
-  pn_decref(ssn);
-  pn_decref(lnk);
-}
-
-static void swap(int array[], int i, int j) {
-  int a = array[i];
-  int b = array[j];
-  array[j] = a;
-  array[i] = b;
-}
-
-static void setup(void **objects) {
-  pn_connection_t *conn = pn_connection();
-  pn_session_t *ssn = pn_session(conn);
-  pn_incref(ssn);
-  pn_link_t *lnk = pn_sender(ssn, "sender");
-  pn_incref(lnk);
-  pn_delivery_t *dlv = pn_delivery(lnk, pn_dtag("dtag", 4));
-  pn_incref(dlv);
-
-  assert(pn_refcount(conn) == 2);
-  assert(pn_refcount(ssn) == 2);
-  assert(pn_refcount(lnk) == 2);
-  assert(pn_refcount(dlv) == 1);
-
-  objects[0] = conn;
-  objects[1] = ssn;
-  objects[2] = lnk;
-  objects[3] = dlv;
-}
-
-static bool decreffed(int *indexes, void **objects, int step, void *object) {
-  for (int i = 0; i <= step; i++) {
-    if (object == objects[indexes[i]]) {
-      return true;
-    }
-  }
-  return false;
-}
-
-static bool live_descendent(int *indexes, void **objects, int step, int objidx) {
-  for (int i = objidx + 1; i < 4; i++) {
-    if (!decreffed(indexes, objects, step, objects[i])) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-static void assert_refcount(void *object, int expected) {
-  int rc = pn_refcount(object);
-  //printf("pn_refcount(%s) = %d\n", pn_object_reify(object)->name, rc);
-  assert(rc == expected);
-}
-
-static void test_decref_order(int *indexes, void **objects) {
-  setup(objects);
-
-  //printf("-----------\n");
-  for (int i = 0; i < 3; i++) {
-    int idx = indexes[i];
-    void *obj = objects[idx];
-    //printf("decreffing %s\n", pn_object_reify(obj)->name);
-    pn_decref(obj);
-    for (int j = 0; j <= i; j++) {
-      // everything we've decreffed already should have a refcount of
-      // 1 because it has been preserved by its parent
-      assert_refcount(objects[indexes[j]], 1);
-    }
-    for (int j = i+1; j < 4; j++) {
-      // everything we haven't decreffed yet should have a refcount of
-      // 2 unless it has a descendant that has not been decrefed (or
-      // it has no child) in which case it should have a refcount of 1
-      int idx = indexes[j];
-      void *obj = objects[idx];
-      assert(!decreffed(indexes, objects, i, obj));
-      if (live_descendent(indexes, objects, i, idx)) {
-        assert_refcount(obj, 2);
-      } else {
-        assert_refcount(obj, 1);
-      }
-    }
-  }
-
-  void *last = objects[indexes[3]];
-  //printf("decreffing %s\n", pn_object_reify(last)->name);
-  pn_decref(last);
-  // all should be gone now, need to run with valgrind to check
-}
-
-static void permute(int n, int *indexes, void **objects) {
-  int j;
-  if (n == 1) {
-    test_decref_order(indexes, objects);
-  } else {
-    for (int i = 1; i <= n; i++) {
-      permute(n-1, indexes, objects);
-      if ((n % 2) == 1) {
-        j = 1;
-      } else {
-        j = i;
-      }
-      swap(indexes, j-1, n-1);
-    }
-  }
-}
-
-static void test_decref_permutations(void) {
-  void *objects[4];
-  int indexes[4] = {0, 1, 2, 3};
-  permute(4, indexes, objects);
-}
-
-static void test_transport(void) {
-  pn_transport_t *transport = pn_transport();
-  assert(pn_refcount(transport) == 1);
-  pn_incref(transport);
-  assert(pn_refcount(transport) == 2);
-  pn_decref(transport);
-  assert(pn_refcount(transport) == 1);
-  pn_free(transport);
-}
-
-static void test_connection_transport(void) {
-  pn_connection_t *connection = pn_connection();
-  assert(pn_refcount(connection) == 1);
-  pn_transport_t *transport = pn_transport();
-  assert(pn_refcount(transport) == 1);
-  pn_transport_bind(transport, connection);
-  assert(pn_refcount(connection) == 2);
-  pn_decref(transport);
-  assert(pn_refcount(transport) == 1); // preserved by the bind
-  assert(pn_refcount(connection) == 1);
-  pn_free(connection);
-}
-
-static void test_transport_connection(void) {
-  pn_transport_t *transport = pn_transport();
-  assert(pn_refcount(transport) == 1);
-  pn_connection_t *connection = pn_connection();
-  assert(pn_refcount(connection) == 1);
-  pn_transport_bind(transport, connection);
-  assert(pn_refcount(connection) == 2);
-  pn_decref(connection);
-  assert(pn_refcount(connection) == 1);
-  assert(pn_refcount(transport) == 1);
-  pn_free(transport);
-}
-
-static void drain(pn_collector_t *collector) {
-  while (pn_collector_next(collector))
-    ;
-}
-
-static void test_collector_connection_transport(void) {
-  pn_collector_t *collector = pn_collector();
-  assert(pn_refcount(collector) == 1);
-  pn_connection_t *connection = pn_connection();
-  assert(pn_refcount(connection) == 1);
-  pn_connection_collect(connection, collector);
-  assert(pn_refcount(collector) == 2);
-  assert(pn_refcount(connection) == 2);
-  drain(collector);
-  assert(pn_refcount(connection) == 1);
-  pn_transport_t *transport = pn_transport();
-  assert(pn_refcount(transport) == 1);
-  pn_transport_bind(transport, connection);
-  assert(pn_refcount(transport) == 1);
-  assert(pn_refcount(connection) == 3);
-  drain(collector);
-  assert(pn_refcount(connection) == 2);
-  pn_decref(transport);
-  assert(pn_refcount(transport) == 1); // preserved by the bind
-  assert(pn_refcount(connection) == 1);
-  pn_free(connection);
-  assert(pn_refcount(transport) == 1); // events
-  assert(pn_refcount(connection) == 1); // events
-  pn_collector_free(collector);
-}
-
-static void test_collector_transport_connection(void) {
-  pn_collector_t *collector = pn_collector();
-  assert(pn_refcount(collector) == 1);
-  pn_transport_t *transport = pn_transport();
-  assert(pn_refcount(transport) == 1);
-  pn_connection_t *connection = pn_connection();
-  assert(pn_refcount(connection) == 1);
-  pn_connection_collect(connection, collector);
-  assert(pn_refcount(collector) == 2);
-  assert(pn_refcount(connection) == 2);
-  drain(collector);
-  assert(pn_refcount(connection) == 1);
-  pn_transport_bind(transport, connection);
-  assert(pn_refcount(connection) == 3);
-  assert(pn_refcount(transport) == 1);
-  drain(collector);
-  assert(pn_refcount(connection) == 2);
-  assert(pn_refcount(transport) == 1);
-  pn_decref(connection);
-  assert(pn_refcount(connection) == 1);
-  assert(pn_refcount(transport) == 1);
-  pn_free(transport);
-  assert(pn_refcount(connection) == 1);
-  assert(pn_refcount(transport) == 1);
-  pn_collector_free(collector);
-}
-
-int main(int argc, char **argv)
-{
-  test_decref_order_csl();
-  test_decref_order_cls();
-  test_decref_order_lcs();
-  test_decref_order_scl();
-  test_decref_order_slc();
-  test_decref_order_lsc();
-
-  test_incref_order_sl();
-  test_incref_order_ls();
-
-  test_decref_permutations();
-
-  test_transport();
-  test_connection_transport();
-  test_transport_connection();
-  test_collector_connection_transport();
-  test_collector_transport_connection();
-  return 0;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/refcount_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/refcount_test.cpp b/c/tests/refcount_test.cpp
new file mode 100644
index 0000000..2fbf42b
--- /dev/null
+++ b/c/tests/refcount_test.cpp
@@ -0,0 +1,371 @@
+/*
+ *
+ * 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 "catch_extra.hpp"
+
+#include <proton/connection.h>
+#include <proton/delivery.h>
+#include <proton/event.h>
+#include <proton/link.h>
+#include <proton/session.h>
+#include <proton/transport.h>
+
+/**
+ * The decref order tests validate that whenever the last pointer to a
+ * child object, e.g. a session or a link, is about to go away, the
+ * parent object takes ownership of that reference if the child object
+ * has not been freed, this avoids reference cycles but allows
+ * navigation from parents to children.
+ **/
+
+#define SETUP_CSL                                                              \
+  pn_connection_t *conn = pn_connection();                                     \
+  pn_session_t *ssn = pn_session(conn);                                        \
+  pn_incref(ssn);                                                              \
+  pn_link_t *lnk = pn_sender(ssn, "sender");                                   \
+  pn_incref(lnk);                                                              \
+                                                                               \
+  REQUIRE(pn_refcount(conn) == 2);                                             \
+  REQUIRE(pn_refcount(ssn) == 2);                                              \
+  REQUIRE(pn_refcount(lnk) == 1);
+
+TEST_CASE("test_decref_order_csl") {
+  SETUP_CSL;
+
+  pn_decref(conn);
+  REQUIRE(pn_refcount(conn) == 1); // session keeps alive
+  pn_decref(ssn);
+  REQUIRE(pn_refcount(ssn) == 1); // link keeps alive
+  pn_decref(lnk);
+  // all gone now (requires valgrind to validate)
+}
+
+TEST_CASE("test_decref_order_cls") {
+  SETUP_CSL;
+
+  pn_decref(conn);
+  REQUIRE(pn_refcount(conn) == 1); // session keeps alive
+  pn_decref(lnk);
+  REQUIRE(pn_refcount(lnk) == 1); // session takes over ownership
+  pn_decref(ssn);
+  // all gone now (requires valgrind to validate)
+}
+
+TEST_CASE("test_decref_order_lcs") {
+  SETUP_CSL;
+
+  pn_decref(lnk);
+  REQUIRE(pn_refcount(lnk) == 1); // session takes over ownership
+  pn_decref(conn);
+  REQUIRE(pn_refcount(conn) == 1); // session keeps alive
+  pn_decref(ssn);
+  // all gone now (requires valgrind to validate)
+}
+
+TEST_CASE("test_decref_order_scl") {
+  SETUP_CSL;
+
+  pn_decref(ssn);
+  REQUIRE(pn_refcount(ssn) == 1); // link keeps alive
+  pn_decref(conn);
+  REQUIRE(pn_refcount(conn) == 1); // session keeps alive
+  pn_decref(lnk);
+  // all gone now (requires valgrind to validate)
+}
+
+TEST_CASE("test_decref_order_slc") {
+  SETUP_CSL;
+
+  pn_decref(ssn);
+  REQUIRE(pn_refcount(ssn) == 1); // link keeps alive
+  pn_decref(lnk);
+  REQUIRE(pn_refcount(ssn) == 1); // connection takes over ownership
+  REQUIRE(pn_refcount(lnk) == 1); // session takes over ownership
+  pn_decref(conn);
+  // all gone now (requires valgrind to validate)
+}
+
+TEST_CASE("test_decref_order_lsc") {
+  SETUP_CSL;
+
+  pn_decref(lnk);
+  REQUIRE(pn_refcount(lnk) == 1); // session takes over ownership
+  REQUIRE(pn_refcount(ssn) == 1);
+  pn_decref(ssn);
+  REQUIRE(pn_refcount(lnk) == 1);
+  REQUIRE(pn_refcount(ssn) == 1); // connection takes over ownership
+  pn_decref(conn);
+  // all gone now (requires valgrind to validate)
+}
+
+/**
+ * The incref order tests verify that once ownership of the last
+ * pointer to a child is taken over by a parent, it is reassigned when
+ * the child is increfed.
+ **/
+
+#define SETUP_INCREF_ORDER                                                     \
+  SETUP_CSL;                                                                   \
+  pn_decref(lnk);                                                              \
+  pn_decref(ssn);                                                              \
+  REQUIRE(pn_refcount(lnk) == 1);                                              \
+  REQUIRE(pn_refcount(ssn) == 1);                                              \
+  REQUIRE(pn_refcount(conn) == 1);
+
+TEST_CASE("test_incref_order_sl") {
+  SETUP_INCREF_ORDER;
+
+  pn_incref(ssn);
+  REQUIRE(pn_refcount(conn) == 2);
+  REQUIRE(pn_refcount(ssn) == 1);
+  REQUIRE(pn_refcount(lnk) == 1);
+  pn_incref(lnk);
+  REQUIRE(pn_refcount(conn) == 2);
+  REQUIRE(pn_refcount(ssn) == 2);
+  REQUIRE(pn_refcount(lnk) == 1);
+
+  pn_decref(conn);
+  pn_decref(ssn);
+  pn_decref(lnk);
+}
+
+TEST_CASE("test_incref_order_ls") {
+  SETUP_INCREF_ORDER;
+
+  pn_incref(lnk);
+  REQUIRE(pn_refcount(conn) == 2);
+  REQUIRE(pn_refcount(ssn) == 1);
+  REQUIRE(pn_refcount(lnk) == 1);
+  pn_incref(ssn);
+  REQUIRE(pn_refcount(conn) == 2);
+  REQUIRE(pn_refcount(ssn) == 2);
+  REQUIRE(pn_refcount(lnk) == 1);
+
+  pn_decref(conn);
+  pn_decref(ssn);
+  pn_decref(lnk);
+}
+
+static void swap(int array[], int i, int j) {
+  int a = array[i];
+  int b = array[j];
+  array[j] = a;
+  array[i] = b;
+}
+
+static void setup(void **objects) {
+  pn_connection_t *conn = pn_connection();
+  pn_session_t *ssn = pn_session(conn);
+  pn_incref(ssn);
+  pn_link_t *lnk = pn_sender(ssn, "sender");
+  pn_incref(lnk);
+  pn_delivery_t *dlv = pn_delivery(lnk, pn_dtag("dtag", 4));
+  pn_incref(dlv);
+
+  REQUIRE(pn_refcount(conn) == 2);
+  REQUIRE(pn_refcount(ssn) == 2);
+  REQUIRE(pn_refcount(lnk) == 2);
+  REQUIRE(pn_refcount(dlv) == 1);
+
+  objects[0] = conn;
+  objects[1] = ssn;
+  objects[2] = lnk;
+  objects[3] = dlv;
+}
+
+static bool decreffed(int *indexes, void **objects, int step, void *object) {
+  for (int i = 0; i <= step; i++) {
+    if (object == objects[indexes[i]]) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool live_descendent(int *indexes, void **objects, int step,
+                            int objidx) {
+  for (int i = objidx + 1; i < 4; i++) {
+    if (!decreffed(indexes, objects, step, objects[i])) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+static void assert_refcount(void *object, int expected) {
+  int rc = pn_refcount(object);
+  // printf("pn_refcount(%s) = %d\n", pn_object_reify(object)->name, rc);
+  REQUIRE(rc == expected);
+}
+
+static void test_decref_order(int *indexes, void **objects) {
+  setup(objects);
+
+  // printf("-----------\n");
+  for (int i = 0; i < 3; i++) {
+    int idx = indexes[i];
+    void *obj = objects[idx];
+    // printf("decreffing %s\n", pn_object_reify(obj)->name);
+    pn_decref(obj);
+    for (int j = 0; j <= i; j++) {
+      // everything we've decreffed already should have a refcount of
+      // 1 because it has been preserved by its parent
+      assert_refcount(objects[indexes[j]], 1);
+    }
+    for (int j = i + 1; j < 4; j++) {
+      // everything we haven't decreffed yet should have a refcount of
+      // 2 unless it has a descendant that has not been decrefed (or
+      // it has no child) in which case it should have a refcount of 1
+      int idx = indexes[j];
+      void *obj = objects[idx];
+      REQUIRE(!decreffed(indexes, objects, i, obj));
+      if (live_descendent(indexes, objects, i, idx)) {
+        assert_refcount(obj, 2);
+      } else {
+        assert_refcount(obj, 1);
+      }
+    }
+  }
+
+  void *last = objects[indexes[3]];
+  // printf("decreffing %s\n", pn_object_reify(last)->name);
+  pn_decref(last);
+  // all should be gone now, need to run with valgrind to check
+}
+
+static void permute(int n, int *indexes, void **objects) {
+  int j;
+  if (n == 1) {
+    test_decref_order(indexes, objects);
+  } else {
+    for (int i = 1; i <= n; i++) {
+      permute(n - 1, indexes, objects);
+      if ((n % 2) == 1) {
+        j = 1;
+      } else {
+        j = i;
+      }
+      swap(indexes, j - 1, n - 1);
+    }
+  }
+}
+
+TEST_CASE("test_decref_permutations") {
+  void *objects[4];
+  int indexes[4] = {0, 1, 2, 3};
+  permute(4, indexes, objects);
+}
+
+TEST_CASE("test_transport") {
+  pn_transport_t *transport = pn_transport();
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_incref(transport);
+  REQUIRE(pn_refcount(transport) == 2);
+  pn_decref(transport);
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_free(transport);
+}
+
+TEST_CASE("test_connection_transport") {
+  pn_connection_t *connection = pn_connection();
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_transport_t *transport = pn_transport();
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_transport_bind(transport, connection);
+  REQUIRE(pn_refcount(connection) == 2);
+  pn_decref(transport);
+  REQUIRE(pn_refcount(transport) == 1); // preserved by the bind
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_free(connection);
+}
+
+TEST_CASE("test_transport_connection") {
+  pn_transport_t *transport = pn_transport();
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_connection_t *connection = pn_connection();
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_transport_bind(transport, connection);
+  REQUIRE(pn_refcount(connection) == 2);
+  pn_decref(connection);
+  REQUIRE(pn_refcount(connection) == 1);
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_free(transport);
+}
+
+static void drain(pn_collector_t *collector) {
+  while (pn_collector_next(collector))
+    ;
+}
+
+TEST_CASE("test_collector_connection_transport") {
+  pn_collector_t *collector = pn_collector();
+  REQUIRE(pn_refcount(collector) == 1);
+  pn_connection_t *connection = pn_connection();
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_connection_collect(connection, collector);
+  REQUIRE(pn_refcount(collector) == 2);
+  REQUIRE(pn_refcount(connection) == 2);
+  drain(collector);
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_transport_t *transport = pn_transport();
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_transport_bind(transport, connection);
+  REQUIRE(pn_refcount(transport) == 1);
+  REQUIRE(pn_refcount(connection) == 3);
+  drain(collector);
+  REQUIRE(pn_refcount(connection) == 2);
+  pn_decref(transport);
+  REQUIRE(pn_refcount(transport) == 1); // preserved by the bind
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_free(connection);
+  REQUIRE(pn_refcount(transport) == 1);  // events
+  REQUIRE(pn_refcount(connection) == 1); // events
+  pn_collector_free(collector);
+}
+
+TEST_CASE("test_collector_transport_connection") {
+  pn_collector_t *collector = pn_collector();
+  REQUIRE(pn_refcount(collector) == 1);
+  pn_transport_t *transport = pn_transport();
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_connection_t *connection = pn_connection();
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_connection_collect(connection, collector);
+  REQUIRE(pn_refcount(collector) == 2);
+  REQUIRE(pn_refcount(connection) == 2);
+  drain(collector);
+  REQUIRE(pn_refcount(connection) == 1);
+  pn_transport_bind(transport, connection);
+  REQUIRE(pn_refcount(connection) == 3);
+  REQUIRE(pn_refcount(transport) == 1);
+  drain(collector);
+  REQUIRE(pn_refcount(connection) == 2);
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_decref(connection);
+  REQUIRE(pn_refcount(connection) == 1);
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_free(transport);
+  REQUIRE(pn_refcount(connection) == 1);
+  REQUIRE(pn_refcount(transport) == 1);
+  pn_collector_free(collector);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/ssl.c
----------------------------------------------------------------------
diff --git a/c/tests/ssl.c b/c/tests/ssl.c
deleted file mode 100644
index 9acb362..0000000
--- a/c/tests/ssl.c
+++ /dev/null
@@ -1,71 +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 <proton/ssl.h>
-
-#include "test_tools.h"
-
-static void test_ssl_protocols(test_t *t)
-{
-  pn_ssl_domain_t *sd = pn_ssl_domain(PN_SSL_MODE_CLIENT);
-
-  // Error no protocol set
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "")==PN_ARG_ERR);
-  // Unknown protocol
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "blah")==PN_ARG_ERR);
-  // Unknown protocol with known protocl prefix
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1.x")==PN_ARG_ERR);
-
-  // known protocols
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1")==0);
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1.1")==0);
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1.2")==0);
-
-  // Multiple protocols
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.1 TLSv1.2")==0);
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.1")==0);
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1.1 TLSv1.2")==0);
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.2")==0);
-
-  // Illegal separators
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1/TLSv1.1 TLSv1.2")==PN_ARG_ERR);
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1-TLSv1.1 TLSv1.2")==PN_ARG_ERR);
-
-  // Legal separators
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1,TLSv1.1;TLSv1.2  ; ")==0);
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1;TLSv1.1 TLSv1.2,,,,")==0);
-
-  // Known followed by unknown protocols
-  TEST_CHECK(t, pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.x;TLSv1_2")==PN_ARG_ERR);
-
-  pn_ssl_domain_free(sd);
-}
-
-int main(int argc, char **argv) {
-  int failed = 0;
-  // Don't run these tests if ssl functionality wasn't compiled
-  if (!pn_ssl_present()) {
-    fprintf(stderr, "No SSL implementation to test\n");
-    return failed;
-  }
-#if !defined(_WIN32)
-  RUN_ARGV_TEST(failed, t, test_ssl_protocols(&t));
-#endif
-  return failed;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/ssl_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/ssl_test.cpp b/c/tests/ssl_test.cpp
new file mode 100644
index 0000000..bd1e228
--- /dev/null
+++ b/c/tests/ssl_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 <proton/ssl.h>
+
+#include "./pn_test.hpp"
+
+TEST_CASE("ssl_protocols") {
+  if (!pn_ssl_present()) {
+    WARN("SSL not available, skipping");
+    return;
+  }
+  pn_test::auto_free<pn_ssl_domain_t, pn_ssl_domain_free> sd(
+      pn_ssl_domain(PN_SSL_MODE_CLIENT));
+
+  // Error no protocol set
+  CHECK(pn_ssl_domain_set_protocols(sd, "") == PN_ARG_ERR);
+  // Unknown protocol
+  CHECK(pn_ssl_domain_set_protocols(sd, "blah") == PN_ARG_ERR);
+  // Unknown protocol with known protocl prefix
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1.x") == PN_ARG_ERR);
+
+  // known protocols
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1") == 0);
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1.1") == 0);
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1.2") == 0);
+
+  // Multiple protocols
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.1 TLSv1.2") == 0);
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.1") == 0);
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1.1 TLSv1.2") == 0);
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.2") == 0);
+
+  // Illegal separators
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1/TLSv1.1 TLSv1.2") == PN_ARG_ERR);
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1-TLSv1.1 TLSv1.2") == PN_ARG_ERR);
+
+  // Legal separators
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1,TLSv1.1;TLSv1.2  ; ") == 0);
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1;TLSv1.1 TLSv1.2,,,,") == 0);
+
+  // Known followed by unknown protocols
+  CHECK(pn_ssl_domain_set_protocols(sd, "TLSv1 TLSv1.x;TLSv1_2") == PN_ARG_ERR);
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/test_handler.h
----------------------------------------------------------------------
diff --git a/c/tests/test_handler.h b/c/tests/test_handler.h
deleted file mode 100644
index 8799fb3..0000000
--- a/c/tests/test_handler.h
+++ /dev/null
@@ -1,184 +0,0 @@
-#ifndef TESTS_TEST_DRIVER_H
-#define TESTS_TEST_DRIVER_H
-
-/*
- * 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 <proton/ssl.h>
-
-/* C event handlers for tests */
-
-#define MAX_EVENT_LOG 2048         /* Max number of event types stored per proactor_test */
-
-struct test_handler_t;
-
-/* Returns 0 if the test should continue processing events, non-0 if processing should pause here */
-typedef pn_event_type_t (*test_handler_fn)(struct test_handler_t*, pn_event_t*);
-
-/* A handler that logs event types and delegates to another handler */
-typedef struct test_handler_t {
-  test_t *t;
-  test_handler_fn f;
-  pn_event_type_t log[MAX_EVENT_LOG]; /* Log of event types */
-  size_t log_size;                    /* Number of events in the log */
-  void *context;                      /* Generic test context */
-  /* Specific context slots for proton objects commonly used by handlers  */
-  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;
-  pn_ssl_domain_t *ssl_domain;
-} test_handler_t;
-
-void test_handler_init(test_handler_t *th, test_t *t, test_handler_fn f) {
-  memset(th, 0, sizeof(*th));
-  th->t = t;
-  th->f = f;
-}
-
-/* Save event type in the handler log */
-void test_handler_log(test_handler_t *th, pn_event_t *e) {
-  TEST_ASSERT(th->log_size < MAX_EVENT_LOG);
-  th->log[th->log_size++] = pn_event_type(e);
-}
-
-/* Keep at most n events in the handler's log, remove old events if necessary */
-void test_handler_clear(test_handler_t *th, size_t n) {
-  if (n == 0) {
-    th->log_size = 0;
-  } else if (n < th->log_size) {
-    memmove(th->log, th->log + th->log_size - n, n * sizeof(pn_event_type_t));
-    th->log_size = n;
-  }
-}
-
-void test_etypes_expect_(test_t *t, pn_event_type_t *etypes, size_t size, const char* file, int line, ...) {
-  va_list ap;
-  va_start(ap, line);             /* ap is null terminated */
-  pn_event_type_t want = (pn_event_type_t)va_arg(ap, int);
-  size_t i = 0;
-  while (want && i < size && want == etypes[i]) {
-    ++i;
-    want = (pn_event_type_t)va_arg(ap, int);
-  }
-  if (i < size || want) {
-    test_errorf_(t, NULL, file, line, "event mismatch");
-    if (!t->inverted) {
-      fprintf(stderr, "after:");
-      for (size_t j = 0; j < i; ++j) { /* These events matched */
-        fprintf(stderr, " %s", pn_event_type_name(etypes[j]));
-      }
-      fprintf(stderr, "\n want:");
-      for (; want; want = (pn_event_type_t)va_arg(ap, int)) {
-        fprintf(stderr, " %s", pn_event_type_name(want));
-      }
-      fprintf(stderr, "\n  got:");
-      for (; i < size; ++i) {
-        fprintf(stderr, " %s", pn_event_type_name(etypes[i]));
-      }
-      fprintf(stderr, "\n");
-    }
-  }
-  va_end(ap);
-}
-
-#define TEST_HANDLER_EXPECT(TH, ...) do {                               \
-    test_etypes_expect_((TH)->t, (TH)->log, (TH)->log_size, __FILE__, __LINE__, __VA_ARGS__); \
-    test_handler_clear((TH), 0);                                         \
-  } while(0)
-
-#define TEST_HANDLER_EXPECT_LAST(TH, ETYPE) do {                        \
-    test_handler_clear((TH), 1);                                        \
-    test_etypes_expect_((TH)->t, (TH)->log, (TH)->log_size, __FILE__, __LINE__, ETYPE, 0); \
-    test_handler_clear((TH), 0);                                         \
-  } while(0)
-
-/* A pn_connection_driver_t with a test_handler */
-typedef struct test_connection_driver_t {
-  test_handler_t handler;
-  pn_connection_driver_t driver;
-} test_connection_driver_t;
-
-void test_connection_driver_init(test_connection_driver_t *d, test_t *t, test_handler_fn f, void* context)
-{
-  test_handler_init(&d->handler, t, f);
-  d->handler.context = context;
-  pn_connection_driver_init(&d->driver, NULL, NULL);
-}
-
-void test_connection_driver_destroy(test_connection_driver_t *d) {
-  pn_connection_driver_destroy(&d->driver);
-}
-
-pn_event_type_t test_connection_driver_handle(test_connection_driver_t *d) {
-  for (pn_event_t *e = pn_connection_driver_next_event(&d->driver);
-       e;
-       e = pn_connection_driver_next_event(&d->driver))
-  {
-    test_handler_log(&d->handler, e);
-    pn_event_type_t et = d->handler.f ? d->handler.f(&d->handler, e) : PN_EVENT_NONE;
-    if (et) return et;
-  }
-  return PN_EVENT_NONE;
-}
-
-/* Transfer data from one driver to another in memory */
-static int test_connection_drivers_xfer(test_connection_driver_t *dst, test_connection_driver_t *src)
-{
-  pn_bytes_t wb = pn_connection_driver_write_buffer(&src->driver);
-  pn_rwbytes_t rb =  pn_connection_driver_read_buffer(&dst->driver);
-  size_t size = rb.size < wb.size ? rb.size : wb.size;
-  if (size) {
-    memcpy(rb.start, wb.start, size);
-    pn_connection_driver_write_done(&src->driver, size);
-    pn_connection_driver_read_done(&dst->driver, size);
-  }
-  return size;
-}
-
-/* Run a pair of test drivers till there is nothing to do or one of their handlers returns non-0
-   In that case return that driver
-*/
-test_connection_driver_t* test_connection_drivers_run(test_connection_driver_t *a, test_connection_driver_t *b)
-{
-  int data = 0;
-  do {
-    if (test_connection_driver_handle(a)) return a;
-    if (test_connection_driver_handle(b)) return b;
-    data = test_connection_drivers_xfer(a, b) + test_connection_drivers_xfer(b, a);
-  } while (data || pn_connection_driver_has_event(&a->driver) || pn_connection_driver_has_event(&b->driver));
-  return NULL;
-}
-
-/* Initialize a client-server driver pair */
-void test_connection_drivers_init(test_t *t, test_connection_driver_t *a, test_handler_fn fa, test_connection_driver_t *b, test_handler_fn fb) {
-  test_connection_driver_init(a, t, fa, NULL);
-  test_connection_driver_init(b, t, fb, NULL);
-  pn_transport_set_server(b->driver.transport);
-}
-
-void test_connection_drivers_destroy(test_connection_driver_t *a, test_connection_driver_t *b) {
-  test_connection_driver_destroy(a);
-  test_connection_driver_destroy(b);
-}
-#endif // TESTS_TEST_DRIVER_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/test_main.cpp
----------------------------------------------------------------------
diff --git a/c/tests/test_main.cpp b/c/tests/test_main.cpp
new file mode 100644
index 0000000..2375b7a
--- /dev/null
+++ b/c/tests/test_main.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 CATCH_CONFIG_MAIN
+#include <catch.hpp>
+
+namespace Catch {
+
+// Same as built-in "console" reporter, but prints the test name before
+// running each test, as a progress indicator. Enable with
+//     -r console.progress
+struct ConsoleProgresReporter : ConsoleReporter {
+
+  ConsoleProgresReporter(ReporterConfig const &config)
+      : ConsoleReporter(config) {}
+
+  static std::string getDescription() {
+    return "Shows each test-case before running, results as plain lines of "
+           "text";
+  }
+
+  void testCaseStarting(TestCaseInfo const &testCaseInfo) CATCH_OVERRIDE {
+    stream << "running test case \"" << testCaseInfo.name << '"' << std::endl;
+    ConsoleReporter::testCaseStarting(testCaseInfo);
+  }
+};
+
+INTERNAL_CATCH_REGISTER_REPORTER("console.progress", ConsoleProgresReporter)
+
+} // namespace Catch

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/test_tools.h
----------------------------------------------------------------------
diff --git a/c/tests/test_tools.h b/c/tests/test_tools.h
deleted file mode 100644
index 164968e..0000000
--- a/c/tests/test_tools.h
+++ /dev/null
@@ -1,262 +0,0 @@
-#ifndef TESTS_TEST_TOOLS_H
-#define TESTS_TEST_TOOLS_H
-
-/*
- * 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 <proton/condition.h>
-#include <proton/connection_driver.h>
-#include <proton/delivery.h>
-#include <proton/event.h>
-#include <proton/link.h>
-#include <proton/message.h>
-#include <proton/proactor.h>
-#include <proton/transport.h>
-#include <proton/type_compat.h>
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* Handy way to make pn_bytes: PN_BYTES_LITERAL(FOO) => pn_bytes("FOO",3) */
-#define PN_BYTES_LITERAL(X) (pn_bytes(sizeof(#X)-1, #X))
-
-/* A struct to collect the results of a test, created by RUN_TEST macro. */
-typedef struct test_t {
-  const char* name;
-  int errors;
-  uintptr_t data;               /* Test can store some non-error data here */
-  bool inverted;                /* An inverted test prints no output and reports failure if it passes */
-} test_t;
-
-/* Internal, use macros. Print error message and increase the t->errors count.
-   All output from test macros goes to stderr so it interleaves with PN_TRACE logs.
-*/
-void test_vlogf_(test_t *t, const char *prefix, const char* expr,
-                 const char* file, int line, const char *fmt, va_list ap)
-{
-  if (t->inverted) return;
-  fprintf(stderr, "%s:%d", file, line);
-  if (prefix && *prefix) fprintf(stderr, ": %s", prefix);
-  if (expr && *expr) fprintf(stderr, ": %s", expr);
-  if (fmt && *fmt) {
-    fprintf(stderr, ": ");
-    vfprintf(stderr, fmt, ap);
-  }
-  if (t) fprintf(stderr, " [%s]", t->name);
-  fprintf(stderr, "\n");
-  fflush(stderr);
-}
-
-void test_logf_(test_t *t, const char *prefix, const char* expr,
-                const char* file, int line, const char *fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  test_vlogf_(t, prefix, expr, file, line, fmt, ap);
-  va_end(ap);
-}
-
-void test_errorf_(test_t *t, const char* expr,
-                  const char* file, int line, const char *fmt, ...) {
-  ++t->errors;
-  va_list ap;
-  va_start(ap, fmt);
-  test_vlogf_(t, "error", expr, file, line, fmt, ap);
-  va_end(ap);
-}
-
-bool test_check_(test_t *t, bool expr, const char *sexpr,
-                 const char *file, int line, const char* fmt, ...) {
-  if (!expr) {
-    ++t->errors;
-    va_list ap;
-    va_start(ap, fmt);
-    test_vlogf_(t, "check failed", sexpr, file, line, fmt, ap);
-    va_end(ap);
-  }
-  return expr;
-}
-
-/* Call via TEST_ASSERT macros */
-void assert_fail_(const char* expr, const char* file, int line, const char *fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  test_vlogf_(NULL, "assertion failed", expr, file, line, fmt, ap);
-  va_end(ap);
-  abort();
-}
-
-/* Unconditional assert (does not depend on NDEBUG) for tests. */
-#define TEST_ASSERT(expr)                                               \
-  ((expr) ?  (void)0 : assert_fail_(#expr, __FILE__, __LINE__, NULL))
-
-/* Unconditional assert with printf-style message (does not depend on NDEBUG) for tests. */
-#define TEST_ASSERTF(expr, ...)                                         \
-  ((expr) ?  (void)0 : assert_fail_(#expr, __FILE__, __LINE__, __VA_ARGS__))
-
-/* Like TEST_ASSERT but includes  errno string for err */
-/* TODO aconway 2017-02-16: not thread safe, replace with safe strerror_r or similar */
-#define TEST_ASSERT_ERRNO(expr, err)            \
-  TEST_ASSERTF((expr), "%s", strerror(err))
-
-
-/* Print a message but don't mark the test as having an error */
-#define TEST_LOGF(TEST, ...)                                            \
-  test_logf_((TEST), "info", NULL, __FILE__, __LINE__, __VA_ARGS__)
-
-/* Print an error with printf-style message, increment TEST->errors */
-#define TEST_ERRORF(TEST, ...)                                  \
-  test_errorf_((TEST), NULL, __FILE__, __LINE__, __VA_ARGS__)
-
-/* If EXPR is false, print and record an error for t including #EXPR  */
-#define TEST_CHECKF(TEST, EXPR, ...)                                    \
-  test_check_((TEST), (EXPR), #EXPR, __FILE__, __LINE__, __VA_ARGS__)
-
-/* If EXPR is false, print and record an error for t including EXPR  */
-#define TEST_CHECK(TEST, EXPR)                                  \
-  test_check_((TEST), (EXPR), #EXPR, __FILE__, __LINE__, "")
-
-/* If EXPR is false, print and record an error for t NOT including #EXPR  */
-#define TEST_CHECKNF(TEST, EXPR, ...)                                   \
-  test_check_((TEST), (EXPR), NULL, __FILE__, __LINE__, __VA_ARGS__)
-
-bool test_etype_equal_(test_t *t, pn_event_type_t want, pn_event_type_t got, const char *file, int line) {
-  return test_check_(t, want == got, NULL, file, line, "want %s got %s",
-                     pn_event_type_name(want),
-                     pn_event_type_name(got));
-}
-#define TEST_ETYPE_EQUAL(TEST, WANT, GOT) test_etype_equal_((TEST), (WANT), (GOT), __FILE__, __LINE__)
-
-bool test_int_equal_(test_t *t, int want, int got, const char *file, int line) {
-  return test_check_(t, want == got, NULL, file, line, "want %d, got %d", want, got);
-}
-#define TEST_INT_EQUAL(TEST, WANT, GOT) test_int_equal_((TEST), (WANT), (GOT), __FILE__, __LINE__)
-
-bool test_size_equal_(test_t *t, size_t want, size_t got, const char *file, int line) {
-  return test_check_(t, want == got, NULL, file, line, "want %zd, got %zd", want, got);
-}
-#define TEST_SIZE_EQUAL(TEST, WANT, GOT) test_size_equal_((TEST), (WANT), (GOT), __FILE__, __LINE__)
-
-bool test_str_equal_(test_t *t, const char* want, const char* got, const char *file, int line) {
-  return test_check_(t, !strcmp(want, got), NULL, file, line, "want '%s', got '%s'", want, got);
-}
-#define TEST_STR_EQUAL(TEST, WANT, GOT) test_str_equal_((TEST), (WANT), (GOT), __FILE__, __LINE__)
-
-#define TEST_INSPECT(TEST, WANT, GOT) do {              \
-    pn_string_t *s = pn_string(NULL);                   \
-    TEST_ASSERT(0 == pn_inspect(GOT, s));               \
-    TEST_STR_EQUAL((TEST), (WANT), pn_string_get(s));   \
-    pn_free(s);                                         \
-  } while (0)
-
-#define TEST_STR_IN(TEST, WANT, GOT)                                    \
-  test_check_((TEST), strstr((GOT), (WANT)), NULL, __FILE__, __LINE__, "'%s' not in '%s'", (WANT), (GOT))
-
-#define TEST_COND_EMPTY(TEST, C)                                        \
-  TEST_CHECKNF((TEST), (!(C) || !pn_condition_is_set(C)), "Unexpected condition - %s:%s", \
-              pn_condition_get_name(C), pn_condition_get_description(C))
-
-#define TEST_COND_DESC(TEST, WANT, C)                                   \
-  (TEST_CHECKNF(t, pn_condition_is_set((C)), "No condition, expected :%s", (WANT)) ? \
-   TEST_STR_IN(t, (WANT), pn_condition_get_description(C)) : 0);
-
-#define TEST_COND_NAME(TEST, WANT, C)                                   \
-  (TEST_CHECKNF(t, pn_condition_is_set((C)), "No condition, expected %s:", (WANT)) ? \
-   TEST_STR_EQUAL(t, (WANT), pn_condition_get_name(C)) : 0);
-
-#define TEST_CONDITION(TEST, NAME, DESC, C) do {        \
-    TEST_COND_NAME(TEST, NAME, C);                      \
-    TEST_COND_DESC(TEST, DESC, C);                      \
-  } while(0)
-
-/* T is name of a test_t variable, EXPR is the test expression (which should update T)
-   FAILED is incremented if the test has errors
-*/
-#define RUN_TEST(FAILED, T, EXPR) do {                                  \
-    fprintf(stderr, "TEST: %s\n", #EXPR);                               \
-    fflush(stdout);                                                     \
-    test_t T = { #EXPR, 0 };                                            \
-    (EXPR);                                                             \
-    if (T.errors && !t.inverted) {                                      \
-      fprintf(stderr, "FAIL: %s (%d errors)\n", #EXPR, T.errors);       \
-      ++(FAILED);                                                       \
-    } else if (!T.errors && t.inverted) {                               \
-      fprintf(stderr, "UNEXPECTED PASS: %s", #EXPR);                    \
-      ++(FAILED);                                                       \
-    }                                                                   \
-  } while(0)
-
-/* Like RUN_TEST but only if one of the argv strings is found in the test EXPR */
-#define RUN_ARGV_TEST(FAILED, T, EXPR) do {     \
-    if (argc == 1) {                            \
-      RUN_TEST(FAILED, T, EXPR);                \
-    } else {                                    \
-      for (int i = 1; i < argc; ++i) {          \
-        if (strstr(#EXPR, argv[i])) {           \
-          RUN_TEST(FAILED, T, EXPR);            \
-          break;                                \
-        }                                       \
-      }                                         \
-    }                                           \
-  } while(0)
-
-/* Ensure buf has at least size bytes, use realloc if need be */
-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;
-  }
-}
-
-static const size_t BUF_MIN = 1024;
-
-/* Encode message m into buffer buf, return the size.
- * The buffer is expanded using realloc() if needed.
- */
-size_t message_encode(pn_message_t* m, pn_rwbytes_t *buf) {
-  int err = 0;
-  rwbytes_ensure(buf, BUF_MIN);
-  size_t size = buf->size;
-  while ((err = pn_message_encode(m, buf->start, &size)) != 0) {
-    if (err == PN_OVERFLOW) {
-      rwbytes_ensure(buf, buf->size * 2);
-      size = buf->size;
-    } else {
-      TEST_ASSERTF(err == 0, "encoding: %s %s", pn_code(err), pn_error_text(pn_message_error(m)));
-    }
-  }
-  return size;
-}
-
-/* Decode message from delivery d into message m.
- * Use buf to hold intermediate message data, expand with realloc() if needed.
- */
-void message_decode(pn_message_t *m, pn_delivery_t *d, pn_rwbytes_t *buf) {
-  pn_link_t *l = pn_delivery_link(d);
-  ssize_t size = pn_delivery_pending(d);
-  rwbytes_ensure(buf, size);
-  ssize_t result = pn_link_recv(l, buf->start, size);
-  TEST_ASSERTF(size == result, "%ld != %ld", (long)size, (long)result);
-  pn_message_clear(m);
-  TEST_ASSERTF(!pn_message_decode(m, buf->start, size), "decode: %s", pn_error_text(pn_message_error(m)));
-}
-
-#endif // TESTS_TEST_TOOLS_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/url_test.cpp
----------------------------------------------------------------------
diff --git a/c/tests/url_test.cpp b/c/tests/url_test.cpp
new file mode 100644
index 0000000..027799f
--- /dev/null
+++ b/c/tests/url_test.cpp
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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 "./pn_test.hpp"
+
+#include "proton/error.h"
+#include "proton/type_compat.h"
+#include "proton/url.h"
+
+using namespace pn_test;
+using Catch::Matchers::Equals;
+
+void check_url(const char *url, const char *scheme, const char *user,
+               const char *pass, const char *host, const char *port,
+               const char *path, bool round_trip = true) {
+  INFO("url=\"" << url << '"');
+  auto_free<pn_url_t, pn_url_free> purl(pn_url_parse(url));
+  CHECK_THAT(scheme, Equals(pn_url_get_scheme(purl)));
+  CHECK_THAT(user, Equals(pn_url_get_username(purl)));
+  CHECK_THAT(pass, Equals(pn_url_get_password(purl)));
+  CHECK_THAT(host, Equals(pn_url_get_host(purl)));
+  CHECK_THAT(port, Equals(pn_url_get_port(purl)));
+  CHECK_THAT(path, Equals(pn_url_get_path(purl)));
+  if (round_trip) CHECK_THAT(url, Equals(pn_url_str(purl)));
+}
+
+TEST_CASE("url") {
+  const char *null = 0;
+  check_url("/Foo.bar:90087@somewhere", null, null, null, null, null,
+            "Foo.bar:90087@somewhere");
+  check_url("host", null, null, null, "host", null, null);
+  check_url("host:423", null, null, null, "host", "423", null);
+  check_url("user@host", null, "user", null, "host", null, null);
+
+  // Can't round-trip passwords with ':', not strictly legal
+  // but the parser allows it.
+  check_url("user:1243^&^:pw@host:423", null, "user", "1243^&^:pw", "host",
+            "423", null, false);
+  check_url("user:1243^&^:pw@host:423/Foo.bar:90087", null, "user",
+            "1243^&^:pw", "host", "423", "Foo.bar:90087", false);
+  check_url("user:1243^&^:pw@host:423/Foo.bar:90087@somewhere", null, "user",
+            "1243^&^:pw", "host", "423", "Foo.bar:90087@somewhere", false);
+
+  check_url("[::1]:amqp", null, null, null, "::1", "amqp", null);
+  check_url("user@[::1]", null, "user", null, "::1", null, null);
+  check_url("user@[::1]:amqp", null, "user", null, "::1", "amqp", null);
+
+  // Can't round-trip passwords with ':', not strictly legal
+  // but the parser allows it.
+  check_url("user:1243^&^:pw@[::1]:amqp", null, "user", "1243^&^:pw", "::1",
+            "amqp", null, false);
+  check_url("user:1243^&^:pw@[::1]:amqp/Foo.bar:90087", null, "user",
+            "1243^&^:pw", "::1", "amqp", "Foo.bar:90087", false);
+  check_url("user:1243^&^:pw@[::1:amqp/Foo.bar:90087", null, "user",
+            "1243^&^:pw", "[::1", "amqp", "Foo.bar:90087", false);
+  check_url("user:1243^&^:pw@::1]:amqp/Foo.bar:90087", null, "user",
+            "1243^&^:pw", "::1]", "amqp", "Foo.bar:90087", false);
+
+  check_url("amqp://user@[::1]", "amqp", "user", null, "::1", null, null);
+  check_url("amqp://user@[::1]:amqp", "amqp", "user", null, "::1", "amqp",
+            null);
+  check_url("amqp://user@[1234:52:0:1260:f2de:f1ff:fe59:8f87]:amqp", "amqp",
+            "user", null, "1234:52:0:1260:f2de:f1ff:fe59:8f87", "amqp", null);
+
+  // Can't round-trip passwords with ':', not strictly legal
+  // but the parser allows it.
+  check_url("amqp://user:1243^&^:pw@[::1]:amqp", "amqp", "user", "1243^&^:pw",
+            "::1", "amqp", null, false);
+  check_url("amqp://user:1243^&^:pw@[::1]:amqp/Foo.bar:90087", "amqp", "user",
+            "1243^&^:pw", "::1", "amqp", "Foo.bar:90087", false);
+
+  check_url("amqp://host", "amqp", null, null, "host", null, null);
+  check_url("amqp://user@host", "amqp", "user", null, "host", null, null);
+  check_url("amqp://user@host/path:%", "amqp", "user", null, "host", null,
+            "path:%");
+  check_url("amqp://user@host:5674/path:%", "amqp", "user", null, "host",
+            "5674", "path:%");
+  check_url("amqp://user@host/path:%", "amqp", "user", null, "host", null,
+            "path:%");
+  check_url("amqp://bigbird@host/queue@host", "amqp", "bigbird", null, "host",
+            null, "queue@host");
+  check_url("amqp://host/queue@host", "amqp", null, null, "host", null,
+            "queue@host");
+  check_url("amqp://host:9765/queue@host", "amqp", null, null, "host", "9765",
+            "queue@host");
+  check_url("user:pass%2fword@host", null, "user", "pass/word", "host", null,
+            null, false);
+  check_url("user:pass%2Fword@host", null, "user", "pass/word", "host", null,
+            null);
+  // Can't round-trip passwords with lowercase hex encoding
+  check_url("us%2fer:password@host", null, "us/er", "password", "host", null,
+            null, false);
+  check_url("us%2Fer:password@host", null, "us/er", "password", "host", null,
+            null);
+  // Can't round-trip passwords with lowercase hex encoding
+  check_url("user:pass%2fword%@host", null, "user", "pass/word%", "host", null,
+            null, false);
+  check_url("localhost/temp-queue://"
+            "ID:ganymede-36663-1408448359876-2:123:0",
+            null, null, null, "localhost", null,
+            "temp-queue://ID:ganymede-36663-1408448359876-2:123:0");
+  check_url("/temp-queue://ID:ganymede-36663-1408448359876-2:123:0", null, null,
+            null, null, null,
+            "temp-queue://ID:ganymede-36663-1408448359876-2:123:0");
+  check_url("amqp://localhost/temp-queue://"
+            "ID:ganymede-36663-1408448359876-2:123:0",
+            "amqp", null, null, "localhost", null,
+            "temp-queue://ID:ganymede-36663-1408448359876-2:123:0");
+  // PROTON-995
+  check_url("amqps://%40user%2F%3A:%40pass%2F%3A@example.net/"
+            "some_topic",
+            "amqps", "@user/:", "@pass/:", "example.net", null, "some_topic");
+  check_url("amqps://user%2F%3A=:pass%2F%3A=@example.net/some_topic", "amqps",
+            "user/:=", "pass/:=", "example.net", null, "some_topic");
+  // Really perverse url
+  check_url("://:@://:", "", "", "", null, "", "/:");
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 794fcbe..794eb29 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -282,7 +282,6 @@ add_cpp_test(map_test)
 add_cpp_test(scalar_test)
 add_cpp_test(value_test)
 add_cpp_test(container_test)
-add_cpp_test(url_test)
 add_cpp_test(reconnect_test)
 add_cpp_test(link_test)
 if (ENABLE_JSONCPP)
@@ -292,3 +291,22 @@ if (ENABLE_JSONCPP)
   # Test data and output directories for connect_config_test
   file(COPY  "${CMAKE_CURRENT_SOURCE_DIR}/testdata" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
 endif()
+
+# TODO aconway 2018-10-31: Catch2 tests
+# This is a simple example of a C++ test using the Catch2 framework.
+# See c/tests/ for more interesting examples.
+# Eventually all the C++ tests will migrate to Catch2.
+
+include_directories(${CMAKE_SOURCE_DIR}/tests/include)
+add_executable(cpp-test src/cpp-test.cpp src/url_test.cpp)
+target_link_libraries(cpp-test qpid-proton-cpp ${PLATFORM_LIBS})
+
+macro(add_catch_test tag)
+  add_test (
+    NAME cpp-${tag}-test
+    COMMAND ${PN_ENV_SCRIPT} -- ${test_env}
+    ${TEST_EXE_PREFIX_CMD} $<TARGET_FILE:cpp-test> "[${tag}]"
+    )
+endmacro(add_catch_test)
+
+add_catch_test(url)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/cpp/src/cpp-test.cpp
----------------------------------------------------------------------
diff --git a/cpp/src/cpp-test.cpp b/cpp/src/cpp-test.cpp
new file mode 100644
index 0000000..12a09dc
--- /dev/null
+++ b/cpp/src/cpp-test.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 CATCH_CONFIG_MAIN
+#include <catch.hpp>

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/cpp/src/url_test.cpp
----------------------------------------------------------------------
diff --git a/cpp/src/url_test.cpp b/cpp/src/url_test.cpp
index 511c8d5..90f56fb 100644
--- a/cpp/src/url_test.cpp
+++ b/cpp/src/url_test.cpp
@@ -17,82 +17,64 @@
  * under the License.
  */
 
-#include "test_bits.hpp"
+#include <catch.hpp>
 #include <proton/url.hpp>
 
 namespace {
+using proton::url;
 
-#define CHECK_URL(S, SCHEME, USER, PWD, HOST, PORT, PATH) do {   \
-        proton::url u(S);                                        \
-        ASSERT_EQUAL(SCHEME, u.scheme());                        \
-        ASSERT_EQUAL(USER, u.user());                            \
-        ASSERT_EQUAL(PWD, u.password());                         \
-        ASSERT_EQUAL(HOST, u.host());                            \
-        ASSERT_EQUAL(PORT, u.port());                            \
-        ASSERT_EQUAL(PATH, u.path());                            \
+#define CHECK_URL(U, SCHEME, USER, PWD, HOST, PORT, PATH) do {  \
+        CHECK((SCHEME) == (U).scheme());                        \
+        CHECK((USER) == (U).user());                            \
+        CHECK((PWD) == (U).password());                         \
+        CHECK((HOST) == (U).host());                            \
+        CHECK((PORT) == (U).port());                            \
+        CHECK((PATH) == (U).path());                            \
     } while(0)
 
-void parse_to_string_test() {
-    CHECK_URL("amqp://foo:xyz/path",
-              "amqp", "", "", "foo", "xyz", "path");
-    CHECK_URL("amqp://username:password@host:1234/path",
-              "amqp", "username", "password", "host", "1234", "path");
-    CHECK_URL("host:1234",
-              "amqp", "", "", "host", "1234", "");
-    CHECK_URL("host",
-              "amqp", "", "", "host", "amqp", "");
-    CHECK_URL("host/path",
-              "amqp", "", "", "host", "amqp", "path");
-    CHECK_URL("amqps://host",
-              "amqps", "", "", "host", "amqps", "");
-    CHECK_URL("/path",
-              "amqp", "", "", "localhost", "amqp", "path");
-    CHECK_URL("",
-              "amqp", "", "", "localhost", "amqp", "");
-    CHECK_URL(":1234",
-              "amqp", "", "", "localhost", "1234", "");
+TEST_CASE("parse URL","[url]") {
+    SECTION("full and defaulted") {
+        CHECK_URL(url("amqp://foo:xyz/path"),
+                  "amqp", "", "", "foo", "xyz", "path");
+        CHECK_URL(url("amqp://username:password@host:1234/path"),
+                  "amqp", "username", "password", "host", "1234", "path");
+        CHECK_URL(url("host:1234"),
+                  "amqp", "", "", "host", "1234", "");
+        CHECK_URL(url("host"),
+                  "amqp", "", "", "host", "amqp", "");
+        CHECK_URL(url("host/path"),
+                  "amqp", "", "", "host", "amqp", "path");
+        CHECK_URL(url("amqps://host"),
+                  "amqps", "", "", "host", "amqps", "");
+        CHECK_URL(url("/path"),
+                  "amqp", "", "", "localhost", "amqp", "path");
+        CHECK_URL(url(""),
+                  "amqp", "", "", "localhost", "amqp", "");
+        CHECK_URL(url(":1234"),
+                  "amqp", "", "", "localhost", "1234", "");
+    }
+    SECTION("starting with //") {
+        CHECK_URL(url("//username:password@host:1234/path"),
+                  "amqp", "username", "password", "host", "1234", "path");
+        CHECK_URL(url("//host:port/path"),
+                  "amqp", "", "", "host", "port", "path");
+        CHECK_URL(url("//host"),
+                  "amqp", "", "", "host", "amqp", "");
+        CHECK_URL(url("//:port"),
+                  "amqp", "", "", "localhost", "port", "");
+        CHECK_URL(url("//:0"),
+                  "amqp", "", "", "localhost", "0", "");
+    }
+    SECTION("no defaults") {
+        CHECK_URL(url("", false),
+                  "", "", "", "", "", "");
+        CHECK_URL(url("//:", false),
+                  "", "", "", "", "", "");
+        CHECK_URL(url("//:0", false),
+                  "", "", "", "", "0", "");
+        CHECK_URL(url("//h:", false),
+                  "", "", "", "h", "", "");
+    }
 }
 
-void parse_slash_slash() {
-    CHECK_URL("//username:password@host:1234/path",
-              "amqp", "username", "password", "host", "1234", "path");
-    CHECK_URL("//host:port/path",
-              "amqp", "", "", "host", "port", "path");
-    CHECK_URL("//host",
-              "amqp", "", "", "host", "amqp", "");
-    CHECK_URL("//:port",
-              "amqp", "", "", "localhost", "port", "");
-    CHECK_URL("//:0",
-              "amqp", "", "", "localhost", "0", "");
-}
-
-#define CHECK_URL_NODEFAULT(S, SCHEME, USER, PWD, HOST, PORT, PATH) do {   \
-        proton::url u(S, false);                                        \
-        ASSERT_EQUAL(SCHEME, u.scheme());                               \
-        ASSERT_EQUAL(USER, u.user());                                   \
-        ASSERT_EQUAL(PWD, u.password());                                \
-        ASSERT_EQUAL(HOST, u.host());                                   \
-        ASSERT_EQUAL(PORT, u.port());                                   \
-        ASSERT_EQUAL(PATH, u.path());                                   \
-    } while(0)
-
-}
-
-void parse_nodefault() {
-    CHECK_URL_NODEFAULT("",
-                        "", "", "", "", "", "");
-    CHECK_URL_NODEFAULT("//:",
-                        "", "", "", "", "", "");
-    CHECK_URL_NODEFAULT("//:0",
-                        "", "", "", "", "0", "");
-    CHECK_URL_NODEFAULT("//h:",
-                        "", "", "", "h", "", "");
-}
-
-int main(int, char**) {
-    int failed = 0;
-    RUN_TEST(failed, parse_to_string_test());
-    RUN_TEST(failed, parse_slash_slash());
-    RUN_TEST(failed, parse_nodefault());
-    return failed;
-}
+} // namespace


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org