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 "e(std::ostream &o, const char *s) {
+ return s ? (o << '"' << s << '"') : (o << "null");
+}
+
+std::ostream &operator<<(std::ostream &o, const pn_condition_t &const_cond) {
+ pn_condition_t *cond = const_cast<pn_condition_t *>(&const_cond);
+ o << "pn_condition{";
+ if (pn_condition_is_set(cond)) {
+ quote(o, pn_condition_get_name(cond)) << ", ";
+ quote(o, pn_condition_get_description(cond));
+ }
+ o << "}";
+ return o;
+}
+
+std::ostream &operator<<(std::ostream &o, const pn_error_t &const_err) {
+ pn_error_t *err = const_cast<pn_error_t *>(&const_err);
+ o << "pn_error{" << pn_code(pn_error_code(err)) << ", ";
+ return quote(o, pn_error_text(err)) << "}";
+}
+
+namespace pn_test {
+
+std::string inspect(void *obj) {
+ auto_free<pn_string_t, pn_string_free> s(pn_string(NULL));
+ pn_inspect(obj, s);
+ return pn_string_get(s);
+}
+
+etypes make_etypes_(int first, ...) {
+ etypes v;
+ va_list ap;
+ va_start(ap, first);
+ for (int i = first; i >= 0; i = va_arg(ap, int)) {
+ v.push_back(static_cast<pn_event_type_t>(i));
+ }
+ va_end(ap);
+ return v;
+}
+
+std::ostream &operator<<(std::ostream &o, const etypes &et) {
+ return o << Catch::toString(static_cast<std::vector<pn_event_type_t> >(et));
+}
+
+pn_bytes_t pn_bytes(const std::string &s) {
+ return ::pn_bytes(s.size(), s.data());
+}
+
+void rwbytes_ensure(pn_rwbytes_t *buf, size_t size) {
+ if (buf->start == NULL || buf->size < size) {
+ buf->start = (char *)realloc(buf->start, size);
+ buf->size = size;
+ }
+}
+
+void message_decode(pn_message_t *m, pn_delivery_t *d, pn_rwbytes_t *buf) {
+ ssize_t size = pn_delivery_pending(d);
+ rwbytes_ensure(buf, size);
+ ssize_t result = pn_link_recv(pn_delivery_link(d), buf->start, size);
+ REQUIRE(size == result);
+ pn_message_clear(m);
+ if (pn_message_decode(m, buf->start, size)) FAIL(pn_message_error(m));
+}
+
+handler::handler()
+ : last_condition(pn_condition()), connection(), session(), link(), sender(),
+ receiver(), delivery(), message() {}
+
+bool handler::dispatch(pn_event_t *e) {
+ log.push_back(pn_event_type(e));
+ if (pn_event_condition(e)) {
+ pn_condition_copy(last_condition, pn_event_condition(e));
+ } else {
+ pn_condition_clear(last_condition);
+ }
+ return handle(e);
+}
+
+etypes handler::log_clear() {
+ etypes ret;
+ std::swap(ret, log);
+ return ret;
+}
+
+pn_event_type_t handler::log_last() {
+ pn_event_type_t et = log.empty() ? PN_EVENT_NONE : log.back();
+ log.clear();
+ return et;
+}
+
+driver::driver(struct handler &h) : handler(h) {
+ pn_connection_driver_init(this, NULL, NULL);
+}
+driver::~driver() { pn_connection_driver_destroy(this); }
+
+pn_event_type_t driver::run(pn_event_type_t stop) {
+ pn_event_t *e = NULL;
+ while ((e = pn_connection_driver_next_event(this))) {
+ pn_event_type_t et = pn_event_type(e);
+ if (handler.dispatch(e) || et == stop) return et;
+ }
+ return PN_EVENT_NONE;
+}
+
+driver_pair::driver_pair(handler &ch, handler &sh) : client(ch), server(sh) {
+ pn_transport_set_server(server.transport);
+}
+
+size_t driver::read(pn_connection_driver_t &src) {
+ pn_bytes_t wb = pn_connection_driver_write_buffer(&src);
+ pn_rwbytes_t rb = pn_connection_driver_read_buffer(this);
+ size_t size = rb.size < wb.size ? rb.size : wb.size;
+ if (size) {
+ std::copy(wb.start, wb.start + size, rb.start);
+ pn_connection_driver_write_done(&src, size);
+ pn_connection_driver_read_done(this, size);
+ }
+ return size;
+}
+
+pn_event_type_t driver_pair::run() {
+ pn_connection_open(client.connection); // Make sure it is open
+ size_t n = 0;
+ do {
+ pn_event_type_t et = PN_EVENT_NONE;
+ if ((et = client.run())) return et;
+ if ((et = server.run())) return et;
+ n = client.read(server) + server.read(client);
+ } while (n || pn_connection_driver_has_event(&client) ||
+ pn_connection_driver_has_event(&server));
+ return PN_EVENT_NONE;
+}
+
+std::string cond_empty::describe() const { return "is empty"; }
+
+bool cond_empty::match(const pn_condition_t &cond) const {
+ return !pn_condition_is_set(const_cast<pn_condition_t *>(&cond));
+}
+
+cond_matches::cond_matches(const std::string &name, const std::string &desc)
+ : name_(name), desc_(desc) {}
+
+std::string cond_matches::describe() const {
+ std::ostringstream o;
+ o << "matches " << Catch::toString(name_);
+ if (!desc_.empty()) o << ", " + Catch::toString(desc_);
+ return o.str();
+}
+
+bool cond_matches::match(const pn_condition_t &const_cond) const {
+ pn_condition_t *cond = const_cast<pn_condition_t *>(&const_cond);
+ const char *name = pn_condition_get_name(cond);
+ const char *desc = pn_condition_get_description(cond);
+ return pn_condition_is_set(cond) && name && name_ == name &&
+ (desc_.empty() || (desc && Catch::contains(desc, desc_)));
+}
+
+std::string error_empty::describe() const { return "is empty"; }
+
+bool error_empty::match(const pn_error_t &err) const {
+ return !pn_error_code(const_cast<pn_error_t *>(&err));
+}
+
+error_matches::error_matches(int code, const std::string &desc)
+ : code_(code), desc_(desc) {}
+
+std::string error_matches::describe() const {
+ std::ostringstream o;
+ o << "matches " << pn_code(code_);
+ if (!desc_.empty()) o << ", " + Catch::toString(desc_);
+ return o.str();
+}
+
+bool error_matches::match(const pn_error_t &const_err) const {
+ pn_error_t *err = const_cast<pn_error_t *>(&const_err);
+ int code = pn_error_code(err);
+ const char *desc = pn_error_text(err);
+ return code_ == code &&
+ (desc_.empty() || (desc && Catch::contains(desc, desc_)));
+}
+
+} // namespace pn_test
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0bdba37d/c/tests/pn_test.hpp
----------------------------------------------------------------------
diff --git a/c/tests/pn_test.hpp b/c/tests/pn_test.hpp
new file mode 100644
index 0000000..bbfa3d5
--- /dev/null
+++ b/c/tests/pn_test.hpp
@@ -0,0 +1,193 @@
+#ifndef TESTS_PN_TEST_HPP
+#define TESTS_PN_TEST_HPP
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/// @file
+///
+/// Wrappers and Catch2 extensions to simplify testing the proton-C library.
+
+#include <catch_extra.hpp>
+
+#include <proton/condition.h>
+#include <proton/connection_driver.h>
+#include <proton/event.h>
+#include <proton/message.h>
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+// String form of C pn_ types used in tests, must be in global C namespace
+// Note objects are passed by reference, not pointer.
+std::ostream &operator<<(std::ostream &, pn_event_type_t);
+std::ostream &operator<<(std::ostream &, const pn_condition_t &);
+std::ostream &operator<<(std::ostream &, const pn_error_t &);
+
+namespace pn_test {
+
+// Holder for T*, calls function Free() in dtor. Not copyable.
+template <class T, void (*Free)(T *)> class auto_free {
+ T *ptr_;
+ auto_free &operator=(auto_free &x);
+ auto_free(auto_free &x);
+
+public:
+ auto_free(T *p = 0) : ptr_(p) {}
+ ~auto_free() { Free(ptr_); }
+ T *get() const { return ptr_; }
+ operator T *() const { return ptr_; }
+};
+
+// pn_free() works for some, but not all pn_xxx_t* types.
+// Add typed pn_string_free() so we can be consistent and safe.
+inline void pn_string_free(pn_string_t *s) { pn_free(s); }
+
+// Call pn_inspect(), return std::string
+std::string inspect(void *);
+
+// List of pn_event_type_t
+typedef std::vector<pn_event_type_t> etypes;
+std::ostream &operator<<(std::ostream &o, const etypes &et);
+
+// Workaround for lack of list initializers in C++03.
+// Use ETYPES macro, don't call make_etypes_ directly
+etypes make_etypes_(int first, ...);
+#define ETYPES(...) (make_etypes_(__VA_ARGS__, -1))
+
+/// Make a pn_bytes_t from a std::string
+pn_bytes_t pn_bytes(const std::string &s);
+
+/// Ensure buf has at least size bytes, use realloc if need be
+void rwbytes_ensure(pn_rwbytes_t *buf, size_t size);
+
+/// Decode message from delivery into buf, expand buf as needed.
+void message_decode(pn_message_t *m, pn_delivery_t *d, pn_rwbytes_t *buf);
+
+// A test handler that logs the type of each event handled, and has
+// slots to store all of the basic proton types for ad-hoc use in
+// tests. Subclass and override the handle() method.
+struct handler {
+ etypes log; // Log of events
+ auto_free<pn_condition_t, pn_condition_free>
+ last_condition; // Condition of last event
+
+ // Slots to save proton objects for use outside the handler.
+ pn_listener_t *listener;
+ pn_connection_t *connection;
+ pn_session_t *session;
+ pn_link_t *link;
+ pn_link_t *sender;
+ pn_link_t *receiver;
+ pn_delivery_t *delivery;
+ pn_message_t *message;
+
+ handler();
+
+ /// dispatch an event: log its type then call handle()
+ /// Returns the value of handle()
+ bool dispatch(pn_event_t *e);
+
+ // Return the current log contents, clear the log.
+ etypes log_clear();
+
+ // Return the last event in the log, clear the log.
+ pn_event_type_t log_last();
+
+protected:
+ // Override this function to handle events.
+ //
+ // Return true to stop dispatching and return control to the test function,
+ // false to continue processing.
+ virtual bool handle(pn_event_t *e) { return false; }
+};
+
+// A pn_connection_driver_t that dispatches to a pn_test::handler
+//
+// driver::run() dispatches events to the handler, but returns if the handler
+// returns true, or if a specific event type is handled. Test functions can
+// alternate between letting the handler run and checking state or calling
+// functions on proton objects in the test function directly. Handlers can
+// automate uninteresting work, the test function can make checks that are
+// clearly located in the flow of the test logic.
+struct driver : public ::pn_connection_driver_t {
+ struct handler &handler;
+
+ driver(struct handler &h);
+ ~driver();
+
+ // Dispatch events till a handler returns true, the `stop` event is handled,
+ // or there are no more events
+ // Returns the last event handled or PN_EVENT_NONE if none were.
+ pn_event_type_t run(pn_event_type_t stop = PN_EVENT_NONE);
+
+ // Transfer available data from src write buffer to this read-buffer and
+ // update both drivers. Return size of data transferred.
+ size_t read(pn_connection_driver_t &src);
+};
+
+// A client/server pair drivers. run() simulates a connection in memory.
+struct driver_pair {
+ driver client, server;
+
+ // Associate handlers with drivers. Sets server.transport to server mode
+ driver_pair(handler &ch, handler &sh);
+
+ // Run the drivers until a handle returns true or there is nothing left to
+ // do. Opens the client.connection() if not already open.
+ // Return the last event handled or PN_EVENT_NONE
+ pn_event_type_t run();
+};
+
+// Matches for use with Catch macros CHECK_THAT and REQUIRE_THAT.
+// Check pn_condition_t and pn_error_t, failed checks report code, name,
+// description etc.
+
+struct cond_empty : public Catch::MatcherBase<pn_condition_t> {
+ std::string describe() const CATCH_OVERRIDE;
+ bool match(const pn_condition_t &cond) const CATCH_OVERRIDE;
+};
+
+class cond_matches : public Catch::MatcherBase<pn_condition_t> {
+ std::string name_, desc_;
+
+public:
+ cond_matches(const std::string &name, const std::string &desc_contains = "");
+ std::string describe() const CATCH_OVERRIDE;
+ bool match(const pn_condition_t &cond) const CATCH_OVERRIDE;
+};
+
+struct error_empty : public Catch::MatcherBase<pn_error_t> {
+ std::string describe() const CATCH_OVERRIDE;
+ bool match(const pn_error_t &) const CATCH_OVERRIDE;
+};
+
+class error_matches : public Catch::MatcherBase<pn_error_t> {
+ int code_;
+ std::string desc_;
+
+public:
+ error_matches(int code, const std::string &desc_contains = "");
+ std::string describe() const CATCH_OVERRIDE;
+ bool match(const pn_error_t &) const CATCH_OVERRIDE;
+};
+
+} // namespace pn_test
+
+#endif // TESTS_PN_TEST_HPP
---------------------------------------------------------------------
To unsubscribe, e-mail: 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