NO-JIRA: c++ connection_driver_test - interrupt hung tests

connection_driver_test throws an error after 1000 iterations with no events
to prevent faulty tests from hanging.


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

Branch: refs/heads/PROTON-1488
Commit: 876daa72de9790f5e2bdf2423075401d80123cfb
Parents: d25089b
Author: Alan Conway <[email protected]>
Authored: Thu May 25 13:19:50 2017 -0400
Committer: Alan Conway <[email protected]>
Committed: Thu May 25 13:27:24 2017 -0400

----------------------------------------------------------------------
 .../cpp/include/proton/io/connection_driver.hpp |   3 +
 .../bindings/cpp/src/connection_driver_test.cpp | 122 +++++++++++--------
 proton-c/bindings/cpp/src/include/test_bits.hpp |  23 +++-
 .../bindings/cpp/src/io/connection_driver.cpp   |   4 +
 4 files changed, 102 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/876daa72/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp 
b/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp
index 759b1fc..56deb00 100644
--- a/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp
+++ b/proton-c/bindings/cpp/include/proton/io/connection_driver.hpp
@@ -176,6 +176,9 @@ PN_CPP_CLASS_EXTERN connection_driver {
     ///
     PN_CPP_EXTERN void disconnected(const error_condition& = 
error_condition());
 
+    /// There are events to be dispatched by dispatch()
+    PN_CPP_EXTERN bool has_events() const;
+
     /// Dispatch all available events and call the corresponding \ref 
messaging_handler methods.
     ///
     /// Returns true if the engine is still active, false if it is finished and

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/876daa72/proton-c/bindings/cpp/src/connection_driver_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/connection_driver_test.cpp 
b/proton-c/bindings/cpp/src/connection_driver_test.cpp
index 760ac55..f179601 100644
--- a/proton-c/bindings/cpp/src/connection_driver_test.cpp
+++ b/proton-c/bindings/cpp/src/connection_driver_test.cpp
@@ -46,13 +46,17 @@ using proton::io::mutable_buffer;
 
 typedef std::deque<char> byte_stream;
 
+static const int MAX_SPIN = 1000; // Give up after 1000 event-less dispatches
+
 /// In memory connection_driver that reads and writes from byte_streams
 struct in_memory_driver : public connection_driver {
 
     byte_stream& reads;
     byte_stream& writes;
+    int spinning;
 
-    in_memory_driver(byte_stream& rd, byte_stream& wr) : reads(rd), writes(wr) 
{}
+    in_memory_driver(byte_stream& rd, byte_stream& wr) :
+        reads(rd), writes(wr), spinning(0) {}
 
     void do_read() {
         mutable_buffer rbuf = read_buffer();
@@ -74,11 +78,19 @@ struct in_memory_driver : public connection_driver {
         }
     }
 
+    void check_idle() {
+        spinning = has_events() ? 0 : spinning+1;
+        if (spinning > MAX_SPIN)
+            throw test::error("no activity, interrupting test");
+    }
+
     void process() {
+        check_idle();
         if (!dispatch())
-            throw std::runtime_error("unexpected close: 
"+connection().error().what());
+            throw test::error("unexpected close: 
"+connection().error().what());
         do_read();
         do_write();
+        check_idle();
         dispatch();
     }
 };
@@ -145,50 +157,50 @@ struct namer : public io::link_namer {
 
 void test_driver_link_id() {
     record_handler ha, hb;
-    driver_pair e(ha, hb);
-    e.a.connect(ha);
-    e.b.accept(hb);
+    driver_pair d(ha, hb);
+    d.a.connect(ha);
+    d.b.accept(hb);
 
     namer na('x');
     namer nb('b');
-    connection ca = e.a.connection();
-    connection cb = e.b.connection();
+    connection ca = d.a.connection();
+    connection cb = d.b.connection();
     set_link_namer(ca, na);
     set_link_namer(cb, nb);
 
-    e.b.connection().open();
+    d.b.connection().open();
 
-    e.a.connection().open_sender("foo");
-    while (ha.senders.empty() || hb.receivers.empty()) e.process();
+    d.a.connection().open_sender("foo");
+    while (ha.senders.empty() || hb.receivers.empty()) d.process();
     sender s = quick_pop(ha.senders);
     ASSERT_EQUAL("x", s.name());
 
     ASSERT_EQUAL("x", quick_pop(hb.receivers).name());
 
-    e.a.connection().open_receiver("bar");
-    while (ha.receivers.empty() || hb.senders.empty()) e.process();
+    d.a.connection().open_receiver("bar");
+    while (ha.receivers.empty() || hb.senders.empty()) d.process();
     ASSERT_EQUAL("y", quick_pop(ha.receivers).name());
     ASSERT_EQUAL("y", quick_pop(hb.senders).name());
 
-    e.b.connection().open_receiver("");
-    while (ha.senders.empty() || hb.receivers.empty()) e.process();
+    d.b.connection().open_receiver("");
+    while (ha.senders.empty() || hb.receivers.empty()) d.process();
     ASSERT_EQUAL("b", quick_pop(ha.senders).name());
     ASSERT_EQUAL("b", quick_pop(hb.receivers).name());
 }
 
 void test_endpoint_close() {
     record_handler ha, hb;
-    driver_pair e(ha, hb);
-    e.a.connection().open_sender("x");
-    e.a.connection().open_receiver("y");
+    driver_pair d(ha, hb);
+    d.a.connection().open_sender("x");
+    d.a.connection().open_receiver("y");
     while (ha.senders.size()+ha.receivers.size() < 2 ||
-           hb.senders.size()+hb.receivers.size() < 2) e.process();
+           hb.senders.size()+hb.receivers.size() < 2) d.process();
     proton::link ax = quick_pop(ha.senders), ay = quick_pop(ha.receivers);
     proton::link bx = quick_pop(hb.receivers), by = quick_pop(hb.senders);
 
     // Close a link
     ax.close(proton::error_condition("err", "foo bar"));
-    while (!bx.closed()) e.process();
+    while (!bx.closed()) d.process();
     proton::error_condition c = bx.error();
     ASSERT_EQUAL("err", c.name());
     ASSERT_EQUAL("foo bar", c.description());
@@ -196,13 +208,13 @@ void test_endpoint_close() {
 
     // Close a link with an empty condition
     ay.close(proton::error_condition());
-    while (!by.closed()) e.process();
+    while (!by.closed()) d.process();
     ASSERT(by.error().empty());
 
     // Close a connection
-    connection ca = e.a.connection(), cb = e.b.connection();
+    connection ca = d.a.connection(), cb = d.b.connection();
     ca.close(proton::error_condition("conn", "bad connection"));
-    while (!cb.closed()) e.process();
+    while (!cb.closed()) d.process();
     ASSERT_EQUAL("conn: bad connection", cb.error().what());
     ASSERT_EQUAL(1u, hb.connection_errors.size());
     ASSERT_EQUAL("conn: bad connection", hb.connection_errors.front());
@@ -211,63 +223,74 @@ void test_endpoint_close() {
 void test_driver_disconnected() {
     // driver.disconnected() aborts the connection and calls the local 
on_transport_error()
     record_handler ha, hb;
-    driver_pair e(ha, hb);
-    e.a.connect(ha);
-    e.b.accept(hb);
-    while (!e.a.connection().active() || !e.b.connection().active())
-        e.process();
+    driver_pair d(ha, hb);
+    d.a.connect(ha);
+    d.b.accept(hb);
+    while (!d.a.connection().active() || !d.b.connection().active())
+        d.process();
 
     // Close a with an error condition. The AMQP connection is still open.
-    e.a.disconnected(proton::error_condition("oops", "driver failure"));
-    ASSERT(!e.a.dispatch());
-    ASSERT(!e.a.connection().closed());
-    ASSERT(e.a.connection().error().empty());
+    d.a.disconnected(proton::error_condition("oops", "driver failure"));
+    ASSERT(!d.a.dispatch());
+    ASSERT(!d.a.connection().closed());
+    ASSERT(d.a.connection().error().empty());
     ASSERT_EQUAL(0u, ha.connection_errors.size());
-    ASSERT_EQUAL("oops: driver failure", e.a.transport().error().what());
+    ASSERT_EQUAL("oops: driver failure", d.a.transport().error().what());
     ASSERT_EQUAL(1u, ha.transport_errors.size());
     ASSERT_EQUAL("oops: driver failure", ha.transport_errors.front());
 
     // In a real app the IO code would detect the abort and do this:
-    e.b.disconnected(proton::error_condition("broken", "it broke"));
-    ASSERT(!e.b.dispatch());
-    ASSERT(!e.b.connection().closed());
-    ASSERT(e.b.connection().error().empty());
+    d.b.disconnected(proton::error_condition("broken", "it broke"));
+    ASSERT(!d.b.dispatch());
+    ASSERT(!d.b.connection().closed());
+    ASSERT(d.b.connection().error().empty());
     ASSERT_EQUAL(0u, hb.connection_errors.size());
     // Proton-C adds (connection aborted) if transport closes too early,
     // and provides a default message if there is no user message.
-    ASSERT_EQUAL("broken: it broke (connection aborted)", 
e.b.transport().error().what());
+    ASSERT_EQUAL("broken: it broke (connection aborted)", 
d.b.transport().error().what());
     ASSERT_EQUAL(1u, hb.transport_errors.size());
     ASSERT_EQUAL("broken: it broke (connection aborted)", 
hb.transport_errors.front());
 }
 
 void test_no_container() {
     // An driver with no container should throw, not crash.
-    connection_driver e;
+    connection_driver d;
     try {
-        e.connection().container();
+        d.connection().container();
         FAIL("expected error");
     } catch (proton::error) {}
 }
 
+void test_spin_interrupt() {
+    // Check the test framework interrupts a spinning driver pair with nothing 
to do.
+    record_handler ha, hb;
+    driver_pair d(ha, hb);
+    try {
+        while (true)
+            d.process();
+        FAIL("expected exception");
+    } catch (test::error) {}
+}
+
 void test_link_filters() {
     // Propagation of link properties
     record_handler ha, hb;
-    driver_pair e(ha, hb);
+    driver_pair d(ha, hb);
 
     source_options opts;
     source::filter_map f;
     f.put("xx", "xxx");
     ASSERT_EQUAL(1U, f.size());
-    e.a.connection().open_sender("x", 
sender_options().source(source_options().filters(f)));
+    d.a.connection().open_sender("x", 
sender_options().source(source_options().filters(f)));
 
     f.clear();
     f.put("yy", "yyy");
     ASSERT_EQUAL(1U, f.size());
-    e.a.connection().open_receiver("y", 
receiver_options().source(source_options().filters(f)));
+    d.a.connection().open_receiver("y", 
receiver_options().source(source_options().filters(f)));
 
     while (ha.senders.size()+ha.receivers.size() < 2 ||
            hb.senders.size()+hb.receivers.size() < 2)
-        e.process();
+        d.process();
 
     proton::sender ax = quick_pop(ha.senders);
     proton::receiver ay = quick_pop(ha.receivers);
@@ -287,12 +310,13 @@ void test_link_filters() {
 
 }
 
-int main(int, char**) {
+int main(int argc, char** argv) {
     int failed = 0;
-    RUN_TEST(failed, test_link_filters());
-    RUN_TEST(failed, test_driver_link_id());
-    RUN_TEST(failed, test_endpoint_close());
-    RUN_TEST(failed, test_driver_disconnected());
-    RUN_TEST(failed, test_no_container());
+    RUN_ARGV_TEST(failed, test_link_filters());
+    RUN_ARGV_TEST(failed, test_driver_link_id());
+    RUN_ARGV_TEST(failed, test_endpoint_close());
+    RUN_ARGV_TEST(failed, test_driver_disconnected());
+    RUN_ARGV_TEST(failed, test_no_container());
+    RUN_ARGV_TEST(failed, test_spin_interrupt());
     return failed;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/876daa72/proton-c/bindings/cpp/src/include/test_bits.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/include/test_bits.hpp 
b/proton-c/bindings/cpp/src/include/test_bits.hpp
index 79a0a17..4e019f2 100644
--- a/proton-c/bindings/cpp/src/include/test_bits.hpp
+++ b/proton-c/bindings/cpp/src/include/test_bits.hpp
@@ -26,12 +26,17 @@
 #include <iostream>
 #include <iterator>
 #include <sstream>
+#include <cstring>
 #include <math.h>
 
 namespace test {
 
 struct fail : public std::logic_error {
-    fail(const std::string& what) : logic_error(what) {}
+    explicit fail(const std::string& what) : logic_error(what) {}
+};
+
+struct error : public std::logic_error {
+    explicit error(const std::string& what) : logic_error(what) {}
 };
 
 template <class T, class U>
@@ -59,6 +64,7 @@ inline void assert_equalish(T want, T got, T delta, const 
std::string& what)
 #define RUN_TEST(BAD_COUNT, TEST)                                       \
     do {                                                                \
         try {                                                           \
+            std::cout << "TEST: " << #TEST << std::endl;                \
             TEST;                                                       \
             break;                                                      \
         } catch(const test::fail& e) {                                        \
@@ -69,6 +75,21 @@ inline void assert_equalish(T want, T got, T delta, const 
std::string& what)
             ++BAD_COUNT;                                                \
     } while(0)
 
+/* Like RUN_TEST but only if one of the argv strings is found in the test EXPR 
*/
+#define RUN_ARGV_TEST(BAD_COUNT, EXPR) do {     \
+    if (argc == 1) {                            \
+      RUN_TEST(BAD_COUNT, EXPR);                \
+    } else {                                    \
+      for (int i = 1; i < argc; ++i) {          \
+        if (strstr(#EXPR, argv[i])) {           \
+            RUN_TEST(BAD_COUNT, EXPR);          \
+          break;                                \
+        }                                       \
+      }                                         \
+    }                                           \
+  } while(0)
+
+
 template<class T> std::string str(const T& x) {
     std::ostringstream s; s << std::boolalpha << x; return s.str();
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/876daa72/proton-c/bindings/cpp/src/io/connection_driver.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/io/connection_driver.cpp 
b/proton-c/bindings/cpp/src/io/connection_driver.cpp
index c00ccf4..da8c2a4 100644
--- a/proton-c/bindings/cpp/src/io/connection_driver.cpp
+++ b/proton-c/bindings/cpp/src/io/connection_driver.cpp
@@ -98,6 +98,10 @@ void connection_driver::accept(const connection_options& 
opts) {
     configure(all, true);
 }
 
+bool connection_driver::has_events() const {
+    return driver_.collector && pn_collector_peek(driver_.collector);
+}
+
 bool connection_driver::dispatch() {
     pn_event_t* c_event;
     while ((c_event = pn_connection_driver_next_event(&driver_)) != NULL) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to