summaryrefslogtreecommitdiff
path: root/cpp/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/tests')
-rw-r--r--cpp/src/tests/.valgrind.supp95
-rw-r--r--cpp/src/tests/AccumulatedAckTest.cpp13
-rw-r--r--cpp/src/tests/Address.cpp98
-rw-r--r--cpp/src/tests/Array.cpp11
-rw-r--r--cpp/src/tests/AsyncCompletion.cpp120
-rw-r--r--cpp/src/tests/AtomicValue.cpp5
-rw-r--r--cpp/src/tests/BasicP2PTest.cpp66
-rw-r--r--cpp/src/tests/BasicPubSubTest.cpp121
-rw-r--r--cpp/src/tests/Blob.cpp128
-rw-r--r--cpp/src/tests/BrokerFixture.h57
-rw-r--r--cpp/src/tests/CMakeLists.txt345
-rw-r--r--cpp/src/tests/ClientMessage.cpp (renamed from cpp/src/tests/BasicPubSubTest.h)43
-rw-r--r--cpp/src/tests/ClientMessageTest.cpp51
-rw-r--r--cpp/src/tests/ClientSessionTest.cpp497
-rw-r--r--cpp/src/tests/ClusterFailover.cpp68
-rw-r--r--cpp/src/tests/ClusterFixture.cpp160
-rw-r--r--cpp/src/tests/ClusterFixture.h115
-rw-r--r--cpp/src/tests/ConnectionOptions.h19
-rw-r--r--cpp/src/tests/ConsoleTest.cpp46
-rw-r--r--cpp/src/tests/DeliveryRecordTest.cpp14
-rw-r--r--cpp/src/tests/DispatcherTest.cpp173
-rw-r--r--cpp/src/tests/DtxWorkRecordTest.cpp18
-rw-r--r--cpp/src/tests/ExchangeTest.cpp159
-rw-r--r--cpp/src/tests/FieldTable.cpp160
-rw-r--r--cpp/src/tests/FieldValue.cpp21
-rw-r--r--cpp/src/tests/ForkedBroker.cpp131
-rw-r--r--cpp/src/tests/ForkedBroker.h76
-rw-r--r--cpp/src/tests/Frame.cpp13
-rw-r--r--cpp/src/tests/FrameDecoder.cpp78
-rw-r--r--cpp/src/tests/FramingTest.cpp29
-rw-r--r--cpp/src/tests/HeaderTest.cpp24
-rw-r--r--cpp/src/tests/HeadersExchangeTest.cpp31
-rw-r--r--cpp/src/tests/IncompleteMessageList.cpp20
-rw-r--r--cpp/src/tests/InitialStatusMap.cpp248
-rw-r--r--cpp/src/tests/InlineAllocator.cpp13
-rw-r--r--cpp/src/tests/InlineVector.cpp15
-rw-r--r--cpp/src/tests/Makefile.am251
-rw-r--r--cpp/src/tests/ManagementTest.cpp115
-rw-r--r--cpp/src/tests/MessageBuilderTest.cpp110
-rw-r--r--cpp/src/tests/MessageReplayTracker.cpp102
-rw-r--r--cpp/src/tests/MessageTest.cpp28
-rw-r--r--cpp/src/tests/MessageUtils.h27
-rw-r--r--cpp/src/tests/MessagingSessionTests.cpp778
-rw-r--r--cpp/src/tests/MockConnectionInputHandler.h100
-rw-r--r--cpp/src/tests/PartialFailure.cpp291
-rw-r--r--cpp/src/tests/PollableCondition.cpp109
-rw-r--r--cpp/src/tests/PollerTest.cpp135
-rw-r--r--cpp/src/tests/ProxyTest.cpp56
-rw-r--r--cpp/src/tests/QueueEvents.cpp238
-rw-r--r--cpp/src/tests/QueueOptionsTest.cpp102
-rw-r--r--cpp/src/tests/QueuePolicyTest.cpp297
-rw-r--r--cpp/src/tests/QueueRegistryTest.cpp17
-rw-r--r--cpp/src/tests/QueueTest.cpp981
-rw-r--r--cpp/src/tests/RangeSet.cpp9
-rw-r--r--cpp/src/tests/RateFlowcontrolTest.cpp71
-rw-r--r--cpp/src/tests/RefCounted.cpp5
-rw-r--r--cpp/src/tests/ReplicationTest.cpp141
-rw-r--r--cpp/src/tests/RetryList.cpp111
-rw-r--r--cpp/src/tests/SequenceNumberTest.cpp12
-rw-r--r--cpp/src/tests/SequenceSet.cpp9
-rw-r--r--cpp/src/tests/SessionState.cpp34
-rw-r--r--cpp/src/tests/Shlib.cpp20
-rw-r--r--cpp/src/tests/SimpleTestCaseBase.cpp87
-rw-r--r--cpp/src/tests/SimpleTestCaseBase.h89
-rw-r--r--cpp/src/tests/SocketProxy.h128
-rw-r--r--cpp/src/tests/StoreStatus.cpp109
-rw-r--r--cpp/src/tests/TestCase.h64
-rw-r--r--cpp/src/tests/TestMessageStore.h15
-rw-r--r--cpp/src/tests/TestOptions.h6
-rw-r--r--cpp/src/tests/TimerTest.cpp26
-rw-r--r--cpp/src/tests/TopicExchangeTest.cpp210
-rw-r--r--cpp/src/tests/TxBufferTest.cpp9
-rw-r--r--cpp/src/tests/TxMocks.h50
-rw-r--r--cpp/src/tests/TxPublishTest.cpp31
-rw-r--r--cpp/src/tests/Url.cpp58
-rw-r--r--cpp/src/tests/Uuid.cpp7
-rw-r--r--cpp/src/tests/Variant.cpp178
-rw-r--r--cpp/src/tests/XmlClientSessionTest.cpp164
-rwxr-xr-xcpp/src/tests/acl.py884
-rwxr-xr-xcpp/src/tests/ais_check57
-rw-r--r--cpp/src/tests/allSegmentTypes.h128
-rw-r--r--cpp/src/tests/amqp_0_10/Map.cpp2
-rw-r--r--cpp/src/tests/amqp_0_10/ProxyTemplate.cpp2
-rw-r--r--cpp/src/tests/amqp_0_10/apply.cpp2
-rw-r--r--cpp/src/tests/amqp_0_10/handlers.cpp2
-rw-r--r--cpp/src/tests/amqp_0_10/serialize.cpp6
-rw-r--r--cpp/src/tests/background.ps155
-rwxr-xr-xcpp/src/tests/benchmark95
-rwxr-xr-xcpp/src/tests/cli_tests.py190
-rw-r--r--cpp/src/tests/client_test.cpp54
-rw-r--r--cpp/src/tests/cluster.cmake86
-rw-r--r--cpp/src/tests/cluster.mk81
-rwxr-xr-xcpp/src/tests/cluster_python_tests5
-rw-r--r--cpp/src/tests/cluster_python_tests_failing.txt31
-rwxr-xr-xcpp/src/tests/cluster_read_credit27
-rw-r--r--cpp/src/tests/cluster_test.cpp1183
-rw-r--r--cpp/src/tests/cluster_test_scripts/README.txt20
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/cluster_check17
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/cluster_start36
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/cluster_stop18
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/config_example.sh25
-rwxr-xr-xcpp/src/tests/cluster_test_scripts/perftest34
-rw-r--r--cpp/src/tests/cluster_tests.fail2
-rwxr-xr-xcpp/src/tests/cluster_tests.py233
-rwxr-xr-xcpp/src/tests/clustered_replication_test110
-rw-r--r--cpp/src/tests/config.null1
-rw-r--r--cpp/src/tests/consume.cpp65
-rw-r--r--cpp/src/tests/datagen.cpp103
-rw-r--r--cpp/src/tests/declare_queues.cpp101
-rw-r--r--cpp/src/tests/dlclose_noop.c2
-rw-r--r--cpp/src/tests/echotest.cpp157
-rw-r--r--cpp/src/tests/exception_test.cpp44
-rw-r--r--cpp/src/tests/failover_soak.cpp826
-rwxr-xr-xcpp/src/tests/fanout_perftest20
-rwxr-xr-xcpp/src/tests/federated_cluster_test152
-rwxr-xr-xcpp/src/tests/federated_cluster_test_with_node_failure23
-rwxr-xr-xcpp/src/tests/federated_topic_test87
-rwxr-xr-xcpp/src/tests/federation.py583
-rw-r--r--cpp/src/tests/find_prog.ps136
-rw-r--r--cpp/src/tests/header_test.cpp59
-rwxr-xr-xcpp/src/tests/header_test.py86
-rw-r--r--cpp/src/tests/interop_runner.cpp251
-rw-r--r--cpp/src/tests/latencytest.cpp223
-rw-r--r--cpp/src/tests/logging.cpp131
-rwxr-xr-xcpp/src/tests/long_cluster_tests.py38
-rwxr-xr-xcpp/src/tests/multiq_perftest20
-rwxr-xr-xcpp/src/tests/perfdist71
-rw-r--r--cpp/src/tests/perftest.cpp286
-rw-r--r--cpp/src/tests/policy.acl1
-rw-r--r--cpp/src/tests/publish.cpp61
-rwxr-xr-xcpp/src/tests/python_tests38
-rw-r--r--cpp/src/tests/python_tests.ps142
-rw-r--r--cpp/src/tests/qpid_ping.cpp123
-rw-r--r--cpp/src/tests/qpid_stream.cpp170
-rw-r--r--cpp/src/tests/qrsh.cpp169
-rw-r--r--cpp/src/tests/qrsh_run.cpp321
-rw-r--r--cpp/src/tests/qrsh_server.cpp1065
-rwxr-xr-xcpp/src/tests/qrsh_utils/10_all30
-rwxr-xr-xcpp/src/tests/qrsh_utils/1_remote_run26
-rwxr-xr-xcpp/src/tests/qrsh_utils/2_forever26
-rwxr-xr-xcpp/src/tests/qrsh_utils/3_kill_it27
-rwxr-xr-xcpp/src/tests/qrsh_utils/4_wait_for_it26
-rwxr-xr-xcpp/src/tests/qrsh_utils/5_exited64
-rwxr-xr-xcpp/src/tests/qrsh_utils/6_get29
-rwxr-xr-xcpp/src/tests/qrsh_utils/7_get_output44
-rwxr-xr-xcpp/src/tests/qrsh_utils/8_any43
-rwxr-xr-x[-rw-r--r--]cpp/src/tests/qrsh_utils/9_alias (renamed from cpp/src/tests/BasicP2PTest.h)34
-rw-r--r--cpp/src/tests/qrsh_utils/qrsh_example_command.cpp52
-rw-r--r--cpp/src/tests/qrsh_utils/qrsh_forever.cpp50
-rw-r--r--cpp/src/tests/qrsh_utils/qsh_doc.txt309
-rwxr-xr-xcpp/src/tests/quick_perftest20
-rwxr-xr-xcpp/src/tests/quick_topictest23
-rw-r--r--cpp/src/tests/quick_topictest.ps130
-rwxr-xr-xcpp/src/tests/quick_txtest22
-rw-r--r--cpp/src/tests/receiver.cpp139
-rwxr-xr-xcpp/src/tests/reliable_replication_test93
-rw-r--r--cpp/src/tests/replaying_sender.cpp163
-rwxr-xr-xcpp/src/tests/replication_test182
-rwxr-xr-xcpp/src/tests/restart_cluster20
-rw-r--r--cpp/src/tests/resuming_receiver.cpp192
-rwxr-xr-xcpp/src/tests/ring_queue_test174
-rwxr-xr-xcpp/src/tests/run-unit-tests20
-rwxr-xr-xcpp/src/tests/run_acl_tests64
-rwxr-xr-xcpp/src/tests/run_cli_tests51
-rwxr-xr-xcpp/src/tests/run_cluster_test26
-rwxr-xr-xcpp/src/tests/run_cluster_tests38
-rwxr-xr-xcpp/src/tests/run_failover_soak38
-rwxr-xr-xcpp/src/tests/run_federation_tests33
-rw-r--r--cpp/src/tests/run_federation_tests.ps180
-rwxr-xr-xcpp/src/tests/run_header_test37
-rw-r--r--cpp/src/tests/run_header_test.ps147
-rwxr-xr-xcpp/src/tests/run_long_cluster_tests23
-rwxr-xr-xcpp/src/tests/run_perftest20
-rwxr-xr-xcpp/src/tests/run_ring_queue_test36
-rwxr-xr-xcpp/src/tests/run_test45
-rw-r--r--cpp/src/tests/run_test.ps173
-rw-r--r--cpp/src/tests/sender.cpp137
-rwxr-xr-xcpp/src/tests/shared_perftest20
-rw-r--r--cpp/src/tests/shlibtest.cpp12
-rw-r--r--cpp/src/tests/ssl.mk23
-rwxr-xr-xcpp/src/tests/ssl_test82
-rwxr-xr-xcpp/src/tests/start_broker22
-rw-r--r--cpp/src/tests/start_broker.ps160
-rwxr-xr-xcpp/src/tests/start_cluster56
-rwxr-xr-xcpp/src/tests/start_cluster_hosts70
-rwxr-xr-xcpp/src/tests/stop_broker22
-rw-r--r--cpp/src/tests/stop_broker.ps156
-rwxr-xr-xcpp/src/tests/stop_cluster24
-rw-r--r--cpp/src/tests/test_env.sh.in68
-rw-r--r--cpp/src/tests/test_store.cpp178
-rw-r--r--cpp/src/tests/test_tools.h48
-rwxr-xr-xcpp/src/tests/test_watchdog36
-rwxr-xr-xcpp/src/tests/test_wrap48
-rw-r--r--cpp/src/tests/testlib.py766
-rw-r--r--cpp/src/tests/topic_listener.cpp121
-rwxr-xr-xcpp/src/tests/topic_perftest20
-rw-r--r--cpp/src/tests/topic_publisher.cpp138
-rwxr-xr-xcpp/src/tests/topictest20
-rw-r--r--cpp/src/tests/topictest.ps170
-rw-r--r--cpp/src/tests/txjob.cpp102
-rw-r--r--cpp/src/tests/txshift.cpp193
-rw-r--r--cpp/src/tests/txtest.cpp115
-rw-r--r--cpp/src/tests/unit_test.h16
-rw-r--r--cpp/src/tests/vg_check5
-rw-r--r--cpp/src/tests/windows/DisableWin32ErrorWindows.cpp72
205 files changed, 19568 insertions, 2882 deletions
diff --git a/cpp/src/tests/.valgrind.supp b/cpp/src/tests/.valgrind.supp
index dd8f7536a1..0e3e045437 100644
--- a/cpp/src/tests/.valgrind.supp
+++ b/cpp/src/tests/.valgrind.supp
@@ -1,4 +1,53 @@
{
+ Leak in TCPConnector: https://bugzilla.redhat.com/show_bug.cgi?id=520600
+ Memcheck:Leak
+ fun:_vgrZU_libcZdsoZa_calloc
+ fun:_dl_allocate_tls
+ fun:*
+ fun:*
+ fun:*
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+}
+
+{
+ Leak in TCPConnector: https://bugzilla.redhat.com/show_bug.cgi?id=520600
+ Memcheck:Leak
+ fun:_vgrZU_libcZdsoZa_calloc
+ fun:_dl_allocate_tls
+ fun:*
+ fun:*
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+}
+
+{
+ Reported on FC5 and RHEL5 when md5 sasl libs are installed
+ Memcheck:Leak
+ fun:*
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:*
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ Benign leak in CPG - patched version.
+ Memcheck:Leak
+ fun:*
+ fun:openais_service_connect
+ fun:cpg_initialize
+}
+
+{
Benign error in libcpg.
Memcheck:Param
socketcall.sendmsg(msg.msg_iov[i])
@@ -24,13 +73,6 @@
}
{
- Bogus epoll_ctl error on i386
- Memcheck:Param
- epoll_ctl(event)
- fun:epoll_ctl
-}
-
-{
boost 103200 -- we think Boost is responsible for these leaks.
Memcheck:Leak
fun:_Znwm
@@ -193,9 +235,42 @@
}
{
- CPG related errors - seem benign but should invesgitate.
+ CPG error - seems benign.
Memcheck:Param
socketcall.sendmsg(msg.msg_iov[i])
- fun:sendmsg
- obj:/usr/lib/openais/libcpg.so.2.0.0
+ obj:*
+ obj:*/libcpg.so.2.0.0
+}
+
+{
+ Known leak in boost.thread 1.33.1. Wildcards for 64/32 bit diffs.
+ Memcheck:Leak
+ fun:*
+ obj:/usr/*/libboost_thread.so.1.33.1
+ fun:_ZN5boost6detail3tss3setEPv
+}
+
+{
+ Shows up on RHEL5: believed benign
+ Memcheck:Cond
+ fun:__strcpy_chk
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+
+{
+ Seems like a use after delete issue in boost unit_test
+ Memcheck:Addr8
+ fun:_ZN5boost9unit_test14framework_implD1Ev
+ fun:exit
+ fun:(below main)
+}
+
+{
+ Seems like a use after delete issue in boost unit_test
+ Memcheck:Addr4
+ fun:_ZN5boost9unit_test14framework_implD1Ev
+ fun:exit
+ fun:(below main)
}
+
diff --git a/cpp/src/tests/AccumulatedAckTest.cpp b/cpp/src/tests/AccumulatedAckTest.cpp
index 028ce71907..c736a519d2 100644
--- a/cpp/src/tests/AccumulatedAckTest.cpp
+++ b/cpp/src/tests/AccumulatedAckTest.cpp
@@ -8,9 +8,9 @@
* 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
@@ -28,6 +28,9 @@ using std::list;
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
+
bool covers(const AccumulatedAck& ack, int i)
{
return ack.covers(SequenceNumber(i));
@@ -97,7 +100,7 @@ QPID_AUTO_TEST_CASE(testUpdateFromCompletionData)
ack.update(mark, ranges);
- for(int i = 0; i <= 15; i++) {
+ for(int i = 0; i <= 15; i++) {
BOOST_CHECK(covers(ack, i));
}
BOOST_CHECK(!covers(ack, 16));
@@ -221,7 +224,7 @@ QPID_AUTO_TEST_CASE(testConsolidation4)
ack.update(SequenceNumber(9), SequenceNumber(9));
ack.update(SequenceNumber(3), SequenceNumber(4));
- for(int i = 0; i <= 15; i++) {
+ for(int i = 0; i <= 15; i++) {
BOOST_CHECK(covers(ack, i));
}
BOOST_CHECK(!covers(ack, 16));
@@ -230,3 +233,5 @@ QPID_AUTO_TEST_CASE(testConsolidation4)
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/Address.cpp b/cpp/src/tests/Address.cpp
new file mode 100644
index 0000000000..f25a27d231
--- /dev/null
+++ b/cpp/src/tests/Address.cpp
@@ -0,0 +1,98 @@
+/*
+ *
+ * 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 <iostream>
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Variant.h"
+
+#include "unit_test.h"
+
+using namespace qpid::messaging;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(AddressSuite)
+
+QPID_AUTO_TEST_CASE(testParseNameOnly)
+{
+ Address address("my-topic");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+}
+
+QPID_AUTO_TEST_CASE(testParseSubject)
+{
+ Address address("my-topic/my-subject");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my-subject"), address.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptions)
+{
+ Address address("my-topic; {a:bc, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOption("a").asString());
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOption("x").asInt64());
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOption("y").asString());
+}
+
+QPID_AUTO_TEST_CASE(testParseSubjectAndOptions)
+{
+ Address address("my-topic/my-subject; {a:bc, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my-subject"), address.getSubject());
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOption("a").asString());
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOption("x").asInt64());
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOption("y").asString());
+}
+
+QPID_AUTO_TEST_CASE(testParseNestedOptions)
+{
+ Address address("my-topic; {a:{p:202, q:'another string'}, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL((uint16_t) 202, address.getOptions()["a"].asMap()["p"].asInt64());
+ BOOST_CHECK_EQUAL(std::string("another string"), address.getOptions()["a"].asMap()["q"].asString());
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOption("x").asInt64());
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOption("y").asString());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithList)
+{
+ Address address("my-topic; {a:[202, 'another string'], x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant::List& list = address.getOptions()["a"].asList();
+ Variant::List::const_iterator i = list.begin();
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL((uint16_t) 202, i->asInt64());
+ BOOST_CHECK(++i != list.end());
+ BOOST_CHECK_EQUAL(std::string("another string"), i->asString());
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOption("x").asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testParseQuotedNameAndSubject)
+{
+ Address address("'my topic with / in it'/'my subject with ; in it'");
+ BOOST_CHECK_EQUAL(std::string("my topic with / in it"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my subject with ; in it"), address.getSubject());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}}
diff --git a/cpp/src/tests/Array.cpp b/cpp/src/tests/Array.cpp
index c779cbe901..7622b89d15 100644
--- a/cpp/src/tests/Array.cpp
+++ b/cpp/src/tests/Array.cpp
@@ -7,9 +7,9 @@
* 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
@@ -25,6 +25,9 @@
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(ArrayTestSuite)
using namespace qpid::framing;
@@ -69,7 +72,7 @@ QPID_AUTO_TEST_CASE(testArrayAssignment)
Array a(data);
b = a;
BOOST_CHECK_EQUAL(a, b);
- }
+ }
std::vector<std::string> data2;
b.collect(data2);
//BOOST_CHECK_EQUAL(data, data2);
@@ -77,3 +80,5 @@ QPID_AUTO_TEST_CASE(testArrayAssignment)
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/AsyncCompletion.cpp b/cpp/src/tests/AsyncCompletion.cpp
new file mode 100644
index 0000000000..e32097106f
--- /dev/null
+++ b/cpp/src/tests/AsyncCompletion.cpp
@@ -0,0 +1,120 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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 "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/sys/BlockingQueue.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/QueueQueryResult.h"
+#include "qpid/client/TypedResult.h"
+
+using namespace std;
+using namespace qpid;
+using namespace client;
+using namespace framing;
+
+namespace qpid { namespace broker {
+class TransactionContext;
+class PersistableQueue;
+}}
+
+using broker::PersistableMessage;
+using broker::NullMessageStore;
+using broker::TransactionContext;
+using broker::PersistableQueue;
+using sys::TIME_SEC;
+using boost::intrusive_ptr;
+
+/** @file Unit tests for async completion.
+ * Using a dummy store, verify that the broker indicates async completion of
+ * message enqueues at the correct time.
+ */
+
+namespace qpid {
+namespace tests {
+
+class AsyncCompletionMessageStore : public NullMessageStore {
+ public:
+ sys::BlockingQueue<boost::intrusive_ptr<PersistableMessage> > enqueued;
+
+ AsyncCompletionMessageStore() : NullMessageStore() {}
+ ~AsyncCompletionMessageStore(){}
+
+ void enqueue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& )
+ {
+ enqueued.push(msg);
+ }
+};
+
+QPID_AUTO_TEST_SUITE(AsyncCompletionTestSuite)
+
+QPID_AUTO_TEST_CASE(testWaitTillComplete) {
+ SessionFixture fix;
+ AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore;
+ boost::shared_ptr<qpid::broker::MessageStore> p;
+ p.reset(store);
+ fix.broker->setStore(p);
+ AsyncSession s = fix.session;
+
+ static const int count = 3;
+
+ s.queueDeclare("q", arg::durable=true);
+ Completion transfers[count];
+ for (int i = 0; i < count; ++i) {
+ Message msg(boost::lexical_cast<string>(i), "q");
+ msg.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+ transfers[i] = s.messageTransfer(arg::content=msg);
+ }
+
+ // Get hold of the broker-side messages.
+ typedef vector<intrusive_ptr<PersistableMessage> > BrokerMessages;
+ BrokerMessages enqueued;
+ for (int j = 0; j < count; ++j)
+ enqueued.push_back(store->enqueued.pop(TIME_SEC));
+
+ // Send a sync, make sure it does not complete till all messages are complete.
+ // In reverse order for fun.
+ Completion sync = s.executionSync(arg::sync=true);
+ for (int k = count-1; k >= 0; --k) {
+ BOOST_CHECK(!transfers[k].isComplete()); // Should not be complete yet.
+ BOOST_CHECK(!sync.isComplete()); // Should not be complete yet.
+ enqueued[k]->enqueueComplete();
+ }
+ sync.wait(); // Should complete now, all messages are completed.
+}
+
+QPID_AUTO_TEST_CASE(testGetResult) {
+ SessionFixture fix;
+ AsyncSession s = fix.session;
+
+ s.queueDeclare("q", arg::durable=true);
+ TypedResult<QueueQueryResult> tr = s.queueQuery("q");
+ QueueQueryResult qq = tr.get();
+ BOOST_CHECK_EQUAL(qq.getQueue(), "q");
+ BOOST_CHECK_EQUAL(qq.getMessageCount(), 0U);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/AtomicValue.cpp b/cpp/src/tests/AtomicValue.cpp
index 05083ad177..d855d993a7 100644
--- a/cpp/src/tests/AtomicValue.cpp
+++ b/cpp/src/tests/AtomicValue.cpp
@@ -21,6 +21,9 @@
#include "test_tools.h"
#include "qpid/sys/AtomicValue.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(AtomicValueTestSuite)
QPID_AUTO_TEST_CASE(test) {
@@ -47,3 +50,5 @@ QPID_AUTO_TEST_CASE(test) {
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/BasicP2PTest.cpp b/cpp/src/tests/BasicP2PTest.cpp
deleted file mode 100644
index f4a4cce7f2..0000000000
--- a/cpp/src/tests/BasicP2PTest.cpp
+++ /dev/null
@@ -1,66 +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 "BasicP2PTest.h"
-
-using namespace qpid;
-using namespace qpid::client;
-
-class BasicP2PTest::Receiver : public Worker, public MessageListener
-{
- const std::string queue;
- std::string tag;
-public:
- Receiver(ConnectionOptions& options, const std::string& _queue, const int _messages)
- : Worker(options, _messages), queue(_queue){}
- void init()
- {
- Queue q(queue, true);
- channel.declareQueue(q);
- framing::FieldTable args;
- channel.bind(Exchange::STANDARD_DIRECT_EXCHANGE, q, queue, args);
- channel.consume(q, tag, this);
- channel.start();
- }
-
- void start()
- {
- }
-
- void received(Message&)
- {
- count++;
- }
-};
-
-void BasicP2PTest::assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options)
-{
- std::string queue = params.getString("P2P_QUEUE_AND_KEY_NAME");
- int messages = params.getInt("P2P_NUM_MESSAGES");
- if (role == "SENDER") {
- worker = std::auto_ptr<Worker>(new Sender(options, Exchange::STANDARD_DIRECT_EXCHANGE, queue, messages));
- } else if(role == "RECEIVER"){
- worker = std::auto_ptr<Worker>(new Receiver(options, queue, messages));
- } else {
- throw Exception("unrecognised role");
- }
- worker->init();
-}
diff --git a/cpp/src/tests/BasicPubSubTest.cpp b/cpp/src/tests/BasicPubSubTest.cpp
deleted file mode 100644
index 1e9ff364f1..0000000000
--- a/cpp/src/tests/BasicPubSubTest.cpp
+++ /dev/null
@@ -1,121 +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 "BasicPubSubTest.h"
-
-using namespace qpid;
-
-class BasicPubSubTest::Receiver : public Worker, public MessageListener
-{
- const Exchange& exchange;
- const std::string queue;
- const std::string key;
- std::string tag;
-public:
- Receiver(ConnectionOptions& options, const Exchange& _exchange, const std::string& _queue, const std::string& _key, const int _messages)
- : Worker(options, _messages), exchange(_exchange), queue(_queue), key(_key){}
-
- void init()
- {
- Queue q(queue, true);
- channel.declareQueue(q);
- framing::FieldTable args;
- channel.bind(exchange, q, key, args);
- channel.consume(q, tag, this);
- channel.start();
- }
-
- void start(){
- }
-
- void received(Message&)
- {
- count++;
- }
-};
-
-class BasicPubSubTest::MultiReceiver : public Worker, public MessageListener
-{
- typedef boost::ptr_vector<Receiver> ReceiverList;
- ReceiverList receivers;
-
-public:
- MultiReceiver(ConnectionOptions& options, const Exchange& exchange, const std::string& key, const int _messages, int receiverCount)
- : Worker(options, _messages)
- {
- for (int i = 0; i != receiverCount; i++) {
- std::string queue = (boost::format("%1%_%2%") % options.clientid % i).str();
- receivers.push_back(new Receiver(options, exchange, queue, key, _messages));
- }
- }
-
- void init()
- {
- for (ReceiverList::size_type i = 0; i != receivers.size(); i++) {
- receivers[i].init();
- }
- }
-
- void start()
- {
- for (ReceiverList::size_type i = 0; i != receivers.size(); i++) {
- receivers[i].start();
- }
- }
-
- void received(Message& msg)
- {
- for (ReceiverList::size_type i = 0; i != receivers.size(); i++) {
- receivers[i].received(msg);
- }
- }
-
- virtual int getCount()
- {
- count = 0;
- for (ReceiverList::size_type i = 0; i != receivers.size(); i++) {
- count += receivers[i].getCount();
- }
- return count;
- }
- virtual void stop()
- {
- for (ReceiverList::size_type i = 0; i != receivers.size(); i++) {
- receivers[i].stop();
- }
- }
-};
-
-void BasicPubSubTest::assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options)
-{
- std::string key = params.getString("PUBSUB_KEY");
- int messages = params.getInt("PUBSUB_NUM_MESSAGES");
- int receivers = params.getInt("PUBSUB_NUM_RECEIVERS");
- if (role == "SENDER") {
- worker = std::auto_ptr<Worker>(new Sender(options, Exchange::STANDARD_TOPIC_EXCHANGE, key, messages));
- } else if(role == "RECEIVER"){
- worker = std::auto_ptr<Worker>(new MultiReceiver(options, Exchange::STANDARD_TOPIC_EXCHANGE, key, messages, receivers));
- } else {
- throw Exception("unrecognised role");
- }
- worker->init();
-}
-
diff --git a/cpp/src/tests/Blob.cpp b/cpp/src/tests/Blob.cpp
index c40e43b96e..e69de29bb2 100644
--- a/cpp/src/tests/Blob.cpp
+++ b/cpp/src/tests/Blob.cpp
@@ -1,128 +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 "qpid/framing/Blob.h"
-
-#include "unit_test.h"
-
-QPID_AUTO_TEST_SUITE(BlobTestSuite)
-
-using namespace std;
-using namespace qpid::framing;
-
-struct Base {
- int id;
- int magic;
-
- Base(int n) : id(n), magic(42) {}
- Base(const Base& c) : id(c.id), magic(42) {}
- ~Base() { BOOST_CHECK_EQUAL(42, magic); } // Detect random data.
-};
-
-template <class T> struct Count : public Base {
- static int instances;
- bool destroyed;
-
- Count(int n) : Base(n), destroyed(false) { ++instances; }
- Count(const Count& c) : Base(c), destroyed(false) { ++instances; }
- ~Count() {
- BOOST_CHECK(!destroyed); // Detect double-destructor
- destroyed=true;
- BOOST_CHECK(--instances >= 0);
- }
-};
-
-template <class T> int Count<T>::instances = 0;
-
-struct Foo : public Count<Foo> { Foo(int n) : Count<Foo>(n) {}; };
-struct Bar : public Count<Bar> { Bar(int n) : Count<Bar>(n) {}; };
-
-typedef Blob<sizeof(Foo), Base> TestBlob ;
-
-QPID_AUTO_TEST_CASE(testBlobCtor) {
- {
- TestBlob empty;
- BOOST_CHECK(empty.empty());
- BOOST_CHECK(empty.get() == 0);
-
- TestBlob empty2(empty);
- BOOST_CHECK(empty2.empty());
-
- TestBlob foo(in_place<Foo>(1));
- BOOST_CHECK(!foo.empty());
- BOOST_CHECK_EQUAL(1, foo.get()->id);
- BOOST_CHECK_EQUAL(1, Foo::instances);
-
- TestBlob foo2(foo);
- BOOST_CHECK(!foo2.empty());
- BOOST_CHECK_EQUAL(1, foo2.get()->id);
- BOOST_CHECK_EQUAL(2, Foo::instances);
- }
-
- BOOST_CHECK_EQUAL(0, Foo::instances);
- BOOST_CHECK_EQUAL(0, Bar::instances);
-}
-
-
-QPID_AUTO_TEST_CASE(testAssign) {
- {
- TestBlob b;
- b = Foo(2);
- BOOST_CHECK_EQUAL(2, b.get()->id);
- BOOST_CHECK_EQUAL(1, Foo::instances);
-
- TestBlob b2(b);
- BOOST_CHECK_EQUAL(2, b.get()->id);
- BOOST_CHECK_EQUAL(2, Foo::instances);
-
- b2 = Bar(3);
- BOOST_CHECK_EQUAL(3, b2.get()->id);
- BOOST_CHECK_EQUAL(1, Foo::instances);
- BOOST_CHECK_EQUAL(1, Bar::instances);
-
- b2 = in_place<Foo>(4);
- BOOST_CHECK_EQUAL(4, b2.get()->id);
- BOOST_CHECK_EQUAL(2, Foo::instances);
- BOOST_CHECK_EQUAL(0, Bar::instances);
-
- b2.clear();
- BOOST_CHECK(b2.empty());
- BOOST_CHECK_EQUAL(1, Foo::instances);
- }
- BOOST_CHECK_EQUAL(0, Foo::instances);
- BOOST_CHECK_EQUAL(0, Bar::instances);
-}
-
-
-QPID_AUTO_TEST_CASE(testClear) {
- TestBlob b(in_place<Foo>(5));
- TestBlob c(b);
- BOOST_CHECK(!c.empty());
- BOOST_CHECK(!b.empty());
- BOOST_CHECK_EQUAL(2, Foo::instances);
-
- c.clear();
- BOOST_CHECK(c.empty());
- BOOST_CHECK_EQUAL(1, Foo::instances);
-
- b.clear();
- BOOST_CHECK(b.empty());
- BOOST_CHECK_EQUAL(0, Foo::instances);
-}
-
-QPID_AUTO_TEST_SUITE_END()
diff --git a/cpp/src/tests/BrokerFixture.h b/cpp/src/tests/BrokerFixture.h
index 09cca066ef..566fbda406 100644
--- a/cpp/src/tests/BrokerFixture.h
+++ b/cpp/src/tests/BrokerFixture.h
@@ -10,9 +10,9 @@
* 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
@@ -23,14 +23,21 @@
*/
#include "SocketProxy.h"
-#include "qpid/sys/Thread.h"
+
#include "qpid/broker/Broker.h"
#include "qpid/client/Connection.h"
#include "qpid/client/ConnectionImpl.h"
#include "qpid/client/Session.h"
#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/LocalQueue.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+#include "qpid/sys/Thread.h"
#include <boost/noncopyable.hpp>
+namespace qpid {
+namespace tests {
+
/**
* A fixture with an in-process broker.
*/
@@ -42,9 +49,16 @@ struct BrokerFixture : private boost::noncopyable {
qpid::sys::Thread brokerThread;
BrokerFixture(Broker::Options opts=Broker::Options()) {
+ // Keep the tests quiet unless logging env. vars have been set by user.
+ if (!::getenv("QPID_LOG_ENABLE") && !::getenv("QPID_TRACE")) {
+ qpid::log::Options logOpts;
+ logOpts.selectors.clear();
+ logOpts.selectors.push_back("error+");
+ qpid::log::Logger::instance().configure(logOpts);
+ }
opts.port=0;
// Management doesn't play well with multiple in-process brokers.
- opts.enableMgmt=false;
+ opts.enableMgmt=false;
opts.workerThreads=1;
opts.dataDir="";
opts.auth=false;
@@ -52,26 +66,35 @@ struct BrokerFixture : private boost::noncopyable {
// TODO aconway 2007-12-05: At one point BrokerFixture
// tests could hang in Connection ctor if the following
// line is removed. This may not be an issue anymore.
- broker->getPort();
+ broker->accept();
+ broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
brokerThread = qpid::sys::Thread(*broker);
};
- ~BrokerFixture() {
+ void shutdownBroker()
+ {
broker->shutdown();
+ broker = BrokerPtr();
+ }
+
+ ~BrokerFixture() {
+ if (broker) broker->shutdown();
brokerThread.join();
}
/** Open a connection to the broker. */
void open(qpid::client::Connection& c) {
- c.open("localhost", broker->getPort());
+ c.open("localhost", broker->getPort(qpid::broker::Broker::TCP_TRANSPORT));
}
- uint16_t getPort() { return broker->getPort(); }
+ uint16_t getPort() { return broker->getPort(qpid::broker::Broker::TCP_TRANSPORT); }
};
/** Connection that opens in its constructor */
struct LocalConnection : public qpid::client::Connection {
LocalConnection(uint16_t port) { open("localhost", port); }
+ LocalConnection(const qpid::client::ConnectionSettings& s) { open(s); }
+ ~LocalConnection() { close(); }
};
/** A local client connection via a socket proxy. */
@@ -80,6 +103,11 @@ struct ProxyConnection : public qpid::client::Connection {
ProxyConnection(int brokerPort) : proxy(brokerPort) {
open("localhost", proxy.getPort());
}
+ ProxyConnection(const qpid::client::ConnectionSettings& s) : proxy(s.port) {
+ qpid::client::ConnectionSettings proxySettings(s);
+ proxySettings.port = proxy.getPort();
+ open(proxySettings);
+ }
~ProxyConnection() { close(); }
};
@@ -92,9 +120,15 @@ struct ClientT {
SessionType session;
qpid::client::SubscriptionManager subs;
qpid::client::LocalQueue lq;
- ClientT(uint16_t port) : connection(port), session(connection.newSession()), subs(session) {}
+ std::string name;
+
+ ClientT(uint16_t port, const std::string& name_=std::string())
+ : connection(port), session(connection.newSession(name_)), subs(session), name(name_) {}
+ ClientT(const qpid::client::ConnectionSettings& settings, const std::string& name_=std::string())
+ : connection(settings), session(connection.newSession(name_)), subs(session), name(name_) {}
- ~ClientT() { connection.close(); }
+ ~ClientT() { close(); }
+ void close() { session.close(); connection.close(); }
};
typedef ClientT<> Client;
@@ -107,7 +141,7 @@ struct SessionFixtureT : BrokerFixture, ClientT<ConnectionType,SessionType> {
SessionFixtureT(Broker::Options opts=Broker::Options()) :
BrokerFixture(opts),
- ClientT<ConnectionType,SessionType>(broker->getPort())
+ ClientT<ConnectionType,SessionType>(broker->getPort(qpid::broker::Broker::TCP_TRANSPORT))
{}
};
@@ -115,5 +149,6 @@ struct SessionFixtureT : BrokerFixture, ClientT<ConnectionType,SessionType> {
typedef SessionFixtureT<LocalConnection> SessionFixture;
typedef SessionFixtureT<ProxyConnection> ProxySessionFixture;
+}} // namespace qpid::tests
#endif /*!TESTS_BROKERFIXTURE_H*/
diff --git a/cpp/src/tests/CMakeLists.txt b/cpp/src/tests/CMakeLists.txt
new file mode 100644
index 0000000000..469d777dce
--- /dev/null
+++ b/cpp/src/tests/CMakeLists.txt
@@ -0,0 +1,345 @@
+#
+# 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.
+#
+
+# Enable dashboard reporting.
+include (CTest)
+
+# Make sure that everything get built before the tests
+# Need to create a var with all the necessary top level targets
+
+add_definitions(-DBOOST_TEST_DYN_LINK)
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+
+include (FindPythonInterp)
+
+# Create the environment scripts for tests
+set (abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
+set (abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
+set (abs_top_srcdir ${CMAKE_SOURCE_DIR})
+set (abs_top_builddir ${CMAKE_BINARY_DIR})
+set (builddir_lib_suffix "")
+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/test_env.sh.in
+ ${CMAKE_CURRENT_BINARY_DIR}/test_env.sh)
+
+
+# If valgrind is selected in the configuration step, set up the path to it
+# for CTest.
+if (ENABLE_VALGRIND)
+ set (MEMORYCHECK_COMMAND ${VALGRIND})
+ set (MEMORYCHECK_COMMAND_OPTIONS "--gen-suppressions=all
+--leak-check=full
+--demangle=yes
+--suppressions=${CMAKE_CURRENT_SOURCE_DIR}/.valgrind.supp
+--num-callers=25
+--log-file=ctest_valgrind.vglog")
+endif (ENABLE_VALGRIND)
+
+# Using the Boost DLLs triggers warning 4275 on Visual Studio
+# (non dll-interface class used as base for dll-interface class).
+# This is ok, so suppress the warning.
+# Also, boost lengthy names trigger warning 4503, decorated name length exceeded
+# and using getenv() triggers insecure CRT warnings which we can silence in the
+# test environment.
+if (MSVC)
+ add_definitions( /wd4275 /wd4503 /D_CRT_SECURE_NO_WARNINGS)
+endif (MSVC)
+
+# Like this to work with cmake 2.4 on Unix
+set (qpid_test_boost_libs
+ ${Boost_REGEX_LIBRARY}
+ ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+
+# Macro to make it easier to remember where the tests are built
+macro(remember_location testname)
+ set (${testname}_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${testname}${CMAKE_EXECUTABLE_SUFFIX})
+endmacro(remember_location)
+
+# Windows uses some process-startup calls to ensure that errors, etc. don't
+# result in error boxes being thrown up. Since it's expected that most test
+# runs will be in scripts, the default is to force these outputs to stderr
+# instead of windows. If you want to remove this code, build without the
+# QPID_WINDOWS_DEFAULT_TEST_OUTPUTS ON.
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ option(QPID_WINDOWS_DEFAULT_TEST_OUTPUTS "Use default error-handling on Windows tests" OFF)
+ if (NOT QPID_WINDOWS_DEFAULT_TEST_OUTPUTS)
+ set(platform_test_additions windows/DisableWin32ErrorWindows.cpp)
+ endif (NOT QPID_WINDOWS_DEFAULT_TEST_OUTPUTS)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+#
+# Unit test program
+#
+# Unit tests are built as a single program to reduce valgrind overhead
+# when running the tests. If you want to build a subset of the tests run
+# ccmake and set unit_tests_to_build to the set you want to build.
+
+set(unit_tests_to_build
+ exception_test
+ RefCounted
+ SessionState
+ logging
+ AsyncCompletion
+ Url
+ Uuid
+ Shlib
+ FieldValue
+ FieldTable
+ Array
+ QueueOptionsTest
+ InlineAllocator
+ InlineVector
+ ClientSessionTest
+ MessagingSessionTests
+ SequenceSet
+ StringUtils
+ IncompleteMessageList
+ RangeSet
+ AtomicValue
+ QueueTest
+ AccumulatedAckTest
+ DtxWorkRecordTest
+ DeliveryRecordTest
+ ExchangeTest
+ HeadersExchangeTest
+ MessageTest
+ QueueRegistryTest
+ QueuePolicyTest
+ FramingTest
+ HeaderTest
+ SequenceNumberTest
+ TimerTest
+ TopicExchangeTest
+ TxBufferTest
+ TxPublishTest
+ MessageBuilderTest
+ ManagementTest
+ MessageReplayTracker
+ ConsoleTest
+ QueueEvents
+ ProxyTest
+ RetryList
+ RateFlowcontrolTest
+ FrameDecoder
+ ReplicationTest
+ ClientMessageTest
+ PollableCondition
+ Variant
+ ClientMessage
+ ${xml_tests}
+ CACHE STRING "Which unit tests to build"
+ )
+
+mark_as_advanced(unit_tests_to_build)
+
+# Disabled till we move to amqp_0_10 codec.
+# amqp_0_10/serialize.cpp allSegmentTypes.h \
+# amqp_0_10/ProxyTemplate.cpp \
+# amqp_0_10/apply.cpp \
+# amqp_0_10/Map.cpp \
+# amqp_0_10/handlers.cpp
+
+add_executable (unit_test unit_test
+ ${unit_tests_to_build} ${platform_test_additions})
+# The generally recommended way to add macro settings is to use
+# COMPILE_DEFINITIONS, but it's a rough go to add more than one definition
+# with a value; in this case, assuming that -D works everywhere is easier.
+set_source_files_properties (ReplicationTest.cpp Shlib.cpp ${xml_tests}
+ PROPERTIES
+ COMPILE_FLAGS
+ "-DQPID_MODULE_SUFFIX=\\\"${CMAKE_SHARED_MODULE_SUFFIX}\\\" -DQPID_MODULE_PREFIX=\\\"${CMAKE_SHARED_MODULE_PREFIX}\\\"")
+target_link_libraries (unit_test
+ ${qpid_test_boost_libs}
+ qpidclient qpidbroker qmfconsole)
+remember_location(unit_test)
+
+add_library (shlibtest MODULE shlibtest.cpp)
+
+if (BUILD_CLUSTER)
+ include (cluster.cmake)
+endif (BUILD_CLUSTER)
+
+# FIXME aconway 2009-11-30: enable SSL
+#if SSL
+#include ssl.mk
+#endif
+
+#
+# Other test programs
+#
+add_executable (perftest perftest.cpp ${platform_test_additions})
+target_link_libraries (perftest qpidclient)
+#perftest_SOURCES=perftest.cpp test_tools.h TestOptions.h ConnectionOptions.h
+remember_location(perftest)
+
+add_executable (txtest txtest.cpp ${platform_test_additions})
+target_link_libraries (txtest qpidclient)
+#txtest_SOURCES=txtest.cpp TestOptions.h ConnectionOptions.h
+remember_location(txtest)
+
+add_executable (latencytest latencytest.cpp ${platform_test_additions})
+target_link_libraries (latencytest qpidclient)
+#latencytest_SOURCES=latencytest.cpp TestOptions.h ConnectionOptions.h
+remember_location(latencytest)
+
+add_executable (echotest echotest.cpp ${platform_test_additions})
+target_link_libraries (echotest qpidclient)
+#echotest_SOURCES=echotest.cpp TestOptions.h ConnectionOptions.h
+remember_location(echotest)
+
+add_executable (client_test client_test.cpp ${platform_test_additions})
+target_link_libraries (client_test qpidclient)
+#client_test_SOURCES=client_test.cpp TestOptions.h ConnectionOptions.h
+remember_location(client_test)
+
+add_executable (topic_listener topic_listener.cpp ${platform_test_additions})
+target_link_libraries (topic_listener qpidclient)
+#topic_listener_SOURCES=topic_listener.cpp TestOptions.h ConnectionOptions.h
+remember_location(topic_listener)
+
+add_executable (topic_publisher topic_publisher.cpp ${platform_test_additions})
+target_link_libraries (topic_publisher qpidclient)
+#topic_publisher_SOURCES=topic_publisher.cpp TestOptions.h ConnectionOptions.h
+remember_location(topic_publisher)
+
+add_executable (publish publish.cpp ${platform_test_additions})
+target_link_libraries (publish qpidclient)
+#publish_SOURCES=publish.cpp TestOptions.h ConnectionOptions.h
+remember_location(publish)
+
+add_executable (consume consume.cpp ${platform_test_additions})
+target_link_libraries (consume qpidclient)
+#consume_SOURCES=consume.cpp TestOptions.h ConnectionOptions.h
+remember_location(consume)
+
+add_executable (header_test header_test.cpp ${platform_test_additions})
+target_link_libraries (header_test qpidclient)
+#header_test_SOURCES=header_test.cpp TestOptions.h ConnectionOptions.h
+remember_location(header_test)
+
+add_executable (declare_queues declare_queues.cpp ${platform_test_additions})
+target_link_libraries (declare_queues qpidclient)
+remember_location(declare_queues)
+
+add_executable (replaying_sender replaying_sender.cpp ${platform_test_additions})
+target_link_libraries (replaying_sender qpidclient)
+remember_location(replaying_sender)
+
+add_executable (resuming_receiver resuming_receiver.cpp ${platform_test_additions})
+target_link_libraries (resuming_receiver qpidclient)
+remember_location(resuming_receiver)
+
+add_executable (txshift txshift.cpp ${platform_test_additions})
+target_link_libraries (txshift qpidclient)
+#txshift_SOURCES=txshift.cpp TestOptions.h ConnectionOptions.h
+remember_location(txshift)
+
+add_executable (txjob txjob.cpp ${platform_test_additions})
+target_link_libraries (txjob qpidclient)
+#txjob_SOURCES=txjob.cpp TestOptions.h ConnectionOptions.h
+remember_location(txjob)
+
+add_executable (receiver receiver.cpp ${platform_test_additions})
+target_link_libraries (receiver qpidclient)
+#receiver_SOURCES=receiver.cpp TestOptions.h ConnectionOptions.h
+remember_location(receiver)
+
+add_executable (sender sender.cpp ${platform_test_additions})
+target_link_libraries (sender qpidclient)
+#sender_SOURCES=sender.cpp TestOptions.h ConnectionOptions.h
+remember_location(sender)
+
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ set (ENV{OUTDIR} ${EXECUTABLE_OUTPUT_PATH})
+ set (test_script_suffix ".ps1")
+ set (shell "powershell")
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+set(test_wrap ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_test${test_script_suffix})
+
+add_test (unit_test ${test_wrap} ${unit_test_LOCATION})
+add_test (start_broker ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/start_broker${test_script_suffix})
+add_test (client_test ${test_wrap} ${client_test_LOCATION})
+add_test (quick_perftest ${test_wrap} ${perftest_LOCATION} --summary --count 100)
+add_test (quick_topictest ${test_wrap} ${CMAKE_CURRENT_SOURCE_DIR}/quick_topictest${test_script_suffix})
+add_test (quick_txtest ${test_wrap} ${txtest_LOCATION} --queues 4 --tx-count 10 --quiet)
+if (PYTHON_EXECUTABLE)
+ add_test (run_header_test ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_header_test${test_script_suffix})
+ add_test (python_tests ${test_wrap} ${CMAKE_CURRENT_SOURCE_DIR}/python_tests${test_script_suffix})
+endif (PYTHON_EXECUTABLE)
+add_test (stop_broker ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/stop_broker${test_script_suffix})
+if (PYTHON_EXECUTABLE)
+ add_test (federation_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_federation_tests${test_script_suffix})
+if (BUILD_ACL)
+ add_test (acl_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_acl_tests${test_script_suffix})
+endif (BUILD_ACL)
+endif (PYTHON_EXECUTABLE)
+
+add_library(test_store MODULE test_store.cpp)
+target_link_libraries (test_store qpidbroker qpidcommon)
+set_target_properties (test_store PROPERTIES PREFIX "")
+
+#EXTRA_DIST += \
+# run_test vg_check \
+# run-unit-tests start_broker python_tests stop_broker \
+# quick_topictest \
+# quick_perftest \
+# quick_txtest \
+# topictest \
+# run_header_test \
+# header_test.py \
+# ssl_test \
+# config.null \
+# ais_check \
+# run_federation_tests \
+# run_acl_tests \
+# .valgrind.supp \
+# MessageUtils.h \
+# TestMessageStore.h \
+# TxMocks.h \
+# start_cluster stop_cluster restart_cluster
+
+add_library (dlclose_noop MODULE dlclose_noop.c)
+#libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir)
+
+#CLEANFILES+=valgrind.out *.log *.vglog* dummy_test $(unit_wrappers)
+#
+## FIXME aconway 2008-05-23: Disabled interop_runner because it uses
+## the obsolete Channel class. Convert to Session and re-enable.
+##
+## check_PROGRAMS += interop_runner
+#
+## interop_runner_SOURCES = \
+## interop_runner.cpp \
+## SimpleTestCaseBase.cpp \
+## BasicP2PTest.cpp \
+## BasicPubSubTest.cpp \
+## SimpleTestCaseBase.h \
+## BasicP2PTest.h \
+## BasicPubSubTest.h \
+## TestCase.h \
+## TestOptions.h ConnectionOptions.h
+## interop_runner_LDADD = $(lib_client) $(lib_common) $(extra_libs)
+#
+#
+## Longer running stability tests, not run by default check: target.
+## Not run under valgrind, too slow
+#LONG_TESTS=fanout_perftest shared_perftest multiq_perftest topic_perftest run_failover_soak
+#EXTRA_DIST+=$(LONG_TESTS) run_perftest
+#check-long:
+# $(MAKE) check TESTS="start_broker $(LONG_TESTS) stop_broker" VALGRIND=
diff --git a/cpp/src/tests/BasicPubSubTest.h b/cpp/src/tests/ClientMessage.cpp
index 242d2847d7..ab1cf9d102 100644
--- a/cpp/src/tests/BasicPubSubTest.h
+++ b/cpp/src/tests/ClientMessage.cpp
@@ -1,5 +1,3 @@
-#ifndef _BasicPubSubTest_
-#define _BasicPubSubTest_
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -9,9 +7,9 @@
* 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
@@ -20,32 +18,29 @@
* under the License.
*
*/
+#include <iostream>
+#include "qpid/messaging/Message.h"
-#include <memory>
-#include <sstream>
-
-#include "qpid/Exception.h"
-#include "qpid/client/Channel.h"
-#include "qpid/client/Message.h"
-#include "qpid/client/Connection.h"
-#include "qpid/client/MessageListener.h"
-#include "SimpleTestCaseBase.h"
-#include <boost/ptr_container/ptr_vector.hpp>
-#include <boost/format.hpp>
+#include "unit_test.h"
+using namespace qpid::messaging;
namespace qpid {
+namespace tests {
-using namespace qpid::client;
+QPID_AUTO_TEST_SUITE(ClientMessageSuite)
-class BasicPubSubTest : public SimpleTestCaseBase
+QPID_AUTO_TEST_CASE(testCopyConstructor)
{
- class Receiver;
- class MultiReceiver;
-public:
- void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options);
-};
-
+ Message m("my-data");
+ m.setSubject("my-subject");
+ m.getHeaders()["a"] = "ABC";
+ Message c(m);
+ BOOST_CHECK_EQUAL(m.getContent(), c.getContent());
+ BOOST_CHECK_EQUAL(m.getSubject(), c.getSubject());
+ BOOST_CHECK_EQUAL(m.getHeaders()["a"], c.getHeaders()["a"]);
}
-#endif
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ClientMessageTest.cpp b/cpp/src/tests/ClientMessageTest.cpp
new file mode 100644
index 0000000000..f925f1c234
--- /dev/null
+++ b/cpp/src/tests/ClientMessageTest.cpp
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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 Unit tests for the client::Message class. */
+
+#include "unit_test.h"
+#include "qpid/client/Message.h"
+
+using namespace qpid::client;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClientMessageTestSuite)
+
+QPID_AUTO_TEST_CASE(MessageCopyAssign) {
+ // Verify that message has normal copy semantics.
+ Message m("foo");
+ BOOST_CHECK_EQUAL("foo", m.getData());
+ Message c(m);
+ BOOST_CHECK_EQUAL("foo", c.getData());
+ Message a;
+ BOOST_CHECK_EQUAL("", a.getData());
+ a = m;
+ BOOST_CHECK_EQUAL("foo", a.getData());
+ a.setData("a");
+ BOOST_CHECK_EQUAL("a", a.getData());
+ c.setData("c");
+ BOOST_CHECK_EQUAL("c", c.getData());
+ BOOST_CHECK_EQUAL("foo", m.getData());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ClientSessionTest.cpp b/cpp/src/tests/ClientSessionTest.cpp
index 0b46d39047..6ca0aa6d44 100644
--- a/cpp/src/tests/ClientSessionTest.cpp
+++ b/cpp/src/tests/ClientSessionTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -19,21 +19,30 @@
*
*/
#include "unit_test.h"
+#include "test_tools.h"
#include "BrokerFixture.h"
-#include "qpid/client/AckPolicy.h"
-#include "qpid/client/Dispatcher.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/AsyncSession.h"
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Thread.h"
#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Time.h"
#include "qpid/client/Session.h"
-#include "qpid/framing/TransferContent.h"
+#include "qpid/client/Message.h"
#include "qpid/framing/reply_exceptions.h"
#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
#include <vector>
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(ClientSessionTest)
using namespace qpid::client;
@@ -42,6 +51,7 @@ using namespace qpid;
using qpid::sys::Monitor;
using qpid::sys::Thread;
using qpid::sys::TIME_SEC;
+using qpid::broker::Broker;
using std::string;
using std::cout;
using std::endl;
@@ -51,22 +61,22 @@ struct DummyListener : public sys::Runnable, public MessageListener {
std::vector<Message> messages;
string name;
uint expected;
- Dispatcher dispatcher;
+ SubscriptionManager submgr;
DummyListener(Session& session, const string& n, uint ex) :
- name(n), expected(ex), dispatcher(session) {}
+ name(n), expected(ex), submgr(session) {}
void run()
{
- dispatcher.listen(name, this);
- dispatcher.run();
+ submgr.subscribe(*this, name);
+ submgr.run();
}
void received(Message& msg)
{
messages.push_back(msg);
if (--expected == 0) {
- dispatcher.stop();
+ submgr.stop();
}
}
};
@@ -94,54 +104,33 @@ struct SimpleListener : public MessageListener
struct ClientSessionFixture : public ProxySessionFixture
{
- void declareSubscribe(const string& q="my-queue",
- const string& dest="my-dest")
- {
- session.queueDeclare(arg::queue=q);
- session.messageSubscribe(arg::queue=q, arg::destination=dest, arg::acquireMode=1);
- session.messageFlow(arg::destination=dest, arg::unit=0, arg::value=0xFFFFFFFF);//messages
- session.messageFlow(arg::destination=dest, arg::unit=1, arg::value=0xFFFFFFFF);//bytes
+ ClientSessionFixture(Broker::Options opts = Broker::Options()) : ProxySessionFixture(opts) {
+ session.queueDeclare(arg::queue="my-queue");
}
};
QPID_AUTO_TEST_CASE(testQueueQuery) {
ClientSessionFixture fix;
fix.session = fix.connection.newSession();
- fix.session.queueDeclare(arg::queue="my-queue", arg::alternateExchange="amq.fanout", arg::exclusive=true, arg::autoDelete=true);
- QueueQueryResult result = fix.session.queueQuery(string("my-queue"));
+ fix.session.queueDeclare(arg::queue="q", arg::alternateExchange="amq.fanout",
+ arg::exclusive=true, arg::autoDelete=true);
+ QueueQueryResult result = fix.session.queueQuery("q");
BOOST_CHECK_EQUAL(false, result.getDurable());
BOOST_CHECK_EQUAL(true, result.getExclusive());
- BOOST_CHECK_EQUAL(string("amq.fanout"),
- result.getAlternateExchange());
-}
-
-QPID_AUTO_TEST_CASE(testTransfer)
-{
- ClientSessionFixture fix;
- fix.session=fix.connection.newSession();
- fix.declareSubscribe();
- fix.session.messageTransfer(arg::acceptMode=1, arg::content=TransferContent("my-message", "my-queue"));
- //get & test the message:
- FrameSet::shared_ptr msg = fix.session.get();
- BOOST_CHECK(msg->isA<MessageTransferBody>());
- BOOST_CHECK_EQUAL(string("my-message"), msg->getContent());
- //confirm receipt:
- AckPolicy autoAck;
- autoAck.ack(Message(*msg), fix.session);
+ BOOST_CHECK_EQUAL("amq.fanout", result.getAlternateExchange());
}
QPID_AUTO_TEST_CASE(testDispatcher)
{
ClientSessionFixture fix;
fix.session =fix.connection.newSession();
- fix.declareSubscribe();
size_t count = 100;
- for (size_t i = 0; i < count; ++i)
- fix.session.messageTransfer(arg::content=TransferContent(boost::lexical_cast<string>(i), "my-queue"));
- DummyListener listener(fix.session, "my-dest", count);
+ for (size_t i = 0; i < count; ++i)
+ fix.session.messageTransfer(arg::content=Message(boost::lexical_cast<string>(i), "my-queue"));
+ DummyListener listener(fix.session, "my-queue", count);
listener.run();
- BOOST_CHECK_EQUAL(count, listener.messages.size());
- for (size_t i = 0; i < count; ++i)
+ BOOST_CHECK_EQUAL(count, listener.messages.size());
+ for (size_t i = 0; i < count; ++i)
BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData());
}
@@ -149,54 +138,49 @@ QPID_AUTO_TEST_CASE(testDispatcherThread)
{
ClientSessionFixture fix;
fix.session =fix.connection.newSession();
- fix.declareSubscribe();
size_t count = 10;
- DummyListener listener(fix.session, "my-dest", count);
+ DummyListener listener(fix.session, "my-queue", count);
sys::Thread t(listener);
for (size_t i = 0; i < count; ++i) {
- fix.session.messageTransfer(arg::content=TransferContent(boost::lexical_cast<string>(i), "my-queue"));
+ fix.session.messageTransfer(arg::content=Message(boost::lexical_cast<string>(i), "my-queue"));
}
t.join();
- BOOST_CHECK_EQUAL(count, listener.messages.size());
- for (size_t i = 0; i < count; ++i)
+ BOOST_CHECK_EQUAL(count, listener.messages.size());
+ for (size_t i = 0; i < count; ++i)
BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData());
}
-// FIXME aconway 2008-05-26: Re-enable with final resume implementation.
-//
-// QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testSuspend0Timeout, 1)
-// {
-// ClientSessionFixture fix;
-// fix.session.suspend(); // session has 0 timeout.
-// try {
-// fix.connection.resume(fix.session);
-// BOOST_FAIL("Expected InvalidArgumentException.");
-// } catch(const InternalErrorException&) {}
-// }
-
-// QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testUseSuspendedError, 1)
-// {
-// ClientSessionFixture fix;
-// fix.session =fix.session.timeout(60);
-// fix.session.suspend();
-// try {
-// fix.session.exchangeQuery(name="amq.fanout");
-// BOOST_FAIL("Expected session suspended exception");
-// } catch(const CommandInvalidException&) {}
-// }
-
-// QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testSuspendResume, 1)
-// {
-// ClientSessionFixture fix;
-// fix.session.timeout(60);
-// fix.declareSubscribe();
-// fix.session.suspend();
-// // Make sure we are still subscribed after resume.
-// fix.connection.resume(fix.session);
-// fix.session.messageTransfer(content=TransferContent("my-message", "my-queue"));
-// FrameSet::shared_ptr msg = fix.session.get();
-// BOOST_CHECK_EQUAL(string("my-message"), msg->getContent());
-// }
+// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
+void testSuspend0Timeout() {
+ ClientSessionFixture fix;
+ fix.session.suspend(); // session has 0 timeout.
+ try {
+ fix.connection.resume(fix.session);
+ BOOST_FAIL("Expected InvalidArgumentException.");
+ } catch(const InternalErrorException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testUseSuspendedError)
+{
+ ClientSessionFixture fix;
+ fix.session.timeout(60);
+ fix.session.suspend();
+ try {
+ fix.session.exchangeQuery(arg::exchange="amq.fanout");
+ BOOST_FAIL("Expected session suspended exception");
+ } catch(const NotAttachedException&) {}
+}
+
+// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
+void testSuspendResume() {
+ ClientSessionFixture fix;
+ fix.session.timeout(60);
+ fix.session.suspend();
+ // Make sure we are still subscribed after resume.
+ fix.connection.resume(fix.session);
+ fix.session.messageTransfer(arg::content=Message("my-message", "my-queue"));
+ BOOST_CHECK_EQUAL("my-message", fix.subs.get("my-queue", TIME_SEC).getData());
+}
QPID_AUTO_TEST_CASE(testSendToSelf) {
@@ -233,8 +217,8 @@ QPID_AUTO_TEST_CASE(testLocalQueue) {
BOOST_CHECK_EQUAL("foo0", lq.pop().getData());
BOOST_CHECK_EQUAL("foo1", lq.pop().getData());
BOOST_CHECK(lq.empty()); // Credit exhausted.
- fix.subs.setFlowControl("lq", FlowControl::unlimited());
- BOOST_CHECK_EQUAL("foo2", lq.pop().getData());
+ fix.subs.getSubscription("lq").setFlowControl(FlowControl::unlimited());
+ BOOST_CHECK_EQUAL("foo2", lq.pop().getData());
}
struct DelayedTransfer : sys::Runnable
@@ -245,7 +229,7 @@ struct DelayedTransfer : sys::Runnable
void run()
{
- sleep(1);
+ qpid::sys::sleep(1);
fixture.session.messageTransfer(arg::content=Message("foo2", "getq"));
}
};
@@ -265,7 +249,7 @@ QPID_AUTO_TEST_CASE(testGet) {
Thread t(sender);
//test timed get where message shows up after a short delay
BOOST_CHECK(fix.subs.get(got, "getq", 5*TIME_SEC));
- BOOST_CHECK_EQUAL("foo2", got.getData());
+ BOOST_CHECK_EQUAL("foo2", got.getData());
t.join();
}
@@ -284,6 +268,345 @@ QPID_AUTO_TEST_CASE(testOpenFailure) {
BOOST_CHECK(!c.isOpen());
}
-QPID_AUTO_TEST_SUITE_END()
+QPID_AUTO_TEST_CASE(testPeriodicExpiration) {
+ Broker::Options opts;
+ opts.queueCleanInterval = 1;
+ ClientSessionFixture fix(opts);
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ for (uint i = 0; i < 10; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ if (i % 2) m.getDeliveryProperties().setTtl(500);
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 10u);
+ qpid::sys::sleep(2);
+ BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 5u);
+}
+
+QPID_AUTO_TEST_CASE(testExpirationOnPop) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ for (uint i = 0; i < 10; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ if (i % 2) m.getDeliveryProperties().setTtl(200);
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ qpid::sys::usleep(300* 1000);
+
+ for (uint i = 0; i < 10; i++) {
+ if (i % 2) continue;
+ Message m;
+ BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRelease) {
+ ClientSessionFixture fix;
+
+ const uint count=10;
+ for (uint i = 0; i < count; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ fix.subs.setAutoStop(false);
+ fix.subs.start();
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+
+ SimpleListener l1;
+ Subscription s1 = fix.subs.subscribe(l1, "my-queue", settings);
+ l1.waitFor(count);
+ s1.cancel();
+
+ for (uint i = 0; i < count; i++) {
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l1.messages[i].getData());
+ }
+ s1.release(s1.getUnaccepted());
+
+ //check that released messages are redelivered
+ settings.autoAck = 1;
+ SimpleListener l2;
+ Subscription s2 = fix.subs.subscribe(l2, "my-queue", settings);
+ l2.waitFor(count);
+ for (uint i = 0; i < count; i++) {
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l2.messages[i].getData());
+ }
+
+ fix.subs.stop();
+ fix.subs.wait();
+ fix.session.close();
+}
+
+QPID_AUTO_TEST_CASE(testCompleteOnAccept) {
+ ClientSessionFixture fix;
+ const uint count = 8;
+ const uint chunk = 4;
+ for (uint i = 0; i < count; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+ settings.completionMode = COMPLETE_ON_ACCEPT;
+ settings.flowControl = FlowControl::messageWindow(chunk);
+
+ LocalQueue q;
+ Subscription s = fix.subs.subscribe(q, "my-queue", settings);
+ fix.session.messageFlush(arg::destination=s.getName());
+ SequenceSet accepted;
+ for (uint i = 0; i < chunk; i++) {
+ Message m;
+ BOOST_CHECK(q.get(m));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ accepted.add(m.getId());
+ }
+ Message m;
+ BOOST_CHECK(!q.get(m));
+
+ s.accept(accepted);
+ fix.session.messageFlush(arg::destination=s.getName());
+ accepted.clear();
+
+ for (uint i = chunk; i < count; i++) {
+ Message m;
+ BOOST_CHECK(q.get(m));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ accepted.add(m.getId());
+ }
+ fix.session.messageAccept(accepted);
+}
+
+namespace
+{
+struct Publisher : qpid::sys::Runnable
+{
+ AsyncSession session;
+ Message message;
+ uint count;
+ Thread thread;
+
+ Publisher(Connection& con, Message m, uint c) : session(con.newSession()), message(m), count(c) {}
+
+ void start()
+ {
+ thread = Thread(*this);
+ }
+
+ void join()
+ {
+ thread.join();
+ }
+
+ void run()
+ {
+ for (uint i = 0; i < count; i++) {
+ session.messageTransfer(arg::content=message);
+ }
+ session.sync();
+ session.close();
+ }
+};
+}
+
+QPID_AUTO_TEST_CASE(testConcurrentSenders)
+{
+ //Ensure concurrent publishing sessions on a connection don't
+ //cause assertions, deadlocks or other undesirables:
+ BrokerFixture fix;
+ Connection connection;
+ ConnectionSettings settings;
+ settings.maxFrameSize = 1024;
+ settings.port = fix.broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+ connection.open(settings);
+ AsyncSession session = connection.newSession();
+ Message message(string(512, 'X'));
+
+ boost::ptr_vector<Publisher> publishers;
+ for (size_t i = 0; i < 5; i++) {
+ publishers.push_back(new Publisher(connection, message, 100));
+ }
+ std::for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::start, _1));
+ std::for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::join, _1));
+ connection.close();
+}
+
+
+QPID_AUTO_TEST_CASE(testExclusiveSubscribe)
+{
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="myq", arg::exclusive=true, arg::autoDelete=true);
+ SubscriptionSettings settings;
+ settings.exclusive = true;
+ LocalQueue q;
+ fix.subs.subscribe(q, "myq", settings, "first");
+ //attempt to create new subscriber should fail
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.subs.subscribe(q, "myq", "second"), ResourceLockedException);
+ ;
+
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveBinding) {
+ FieldTable options;
+ options.setString("qpid.exclusive-binding", "anything");
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="queue-1", arg::exclusive=true, arg::autoDelete=true);
+ fix.session.queueDeclare(arg::queue="queue-2", arg::exclusive=true, arg::autoDelete=true);
+ fix.session.exchangeBind(arg::exchange="amq.direct", arg::queue="queue-1", arg::bindingKey="my-key", arg::arguments=options);
+ fix.session.messageTransfer(arg::destination="amq.direct", arg::content=Message("message1", "my-key"));
+ fix.session.exchangeBind(arg::exchange="amq.direct", arg::queue="queue-2", arg::bindingKey="my-key", arg::arguments=options);
+ fix.session.messageTransfer(arg::destination="amq.direct", arg::content=Message("message2", "my-key"));
+
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "queue-1"));
+ BOOST_CHECK_EQUAL("message1", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "queue-1"));
+
+ BOOST_CHECK(fix.subs.get(got, "queue-2"));
+ BOOST_CHECK_EQUAL("message2", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "queue-2"));
+}
+
+QPID_AUTO_TEST_CASE(testResubscribeWithLocalQueue) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="some-queue", arg::exclusive=true, arg::autoDelete=true);
+ LocalQueue p, q;
+ fix.subs.subscribe(p, "some-queue");
+ fix.subs.cancel("some-queue");
+ fix.subs.subscribe(q, "some-queue");
+
+ fix.session.messageTransfer(arg::content=Message("some-data", "some-queue"));
+ fix.session.messageFlush(arg::destination="some-queue");
+
+ Message got;
+ BOOST_CHECK(!p.get(got));
+
+ BOOST_CHECK(q.get(got));
+ BOOST_CHECK_EQUAL("some-data", got.getData());
+ BOOST_CHECK(!q.get(got));
+}
+
+QPID_AUTO_TEST_CASE(testReliableDispatch) {
+ ClientSessionFixture fix;
+ std::string queue("a-queue");
+ fix.session.queueDeclare(arg::queue=queue, arg::autoDelete=true);
+
+ ConnectionSettings settings;
+ settings.port = fix.broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+
+ Connection c1;
+ c1.open(settings);
+ Session s1 = c1.newSession();
+ SubscriptionManager subs1(s1);
+ LocalQueue q1;
+ subs1.subscribe(q1, queue, FlowControl());//first subscriber has no credit
+
+ Connection c2;
+ c2.open(settings);
+ Session s2 = c2.newSession();
+ SubscriptionManager subs2(s2);
+ LocalQueue q2;
+ subs2.subscribe(q2, queue);//second subscriber has credit
+ fix.session.messageTransfer(arg::content=Message("my-message", queue));
+
+ //check that the second consumer gets the message
+ Message got;
+ BOOST_CHECK(q2.get(got, 1*TIME_SEC));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+
+ c1.close();
+ c2.close();
+}
+
+QPID_AUTO_TEST_CASE(testSessionCloseOnInvalidSession) {
+ Session session;
+ session.close();
+}
+
+QPID_AUTO_TEST_CASE(testLVQVariedSize) {
+ ClientSessionFixture fix;
+ std::string queue("my-lvq");
+ QueueOptions args;
+ args.setOrdering(LVQ_NO_BROWSE);
+ fix.session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+
+ std::string key;
+ args.getLVQKey(key);
+
+ for (size_t i = 0; i < 10; i++) {
+ std::ostringstream data;
+ size_t size = 100 - ((i % 10) * 10);
+ data << std::string(size, 'x');
+
+ Message m(data.str(), queue);
+ m.getHeaders().setString(key, "abc");
+ fix.session.messageTransfer(arg::content=m);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSessionManagerSetFlowControl) {
+ ClientSessionFixture fix;
+ std::string name("dummy");
+ LocalQueue queue;
+ SubscriptionSettings settings;
+ settings.flowControl = FlowControl();
+ fix.session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true);
+ fix.subs.subscribe(queue, name, settings);
+ fix.session.messageTransfer(arg::content=Message("my-message", name));
+ fix.subs.setFlowControl(name, 1, FlowControl::UNLIMITED, false);
+ fix.session.messageFlush(name);
+ Message got;
+ BOOST_CHECK(queue.get(got, 0));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+}
+
+QPID_AUTO_TEST_CASE(testGetThenSubscribe) {
+ ClientSessionFixture fix;
+ std::string name("myqueue");
+ fix.session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true);
+ fix.session.messageTransfer(arg::content=Message("one", name));
+ fix.session.messageTransfer(arg::content=Message("two", name));
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, name));
+ BOOST_CHECK_EQUAL("one", got.getData());
+
+ DummyListener listener(fix.session, name, 1);
+ listener.run();
+ BOOST_CHECK_EQUAL(1u, listener.messages.size());
+ if (!listener.messages.empty()) {
+ BOOST_CHECK_EQUAL("two", listener.messages[0].getData());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSessionIsValid) {
+ ClientSessionFixture fix;
+ BOOST_CHECK(fix.session.isValid());
+ Session session;
+ BOOST_CHECK(!session.isValid());
+}
+
+QPID_AUTO_TEST_CASE(testExpirationNotAltered) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ Message m("my-message", "my-queue");
+ m.getDeliveryProperties().setTtl(60000);
+ m.getDeliveryProperties().setExpiration(12345);
+ fix.session.messageTransfer(arg::content=m);
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "my-queue"));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+ BOOST_CHECK_EQUAL(12345u, got.getDeliveryProperties().getExpiration());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ClusterFailover.cpp b/cpp/src/tests/ClusterFailover.cpp
new file mode 100644
index 0000000000..6b1ef99807
--- /dev/null
+++ b/cpp/src/tests/ClusterFailover.cpp
@@ -0,0 +1,68 @@
+/*
+ *
+ * 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 Tests for partial failure in a cluster.
+ * Partial failure means some nodes experience a failure while others do not.
+ * In this case the failed nodes must shut down.
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "ClusterFixture.h"
+#include "qpid/client/FailoverManager.h"
+#include <boost/assign.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClusterFailoverTestSuite)
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using namespace qpid::client::arg;
+using namespace boost::assign;
+using broker::Broker;
+using boost::shared_ptr;
+
+// Timeout for tests that wait for messages
+const sys::Duration TIMEOUT=sys::TIME_SEC/4;
+
+
+// Test re-connecting with same session name after a failure.
+QPID_AUTO_TEST_CASE(testReconnectSameSessionName) {
+ ostringstream clusterLib;
+ clusterLib << getLibPath("CLUSTER_LIB");
+ ClusterFixture::Args args = list_of<string>("--auth")("no")("--no-module-dir")("--no-data-dir")("--load-module")(clusterLib.str());
+ ClusterFixture cluster(2, args, -1);
+ Client c0(cluster[0], "foo");
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size()); // wait for both.
+ cluster.killWithSilencer(0, c0.connection, 9);
+ Client c1(cluster[1], "foo"); // Using same name, should be cleaned up.
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ClusterFixture.cpp b/cpp/src/tests/ClusterFixture.cpp
new file mode 100644
index 0000000000..fd90ed170e
--- /dev/null
+++ b/cpp/src/tests/ClusterFixture.cpp
@@ -0,0 +1,160 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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 "unit_test.h"
+#include "ForkedBroker.h"
+#include "BrokerFixture.h"
+
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/FailoverListener.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Logger.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/assign.hpp>
+
+#include <string>
+#include <iostream>
+#include <iterator>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <iterator>
+
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using qpid::sys::TIME_SEC;
+using qpid::broker::Broker;
+using boost::shared_ptr;
+using qpid::cluster::Cluster;
+using boost::assign::list_of;
+
+
+#include "ClusterFixture.h"
+
+namespace qpid {
+namespace tests {
+
+ClusterFixture::ClusterFixture(size_t n, const Args& args_, int localIndex_)
+ : name(Uuid(true).str()), localIndex(localIndex_), userArgs(args_)
+{
+ add(n);
+}
+
+ClusterFixture::ClusterFixture(size_t n, boost::function<void (Args&, size_t)> updateArgs_, int localIndex_)
+ : name(Uuid(true).str()), localIndex(localIndex_), updateArgs(updateArgs_)
+{
+ add(n);
+}
+
+ClusterFixture::Args ClusterFixture::makeArgs(const std::string& prefix, size_t index) {
+ Args args = list_of<string>("qpidd ")
+ ("--cluster-name")(name)
+ ("--log-prefix")(prefix);
+ args.insert(args.end(), userArgs.begin(), userArgs.end());
+ if (updateArgs) updateArgs(args, index);
+ return args;
+}
+
+void ClusterFixture::add() {
+ if (size() != size_t(localIndex)) { // fork a broker process.
+ std::ostringstream os; os << "fork" << size();
+ std::string prefix = os.str();
+ forkedBrokers.push_back(shared_ptr<ForkedBroker>(new ForkedBroker(makeArgs(prefix, size()))));
+ push_back(forkedBrokers.back()->getPort());
+ }
+ else { // Run in this process
+ addLocal();
+ }
+}
+
+namespace {
+/** Parse broker & cluster options */
+Broker::Options parseOpts(size_t argc, const char* argv[]) {
+ Broker::Options opts;
+ Plugin::addOptions(opts); // Pick up cluster options.
+ opts.parse(argc, argv, "", true); // Allow-unknown for --load-module
+ return opts;
+}
+}
+
+void ClusterFixture::addLocal() {
+ assert(int(size()) == localIndex);
+ ostringstream os; os << "local" << localIndex;
+ string prefix = os.str();
+ Args args(makeArgs(prefix, localIndex));
+ vector<const char*> argv(args.size());
+ transform(args.begin(), args.end(), argv.begin(), boost::bind(&string::c_str, _1));
+ qpid::log::Logger::instance().setPrefix(prefix);
+ localBroker.reset(new BrokerFixture(parseOpts(argv.size(), &argv[0])));
+ push_back(localBroker->getPort());
+ forkedBrokers.push_back(shared_ptr<ForkedBroker>());
+}
+
+bool ClusterFixture::hasLocal() const { return localIndex >= 0 && size_t(localIndex) < size(); }
+
+/** Kill a forked broker with sig, or shutdown localBroker if n==0. */
+void ClusterFixture::kill(size_t n, int sig) {
+ if (n == size_t(localIndex))
+ localBroker->broker->shutdown();
+ else
+ forkedBrokers[n]->kill(sig);
+}
+
+/** Kill a broker and suppressing errors from closing connection c. */
+void ClusterFixture::killWithSilencer(size_t n, client::Connection& c, int sig) {
+ ScopedSuppressLogging sl;
+ kill(n,sig);
+ try { c.close(); } catch(...) {}
+}
+
+/**
+ * Get the known broker ports from a Connection.
+ *@param n if specified wait for the cluster size to be n, up to a timeout.
+ */
+std::set<int> knownBrokerPorts(qpid::client::Connection& c, int n) {
+ FailoverListener fl(c);
+ std::vector<qpid::Url> urls = fl.getKnownBrokers();
+ if (n >= 0 && unsigned(n) != urls.size()) {
+ // Retry up to 10 secs in .1 second intervals.
+ for (size_t retry=100; urls.size() != unsigned(n) && retry != 0; --retry) {
+ qpid::sys::usleep(1000*100); // 0.1 secs
+ urls = fl.getKnownBrokers();
+ }
+ }
+ std::set<int> s;
+ for (std::vector<qpid::Url>::const_iterator i = urls.begin(); i != urls.end(); ++i)
+ s.insert((*i)[0].get<qpid::TcpAddress>()->port);
+ return s;
+}
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ClusterFixture.h b/cpp/src/tests/ClusterFixture.h
new file mode 100644
index 0000000000..1eee32b9a4
--- /dev/null
+++ b/cpp/src/tests/ClusterFixture.h
@@ -0,0 +1,115 @@
+#ifndef CLUSTER_FIXTURE_H
+#define CLUSTER_FIXTURE_H
+
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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 "unit_test.h"
+#include "ForkedBroker.h"
+#include "BrokerFixture.h"
+
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/FailoverListener.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/framing/AMQBody.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/enum.h"
+#include "qpid/log/Logger.h"
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <iostream>
+#include <iterator>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <iterator>
+
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using qpid::sys::TIME_SEC;
+using qpid::broker::Broker;
+using boost::shared_ptr;
+using qpid::cluster::Cluster;
+
+namespace qpid {
+namespace tests {
+
+/** Cluster fixture is a vector of ports for the replicas.
+ *
+ * At most one replica (by default replica 0) is in the current
+ * process, all others are forked as children.
+ */
+class ClusterFixture : public vector<uint16_t> {
+ public:
+ typedef std::vector<std::string> Args;
+
+ /** @param localIndex can be -1 meaning don't automatically start a local broker.
+ * A local broker can be started with addLocal().
+ */
+ ClusterFixture(size_t n, const Args& args, int localIndex=-1);
+
+ /**@param updateArgs function is passed the index of the cluster member and can update the arguments. */
+ ClusterFixture(size_t n, boost::function<void (Args&, size_t)> updateArgs, int localIndex=-1);
+
+ void add(size_t n) { for (size_t i=0; i < n; ++i) add(); }
+ void add(); // Add a broker.
+ void setup();
+
+ bool hasLocal() const;
+
+ /** Kill a forked broker with sig, or shutdown localBroker. */
+ void kill(size_t n, int sig=SIGINT);
+
+ /** Kill a broker and suppressing errors from closing connection c. */
+ void killWithSilencer(size_t n, client::Connection& c, int sig=SIGINT);
+
+ private:
+
+ void addLocal(); // Add a local broker.
+ Args makeArgs(const std::string& prefix, size_t index);
+ string name;
+ std::auto_ptr<BrokerFixture> localBroker;
+ int localIndex;
+ std::vector<shared_ptr<ForkedBroker> > forkedBrokers;
+ Args userArgs;
+ boost::function<void (Args&, size_t)> updateArgs;
+};
+
+/**
+ * Get the known broker ports from a Connection.
+ *@param n if specified wait for the cluster size to be n, up to a timeout.
+ */
+std::set<int> knownBrokerPorts(qpid::client::Connection& source, int n=-1);
+
+}} // namespace qpid::tests
+
+#endif /*!CLUSTER_FIXTURE_H*/
diff --git a/cpp/src/tests/ConnectionOptions.h b/cpp/src/tests/ConnectionOptions.h
index 1579ac0b57..6fd6c2c63f 100644
--- a/cpp/src/tests/ConnectionOptions.h
+++ b/cpp/src/tests/ConnectionOptions.h
@@ -10,9 +10,9 @@
* 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
@@ -25,6 +25,8 @@
#include "qpid/client/ConnectionSettings.h"
#include "qpid/Options.h"
+namespace qpid {
+
/**
* Options parser for ConnectionOptions.
*/
@@ -35,19 +37,26 @@ struct ConnectionOptions : public qpid::Options,
{
using namespace qpid;
addOptions()
- ("broker,b", optValue(host, "HOST"), "Broker host to connect to")
+ ("broker,b", optValue(host, "HOST"), "Broker host to connect to")
("port,p", optValue(port, "PORT"), "Broker port to connect to")
+ ("protocol,P", optValue(protocol, "tcp|rdma"), "Protocol to use for broker connection")
("virtualhost,v", optValue(virtualhost, "VHOST"), "virtual host")
("username", optValue(username, "USER"), "user name for broker log in.")
("password", optValue(password, "PASSWORD"), "password for broker log in.")
("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.")
("locale", optValue(locale, "LOCALE"), "locale to use.")
("max-channels", optValue(maxChannels, "N"), "the maximum number of channels the client requires.")
+ ("heartbeat", optValue(heartbeat, "N"), "Desired heartbeat interval in seconds.")
("max-frame-size", optValue(maxFrameSize, "N"), "the maximum frame size to request.")
- ("bounds-multiplier", optValue(bounds, "N"),
+ ("bounds-multiplier", optValue(bounds, "N"),
"bound size of write queue (as a multiple of the max frame size).")
- ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay");
+ ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay")
+ ("service", optValue(service, "SERVICE-NAME"), "SASL service name.")
+ ("min-ssf", optValue(minSsf, "N"), "Minimum acceptable strength for SASL security layer")
+ ("max-ssf", optValue(maxSsf, "N"), "Maximum acceptable strength for SASL security layer");
}
};
+} // namespace qpid
+
#endif /*!QPID_CLIENT_CONNECTIONOPTIONS_H*/
diff --git a/cpp/src/tests/ConsoleTest.cpp b/cpp/src/tests/ConsoleTest.cpp
new file mode 100644
index 0000000000..107472ed9e
--- /dev/null
+++ b/cpp/src/tests/ConsoleTest.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.
+ *
+ */
+
+#include "qpid/console/Package.h"
+#include "qpid/console/ClassKey.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ConsoleTestSuite)
+
+using namespace qpid::framing;
+using namespace qpid::console;
+
+QPID_AUTO_TEST_CASE(testClassKey) {
+ uint8_t hash[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+ ClassKey k("com.redhat.test", "class", hash);
+
+ BOOST_CHECK_EQUAL(k.getPackageName(), "com.redhat.test");
+ BOOST_CHECK_EQUAL(k.getClassName(), "class");
+ BOOST_CHECK_EQUAL(k.getHashString(), "00010203-04050607-08090a0b-0c0d0e0f");
+ BOOST_CHECK_EQUAL(k.str(), "com.redhat.test:class(00010203-04050607-08090a0b-0c0d0e0f)");
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/DeliveryRecordTest.cpp b/cpp/src/tests/DeliveryRecordTest.cpp
index 43161b4065..17f9a0d148 100644
--- a/cpp/src/tests/DeliveryRecordTest.cpp
+++ b/cpp/src/tests/DeliveryRecordTest.cpp
@@ -8,9 +8,9 @@
* 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
@@ -31,6 +31,9 @@ using namespace qpid::framing;
using boost::dynamic_pointer_cast;
using std::list;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(DeliveryRecordTestSuite)
QPID_AUTO_TEST_CASE(testSort)
@@ -45,16 +48,19 @@ QPID_AUTO_TEST_CASE(testSort)
list<DeliveryRecord> records;
for (list<SequenceNumber>::iterator i = ids.begin(); i != ids.end(); i++) {
- records.push_back(DeliveryRecord(QueuedMessage(0), Queue::shared_ptr(), "tag", DeliveryToken::shared_ptr(), *i, false, false));
+ DeliveryRecord r(QueuedMessage(0), Queue::shared_ptr(), "tag", false, false, false);
+ r.setId(*i);
+ records.push_back(r);
}
records.sort();
SequenceNumber expected(0);
for (list<DeliveryRecord>::iterator i = records.begin(); i != records.end(); i++) {
- BOOST_CHECK(i->matches(++expected));
+ BOOST_CHECK(i->getId() == ++expected);
}
}
QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/DispatcherTest.cpp b/cpp/src/tests/DispatcherTest.cpp
index 7631956acc..17b3b4e3e6 100644
--- a/cpp/src/tests/DispatcherTest.cpp
+++ b/cpp/src/tests/DispatcherTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -20,7 +20,10 @@
*/
#include "qpid/sys/Poller.h"
+#include "qpid/sys/IOHandle.h"
#include "qpid/sys/Dispatcher.h"
+#include "qpid/sys/DispatchHandle.h"
+#include "qpid/sys/posix/PrivatePosix.h"
#include "qpid/sys/Thread.h"
#include <sys/types.h>
@@ -28,6 +31,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
+#include <signal.h>
#include <iostream>
#include <boost/bind.hpp>
@@ -35,6 +39,9 @@
using namespace std;
using namespace qpid::sys;
+namespace qpid {
+namespace tests {
+
int writeALot(int fd, const string& s) {
int bytesWritten = 0;
do {
@@ -42,7 +49,7 @@ int writeALot(int fd, const string& s) {
int lastWrite = ::write(fd, s.c_str(), s.size());
if ( lastWrite >= 0) {
bytesWritten += lastWrite;
- }
+ }
} while (errno != EAGAIN);
return bytesWritten;
}
@@ -50,13 +57,13 @@ int writeALot(int fd, const string& s) {
int readALot(int fd) {
int bytesRead = 0;
char buf[10240];
-
+
do {
errno = 0;
int lastRead = ::read(fd, buf, sizeof(buf));
if ( lastRead >= 0) {
bytesRead += lastRead;
- }
+ }
} while (errno != EAGAIN);
return bytesRead;
}
@@ -74,55 +81,169 @@ void reader(DispatchHandle& h, int fd) {
h.rewatch();
}
-int main(int argc, char** argv)
+void rInterrupt(DispatchHandle&) {
+ cerr << "R";
+}
+
+void wInterrupt(DispatchHandle&) {
+ cerr << "W";
+}
+
+DispatchHandle::Callback rcb = rInterrupt;
+DispatchHandle::Callback wcb = wInterrupt;
+
+DispatchHandleRef *volatile rh = 0;
+DispatchHandleRef *volatile wh = 0;
+
+volatile bool stopWait = false;
+volatile bool phase1finished = false;
+
+timer_t timer;
+
+void stop_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) {
+ stopWait = true;
+}
+
+void timer_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) {
+ static int count = 0;
+ if (count++ < 10) {
+ rh->call(rcb);
+ wh->call(wcb);
+ } else {
+ phase1finished = true;
+ assert(::timer_delete(timer) == 0);
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int /*argc*/, char** /*argv*/)
{
// Create poller
Poller::shared_ptr poller(new Poller);
-
+
// Create dispatcher thread
Dispatcher d(poller);
Dispatcher d1(poller);
- //Dispatcher d2(poller);
- //Dispatcher d3(poller);
+ Dispatcher d2(poller);
+ Dispatcher d3(poller);
Thread dt(d);
Thread dt1(d1);
- //Thread dt2(d2);
- //Thread dt3(d3);
+ Thread dt2(d2);
+ Thread dt3(d3);
// Setup sender and receiver
int sv[2];
- int rc = ::socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
+ int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
assert(rc >= 0);
-
+
// Set non-blocking
rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK);
assert(rc >= 0);
rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK);
assert(rc >= 0);
-
+
// Make up a large string
string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;";
for (int i = 0; i < 8; i++)
testString += testString;
- DispatchHandle rh(sv[0], boost::bind(reader, _1, sv[0]), 0);
- DispatchHandle wh(sv[1], 0, boost::bind(writer, _1, sv[1], testString));
+ PosixIOHandle f0(sv[0]);
+ PosixIOHandle f1(sv[1]);
+
+ rh = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0);
+ wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0);
+
+ rh->startWatch(poller);
+ wh->startWatch(poller);
+
+ // Set up a regular itimer interupt
+
+ // Ignore signal in this thread
+ ::sigset_t sm;
+ ::sigemptyset(&sm);
+ ::sigaddset(&sm, SIGRTMIN);
+ ::pthread_sigmask(SIG_BLOCK, &sm, 0);
+
+ // Signal handling
+ struct ::sigaction sa;
+ sa.sa_sigaction = timer_handler;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ ::sigemptyset(&sa.sa_mask);
+ rc = ::sigaction(SIGRTMIN, &sa,0);
+ assert(rc == 0);
+
+ ::sigevent se;
+ ::memset(&se, 0, sizeof(se)); // Clear to make valgrind happy (this *is* the neatest way to do this portably - sigh)
+ se.sigev_notify = SIGEV_SIGNAL;
+ se.sigev_signo = SIGRTMIN;
+ rc = ::timer_create(CLOCK_REALTIME, &se, &timer);
+ assert(rc == 0);
+
+ itimerspec ts = {
+ /*.it_value = */ {2, 0}, // s, ns
+ /*.it_interval = */ {2, 0}}; // s, ns
+
+ rc = ::timer_settime(timer, 0, &ts, 0);
+ assert(rc == 0);
+
+ // wait
+ while (!phase1finished) {
+ ::sleep(1);
+ }
+
+ // Now test deleting/creating DispatchHandles in tight loop, so that we are likely to still be using the
+ // attached PollerHandles after deleting the DispatchHandle
+ DispatchHandleRef* t = wh;
+ wh = 0;
+ delete t;
+ t = rh;
+ rh = 0;
+ delete t;
+
+ sa.sa_sigaction = stop_handler;
+ rc = ::sigaction(SIGRTMIN, &sa,0);
+ assert(rc == 0);
+
+ itimerspec nts = {
+ /*.it_value = */ {30, 0}, // s, ns
+ /*.it_interval = */ {30, 0}}; // s, ns
+
+ rc = ::timer_create(CLOCK_REALTIME, &se, &timer);
+ assert(rc == 0);
+ rc = ::timer_settime(timer, 0, &nts, 0);
+ assert(rc == 0);
+
+ DispatchHandleRef* rh1;
+ DispatchHandleRef* wh1;
+
+ struct timespec w = {0, 1000000};
+ while (!stopWait) {
+ rh1 = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0);
+ wh1 = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0);
+ rh1->startWatch(poller);
+ wh1->startWatch(poller);
+
+ ::nanosleep(&w, 0);
+
+ delete wh1;
+ delete rh1;
+ }
+
+ rc = ::timer_delete(timer);
+ assert(rc == 0);
- rh.watch(poller);
- wh.watch(poller);
-
- // wait 2 minutes then shutdown
- sleep(60);
-
poller->shutdown();
dt.join();
dt1.join();
- //dt2.join();
- //dt3.join();
+ dt2.join();
+ dt3.join();
- cout << "Wrote: " << writtenBytes << "\n";
+ cout << "\nWrote: " << writtenBytes << "\n";
cout << "Read: " << readBytes << "\n";
-
+
return 0;
}
diff --git a/cpp/src/tests/DtxWorkRecordTest.cpp b/cpp/src/tests/DtxWorkRecordTest.cpp
index c7c1b460ff..9d7666dca4 100644
--- a/cpp/src/tests/DtxWorkRecordTest.cpp
+++ b/cpp/src/tests/DtxWorkRecordTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -27,6 +27,9 @@
using namespace qpid::broker;
using boost::static_pointer_cast;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(DtxWorkRecordTestSuite)
QPID_AUTO_TEST_CASE(testOnePhaseCommit){
@@ -44,7 +47,7 @@ QPID_AUTO_TEST_CASE(testOnePhaseCommit){
DtxBuffer::shared_ptr bufferB(new DtxBuffer());
bufferB->enlist(static_pointer_cast<TxOp>(opB));
bufferB->markEnded();
-
+
DtxWorkRecord work("my-xid", &store);
work.add(bufferA);
work.add(bufferB);
@@ -77,7 +80,7 @@ QPID_AUTO_TEST_CASE(testFailOnOnePhaseCommit){
DtxBuffer::shared_ptr bufferC(new DtxBuffer());
bufferC->enlist(static_pointer_cast<TxOp>(opC));
bufferC->markEnded();
-
+
DtxWorkRecord work("my-xid", &store);
work.add(bufferA);
work.add(bufferB);
@@ -108,7 +111,7 @@ QPID_AUTO_TEST_CASE(testTwoPhaseCommit){
DtxBuffer::shared_ptr bufferB(new DtxBuffer());
bufferB->enlist(static_pointer_cast<TxOp>(opB));
bufferB->markEnded();
-
+
DtxWorkRecord work("my-xid", &store);
work.add(bufferA);
work.add(bufferB);
@@ -142,7 +145,7 @@ QPID_AUTO_TEST_CASE(testFailOnTwoPhaseCommit){
DtxBuffer::shared_ptr bufferC(new DtxBuffer());
bufferC->enlist(static_pointer_cast<TxOp>(opC));
bufferC->markEnded();
-
+
DtxWorkRecord work("my-xid", &store);
work.add(bufferA);
work.add(bufferB);
@@ -171,7 +174,7 @@ QPID_AUTO_TEST_CASE(testRollback){
DtxBuffer::shared_ptr bufferB(new DtxBuffer());
bufferB->enlist(static_pointer_cast<TxOp>(opB));
bufferB->markEnded();
-
+
DtxWorkRecord work("my-xid", &store);
work.add(bufferA);
work.add(bufferB);
@@ -187,3 +190,4 @@ QPID_AUTO_TEST_CASE(testRollback){
QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ExchangeTest.cpp b/cpp/src/tests/ExchangeTest.cpp
index e496f0c478..88a1cd99c2 100644
--- a/cpp/src/tests/ExchangeTest.cpp
+++ b/cpp/src/tests/ExchangeTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -39,9 +39,12 @@ using namespace qpid::framing;
using namespace qpid::sys;
using namespace qpid;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(ExchangeTestSuite)
-QPID_AUTO_TEST_CASE(testMe)
+QPID_AUTO_TEST_CASE(testMe)
{
Queue::shared_ptr queue(new Queue("queue", true));
Queue::shared_ptr queue2(new Queue("queue2", true));
@@ -57,7 +60,7 @@ QPID_AUTO_TEST_CASE(testMe)
queue.reset();
queue2.reset();
- intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "key", "id"));
+ intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "key", false, "id"));
DeliverableMessage msg(msgPtr);
topic.route(msg, "abc", 0);
direct.route(msg, "abc", 0);
@@ -70,15 +73,15 @@ QPID_AUTO_TEST_CASE(testIsBound)
Queue::shared_ptr b(new Queue("b", true));
Queue::shared_ptr c(new Queue("c", true));
Queue::shared_ptr d(new Queue("d", true));
-
+
string k1("abc");
string k2("def");
string k3("xyz");
FanOutExchange fanout("fanout");
- fanout.bind(a, "", 0);
- fanout.bind(b, "", 0);
- fanout.bind(c, "", 0);
+ BOOST_CHECK(fanout.bind(a, "", 0));
+ BOOST_CHECK(fanout.bind(b, "", 0));
+ BOOST_CHECK(fanout.bind(c, "", 0));
BOOST_CHECK(fanout.isBound(a, 0, 0));
BOOST_CHECK(fanout.isBound(b, 0, 0));
@@ -86,10 +89,10 @@ QPID_AUTO_TEST_CASE(testIsBound)
BOOST_CHECK(!fanout.isBound(d, 0, 0));
DirectExchange direct("direct");
- direct.bind(a, k1, 0);
- direct.bind(a, k3, 0);
- direct.bind(b, k2, 0);
- direct.bind(c, k1, 0);
+ BOOST_CHECK(direct.bind(a, k1, 0));
+ BOOST_CHECK(direct.bind(a, k3, 0));
+ BOOST_CHECK(direct.bind(b, k2, 0));
+ BOOST_CHECK(direct.bind(c, k1, 0));
BOOST_CHECK(direct.isBound(a, 0, 0));
BOOST_CHECK(direct.isBound(a, &k1, 0));
@@ -104,10 +107,10 @@ QPID_AUTO_TEST_CASE(testIsBound)
BOOST_CHECK(!direct.isBound(d, &k3, 0));
TopicExchange topic("topic");
- topic.bind(a, k1, 0);
- topic.bind(a, k3, 0);
- topic.bind(b, k2, 0);
- topic.bind(c, k1, 0);
+ BOOST_CHECK(topic.bind(a, k1, 0));
+ BOOST_CHECK(topic.bind(a, k3, 0));
+ BOOST_CHECK(topic.bind(b, k2, 0));
+ BOOST_CHECK(topic.bind(c, k1, 0));
BOOST_CHECK(topic.isBound(a, 0, 0));
BOOST_CHECK(topic.isBound(a, &k1, 0));
@@ -139,7 +142,7 @@ QPID_AUTO_TEST_CASE(testIsBound)
headers.bind(a, "", &args3);
headers.bind(b, "", &args2);
headers.bind(c, "", &args1);
-
+
BOOST_CHECK(headers.isBound(a, 0, 0));
BOOST_CHECK(headers.isBound(a, 0, &args1));
BOOST_CHECK(headers.isBound(a, 0, &args3));
@@ -153,7 +156,7 @@ QPID_AUTO_TEST_CASE(testIsBound)
BOOST_CHECK(!headers.isBound(d, 0, &args3));
}
-QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare)
+QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare)
{
ExchangeRegistry exchanges;
exchanges.declare("my-exchange", "direct", false, FieldTable());
@@ -162,7 +165,125 @@ QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare)
exchanges.get("my-exchange");
} catch (const NotFoundException&) {}
std::pair<Exchange::shared_ptr, bool> response = exchanges.declare("my-exchange", "direct", false, FieldTable());
- BOOST_CHECK_EQUAL(string("direct"), response.first->getType());
+ BOOST_CHECK_EQUAL(string("direct"), response.first->getType());
+}
+
+intrusive_ptr<Message> cmessage(std::string exchange, std::string routingKey) {
+ intrusive_ptr<Message> msg(new Message());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ return msg;
+}
+
+QPID_AUTO_TEST_CASE(testSequenceOptions)
+{
+ FieldTable args;
+ args.setInt("qpid.msg_sequence",1);
+ char* buff = new char[10000];
+ framing::Buffer buffer(buff,10000);
+ {
+ DirectExchange direct("direct1", false, args);
+
+ intrusive_ptr<Message> msg1 = cmessage("e", "A");
+ intrusive_ptr<Message> msg2 = cmessage("e", "B");
+ intrusive_ptr<Message> msg3 = cmessage("e", "C");
+
+ DeliverableMessage dmsg1(msg1);
+ DeliverableMessage dmsg2(msg2);
+ DeliverableMessage dmsg3(msg3);
+
+ direct.route(dmsg1, "abc", 0);
+ direct.route(dmsg2, "abc", 0);
+ direct.route(dmsg3, "abc", 0);
+
+ BOOST_CHECK_EQUAL(1, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+ BOOST_CHECK_EQUAL(2, msg2->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+ BOOST_CHECK_EQUAL(3, msg3->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ FanOutExchange fanout("fanout1", false, args);
+ HeadersExchange header("headers1", false, args);
+ TopicExchange topic ("topic1", false, args);
+
+ // check other exchanges, that they preroute
+ intrusive_ptr<Message> msg4 = cmessage("e", "A");
+ intrusive_ptr<Message> msg5 = cmessage("e", "B");
+ intrusive_ptr<Message> msg6 = cmessage("e", "C");
+
+ DeliverableMessage dmsg4(msg4);
+ DeliverableMessage dmsg5(msg5);
+ DeliverableMessage dmsg6(msg6);
+
+ fanout.route(dmsg4, "abc", 0);
+ BOOST_CHECK_EQUAL(1, msg4->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ FieldTable headers;
+ header.route(dmsg5, "abc", &headers);
+ BOOST_CHECK_EQUAL(1, msg5->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ topic.route(dmsg6, "abc", 0);
+ BOOST_CHECK_EQUAL(1, msg6->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+ direct.encode(buffer);
+ }
+ {
+
+ ExchangeRegistry exchanges;
+ buffer.reset();
+ DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer);
+
+ intrusive_ptr<Message> msg1 = cmessage("e", "A");
+ DeliverableMessage dmsg1(msg1);
+ exch_dec->route(dmsg1, "abc", 0);
+
+ BOOST_CHECK_EQUAL(4, msg1->getApplicationHeaders()->getAsInt64("qpid.msg_sequence"));
+
+ }
+ delete [] buff;
+}
+
+QPID_AUTO_TEST_CASE(testIVEOption)
+{
+ FieldTable args;
+ args.setInt("qpid.ive",1);
+ DirectExchange direct("direct1", false, args);
+ FanOutExchange fanout("fanout1", false, args);
+ HeadersExchange header("headers1", false, args);
+ TopicExchange topic ("topic1", false, args);
+
+ intrusive_ptr<Message> msg1 = cmessage("direct1", "abc");
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString("a", "abc");
+ DeliverableMessage dmsg1(msg1);
+
+ FieldTable args2;
+ args2.setString("x-match", "any");
+ args2.setString("a", "abc");
+
+ direct.route(dmsg1, "abc", 0);
+ fanout.route(dmsg1, "abc", 0);
+ header.route(dmsg1, "abc", &args2);
+ topic.route(dmsg1, "abc", 0);
+ Queue::shared_ptr queue(new Queue("queue", true));
+ Queue::shared_ptr queue1(new Queue("queue1", true));
+ Queue::shared_ptr queue2(new Queue("queue2", true));
+ Queue::shared_ptr queue3(new Queue("queue3", true));
+
+ BOOST_CHECK(HeadersExchange::match(args2, msg1->getProperties<MessageProperties>()->getApplicationHeaders()));
+
+ BOOST_CHECK(direct.bind(queue, "abc", 0));
+ BOOST_CHECK(fanout.bind(queue1, "abc", 0));
+ BOOST_CHECK(header.bind(queue2, "", &args2));
+ BOOST_CHECK(topic.bind(queue3, "abc", 0));
+
+ BOOST_CHECK_EQUAL(1u,queue->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue1->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue2->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue3->getMessageCount());
+
}
+
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/FieldTable.cpp b/cpp/src/tests/FieldTable.cpp
index 315801b428..fe2a14ec03 100644
--- a/cpp/src/tests/FieldTable.cpp
+++ b/cpp/src/tests/FieldTable.cpp
@@ -7,9 +7,9 @@
* 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
@@ -19,21 +19,26 @@
*
*/
#include <iostream>
+#include "qpid/framing/Array.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/FieldValue.h"
-#include <alloca.h>
+#include "qpid/framing/List.h"
+#include "qpid/sys/alloca.h"
#include "unit_test.h"
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(FieldTableTestSuite)
QPID_AUTO_TEST_CASE(testMe)
{
FieldTable ft;
ft.setString("A", "BCDE");
- BOOST_CHECK(string("BCDE") == ft.getString("A"));
+ BOOST_CHECK(string("BCDE") == ft.getAsString("A"));
char buff[100];
Buffer wbuffer(buff, 100);
@@ -42,7 +47,7 @@ QPID_AUTO_TEST_CASE(testMe)
Buffer rbuffer(buff, 100);
FieldTable ft2;
rbuffer.get(ft2);
- BOOST_CHECK(string("BCDE") == ft2.getString("A"));
+ BOOST_CHECK(string("BCDE") == ft2.getAsString("A"));
}
@@ -55,11 +60,11 @@ QPID_AUTO_TEST_CASE(testAssignment)
a.setInt("B", 1234);
b = a;
a.setString("A", "CCCC");
-
- BOOST_CHECK(string("CCCC") == a.getString("A"));
- BOOST_CHECK(string("BBBB") == b.getString("A"));
- BOOST_CHECK_EQUAL(1234, a.getInt("B"));
- BOOST_CHECK_EQUAL(1234, b.getInt("B"));
+
+ BOOST_CHECK(string("CCCC") == a.getAsString("A"));
+ BOOST_CHECK(string("BBBB") == b.getAsString("A"));
+ BOOST_CHECK_EQUAL(1234, a.getAsInt("B"));
+ BOOST_CHECK_EQUAL(1234, b.getAsInt("B"));
BOOST_CHECK(IntegerValue(1234) == *a.get("B"));
BOOST_CHECK(IntegerValue(1234) == *b.get("B"));
@@ -67,19 +72,142 @@ QPID_AUTO_TEST_CASE(testAssignment)
{
FieldTable c;
c = a;
-
- char* buff = static_cast<char*>(::alloca(c.size()));
- Buffer wbuffer(buff, c.size());
+
+ char* buff = static_cast<char*>(::alloca(c.encodedSize()));
+ Buffer wbuffer(buff, c.encodedSize());
wbuffer.put(c);
- Buffer rbuffer(buff, c.size());
+ Buffer rbuffer(buff, c.encodedSize());
rbuffer.get(d);
BOOST_CHECK_EQUAL(c, d);
- BOOST_CHECK(string("CCCC") == c.getString("A"));
+ BOOST_CHECK(string("CCCC") == c.getAsString("A"));
BOOST_CHECK(IntegerValue(1234) == *c.get("B"));
}
- BOOST_CHECK(string("CCCC") == d.getString("A"));
+ BOOST_CHECK(string("CCCC") == d.getAsString("A"));
BOOST_CHECK(IntegerValue(1234) == *d.get("B"));
}
+
+QPID_AUTO_TEST_CASE(testNestedValues)
+{
+ double d = 1.2345;
+ uint32_t u = 101;
+ char buff[1000];
+ {
+ FieldTable a;
+ FieldTable b;
+ std::vector<std::string> items;
+ items.push_back("one");
+ items.push_back("two");
+ Array c(items);
+ List list;
+ list.push_back(List::ValuePtr(new Str16Value("red")));
+ list.push_back(List::ValuePtr(new Unsigned32Value(u)));
+ list.push_back(List::ValuePtr(new Str8Value("yellow")));
+ list.push_back(List::ValuePtr(new DoubleValue(d)));
+
+ a.setString("id", "A");
+ b.setString("id", "B");
+ a.setTable("B", b);
+ a.setArray("C", c);
+ a.set("my-list", FieldTable::ValuePtr(new ListValue(list)));
+
+
+ Buffer wbuffer(buff, 100);
+ wbuffer.put(a);
+ }
+ {
+ Buffer rbuffer(buff, 100);
+ FieldTable a;
+ FieldTable b;
+ Array c;
+ rbuffer.get(a);
+ BOOST_CHECK(string("A") == a.getAsString("id"));
+ a.getTable("B", b);
+ BOOST_CHECK(string("B") == b.getAsString("id"));
+ a.getArray("C", c);
+ std::vector<std::string> items;
+ c.collect(items);
+ BOOST_CHECK((uint) 2 == items.size());
+ BOOST_CHECK(string("one") == items[0]);
+ BOOST_CHECK(string("two") == items[1]);
+
+ List list;
+ BOOST_CHECK(a.get("my-list")->get<List>(list));
+ List::const_iterator i = list.begin();
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("red"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(u, (uint32_t) (*i)->get<int>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("yellow"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(d, (*i)->get<double>());
+
+ i++;
+ BOOST_CHECK(i == list.end());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testFloatAndDouble)
+{
+ char buff[100];
+ float f = 5.672f;
+ double d = 56.720001;
+ {
+ FieldTable a;
+ a.setString("string", "abc");
+ a.setInt("int", 5672);
+ a.setFloat("float", f);
+ a.setDouble("double", d);
+
+ Buffer wbuffer(buff, 100);
+ wbuffer.put(a);
+ }
+ {
+ Buffer rbuffer(buff, 100);
+ FieldTable a;
+ rbuffer.get(a);
+ BOOST_CHECK(string("abc") == a.getAsString("string"));
+ BOOST_CHECK(5672 == a.getAsInt("int"));
+ float f2;
+ BOOST_CHECK(!a.getFloat("string", f2));
+ BOOST_CHECK(!a.getFloat("int", f2));
+ BOOST_CHECK(a.getFloat("float", f2));
+ BOOST_CHECK(f2 == f);
+
+ double d2;
+ BOOST_CHECK(!a.getDouble("string", d2));
+ BOOST_CHECK(!a.getDouble("int", d2));
+ BOOST_CHECK(a.getDouble("double", d2));
+ BOOST_CHECK(d2 == d);
+ }
+}
+
+QPID_AUTO_TEST_CASE(test64GetAndSetConverts)
+{
+ FieldTable args;
+ args.setInt64("a",100);
+ args.setInt64("b",-(int64_t) ((int64_t) 1<<34));
+
+ args.setUInt64("c",1u);
+ args.setUInt64("d",(uint64_t) ((uint64_t) 1<<34));
+ BOOST_CHECK_EQUAL(1u, args.getAsUInt64("c"));
+ BOOST_CHECK_EQUAL(100u, args.getAsUInt64("a"));
+ BOOST_CHECK_EQUAL(1, args.getAsInt64("c"));
+ BOOST_CHECK_EQUAL(100, args.getAsInt64("a"));
+ BOOST_CHECK_EQUAL(-(int64_t) ((int64_t) 1<<34), args.getAsInt64("b"));
+ BOOST_CHECK_EQUAL((uint64_t) ((uint64_t) 1<<34), args.getAsUInt64("d"));
+ BOOST_CHECK_EQUAL((int64_t) ((int64_t) 1<<34), args.getAsInt64("d"));
+
+}
+
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/FieldValue.cpp b/cpp/src/tests/FieldValue.cpp
index eacf098034..0ebd0d7d44 100644
--- a/cpp/src/tests/FieldValue.cpp
+++ b/cpp/src/tests/FieldValue.cpp
@@ -6,9 +6,9 @@
* 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
@@ -20,21 +20,24 @@
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(FieldValueTestSuite)
using namespace qpid::framing;
-StringValue s("abc");
+Str16Value s("abc");
IntegerValue i(42);
//DecimalValue d(1234,2);
//FieldTableValue ft;
//EmptyValue e;
-QPID_AUTO_TEST_CASE(testStringValueEquals)
+QPID_AUTO_TEST_CASE(testStr16ValueEquals)
{
-
- BOOST_CHECK(StringValue("abc") == s);
- BOOST_CHECK(StringValue("foo") != s);
+
+ BOOST_CHECK(Str16Value("abc") == s);
+ BOOST_CHECK(Str16Value("foo") != s);
BOOST_CHECK(s != i);
BOOST_CHECK(s.convertsTo<std::string>() == true);
BOOST_CHECK(s.convertsTo<int>() == false);
@@ -73,7 +76,7 @@ QPID_AUTO_TEST_CASE(testFieldTableValueEquals)
BOOST_CHECK_EQUAL(std::string("FOO"),
ft.getValue().getString("foo"));
BOOST_CHECK_EQUAL(7, ft.getValue().getInt("magic"));
-
+
FieldTableValue f2;
BOOST_CHECK(ft != f2);
f2.getValue().setString("foo", "FOO");
@@ -88,3 +91,5 @@ QPID_AUTO_TEST_CASE(testFieldTableValueEquals)
#endif
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ForkedBroker.cpp b/cpp/src/tests/ForkedBroker.cpp
new file mode 100644
index 0000000000..e1a96c8e90
--- /dev/null
+++ b/cpp/src/tests/ForkedBroker.cpp
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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 "ForkedBroker.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <signal.h>
+
+using namespace std;
+using qpid::ErrnoException;
+
+namespace std {
+static ostream& operator<<(ostream& o, const qpid::tests::ForkedBroker::Args& a) {
+copy(a.begin(), a.end(), ostream_iterator<string>(o, " "));
+return o;
+}
+}
+
+namespace qpid {
+namespace tests {
+
+ForkedBroker::ForkedBroker(const Args& constArgs) {
+ Args args(constArgs);
+ Args::iterator i = find(args.begin(), args.end(), string("TMP_DATA_DIR"));
+ if (i != args.end()) {
+ args.erase(i);
+ char dd[] = "/tmp/ForkedBroker.XXXXXX";
+ if (!mkdtemp(dd))
+ throw qpid::ErrnoException("Can't create data dir");
+ dataDir = dd;
+ args.push_back("--data-dir");
+ args.push_back(dataDir);
+ }
+ init(args);
+}
+
+ForkedBroker::~ForkedBroker() {
+ try { kill(); }
+ catch (const std::exception& e) {
+ QPID_LOG(error, QPID_MSG("Killing forked broker: " << e.what()));
+ }
+ if (!dataDir.empty())
+ {
+ int unused_ret; // Suppress warnings about ignoring return value.
+ unused_ret = ::system(("rm -rf "+dataDir).c_str());
+ }
+}
+
+void ForkedBroker::kill(int sig) {
+ if (pid == 0) return;
+ int savePid = pid;
+ pid = 0; // Reset pid here in case of an exception.
+ using qpid::ErrnoException;
+ if (::kill(savePid, sig) < 0)
+ throw ErrnoException("kill failed");
+ int status;
+ if (::waitpid(savePid, &status, 0) < 0 && sig != 9)
+ throw ErrnoException("wait for forked process failed");
+ if (WEXITSTATUS(status) != 0 && sig != 9)
+ throw qpid::Exception(QPID_MSG("Forked broker exited with: " << WEXITSTATUS(status)));
+}
+
+bool isLogOption(const std::string& s) {
+ const char * log_enable = "--log-enable",
+ * trace = "--trace";
+ return( (! strncmp(s.c_str(), log_enable, strlen(log_enable))) ||
+ (! strncmp(s.c_str(), trace, strlen(trace)))
+ );
+}
+
+void ForkedBroker::init(const Args& userArgs) {
+ using qpid::ErrnoException;
+ port = 0;
+ int pipeFds[2];
+ if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe");
+ pid = ::fork();
+ if (pid < 0) throw ErrnoException("Fork failed");
+ if (pid) { // parent
+ ::close(pipeFds[1]);
+ FILE* f = ::fdopen(pipeFds[0], "r");
+ if (!f) throw ErrnoException("fopen failed");
+ if (::fscanf(f, "%d", &port) != 1) {
+ if (ferror(f)) throw ErrnoException("Error reading port number from child.");
+ else throw qpid::Exception("EOF reading port number from child.");
+ }
+ ::close(pipeFds[0]);
+ }
+ else { // child
+ ::close(pipeFds[0]);
+ int fd = ::dup2(pipeFds[1], 1); // pipe stdout to the parent.
+ if (fd < 0) throw ErrnoException("dup2 failed");
+ const char* prog = ::getenv("QPIDD_EXEC");
+ if (!prog) prog = "../qpidd"; // This only works from within svn checkout
+ Args args(userArgs);
+ args.push_back("--port=0");
+ // Keep quiet except for errors.
+ if (!::getenv("QPID_TRACE") && !::getenv("QPID_LOG_ENABLE")
+ && find_if(userArgs.begin(), userArgs.end(), isLogOption) == userArgs.end())
+ args.push_back("--log-enable=error+");
+ std::vector<const char*> argv(args.size());
+ std::transform(args.begin(), args.end(), argv.begin(), boost::bind(&std::string::c_str, _1));
+ argv.push_back(0);
+ QPID_LOG(debug, "ForkedBroker exec " << prog << ": " << args);
+ execv(prog, const_cast<char* const*>(&argv[0]));
+ QPID_LOG(critical, "execv failed to start broker: prog=\"" << prog << "\"; args=\"" << args << "\"; errno=" << errno << " (" << std::strerror(errno) << ")");
+ ::exit(1);
+ }
+}
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ForkedBroker.h b/cpp/src/tests/ForkedBroker.h
index a7869ff602..ddbad185d8 100644
--- a/cpp/src/tests/ForkedBroker.h
+++ b/cpp/src/tests/ForkedBroker.h
@@ -1,4 +1,5 @@
#ifndef TESTS_FORKEDBROKER_H
+#define TESTS_FORKEDBROKER_H
/*
@@ -10,9 +11,9 @@
* 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
@@ -30,9 +31,12 @@
#include <stdio.h>
#include <sys/wait.h>
+namespace qpid {
+namespace tests {
+
/**
* Class to fork a broker child process.
- *
+ *
* For most tests a BrokerFixture may be more convenient as it starts
* a broker in the same process which allows you to easily debug into
* the broker.
@@ -41,68 +45,34 @@
* those brokers can't coexist in the same process (e.g. for cluster
* tests where CPG doesn't allow multiple group members in a single
* process.)
- *
+ *
*/
class ForkedBroker {
public:
- ForkedBroker(std::vector<const char*> argv) { init(argv); }
-
- ForkedBroker(int argc, const char* const argv[]) {
- std::vector<const char*> args(argv, argv+argc);
- init(args);
- }
+ typedef std::vector<std::string> Args;
- ~ForkedBroker() {
- try { stop(); } catch(const std::exception& e) {
- QPID_LOG(error, QPID_MSG("Stopping forked broker: " << e.what()));
- }
- }
-
- void stop() {
- using qpid::ErrnoException;
- if (pid == 0) return;
- if (::kill(pid, SIGINT) < 0) throw ErrnoException("kill failed");
- int status;
- if (::waitpid(pid, &status, 0) < 0) throw ErrnoException("wait for forked process failed");
- if (WEXITSTATUS(status) != 0)
- throw qpid::Exception(QPID_MSG("forked broker exited with: " << WEXITSTATUS(status)));
- pid = 0;
- }
+ // argv args are passed to broker.
+ //
+ // Special value "TMP_DATA_DIR" is substituted with a temporary
+ // data directory for the broker.
+ //
+ ForkedBroker(const Args& argv);
+ ~ForkedBroker();
+ void kill(int sig=SIGINT);
+ int wait(); // Wait for exit, return exit status.
uint16_t getPort() { return port; }
+ pid_t getPID() { return pid; }
private:
- void init(const std::vector<const char*>& args) {
- using qpid::ErrnoException;
- pid = 0;
- port = 0;
- int pipeFds[2];
- if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe");
- pid = ::fork();
- if (pid < 0) throw ErrnoException("Fork failed");
- if (pid) { // parent
- ::close(pipeFds[1]);
- FILE* f = ::fdopen(pipeFds[0], "r");
- if (!f) throw ErrnoException("fopen failed");
- if (::fscanf(f, "%d", &port) != 1) throw ErrnoException("ill-formatted port");
- }
- else { // child
- ::close(pipeFds[0]);
- int fd = ::dup2(pipeFds[1], 1);
- if (fd < 0) throw ErrnoException("dup2 failed");
- const char* prog = "../qpidd";
- std::vector<const char*> args2(args);
- args2.push_back("--port=0");
- args2.push_back("--mgmt-enable=no"); // TODO aconway 2008-07-16: why does mgmt cause problems?
- args2.push_back(0);
- execv(prog, const_cast<char* const*>(&args2[0]));
- throw ErrnoException("execv failed");
- }
- }
+ void init(const Args& args);
pid_t pid;
int port;
+ std::string dataDir;
};
+}} // namespace qpid::tests
+
#endif /*!TESTS_FORKEDBROKER_H*/
diff --git a/cpp/src/tests/Frame.cpp b/cpp/src/tests/Frame.cpp
index 9ee3848b7b..1270eabba3 100644
--- a/cpp/src/tests/Frame.cpp
+++ b/cpp/src/tests/Frame.cpp
@@ -7,9 +7,9 @@
* 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
@@ -23,6 +23,9 @@
#include <boost/lexical_cast.hpp>
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(FrameTestSuite)
using namespace std;
@@ -33,7 +36,7 @@ QPID_AUTO_TEST_CASE(testContentBody) {
Frame f(42, AMQContentBody("foobar"));
AMQBody* body=f.getBody();
BOOST_CHECK(dynamic_cast<AMQContentBody*>(body));
- Buffer b(f.size());
+ Buffer b(f.encodedSize();
f.encode(b);
b.flip();
Frame g;
@@ -50,7 +53,7 @@ QPID_AUTO_TEST_CASE(testMethodBody) {
42, QueueDeclareBody(ProtocolVersion(), 1, "q", "altex",
true, false, true, false, true, args));
BOOST_CHECK_EQUAL(f.getChannel(), 42);
- Buffer b(f.size());
+ Buffer b(f.encodedSize();
f.encode(b);
b.flip();
Frame g;
@@ -78,3 +81,5 @@ QPID_AUTO_TEST_CASE(testLoop) {
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/FrameDecoder.cpp b/cpp/src/tests/FrameDecoder.cpp
new file mode 100644
index 0000000000..9eeff2a41e
--- /dev/null
+++ b/cpp/src/tests/FrameDecoder.cpp
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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 "unit_test.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FrameDecoder.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/Buffer.h"
+#include <string>
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FrameDecoderTest)
+
+using namespace std;
+using namespace qpid::framing;
+
+
+string makeData(int size) {
+ string data;
+ data.resize(size);
+ for (int i =0; i < size; ++i)
+ data[i] = 'a' + (i%26);
+ return data;
+}
+string encodeFrame(string data) {
+ AMQFrame f((AMQContentBody(data)));
+ string encoded;
+ encoded.resize(f.encodedSize());
+ Buffer b(&encoded[0], encoded.size());
+ f.encode(b);
+ return encoded;
+}
+
+string getData(const AMQFrame& frame) {
+ const AMQContentBody* content = dynamic_cast<const AMQContentBody*>(frame.getBody());
+ BOOST_CHECK(content);
+ return content->getData();
+}
+
+QPID_AUTO_TEST_CASE(testByteFragments) {
+ string data = makeData(42);
+ string encoded = encodeFrame(data);
+ FrameDecoder decoder;
+ for (size_t i = 0; i < encoded.size()-1; ++i) {
+ Buffer buf(&encoded[i], 1);
+ BOOST_CHECK(!decoder.decode(buf));
+ }
+ Buffer buf(&encoded[encoded.size()-1], 1);
+ BOOST_CHECK(decoder.decode(buf));
+ BOOST_CHECK_EQUAL(data, getData(decoder.getFrame()));
+}
+
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/FramingTest.cpp b/cpp/src/tests/FramingTest.cpp
index f82507c0a7..3d0fa0c0de 100644
--- a/cpp/src/tests/FramingTest.cpp
+++ b/cpp/src/tests/FramingTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -39,8 +39,11 @@ using namespace qpid;
using namespace qpid::framing;
using namespace std;
+namespace qpid {
+namespace tests {
+
template <class T>
-std::string tostring(const T& x)
+std::string tostring(const T& x)
{
std::ostringstream out;
out << x;
@@ -49,7 +52,7 @@ std::string tostring(const T& x)
QPID_AUTO_TEST_SUITE(FramingTestSuite)
-QPID_AUTO_TEST_CASE(testMessageTransferBody)
+QPID_AUTO_TEST_CASE(testMessageTransferBody)
{
char buffer[1024];
ProtocolVersion version(highestProtocolVersion);
@@ -62,8 +65,8 @@ QPID_AUTO_TEST_CASE(testMessageTransferBody)
out.decode(rbuff);
BOOST_CHECK_EQUAL(tostring(in), tostring(out));
}
-
-QPID_AUTO_TEST_CASE(testConnectionSecureBody)
+
+QPID_AUTO_TEST_CASE(testConnectionSecureBody)
{
char buffer[1024];
ProtocolVersion version(highestProtocolVersion);
@@ -88,10 +91,10 @@ QPID_AUTO_TEST_CASE(testConnectionRedirectBody)
Array hosts(0x95);
hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a)));
hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b)));
-
+
ConnectionRedirectBody in(version, a, hosts);
in.encode(wbuff);
-
+
Buffer rbuff(buffer, sizeof(buffer));
ConnectionRedirectBody out(version);
out.decode(rbuff);
@@ -111,7 +114,7 @@ QPID_AUTO_TEST_CASE(testQueueDeclareBody)
out.decode(rbuff);
BOOST_CHECK_EQUAL(tostring(in), tostring(out));
}
-
+
QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame)
{
char buffer[1024];
@@ -122,8 +125,8 @@ QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame)
Array hosts(0x95);
hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(a)));
hosts.add(boost::shared_ptr<FieldValue>(new Str16Value(b)));
-
- AMQFrame in(in_place<ConnectionRedirectBody>(version, a, hosts));
+
+ AMQFrame in((ConnectionRedirectBody(version, a, hosts)));
in.setChannel(999);
in.encode(wbuff);
@@ -138,7 +141,7 @@ QPID_AUTO_TEST_CASE(testMessageCancelBodyFrame)
char buffer[1024];
ProtocolVersion version(highestProtocolVersion);
Buffer wbuff(buffer, sizeof(buffer));
- AMQFrame in(in_place<MessageCancelBody>(version, "tag"));
+ AMQFrame in((MessageCancelBody(version, "tag")));
in.setChannel(999);
in.encode(wbuff);
@@ -149,3 +152,5 @@ QPID_AUTO_TEST_CASE(testMessageCancelBodyFrame)
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/HeaderTest.cpp b/cpp/src/tests/HeaderTest.cpp
index c3b64419ed..4b16f3c793 100644
--- a/cpp/src/tests/HeaderTest.cpp
+++ b/cpp/src/tests/HeaderTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -26,9 +26,12 @@
using namespace qpid::framing;
using namespace std;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(HeaderTestSuite)
-QPID_AUTO_TEST_CASE(testGenericProperties)
+QPID_AUTO_TEST_CASE(testGenericProperties)
{
AMQHeaderBody body;
body.get<MessageProperties>(true)->getApplicationHeaders().setString(
@@ -39,7 +42,7 @@ QPID_AUTO_TEST_CASE(testGenericProperties)
Buffer rbuffer(buff, 100);
AMQHeaderBody body2;
- body2.decode(rbuffer, body.size());
+ body2.decode(rbuffer, body.encodedSize());
MessageProperties* props =
body2.get<MessageProperties>(true);
BOOST_CHECK_EQUAL(
@@ -47,10 +50,10 @@ QPID_AUTO_TEST_CASE(testGenericProperties)
props->getApplicationHeaders().get("A")->get<string>());
}
-QPID_AUTO_TEST_CASE(testMessageProperties)
+QPID_AUTO_TEST_CASE(testMessageProperties)
{
- AMQFrame out(in_place<AMQHeaderBody>());
- MessageProperties* props1 =
+ AMQFrame out((AMQHeaderBody()));
+ MessageProperties* props1 =
out.castBody<AMQHeaderBody>()->get<MessageProperties>(true);
props1->setContentLength(42);
@@ -82,10 +85,10 @@ QPID_AUTO_TEST_CASE(testMessageProperties)
}
-QPID_AUTO_TEST_CASE(testDeliveryProperies)
+QPID_AUTO_TEST_CASE(testDeliveryProperies)
{
- AMQFrame out(in_place<AMQHeaderBody>());
- DeliveryProperties* props1 =
+ AMQFrame out((AMQHeaderBody()));
+ DeliveryProperties* props1 =
out.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true);
props1->setDiscardUnroutable(true);
@@ -108,3 +111,4 @@ QPID_AUTO_TEST_CASE(testDeliveryProperies)
QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/HeadersExchangeTest.cpp b/cpp/src/tests/HeadersExchangeTest.cpp
index 46933f955a..40deb59c86 100644
--- a/cpp/src/tests/HeadersExchangeTest.cpp
+++ b/cpp/src/tests/HeadersExchangeTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -28,9 +28,12 @@
using namespace qpid::broker;
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(HeadersExchangeTestSuite)
-
-QPID_AUTO_TEST_CASE(testMatchAll)
+
+QPID_AUTO_TEST_CASE(testMatchAll)
{
FieldTable b, m, n;
b.setString("x-match", "all");
@@ -43,7 +46,7 @@ QPID_AUTO_TEST_CASE(testMatchAll)
// Ignore extras.
m.setString("extra", "x");
BOOST_CHECK(HeadersExchange::match(b, m));
-
+
// Fail mismatch, wrong value.
m.setString("foo", "NotFoo");
BOOST_CHECK(!HeadersExchange::match(b, m));
@@ -54,7 +57,7 @@ QPID_AUTO_TEST_CASE(testMatchAll)
BOOST_CHECK(!HeadersExchange::match(b, n));
}
-QPID_AUTO_TEST_CASE(testMatchAny)
+QPID_AUTO_TEST_CASE(testMatchAny)
{
FieldTable b, m, n;
b.setString("x-match", "any");
@@ -67,7 +70,7 @@ QPID_AUTO_TEST_CASE(testMatchAny)
BOOST_CHECK(HeadersExchange::match(b, m));
}
-QPID_AUTO_TEST_CASE(testMatchEmptyValue)
+QPID_AUTO_TEST_CASE(testMatchEmptyValue)
{
FieldTable b, m;
b.setString("x-match", "all");
@@ -82,23 +85,23 @@ QPID_AUTO_TEST_CASE(testMatchEmptyArgs)
{
FieldTable b, m;
m.setString("foo", "FOO");
-
+
b.setString("x-match", "all");
BOOST_CHECK(HeadersExchange::match(b, m));
b.setString("x-match", "any");
BOOST_CHECK(!HeadersExchange::match(b, m));
}
-
-QPID_AUTO_TEST_CASE(testMatchNoXMatch)
+
+QPID_AUTO_TEST_CASE(testMatchNoXMatch)
{
FieldTable b, m;
b.setString("foo", "FOO");
m.setString("foo", "FOO");
BOOST_CHECK(!HeadersExchange::match(b, m));
}
-
-QPID_AUTO_TEST_CASE(testBindNoXMatch)
+
+QPID_AUTO_TEST_CASE(testBindNoXMatch)
{
HeadersExchange exchange("test");
Queue::shared_ptr queue;
@@ -112,4 +115,6 @@ QPID_AUTO_TEST_CASE(testBindNoXMatch)
}
}
-QPID_AUTO_TEST_SUITE_END()
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/IncompleteMessageList.cpp b/cpp/src/tests/IncompleteMessageList.cpp
index 925cdbf43e..303d83cd66 100644
--- a/cpp/src/tests/IncompleteMessageList.cpp
+++ b/cpp/src/tests/IncompleteMessageList.cpp
@@ -7,9 +7,9 @@
* 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
@@ -27,6 +27,9 @@
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(IncompleteMessageListTestSuite)
using namespace qpid::broker;
@@ -41,7 +44,7 @@ struct Checker
Checker(uint start, uint end) {
for (uint i = start; i <= end; i++) {
ids.push_back(i);
- }
+ }
}
Checker& expect(const SequenceNumber& id) {
@@ -49,11 +52,11 @@ struct Checker
return *this;
}
- void operator()(boost::intrusive_ptr<Message> msg) {
+ void operator()(boost::intrusive_ptr<Message> msg) {
BOOST_CHECK(!ids.empty());
BOOST_CHECK_EQUAL(msg->getCommandId(), ids.front());
ids.pop_front();
- }
+ }
};
QPID_AUTO_TEST_CASE(testProcessSimple)
@@ -73,6 +76,7 @@ QPID_AUTO_TEST_CASE(testProcessSimple)
QPID_AUTO_TEST_CASE(testProcessWithIncomplete)
{
+ Queue::shared_ptr queue;
IncompleteMessageList list;
SequenceNumber counter(1);
boost::intrusive_ptr<Message> middle;
@@ -82,7 +86,7 @@ QPID_AUTO_TEST_CASE(testProcessWithIncomplete)
list.add(msg);
if (i == 2) {
//mark a message in the middle as incomplete
- msg->enqueueAsync();
+ msg->enqueueAsync(queue, 0);
middle = msg;
}
}
@@ -90,7 +94,7 @@ QPID_AUTO_TEST_CASE(testProcessWithIncomplete)
list.process(Checker(1, 2), false);
//mark message complete and re-process to get remaining messages sent to listener
middle->enqueueComplete();
- list.process(Checker(3, 5), false);
+ list.process(Checker(3, 5), false);
}
@@ -126,3 +130,5 @@ QPID_AUTO_TEST_CASE(testSyncProcessWithIncomplete)
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/InitialStatusMap.cpp b/cpp/src/tests/InitialStatusMap.cpp
new file mode 100644
index 0000000000..82450eac22
--- /dev/null
+++ b/cpp/src/tests/InitialStatusMap.cpp
@@ -0,0 +1,248 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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 "unit_test.h"
+#include "test_tools.h"
+#include "qpid/cluster/InitialStatusMap.h"
+#include "qpid/framing/Uuid.h"
+#include <boost/assign.hpp>
+
+using namespace std;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::framing::cluster;
+using namespace boost::assign;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(InitialStatusMapTestSuite)
+
+typedef InitialStatusMap::Status Status;
+
+Status activeStatus(const Uuid& id=Uuid()) {
+ return Status(ProtocolVersion(), 0, true, id, STORE_STATE_NO_STORE, Uuid());
+}
+
+Status newcomerStatus(const Uuid& id=Uuid()) {
+ return Status(ProtocolVersion(), 0, false, id, STORE_STATE_NO_STORE, Uuid());
+}
+
+Status storeStatus(bool active, StoreState state, Uuid start=Uuid(), Uuid stop=Uuid()) {
+ return Status(ProtocolVersion(), 0, active, start, state, stop);
+}
+
+QPID_AUTO_TEST_CASE(testFirstInCluster) {
+ // Single member is first in cluster.
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+ BOOST_CHECK(!map.isComplete());
+ MemberSet members = list_of(MemberId(0));
+ map.configChange(members);
+ BOOST_CHECK(!map.isComplete());
+ map.received(MemberId(0), newcomerStatus(id));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(map.getElders().empty());
+ BOOST_CHECK(!map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(id, map.getClusterId());
+}
+
+QPID_AUTO_TEST_CASE(testJoinExistingCluster) {
+ // Single member 0 joins existing cluster 1,2
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+ MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2));
+ map.configChange(members);
+ BOOST_CHECK(map.isResendNeeded());
+ BOOST_CHECK(!map.isComplete());
+ map.received(MemberId(0), newcomerStatus());
+ map.received(MemberId(1), activeStatus(id));
+ BOOST_CHECK(!map.isComplete());
+ map.received(MemberId(2), activeStatus(id));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(1)(2));
+ BOOST_CHECK(map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+
+ // Check that transitionToComplete is reset.
+ map.configChange(list_of<MemberId>(0)(1));
+ BOOST_CHECK(!map.transitionToComplete());
+}
+
+QPID_AUTO_TEST_CASE(testMultipleFirstInCluster) {
+ // Multiple members 0,1,2 join at same time.
+ InitialStatusMap map(MemberId(1), 1); // self is 1
+ Uuid id(true);
+ MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2));
+ map.configChange(members);
+ BOOST_CHECK(map.isResendNeeded());
+
+ // All new members
+ map.received(MemberId(0), newcomerStatus(id));
+ map.received(MemberId(1), newcomerStatus());
+ map.received(MemberId(2), newcomerStatus());
+ BOOST_CHECK(!map.isResendNeeded());
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of(MemberId(2)));
+ BOOST_CHECK(!map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testMultipleJoinExisting) {
+ // Multiple members 1,2,3 join existing cluster containing 0.
+ InitialStatusMap map(MemberId(2), 1); // self is 2
+ Uuid id(true);
+ MemberSet members = list_of(MemberId(0))(MemberId(1))(MemberId(2))(MemberId(3));
+ map.configChange(members);
+ BOOST_CHECK(map.isResendNeeded());
+
+ map.received(MemberId(1), newcomerStatus());
+ map.received(MemberId(2), newcomerStatus());
+ map.received(MemberId(3), newcomerStatus());
+ map.received(MemberId(0), activeStatus(id));
+ BOOST_CHECK(!map.isResendNeeded());
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of(MemberId(0))(MemberId(3)));
+ BOOST_CHECK(map.isUpdateNeeded());
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testMembersLeave) {
+ // Test that map completes if members leave rather than send status.
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+ map.configChange(list_of(MemberId(0))(MemberId(1))(MemberId(2)));
+ map.received(MemberId(0), newcomerStatus());
+ map.received(MemberId(1), activeStatus(id));
+ BOOST_CHECK(!map.isComplete());
+ map.configChange(list_of(MemberId(0))(MemberId(1))); // 2 left
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of(MemberId(1)));
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testInteveningConfig) {
+ // Multiple config changes arrives before we complete the map.
+ InitialStatusMap map(MemberId(0), 1);
+ Uuid id(true);
+
+ map.configChange(list_of<MemberId>(0)(1));
+ BOOST_CHECK(map.isResendNeeded());
+ map.received(MemberId(0), newcomerStatus());
+ BOOST_CHECK(!map.isComplete());
+ BOOST_CHECK(!map.isResendNeeded());
+ // New member 2 joins before we receive 1
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ BOOST_CHECK(!map.isComplete());
+ BOOST_CHECK(map.isResendNeeded());
+ map.received(1, activeStatus(id));
+ map.received(2, newcomerStatus());
+ // We should not be complete as we haven't received 0 since new member joined
+ BOOST_CHECK(!map.isComplete());
+ BOOST_CHECK(!map.isResendNeeded());
+
+ map.received(0, newcomerStatus());
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK_EQUAL(map.getElders(), list_of<MemberId>(1));
+ BOOST_CHECK_EQUAL(map.getClusterId(), id);
+}
+
+QPID_AUTO_TEST_CASE(testInitialSize) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1));
+ map.received(MemberId(0), newcomerStatus());
+ map.received(MemberId(1), newcomerStatus());
+ BOOST_CHECK(!map.isComplete());
+
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), newcomerStatus());
+ map.received(MemberId(1), newcomerStatus());
+ map.received(MemberId(2), newcomerStatus());
+ BOOST_CHECK(map.isComplete());
+}
+
+QPID_AUTO_TEST_CASE(testAllCleanNoUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testAllEmptyNoUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testAllNoStoreNoUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_NO_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_NO_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_NO_STORE));
+ BOOST_CHECK(map.isComplete());
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testDirtyNeedUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_DIRTY_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testEmptyNeedUpdate) {
+ InitialStatusMap map(MemberId(0), 3);
+ map.configChange(list_of<MemberId>(0)(1)(2));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE));
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(map.isUpdateNeeded());
+}
+
+QPID_AUTO_TEST_CASE(testEmptyAlone) {
+ InitialStatusMap map(MemberId(0), 1);
+ map.configChange(list_of<MemberId>(0));
+ map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE));
+ BOOST_CHECK(map.transitionToComplete());
+ BOOST_CHECK(!map.isUpdateNeeded());
+}
+
+// FIXME aconway 2009-11-20: consistency tests for mixed stores,
+// tests for manual intervention case.
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/InlineAllocator.cpp b/cpp/src/tests/InlineAllocator.cpp
index fe6eaefaf4..a4c4d64cea 100644
--- a/cpp/src/tests/InlineAllocator.cpp
+++ b/cpp/src/tests/InlineAllocator.cpp
@@ -7,9 +7,9 @@
* 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
@@ -22,6 +22,9 @@
#include "qpid/InlineAllocator.h"
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(InlineAllocatorTestSuite)
using namespace qpid;
@@ -48,16 +51,18 @@ QPID_AUTO_TEST_CASE(testAllocateFull) {
char* p = alloc.allocate(1);
BOOST_CHECK(p == (char*)&alloc);
-
+
char* q = alloc.allocate(1);
BOOST_CHECK(q != (char*)&alloc);
alloc.deallocate(p,1);
p = alloc.allocate(1);
BOOST_CHECK(p == (char*)&alloc);
-
+
alloc.deallocate(p,1);
alloc.deallocate(q,1);
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/InlineVector.cpp b/cpp/src/tests/InlineVector.cpp
index bcd36e47b4..ba5165886d 100644
--- a/cpp/src/tests/InlineVector.cpp
+++ b/cpp/src/tests/InlineVector.cpp
@@ -7,9 +7,9 @@
* 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
@@ -22,6 +22,9 @@
#include "qpid/InlineVector.h"
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(InlineVectorTestSuite)
using namespace qpid;
@@ -30,6 +33,10 @@ using namespace std;
typedef InlineVector<int, 3> Vec;
bool isInline(const Vec& v) {
+ // If nothing, give it the benefit of the doubt;
+ // can't take address of nothing.
+ if (v.size() <= 0)
+ return true;
return (const char*)&v <= (const char*)(&v[0]) &&
(const char*)(&v[0]) < (const char*)&v+sizeof(v);
}
@@ -113,7 +120,9 @@ QPID_AUTO_TEST_CASE(testAssign) {
QPID_AUTO_TEST_CASE(testResize) {
Vec v;
v.resize(5);
- BOOST_CHECK(!isInline(v));
+ BOOST_CHECK(!isInline(v));
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am
index 31328ef59a..9a6e67ca2e 100644
--- a/cpp/src/tests/Makefile.am
+++ b/cpp/src/tests/Makefile.am
@@ -1,11 +1,34 @@
+#
+# 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.
+#
+
AM_CXXFLAGS = $(WARNING_CFLAGS) -DBOOST_TEST_DYN_LINK
-INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../gen -I$(top_builddir)/src/gen
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src
+PUBLIC_INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include # Use public API only
abs_builddir=@abs_builddir@
+abs_srcdir=@abs_srcdir@
+
extra_libs =
lib_client = $(abs_builddir)/../libqpidclient.la
lib_common = $(abs_builddir)/../libqpidcommon.la
lib_broker = $(abs_builddir)/../libqpidbroker.la
+lib_console = $(abs_builddir)/../libqmfconsole.la
# lib_amqp_0_10 = $(abs_builddir)/../libqpidamqp_0_10.la
#
@@ -16,6 +39,19 @@ check_LTLIBRARIES=
TESTS=
EXTRA_DIST=
CLEANFILES=
+LONG_TESTS=
+
+#
+# Destination for intalled programs and tests defined here
+#
+qpidexecdir = $(libexecdir)/qpid
+qpidexec_PROGRAMS =
+qpidexec_SCRIPTS =
+qpidtestdir = $(qpidexecdir)/tests
+qpidtest_PROGRAMS =
+qpidtest_SCRIPTS =
+tmoduledir = $(libdir)/qpid/tests
+tmodule_LTLIBRARIES=
#
# Unit test program
@@ -28,18 +64,21 @@ CLEANFILES=
TESTS+=unit_test
check_PROGRAMS+=unit_test
unit_test_LDADD=-lboost_unit_test_framework -lboost_regex \
- $(lib_client) $(lib_broker) # $(lib_amqp_0_10)
+ $(lib_client) $(lib_broker) $(lib_console)
unit_test_SOURCES= unit_test.cpp unit_test.h \
+ MessagingSessionTests.cpp \
+ ClientSessionTest.cpp \
BrokerFixture.h SocketProxy.h \
exception_test.cpp \
RefCounted.cpp \
- SessionState.cpp Blob.cpp logging.cpp \
+ SessionState.cpp logging.cpp \
+ AsyncCompletion.cpp \
Url.cpp Uuid.cpp \
Shlib.cpp FieldValue.cpp FieldTable.cpp Array.cpp \
+ QueueOptionsTest.cpp \
InlineAllocator.cpp \
InlineVector.cpp \
- ClientSessionTest.cpp \
SequenceSet.cpp \
StringUtils.cpp \
IncompleteMessageList.cpp \
@@ -63,7 +102,22 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
TxPublishTest.cpp \
MessageBuilderTest.cpp \
ConnectionOptions.h \
- ForkedBroker.h
+ ForkedBroker.h \
+ ForkedBroker.cpp \
+ ManagementTest.cpp \
+ MessageReplayTracker.cpp \
+ ConsoleTest.cpp \
+ QueueEvents.cpp \
+ ProxyTest.cpp \
+ RetryList.cpp \
+ RateFlowcontrolTest.cpp \
+ FrameDecoder.cpp \
+ ReplicationTest.cpp \
+ ClientMessageTest.cpp \
+ PollableCondition.cpp \
+ Variant.cpp \
+ Address.cpp \
+ ClientMessage.cpp
if HAVE_XML
unit_test_SOURCES+= XmlClientSessionTest.cpp
@@ -77,94 +131,227 @@ endif
# amqp_0_10/Map.cpp \
# amqp_0_10/handlers.cpp
+TESTLIBFLAGS = -module -rpath $(abs_builddir)
check_LTLIBRARIES += libshlibtest.la
-libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir)
+libshlibtest_la_LDFLAGS = $(TESTLIBFLAGS)
libshlibtest_la_SOURCES = shlibtest.cpp
+tmodule_LTLIBRARIES += test_store.la
+test_store_la_SOURCES = test_store.cpp
+test_store_la_LIBADD = $(lib_broker)
+test_store_la_LDFLAGS = -module
+
include cluster.mk
+if SSL
+include ssl.mk
+endif
+
+# receiver, sender are installed and therefore built as part of make, not make check
+qpidtest_PROGRAMS += receiver
+receiver_SOURCES = \
+ receiver.cpp \
+ TestOptions.h \
+ ConnectionOptions.h
+receiver_LDADD = $(lib_client)
+
+qpidtest_PROGRAMS += sender
+sender_SOURCES = \
+ sender.cpp \
+ TestOptions.h \
+ ConnectionOptions.h
+sender_LDADD = $(lib_client)
#
# Other test programs
#
check_PROGRAMS+=perftest
perftest_SOURCES=perftest.cpp test_tools.h TestOptions.h ConnectionOptions.h
+perftest_INCLUDES=$(PUBLIC_INCLUDES)
perftest_LDADD=$(lib_client)
check_PROGRAMS+=txtest
+txtest_INCLUDES=$(PUBLIC_INCLUDES)
txtest_SOURCES=txtest.cpp TestOptions.h ConnectionOptions.h
txtest_LDADD=$(lib_client)
check_PROGRAMS+=latencytest
+latencytest_INCLUDES=$(PUBLIC_INCLUDES)
latencytest_SOURCES=latencytest.cpp TestOptions.h ConnectionOptions.h
latencytest_LDADD=$(lib_client)
+check_PROGRAMS+=echotest
+echotest_INCLUDES=$(PUBLIC_INCLUDES)
+echotest_SOURCES=echotest.cpp TestOptions.h ConnectionOptions.h
+echotest_LDADD=$(lib_client)
+
check_PROGRAMS+=client_test
+client_test_INCLUDES=$(PUBLIC_INCLUDES)
client_test_SOURCES=client_test.cpp TestOptions.h ConnectionOptions.h
client_test_LDADD=$(lib_client)
check_PROGRAMS+=topic_listener
+topic_listener_INCLUDES=$(PUBLIC_INCLUDES)
topic_listener_SOURCES=topic_listener.cpp TestOptions.h ConnectionOptions.h
topic_listener_LDADD=$(lib_client)
check_PROGRAMS+=topic_publisher
+topic_publisher_INCLUDES=$(PUBLIC_INCLUDES)
topic_publisher_SOURCES=topic_publisher.cpp TestOptions.h ConnectionOptions.h
topic_publisher_LDADD=$(lib_client)
check_PROGRAMS+=publish
+publish_INCLUDES=$(PUBLIC_INCLUDES)
publish_SOURCES=publish.cpp TestOptions.h ConnectionOptions.h
publish_LDADD=$(lib_client)
check_PROGRAMS+=consume
+consume_INCLUDES=$(PUBLIC_INCLUDES)
consume_SOURCES=consume.cpp TestOptions.h ConnectionOptions.h
consume_LDADD=$(lib_client)
-TESTS_ENVIRONMENT = VALGRIND=$(VALGRIND) srcdir=$(srcdir) QPID_DATA_DIR= $(srcdir)/run_test
+check_PROGRAMS+=header_test
+header_test_INCLUDES=$(PUBLIC_INCLUDES)
+header_test_SOURCES=header_test.cpp TestOptions.h ConnectionOptions.h
+header_test_LDADD=$(lib_client)
+
+check_PROGRAMS+=failover_soak
+failover_soak_INCLUDES=$(PUBLIC_INCLUDES)
+failover_soak_SOURCES=failover_soak.cpp ForkedBroker.h ForkedBroker.cpp
+failover_soak_LDADD=$(lib_client) $(lib_broker)
+
+check_PROGRAMS+=declare_queues
+declare_queues_INCLUDES=$(PUBLIC_INCLUDES)
+declare_queues_SOURCES=declare_queues.cpp
+declare_queues_LDADD=$(lib_client)
+
+check_PROGRAMS+=replaying_sender
+replaying_sender_INCLUDES=$(PUBLIC_INCLUDES)
+replaying_sender_SOURCES=replaying_sender.cpp
+replaying_sender_LDADD=$(lib_client)
+
+check_PROGRAMS+=resuming_receiver
+resuming_receiver_INCLUDES=$(PUBLIC_INCLUDES)
+resuming_receiver_SOURCES=resuming_receiver.cpp
+resuming_receiver_LDADD=$(lib_client)
+
+check_PROGRAMS+=txshift
+txshift_INCLUDES=$(PUBLIC_INCLUDES)
+txshift_SOURCES=txshift.cpp TestOptions.h ConnectionOptions.h
+txshift_LDADD=$(lib_client)
-system_tests = client_test quick_perftest quick_topictest
-TESTS += start_broker $(system_tests) python_tests stop_broker run_federation_tests
+check_PROGRAMS+=txjob
+txjob_INCLUDES=$(PUBLIC_INCLUDES)
+txjob_SOURCES=txjob.cpp TestOptions.h ConnectionOptions.h
+txjob_LDADD=$(lib_client)
+
+check_PROGRAMS+=PollerTest
+PollerTest_SOURCES=PollerTest.cpp
+PollerTest_LDADD=$(lib_common) $(SOCKLIBS)
+
+check_PROGRAMS+=DispatcherTest
+DispatcherTest_SOURCES=DispatcherTest.cpp
+DispatcherTest_LDADD=$(lib_common) $(SOCKLIBS)
+
+check_PROGRAMS+=qpid_ping
+qpid_ping_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_ping_SOURCES=qpid_ping.cpp test_tools.h TestOptions.h ConnectionOptions.h
+qpid_ping_LDADD=$(lib_client)
+
+check_PROGRAMS+=datagen
+datagen_SOURCES=datagen.cpp
+datagen_LDADD=$(lib_common)
+
+check_PROGRAMS+=qrsh_server
+qrsh_server_SOURCES=qrsh_server.cpp
+qrsh_server_LDADD=$(lib_client)
+
+check_PROGRAMS+=qrsh_run
+qrsh_run_SOURCES=qrsh_run.cpp
+qrsh_run_LDADD=$(lib_client)
+
+check_PROGRAMS+=qrsh
+qrsh_SOURCES=qrsh.cpp
+qrsh_LDADD=$(lib_client)
+
+check_PROGRAMS+=qpid_stream
+qpid_stream_INCLUDES=$(PUBLIC_INCLUDES)
+qpid_stream_SOURCES=qpid_stream.cpp
+qpid_stream_LDADD=$(lib_client)
+
+TESTS_ENVIRONMENT = \
+ VALGRIND=$(VALGRIND) \
+ LIBTOOL="$(LIBTOOL)" \
+ QPID_DATA_DIR= \
+ BOOST_TEST_SHOW_PROGRESS=yes \
+ $(srcdir)/run_test
+
+system_tests = client_test quick_perftest quick_topictest run_header_test quick_txtest
+TESTS += start_broker $(system_tests) python_tests stop_broker run_federation_tests run_acl_tests run_cli_tests replication_test
EXTRA_DIST += \
run_test vg_check \
run-unit-tests start_broker python_tests stop_broker \
quick_topictest \
quick_perftest \
+ quick_txtest \
topictest \
+ run_header_test \
+ header_test.py \
+ ssl_test \
+ config.null \
+ ais_check \
run_federation_tests \
+ run_cli_tests \
+ run_acl_tests \
.valgrind.supp \
MessageUtils.h \
TestMessageStore.h \
- MockConnectionInputHandler.h \
TxMocks.h \
- start_cluster stop_cluster restart_cluster
+ replication_test \
+ run_perftest \
+ ring_queue_test \
+ run_ring_queue_test \
+ cluster.cmake
check_LTLIBRARIES += libdlclose_noop.la
libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir)
libdlclose_noop_la_SOURCES = dlclose_noop.c
-CLEANFILES+=valgrind.out *.log *.vglog* dummy_test $(unit_wrappers)
+CLEANFILES+=valgrind.out *.log *.vglog* dummy_test qpidd.port $(unit_wrappers)
-# FIXME aconway 2008-05-23: Disabled interop_runner because it uses
-# the obsolete Channel class. Convert to Session and re-enable.
-#
-# check_PROGRAMS += interop_runner
+# Longer running stability tests, not run by default check: target.
+# Not run under valgrind, too slow
-# interop_runner_SOURCES = \
-# interop_runner.cpp \
-# SimpleTestCaseBase.cpp \
-# BasicP2PTest.cpp \
-# BasicPubSubTest.cpp \
-# SimpleTestCaseBase.h \
-# BasicP2PTest.h \
-# BasicPubSubTest.h \
-# TestCase.h \
-# TestOptions.h ConnectionOptions.h
-# interop_runner_LDADD = $(lib_client) $(lib_common) $(extra_libs)
+LONG_TESTS+=start_broker fanout_perftest shared_perftest multiq_perftest topic_perftest run_ring_queue_test stop_broker \
+ run_failover_soak reliable_replication_test \
+ federated_cluster_test_with_node_failure
+EXTRA_DIST+= \
+ CMakeLists.txt \
+ fanout_perftest \
+ shared_perftest \
+ multiq_perftest \
+ topic_perftest \
+ run_failover_soak \
+ reliable_replication_test \
+ federated_cluster_test_with_node_failure \
+ windows/DisableWin32ErrorWindows.cpp
-# Longer running stability tests, not run by default check: target.
-# Not run under valgrind, too slow
-LONG_TESTS=fanout_perftest shared_perftest multiq_perftest topic_perftest
-EXTRA_DIST+=$(LONG_TESTS) run_perftest
check-long:
- $(MAKE) check TESTS="start_broker $(LONG_TESTS) stop_broker" VALGRIND=
+ $(MAKE) check TESTS="$(LONG_TESTS)" VALGRIND=
+
+check: python_prep
+
+PYTHON_SRC_DIR=$(abs_srcdir)/../../../python
+PYTHON_BLD_DIR=$(abs_builddir)/python
+AMQP_SPEC_DIR=$(abs_srcdir)/../../../specs
+
+# Generate python client as part of the all-am target so it gets built before tests.
+all-am: python_prep
+
+python_prep:
+ if test -d $(PYTHON_SRC_DIR) -a -d $(AMQP_SPEC_DIR); \
+ then $(MAKE) -C $(PYTHON_SRC_DIR) install PREFIX=$(PYTHON_BLD_DIR) PYTHON_LIB=$(PYTHON_BLD_DIR) EXEC_PREFIX=$(PYTHON_BLD_DIR)/commands AMQP_SPEC_DIR=$(AMQP_SPEC_DIR); \
+ else echo "WARNING: python client not built, missing one of $(PYTHON_SRC_DIR) $(AMQP_SPEC_DIR)"; fi
+
diff --git a/cpp/src/tests/ManagementTest.cpp b/cpp/src/tests/ManagementTest.cpp
new file mode 100644
index 0000000000..d05b4676ba
--- /dev/null
+++ b/cpp/src/tests/ManagementTest.cpp
@@ -0,0 +1,115 @@
+/*
+ *
+ * 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 "qpid/management/ManagementObject.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/console/ObjectId.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ManagementTestSuite)
+
+using namespace qpid::framing;
+using namespace qpid::management;
+
+QPID_AUTO_TEST_CASE(testObjectIdSerializeStream) {
+ std::string text("0-10-4-2500-80000000000");
+ std::stringstream input(text);
+
+ ObjectId oid(input);
+
+ std::stringstream output;
+ output << oid;
+
+ BOOST_CHECK_EQUAL(text, output.str());
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdSerializeString) {
+ std::string text("0-10-4-2500-80000000000");
+
+ ObjectId oid(text);
+
+ std::stringstream output;
+ output << oid;
+
+ BOOST_CHECK_EQUAL(text, output.str());
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdEncode) {
+ char buffer[100];
+ Buffer msgBuf(buffer, 100);
+ msgBuf.putLongLong(0x1002000030000004LL);
+ msgBuf.putLongLong(0x0000000000000005LL);
+ msgBuf.reset();
+
+ ObjectId oid(msgBuf);
+
+ std::stringstream out1;
+ out1 << oid;
+
+ BOOST_CHECK_EQUAL(out1.str(), "1-2-3-4-5");
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdAttach) {
+ AgentAttachment agent;
+ ObjectId oid(&agent, 10, 20, 50);
+
+ std::stringstream out1;
+ out1 << oid;
+ BOOST_CHECK_EQUAL(out1.str(), "10-20-0-0-50");
+
+ agent.setBanks(30, 40);
+ std::stringstream out2;
+ out2 << oid;
+ BOOST_CHECK_EQUAL(out2.str(), "10-20-30-40-50");
+}
+
+QPID_AUTO_TEST_CASE(testConsoleObjectId) {
+ qpid::console::ObjectId oid1, oid2;
+
+ oid1.setValue(1, 2);
+ oid2.setValue(3, 4);
+
+ BOOST_CHECK(oid1 < oid2);
+ BOOST_CHECK(oid1 <= oid2);
+ BOOST_CHECK(oid2 > oid1);
+ BOOST_CHECK(oid2 >= oid1);
+ BOOST_CHECK(oid1 != oid2);
+ BOOST_CHECK(oid1 == oid1);
+
+ oid1.setValue(3, 6);
+ oid2.setValue(3, 4);
+
+ BOOST_CHECK(oid1 > oid2);
+ BOOST_CHECK(oid1 >= oid2);
+ BOOST_CHECK(oid2 < oid1);
+ BOOST_CHECK(oid2 <= oid1);
+ BOOST_CHECK(oid1 != oid2);
+
+ oid2.setValue(3, 6);
+ BOOST_CHECK(oid1 == oid2);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/MessageBuilderTest.cpp b/cpp/src/tests/MessageBuilderTest.cpp
index 63c3a800de..c2fb8ad32e 100644
--- a/cpp/src/tests/MessageBuilderTest.cpp
+++ b/cpp/src/tests/MessageBuilderTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -27,20 +27,22 @@
#include "unit_test.h"
#include <list>
-using namespace boost;
using namespace qpid::broker;
using namespace qpid::framing;
using namespace qpid::sys;
+namespace qpid {
+namespace tests {
+
class MockMessageStore : public NullMessageStore
{
enum Op {STAGE=1, APPEND=2};
uint64_t id;
- intrusive_ptr<PersistableMessage> expectedMsg;
+ boost::intrusive_ptr<PersistableMessage> expectedMsg;
string expectedData;
std::list<Op> ops;
-
+
void checkExpectation(Op actual)
{
BOOST_CHECK_EQUAL(ops.front(), actual);
@@ -50,40 +52,47 @@ class MockMessageStore : public NullMessageStore
public:
MockMessageStore() : id(0), expectedMsg(0) {}
- void expectStage(PersistableMessage& msg)
- {
+ void expectStage(PersistableMessage& msg)
+ {
expectedMsg = &msg;
- ops.push_back(STAGE);
+ ops.push_back(STAGE);
}
- void expectAppendContent(PersistableMessage& msg, const string& data)
- {
+ void expectAppendContent(PersistableMessage& msg, const string& data)
+ {
expectedMsg = &msg;
expectedData = data;
- ops.push_back(APPEND);
+ ops.push_back(APPEND);
}
- void stage(const intrusive_ptr<PersistableMessage>& msg)
+ void stage(const boost::intrusive_ptr<PersistableMessage>& msg)
{
checkExpectation(STAGE);
BOOST_CHECK_EQUAL(expectedMsg, msg);
msg->setPersistenceId(++id);
}
- void appendContent(const intrusive_ptr<const PersistableMessage>& msg,
+ void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
const string& data)
{
checkExpectation(APPEND);
- BOOST_CHECK_EQUAL(static_pointer_cast<const PersistableMessage>(expectedMsg), msg);
- BOOST_CHECK_EQUAL(expectedData, data);
+ BOOST_CHECK_EQUAL(boost::static_pointer_cast<const PersistableMessage>(expectedMsg), msg);
+ BOOST_CHECK_EQUAL(expectedData, data);
}
bool expectationsMet()
{
return ops.empty();
}
+
+ //don't treat this store as a null impl
+ bool isNull() const
+ {
+ return false;
+ }
+
};
-
+
QPID_AUTO_TEST_SUITE(MessageBuilderTestSuite)
QPID_AUTO_TEST_CASE(testHeaderOnly)
@@ -94,11 +103,10 @@ QPID_AUTO_TEST_CASE(testHeaderOnly)
std::string exchange("builder-exchange");
std::string key("builder-exchange");
- AMQFrame method(in_place<MessageTransferBody>(
- ProtocolVersion(), exchange, 0, 0));
- AMQFrame header(in_place<AMQHeaderBody>());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
- header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0);
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0);
header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
builder.handle(method);
@@ -119,15 +127,15 @@ QPID_AUTO_TEST_CASE(test1ContentFrame)
std::string exchange("builder-exchange");
std::string key("builder-exchange");
- AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0));
- AMQFrame header(in_place<AMQHeaderBody>());
- AMQFrame content(in_place<AMQContentBody>(data));
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content((AMQContentBody(data)));
method.setEof(false);
header.setBof(false);
header.setEof(false);
content.setBof(false);
- header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size());
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size());
header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
builder.handle(method);
@@ -138,7 +146,7 @@ QPID_AUTO_TEST_CASE(test1ContentFrame)
BOOST_CHECK(builder.getMessage());
BOOST_CHECK(!builder.getMessage()->getFrames().isComplete());
- builder.handle(content);
+ builder.handle(content);
BOOST_CHECK(builder.getMessage());
BOOST_CHECK(builder.getMessage()->getFrames().isComplete());
}
@@ -153,11 +161,10 @@ QPID_AUTO_TEST_CASE(test2ContentFrames)
std::string exchange("builder-exchange");
std::string key("builder-exchange");
- AMQFrame method(in_place<MessageTransferBody>(
- ProtocolVersion(), exchange, 0, 0));
- AMQFrame header(in_place<AMQHeaderBody>());
- AMQFrame content1(in_place<AMQContentBody>(data1));
- AMQFrame content2(in_place<AMQContentBody>(data2));
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content1((AMQContentBody(data1)));
+ AMQFrame content2((AMQContentBody(data2)));
method.setEof(false);
header.setBof(false);
header.setEof(false);
@@ -165,7 +172,7 @@ QPID_AUTO_TEST_CASE(test2ContentFrames)
content1.setEof(false);
content2.setBof(false);
- header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size());
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size());
header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
builder.handle(method);
@@ -184,19 +191,18 @@ QPID_AUTO_TEST_CASE(testStaging)
MockMessageStore store;
MessageBuilder builder(&store, 5);
builder.start(SequenceNumber());
-
+
std::string data1("abcdefg");
std::string data2("hijklmn");
std::string exchange("builder-exchange");
std::string key("builder-exchange");
- AMQFrame method(in_place<MessageTransferBody>(
- ProtocolVersion(), exchange, 0, 0));
- AMQFrame header(in_place<AMQHeaderBody>());
- AMQFrame content1(in_place<AMQContentBody>(data1));
- AMQFrame content2(in_place<AMQContentBody>(data2));
+ AMQFrame method(MessageTransferBody(ProtocolVersion(), exchange, 0, 0));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content1((AMQContentBody(data1)));
+ AMQFrame content2((AMQContentBody(data2)));
- header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size());
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size());
header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
builder.handle(method);
@@ -214,4 +220,32 @@ QPID_AUTO_TEST_CASE(testStaging)
BOOST_CHECK(!builder.getMessage()->isContentLoaded());
}
+QPID_AUTO_TEST_CASE(testNoManagementStaging)
+{
+ // Make sure management messages don't stage
+ MockMessageStore store;
+ MessageBuilder builder(&store, 5);
+ builder.start(SequenceNumber());
+
+ std::string data1("abcdefg");
+ std::string exchange("qpid.management");
+ std::string key("builder-exchange");
+
+ AMQFrame method(MessageTransferBody(ProtocolVersion(), exchange, 0, 0));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content1((AMQContentBody(data1)));
+
+ header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size());
+ header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key);
+
+ builder.handle(method);
+ builder.handle(header);
+
+ builder.handle(content1);
+ BOOST_CHECK(store.expectationsMet());
+ BOOST_CHECK_EQUAL((uint64_t) 0, builder.getMessage()->getPersistenceId());
+}
+
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/MessageReplayTracker.cpp b/cpp/src/tests/MessageReplayTracker.cpp
new file mode 100644
index 0000000000..3d79ee53c2
--- /dev/null
+++ b/cpp/src/tests/MessageReplayTracker.cpp
@@ -0,0 +1,102 @@
+/*
+ *
+ * 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 "unit_test.h"
+#include "BrokerFixture.h"
+#include "qpid/client/MessageReplayTracker.h"
+#include "qpid/sys/Time.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessageReplayTrackerTests)
+
+using namespace qpid::client;
+using namespace qpid::sys;
+using std::string;
+
+class ReplayBufferChecker
+{
+ public:
+
+ ReplayBufferChecker(uint from, uint to) : end(to), i(from) {}
+
+ void operator()(const Message& m)
+ {
+ if (i > end) BOOST_FAIL("Extra message found: " + m.getData());
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i++)).str(), m.getData());
+ }
+ private:
+ const uint end;
+ uint i;
+
+};
+
+QPID_AUTO_TEST_CASE(testReplay)
+{
+ ProxySessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ MessageReplayTracker tracker(10);
+ tracker.init(fix.session);
+ for (uint i = 0; i < 5; i++) {
+ Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ tracker.send(message);
+ }
+ ReplayBufferChecker checker(1, 10);
+ tracker.foreach(checker);
+
+ tracker.replay(fix.session);
+ for (uint j = 0; j < 2; j++) {//each message should have been sent twice
+ for (uint i = 0; i < 5; i++) {
+ Message m;
+ BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ }
+ }
+ Message m;
+ BOOST_CHECK(!fix.subs.get(m, "my-queue"));
+}
+
+QPID_AUTO_TEST_CASE(testCheckCompletion)
+{
+ ProxySessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ MessageReplayTracker tracker(10);
+ tracker.init(fix.session);
+ for (uint i = 0; i < 5; i++) {
+ Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ tracker.send(message);
+ }
+ fix.session.sync();//ensures all messages are complete
+ tracker.checkCompletion();
+ tracker.replay(fix.session);
+ Message received;
+ for (uint i = 0; i < 5; i++) {
+ BOOST_CHECK(fix.subs.get(received, "my-queue"));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), received.getData());
+ }
+ BOOST_CHECK(!fix.subs.get(received, "my-queue"));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/MessageTest.cpp b/cpp/src/tests/MessageTest.cpp
index b5c1648caf..7d67c92b37 100644
--- a/cpp/src/tests/MessageTest.cpp
+++ b/cpp/src/tests/MessageTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -24,16 +24,18 @@
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/framing/FieldValue.h"
#include "qpid/framing/Uuid.h"
+#include "qpid/sys/alloca.h"
#include "unit_test.h"
#include <iostream>
-#include <alloca.h>
-using namespace boost;
using namespace qpid::broker;
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(MessageTestSuite)
QPID_AUTO_TEST_CASE(testEncodeDecode)
@@ -44,13 +46,12 @@ QPID_AUTO_TEST_CASE(testEncodeDecode)
string data1("abcdefg");
string data2("hijklmn");
- intrusive_ptr<Message> msg(new Message());
+ boost::intrusive_ptr<Message> msg(new Message());
- AMQFrame method(in_place<MessageTransferBody>(
- ProtocolVersion(), exchange, 0, 0));
- AMQFrame header(in_place<AMQHeaderBody>());
- AMQFrame content1(in_place<AMQContentBody>(data1));
- AMQFrame content2(in_place<AMQContentBody>(data2));
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+ AMQFrame content1((AMQContentBody(data1)));
+ AMQFrame content2((AMQContentBody(data2)));
msg->getFrames().append(method);
msg->getFrames().append(header);
@@ -58,7 +59,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode)
msg->getFrames().append(content2);
MessageProperties* mProps = msg->getFrames().getHeaders()->get<MessageProperties>(true);
- mProps->setContentLength(data1.size() + data2.size());
+ mProps->setContentLength(data1.size() + data2.size());
mProps->setMessageId(messageId);
FieldTable applicationHeaders;
applicationHeaders.setString("abc", "xyz");
@@ -71,7 +72,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode)
char* buff = static_cast<char*>(::alloca(msg->encodedSize()));
Buffer wbuffer(buff, msg->encodedSize());
msg->encode(wbuffer);
-
+
Buffer rbuffer(buff, msg->encodedSize());
msg = new Message();
msg->decodeHeader(rbuffer);
@@ -81,10 +82,11 @@ QPID_AUTO_TEST_CASE(testEncodeDecode)
BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->contentSize());
BOOST_CHECK_EQUAL((uint64_t) data1.size() + data2.size(), msg->getProperties<MessageProperties>()->getContentLength());
BOOST_CHECK_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId());
- BOOST_CHECK_EQUAL(string("xyz"), msg->getProperties<MessageProperties>()->getApplicationHeaders().getString("abc"));
+ BOOST_CHECK_EQUAL(string("xyz"), msg->getProperties<MessageProperties>()->getApplicationHeaders().getAsString("abc"));
BOOST_CHECK_EQUAL((uint8_t) PERSISTENT, msg->getProperties<DeliveryProperties>()->getDeliveryMode());
BOOST_CHECK(msg->isPersistent());
}
QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/MessageUtils.h b/cpp/src/tests/MessageUtils.h
index 21ee834ba7..a1b140d484 100644
--- a/cpp/src/tests/MessageUtils.h
+++ b/cpp/src/tests/MessageUtils.h
@@ -7,9 +7,9 @@
* 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
@@ -20,7 +20,6 @@
*/
#include "qpid/broker/Message.h"
-#include "qpid/broker/MessageDelivery.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/framing/Uuid.h"
@@ -29,28 +28,36 @@ using namespace qpid;
using namespace broker;
using namespace framing;
+namespace qpid {
+namespace tests {
+
struct MessageUtils
{
- static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="",
- const Uuid& messageId=Uuid(true), uint64_t contentSize = 0)
+ static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="",
+ const bool durable = false, const Uuid& messageId=Uuid(true),
+ uint64_t contentSize = 0)
{
- boost::intrusive_ptr<Message> msg(new Message());
+ boost::intrusive_ptr<broker::Message> msg(new broker::Message());
- AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0));
- AMQFrame header(in_place<AMQHeaderBody>());
+ AMQFrame method(( MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
msg->getFrames().append(method);
msg->getFrames().append(header);
MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true);
- props->setContentLength(contentSize);
+ props->setContentLength(contentSize);
props->setMessageId(messageId);
msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ if (durable)
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(2);
return msg;
}
static void addContent(boost::intrusive_ptr<Message> msg, const string& data)
{
- AMQFrame content(in_place<AMQContentBody>(data));
+ AMQFrame content((AMQContentBody(data)));
msg->getFrames().append(content);
}
};
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/MessagingSessionTests.cpp b/cpp/src/tests/MessagingSessionTests.cpp
new file mode 100644
index 0000000000..4125c51698
--- /dev/null
+++ b/cpp/src/tests/MessagingSessionTests.cpp
@@ -0,0 +1,778 @@
+/*
+ *
+ * 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 "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/ListContent.h"
+#include "qpid/messaging/ListView.h"
+#include "qpid/messaging/MapContent.h"
+#include "qpid/messaging/MapView.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/ExchangeQueryResult.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Time.h"
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <string>
+#include <vector>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessagingSessionTests)
+
+using namespace qpid::messaging;
+using namespace qpid;
+using qpid::broker::Broker;
+using qpid::framing::Uuid;
+
+struct BrokerAdmin
+{
+ qpid::client::Connection connection;
+ qpid::client::Session session;
+
+ BrokerAdmin(uint16_t port)
+ {
+ connection.open("localhost", port);
+ session = connection.newSession();
+ }
+
+ void createQueue(const std::string& name)
+ {
+ session.queueDeclare(qpid::client::arg::queue=name);
+ }
+
+ void deleteQueue(const std::string& name)
+ {
+ session.queueDelete(qpid::client::arg::queue=name);
+ }
+
+ void createExchange(const std::string& name, const std::string& type)
+ {
+ session.exchangeDeclare(qpid::client::arg::exchange=name, qpid::client::arg::type=type);
+ }
+
+ void deleteExchange(const std::string& name)
+ {
+ session.exchangeDelete(qpid::client::arg::exchange=name);
+ }
+
+ bool checkQueueExists(const std::string& name)
+ {
+ return session.queueQuery(name).getQueue() == name;
+ }
+
+ bool checkExchangeExists(const std::string& name, std::string& type)
+ {
+ qpid::framing::ExchangeQueryResult result = session.exchangeQuery(name);
+ type = result.getType();
+ return !result.getNotFound();
+ }
+
+ ~BrokerAdmin()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct MessagingFixture : public BrokerFixture
+{
+ Connection connection;
+ Session session;
+ BrokerAdmin admin;
+
+ MessagingFixture(Broker::Options opts = Broker::Options()) :
+ BrokerFixture(opts),
+ connection(Connection::open((boost::format("amqp:tcp:localhost:%1%") % (broker->getPort(Broker::TCP_TRANSPORT))).str())),
+ session(connection.newSession()),
+ admin(broker->getPort(Broker::TCP_TRANSPORT)) {}
+
+ void ping(const qpid::messaging::Address& address)
+ {
+ Receiver r = session.createReceiver(address);
+ Sender s = session.createSender(address);
+ Message out(Uuid(true).str());
+ s.send(out);
+ Message in;
+ BOOST_CHECK(r.fetch(in, 5*qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ r.cancel();
+ s.cancel();
+ }
+
+ ~MessagingFixture()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct QueueFixture : MessagingFixture
+{
+ std::string queue;
+
+ QueueFixture(const std::string& name = "test-queue") : queue(name)
+ {
+ admin.createQueue(queue);
+ }
+
+ ~QueueFixture()
+ {
+ admin.deleteQueue(queue);
+ }
+
+};
+
+struct TopicFixture : MessagingFixture
+{
+ std::string topic;
+
+ TopicFixture(const std::string& name = "test-topic", const std::string& type="fanout") : topic(name)
+ {
+ admin.createExchange(topic, type);
+ }
+
+ ~TopicFixture()
+ {
+ admin.deleteExchange(topic);
+ }
+
+};
+
+struct MultiQueueFixture : MessagingFixture
+{
+ typedef std::vector<std::string>::const_iterator const_iterator;
+ std::vector<std::string> queues;
+
+ MultiQueueFixture(const std::vector<std::string>& names = boost::assign::list_of<std::string>("q1")("q2")("q3")) : queues(names)
+ {
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.createQueue(*i);
+ }
+ }
+
+ ~MultiQueueFixture()
+ {
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.deleteQueue(*i);
+ }
+ }
+
+};
+std::vector<std::string> fetch(Receiver& receiver, int count, qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5)
+{
+ std::vector<std::string> data;
+ Message message;
+ for (int i = 0; i < count && receiver.fetch(message, timeout); i++) {
+ data.push_back(message.getContent());
+ }
+ return data;
+}
+
+
+void send(Sender& sender, uint count = 1, uint start = 1, const std::string& base = "Message")
+{
+ for (uint i = start; i < start + count; ++i) {
+ sender.send(Message((boost::format("%1%_%2%") % base % i).str()));
+ }
+}
+
+void receive(Receiver& receiver, uint count = 1, uint start = 1,
+ const std::string& base = "Message", qpid::sys::Duration timeout=qpid::sys::TIME_SEC*5)
+{
+ for (uint i = start; i < start + count; ++i) {
+ BOOST_CHECK_EQUAL(receiver.fetch(timeout).getContent(), (boost::format("%1%_%2%") % base % i).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSimpleSendReceive)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+}
+
+QPID_AUTO_TEST_CASE(testSendReceiveHeaders)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ for (uint i = 0; i < 10; ++i) {
+ out.getHeaders()["a"] = i;
+ sender.send(out);
+ }
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in;
+ for (uint i = 0; i < 10; ++i) {
+ BOOST_CHECK(receiver.fetch(in, 5 * qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(in.getHeaders()["a"].asUint32(), i);
+ fix.session.acknowledge();
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSenderError)
+{
+ MessagingFixture fix;
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress"), qpid::messaging::InvalidAddress);
+ fix.session = fix.connection.newSession();
+ BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress; {create:receiver}"),
+ qpid::messaging::InvalidAddress);
+}
+
+QPID_AUTO_TEST_CASE(testReceiverError)
+{
+ MessagingFixture fix;
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress"), qpid::messaging::InvalidAddress);
+ fix.session = fix.connection.newSession();
+ BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress; {create:sender}"),
+ qpid::messaging::InvalidAddress);
+}
+
+QPID_AUTO_TEST_CASE(testSimpleTopic)
+{
+ TopicFixture fix;
+
+ Sender sender = fix.session.createSender(fix.topic);
+ Message msg("one");
+ sender.send(msg);
+ Receiver sub1 = fix.session.createReceiver(fix.topic);
+ sub1.setCapacity(10u);
+ msg.setContent("two");
+ sender.send(msg);
+ Receiver sub2 = fix.session.createReceiver(fix.topic);
+ sub2.setCapacity(10u);
+ msg.setContent("three");
+ sender.send(msg);
+ Receiver sub3 = fix.session.createReceiver(fix.topic);
+ sub3.setCapacity(10u);
+ msg.setContent("four");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub2, 2), boost::assign::list_of<std::string>("three")("four"));
+ sub2.cancel();
+
+ msg.setContent("five");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub1, 4), boost::assign::list_of<std::string>("two")("three")("four")("five"));
+ BOOST_CHECK_EQUAL(fetch(sub3, 2), boost::assign::list_of<std::string>("four")("five"));
+ Message in;
+ BOOST_CHECK(!sub2.fetch(in, 0));//TODO: or should this raise an error?
+
+
+ //TODO: check pending messages...
+}
+
+QPID_AUTO_TEST_CASE(testNextReceiver)
+{
+ MultiQueueFixture fix;
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Receiver r = fix.session.createReceiver(fix.queues[i]);
+ r.setCapacity(10u);
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Sender s = fix.session.createSender(fix.queues[i]);
+ Message msg((boost::format("Message_%1%") % (i+1)).str());
+ s.send(msg);
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Message msg;
+ BOOST_CHECK(fix.session.nextReceiver().fetch(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testMapMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ MapContent content(out);
+ content["abc"] = "def";
+ content["pi"] = 3.14f;
+ content.encode();
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ MapView view(in);
+ BOOST_CHECK_EQUAL(view["abc"].asString(), "def");
+ BOOST_CHECK_EQUAL(view["pi"].asFloat(), 3.14f);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testListMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ ListContent content(out);
+ content.push_back(Variant("abc"));
+ content.push_back(Variant(1234));
+ content.push_back(Variant("def"));
+ content.push_back(Variant(56.789));
+ content.encode();
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ ListView view(in);
+ BOOST_CHECK_EQUAL(view.size(), content.size());
+ BOOST_CHECK_EQUAL(view.front().asString(), "abc");
+ BOOST_CHECK_EQUAL(view.back().asDouble(), 56.789);
+
+ ListView::const_iterator i = view.begin();
+ BOOST_CHECK(i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "abc");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asInt64(), 1234);
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "def");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asDouble(), 56.789);
+ BOOST_CHECK(++i == view.end());
+
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testReject)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message m1("reject-me");
+ sender.send(m1);
+ Message m2("accept-me");
+ sender.send(m2);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(in.getContent(), m1.getContent());
+ fix.session.reject(in);
+ in = receiver.fetch(5 * qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(in.getContent(), m2.getContent());
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testAvailable)
+{
+ MultiQueueFixture fix;
+
+ Receiver r1 = fix.session.createReceiver(fix.queues[0]);
+ r1.setCapacity(100);
+
+ Receiver r2 = fix.session.createReceiver(fix.queues[1]);
+ r2.setCapacity(100);
+
+ Sender s1 = fix.session.createSender(fix.queues[0]);
+ Sender s2 = fix.session.createSender(fix.queues[1]);
+
+ for (uint i = 0; i < 10; ++i) {
+ s1.send(Message((boost::format("A_%1%") % (i+1)).str()));
+ }
+ for (uint i = 0; i < 5; ++i) {
+ s2.send(Message((boost::format("B_%1%") % (i+1)).str()));
+ }
+ qpid::sys::sleep(1);//is there any avoid an arbitrary sleep while waiting for messages to be dispatched?
+ for (uint i = 0; i < 5; ++i) {
+ BOOST_CHECK_EQUAL(fix.session.available(), 15u - 2*i);
+ BOOST_CHECK_EQUAL(r1.available(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str());
+ BOOST_CHECK_EQUAL(r2.available(), 5u - i);
+ BOOST_CHECK_EQUAL(r2.fetch().getContent(), (boost::format("B_%1%") % (i+1)).str());
+ fix.session.acknowledge();
+ }
+ for (uint i = 5; i < 10; ++i) {
+ BOOST_CHECK_EQUAL(fix.session.available(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.available(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testPendingAck)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ for (uint i = 0; i < 10; ++i) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ for (uint i = 0; i < 10; ++i) {
+ BOOST_CHECK_EQUAL(receiver.fetch().getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+ BOOST_CHECK_EQUAL(fix.session.pendingAck(), 0u);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(fix.session.pendingAck(), 10u);
+ fix.session.sync();
+ BOOST_CHECK_EQUAL(fix.session.pendingAck(), 0u);
+}
+
+QPID_AUTO_TEST_CASE(testPendingSend)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ send(sender, 10);
+ //Note: this test relies on 'inside knowledge' of the sender
+ //implementation and the fact that the simple test case makes it
+ //possible to predict when completion information will be sent to
+ //the client. TODO: is there a better way of testing this?
+ BOOST_CHECK_EQUAL(sender.pending(), 10u);
+ fix.session.sync();
+ BOOST_CHECK_EQUAL(sender.pending(), 0u);
+
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ receive(receiver, 10);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testBrowse)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ send(sender, 10);
+ Receiver browser1 = fix.session.createReceiver(fix.queue + "; {browse:true}");
+ receive(browser1, 10);
+ Receiver browser2 = fix.session.createReceiver(fix.queue + "; {browse:true}");
+ receive(browser2, 10);
+ Receiver consumer = fix.session.createReceiver(fix.queue);
+ receive(consumer, 10);
+ fix.session.acknowledge();
+}
+
+struct QueueCreatePolicyFixture : public MessagingFixture
+{
+ qpid::messaging::Address address;
+
+ QueueCreatePolicyFixture(const std::string& a) : address(a) {}
+
+ void test()
+ {
+ ping(address);
+ BOOST_CHECK(admin.checkQueueExists(address.getName()));
+ }
+
+ ~QueueCreatePolicyFixture()
+ {
+ admin.deleteQueue(address.getName());
+ }
+};
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueAlways)
+{
+ QueueCreatePolicyFixture fix("#; {create:always, node-properties:{type:queue}}");
+ fix.test();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueReceiver)
+{
+ QueueCreatePolicyFixture fix("#; {create:receiver, node-properties:{type:queue}}");
+ Receiver r = fix.session.createReceiver(fix.address);
+ fix.test();
+ r.cancel();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueSender)
+{
+ QueueCreatePolicyFixture fix("#; {create:sender, node-properties:{type:queue}}");
+ Sender s = fix.session.createSender(fix.address);
+ fix.test();
+ s.cancel();
+}
+
+struct ExchangeCreatePolicyFixture : public MessagingFixture
+{
+ qpid::messaging::Address address;
+ const std::string exchangeType;
+
+ ExchangeCreatePolicyFixture(const std::string& a, const std::string& t) :
+ address(a), exchangeType(t) {}
+
+ void test()
+ {
+ ping(address);
+ std::string actualType;
+ BOOST_CHECK(admin.checkExchangeExists(address.getName(), actualType));
+ BOOST_CHECK_EQUAL(exchangeType, actualType);
+ }
+
+ ~ExchangeCreatePolicyFixture()
+ {
+ admin.deleteExchange(address.getName());
+ }
+};
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopic)
+{
+ ExchangeCreatePolicyFixture fix("#; {create:always, node-properties:{type:topic}}",
+ "topic");
+ fix.test();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopicReceiverFanout)
+{
+ ExchangeCreatePolicyFixture fix("#/my-subject; {create:receiver, node-properties:{type:topic, x-properties:{type:fanout}}}", "fanout");
+ Receiver r = fix.session.createReceiver(fix.address);
+ fix.test();
+ r.cancel();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopicSenderDirect)
+{
+ ExchangeCreatePolicyFixture fix("#/my-subject; {create:sender, node-properties:{type:topic, x-properties:{type:direct}}}", "direct");
+ Sender s = fix.session.createSender(fix.address);
+ fix.test();
+ s.cancel();
+}
+
+struct DeletePolicyFixture : public MessagingFixture
+{
+ enum Mode {RECEIVER, SENDER, ALWAYS, NEVER};
+
+ std::string getPolicy(Mode mode)
+ {
+ switch (mode) {
+ case SENDER:
+ return "{delete:sender}";
+ case RECEIVER:
+ return "{delete:receiver}";
+ case ALWAYS:
+ return "{delete:always}";
+ case NEVER:
+ return "{delete:never}";
+ }
+ return "";
+ }
+
+ void testAll()
+ {
+ test(RECEIVER);
+ test(SENDER);
+ test(ALWAYS);
+ test(NEVER);
+ }
+
+ virtual ~DeletePolicyFixture() {}
+ virtual void create(const qpid::messaging::Address&) = 0;
+ virtual void destroy(const qpid::messaging::Address&) = 0;
+ virtual bool exists(const qpid::messaging::Address&) = 0;
+
+ void test(Mode mode)
+ {
+ qpid::messaging::Address address("#; " + getPolicy(mode));
+ create(address);
+
+ Sender s = session.createSender(address);
+ Receiver r = session.createReceiver(address);
+ switch (mode) {
+ case RECEIVER:
+ s.cancel();
+ BOOST_CHECK(exists(address));
+ r.cancel();
+ BOOST_CHECK(!exists(address));
+ break;
+ case SENDER:
+ r.cancel();
+ BOOST_CHECK(exists(address));
+ s.cancel();
+ BOOST_CHECK(!exists(address));
+ break;
+ case ALWAYS:
+ s.cancel();
+ BOOST_CHECK(!exists(address));
+ break;
+ case NEVER:
+ r.cancel();
+ BOOST_CHECK(exists(address));
+ s.cancel();
+ BOOST_CHECK(exists(address));
+ destroy(address);
+ }
+ }
+};
+
+struct QueueDeletePolicyFixture : DeletePolicyFixture
+{
+ void create(const qpid::messaging::Address& address)
+ {
+ admin.createQueue(address.getName());
+ }
+ void destroy(const qpid::messaging::Address& address)
+ {
+ admin.deleteQueue(address.getName());
+ }
+ bool exists(const qpid::messaging::Address& address)
+ {
+ return admin.checkQueueExists(address.getName());
+ }
+};
+
+struct ExchangeDeletePolicyFixture : DeletePolicyFixture
+{
+ const std::string exchangeType;
+ ExchangeDeletePolicyFixture(const std::string type = "topic") : exchangeType(type) {}
+
+ void create(const qpid::messaging::Address& address)
+ {
+ admin.createExchange(address.getName(), exchangeType);
+ }
+ void destroy(const qpid::messaging::Address& address)
+ {
+ admin.deleteExchange(address.getName());
+ }
+ bool exists(const qpid::messaging::Address& address)
+ {
+ std::string actualType;
+ return admin.checkExchangeExists(address.getName(), actualType) && actualType == exchangeType;
+ }
+};
+
+QPID_AUTO_TEST_CASE(testDeletePolicyQueue)
+{
+ QueueDeletePolicyFixture fix;
+ fix.testAll();
+}
+
+QPID_AUTO_TEST_CASE(testDeletePolicyExchange)
+{
+ ExchangeDeletePolicyFixture fix;
+ fix.testAll();
+}
+
+QPID_AUTO_TEST_CASE(testAssertPolicyQueue)
+{
+ MessagingFixture fix;
+ std::string a1 = "q; {create:always, assert:always, node-properties:{type:queue, durable:false, x-properties:{qpid.max-count:100}}}";
+ Sender s1 = fix.session.createSender(a1);
+ s1.cancel();
+ Receiver r1 = fix.session.createReceiver(a1);
+ r1.cancel();
+
+ std::string a2 = "q; {assert:receiver, node-properties:{durable:true, x-properties:{qpid.max-count:100}}}";
+ Sender s2 = fix.session.createSender(a2);
+ s2.cancel();
+ BOOST_CHECK_THROW(fix.session.createReceiver(a2), qpid::messaging::InvalidAddress);
+
+ std::string a3 = "q; {assert:sender, node-properties:{x-properties:{qpid.max-count:99}}}";
+ BOOST_CHECK_THROW(fix.session.createSender(a3), qpid::messaging::InvalidAddress);
+ Receiver r3 = fix.session.createReceiver(a3);
+ r3.cancel();
+
+ fix.admin.deleteQueue("q");
+}
+
+QPID_AUTO_TEST_CASE(testGetSender)
+{
+ QueueFixture fix;
+ std::string name = fix.session.createSender(fix.queue).getName();
+ Sender sender = fix.session.getSender(name);
+ BOOST_CHECK_EQUAL(name, sender.getName());
+ Message out(Uuid(true).str());
+ sender.send(out);
+ Message in;
+ BOOST_CHECK(fix.session.createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.session.getSender("UnknownSender"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetReceiver)
+{
+ QueueFixture fix;
+ std::string name = fix.session.createReceiver(fix.queue).getName();
+ Receiver receiver = fix.session.getReceiver(name);
+ BOOST_CHECK_EQUAL(name, receiver.getName());
+ Message out(Uuid(true).str());
+ fix.session.createSender(fix.queue).send(out);
+ Message in;
+ BOOST_CHECK(receiver.fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.session.getReceiver("UnknownReceiver"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetSessionFromConnection)
+{
+ QueueFixture fix;
+ fix.connection.newSession("my-session");
+ Session session = fix.connection.getSession("my-session");
+ Message out(Uuid(true).str());
+ session.createSender(fix.queue).send(out);
+ Message in;
+ BOOST_CHECK(session.createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.connection.getSession("UnknownSession"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetConnectionFromSession)
+{
+ QueueFixture fix;
+ Message out(Uuid(true).str());
+ Sender sender = fix.session.createSender(fix.queue);
+ sender.send(out);
+ Message in;
+ sender.getSession().getConnection().newSession("incoming");
+ BOOST_CHECK(fix.connection.getSession("incoming").createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+}
+
+QPID_AUTO_TEST_CASE(testTx)
+{
+ QueueFixture fix;
+ Session ssn1 = fix.connection.newSession(true);
+ Session ssn2 = fix.connection.newSession(true);
+ Sender sender1 = ssn1.createSender(fix.queue);
+ Sender sender2 = ssn2.createSender(fix.queue);
+ Receiver receiver1 = ssn1.createReceiver(fix.queue);
+ Receiver receiver2 = ssn2.createReceiver(fix.queue);
+ Message in;
+
+ send(sender1, 5, 1, "A");
+ send(sender2, 5, 1, "B");
+ ssn2.commit();
+ receive(receiver1, 5, 1, "B");//(only those from sender2 should be received)
+ BOOST_CHECK(!receiver1.fetch(in, 0));//check there are no more messages
+ ssn1.rollback();
+ receive(receiver2, 5, 1, "B");
+ BOOST_CHECK(!receiver2.fetch(in, 0));//check there are no more messages
+ ssn2.rollback();
+ receive(receiver1, 5, 1, "B");
+ BOOST_CHECK(!receiver1.fetch(in, 0));//check there are no more messages
+ ssn1.commit();
+ //check neither receiver gets any more messages:
+ BOOST_CHECK(!receiver1.fetch(in, 0));
+ BOOST_CHECK(!receiver2.fetch(in, 0));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/MockConnectionInputHandler.h b/cpp/src/tests/MockConnectionInputHandler.h
deleted file mode 100644
index 89b6155355..0000000000
--- a/cpp/src/tests/MockConnectionInputHandler.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef _tests_MockConnectionInputHandler_h
-#define _tests_MockConnectionInputHandler_h
-
-/*
- *
- * Copyright (c) 2006 The Apache Software Foundation
- *
- * Licensed 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 "qpid/sys/ConnectionInputHandler.h"
-#include "qpid/sys/ConnectionInputHandlerFactory.h"
-#include "qpid/sys/Monitor.h"
-
-struct MockConnectionInputHandler : public qpid::sys::ConnectionInputHandler {
-
- MockConnectionInputHandler() : state(START) {}
-
- ~MockConnectionInputHandler() {}
-
- void received(qpid::framing::AMQFrame* framep) {
- qpid::sys::Monitor::ScopedLock l(monitor);
- frame = *framep;
- setState(GOT_FRAME);
- }
-
- qpid::framing::AMQFrame waitForFrame() {
- waitFor(GOT_FRAME);
- return frame;
- }
-
- void waitForClosed() {
- waitFor(CLOSED);
- }
-
- void closed() {
- qpid::sys::Monitor::ScopedLock l(monitor);
- setState(CLOSED);
- }
-
- void idleOut() {}
- void idleIn() {}
-
- private:
- typedef enum { START, GOT_FRAME, CLOSED } State;
-
- void setState(State s) {
- state = s;
- monitor.notify();
- }
-
- void waitFor(State s) {
- qpid::sys::Monitor::ScopedLock l(monitor);
- qpid::sys::Time deadline = qpid::sys::now() + 10*qpid::sys::TIME_SEC;
- while (state != s)
- CPPUNIT_ASSERT(monitor.wait(deadline));
- }
-
- qpid::sys::Monitor monitor;
- State state;
- qpid::framing::AMQFrame frame;
-};
-
-
-struct MockConnectionInputHandlerFactory : public qpid::sys::ConnectionInputHandlerFactory {
- MockConnectionInputHandlerFactory() : handler(0) {}
-
- qpid::sys::ConnectionInputHandler* create(qpid::sys::ConnectionOutputHandler*) {
- qpid::sys::Monitor::ScopedLock lock(monitor);
- handler = new MockConnectionInputHandler();
- monitor.notifyAll();
- return handler;
- }
-
- void waitForHandler() {
- qpid::sys::Monitor::ScopedLock lock(monitor);
- qpid::sys::Time deadline =
- qpid::sys::now() + 500 * qpid::sys::TIME_SEC;
- while (handler == 0)
- CPPUNIT_ASSERT(monitor.wait(deadline));
- }
-
- MockConnectionInputHandler* handler;
- qpid::sys::Monitor monitor;
-};
-
-
-
-#endif /*!_tests_MockConnectionInputHandler_h*/
diff --git a/cpp/src/tests/PartialFailure.cpp b/cpp/src/tests/PartialFailure.cpp
new file mode 100644
index 0000000000..63ee28017a
--- /dev/null
+++ b/cpp/src/tests/PartialFailure.cpp
@@ -0,0 +1,291 @@
+/*
+ *
+ * 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 Tests for partial failure in a cluster.
+ * Partial failure means some nodes experience a failure while others do not.
+ * In this case the failed nodes must shut down.
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "ClusterFixture.h"
+#include <boost/assign.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(PartialFailureTestSuite)
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::client;
+using namespace qpid::client::arg;
+using namespace boost::assign;
+using broker::Broker;
+using boost::shared_ptr;
+
+// Timeout for tests that wait for messages
+const sys::Duration TIMEOUT=sys::TIME_SEC/4;
+
+static bool isLogOption(const std::string& s) { return boost::starts_with(s, "--log-enable"); }
+
+void updateArgs(ClusterFixture::Args& args, size_t index) {
+ ostringstream clusterLib, testStoreLib, storeName;
+ clusterLib << getLibPath("CLUSTER_LIB");
+ testStoreLib << getLibPath("TEST_STORE_LIB");
+ storeName << "s" << index;
+ args.push_back("--auth");
+ args.push_back("no");
+ args.push_back("--no-module-dir");
+ args.push_back("--load-module");
+ args.push_back(clusterLib.str());
+ args.push_back("--load-module");
+ args.push_back(testStoreLib.str());
+ args.push_back("--test-store-name");
+ args.push_back(storeName.str());
+ args.push_back("TMP_DATA_DIR");
+
+ // These tests generate errors deliberately, disable error logging unless a log env var is set.
+ if (!::getenv("QPID_TRACE") && !::getenv("QPID_LOG_ENABLE")) {
+ remove_if(args.begin(), args.end(), isLogOption);
+ args.push_back("--log-enable=critical+:DISABLED"); // hacky way to disable logs.
+ }
+}
+
+Message pMessage(string data, string q) {
+ Message msg(data, q);
+ msg.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+ return msg;
+}
+
+void queueAndSub(Client& c) {
+ c.session.queueDeclare(c.name, durable=true);
+ c.subs.subscribe(c.lq, c.name);
+}
+
+// Handle near-simultaneous errors
+QPID_AUTO_TEST_CASE(testCoincidentErrors) {
+ ClusterFixture cluster(2, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+
+ c0.session.queueDeclare("q", durable=true);
+ {
+ ScopedSuppressLogging allQuiet;
+ async(c0.session).messageTransfer(content=pMessage("TEST_STORE_DO: s0[exception]", "q"));
+ async(c1.session).messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception]", "q"));
+
+ int alive=0;
+ try { Client c00(cluster[0], "c00"); ++alive; c00.close(); } catch (...) {}
+ try { Client c11(cluster[1], "c11"); ++alive; c11.close(); } catch (...) {}
+
+ BOOST_CHECK_EQUAL(alive, 1);
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ }
+}
+
+// Verify normal cluster-wide errors.
+QPID_AUTO_TEST_CASE(testNormalErrors) {
+ // FIXME aconway 2009-04-10: Would like to put a scope just around
+ // the statements expected to fail (in BOOST_CHECK_yTHROW) but that
+ // sproadically lets out messages, possibly because they're in
+ // Connection thread.
+
+ ClusterFixture cluster(3, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ Client c2(cluster[2], "c2");
+
+ {
+ ScopedSuppressLogging allQuiet;
+ queueAndSub(c0);
+ c0.session.messageTransfer(content=Message("x", "c0"));
+ BOOST_CHECK_EQUAL(c0.lq.get(TIMEOUT).getData(), "x");
+
+ // Session error.
+ BOOST_CHECK_THROW(c0.session.exchangeBind(), SessionException);
+ c1.session.messageTransfer(content=Message("stay", "c0")); // Will stay on queue, session c0 is dead.
+
+ // Connection error, kill c1 on all members.
+ queueAndSub(c1);
+ BOOST_CHECK_THROW(
+ c1.session.messageTransfer(
+ content=pMessage("TEST_STORE_DO: s0[exception] s1[exception] s2[exception] testNormalErrors", "c1")),
+ ConnectionException);
+ c2.session.messageTransfer(content=Message("stay", "c1")); // Will stay on queue, session/connection c1 is dead.
+
+ BOOST_CHECK_EQUAL(3u, knownBrokerPorts(c2.connection, 3).size());
+ BOOST_CHECK_EQUAL(c2.subs.get("c0", TIMEOUT).getData(), "stay");
+ BOOST_CHECK_EQUAL(c2.subs.get("c1", TIMEOUT).getData(), "stay");
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ }
+}
+
+
+// Test errors after a new member joins to verify frame-sequence-numbers are ok in update.
+QPID_AUTO_TEST_CASE(testErrorAfterJoin) {
+ ClusterFixture cluster(1, updateArgs, -1);
+ Client c0(cluster[0]);
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+
+ // Kill the new guy
+ cluster.add();
+ Client c1(cluster[1]);
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] testErrorAfterJoin", "q"));
+ BOOST_CHECK_THROW(c1.session.messageTransfer(content=pMessage("xxx", "q")), TransportFailure);
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c0.connection, 1).size());
+
+ // Kill the old guy
+ cluster.add();
+ Client c2(cluster[2]);
+ c2.session.messageTransfer(content=pMessage("TEST_STORE_DO: s0[exception] testErrorAfterJoin2", "q"));
+ BOOST_CHECK_THROW(c0.session.messageTransfer(content=pMessage("xxx", "q")), TransportFailure);
+
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c2.connection, 1).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ }
+}
+
+// Test that if one member fails and others do not, the failure leaves the cluster.
+QPID_AUTO_TEST_CASE(testSinglePartialFailure) {
+ ClusterFixture cluster(3, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ Client c2(cluster[2], "c2");
+
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+ // Cause partial failure on c1
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] testSinglePartialFailure", "q"));
+ BOOST_CHECK_THROW(c1.session.queueQuery("q"), TransportFailure);
+
+ c0.session.messageTransfer(content=pMessage("b", "q"));
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 3u);
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size());
+
+ // Cause partial failure on c2
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s2[exception] testSinglePartialFailure2", "q"));
+ BOOST_CHECK_THROW(c2.session.queueQuery("q"), TransportFailure);
+
+ c0.session.messageTransfer(content=pMessage("c", "q"));
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 5u);
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c0.connection, 1).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ }
+}
+
+// Test multiple partial falures: 2 fail 2 pass
+QPID_AUTO_TEST_CASE(testMultiPartialFailure) {
+ ClusterFixture cluster(4, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ Client c2(cluster[2], "c2");
+ Client c3(cluster[3], "c3");
+
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+
+ // Cause partial failure on c1, c2
+ c0.session.messageTransfer(content=pMessage("TEST_STORE_DO: s1[exception] s2[exception] testMultiPartialFailure", "q"));
+ BOOST_CHECK_THROW(c1.session.queueQuery("q"), TransportFailure);
+ BOOST_CHECK_THROW(c2.session.queueQuery("q"), TransportFailure);
+
+ c0.session.messageTransfer(content=pMessage("b", "q"));
+ c3.session.messageTransfer(content=pMessage("c", "q"));
+ BOOST_CHECK_EQUAL(c3.session.queueQuery("q").getMessageCount(), 4u);
+ // FIXME aconway 2009-06-30: This check fails sporadically with 2 != 3.
+ // It should pass reliably.
+ // BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c0.connection, 2).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ c1.close();
+ c2.close();
+ c3.close();
+ }
+}
+
+/** FIXME aconway 2009-04-10:
+ * The current approach to shutting down a process in test_store
+ * sometimes leads to assertion failures and errors in the shut-down
+ * process. Need a cleaner solution
+ */
+#if 0
+QPID_AUTO_TEST_CASE(testPartialFailureMemberLeaves) {
+ ClusterFixture cluster(2, updateArgs, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+
+ {
+ ScopedSuppressLogging allQuiet;
+
+ c0.session.queueDeclare("q", durable=true);
+ c0.session.messageTransfer(content=pMessage("a", "q"));
+
+ // Cause failure on member 0 and simultaneous crash on member 1.
+ BOOST_CHECK_THROW(
+ c0.session.messageTransfer(
+ content=pMessage("TEST_STORE_DO: s0[exception] s1[exit_process] testPartialFailureMemberLeaves", "q")),
+ ConnectionException);
+ cluster.wait(1);
+
+ Client c00(cluster[0], "c00"); // Old connection is dead.
+ BOOST_CHECK_EQUAL(c00.session.queueQuery("q").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(1u, knownBrokerPorts(c00.connection, 1).size());
+
+ // Close inside ScopedSuppressLogging to avoid warnings
+ c0.close();
+ }
+}
+#endif
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/PollableCondition.cpp b/cpp/src/tests/PollableCondition.cpp
new file mode 100644
index 0000000000..f9b3c25c93
--- /dev/null
+++ b/cpp/src/tests/PollableCondition.cpp
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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 "unit_test.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/PollableCondition.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(PollableConditionTest)
+
+using namespace qpid::sys;
+
+const Duration SHORT = TIME_SEC/100;
+const Duration LONG = TIME_SEC/10;
+
+class Callback {
+ public:
+ enum Action { NONE, CLEAR };
+
+ Callback() : count(), action(NONE) {}
+
+ void call(PollableCondition& pc) {
+ Mutex::ScopedLock l(lock);
+ ++count;
+ switch(action) {
+ case NONE: break;
+ case CLEAR: pc.clear(); break;
+ }
+ action = NONE;
+ lock.notify();
+ }
+
+ bool isCalling() { Mutex::ScopedLock l(lock); return wait(LONG); }
+
+ bool isNotCalling() { Mutex::ScopedLock l(lock); return !wait(SHORT); }
+
+ bool nextCall(Action a=NONE) {
+ Mutex::ScopedLock l(lock);
+ action = a;
+ return wait(LONG);
+ }
+
+ private:
+ bool wait(Duration timeout) {
+ int n = count;
+ AbsTime deadline(now(), timeout);
+ while (n == count && lock.wait(deadline))
+ ;
+ return n != count;
+ }
+
+ Monitor lock;
+ int count;
+ Action action;
+};
+
+QPID_AUTO_TEST_CASE(testPollableCondition) {
+ boost::shared_ptr<Poller> poller(new Poller());
+ Callback callback;
+ PollableCondition pc(boost::bind(&Callback::call, &callback, _1), poller);
+
+ Thread runner = Thread(*poller);
+
+ BOOST_CHECK(callback.isNotCalling()); // condition is not set.
+
+ pc.set();
+ BOOST_CHECK(callback.isCalling()); // Set.
+ BOOST_CHECK(callback.isCalling()); // Still set.
+
+ callback.nextCall(Callback::CLEAR);
+ BOOST_CHECK(callback.isNotCalling()); // Cleared
+
+ pc.set();
+ BOOST_CHECK(callback.isCalling()); // Set.
+ callback.nextCall(Callback::CLEAR);
+ BOOST_CHECK(callback.isNotCalling()); // Cleared.
+
+ poller->shutdown();
+ runner.join();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} //namespace qpid::tests
diff --git a/cpp/src/tests/PollerTest.cpp b/cpp/src/tests/PollerTest.cpp
index fcb1d0dadf..11337d6be3 100644
--- a/cpp/src/tests/PollerTest.cpp
+++ b/cpp/src/tests/PollerTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -23,7 +23,9 @@
* Use socketpair to test the poller
*/
+#include "qpid/sys/IOHandle.h"
#include "qpid/sys/Poller.h"
+#include "qpid/sys/posix/PrivatePosix.h"
#include <string>
#include <iostream>
@@ -48,7 +50,7 @@ int writeALot(int fd, const string& s) {
int lastWrite = ::write(fd, s.c_str(), s.size());
if ( lastWrite >= 0) {
bytesWritten += lastWrite;
- }
+ }
} while (errno != EAGAIN);
return bytesWritten;
}
@@ -56,32 +58,32 @@ int writeALot(int fd, const string& s) {
int readALot(int fd) {
int bytesRead = 0;
char buf[1024];
-
+
do {
errno = 0;
int lastRead = ::read(fd, buf, sizeof(buf));
if ( lastRead >= 0) {
bytesRead += lastRead;
- }
+ }
} while (errno != EAGAIN);
return bytesRead;
}
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
{
- try
+ try
{
int sv[2];
- int rc = ::socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
+ int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
assert(rc >= 0);
-
+
// Set non-blocking
rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK);
assert(rc >= 0);
rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK);
assert(rc >= 0);
-
+
// Make up a large string
string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;";
for (int i = 0; i < 6; i++)
@@ -95,66 +97,127 @@ int main(int argc, char** argv)
// Write as much as we can to socket 0
int bytesWritten = writeALot(sv[0], testString);
cout << "Wrote(0): " << bytesWritten << " bytes\n";
-
+
// Read as much as we can from socket 1
bytesRead = readALot(sv[1]);
assert(bytesRead == bytesWritten);
cout << "Read(1): " << bytesRead << " bytes\n";
auto_ptr<Poller> poller(new Poller);
-
- PollerHandle h0(sv[0]);
- PollerHandle h1(sv[1]);
-
- poller->addFd(h0, Poller::INOUT);
-
- // Wait for 500ms - h0 should be writable
+
+ PosixIOHandle f0(sv[0]);
+ PosixIOHandle f1(sv[1]);
+
+ PollerHandle h0(f0);
+ PollerHandle h1(f1);
+
+ poller->registerHandle(h0);
+ poller->monitorHandle(h0, Poller::INOUT);
+
+ // h0 should be writable
Poller::Event event = poller->wait();
assert(event.handle == &h0);
- assert(event.dir == Poller::OUT);
-
+ assert(event.type == Poller::WRITABLE);
+
// Write as much as we can to socket 0
bytesWritten = writeALot(sv[0], testString);
cout << "Wrote(0): " << bytesWritten << " bytes\n";
-
+
// Wait for 500ms - h0 no longer writable
- poller->rearmFd(h0);
event = poller->wait(500000000);
assert(event.handle == 0);
// Test we can read it all now
- poller->addFd(h1, Poller::INOUT);
+ poller->registerHandle(h1);
+ poller->monitorHandle(h1, Poller::INOUT);
event = poller->wait();
assert(event.handle == &h1);
- assert(event.dir == Poller::INOUT);
-
+ assert(event.type == Poller::READ_WRITABLE);
+
bytesRead = readALot(sv[1]);
assert(bytesRead == bytesWritten);
cout << "Read(1): " << bytesRead << " bytes\n";
-
- // At this point h1 should have been disabled from the poller
- // (as it was just returned) and h0 can write again
+
+ // Test poller interrupt
+ assert(poller->interrupt(h0) == true);
event = poller->wait();
assert(event.handle == &h0);
- assert(event.dir == Poller::OUT);
+ assert(event.type == Poller::INTERRUPTED);
+
+ // Test multiple interrupts
+ assert(poller->interrupt(h0) == true);
+ assert(poller->interrupt(h1) == true);
- // Now both the handles should be disabled
+ // Make sure we can interrupt them again
+ assert(poller->interrupt(h0) == true);
+ assert(poller->interrupt(h1) == true);
+
+ // Make sure that they both come out
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h0 || event.handle == &h1);
+ if (event.handle == &h0) {
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h1);
+ } else {
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h0);
+ }
+
+ poller->unmonitorHandle(h1, Poller::INOUT);
+
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ // We didn't write anything so it should still be writable
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ poller->unmonitorHandle(h0, Poller::INOUT);
+
+ event = poller->wait(500000000);
+ assert(event.handle == 0);
+
+ poller->unregisterHandle(h1);
+ assert(poller->interrupt(h1) == false);
+
+ // close the other end to force a disconnect
+ ::close(sv[1]);
+
+ // Now make sure that we are readable followed by disconnected
+ // and after that we never return again
+ poller->monitorHandle(h0, Poller::INOUT);
event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::READABLE);
+ event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::DISCONNECTED);
+ event = poller->wait(1500000000);
assert(event.handle == 0);
-
+
+ // Now we're disconnected monitoring should have no effect at all
+ poller->unmonitorHandle(h0, Poller::INOUT);
+ event = poller->wait(1500000000);
+ assert(event.handle == 0);
+
+ poller->unregisterHandle(h0);
+ assert(poller->interrupt(h0) == false);
+
// Test shutdown
poller->shutdown();
event = poller->wait();
assert(event.handle == 0);
- assert(event.dir == Poller::SHUTDOWN);
+ assert(event.type == Poller::SHUTDOWN);
event = poller->wait();
assert(event.handle == 0);
- assert(event.dir == Poller::SHUTDOWN);
+ assert(event.type == Poller::SHUTDOWN);
- poller->delFd(h1);
- poller->delFd(h0);
-
return 0;
} catch (exception& e) {
cout << "Caught exception " << e.what() << "\n";
diff --git a/cpp/src/tests/ProxyTest.cpp b/cpp/src/tests/ProxyTest.cpp
new file mode 100644
index 0000000000..a926b28395
--- /dev/null
+++ b/cpp/src/tests/ProxyTest.cpp
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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 <iostream>
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/ExecutionSyncBody.h"
+#include "qpid/framing/Proxy.h"
+
+#include "unit_test.h"
+
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ProxyTestSuite)
+
+
+QPID_AUTO_TEST_CASE(testScopedSync)
+{
+ struct DummyHandler : FrameHandler
+ {
+ void handle(AMQFrame& f) {
+ AMQMethodBody* m = f.getMethod();
+ BOOST_CHECK(m);
+ BOOST_CHECK(m->isA<ExecutionSyncBody>());
+ BOOST_CHECK(m->isSync());
+ }
+ };
+ DummyHandler f;
+ Proxy p(f);
+ Proxy::ScopedSync s(p);
+ p.send(ExecutionSyncBody(p.getVersion()));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueueEvents.cpp b/cpp/src/tests/QueueEvents.cpp
new file mode 100644
index 0000000000..bd18fa45fb
--- /dev/null
+++ b/cpp/src/tests/QueueEvents.cpp
@@ -0,0 +1,238 @@
+/*
+ *
+ * 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 "MessageUtils.h"
+#include "unit_test.h"
+#include "BrokerFixture.h"
+#include "qpid/broker/Message.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueEvents.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/sys/Dispatcher.h"
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueEventsSuite)
+
+using namespace qpid::client;
+using namespace qpid::broker;
+using namespace qpid::sys;
+using qpid::framing::SequenceNumber;
+
+struct EventChecker
+{
+ typedef std::deque<QueueEvents::Event> Events;
+
+ Events events;
+ boost::shared_ptr<Poller> poller;
+
+ void handle(QueueEvents::Event e)
+ {
+ if (events.empty()) {
+ BOOST_FAIL("Unexpected event received");
+ } else {
+ BOOST_CHECK_EQUAL(events.front().type, e.type);
+ BOOST_CHECK_EQUAL(events.front().msg.queue, e.msg.queue);
+ BOOST_CHECK_EQUAL(events.front().msg.payload, e.msg.payload);
+ BOOST_CHECK_EQUAL(events.front().msg.position, e.msg.position);
+ events.pop_front();
+ }
+ if (events.empty() && poller) poller->shutdown();
+ }
+
+ void expect(QueueEvents::Event e)
+ {
+ events.push_back(e);
+ }
+};
+
+QPID_AUTO_TEST_CASE(testBasicEventProcessing)
+{
+ boost::shared_ptr<Poller> poller(new Poller());
+ sys::Dispatcher dispatcher(poller);
+ Thread dispatchThread(dispatcher);
+ QueueEvents events(poller);
+ EventChecker listener;
+ listener.poller = poller;
+ events.registerListener("dummy", boost::bind(&EventChecker::handle, &listener, _1));
+ //signal occurence of some events:
+ Queue queue("queue1");
+ SequenceNumber id;
+ QueuedMessage event1(&queue, MessageUtils::createMessage(), id);
+ QueuedMessage event2(&queue, MessageUtils::createMessage(), ++id);
+
+ //define events expected by listener:
+ listener.expect(QueueEvents::Event(QueueEvents::ENQUEUE, event1));
+ listener.expect(QueueEvents::Event(QueueEvents::ENQUEUE, event2));
+ listener.expect(QueueEvents::Event(QueueEvents::DEQUEUE, event1));
+
+ events.enqueued(event1);
+ events.enqueued(event2);
+ events.dequeued(event1);
+
+ dispatchThread.join();
+ events.shutdown();
+ events.unregisterListener("dummy");
+}
+
+
+struct EventRecorder
+{
+ struct EventRecord
+ {
+ QueueEvents::EventType type;
+ std::string queue;
+ std::string content;
+ SequenceNumber position;
+ };
+
+ typedef std::deque<EventRecord> Events;
+
+ Events events;
+
+ void handle(QueueEvents::Event event)
+ {
+ EventRecord record;
+ record.type = event.type;
+ record.queue = event.msg.queue->getName();
+ event.msg.payload->getFrames().getContent(record.content);
+ record.position = event.msg.position;
+ events.push_back(record);
+ }
+
+ void check(QueueEvents::EventType type, const std::string& queue, const std::string& content, const SequenceNumber& position)
+ {
+ if (events.empty()) {
+ BOOST_FAIL("Missed event");
+ } else {
+ BOOST_CHECK_EQUAL(events.front().type, type);
+ BOOST_CHECK_EQUAL(events.front().queue, queue);
+ BOOST_CHECK_EQUAL(events.front().content, content);
+ BOOST_CHECK_EQUAL(events.front().position, position);
+ events.pop_front();
+ }
+ }
+ void checkEnqueue(const std::string& queue, const std::string& data, const SequenceNumber& position)
+ {
+ check(QueueEvents::ENQUEUE, queue, data, position);
+ }
+
+ void checkDequeue(const std::string& queue, const std::string& data, const SequenceNumber& position)
+ {
+ check(QueueEvents::DEQUEUE, queue, data, position);
+ }
+};
+
+QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing)
+{
+ ProxySessionFixture fixture;
+ //register dummy event listener to broker
+ EventRecorder listener;
+ fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1));
+
+ //declare queue with event options specified
+ QueueOptions options;
+ options.enableQueueEvents(false);
+ std::string q("queue-events-test");
+ fixture.session.queueDeclare(arg::queue=q, arg::arguments=options);
+ //send and consume some messages
+ LocalQueue incoming;
+ Subscription sub = fixture.subs.subscribe(incoming, q);
+ for (int i = 0; i < 5; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 3; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ for (int i = 5; i < 10; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 3; i < 10; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ fixture.connection.close();
+ fixture.broker->getQueueEvents().shutdown();
+
+ //check listener was notified of all events, and in correct order
+ SequenceNumber enqueueId(1);
+ SequenceNumber dequeueId(1);
+ for (int i = 0; i < 5; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+ for (int i = 0; i < 3; i++) {
+ listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++);
+ }
+ for (int i = 5; i < 10; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+ for (int i = 3; i < 10; i++) {
+ listener.checkDequeue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), dequeueId++);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly)
+{
+ ProxySessionFixture fixture;
+ //register dummy event listener to broker
+ EventRecorder listener;
+ fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1));
+
+ //declare queue with event options specified
+ QueueOptions options;
+ options.enableQueueEvents(true);
+ std::string q("queue-events-test");
+ fixture.session.queueDeclare(arg::queue=q, arg::arguments=options);
+ //send and consume some messages
+ LocalQueue incoming;
+ Subscription sub = fixture.subs.subscribe(incoming, q);
+ for (int i = 0; i < 5; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 3; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ for (int i = 5; i < 10; i++) {
+ fixture.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 3; i < 10; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ fixture.connection.close();
+ fixture.broker->getQueueEvents().shutdown();
+
+ //check listener was notified of all events, and in correct order
+ SequenceNumber enqueueId(1);
+ SequenceNumber dequeueId(1);
+ for (int i = 0; i < 5; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+ for (int i = 5; i < 10; i++) {
+ listener.checkEnqueue(q, (boost::format("%1%_%2%") % "Message" % (i+1)).str(), enqueueId++);
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueueOptionsTest.cpp b/cpp/src/tests/QueueOptionsTest.cpp
new file mode 100644
index 0000000000..f2fbaba2c1
--- /dev/null
+++ b/cpp/src/tests/QueueOptionsTest.cpp
@@ -0,0 +1,102 @@
+/*
+ *
+ * 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 <iostream>
+#include "qpid/framing/Array.h"
+#include "qpid/client/QueueOptions.h"
+
+#include "unit_test.h"
+
+using namespace qpid::client;
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueOptionsTestSuite)
+
+QPID_AUTO_TEST_CASE(testSizePolicy)
+{
+ QueueOptions ft;
+
+ ft.setSizePolicy(REJECT,1,2);
+
+ BOOST_CHECK(QueueOptions::strREJECT == ft.getAsString(QueueOptions::strTypeKey));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey));
+
+ ft.setSizePolicy(FLOW_TO_DISK,0,2);
+ BOOST_CHECK(QueueOptions::strFLOW_TO_DISK == ft.getAsString(QueueOptions::strTypeKey));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey));
+
+ ft.setSizePolicy(RING,1,0);
+ BOOST_CHECK(QueueOptions::strRING == ft.getAsString(QueueOptions::strTypeKey));
+
+ ft.setSizePolicy(RING_STRICT,1,0);
+ BOOST_CHECK(QueueOptions::strRING_STRICT == ft.getAsString(QueueOptions::strTypeKey));
+
+ ft.clearSizePolicy();
+ BOOST_CHECK(!ft.isSet(QueueOptions::strTypeKey));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strMaxCountKey));
+}
+
+QPID_AUTO_TEST_CASE(testFlags)
+{
+ QueueOptions ft;
+
+ ft.setPersistLastNode();
+ ft.setOrdering(LVQ);
+
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strPersistLastNode));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strLastValueQueue));
+
+ ft.clearPersistLastNode();
+ ft.setOrdering(FIFO);
+
+ BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue));
+
+}
+
+QPID_AUTO_TEST_CASE(testSetOrdering)
+{
+ //ensure setOrdering(FIFO) works even if not preceded by a call to
+ //setOrdering(LVQ)
+ QueueOptions ft;
+ ft.setOrdering(FIFO);
+ BOOST_CHECK(!ft.isSet(QueueOptions::strLastValueQueue));
+
+}
+
+QPID_AUTO_TEST_CASE(testClearPersistLastNode)
+{
+ //ensure clear works even if not preceded by the setting on the
+ //option
+ QueueOptions ft;
+ ft.clearPersistLastNode();
+ BOOST_CHECK(!ft.isSet(QueueOptions::strPersistLastNode));
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueuePolicyTest.cpp b/cpp/src/tests/QueuePolicyTest.cpp
index db88682010..875976db85 100644
--- a/cpp/src/tests/QueuePolicyTest.cpp
+++ b/cpp/src/tests/QueuePolicyTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -18,64 +18,287 @@
* under the License.
*
*/
-#include "qpid/broker/QueuePolicy.h"
#include "unit_test.h"
+#include "test_tools.h"
+
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "MessageUtils.h"
+#include "BrokerFixture.h"
using namespace qpid::broker;
+using namespace qpid::client;
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite)
+QueuedMessage createMessage(uint32_t size)
+{
+ QueuedMessage msg;
+ msg.payload = MessageUtils::createMessage();
+ MessageUtils::addContent(msg.payload, std::string (size, 'x'));
+ return msg;
+}
+
+
QPID_AUTO_TEST_CASE(testCount)
{
- QueuePolicy policy(5, 0);
- BOOST_CHECK(!policy.limitExceeded());
- for (int i = 0; i < 5; i++) policy.enqueued(10);
- BOOST_CHECK_EQUAL((uint64_t) 0, policy.getMaxSize());
- BOOST_CHECK_EQUAL((uint32_t) 5, policy.getMaxCount());
- BOOST_CHECK(!policy.limitExceeded());
- policy.enqueued(10);
- BOOST_CHECK(policy.limitExceeded());
- policy.dequeued(10);
- BOOST_CHECK(!policy.limitExceeded());
- policy.enqueued(10);
- BOOST_CHECK(policy.limitExceeded());
+ std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 5, 0));
+ BOOST_CHECK_EQUAL((uint64_t) 0, policy->getMaxSize());
+ BOOST_CHECK_EQUAL((uint32_t) 5, policy->getMaxCount());
+
+ QueuedMessage msg = createMessage(10);
+ for (size_t i = 0; i < 5; i++) {
+ policy->tryEnqueue(msg.payload);
+ }
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on enqueuing sixth message");
+ } catch (const ResourceLimitExceededException&) {}
+
+ policy->dequeued(msg);
+ policy->tryEnqueue(msg.payload);
+
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)");
+ } catch (const ResourceLimitExceededException&) {}
}
QPID_AUTO_TEST_CASE(testSize)
{
- QueuePolicy policy(0, 50);
- for (int i = 0; i < 5; i++) policy.enqueued(10);
- BOOST_CHECK(!policy.limitExceeded());
- policy.enqueued(10);
- BOOST_CHECK(policy.limitExceeded());
- policy.dequeued(10);
- BOOST_CHECK(!policy.limitExceeded());
- policy.enqueued(10);
- BOOST_CHECK(policy.limitExceeded());
+ std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 0, 50));
+ QueuedMessage msg = createMessage(10);
+
+ for (size_t i = 0; i < 5; i++) {
+ policy->tryEnqueue(msg.payload);
+ }
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+
+ policy->dequeued(msg);
+ policy->tryEnqueue(msg.payload);
+
+ try {
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
}
QPID_AUTO_TEST_CASE(testBoth)
{
- QueuePolicy policy(5, 50);
- for (int i = 0; i < 5; i++) policy.enqueued(11);
- BOOST_CHECK(policy.limitExceeded());
- policy.dequeued(20);
- BOOST_CHECK(!policy.limitExceeded());//fails
- policy.enqueued(5);
- policy.enqueued(10);
- BOOST_CHECK(policy.limitExceeded());
+ std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy("test", 5, 50));
+ try {
+ QueuedMessage msg = createMessage(51);
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on single message exceeding 50. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+
+ std::vector<QueuedMessage> messages;
+ messages.push_back(createMessage(15));
+ messages.push_back(createMessage(10));
+ messages.push_back(createMessage(11));
+ messages.push_back(createMessage(2));
+ messages.push_back(createMessage(7));
+ for (size_t i = 0; i < messages.size(); i++) {
+ policy->tryEnqueue(messages[i].payload);
+ }
+ //size = 45 at this point, count = 5
+ try {
+ QueuedMessage msg = createMessage(5);
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on count exceeding 6. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+ try {
+ QueuedMessage msg = createMessage(10);
+ policy->tryEnqueue(msg.payload);
+ BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy);
+ } catch (const ResourceLimitExceededException&) {}
+
+
+ policy->dequeued(messages[0]);
+ try {
+ QueuedMessage msg = createMessage(20);
+ policy->tryEnqueue(msg.payload);
+ } catch (const ResourceLimitExceededException&) {
+ BOOST_FAIL("Policy failed incorrectly after dequeue. " << *policy);
+ }
}
QPID_AUTO_TEST_CASE(testSettings)
{
//test reading and writing the policy from/to field table
+ std::auto_ptr<QueuePolicy> a(QueuePolicy::createQueuePolicy("test", 101, 303));
FieldTable settings;
- QueuePolicy a(101, 303);
- a.update(settings);
- QueuePolicy b(settings);
- BOOST_CHECK_EQUAL(a.getMaxCount(), b.getMaxCount());
- BOOST_CHECK_EQUAL(a.getMaxSize(), b.getMaxSize());
+ a->update(settings);
+ std::auto_ptr<QueuePolicy> b(QueuePolicy::createQueuePolicy("test", settings));
+ BOOST_CHECK_EQUAL(a->getMaxCount(), b->getMaxCount());
+ BOOST_CHECK_EQUAL(a->getMaxSize(), b->getMaxSize());
+}
+
+QPID_AUTO_TEST_CASE(testRingPolicy)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("my-ring-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ client::Message msg;
+ for (int i = 5; i < 10; i++) {
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+
+ for (int i = 10; i < 20; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 15; i < 20; i++) {
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+}
+
+QPID_AUTO_TEST_CASE(testStrictRingPolicy)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING_STRICT);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("my-ring-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ LocalQueue incoming;
+ SubscriptionSettings settings(FlowControl::unlimited());
+ settings.autoAck = 0; // no auto ack.
+ Subscription sub = f.subs.subscribe(incoming, q, settings);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 5; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.session.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testPolicyWithDtx)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("my-policy-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ LocalQueue incoming;
+ SubscriptionSettings settings(FlowControl::unlimited());
+ settings.autoAck = 0; // no auto ack.
+ Subscription sub = f.subs.subscribe(incoming, q, settings);
+ f.session.dtxSelect();
+ Xid tx1(1, "test-dtx-mgr", "tx1");
+ f.session.dtxStart(arg::xid=tx1);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ f.session.dtxEnd(arg::xid=tx1);
+ f.session.dtxCommit(arg::xid=tx1, arg::onePhase=true);
+
+ Xid tx2(1, "test-dtx-mgr", "tx2");
+ f.session.dtxStart(arg::xid=tx2);
+ for (int i = 0; i < 5; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ SequenceSet accepting=sub.getUnaccepted();
+ f.session.messageAccept(accepting);
+ f.session.dtxEnd(arg::xid=tx2);
+ f.session.dtxPrepare(arg::xid=tx2);
+ f.session.dtxRollback(arg::xid=tx2);
+ f.session.messageRelease(accepting);
+
+ Xid tx3(1, "test-dtx-mgr", "tx3");
+ f.session.dtxStart(arg::xid=tx3);
+ for (int i = 0; i < 5; i++) {
+ incoming.pop();
+ }
+ accepting=sub.getUnaccepted();
+ f.session.messageAccept(accepting);
+ f.session.dtxEnd(arg::xid=tx3);
+ f.session.dtxPrepare(arg::xid=tx3);
+
+ Session other = f.connection.newSession();
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ other.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+
+ f.session.dtxCommit(arg::xid=tx3);
+ //now retry and this time should succeed
+ other = f.connection.newSession();
+ other.messageTransfer(arg::content=client::Message("Message_6", q));
+}
+
+QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore)
+{
+ //Ensure that with no store loaded, we don't flow to disk but
+ //fallback to rejecting messages
+ QueueOptions args;
+ args.setSizePolicy(FLOW_TO_DISK, 0, 5);
+
+ ProxySessionFixture f;
+ std::string q("my-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ LocalQueue incoming;
+ SubscriptionSettings settings(FlowControl::unlimited());
+ settings.autoAck = 0; // no auto ack.
+ Subscription sub = f.subs.subscribe(incoming, q, settings);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 5; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.session.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testPolicyFailureOnCommit)
+{
+ FieldTable args;
+ std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT);
+ policy->update(args);
+
+ ProxySessionFixture f;
+ std::string q("q");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ f.session.txSelect();
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ BOOST_CHECK_THROW(f.session.txCommit(), InternalErrorException);
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueueRegistryTest.cpp b/cpp/src/tests/QueueRegistryTest.cpp
index 7ad4e0b89d..712cb568c3 100644
--- a/cpp/src/tests/QueueRegistryTest.cpp
+++ b/cpp/src/tests/QueueRegistryTest.cpp
@@ -6,9 +6,9 @@
* 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
@@ -23,6 +23,9 @@
using namespace qpid::broker;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(QueueRegistryTest)
QPID_AUTO_TEST_CASE(testDeclare)
@@ -49,7 +52,7 @@ QPID_AUTO_TEST_CASE(testDeclare)
BOOST_CHECK_EQUAL(bar, q->getName());
}
-QPID_AUTO_TEST_CASE(testDeclareTmp)
+QPID_AUTO_TEST_CASE(testDeclareTmp)
{
QueueRegistry reg;
std::pair<Queue::shared_ptr, bool> qc;
@@ -58,8 +61,8 @@ QPID_AUTO_TEST_CASE(testDeclareTmp)
BOOST_CHECK(qc.second);
BOOST_CHECK_EQUAL(std::string("tmp_1"), qc.first->getName());
}
-
-QPID_AUTO_TEST_CASE(testFind)
+
+QPID_AUTO_TEST_CASE(testFind)
{
std::string foo("foo");
std::string bar("bar");
@@ -75,7 +78,7 @@ QPID_AUTO_TEST_CASE(testFind)
BOOST_CHECK_EQUAL(bar, q->getName());
}
-QPID_AUTO_TEST_CASE(testDestroy)
+QPID_AUTO_TEST_CASE(testDestroy)
{
std::string foo("foo");
QueueRegistry reg;
@@ -92,3 +95,5 @@ QPID_AUTO_TEST_CASE(testDestroy)
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueueTest.cpp b/cpp/src/tests/QueueTest.cpp
index 20b3d90eb6..6c2adf5c87 100644
--- a/cpp/src/tests/QueueTest.cpp
+++ b/cpp/src/tests/QueueTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -18,29 +18,44 @@
* under the License.
*
*/
+#include "MessageUtils.h"
#include "unit_test.h"
+#include "test_tools.h"
#include "qpid/Exception.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/FanOutExchange.h"
#include "qpid/broker/Queue.h"
#include "qpid/broker/Deliverable.h"
#include "qpid/broker/ExchangeRegistry.h"
#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/reply_exceptions.h"
#include <iostream>
#include "boost/format.hpp"
using boost::intrusive_ptr;
using namespace qpid;
using namespace qpid::broker;
+using namespace qpid::client;
using namespace qpid::framing;
using namespace qpid::sys;
+namespace qpid {
+namespace tests {
+
class TestConsumer : public virtual Consumer{
public:
- typedef boost::shared_ptr<TestConsumer> shared_ptr;
+ typedef boost::shared_ptr<TestConsumer> shared_ptr;
intrusive_ptr<Message> last;
bool received;
- TestConsumer(): received(false) {};
+ TestConsumer(bool acquire = true):Consumer(acquire), received(false) {};
virtual bool deliver(QueuedMessage& msg){
last = msg.payload;
@@ -53,19 +68,20 @@ public:
class FailOnDeliver : public Deliverable
{
- Message msg;
+ boost::intrusive_ptr<Message> msg;
public:
- void deliverTo(Queue::shared_ptr& queue)
+ FailOnDeliver() : msg(MessageUtils::createMessage()) {}
+ void deliverTo(const boost::shared_ptr<Queue>& queue)
{
throw Exception(QPID_MSG("Invalid delivery to " << queue->getName()));
}
- Message& getMessage() { return msg; }
+ Message& getMessage() { return *(msg.get()); }
};
-intrusive_ptr<Message> message(std::string exchange, std::string routingKey) {
+intrusive_ptr<Message> create_message(std::string exchange, std::string routingKey) {
intrusive_ptr<Message> msg(new Message());
- AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0));
- AMQFrame header(in_place<AMQHeaderBody>());
+ AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
msg->getFrames().append(method);
msg->getFrames().append(header);
msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
@@ -77,68 +93,69 @@ QPID_AUTO_TEST_SUITE(QueueTestSuite)
QPID_AUTO_TEST_CASE(testAsyncMessage) {
Queue::shared_ptr queue(new Queue("my_test_queue", true));
intrusive_ptr<Message> received;
-
- TestConsumer c1;
+
+ TestConsumer::shared_ptr c1(new TestConsumer());
queue->consume(c1);
-
-
+
+
//Test basic delivery:
- intrusive_ptr<Message> msg1 = message("e", "A");
- msg1->enqueueAsync();//this is done on enqueue which is not called from process
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process
queue->process(msg1);
sleep(2);
-
- BOOST_CHECK(!c1.received);
+
+ BOOST_CHECK(!c1->received);
msg1->enqueueComplete();
-
+
received = queue->get().payload;
- BOOST_CHECK_EQUAL(msg1.get(), received.get());
+ BOOST_CHECK_EQUAL(msg1.get(), received.get());
}
-
-
+
+
QPID_AUTO_TEST_CASE(testAsyncMessageCount){
Queue::shared_ptr queue(new Queue("my_test_queue", true));
- intrusive_ptr<Message> msg1 = message("e", "A");
- msg1->enqueueAsync();//this is done on enqueue which is not called from process
-
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process
+
queue->process(msg1);
sleep(2);
uint32_t compval=0;
- BOOST_CHECK_EQUAL(compval, queue->getMessageCount());
+ BOOST_CHECK_EQUAL(compval, queue->getEnqueueCompleteMessageCount());
msg1->enqueueComplete();
compval=1;
- BOOST_CHECK_EQUAL(compval, queue->getMessageCount());
+ BOOST_CHECK_EQUAL(compval, queue->getEnqueueCompleteMessageCount());
+ BOOST_CHECK_EQUAL(compval, queue->getMessageCount());
}
QPID_AUTO_TEST_CASE(testConsumers){
Queue::shared_ptr queue(new Queue("my_queue", true));
-
+
//Test adding consumers:
- TestConsumer c1;
- TestConsumer c2;
+ TestConsumer::shared_ptr c1(new TestConsumer());
+ TestConsumer::shared_ptr c2(new TestConsumer());
queue->consume(c1);
queue->consume(c2);
-
+
BOOST_CHECK_EQUAL(uint32_t(2), queue->getConsumerCount());
-
+
//Test basic delivery:
- intrusive_ptr<Message> msg1 = message("e", "A");
- intrusive_ptr<Message> msg2 = message("e", "B");
- intrusive_ptr<Message> msg3 = message("e", "C");
-
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
queue->deliver(msg1);
BOOST_CHECK(queue->dispatch(c1));
- BOOST_CHECK_EQUAL(msg1.get(), c1.last.get());
-
+ BOOST_CHECK_EQUAL(msg1.get(), c1->last.get());
+
queue->deliver(msg2);
BOOST_CHECK(queue->dispatch(c2));
- BOOST_CHECK_EQUAL(msg2.get(), c2.last.get());
-
- c1.received = false;
+ BOOST_CHECK_EQUAL(msg2.get(), c2->last.get());
+
+ c1->received = false;
queue->deliver(msg3);
BOOST_CHECK(queue->dispatch(c1));
- BOOST_CHECK_EQUAL(msg3.get(), c1.last.get());
-
+ BOOST_CHECK_EQUAL(msg3.get(), c1->last.get());
+
//Test cancellation:
queue->cancel(c1);
BOOST_CHECK_EQUAL(uint32_t(1), queue->getConsumerCount());
@@ -152,15 +169,15 @@ QPID_AUTO_TEST_CASE(testRegistry){
registry.declare("queue1", true, true);
registry.declare("queue2", true, true);
registry.declare("queue3", true, true);
-
+
BOOST_CHECK(registry.find("queue1"));
BOOST_CHECK(registry.find("queue2"));
BOOST_CHECK(registry.find("queue3"));
-
+
registry.destroy("queue1");
registry.destroy("queue2");
registry.destroy("queue3");
-
+
BOOST_CHECK(!registry.find("queue1"));
BOOST_CHECK(!registry.find("queue2"));
BOOST_CHECK(!registry.find("queue3"));
@@ -168,17 +185,17 @@ QPID_AUTO_TEST_CASE(testRegistry){
QPID_AUTO_TEST_CASE(testDequeue){
Queue::shared_ptr queue(new Queue("my_queue", true));
- intrusive_ptr<Message> msg1 = message("e", "A");
- intrusive_ptr<Message> msg2 = message("e", "B");
- intrusive_ptr<Message> msg3 = message("e", "C");
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
intrusive_ptr<Message> received;
-
+
queue->deliver(msg1);
queue->deliver(msg2);
queue->deliver(msg3);
-
+
BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount());
-
+
received = queue->get().payload;
BOOST_CHECK_EQUAL(msg1.get(), received.get());
BOOST_CHECK_EQUAL(uint32_t(2), queue->getMessageCount());
@@ -187,23 +204,22 @@ QPID_AUTO_TEST_CASE(testDequeue){
BOOST_CHECK_EQUAL(msg2.get(), received.get());
BOOST_CHECK_EQUAL(uint32_t(1), queue->getMessageCount());
- TestConsumer consumer;
+ TestConsumer::shared_ptr consumer(new TestConsumer());
queue->consume(consumer);
queue->dispatch(consumer);
- if (!consumer.received)
+ if (!consumer->received)
sleep(2);
- BOOST_CHECK_EQUAL(msg3.get(), consumer.last.get());
+ BOOST_CHECK_EQUAL(msg3.get(), consumer->last.get());
BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount());
received = queue->get().payload;
BOOST_CHECK(!received);
BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount());
-
+
}
-QPID_AUTO_TEST_CASE(testBound)
-{
+QPID_AUTO_TEST_CASE(testBound){
//test the recording of bindings, and use of those to allow a queue to be unbound
string key("my-key");
FieldTable args;
@@ -231,11 +247,856 @@ QPID_AUTO_TEST_CASE(testBound)
queue->unbind(exchanges, queue);
//ensure the remaining exchanges don't still have the queue bound to them:
- FailOnDeliver deliverable;
+ FailOnDeliver deliverable;
exchange1->route(deliverable, key, &args);
exchange3->route(deliverable, key, &args);
}
-QPID_AUTO_TEST_SUITE_END()
+QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){
+ client::QueueOptions args;
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue(new Queue("my-queue", true));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
+ //enqueue 2 messages
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+
+ //change mode
+ queue->setLastNodeFailure();
+
+ //enqueue 1 message
+ queue->deliver(msg3);
+
+ //check all have persistent ids.
+ BOOST_CHECK(msg1->isPersistent());
+ BOOST_CHECK(msg2->isPersistent());
+ BOOST_CHECK(msg3->isPersistent());
+
+}
+
+
+QPID_AUTO_TEST_CASE(testSeek){
+
+ Queue::shared_ptr queue(new Queue("my-queue", true));
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
+ //enqueue 2 messages
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+
+ TestConsumer::shared_ptr consumer(new TestConsumer(false));
+ SequenceNumber seq(2);
+ consumer->position = seq;
+
+ QueuedMessage qm;
+ queue->dispatch(consumer);
+
+ BOOST_CHECK_EQUAL(msg3.get(), consumer->last.get());
+ queue->dispatch(consumer);
+ queue->dispatch(consumer); // make sure over-run is safe
+
+}
+
+QPID_AUTO_TEST_CASE(testSearch){
+
+ Queue::shared_ptr queue(new Queue("my-queue", true));
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+
+ //enqueue 2 messages
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+
+ SequenceNumber seq(2);
+ QueuedMessage qm = queue->find(seq);
+
+ BOOST_CHECK_EQUAL(seq.getValue(), qm.position.getValue());
+
+ queue->acquire(qm);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
+ SequenceNumber seq1(3);
+ QueuedMessage qm1 = queue->find(seq1);
+ BOOST_CHECK_EQUAL(seq1.getValue(), qm1.position.getValue());
+
+}
+const std::string nullxid = "";
+
+class SimpleDummyCtxt : public TransactionContext {};
+
+class DummyCtxt : public TPCTransactionContext
+{
+ const std::string xid;
+ public:
+ DummyCtxt(const std::string& _xid) : xid(_xid) {}
+ static std::string getXid(TransactionContext& ctxt)
+ {
+ DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt));
+ return c ? c->xid : nullxid;
+ }
+};
+
+class TestMessageStoreOC : public MessageStore
+{
+ std::set<std::string> prepared;
+ uint64_t nextPersistenceId;
+ public:
+
+ uint enqCnt;
+ uint deqCnt;
+ bool error;
+
+ TestMessageStoreOC() : MessageStore(),nextPersistenceId(1),enqCnt(0),deqCnt(0),error(false) {}
+ ~TestMessageStoreOC(){}
+
+ virtual void dequeue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& /*msg*/,
+ const PersistableQueue& /*queue*/)
+ {
+ if (error) throw Exception("Dequeue error test");
+ deqCnt++;
+ }
+
+ virtual void enqueue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& /* queue */)
+ {
+ if (error) throw Exception("Enqueue error test");
+ enqCnt++;
+ msg->enqueueComplete();
+ }
+
+ void createError()
+ {
+ error=true;
+ }
+
+ bool init(const Options*) { return true; }
+ void truncateInit(const bool) {}
+ void create(PersistableQueue& queue, const framing::FieldTable&) { queue.setPersistenceId(nextPersistenceId++); }
+ void destroy(PersistableQueue&) {}
+ void create(const PersistableExchange& exchange, const framing::FieldTable&) { exchange.setPersistenceId(nextPersistenceId++); }
+ void destroy(const PersistableExchange&) {}
+ void bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {}
+ void unbind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&) {}
+ void create(const PersistableConfig& config) { config.setPersistenceId(nextPersistenceId++); }
+ void destroy(const PersistableConfig&) {}
+ void stage(const boost::intrusive_ptr<PersistableMessage>&) {}
+ void destroy(PersistableMessage&) {}
+ void appendContent(const boost::intrusive_ptr<const PersistableMessage>&, const std::string&) {}
+ void loadContent(const qpid::broker::PersistableQueue&, const boost::intrusive_ptr<const PersistableMessage>&,
+ std::string&, uint64_t, uint32_t) { throw qpid::framing::InternalErrorException("Can't load content; persistence not enabled"); }
+ void flush(const qpid::broker::PersistableQueue&) {}
+ uint32_t outstandingQueueAIO(const PersistableQueue&) { return 0; }
+
+ std::auto_ptr<TransactionContext> begin() { return std::auto_ptr<TransactionContext>(new SimpleDummyCtxt()); }
+ std::auto_ptr<TPCTransactionContext> begin(const std::string& xid) { return std::auto_ptr<TPCTransactionContext>(new DummyCtxt(xid)); }
+ void prepare(TPCTransactionContext& ctxt) { prepared.insert(DummyCtxt::getXid(ctxt)); }
+ void commit(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); }
+ void abort(TransactionContext& ctxt) { prepared.erase(DummyCtxt::getXid(ctxt)); }
+ void collectPreparedXids(std::set<std::string>& out) { out.insert(prepared.begin(), prepared.end()); }
+
+ void recover(RecoveryManager&) {}
+};
+
+
+QPID_AUTO_TEST_CASE(testLVQOrdering){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+
+ Queue::shared_ptr queue(new Queue("my-queue", true ));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg4 = create_message("e", "D");
+ intrusive_ptr<Message> received;
+
+ //set deliever match for LVQ a,b,c,a
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+
+ //enqueue 4 message
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+ queue->deliver(msg4);
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg4.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg2.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg3.get(), received.get());
+
+ intrusive_ptr<Message> msg5 = create_message("e", "A");
+ intrusive_ptr<Message> msg6 = create_message("e", "B");
+ intrusive_ptr<Message> msg7 = create_message("e", "C");
+ msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg7->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ queue->deliver(msg5);
+ queue->deliver(msg6);
+ queue->deliver(msg7);
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg5.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg6.get(), received.get());
+
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg7.get(), received.get());
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQEmptyKey){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+ Queue::shared_ptr queue(new Queue("my-queue", true ));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQAcquire){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+
+ Queue::shared_ptr queue(new Queue("my-queue", true ));
+ queue->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg4 = create_message("e", "D");
+ intrusive_ptr<Message> msg5 = create_message("e", "F");
+ intrusive_ptr<Message> msg6 = create_message("e", "G");
+
+ //set deliever match for LVQ a,b,c,a
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
+ msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+
+ //enqueue 4 message
+ queue->deliver(msg1);
+ queue->deliver(msg2);
+ queue->deliver(msg3);
+ queue->deliver(msg4);
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ framing::SequenceNumber sequence(1);
+ QueuedMessage qmsg(queue.get(), msg1, sequence);
+ QueuedMessage qmsg2(queue.get(), msg2, ++sequence);
+
+ BOOST_CHECK(!queue->acquire(qmsg));
+ BOOST_CHECK(queue->acquire(qmsg2));
+
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
+
+ queue->deliver(msg5);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ // set mode to no browse and check
+ args.setOrdering(client::LVQ_NO_BROWSE);
+ queue->configure(args);
+ TestConsumer::shared_ptr c1(new TestConsumer(false));
+
+ queue->dispatch(c1);
+ queue->dispatch(c1);
+ queue->dispatch(c1);
+
+ queue->deliver(msg6);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 3u);
+
+ intrusive_ptr<Message> received;
+ received = queue->get().payload;
+ BOOST_CHECK_EQUAL(msg4.get(), received.get());
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQMultiQueue){
+
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true ));
+ Queue::shared_ptr queue2(new Queue("my-queue", true ));
+ intrusive_ptr<Message> received;
+ queue1->configure(args);
+ queue2->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "A");
+
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+
+ queue1->deliver(msg1);
+ queue2->deliver(msg1);
+ queue1->deliver(msg2);
+
+ received = queue1->get().payload;
+ BOOST_CHECK_EQUAL(msg2.get(), received.get());
+
+ received = queue2->get().payload;
+ BOOST_CHECK_EQUAL(msg1.get(), received.get());
+
+}
+
+QPID_AUTO_TEST_CASE(testLVQRecover){
+
+/* simulate this
+ 1. start 2 nodes
+ 2. create cluster durable lvq
+ 3. send a transient message to the queue
+ 4. kill one of the nodes (to trigger force persistent behaviour)...
+ 5. then restart it (to turn off force persistent behaviour)
+ 6. send another transient message with same lvq key as in 3
+ 7. kill the second node again (retrigger force persistent)
+ 8. stop and recover the first node
+*/
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ // set queue mode
+ args.setOrdering(client::LVQ);
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
+ intrusive_ptr<Message> received;
+ queue1->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg2 = create_message("e", "A");
+ // 2
+ string key;
+ args.getLVQKey(key);
+ BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
+
+ msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ // 3
+ queue1->deliver(msg1);
+ // 4
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ // 5
+ queue1->clearLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ // 6
+ queue1->deliver(msg2);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 2u);
+ BOOST_CHECK_EQUAL(testStore.deqCnt, 1u);
+}
+
+void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0)
+{
+ for (uint i = 0; i < count; i++) {
+ intrusive_ptr<Message> m = create_message("exchange", "key");
+ if (i % 2) {
+ if (oddTtl) m->getProperties<DeliveryProperties>()->setTtl(oddTtl);
+ } else {
+ if (evenTtl) m->getProperties<DeliveryProperties>()->setTtl(evenTtl);
+ }
+ m->setTimestamp(new broker::ExpiryPolicy);
+ queue.deliver(m);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testPurgeExpired) {
+ Queue queue("my-queue");
+ addMessagesToQueue(10, queue);
+ BOOST_CHECK_EQUAL(queue.getMessageCount(), 10u);
+ ::usleep(300*1000);
+ queue.purgeExpired();
+ BOOST_CHECK_EQUAL(queue.getMessageCount(), 5u);
+}
+
+QPID_AUTO_TEST_CASE(testQueueCleaner) {
+ Timer timer;
+ QueueRegistry queues;
+ Queue::shared_ptr queue = queues.declare("my-queue").first;
+ addMessagesToQueue(10, *queue, 200, 400);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 10u);
+
+ QueueCleaner cleaner(queues, timer);
+ cleaner.start(100 * qpid::sys::TIME_MSEC);
+ ::usleep(300*1000);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 5u);
+ ::usleep(300*1000);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 0u);
+}
+
+QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
+
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("queue1", true, &testStore ));
+ queue1->configure(args);
+ Queue::shared_ptr queue2(new Queue("queue2", true, &testStore ));
+ queue2->configure(args);
+
+ intrusive_ptr<Message> msg1 = create_message("e", "A");
+
+ queue1->deliver(msg1);
+ queue2->deliver(msg1);
+
+ //change mode
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 2u);
+
+ // check they don't get stored twice
+ queue1->setLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 2u);
+
+ intrusive_ptr<Message> msg2 = create_message("e", "B");
+ queue1->deliver(msg2);
+ queue2->deliver(msg2);
+
+ queue1->clearLastNodeFailure();
+ queue2->clearLastNodeFailure();
+ // check only new messages get forced
+ queue1->setLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 4u);
+
+ // check no failure messages are stored
+ queue1->clearLastNodeFailure();
+ queue2->clearLastNodeFailure();
+
+ intrusive_ptr<Message> msg3 = create_message("e", "B");
+ queue1->deliver(msg3);
+ queue2->deliver(msg3);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 4u);
+ queue1->setLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 6u);
+
+ // check requeue 1
+ intrusive_ptr<Message> msg4 = create_message("e", "C");
+ intrusive_ptr<Message> msg5 = create_message("e", "D");
+
+ framing::SequenceNumber sequence(1);
+ QueuedMessage qmsg1(queue1.get(), msg4, sequence);
+ QueuedMessage qmsg2(queue2.get(), msg5, ++sequence);
+
+ queue1->requeue(qmsg1);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 7u);
+
+ // check requeue 2
+ queue2->clearLastNodeFailure();
+ queue2->requeue(qmsg2);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 7u);
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 8u);
+
+ queue2->clearLastNodeFailure();
+ queue2->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 8u);
+}
+
+QPID_AUTO_TEST_CASE(testLastNodeRecoverAndFail){
+/*
+simulate this:
+ 1. start two nodes
+ 2. create cluster durable queue and add some messages
+ 3. kill one node (trigger force-persistent behaviour)
+ 4. stop and recover remaining node
+ 5. add another node
+ 6. kill that new node again
+make sure that an attempt to re-enqueue a message does not happen which will
+result in the last man standing exiting with an error.
+
+we need to make sure that recover is safe, i.e. messages are
+not requeued to the store.
+*/
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ // set queue mode
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
+ intrusive_ptr<Message> received;
+ queue1->configure(args);
+
+ // check requeue 1
+ intrusive_ptr<Message> msg1 = create_message("e", "C");
+ intrusive_ptr<Message> msg2 = create_message("e", "D");
+
+ queue1->recover(msg1);
+
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+
+ queue1->clearLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+
+ queue1->deliver(msg2);
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 1u);
+
+}
+
+QPID_AUTO_TEST_CASE(testLastNodeJournalError){
+/*
+simulate store exception going into last node standing
+
+*/
+ TestMessageStoreOC testStore;
+ client::QueueOptions args;
+ // set queue mode
+ args.setPersistLastNode();
+
+ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
+ intrusive_ptr<Message> received;
+ queue1->configure(args);
+
+ // check requeue 1
+ intrusive_ptr<Message> msg1 = create_message("e", "C");
+
+ queue1->deliver(msg1);
+ testStore.createError();
+
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ queue1->setLastNodeFailure();
+ BOOST_CHECK_EQUAL(testStore.enqCnt, 0u);
+
+}
+
+intrusive_ptr<Message> mkMsg(MessageStore& store, std::string content = "", bool durable = false)
+{
+ intrusive_ptr<Message> msg = MessageUtils::createMessage("", "", durable);
+ if (content.size()) MessageUtils::addContent(msg, content);
+ msg->setStore(&store);
+ return msg;
+}
+
+QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){
+
+ TestMessageStoreOC testStore;
+ client::QueueOptions args0; // No size policy
+ client::QueueOptions args1;
+ args1.setSizePolicy(FLOW_TO_DISK, 0, 1);
+ client::QueueOptions args2;
+ args2.setSizePolicy(FLOW_TO_DISK, 0, 2);
+
+ // --- Fanout exchange bound to single transient queue -------------------------------------------------------------
+
+ FanOutExchange sbtFanout1("sbtFanout1", false, args0); // single binding to transient queue
+ Queue::shared_ptr tq1(new Queue("tq1", true)); // transient w/ limit
+ tq1->configure(args1);
+ sbtFanout1.bind(tq1, "", 0);
+
+ intrusive_ptr<Message> msg01 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg01(msg01);
+ sbtFanout1.route(dmsg01, "", 0); // Brings queue 1 to capacity limit
+ msg01->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg01->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg02 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg02(msg02);
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg02, "", 0), ResourceLimitExceededException);
+ msg02->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg02->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg03 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg03(msg03);
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg03, "", 0), ResourceLimitExceededException);
+ msg03->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg03->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg04 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg04(msg04);
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg04, "", 0), ResourceLimitExceededException);
+ msg04->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg04->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ intrusive_ptr<Message> msg05 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg05(msg05);
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg05, "", 0), ResourceLimitExceededException);
+ msg05->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg05->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
+
+ // --- Fanout exchange bound to single durable queue ---------------------------------------------------------------
+
+ FanOutExchange sbdFanout2("sbdFanout2", false, args0); // single binding to durable queue
+ Queue::shared_ptr dq2(new Queue("dq2", true, &testStore)); // durable w/ limit
+ dq2->configure(args1);
+ sbdFanout2.bind(dq2, "", 0);
+
+ intrusive_ptr<Message> msg06 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg06(msg06);
+ sbdFanout2.route(dmsg06, "", 0); // Brings queue 2 to capacity limit
+ msg06->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg06->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg07 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg07(msg07);
+ sbdFanout2.route(dmsg07, "", 0);
+ msg07->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg07->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(2u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg08 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg08(msg08);
+ sbdFanout2.route(dmsg08, "", 0);
+ msg08->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg08->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(3u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg09 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg09(msg09);
+ sbdFanout2.route(dmsg09, "", 0);
+ msg09->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg09->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(4u, dq2->getMessageCount());
+
+ intrusive_ptr<Message> msg10 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg10(msg10);
+ sbdFanout2.route(dmsg10, "", 0);
+ msg10->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg10->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(5u, dq2->getMessageCount());
+
+ // --- Fanout exchange bound to multiple durable queues ------------------------------------------------------------
+
+ FanOutExchange mbdFanout3("mbdFanout3", false, args0); // multiple bindings to durable queues
+ Queue::shared_ptr dq3(new Queue("dq3", true, &testStore)); // durable w/ limit 2
+ dq3->configure(args2);
+ mbdFanout3.bind(dq3, "", 0);
+ Queue::shared_ptr dq4(new Queue("dq4", true, &testStore)); // durable w/ limit 1
+ dq4->configure(args1);
+ mbdFanout3.bind(dq4, "", 0);
+ Queue::shared_ptr dq5(new Queue("dq5", true, &testStore)); // durable no limit
+ dq5->configure(args0);
+ mbdFanout3.bind(dq5, "", 0);
+
+ intrusive_ptr<Message> msg11 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg11(msg11);
+ mbdFanout3.route(dmsg11, "", 0); // Brings queues 3 and 4 to capacity limit
+ msg11->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg11->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg12 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg12(msg12);
+ mbdFanout3.route(dmsg12, "", 0);
+ msg12->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg12->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point!
+ BOOST_CHECK_EQUAL(2u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(2u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(2u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg13 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg13(msg13);
+ mbdFanout3.route(dmsg13, "", 0);
+ msg13->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg13->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(3u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(3u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(3u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg14 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg14(msg14);
+ mbdFanout3.route(dmsg14, "", 0);
+ msg14->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg14->isContentReleased(), false); // XXXX - consequence of transient msg multi-queue ftd policy-handling limitations, fix in broker at some point!
+ BOOST_CHECK_EQUAL(4u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(4u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(4u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg15 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg15(msg15);
+ mbdFanout3.route(dmsg15, "", 0);
+ msg15->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg15->isContentReleased(), true);
+ BOOST_CHECK_EQUAL(5u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(5u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(5u, dq5->getMessageCount());
+
+ // Bind a transient queue, this should block the release of any further messages.
+ // Note: this will result in a violation of the count policy of dq3 and dq4 - but this
+ // is expected until a better overall multi-queue design is implemented. Similarly
+ // for the other tests in this section.
+
+ Queue::shared_ptr tq6(new Queue("tq6", true)); // transient no limit
+ tq6->configure(args0);
+ mbdFanout3.bind(tq6, "", 0);
+
+ intrusive_ptr<Message> msg16 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg16(msg16);
+ mbdFanout3.route(dmsg16, "", 0);
+ msg16->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg16->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(6u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(6u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(6u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg17 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg17(msg17);
+ mbdFanout3.route(dmsg17, "", 0);
+ msg17->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg17->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(7u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(7u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(7u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg18 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg18(msg18);
+ mbdFanout3.route(dmsg18, "", 0);
+ msg18->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg18->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(8u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(8u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(8u, dq5->getMessageCount());
+
+ intrusive_ptr<Message> msg19 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg19(msg19);
+ mbdFanout3.route(dmsg19, "", 0);
+ msg19->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg19->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(9u, dq3->getMessageCount());
+ BOOST_CHECK_EQUAL(9u, dq4->getMessageCount());
+ BOOST_CHECK_EQUAL(9u, dq5->getMessageCount());
+
+
+ // --- Fanout exchange bound to multiple durable and transient queues ----------------------------------------------
+
+ FanOutExchange mbmFanout4("mbmFanout4", false, args0); // multiple bindings to durable/transient queues
+ Queue::shared_ptr dq7(new Queue("dq7", true, &testStore)); // durable no limit
+ dq7->configure(args0);
+ mbmFanout4.bind(dq7, "", 0);
+ Queue::shared_ptr dq8(new Queue("dq8", true, &testStore)); // durable w/ limit
+ dq8->configure(args1);
+ mbmFanout4.bind(dq8, "", 0);
+ Queue::shared_ptr tq9(new Queue("tq9", true)); // transient no limit
+ tq9->configure(args0);
+ mbmFanout4.bind(tq9, "", 0);
+
+ intrusive_ptr<Message> msg20 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg20(msg20);
+ mbmFanout4.route(dmsg20, "", 0); // Brings queue 7 to capacity limit
+ msg20->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg20->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(1u, dq7->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, dq8->getMessageCount());
+ BOOST_CHECK_EQUAL(1u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg21 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
+ DeliverableMessage dmsg21(msg21);
+ mbmFanout4.route(dmsg21, "", 0);
+ msg21->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg21->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(2u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(2u, dq8->getMessageCount());
+ BOOST_CHECK_EQUAL(2u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg22 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
+ DeliverableMessage dmsg22(msg22);
+ mbmFanout4.route(dmsg22, "", 0);
+ msg22->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg22->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(3u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(3u, dq8->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(3u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg23 = mkMsg(testStore); // transient no content
+ DeliverableMessage dmsg23(msg23);
+ mbmFanout4.route(dmsg23, "", 0);
+ msg23->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg23->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(4u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(4u, dq8->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(4u, tq9->getMessageCount());
+
+ intrusive_ptr<Message> msg24 = mkMsg(testStore, "", true); // durable no content
+ DeliverableMessage dmsg24(msg24);
+ mbmFanout4.route(dmsg24, "", 0);
+ msg24->tryReleaseContent();
+ BOOST_CHECK_EQUAL(msg24->isContentReleased(), false);
+ BOOST_CHECK_EQUAL(5u, dq7->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(5u, dq8->getMessageCount()); // over limit
+ BOOST_CHECK_EQUAL(5u, tq9->getMessageCount());
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/RangeSet.cpp b/cpp/src/tests/RangeSet.cpp
index 9c602de78d..db3a964086 100644
--- a/cpp/src/tests/RangeSet.cpp
+++ b/cpp/src/tests/RangeSet.cpp
@@ -24,6 +24,9 @@
using namespace std;
using namespace qpid;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(RangeSetTestSuite)
typedef qpid::Range<int> TestRange;
@@ -44,8 +47,8 @@ QPID_AUTO_TEST_CASE(testRangeSetAddPoint) {
BOOST_CHECK_MESSAGE(r.contains(TestRange(3,4)), r);
BOOST_CHECK(!r.empty());
r += 5;
- BOOST_CHECK_MESSAGE(r.contains(5), r);
- BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r);
+ BOOST_CHECK_MESSAGE(r.contains(5), r);
+ BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r);
BOOST_CHECK_MESSAGE(!r.contains(TestRange(3,6)), r);
r += 4;
BOOST_CHECK_MESSAGE(r.contains(TestRange(3,6)), r);
@@ -139,3 +142,5 @@ QPID_AUTO_TEST_CASE(testRangeContaining) {
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/RateFlowcontrolTest.cpp b/cpp/src/tests/RateFlowcontrolTest.cpp
new file mode 100644
index 0000000000..80ad06af8c
--- /dev/null
+++ b/cpp/src/tests/RateFlowcontrolTest.cpp
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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 "unit_test.h"
+
+#include "qpid/broker/RateFlowcontrol.h"
+#include "qpid/sys/Time.h"
+
+using namespace qpid::broker;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(RateFlowcontrolTestSuite)
+
+QPID_AUTO_TEST_CASE(RateFlowcontrolTest)
+{
+ // BOOST_CHECK(predicate);
+ // BOOST_CHECK_EQUAL(a, b);
+
+ RateFlowcontrol fc(100);
+ AbsTime n=AbsTime::now();
+
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U );
+
+ fc.sentCredit(n, 0);
+
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 0U );
+ fc.sentCredit(n, 50);
+
+ Duration d=250*TIME_MSEC;
+
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 25), 0U );
+ BOOST_CHECK_EQUAL( fc.availableCredit(n), 25U );
+
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 23), 48U );
+ BOOST_CHECK_EQUAL( fc.availableCredit(n), 48U );
+ fc.sentCredit(n, 48);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 50), 0U);
+ BOOST_CHECK(fc.flowStopped());
+
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 25U);
+ n = AbsTime(n,d);
+ BOOST_CHECK_EQUAL( fc.receivedMessage(n, 0), 50U);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/RefCounted.cpp b/cpp/src/tests/RefCounted.cpp
index 8c679a3d2e..e4c1da5696 100644
--- a/cpp/src/tests/RefCounted.cpp
+++ b/cpp/src/tests/RefCounted.cpp
@@ -27,6 +27,9 @@ using boost::intrusive_ptr;
using namespace std;
using namespace qpid;
+namespace qpid {
+namespace tests {
+
struct CountMe : public RefCounted {
static int instances;
CountMe() { ++instances; }
@@ -48,3 +51,5 @@ QPID_AUTO_TEST_CASE(testRefCounted) {
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ReplicationTest.cpp b/cpp/src/tests/ReplicationTest.cpp
new file mode 100644
index 0000000000..3b289d1b4e
--- /dev/null
+++ b/cpp/src/tests/ReplicationTest.cpp
@@ -0,0 +1,141 @@
+/*
+ *
+ * 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 "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+
+#include "qpid/Plugin.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/replication/constants.h"
+#include "qpid/sys/Shlib.h"
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include <boost/assign.hpp>
+#include <boost/bind.hpp>
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::replication::constants;
+using boost::assign::list_of;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ReplicationTestSuite)
+
+// FIXME aconway 2009-11-26: clean this up.
+// The CMake-based build passes in the module suffix; if it's not there, this
+// is a Linux/UNIX libtool-based build.
+#if defined (QPID_MODULE_SUFFIX)
+qpid::sys::Shlib plugin("replicating_listener" QPID_MODULE_SUFFIX);
+#else
+qpid::sys::Shlib plugin(getLibPath("REPLICATING_LISTENER_LIB"));
+#endif
+
+qpid::broker::Broker::Options getBrokerOpts(const std::vector<std::string>& args)
+{
+ std::vector<const char*> argv(args.size());
+ transform(args.begin(), args.end(), argv.begin(), boost::bind(&string::c_str, _1));
+
+ qpid::broker::Broker::Options opts;
+ qpid::Plugin::addOptions(opts);
+ opts.parse(argv.size(), &argv[0], "", true);
+ return opts;
+}
+
+QPID_AUTO_TEST_CASE(testReplicationExchange)
+{
+ qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of<string>("qpidd")
+ ("--replication-exchange-name=qpid.replication")));
+ ProxySessionFixture f(brokerOpts);
+
+
+ std::string dataQ("queue-1");
+ std::string eventQ("event-queue-1");
+ std::string dataQ2("queue-2");
+ std::string eventQ2("event-queue-2");
+ FieldTable eventQopts;
+ eventQopts.setString("qpid.insert_sequence_numbers", REPLICATION_EVENT_SEQNO);
+
+ f.session.queueDeclare(arg::queue=eventQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts);
+ f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ, arg::bindingKey=dataQ);
+
+ f.session.queueDeclare(arg::queue=eventQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=eventQopts);
+ f.session.exchangeBind(arg::exchange="qpid.replication", arg::queue=eventQ2, arg::bindingKey=dataQ2);
+
+ QueueOptions args;
+ args.enableQueueEvents(false);
+ f.session.queueDeclare(arg::queue=dataQ, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ f.session.queueDeclare(arg::queue=dataQ2, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), dataQ));
+ f.session.messageTransfer(arg::content=Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), dataQ2));
+ }
+ Message msg;
+ LocalQueue incoming;
+ Subscription sub = f.subs.subscribe(incoming, dataQ);
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, dataQ));
+
+ sub.cancel();
+ sub = f.subs.subscribe(incoming, eventQ);
+ //check that we received enqueue events for first queue:
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), ENQUEUE);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+1));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ //check that we received dequeue events for first queue:
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), DEQUEUE);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(DEQUEUED_MESSAGE_POSITION), (i+1));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+11));
+ }
+
+ sub.cancel();
+ sub = f.subs.subscribe(incoming, eventQ2);
+ //check that we received enqueue events for second queue:
+ for (int i = 0; i < 10; i++) {
+ BOOST_CHECK(incoming.get(msg, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsString(REPLICATION_TARGET_QUEUE), dataQ2);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt(REPLICATION_EVENT_TYPE), ENQUEUE);
+ BOOST_CHECK_EQUAL(msg.getHeaders().getAsInt64(REPLICATION_EVENT_SEQNO), (i+1));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/RetryList.cpp b/cpp/src/tests/RetryList.cpp
new file mode 100644
index 0000000000..d1d22348a3
--- /dev/null
+++ b/cpp/src/tests/RetryList.cpp
@@ -0,0 +1,111 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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 "unit_test.h"
+#include "test_tools.h"
+#include "qpid/broker/RetryList.h"
+
+using namespace qpid;
+using namespace qpid::broker;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(RetryListTestSuite)
+
+struct RetryListFixture
+{
+ RetryList list;
+ std::vector<Url> urls;
+ std::vector<TcpAddress> expected;
+
+ void addUrl(const std::string& s)
+ {
+ urls.push_back(Url(s));
+ }
+
+ void addExpectation(const std::string& host, uint16_t port)
+ {
+ expected.push_back(TcpAddress(host, port));
+ }
+
+ void check()
+ {
+ list.reset(urls);
+ for (int t = 0; t < 2; t++) {
+ TcpAddress next;
+ for (std::vector<TcpAddress>::const_iterator i = expected.begin(); i != expected.end(); ++i) {
+ BOOST_CHECK(list.next(next));
+ BOOST_CHECK_EQUAL(i->host, next.host);
+ BOOST_CHECK_EQUAL(i->port, next.port);
+ }
+ BOOST_CHECK(!list.next(next));
+ }
+ }
+};
+
+QPID_AUTO_TEST_CASE(testWithSingleAddress)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:host:5673");
+ test.addExpectation("host", 5673);
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:host1,host2:2222,tcp:host3:5673,host4:1");
+
+ test.addExpectation("host1", 5672);
+ test.addExpectation("host2", 2222);
+ test.addExpectation("host3", 5673);
+ test.addExpectation("host4", 1);
+
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:my-host");
+ test.addUrl("amqp:host1:6666,host2:2222,tcp:host3:5673,host4:1");
+ test.addUrl("amqp:host5,host6:2222,tcp:host7:5673");
+
+ test.addExpectation("my-host", 5672);
+ test.addExpectation("host1", 6666);
+ test.addExpectation("host2", 2222);
+ test.addExpectation("host3", 5673);
+ test.addExpectation("host4", 1);
+ test.addExpectation("host5", 5672);
+ test.addExpectation("host6", 2222);
+ test.addExpectation("host7", 5673);
+
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testEmptyList)
+{
+ RetryListFixture test;
+ test.check();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/SequenceNumberTest.cpp b/cpp/src/tests/SequenceNumberTest.cpp
index e4c6d066ef..f3c934e3ca 100644
--- a/cpp/src/tests/SequenceNumberTest.cpp
+++ b/cpp/src/tests/SequenceNumberTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -26,6 +26,8 @@
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
void checkDifference(SequenceNumber& a, SequenceNumber& b, int gap)
{
@@ -54,7 +56,7 @@ void checkComparison(SequenceNumber& a, SequenceNumber& b, int gap)
BOOST_CHECK(++a < ++b);//test prefix
}
//keep incrementing until a also wraps around
- for (int i = 0; i < (gap + 2); i++) {
+ for (int i = 0; i < (gap + 2); i++) {
BOOST_CHECK(a++ < b++);//test postfix
}
//let a 'catch up'
@@ -91,7 +93,7 @@ QPID_AUTO_TEST_CASE(testIncrementPostfix)
BOOST_CHECK(b != c);
}
-QPID_AUTO_TEST_CASE(testIncrementPrefix)
+QPID_AUTO_TEST_CASE(testIncrementPrefix)
{
SequenceNumber a;
SequenceNumber b;
@@ -203,3 +205,5 @@ QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround2)
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/SequenceSet.cpp b/cpp/src/tests/SequenceSet.cpp
index ba2f1391a1..aaeb68e3c5 100644
--- a/cpp/src/tests/SequenceSet.cpp
+++ b/cpp/src/tests/SequenceSet.cpp
@@ -20,6 +20,9 @@
#include "unit_test.h"
#include <list>
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(SequenceSetTestSuite)
using namespace qpid::framing;
@@ -72,7 +75,7 @@ QPID_AUTO_TEST_CASE(testAdd) {
BOOST_CHECK(!s.contains(i));
RangeExpectations().expect(2, 5).expect(8, 8).check(s);
-
+
SequenceSet t;
t.add(6, 10);
t.add(s);
@@ -90,7 +93,7 @@ QPID_AUTO_TEST_CASE(testAdd2) {
SequenceSet s;
s.add(7,6);
s.add(4,4);
- s.add(3,10);
+ s.add(3,10);
s.add(2);
RangeExpectations().expect(2, 10).check(s);
}
@@ -137,4 +140,4 @@ QPID_AUTO_TEST_CASE(testRemove) {
QPID_AUTO_TEST_SUITE_END()
-
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/SessionState.cpp b/cpp/src/tests/SessionState.cpp
index ba966da9b1..157cabfb63 100644
--- a/cpp/src/tests/SessionState.cpp
+++ b/cpp/src/tests/SessionState.cpp
@@ -28,6 +28,9 @@
#include <functional>
#include <numeric>
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(SessionStateTestSuite)
using namespace std;
@@ -45,8 +48,7 @@ T applyAccumulate(Iter begin, Iter end, T seed, const F& f) {
// Create a frame with a one-char string.
AMQFrame& frame(char s) {
- static AMQFrame frame;
- frame.setBody(AMQContentBody(string(&s, 1)));
+ static AMQFrame frame((AMQContentBody(string(&s, 1))));
return frame;
}
@@ -64,7 +66,7 @@ string str(const boost::iterator_range<vector<AMQFrame>::const_iterator>& frames
}
// Make a transfer command frame.
AMQFrame transferFrame(bool hasContent) {
- AMQFrame t(in_place<MessageTransferBody>());
+ AMQFrame t((MessageTransferBody()));
t.setFirstFrame(true);
t.setLastFrame(true);
t.setFirstSegment(true);
@@ -73,7 +75,7 @@ AMQFrame transferFrame(bool hasContent) {
}
// Make a content frame
AMQFrame contentFrame(string content, bool isLast=true) {
- AMQFrame f(in_place<AMQContentBody>(content));
+ AMQFrame f((AMQContentBody(content)));
f.setFirstFrame(true);
f.setLastFrame(true);
f.setFirstSegment(false);
@@ -85,7 +87,7 @@ AMQFrame contentFrameChar(char content, bool isLast=true) {
}
// Send frame & return size of frame.
-size_t send(qpid::SessionState& s, const AMQFrame& f) { s.senderRecord(f); return f.size(); }
+size_t send(qpid::SessionState& s, const AMQFrame& f) { s.senderRecord(f); return f.encodedSize(); }
// Send transfer command with no content.
size_t transfer0(qpid::SessionState& s) { return send(s, transferFrame(false)); }
// Send transfer frame with single content frame.
@@ -95,7 +97,7 @@ size_t transfer1(qpid::SessionState& s, string content) {
size_t transfer1Char(qpid::SessionState& s, char content) {
return transfer1(s, string(1,content));
}
-
+
// Send transfer frame with multiple single-byte content frames.
size_t transferN(qpid::SessionState& s, string content) {
size_t size=send(s, transferFrame(!content.empty()));
@@ -116,8 +118,8 @@ size_t transfers(qpid::SessionState& s, string content) {
bind(transfer1Char, ref(s), _1));
}
-size_t contentFrameSize(size_t n=1) { return AMQFrame(in_place<AMQContentBody>()).size() + n; }
-size_t transferFrameSize() { return AMQFrame(in_place<MessageTransferBody>()).size(); }
+size_t contentFrameSize(size_t n=1) { return AMQFrame(( AMQContentBody())).encodedSize() + n; }
+size_t transferFrameSize() { return AMQFrame((MessageTransferBody())).encodedSize(); }
// ==== qpid::SessionState test classes
@@ -134,8 +136,8 @@ QPID_AUTO_TEST_CASE(testSendGetReplyList) {
transferN(s, "xyz");
BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))),"CabcCdCeCfCxyz");
// Ignore controls.
- s.senderRecord(AMQFrame(in_place<SessionFlushBody>()));
- BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz");
+ s.senderRecord(AMQFrame(new SessionFlushBody()));
+ BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))),"CeCfCxyz");
}
QPID_AUTO_TEST_CASE(testNeedFlush) {
@@ -186,7 +188,7 @@ QPID_AUTO_TEST_CASE(testPeerConfirmed) {
s.senderConfirmed(SessionPoint(5));
BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(5,0))), "CxCy");
BOOST_CHECK(s.senderNeedFlush());
-
+
s.senderConfirmed(SessionPoint(6));
BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(6,0))), "Cy");
BOOST_CHECK(!s.senderNeedFlush());
@@ -196,7 +198,7 @@ QPID_AUTO_TEST_CASE(testPeerCompleted) {
qpid::SessionState s;
s.setTimeout(1);
s.senderGetCommandPoint();
- // Completion implies confirmation
+ // Completion implies confirmation
transfers(s, "abc");
BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCc");
SequenceSet set(SequenceSet() + 0 + 1);
@@ -206,7 +208,7 @@ QPID_AUTO_TEST_CASE(testPeerCompleted) {
transfers(s, "def");
// We dont do out-of-order confirmation, so this will only confirm up to 3:
set = SequenceSet(SequenceSet() + 2 + 3 + 5);
- s.senderCompleted(set);
+ s.senderCompleted(set);
BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CeCf");
}
@@ -216,11 +218,11 @@ QPID_AUTO_TEST_CASE(testReceive) {
s.receiverSetCommandPoint(SessionPoint());
BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(0));
BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(0));
-
+
BOOST_CHECK(s.receiverRecord(transferFrame(false)));
BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(1));
BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(1));
-
+
BOOST_CHECK(s.receiverRecord(transferFrame(true)));
SessionPoint point = SessionPoint(1, transferFrameSize());
BOOST_CHECK_EQUAL(s.receiverGetExpected(), point);
@@ -298,3 +300,5 @@ QPID_AUTO_TEST_CASE(testNeedKnownCompleted) {
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/Shlib.cpp b/cpp/src/tests/Shlib.cpp
index 426a052c9f..692cfcdff9 100644
--- a/cpp/src/tests/Shlib.cpp
+++ b/cpp/src/tests/Shlib.cpp
@@ -24,13 +24,23 @@
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(ShlibTestSuite)
using namespace qpid::sys;
typedef void (*CallMe)(int*);
+
QPID_AUTO_TEST_CASE(testShlib) {
+ // The CMake-based build passes in the module suffix; if it's not there,
+ // this is a Linux/UNIX libtool-based build.
+#if defined (QPID_MODULE_PREFIX) && defined (QPID_MODULE_SUFFIX)
+ Shlib sh("./" QPID_MODULE_PREFIX "shlibtest" QPID_MODULE_SUFFIX);
+#else
Shlib sh(".libs/libshlibtest.so");
+#endif
// Double cast to avoid ISO warning.
CallMe callMe=sh.getSymbol<CallMe>("callMe");
BOOST_REQUIRE(callMe != 0);
@@ -44,17 +54,23 @@ QPID_AUTO_TEST_CASE(testShlib) {
}
catch (const qpid::Exception&) {}
}
-
+
QPID_AUTO_TEST_CASE(testAutoShlib) {
int unloaded = 0;
{
+#if defined (QPID_MODULE_PREFIX) && defined (QPID_MODULE_SUFFIX)
+ AutoShlib sh("./" QPID_MODULE_PREFIX "shlibtest" QPID_MODULE_SUFFIX);
+#else
AutoShlib sh(".libs/libshlibtest.so");
+#endif
CallMe callMe=sh.getSymbol<CallMe>("callMe");
BOOST_REQUIRE(callMe != 0);
callMe(&unloaded);
}
BOOST_CHECK_EQUAL(42, unloaded);
}
-
+
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/SimpleTestCaseBase.cpp b/cpp/src/tests/SimpleTestCaseBase.cpp
deleted file mode 100644
index 2739734731..0000000000
--- a/cpp/src/tests/SimpleTestCaseBase.cpp
+++ /dev/null
@@ -1,87 +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 "SimpleTestCaseBase.h"
-
-using namespace qpid;
-
-void SimpleTestCaseBase::start()
-{
- if (worker.get()) {
- worker->start();
- }
-}
-
-void SimpleTestCaseBase::stop()
-{
- if (worker.get()) {
- worker->stop();
- }
-}
-
-void SimpleTestCaseBase::report(client::Message& report)
-{
- if (worker.get()) {
- report.getHeaders().setInt("MESSAGE_COUNT", worker->getCount());
- //add number of messages sent or received
- std::stringstream reportstr;
- reportstr << worker->getCount();
- report.setData(reportstr.str());
- }
-}
-
-SimpleTestCaseBase::Sender::Sender(ConnectionOptions& options,
- const Exchange& _exchange,
- const std::string& _key,
- const int _messages)
- : Worker(options, _messages), exchange(_exchange), key(_key) {}
-
-void SimpleTestCaseBase::Sender::init()
-{
- channel.start();
-}
-
-void SimpleTestCaseBase::Sender::start(){
- Message msg;
- while (count < messages) {
- channel.publish(msg, exchange, key);
- count++;
- }
- stop();
-}
-
-SimpleTestCaseBase::Worker::Worker(ConnectionOptions& options, const int _messages) :
- messages(_messages), count(0)
-{
- connection.open(options.host, options.port);
- connection.openChannel(channel);
-}
-
-void SimpleTestCaseBase::Worker::stop()
-{
- channel.close();
- connection.close();
-}
-
-int SimpleTestCaseBase::Worker::getCount()
-{
- return count;
-}
-
diff --git a/cpp/src/tests/SimpleTestCaseBase.h b/cpp/src/tests/SimpleTestCaseBase.h
deleted file mode 100644
index 0c1052d0c2..0000000000
--- a/cpp/src/tests/SimpleTestCaseBase.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef _SimpleTestCaseBase_
-#define _SimpleTestCaseBase_
-/*
- *
- * 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 <memory>
-#include <sstream>
-
-#include "qpid/Exception.h"
-#include "qpid/client/Channel.h"
-#include "qpid/client/Message.h"
-#include "qpid/client/Connection.h"
-#include "ConnectionOptions.h"
-#include "qpid/client/MessageListener.h"
-#include "TestCase.h"
-
-
-namespace qpid {
-
-using namespace qpid::client;
-
-class SimpleTestCaseBase : public TestCase
-{
-protected:
- class Worker
- {
- protected:
- client::Connection connection;
- client::Channel channel;
- const int messages;
- int count;
-
- public:
-
- Worker(ConnectionOptions& options, const int messages);
- virtual ~Worker(){}
-
- virtual void stop();
- virtual int getCount();
- virtual void init() = 0;
- virtual void start() = 0;
- };
-
- class Sender : public Worker
- {
- const Exchange& exchange;
- const std::string key;
- public:
- Sender(ConnectionOptions& options,
- const Exchange& exchange,
- const std::string& key,
- const int messages);
- void init();
- void start();
- };
-
- std::auto_ptr<Worker> worker;
-
-public:
- virtual void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options) = 0;
-
- virtual ~SimpleTestCaseBase() {}
-
- void start();
- void stop();
- void report(client::Message& report);
-};
-
-}
-
-#endif
diff --git a/cpp/src/tests/SocketProxy.h b/cpp/src/tests/SocketProxy.h
index a1a1351c7d..4582dc36fd 100644
--- a/cpp/src/tests/SocketProxy.h
+++ b/cpp/src/tests/SocketProxy.h
@@ -21,45 +21,65 @@
*
*/
+#include "qpid/sys/IOHandle.h"
+#ifdef _WIN32
+# include "qpid/sys/windows/IoHandlePrivate.h"
+ typedef SOCKET FdType;
+#else
+# include "qpid/sys/posix/PrivatePosix.h"
+ typedef int FdType;
+#endif
#include "qpid/sys/Socket.h"
-#include "qpid/sys/Poller.h"
#include "qpid/sys/Runnable.h"
#include "qpid/sys/Thread.h"
#include "qpid/sys/Mutex.h"
-#include "qpid/client/Connection.h"
#include "qpid/log/Statement.h"
-#include <algorithm>
+namespace qpid {
+namespace tests {
/**
- * A simple socket proxy that forwards to another socket.
+ * A simple socket proxy that forwards to another socket.
* Used between client & local broker to simulate network failures.
*/
class SocketProxy : private qpid::sys::Runnable
{
+ // Need a Socket we can get the fd from
+ class LowSocket : public qpid::sys::Socket {
+ public:
+#ifdef _WIN32
+ FdType getFd() { return toSocketHandle(*this); }
+#else
+ FdType getFd() { return toFd(impl); }
+#endif
+ };
+
public:
/** Connect to connectPort on host, start a forwarding thread.
* Listen for connection on getPort().
*/
SocketProxy(int connectPort, const std::string host="localhost")
- : closed(false), port(listener.listen()), dropClient(), dropServer()
+ : closed(false), joined(true),
+ port(listener.listen()), dropClient(), dropServer()
{
client.connect(host, connectPort);
+ joined = false;
thread = qpid::sys::Thread(static_cast<qpid::sys::Runnable*>(this));
}
-
- ~SocketProxy() { close(); }
+
+ ~SocketProxy() { close(); if (!joined) thread.join(); }
/** Simulate a network disconnect. */
void close() {
{
qpid::sys::Mutex::ScopedLock l(lock);
- if (closed) return;
+ if (closed) { return; }
closed=true;
}
- poller.shutdown();
- if (thread.id() != qpid::sys::Thread::current().id())
- thread.join();
+ if (thread.id() != qpid::sys::Thread::current().id()) {
+ thread.join();
+ joined = true;
+ }
client.close();
}
@@ -75,7 +95,7 @@ class SocketProxy : private qpid::sys::Runnable
}
uint16_t getPort() const { return port; }
-
+
private:
static void throwErrno(const std::string& msg) {
throw qpid::Exception(msg+":"+qpid::sys::strError(errno));
@@ -85,59 +105,77 @@ class SocketProxy : private qpid::sys::Runnable
}
void run() {
- std::auto_ptr<qpid::sys::Socket> server;
+ std::auto_ptr<LowSocket> server;
try {
- qpid::sys::PollerHandle listenerHandle(listener);
- poller.addFd(listenerHandle, qpid::sys::Poller::IN);
- qpid::sys::Poller::Event event = poller.wait();
- throwIf(event.type == qpid::sys::Poller::SHUTDOWN, "SocketProxy: Closed by close()");
- throwIf(!(event.type == qpid::sys::Poller::READABLE && event.handle == &listenerHandle), "SocketProxy: Accept failed");
-
- poller.delFd(listenerHandle);
- server.reset(listener.accept(0, 0));
-
- // Pump data between client & server sockets
- qpid::sys::PollerHandle clientHandle(client);
- qpid::sys::PollerHandle serverHandle(*server);
- poller.addFd(clientHandle, qpid::sys::Poller::IN);
- poller.addFd(serverHandle, qpid::sys::Poller::IN);
+ fd_set socks;
+ FdType maxFd = listener.getFd();
+ struct timeval tmo;
+ for (;;) {
+ FD_ZERO(&socks);
+ FD_SET(maxFd, &socks);
+ tmo.tv_sec = 0;
+ tmo.tv_usec = 500 * 1000;
+ if (select(maxFd+1, &socks, 0, 0, &tmo) == 0) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ throwIf(closed, "SocketProxy: Closed by close()");
+ continue;
+ }
+ throwIf(!FD_ISSET(maxFd, &socks), "SocketProxy: Accept failed");
+ break; // Accept ready... go to next step
+ }
+ server.reset(reinterpret_cast<LowSocket *>(listener.accept()));
+ maxFd = server->getFd();
+ if (client.getFd() > maxFd)
+ maxFd = client.getFd();
char buffer[1024];
for (;;) {
- qpid::sys::Poller::Event event = poller.wait();
- throwIf(event.type == qpid::sys::Poller::SHUTDOWN, "SocketProxy: Closed by close()");
- throwIf(event.type == qpid::sys::Poller::DISCONNECTED, "SocketProxy: client/server disconnected");
- if (event.handle == &serverHandle) {
- ssize_t n = server->read(buffer, sizeof(buffer));
+ FD_ZERO(&socks);
+ tmo.tv_sec = 0;
+ tmo.tv_usec = 500 * 1000;
+ FD_SET(client.getFd(), &socks);
+ FD_SET(server->getFd(), &socks);
+ if (select(maxFd+1, &socks, 0, 0, &tmo) == 0) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ throwIf(closed, "SocketProxy: Closed by close()");
+ continue;
+ }
+ // Something is set; relay data as needed until something closes
+ if (FD_ISSET(server->getFd(), &socks)) {
+ int n = server->read(buffer, sizeof(buffer));
+ throwIf(n <= 0, "SocketProxy: server disconnected");
if (!dropServer) client.write(buffer, n);
- poller.rearmFd(serverHandle);
- } else if (event.handle == &clientHandle) {
- ssize_t n = client.read(buffer, sizeof(buffer));
- if (!dropClient) server->write(buffer, n);
- poller.rearmFd(clientHandle);
- } else {
- throwIf(true, "SocketProxy: No handle ready");
}
+ if (FD_ISSET(client.getFd(), &socks)) {
+ int n = client.read(buffer, sizeof(buffer));
+ throwIf(n <= 0, "SocketProxy: client disconnected");
+ if (!dropServer) server->write(buffer, n);
+ }
+ if (!FD_ISSET(client.getFd(), &socks) &&
+ !FD_ISSET(server->getFd(), &socks))
+ throwIf(true, "SocketProxy: No handle ready");
}
}
catch (const std::exception& e) {
QPID_LOG(debug, "SocketProxy::run exception: " << e.what());
}
try {
- if (server.get()) server->close();
- close();
- }
+ if (server.get()) server->close();
+ close();
+ }
catch (const std::exception& e) {
QPID_LOG(debug, "SocketProxy::run exception in client/server close()" << e.what());
}
}
mutable qpid::sys::Mutex lock;
- bool closed;
- qpid::sys::Poller poller;
- qpid::sys::Socket client, listener;
+ mutable bool closed;
+ bool joined;
+ LowSocket client, listener;
uint16_t port;
qpid::sys::Thread thread;
bool dropClient, dropServer;
};
+}} // namespace qpid::tests
+
#endif
diff --git a/cpp/src/tests/StoreStatus.cpp b/cpp/src/tests/StoreStatus.cpp
new file mode 100644
index 0000000000..153e4a33db
--- /dev/null
+++ b/cpp/src/tests/StoreStatus.cpp
@@ -0,0 +1,109 @@
+ /*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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 "unit_test.h"
+#include "test_tools.h"
+#include "qpid/cluster/StoreStatus.h"
+#include "qpid/framing/Uuid.h"
+#include <boost/assign.hpp>
+#include <boost/filesystem/operations.hpp>
+
+using namespace std;
+using namespace qpid::cluster;
+using namespace qpid::framing;
+using namespace qpid::framing::cluster;
+using namespace boost::assign;
+using namespace boost::filesystem;
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(StoreStatusTestSuite)
+
+const char* TEST_DIR = "StoreStatus.tmp";
+
+QPID_AUTO_TEST_CASE(testLoadEmpty) {
+ create_directory(TEST_DIR);
+ StoreStatus ss(TEST_DIR);
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_NO_STORE);
+ BOOST_CHECK(!ss.getClusterId());
+ BOOST_CHECK(!ss.getShutdownId());
+ ss.load();
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_EMPTY_STORE);
+ BOOST_CHECK(!ss.getShutdownId());
+ remove_all(TEST_DIR);
+}
+
+QPID_AUTO_TEST_CASE(testSaveLoadDirty) {
+ create_directory(TEST_DIR);
+ Uuid clusterId = Uuid(true);
+ StoreStatus ss(TEST_DIR);
+ ss.load();
+ ss.dirty(clusterId);
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_DIRTY_STORE);
+
+ StoreStatus ss2(TEST_DIR);
+ ss2.load();
+ BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE);
+ BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
+ BOOST_CHECK(!ss2.getShutdownId());
+ remove_all(TEST_DIR);
+}
+
+QPID_AUTO_TEST_CASE(testSaveLoadClean) {
+ create_directory(TEST_DIR);
+ Uuid clusterId = Uuid(true);
+ Uuid shutdownId = Uuid(true);
+ StoreStatus ss(TEST_DIR);
+ ss.load();
+ ss.dirty(clusterId);
+ ss.clean(shutdownId);
+ BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_CLEAN_STORE);
+
+ StoreStatus ss2(TEST_DIR);
+ ss2.load();
+ BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_CLEAN_STORE);
+ BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
+ BOOST_CHECK_EQUAL(ss2.getShutdownId(), shutdownId);
+ remove_all(TEST_DIR);
+}
+
+QPID_AUTO_TEST_CASE(testMarkDirty) {
+ // Save clean then mark to dirty.
+ create_directory(TEST_DIR);
+ Uuid clusterId = Uuid(true);
+ Uuid shutdownId = Uuid(true);
+ StoreStatus ss(TEST_DIR);
+ ss.load();
+ ss.dirty(clusterId);
+ ss.clean(shutdownId);
+ ss.dirty(clusterId);
+
+ StoreStatus ss2(TEST_DIR);
+ ss2.load();
+ BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE);
+ BOOST_CHECK_EQUAL(ss2.getClusterId(), clusterId);
+ BOOST_CHECK(!ss2.getShutdownId());
+ remove_all(TEST_DIR);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/TestCase.h b/cpp/src/tests/TestCase.h
deleted file mode 100644
index ba3330c951..0000000000
--- a/cpp/src/tests/TestCase.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef _TestCase_
-#define _TestCase_
-/*
- *
- * 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 "ConnectionOptions.h"
-#include "qpid/client/Message.h"
-
-
-namespace qpid {
-
-/**
- * Interface to be implemented by test cases for use with the test
- * runner.
- */
-class TestCase
-{
-public:
- /**
- * Directs the test case to act in a particular role. Some roles
- * may be 'activated' at this stage others may require an explicit
- * start request.
- */
- virtual void assign(const std::string& role, framing::FieldTable& params, client::ConnectionOptions& options) = 0;
- /**
- * Each test will be started on its own thread, which should block
- * until the test completes (this may or may not require an
- * explicit stop() request).
- */
- virtual void start() = 0;
- /**
- * Requests that the test be stopped if still running.
- */
- virtual void stop() = 0;
- /**
- * Allows the test to fill in details on the final report
- * message. Will be called only after start has returned.
- */
- virtual void report(client::Message& report) = 0;
-
- virtual ~TestCase() {}
-};
-
-}
-
-#endif
diff --git a/cpp/src/tests/TestMessageStore.h b/cpp/src/tests/TestMessageStore.h
index a6fe716e9c..20e0b755b2 100644
--- a/cpp/src/tests/TestMessageStore.h
+++ b/cpp/src/tests/TestMessageStore.h
@@ -10,9 +10,9 @@
* 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
@@ -28,6 +28,9 @@ using namespace qpid;
using namespace qpid::broker;
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
+
typedef std::pair<string, boost::intrusive_ptr<PersistableMessage> > msg_queue_pair;
class TestMessageStore : public NullMessageStore
@@ -35,7 +38,7 @@ class TestMessageStore : public NullMessageStore
public:
std::vector<boost::intrusive_ptr<PersistableMessage> > dequeued;
std::vector<msg_queue_pair> enqueued;
-
+
void dequeue(TransactionContext*,
const boost::intrusive_ptr<PersistableMessage>& msg,
const PersistableQueue& /*queue*/)
@@ -47,12 +50,14 @@ class TestMessageStore : public NullMessageStore
const boost::intrusive_ptr<PersistableMessage>& msg,
const PersistableQueue& queue)
{
- msg->enqueueComplete();
+ msg->enqueueComplete();
enqueued.push_back(msg_queue_pair(queue.getName(), msg));
}
- TestMessageStore() : NullMessageStore(false) {}
+ TestMessageStore() : NullMessageStore() {}
~TestMessageStore(){}
};
+}} // namespace qpid::tests
+
#endif
diff --git a/cpp/src/tests/TestOptions.h b/cpp/src/tests/TestOptions.h
index a400fe5ecb..f8da0f59cf 100644
--- a/cpp/src/tests/TestOptions.h
+++ b/cpp/src/tests/TestOptions.h
@@ -9,9 +9,9 @@
* 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
@@ -67,7 +67,7 @@ struct TestOptions : public qpid::Options
connection.open(con);
}
-
+
bool help;
ConnectionOptions con;
qpid::log::Options log;
diff --git a/cpp/src/tests/TimerTest.cpp b/cpp/src/tests/TimerTest.cpp
index 50712ff79c..1552421ba0 100644
--- a/cpp/src/tests/TimerTest.cpp
+++ b/cpp/src/tests/TimerTest.cpp
@@ -8,9 +8,9 @@
* 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
@@ -19,7 +19,7 @@
* under the License.
*
*/
-#include "qpid/broker/Timer.h"
+#include "qpid/sys/Timer.h"
#include "qpid/sys/Monitor.h"
#include "unit_test.h"
#include <math.h>
@@ -28,11 +28,13 @@
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
-using namespace qpid::broker;
using namespace qpid::sys;
using boost::intrusive_ptr;
using boost::dynamic_pointer_cast;
+namespace qpid {
+namespace tests {
+
class Counter
{
Mutex lock;
@@ -45,7 +47,7 @@ class Counter
return ++counter;
}
};
-
+
class TestTask : public TimerTask
{
const AbsTime start;
@@ -57,7 +59,7 @@ class TestTask : public TimerTask
Counter& counter;
public:
- TestTask(Duration timeout, Counter& _counter)
+ TestTask(Duration timeout, Counter& _counter)
: TimerTask(timeout), start(now()), expected(timeout), end(start), fired(false), counter(_counter) {}
void fire()
@@ -75,7 +77,11 @@ class TestTask : public TimerTask
BOOST_CHECK(fired);
BOOST_CHECK_EQUAL(expected_position, position);
Duration actual(start, end);
+#ifdef _WIN32
+ uint64_t difference = _abs64(expected - actual);
+#else
uint64_t difference = abs(expected - actual);
+#endif
std::string msg(boost::lexical_cast<std::string>(boost::format("tolerance = %1%, difference = %2%") % tolerance % difference));
BOOST_CHECK_MESSAGE(difference < tolerance, msg);
}
@@ -103,14 +109,14 @@ QPID_AUTO_TEST_CASE(testGeneral)
intrusive_ptr<TestTask> task2(new TestTask(Duration(1 * TIME_SEC), counter));
intrusive_ptr<TestTask> task3(new TestTask(Duration(4 * TIME_SEC), counter));
intrusive_ptr<TestTask> task4(new TestTask(Duration(2 * TIME_SEC), counter));
-
+
timer.add(task1);
timer.add(task2);
timer.add(task3);
timer.add(task4);
-
+
dynamic_pointer_cast<TestTask>(task3)->wait(Duration(6 * TIME_SEC));
-
+
dynamic_pointer_cast<TestTask>(task1)->check(3);
dynamic_pointer_cast<TestTask>(task2)->check(1);
dynamic_pointer_cast<TestTask>(task3)->check(4);
@@ -118,3 +124,5 @@ QPID_AUTO_TEST_CASE(testGeneral)
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/TopicExchangeTest.cpp b/cpp/src/tests/TopicExchangeTest.cpp
index af4263de34..c103620dbf 100644
--- a/cpp/src/tests/TopicExchangeTest.cpp
+++ b/cpp/src/tests/TopicExchangeTest.cpp
@@ -6,9 +6,9 @@
* 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
@@ -21,147 +21,107 @@
#include "test_tools.h"
using namespace qpid::broker;
+using namespace std;
-Tokens makeTokens(const char** begin, const char** end)
-{
- Tokens t;
- t.insert(t.end(), begin, end);
- return t;
-}
-
-// Calculate size of an array.
-#define LEN(a) (sizeof(a)/sizeof(a[0]))
-
-// Convert array to token vector
-#define TOKENS(a) makeTokens(a, a + LEN(a))
-
-#define ASSERT_NORMALIZED(expect, pattern) \
- BOOST_CHECK_EQUAL(Tokens(expect), static_cast<Tokens>(TopicPattern(pattern)))
-
+namespace qpid {
+namespace tests {
QPID_AUTO_TEST_SUITE(TopicExchangeTestSuite)
-QPID_AUTO_TEST_CASE(testTokens)
-{
- Tokens tokens("hello.world");
- const char* expect[] = {"hello", "world"};
- BOOST_CHECK_EQUAL(TOKENS(expect), tokens);
-
- tokens = "a.b.c";
- const char* expect2[] = { "a", "b", "c" };
- BOOST_CHECK_EQUAL(TOKENS(expect2), tokens);
-
- tokens = "";
- BOOST_CHECK(tokens.empty());
-
- tokens = "x";
- const char* expect3[] = { "x" };
- BOOST_CHECK_EQUAL(TOKENS(expect3), tokens);
-
- tokens = (".x");
- const char* expect4[] = { "", "x" };
- BOOST_CHECK_EQUAL(TOKENS(expect4), tokens);
-
- tokens = ("x.");
- const char* expect5[] = { "x", "" };
- BOOST_CHECK_EQUAL(TOKENS(expect5), tokens);
-
- tokens = (".");
- const char* expect6[] = { "", "" };
- BOOST_CHECK_EQUAL(TOKENS(expect6), tokens);
-
- tokens = ("..");
- const char* expect7[] = { "", "", "" };
- BOOST_CHECK_EQUAL(TOKENS(expect7), tokens);
-}
+#define CHECK_NORMALIZED(expect, pattern) BOOST_CHECK_EQUAL(expect, TopicExchange::normalize(pattern));
-QPID_AUTO_TEST_CASE(testNormalize)
-{
- BOOST_CHECK(TopicPattern("").empty());
- ASSERT_NORMALIZED("a.b.c", "a.b.c");
- ASSERT_NORMALIZED("a.*.c", "a.*.c");
- ASSERT_NORMALIZED("#", "#");
- ASSERT_NORMALIZED("#", "#.#.#.#");
- ASSERT_NORMALIZED("*.*.*.#", "#.*.#.*.#.#.*");
- ASSERT_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*.#");
- ASSERT_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*");
-}
-
-QPID_AUTO_TEST_CASE(testPlain)
+QPID_AUTO_TEST_CASE(testNormalize)
{
- TopicPattern p("ab.cd.e");
- BOOST_CHECK(p.match("ab.cd.e"));
- BOOST_CHECK(!p.match("abx.cd.e"));
- BOOST_CHECK(!p.match("ab.cd"));
- BOOST_CHECK(!p.match("ab.cd..e."));
- BOOST_CHECK(!p.match("ab.cd.e."));
- BOOST_CHECK(!p.match(".ab.cd.e"));
-
- p = "";
- BOOST_CHECK(p.match(""));
-
- p = ".";
- BOOST_CHECK(p.match("."));
+ CHECK_NORMALIZED("", "");
+ CHECK_NORMALIZED("a.b.c", "a.b.c");
+ CHECK_NORMALIZED("a.*.c", "a.*.c");
+ CHECK_NORMALIZED("#", "#");
+ CHECK_NORMALIZED("#", "#.#.#.#");
+ CHECK_NORMALIZED("*.*.*.#", "#.*.#.*.#.#.*");
+ CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*.#");
+ CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*");
+ CHECK_NORMALIZED("*.*.*.#", "*.#.#.*.*.#");
}
-
-QPID_AUTO_TEST_CASE(testStar)
+QPID_AUTO_TEST_CASE(testPlain)
{
- TopicPattern p("a.*.b");
- BOOST_CHECK(p.match("a.xx.b"));
- BOOST_CHECK(!p.match("a.b"));
-
- p = "*.x";
- BOOST_CHECK(p.match("y.x"));
- BOOST_CHECK(p.match(".x"));
- BOOST_CHECK(!p.match("x"));
-
- p = "x.x.*";
- BOOST_CHECK(p.match("x.x.y"));
- BOOST_CHECK(p.match("x.x."));
- BOOST_CHECK(!p.match("x.x"));
- BOOST_CHECK(!p.match("q.x.y"));
+ string pattern("ab.cd.e");
+ BOOST_CHECK(TopicExchange::match(pattern, "ab.cd.e"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "abx.cd.e"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "ab.cd"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "ab.cd..e."));
+ BOOST_CHECK(!TopicExchange::match(pattern, "ab.cd.e."));
+ BOOST_CHECK(!TopicExchange::match(pattern, ".ab.cd.e"));
+
+ pattern = "";
+ BOOST_CHECK(TopicExchange::match(pattern, ""));
+
+ pattern = ".";
+ BOOST_CHECK(TopicExchange::match(pattern, "."));
}
-QPID_AUTO_TEST_CASE(testHash)
+
+QPID_AUTO_TEST_CASE(testStar)
{
- TopicPattern p("a.#.b");
- BOOST_CHECK(p.match("a.b"));
- BOOST_CHECK(p.match("a.x.b"));
- BOOST_CHECK(p.match("a..x.y.zz.b"));
- BOOST_CHECK(!p.match("a.b."));
- BOOST_CHECK(!p.match("q.x.b"));
-
- p = "a.#";
- BOOST_CHECK(p.match("a"));
- BOOST_CHECK(p.match("a.b"));
- BOOST_CHECK(p.match("a.b.c"));
-
- p = "#.a";
- BOOST_CHECK(p.match("a"));
- BOOST_CHECK(p.match("x.y.a"));
+ string pattern("a.*.b");
+ BOOST_CHECK(TopicExchange::match(pattern, "a.xx.b"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "a.b"));
+
+ pattern = "*.x";
+ BOOST_CHECK(TopicExchange::match(pattern, "y.x"));
+ BOOST_CHECK(TopicExchange::match(pattern, ".x"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "x"));
+
+ pattern = "x.x.*";
+ BOOST_CHECK(TopicExchange::match(pattern, "x.x.y"));
+ BOOST_CHECK(TopicExchange::match(pattern, "x.x."));
+ BOOST_CHECK(!TopicExchange::match(pattern, "x.x"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "q.x.y"));
}
-QPID_AUTO_TEST_CASE(testMixed)
+QPID_AUTO_TEST_CASE(testHash)
{
- TopicPattern p("*.x.#.y");
- BOOST_CHECK(p.match("a.x.y"));
- BOOST_CHECK(p.match("a.x.p.qq.y"));
- BOOST_CHECK(!p.match("a.a.x.y"));
- BOOST_CHECK(!p.match("aa.x.b.c"));
-
- p = "a.#.b.*";
- BOOST_CHECK(p.match("a.b.x"));
- BOOST_CHECK(p.match("a.x.x.x.b.x"));
+ string pattern("a.#.b");
+ BOOST_CHECK(TopicExchange::match(pattern, "a.b"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a.x.b"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a..x.y.zz.b"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "a.b."));
+ BOOST_CHECK(!TopicExchange::match(pattern, "q.x.b"));
+
+ pattern = "a.#";
+ BOOST_CHECK(TopicExchange::match(pattern, "a"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a.b"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a.b.c"));
+
+ pattern = "#.a";
+ BOOST_CHECK(TopicExchange::match(pattern, "a"));
+ BOOST_CHECK(TopicExchange::match(pattern, "x.y.a"));
+
+ pattern = "a.#.b.#.c";
+ BOOST_CHECK(TopicExchange::match(pattern, "a.b.c"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a.x.b.y.c"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a.x.x.b.y.y.c"));
}
-QPID_AUTO_TEST_CASE(testCombo)
+QPID_AUTO_TEST_CASE(testMixed)
{
- TopicPattern p("*.#.#.*.*.#");
- BOOST_CHECK(p.match("x.y.z"));
- BOOST_CHECK(p.match("x.y.z.a.b.c"));
- BOOST_CHECK(!p.match("x.y"));
- BOOST_CHECK(!p.match("x"));
+ string pattern("*.x.#.y");
+ BOOST_CHECK(TopicExchange::match(pattern, "a.x.y"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a.x.p.qq.y"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "a.a.x.y"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "aa.x.b.c"));
+
+ pattern = "a.#.b.*";
+ BOOST_CHECK(TopicExchange::match(pattern, "a.b.x"));
+ BOOST_CHECK(TopicExchange::match(pattern, "a.x.x.x.b.x"));
+
+ pattern = "*.*.*.#";
+ BOOST_CHECK(TopicExchange::match(pattern, "x.y.z"));
+ BOOST_CHECK(TopicExchange::match(pattern, "x.y.z.a.b.c"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "x.y"));
+ BOOST_CHECK(!TopicExchange::match(pattern, "x"));
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/TxBufferTest.cpp b/cpp/src/tests/TxBufferTest.cpp
index 3d6a12cacc..4807026ab7 100644
--- a/cpp/src/tests/TxBufferTest.cpp
+++ b/cpp/src/tests/TxBufferTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -27,6 +27,9 @@
using namespace qpid::broker;
using boost::static_pointer_cast;
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(TxBufferTestSuite)
QPID_AUTO_TEST_CASE(testCommitLocal)
@@ -174,3 +177,5 @@ QPID_AUTO_TEST_CASE(testBufferIsClearedAfterCommit)
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/TxMocks.h b/cpp/src/tests/TxMocks.h
index 86864b987e..a34d864bae 100644
--- a/cpp/src/tests/TxMocks.h
+++ b/cpp/src/tests/TxMocks.h
@@ -7,9 +7,9 @@
* 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
@@ -32,6 +32,9 @@ using namespace qpid::broker;
using boost::static_pointer_cast;
using std::string;
+namespace qpid {
+namespace tests {
+
template <class T> void assertEqualVector(std::vector<T>& expected, std::vector<T>& actual){
unsigned int i = 0;
while(i < expected.size() && i < actual.size()){
@@ -62,15 +65,15 @@ class MockTxOp : public TxOp, public TxOpConstants{
string debugName;
public:
typedef boost::shared_ptr<MockTxOp> shared_ptr;
-
+
MockTxOp() : failOnPrepare(false) {}
MockTxOp(bool _failOnPrepare) : failOnPrepare(_failOnPrepare) {}
-
+
void setDebugName(string name){
debugName = name;
}
- void printExpected(){
+ void printExpected(){
std::cout << std::endl << "MockTxOp[" << debugName << "] expects: ";
for (std::vector<string>::iterator i = expected.begin(); i < expected.end(); i++) {
if(i != expected.begin()) std::cout << ", ";
@@ -79,7 +82,7 @@ public:
std::cout << std::endl;
}
- void printActual(){
+ void printActual(){
std::cout << std::endl << "MockTxOp[" << debugName << "] actual: ";
for (std::vector<string>::iterator i = actual.begin(); i < actual.end(); i++) {
if(i != actual.begin()) std::cout << ", ";
@@ -87,7 +90,7 @@ public:
}
std::cout << std::endl;
}
-
+
bool prepare(TransactionContext*) throw(){
actual.push_back(PREPARE);
return !failOnPrepare;
@@ -114,7 +117,10 @@ public:
void check(){
assertEqualVector(expected, actual);
}
- ~MockTxOp(){}
+
+ void accept(TxOpConstVisitor&) const {}
+
+ ~MockTxOp(){}
};
class MockTransactionalStore : public TransactionalStore{
@@ -125,10 +131,10 @@ class MockTransactionalStore : public TransactionalStore{
const string ABORT;
std::vector<string> expected;
std::vector<string> actual;
-
+
enum states {OPEN = 1, PREPARED = 2, COMMITTED = 3, ABORTED = 4};
int state;
-
+
class TestTransactionContext : public TPCTransactionContext{
MockTransactionalStore* store;
public:
@@ -142,29 +148,29 @@ class MockTransactionalStore : public TransactionalStore{
if(!store->isOpen() && !store->isPrepared()) throw "txn already completed";
store->state = COMMITTED;
}
-
+
void abort(){
if(!store->isOpen() && !store->isPrepared()) throw "txn already completed";
store->state = ABORTED;
}
~TestTransactionContext(){}
};
-
+
public:
MockTransactionalStore() :
BEGIN("BEGIN"), BEGIN2PC("BEGIN2PC"), PREPARE("PREPARE"), COMMIT("COMMIT"), ABORT("ABORT"), state(OPEN){}
void collectPreparedXids(std::set<std::string>&)
{
- throw "Operation not supported";
+ throw "Operation not supported";
}
-
- std::auto_ptr<TPCTransactionContext> begin(const std::string&){
+
+ std::auto_ptr<TPCTransactionContext> begin(const std::string&){
actual.push_back(BEGIN2PC);
std::auto_ptr<TPCTransactionContext> txn(new TestTransactionContext(this));
return txn;
}
- std::auto_ptr<TransactionContext> begin(){
+ std::auto_ptr<TransactionContext> begin(){
actual.push_back(BEGIN);
std::auto_ptr<TransactionContext> txn(new TestTransactionContext(this));
return txn;
@@ -180,7 +186,7 @@ public:
void abort(TransactionContext& ctxt){
actual.push_back(ABORT);
dynamic_cast<TestTransactionContext&>(ctxt).abort();
- }
+ }
MockTransactionalStore& expectBegin(){
expected.push_back(BEGIN);
return *this;
@@ -204,23 +210,25 @@ public:
void check(){
assertEqualVector(expected, actual);
}
-
+
bool isPrepared(){
return state == PREPARED;
}
-
+
bool isCommitted(){
return state == COMMITTED;
}
-
+
bool isAborted(){
return state == ABORTED;
}
-
+
bool isOpen() const{
return state == OPEN;
}
~MockTransactionalStore(){}
};
+}} // namespace qpid::tests
+
#endif
diff --git a/cpp/src/tests/TxPublishTest.cpp b/cpp/src/tests/TxPublishTest.cpp
index 9e9715c987..6b44d95baa 100644
--- a/cpp/src/tests/TxPublishTest.cpp
+++ b/cpp/src/tests/TxPublishTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -35,35 +35,38 @@ using boost::intrusive_ptr;
using namespace qpid::broker;
using namespace qpid::framing;
+namespace qpid {
+namespace tests {
+
struct TxPublishTest
{
-
+
TestMessageStore store;
Queue::shared_ptr queue1;
Queue::shared_ptr queue2;
intrusive_ptr<Message> msg;
TxPublish op;
-
+
TxPublishTest() :
- queue1(new Queue("queue1", false, &store, 0)),
- queue2(new Queue("queue2", false, &store, 0)),
- msg(MessageUtils::createMessage("exchange", "routing_key", "id")),
+ queue1(new Queue("queue1", false, &store, 0)),
+ queue2(new Queue("queue2", false, &store, 0)),
+ msg(MessageUtils::createMessage("exchange", "routing_key", false, "id")),
op(msg)
{
msg->getProperties<DeliveryProperties>()->setDeliveryMode(PERSISTENT);
op.deliverTo(queue1);
op.deliverTo(queue2);
- }
+ }
};
QPID_AUTO_TEST_SUITE(TxPublishTestSuite)
-
+
QPID_AUTO_TEST_CASE(testPrepare)
{
TxPublishTest t;
- intrusive_ptr<PersistableMessage> pmsg = static_pointer_cast<PersistableMessage>(t.msg);
+ intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(t.msg);
//ensure messages are enqueued in store
t.op.prepare(0);
BOOST_CHECK_EQUAL((size_t) 2, t.store.enqueued.size());
@@ -71,7 +74,7 @@ QPID_AUTO_TEST_CASE(testPrepare)
BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[0].second);
BOOST_CHECK_EQUAL(string("queue2"), t.store.enqueued[1].first);
BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[1].second);
- BOOST_CHECK_EQUAL( true, ( static_pointer_cast<PersistableMessage>(t.msg))->isEnqueueComplete());
+ BOOST_CHECK_EQUAL( true, ( boost::static_pointer_cast<PersistableMessage>(t.msg))->isEnqueueComplete());
}
QPID_AUTO_TEST_CASE(testCommit)
@@ -84,11 +87,13 @@ QPID_AUTO_TEST_CASE(testCommit)
BOOST_CHECK_EQUAL((uint32_t) 1, t.queue1->getMessageCount());
intrusive_ptr<Message> msg_dequeue = t.queue1->get().payload;
- BOOST_CHECK_EQUAL( true, (static_pointer_cast<PersistableMessage>(msg_dequeue))->isEnqueueComplete());
+ BOOST_CHECK_EQUAL( true, (boost::static_pointer_cast<PersistableMessage>(msg_dequeue))->isEnqueueComplete());
BOOST_CHECK_EQUAL(t.msg, msg_dequeue);
BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount());
- BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload);
+ BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload);
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/Url.cpp b/cpp/src/tests/Url.cpp
index d2892d92bf..343186eb1f 100644
--- a/cpp/src/tests/Url.cpp
+++ b/cpp/src/tests/Url.cpp
@@ -26,39 +26,47 @@ using namespace std;
using namespace qpid;
using namespace boost::assign;
-QPID_AUTO_TEST_SUITE(UrlTestSuite)
+namespace qpid {
+namespace tests {
-QPID_AUTO_TEST_CASE(testUrl_str) {
- Url url;
- url.push_back(TcpAddress("foo.com"));
- url.push_back(TcpAddress("bar.com", 6789));
- BOOST_CHECK_EQUAL("amqp:tcp:foo.com:5672,tcp:bar.com:6789", url.str());
- BOOST_CHECK(Url().str().empty());
-}
+QPID_AUTO_TEST_SUITE(UrlTestSuite)
+#define URL_CHECK_STR(STR) BOOST_CHECK_EQUAL(Url(STR).str(), STR)
+#define URL_CHECK_INVALID(STR) BOOST_CHECK_THROW(Url(STR), Url::Invalid)
-QPID_AUTO_TEST_CASE(testUrl_parse) {
- Url url;
- url.parse("amqp:foo.com,tcp:bar.com:1234");
- BOOST_CHECK_EQUAL(2u, url.size());
- BOOST_CHECK_EQUAL("foo.com", boost::get<TcpAddress>(url[0]).host);
- BOOST_CHECK_EQUAL("amqp:tcp:foo.com:5672,tcp:bar.com:1234", url.str());
+QPID_AUTO_TEST_CASE(TestParseTcp) {
+ URL_CHECK_STR("amqp:tcp:host:42");
+ URL_CHECK_STR("amqp:tcp:host-._~%ff%23:42"); // unreserved chars and pct encoded hex.
- url.parse("amqp:foo/ignorethis");
- BOOST_CHECK_EQUAL("amqp:tcp:foo:5672", url.str());
+ // Check defaults
+ BOOST_CHECK_EQUAL(Url("amqp:host:42").str(), "amqp:tcp:host:42");
+ BOOST_CHECK_EQUAL(Url("amqp:tcp:host").str(), "amqp:tcp:host:5672");
+ BOOST_CHECK_EQUAL(Url("amqp:tcp:").str(), "amqp:tcp:127.0.0.1:5672");
+ BOOST_CHECK_EQUAL(Url("amqp:").str(), "amqp:tcp:127.0.0.1:5672");
+ BOOST_CHECK_EQUAL(Url("amqp::42").str(), "amqp:tcp:127.0.0.1:42");
- url.parse("amqp:");
- BOOST_CHECK_EQUAL("amqp:tcp::5672", url.str());
+ URL_CHECK_INVALID("amqp::badHost!#$#");
+ URL_CHECK_INVALID("amqp::host:badPort");
+}
- try {
- url.parse("invalid url");
- BOOST_FAIL("Expected InvalidUrl exception");
- }
- catch (const Url::InvalidUrl&) {}
+QPID_AUTO_TEST_CASE(TestParseExample) {
+ URL_CHECK_STR("amqp:example:x");
+ URL_CHECK_INVALID("amqp:example:badExample");
+}
- url.parseNoThrow("invalid url");
- BOOST_CHECK(url.empty());
+QPID_AUTO_TEST_CASE(TestParseMultiAddress) {
+ URL_CHECK_STR("amqp:tcp:host:0,example:y,tcp:foo:0,example:1");
+ URL_CHECK_STR("amqp:example:z,tcp:foo:0");
+ URL_CHECK_INVALID("amqp:tcp:h:0,");
+ URL_CHECK_INVALID(",amqp:tcp:h");
}
+QPID_AUTO_TEST_CASE(TestInvalidAddress) {
+ URL_CHECK_INVALID("xxxx");
+ URL_CHECK_INVALID("");
+}
+
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/Uuid.cpp b/cpp/src/tests/Uuid.cpp
index ee86d75a26..a6ddb9b5a5 100644
--- a/cpp/src/tests/Uuid.cpp
+++ b/cpp/src/tests/Uuid.cpp
@@ -18,11 +18,14 @@
#include "qpid/framing/Uuid.h"
#include "qpid/framing/Buffer.h"
+#include "qpid/sys/alloca.h"
#include "unit_test.h"
#include <set>
-#include <alloca.h>
+
+namespace qpid {
+namespace tests {
QPID_AUTO_TEST_SUITE(UuidTestSuite)
@@ -77,3 +80,5 @@ QPID_AUTO_TEST_CASE(testUuidEncodeDecode) {
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/Variant.cpp b/cpp/src/tests/Variant.cpp
new file mode 100644
index 0000000000..21005779f0
--- /dev/null
+++ b/cpp/src/tests/Variant.cpp
@@ -0,0 +1,178 @@
+/*
+ *
+ * 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 <iostream>
+#include "qpid/messaging/Variant.h"
+
+#include "unit_test.h"
+
+using namespace qpid::messaging;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(VariantSuite)
+
+QPID_AUTO_TEST_CASE(testConversions)
+{
+ Variant value;
+
+ //string to float/double
+ value = "1.5";
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+
+ //float to string or double
+ value = 1.5f;
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //double to string (conversion to float not valid)
+ value = 1.5;
+ BOOST_CHECK_THROW(value.asFloat(), InvalidConversion);
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //uint8 to larger unsigned ints and string
+ value = (uint8_t) 7;
+ BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("7"), value.asString());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+
+ value = (uint16_t) 8;
+ BOOST_CHECK_EQUAL(std::string("8"), value.asString());
+ value = (uint32_t) 9;
+ BOOST_CHECK_EQUAL(std::string("9"), value.asString());
+
+ //uint32 to larger unsigned ints and string
+ value = (uint32_t) 9999999;
+ BOOST_CHECK_EQUAL((uint32_t) 9999999, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 9999999, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("9999999"), value.asString());
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "true";
+ BOOST_CHECK(value.asBool());
+ value = "false";
+ BOOST_CHECK(!value.asBool());
+ value = "1";
+ BOOST_CHECK(value.asBool());
+ value = "0";
+ BOOST_CHECK(!value.asBool());
+ value = "other";
+ BOOST_CHECK_THROW(value.asBool(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testAssignment)
+{
+ Variant value("abc");
+ Variant other = value;
+ BOOST_CHECK_EQUAL(VAR_STRING, value.getType());
+ BOOST_CHECK_EQUAL(other.getType(), value.getType());
+ BOOST_CHECK_EQUAL(other.asString(), value.asString());
+
+ const uint32_t i(1000);
+ value = i;
+ BOOST_CHECK_EQUAL(VAR_UINT32, value.getType());
+ BOOST_CHECK_EQUAL(VAR_STRING, other.getType());
+}
+
+QPID_AUTO_TEST_CASE(testList)
+{
+ const std::string s("abc");
+ const float f(9.876f);
+ const int16_t x(1000);
+
+ Variant value = Variant::List();
+ value.asList().push_back(Variant(s));
+ value.asList().push_back(Variant(f));
+ value.asList().push_back(Variant(x));
+ BOOST_CHECK_EQUAL(3u, value.asList().size());
+ Variant::List::const_iterator i = value.asList().begin();
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_STRING, i->getType());
+ BOOST_CHECK_EQUAL(s, i->asString());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_FLOAT, i->getType());
+ BOOST_CHECK_EQUAL(f, i->asFloat());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_INT16, i->getType());
+ BOOST_CHECK_EQUAL(x, i->asInt16());
+ i++;
+
+ BOOST_CHECK(i == value.asList().end());
+}
+
+QPID_AUTO_TEST_CASE(testMap)
+{
+ const std::string red("red");
+ const float pi(3.14f);
+ const int16_t x(1000);
+
+ Variant value = Variant::Map();
+ value.asMap()["colour"] = red;
+ value.asMap()["pi"] = pi;
+ value.asMap()["my-key"] = x;
+ BOOST_CHECK_EQUAL(3u, value.asMap().size());
+
+ BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["colour"].getType());
+ BOOST_CHECK_EQUAL(red, value.asMap()["colour"].asString());
+
+ BOOST_CHECK_EQUAL(VAR_FLOAT, value.asMap()["pi"].getType());
+ BOOST_CHECK_EQUAL(pi, value.asMap()["pi"].asFloat());
+
+ BOOST_CHECK_EQUAL(VAR_INT16, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(x, value.asMap()["my-key"].asInt16());
+
+ value.asMap()["my-key"] = "now it's a string";
+ BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString());
+}
+
+QPID_AUTO_TEST_CASE(testIsEqualTo)
+{
+ BOOST_CHECK_EQUAL(Variant("abc"), Variant("abc"));
+ BOOST_CHECK_EQUAL(Variant(1234), Variant(1234));
+
+ Variant a = Variant::Map();
+ a.asMap()["colour"] = "red";
+ a.asMap()["pi"] = 3.14f;
+ a.asMap()["my-key"] = 1234;
+ Variant b = Variant::Map();
+ b.asMap()["colour"] = "red";
+ b.asMap()["pi"] = 3.14f;
+ b.asMap()["my-key"] = 1234;
+ BOOST_CHECK_EQUAL(a, b);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/XmlClientSessionTest.cpp b/cpp/src/tests/XmlClientSessionTest.cpp
index d0a1520c81..b59abbf2cc 100644
--- a/cpp/src/tests/XmlClientSessionTest.cpp
+++ b/cpp/src/tests/XmlClientSessionTest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -18,15 +18,18 @@
* under the License.
*
*/
+
#include "unit_test.h"
+#include "test_tools.h"
#include "BrokerFixture.h"
+#include "qpid/sys/Shlib.h"
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Thread.h"
#include "qpid/sys/Runnable.h"
-#include "qpid/framing/TransferContent.h"
+#include "qpid/client/Message.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/client/Connection.h"
-#include "qpid/client/Dispatcher.h"
+#include "qpid/client/SubscriptionManager.h"
#include "qpid/client/LocalQueue.h"
#include "qpid/client/Session.h"
#include "qpid/client/SubscriptionManager.h"
@@ -36,6 +39,9 @@
#include <vector>
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(XmlClientSessionTest)
using namespace qpid::client;
@@ -43,35 +49,14 @@ using namespace qpid::client;
using namespace qpid::client::arg;
using namespace qpid::framing;
using namespace qpid;
+using qpid::sys::Shlib;
using qpid::sys::Monitor;
using std::string;
using std::cout;
using std::endl;
-struct DummyListener : public sys::Runnable, public MessageListener {
- std::vector<Message> messages;
- string name;
- uint expected;
- Dispatcher dispatcher;
-
- DummyListener(Session& session, const string& n, uint ex) :
- name(n), expected(ex), dispatcher(session) {}
-
- void run()
- {
- dispatcher.listen(name, this);
- dispatcher.run();
- }
-
- void received(Message& msg)
- {
- messages.push_back(msg);
- if (--expected == 0)
- dispatcher.stop();
- }
-};
-
+Shlib shlib(getLibPath("XML_LIB"));
class SubscribedLocalQueue : public LocalQueue {
private:
@@ -118,39 +103,124 @@ struct ClientSessionFixture : public ProxySessionFixture
// ########### START HERE ####################################
+
+
QPID_AUTO_TEST_CASE(testXmlBinding) {
- ClientSessionFixture f;
+ ClientSessionFixture f;
+
+ SubscriptionManager subscriptions(f.session);
+ SubscribedLocalQueue localQueue(subscriptions);
+
+ f.session.exchangeDeclare(qpid::client::arg::exchange="xml", qpid::client::arg::type="xml");
+ f.session.queueDeclare(qpid::client::arg::queue="odd_blue");
+ subscriptions.subscribe(localQueue, "odd_blue");
+
+ FieldTable binding;
+ binding.setString("xquery", "declare variable $color external;"
+ "(./message/id mod 2 = 1) and ($color = 'blue')");
+ f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding);
+
+ Message message;
+ message.getDeliveryProperties().setRoutingKey("query_name");
- SubscriptionManager subscriptions(f.session);
- SubscribedLocalQueue localQueue(subscriptions);
+ message.getHeaders().setString("color", "blue");
+ string m = "<message><id>1</id></message>";
+ message.setData(m);
- f.session.exchangeDeclare(qpid::client::arg::exchange="xml", qpid::client::arg::type="xml");
- f.session.queueDeclare(qpid::client::arg::queue="odd_blue");
- subscriptions.subscribe(localQueue, "odd_blue");
+ f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml");
- FieldTable binding;
- binding.setString("xquery", "declare variable $color external;"
- "(./message/id mod 2 = 1) and ($color = 'blue')");
- f.session.exchangeBind(qpid::client::arg::exchange="xml", qpid::client::arg::queue="odd_blue", qpid::client::arg::bindingKey="query_name", qpid::client::arg::arguments=binding);
+ Message m2 = localQueue.get();
+ BOOST_CHECK_EQUAL(m, m2.getData());
+}
+
+/**
+ * Ensure that multiple queues can be bound using the same routing key
+ */
+QPID_AUTO_TEST_CASE(testXMLBindMultipleQueues) {
+ ClientSessionFixture f;
+
+
+ f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml");
+ f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true);
+ f.session.queueDeclare(arg::queue="red", arg::exclusive=true, arg::autoDelete=true);
+
+ FieldTable blue;
+ blue.setString("xquery", "./colour = 'blue'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-colour", arg::arguments=blue);
+ FieldTable red;
+ red.setString("xquery", "./colour = 'red'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-colour", arg::arguments=red);
+
+ Message sent1("<colour>blue</colour>", "by-colour");
+ f.session.messageTransfer(arg::content=sent1, arg::destination="xml");
+
+ Message sent2("<colour>red</colour>", "by-colour");
+ f.session.messageTransfer(arg::content=sent2, arg::destination="xml");
+
+ Message received;
+ BOOST_CHECK(f.subs.get(received, "blue"));
+ BOOST_CHECK_EQUAL(sent1.getData(), received.getData());
+ BOOST_CHECK(f.subs.get(received, "red"));
+ BOOST_CHECK_EQUAL(sent2.getData(), received.getData());
+}
+
+//### Test: Bad XML does not kill the server - and does not even
+// raise an exception, the content is not required to be XML.
+
+QPID_AUTO_TEST_CASE(testXMLSendBadXML) {
+ ClientSessionFixture f;
- Message message;
- message.getDeliveryProperties().setRoutingKey("query_name");
+ f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml");
+ f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true)\
+ ;
+ f.session.queueDeclare(arg::queue="red", arg::exclusive=true, arg::autoDelete=true);
- message.getHeaders().setString("color", "blue");
- string m = "<message><id>1</id></message>";
- message.setData(m);
+ FieldTable blue;
+ blue.setString("xquery", "./colour = 'blue'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-c\
+olour", arg::arguments=blue);
+ FieldTable red;
+ red.setString("xquery", "./colour = 'red'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="red", arg::bindingKey="by-co\
+lour", arg::arguments=red);
- f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml");
+ Message sent1("<>colour>blue</colour>", "by-colour");
+ f.session.messageTransfer(arg::content=sent1, arg::destination="xml");
- Message m2 = localQueue.get();
- BOOST_CHECK_EQUAL(m, m2.getData());
+ BOOST_CHECK_EQUAL(1, 1);
}
-//### Test: Bad XML does not kill the server
-//### Test: Bad XQuery does not kill the server
+//### Test: Bad XQuery does not kill the server, but does raise an exception
+
+QPID_AUTO_TEST_CASE(testXMLBadXQuery) {
+ ClientSessionFixture f;
+
+ f.session.exchangeDeclare(arg::exchange="xml", arg::type="xml");
+ f.session.queueDeclare(arg::queue="blue", arg::exclusive=true, arg::autoDelete=true)\
+ ;
+
+ try {
+ ScopedSuppressLogging sl; // Supress logging of error messages for expected error.
+ FieldTable blue;
+ blue.setString("xquery", "./colour $=! 'blue'");
+ f.session.exchangeBind(arg::exchange="xml", arg::queue="blue", arg::bindingKey="by-c\
+olour", arg::arguments=blue);
+ }
+ catch (const InternalErrorException& e) {
+ return;
+ }
+ BOOST_ERROR("A bad XQuery must raise an exception when used in an XML Binding.");
+
+}
+
+
+//### Test: Each session can provide its own definition for a query name
+
+
//### Test: Bindings persist, surviving broker restart
QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/acl.py b/cpp/src/tests/acl.py
new file mode 100755
index 0000000000..b27f2f1916
--- /dev/null
+++ b/cpp/src/tests/acl.py
@@ -0,0 +1,884 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import sys
+import qpid
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import uuid4
+from qpid.testlib import TestBase010
+from qmf.console import Session
+from qpid.datatypes import Message
+
+class ACLFile:
+ def __init__(self):
+ self.f = open('data_dir/policy.acl','w');
+
+ def write(self,line):
+ self.f.write(line)
+
+ def close(self):
+ self.f.close()
+
+class ACLTests(TestBase010):
+
+ def get_session(self, user, passwd):
+ socket = connect(self.broker.host, self.broker.port)
+ connection = Connection (sock=socket, username=user, password=passwd)
+ connection.start()
+ return connection.session(str(uuid4()))
+
+ def reload_acl(self):
+ acl = self.qmf.getObjects(_class="acl")[0]
+ return acl.reloadACLFile()
+
+ def setUp(self):
+ aclf = ACLFile()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ TestBase010.setUp(self)
+ self.startQmf()
+ self.reload_acl()
+
+ #=====================================
+ # ACL general tests
+ #=====================================
+
+ def test_deny_mode(self):
+ """
+ Test the deny all mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl allow bob@QPID create queue\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ try:
+ session.queue_declare(queue="deny_queue")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request");
+ self.fail("Error during queue create request");
+
+ try:
+ session.exchange_bind(exchange="amq.direct", queue="deny_queue", binding_key="routing_key")
+ self.fail("ACL should deny queue bind request");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+
+ def test_allow_mode(self):
+ """
+ Test the allow all mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl deny bob@QPID bind exchange\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ try:
+ session.queue_declare(queue="allow_queue")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request");
+ self.fail("Error during queue create request");
+
+ try:
+ session.exchange_bind(exchange="amq.direct", queue="allow_queue", binding_key="routing_key")
+ self.fail("ACL should deny queue bind request");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+
+
+ def test_group_and_user_with_same_name(self):
+ """
+ Test a group and user with same name
+ Ex. group admin admin
+ """
+ aclf = ACLFile()
+ aclf.write('group bob@QPID bob@QPID\n')
+ aclf.write('acl deny bob@QPID bind exchange\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ try:
+ session.queue_declare(queue="allow_queue")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request");
+ self.fail("Error during queue create request");
+
+ try:
+ session.exchange_bind(exchange="amq.direct", queue="allow_queue", binding_key="routing_key")
+ self.fail("ACL should deny queue bind request");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+
+
+ #=====================================
+ # ACL file format tests
+ #=====================================
+
+ def test_empty_groups(self):
+ """
+ Test empty groups
+ """
+ aclf = ACLFile()
+ aclf.write('acl group\n')
+ aclf.write('acl group admins bob@QPID joe@QPID\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("Insufficient tokens for acl definition",0,len(result.text)) == -1):
+ self.fail("ACL Reader should reject the acl file due to empty group name")
+
+ def test_illegal_acl_formats(self):
+ """
+ Test illegal acl formats
+ """
+ aclf = ACLFile()
+ aclf.write('acl group admins bob@QPID joe@QPID\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("Unknown ACL permission",0,len(result.text)) == -1):
+ self.fail(result)
+
+ def test_illegal_extension_lines(self):
+ """
+ Test illegal extension lines
+ """
+
+ aclf = ACLFile()
+ aclf.write('group admins bob@QPID \ ')
+ aclf.write(' \ \n')
+ aclf.write('joe@QPID \n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("contains illegal characters",0,len(result.text)) == -1):
+ self.fail(result)
+
+ def test_user_without_realm(self):
+ """
+ Test a user defined without a realm
+ Ex. group admin rajith
+ """
+ aclf = ACLFile()
+ aclf.write('group admin bob\n')
+ aclf.write('acl deny admin bind exchange\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("Username 'bob' must contain a realm",0,len(result.text)) == -1):
+ self.fail(result)
+
+
+ #=====================================
+ # ACL queue tests
+ #=====================================
+
+ def test_queue_allow_mode(self):
+ """
+ Test cases for queue acl in allow mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n')
+ aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
+ aclf.write('acl deny bob@QPID access queue name=q3\n')
+ aclf.write('acl deny bob@QPID purge queue name=q3\n')
+ aclf.write('acl deny bob@QPID delete queue name=q4\n')
+ aclf.write('acl deny bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q1", durable=True, passive=True)
+ self.fail("ACL should deny queue create request with name=q1 durable=true passive=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2 exclusive=true qpid.policy_type=ring");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring_strict"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2 exclusive=true qpid.policy_type=ring_strict");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", exclusive=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2, qpid.max_size=500 and qpid.max_count=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 100
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2, qpid.max_size=100 and qpid.max_count=200 ");
+ try:
+ session.queue_declare(queue="q3", exclusive=True)
+ session.queue_declare(queue="q4", durable=True)
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q3 and q4 with any parameter");
+
+ try:
+ session.queue_query(queue="q3")
+ self.fail("ACL should deny queue query request for q3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q3")
+ self.fail("ACL should deny queue purge request for q3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q4")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue purge request for q4");
+
+ try:
+ session.queue_delete(queue="q4")
+ self.fail("ACL should deny queue delete request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_delete(queue="q3")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue delete request for q3");
+
+
+ def test_queue_deny_mode(self):
+ """
+ Test cases for queue acl in deny mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n')
+ aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
+ aclf.write('acl allow bob@QPID access queue name=q3\n')
+ aclf.write('acl allow bob@QPID purge queue name=q3\n')
+ aclf.write('acl allow bob@QPID create queue name=q3\n')
+ aclf.write('acl allow bob@QPID create queue name=q4\n')
+ aclf.write('acl allow bob@QPID delete queue name=q4\n')
+ aclf.write('acl allow bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q1", durable=True, passive=True)
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q1 durable=true passive=true");
+
+ try:
+ session.queue_declare(queue="q1", durable=False, passive=False)
+ self.fail("ACL should deny queue create request with name=q1 durable=true passive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q2", exclusive=False)
+ self.fail("ACL should deny queue create request with name=q2 exclusive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2 maxqueuesize=500 maxqueuecount=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 100
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2 maxqueuesize=500 maxqueuecount=200");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q2 with exclusive=true policytype=ring");
+
+ try:
+ session.queue_declare(queue="q3")
+ session.queue_declare(queue="q4")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q3 and q4");
+
+ try:
+ session.queue_query(queue="q4")
+ self.fail("ACL should deny queue query request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q4")
+ self.fail("ACL should deny queue purge request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q3")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue purge request for q3");
+
+ try:
+ session.queue_query(queue="q3")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue query request for q3");
+
+ try:
+ session.queue_delete(queue="q3")
+ self.fail("ACL should deny queue delete request for q3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_delete(queue="q4")
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow queue delete request for q4");
+
+ #=====================================
+ # ACL exchange tests
+ #=====================================
+
+ def test_exchange_acl_allow_mode(self):
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue="baz")
+
+ """
+ Test cases for exchange acl in allow mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n')
+ aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n')
+ aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
+ aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID unbind exchange name=myEx queuename=q1 routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID delete exchange name=myEx\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.exchange_declare(exchange='myEx', type='direct')
+
+ try:
+ session.exchange_declare(exchange='testEx', durable=True, passive=True)
+ self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='testEx', type='direct', durable=True, passive=False)
+ except qpid.session.SessionException, e:
+ print e
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true and passive=true");
+
+ try:
+ session.exchange_declare(exchange='ex1', type='direct')
+ self.fail("ACL should deny exchange create request with name=ex1 type=direct");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myXml', type='direct')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for myXml with any parameter");
+
+ try:
+ session.exchange_query(name='myEx')
+ self.fail("ACL should deny exchange query request for myEx");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*')
+ self.fail("ACL should deny exchange bound request for myEx with queuename=q1 and routing_key='rk1.*' ");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='amq.topic')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange query request for exchange='amq.topic'");
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk2.*'");
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1')
+ self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q1', binding_key='x')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q1', binding_key='x'");
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q2', binding_key='rk1')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q2', binding_key='rk1'");
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q1', binding_key='rk1')
+ self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q1', binding_key='x')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q1', binding_key='x'");
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q2', binding_key='rk1')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q2', binding_key='rk1'");
+
+ try:
+ session.exchange_delete(exchange='myEx')
+ self.fail("ACL should deny exchange delete request for myEx");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_delete(exchange='myXml')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange delete request for myXml");
+
+
+ def test_exchange_acl_deny_mode(self):
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue='bar')
+
+ """
+ Test cases for exchange acl in deny mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n')
+ aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
+ aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
+ aclf.write('acl allow bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
+ aclf.write('acl allow bob@QPID delete exchange name=myEx\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=True, passive=False)
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for myEx with durable=true and passive=false");
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ self.fail("ACL should deny exchange create request with name=myEx durable=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='foo.bar')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'");
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='baz', binding_key='foo.bar')
+ self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='fooz.bar')
+ self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='foo.bar')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'");
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='baz', binding_key='foo.bar')
+ self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='fooz.bar')
+ self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='amq.topic')
+ self.fail("ACL should deny exchange query request for amq.topic");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*')
+ self.fail("ACL should deny exchange bound request for amq.topic with queuename=q1 and routing_key='rk2.*' ");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='myEx')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange query request for exchange='myEx'");
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk1.*'");
+
+ try:
+ session.exchange_delete(exchange='myXml')
+ self.fail("ACL should deny exchange delete request for myXml");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_delete(exchange='myEx')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow exchange delete request for myEx");
+
+ #=====================================
+ # ACL consume tests
+ #=====================================
+
+ def test_consume_allow_mode(self):
+ """
+ Test cases for consume in allow mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl deny bob@QPID consume queue name=q1\n')
+ aclf.write('acl deny bob@QPID consume queue name=q2\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ try:
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.queue_declare(queue='q3')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow create queue request");
+
+ try:
+ session.message_subscribe(queue='q1', destination='myq1')
+ self.fail("ACL should deny subscription for queue='q1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_subscribe(queue='q2', destination='myq1')
+ self.fail("ACL should deny subscription for queue='q2'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_subscribe(queue='q3', destination='myq1')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow subscription for q3");
+
+
+ def test_consume_deny_mode(self):
+ """
+ Test cases for consume in allow mode
+ """
+ aclf = ACLFile()
+ aclf.write('acl allow bob@QPID consume queue name=q1\n')
+ aclf.write('acl allow bob@QPID consume queue name=q2\n')
+ aclf.write('acl allow bob@QPID create queue\n')
+ aclf.write('acl allow anonymous all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ try:
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.queue_declare(queue='q3')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow create queue request");
+
+ try:
+ session.message_subscribe(queue='q1', destination='myq1')
+ session.message_subscribe(queue='q2', destination='myq2')
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow subscription for q1 and q2");
+
+ try:
+ session.message_subscribe(queue='q3', destination='myq3')
+ self.fail("ACL should deny subscription for queue='q3'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+
+ #=====================================
+ # ACL publish tests
+ #=====================================
+
+ def test_publish_acl_allow_mode(self):
+ """
+ Test various publish acl
+ """
+ aclf = ACLFile()
+ aclf.write('acl deny bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID publish exchange name=amq.topic\n')
+ aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ props = session.delivery_properties(routing_key="rk1")
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.topic", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.topic");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange myEx with routing key rk1");
+
+
+ props = session.delivery_properties(routing_key="rk2")
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk2");
+
+
+ def test_publish_acl_deny_mode(self):
+ """
+ Test various publish acl
+ """
+ aclf = ACLFile()
+ aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
+ aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n')
+ aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n')
+ aclf.write('acl allow bob@QPID create exchange\n')
+ aclf.write('acl allow anonymous all all \n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ props = session.delivery_properties(routing_key="rk2")
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk2");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.topic", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.topic with any routing key");
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange myEx with routing key=rk2");
+
+ props = session.delivery_properties(routing_key="rk1")
+
+ try:
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=myEx routingkey=rk1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(530,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (530 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk1");
diff --git a/cpp/src/tests/ais_check b/cpp/src/tests/ais_check
index d76841eb1d..92eaa9dd39 100755
--- a/cpp/src/tests/ais_check
+++ b/cpp/src/tests/ais_check
@@ -1,37 +1,34 @@
#!/bin/sh
-srcdir=`dirname $0`
-
-# Check AIS requirements tests if found.
-id -nG | grep '\<ais\>' >/dev/null || \
- NOGROUP="You are not a member of the ais group."
-ps -u root | grep aisexec >/dev/null || \
- NOAISEXEC="The aisexec daemon is not running as root"
-
-if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then
- cat <<EOF
-
- =========== WARNING: NOT RUNNING AIS TESTS ==============
-
- Tests that depend on the openais library (used for clustering)
- will not be run because:
+#
+# 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.
+#
- $NOGROUP
- $NOAISEXEC
+srcdir=`dirname $0`
- ==========================================================
-
-EOF
+# Check AIS requirements and run tests if found.
+ps -u root | grep 'aisexec\|corosync' >/dev/null || {
+ echo WARNING: Skipping cluster tests, the aisexec or corosync daemon is not running.
exit 0; # A warning, not a failure.
-fi
+}
-# Execute command with the ais group set.
+# Execute command with the ais group set if user is a member.
with_ais_group() {
- id -nG | grep '\<ais\>' >/dev/null || { echo "You are not a member of the ais group."; exit 1; }
- echo $* | newgrp ais
+ if id -nG | grep '\<ais\>' >/dev/null; then sg ais -c "$*"
+ else "$@"
+ fi
}
-
-# Run the tests
-srcdir=`dirname $0`
-with_ais_group $srcdir/run_test ./cluster_test || ERROR=1
-exit $ERROR
-
diff --git a/cpp/src/tests/allSegmentTypes.h b/cpp/src/tests/allSegmentTypes.h
deleted file mode 100644
index e942250c89..0000000000
--- a/cpp/src/tests/allSegmentTypes.h
+++ /dev/null
@@ -1,128 +0,0 @@
-#ifndef TESTS_ALLSEGMENTTYPES_H
-#define TESTS_ALLSEGMENTTYPES_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.
- *
- */
-
-///
-/// This file was automatically generated from the AMQP specification.
-/// Do not edit.
-///
-
-
-#include "qpid/amqp_0_10/specification.h"
-#include "qpid/amqp_0_10/Header.h"
-#include "qpid/amqp_0_10/Body.h"
-
-using namespace qpid::amqp_0_10;
-
-template <class Op> size_t allSegmentTypes(Op& op) {
- op(Header());
- op(Body());
- op(ControlHolder(connection::Start()));
- op(ControlHolder(connection::StartOk()));
- op(ControlHolder(connection::Secure()));
- op(ControlHolder(connection::SecureOk()));
- op(ControlHolder(connection::Tune()));
- op(ControlHolder(connection::TuneOk()));
- op(ControlHolder(connection::Open()));
- op(ControlHolder(connection::OpenOk()));
- // op(ControlHolder(connection::Redirect())); // known-hosts array
- op(ControlHolder(connection::Heartbeat()));
- // op(ControlHolder(connection::Close())); // class/method dropped
- op(ControlHolder(connection::CloseOk()));
- op(ControlHolder(session::Attach()));
- op(ControlHolder(session::Attached()));
- op(ControlHolder(session::Detach()));
- op(ControlHolder(session::Detached()));
- op(ControlHolder(session::RequestTimeout()));
- op(ControlHolder(session::Timeout()));
- op(ControlHolder(session::CommandPoint()));
- // op(ControlHolder(session::Expected())); // fragments array encoding problem
- // op(ControlHolder(session::Confirmed())); // fragments array encoding problem
- op(ControlHolder(session::Completed()));
- op(ControlHolder(session::KnownCompleted()));
- op(ControlHolder(session::Flush()));
- op(ControlHolder(session::Gap()));
- // FIXME aconway 2008-04-15: command encoding, fix headers, fix sized structs.
- op(CommandHolder(execution::Sync()));
- op(CommandHolder(execution::Result()));
-
- // FIXME aconway 2008-04-16: investigate remaining failures.
- // op(CommandHolder(execution::Exception()));
- op(CommandHolder(message::Transfer()));
- op(CommandHolder(message::Accept()));
- // op(CommandHolder(message::Reject()));
- op(CommandHolder(message::Release()));
- op(CommandHolder(message::Acquire()));
- // op(CommandHolder(message::Resume()));
- op(CommandHolder(message::Subscribe()));
- op(CommandHolder(message::Cancel()));
- op(CommandHolder(message::SetFlowMode()));
- op(CommandHolder(message::Flow()));
- op(CommandHolder(message::Flush()));
- op(CommandHolder(message::Stop()));
- op(CommandHolder(tx::Select()));
- op(CommandHolder(tx::Commit()));
- op(CommandHolder(tx::Rollback()));
- op(CommandHolder(dtx::Select()));
- // op(CommandHolder(dtx::Start()));
- // op(CommandHolder(dtx::End()));
- // op(CommandHolder(dtx::Commit()));
- // op(CommandHolder(dtx::Forget()));
- // op(CommandHolder(dtx::GetTimeout()));
- // op(CommandHolder(dtx::Prepare()));
- // op(CommandHolder(dtx::Recover()));
- // op(CommandHolder(dtx::Rollback()));
- // op(CommandHolder(dtx::SetTimeout()));
- op(CommandHolder(exchange::Declare()));
- op(CommandHolder(exchange::Delete()));
- op(CommandHolder(exchange::Query()));
- op(CommandHolder(exchange::Bind()));
- op(CommandHolder(exchange::Unbind()));
- op(CommandHolder(exchange::Bound()));
- op(CommandHolder(queue::Declare()));
- op(CommandHolder(queue::Delete()));
- op(CommandHolder(queue::Purge()));
- op(CommandHolder(queue::Query()));
- // op(CommandHolder(file::Qos()));
- // op(CommandHolder(file::QosOk()));
-// op(CommandHolder(file::Consume()));
-// op(CommandHolder(file::ConsumeOk()));
-// op(CommandHolder(file::Cancel()));
-// op(CommandHolder(file::Open()));
-// op(CommandHolder(file::OpenOk()));
-// op(CommandHolder(file::Stage()));
-// op(CommandHolder(file::Publish()));
-// op(CommandHolder(file::Return()));
-// op(CommandHolder(file::Deliver()));
-// op(CommandHolder(file::Ack()));
-// op(CommandHolder(file::Reject()));
-// op(CommandHolder(stream::Qos()));
-// op(CommandHolder(stream::QosOk()));
-// op(CommandHolder(stream::Consume()));
-// op(CommandHolder(stream::ConsumeOk()));
-// op(CommandHolder(stream::Cancel()));
-// op(CommandHolder(stream::Publish()));
-// op(CommandHolder(stream::Return()));
-// op(CommandHolder(stream::Deliver()));
- return 0;
-}
-#endif /*!TESTS_ALLSEGMENTTYPES_H*/
diff --git a/cpp/src/tests/amqp_0_10/Map.cpp b/cpp/src/tests/amqp_0_10/Map.cpp
index efde967050..ffb235829e 100644
--- a/cpp/src/tests/amqp_0_10/Map.cpp
+++ b/cpp/src/tests/amqp_0_10/Map.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "unit_test.h"
+#include "amqp_0_10/unit_test.h"
#include "qpid/amqp_0_10/Map.h"
#include "qpid/amqp_0_10/Array.h"
#include "qpid/amqp_0_10/Struct32.h"
diff --git a/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp b/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp
index 9d4fcb8935..f54ee0da22 100644
--- a/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp
+++ b/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "unit_test.h"
+#include "amqp_0_10/unit_test.h"
#include "qpid/amqp_0_10/ProxyTemplate.h"
#include <boost/any.hpp>
diff --git a/cpp/src/tests/amqp_0_10/apply.cpp b/cpp/src/tests/amqp_0_10/apply.cpp
index 5a67c28c79..0aa4421791 100644
--- a/cpp/src/tests/amqp_0_10/apply.cpp
+++ b/cpp/src/tests/amqp_0_10/apply.cpp
@@ -18,7 +18,7 @@
* under the License.
*
*/
-#include "unit_test.h"
+#include "amqp_0_10/unit_test.h"
#include "qpid/amqp_0_10/specification.h"
#include "qpid/amqp_0_10/ApplyControl.h"
diff --git a/cpp/src/tests/amqp_0_10/handlers.cpp b/cpp/src/tests/amqp_0_10/handlers.cpp
index 428643c802..91bb304a17 100644
--- a/cpp/src/tests/amqp_0_10/handlers.cpp
+++ b/cpp/src/tests/amqp_0_10/handlers.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "unit_test.h"
+#include "amqp_0_10/unit_test.h"
#include "qpid/Exception.h"
#include "qpid/amqp_0_10/Unit.h"
#include "qpid/amqp_0_10/ControlHolder.h"
diff --git a/cpp/src/tests/amqp_0_10/serialize.cpp b/cpp/src/tests/amqp_0_10/serialize.cpp
index 0cfeb8d28d..975d6206ec 100644
--- a/cpp/src/tests/amqp_0_10/serialize.cpp
+++ b/cpp/src/tests/amqp_0_10/serialize.cpp
@@ -19,8 +19,8 @@
*
*/
-#include "unit_test.h"
-#include "allSegmentTypes.h"
+#include "amqp_0_10/unit_test.h"
+#include "amqp_0_10/allSegmentTypes.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/Buffer.h"
@@ -34,7 +34,7 @@
#include "qpid/amqp_0_10/FrameHeader.h"
#include "qpid/amqp_0_10/Map.h"
#include "qpid/amqp_0_10/Unit.h"
-#include "tests/allSegmentTypes.h"
+#include "allSegmentTypes.h"
#include <boost/test/test_case_template.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
diff --git a/cpp/src/tests/background.ps1 b/cpp/src/tests/background.ps1
new file mode 100644
index 0000000000..36e9e4e6e9
--- /dev/null
+++ b/cpp/src/tests/background.ps1
@@ -0,0 +1,55 @@
+#
+# 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.
+#
+
+# Run a PowerShell scriptblock in a background process.
+param(
+ [scriptblock] $script # scriptblock to run
+)
+
+# break out of the script on any errors
+trap { break }
+
+# In order to pass a scriptblock to another powershell instance, it must
+# be encoded to pass through the command line.
+$encodedScript = [convert]::ToBase64String(
+ [Text.Encoding]::Unicode.GetBytes([string] $script))
+
+#$p = new-object System.Diagnostics.Process
+$si = new-object System.Diagnostics.ProcessStartInfo
+$si.WorkingDirectory = $pwd
+$si.FileName = (get-command powershell.exe).Definition
+$si.Arguments = "-encodedCommand $encodedScript"
+
+###### debugging setup
+#$si.CreateNoWindow = $true
+# UseShellExecute false required for RedirectStandard(Error, Output)
+#$si.UseShellExecute = $false
+#$si.RedirectStandardError = $true
+#$si.RedirectStandardOutput = $true
+######
+$si.UseShellExecute = $true
+
+##### Debugging, instead of the plain Start() above.
+#$output = [io.File]::AppendText("start.out")
+#$error = [io.File]::AppendText("start.err")
+$p = [System.Diagnostics.Process]::Start($si)
+#$output.WriteLine($p.StandardOutput.ReadToEnd())
+#$error.WriteLine($p.StandardError.ReadToEnd())
+#$p.WaitForExit()
+#$output.Close()
diff --git a/cpp/src/tests/benchmark b/cpp/src/tests/benchmark
new file mode 100755
index 0000000000..c075837847
--- /dev/null
+++ b/cpp/src/tests/benchmark
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# 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.
+#
+# A basic "benchmark" to generate performacne samples of throughput
+# and latency against a single cluster member while they are replicating.
+#
+# Must be run in the qpid src/tests build directory.
+#
+
+usage() {
+cat <<EOF
+Usage: $0 [options] -- client hosts --- broker hosts
+Read the script for options.
+EOF
+}
+# Defaults
+TESTDIR=${TESTDIR:-$PWD} # Absolute path to test exes on all hosts.
+SCRIPTDIR=${SCRIPTDIR:-`dirname $0`} # Path to local test scripts directory.
+SAMPLES=10 # Runs of each test.
+COUNT=${COUNT:-10000} # Count for pub/sub tests.
+SIZE=${SIZE:-600} # Size of messages
+ECHO=${ECHO:-1000} # Count for echo test.
+NSUBS=${NSUBS:-4}
+NPUBS=${NPUBS:-4}
+
+collect() { eval $COLLECT=\""\$$COLLECT $*"\"; }
+COLLECT=ARGS
+while test $# -gt 0; do
+ case $1 in
+ --testdir) TESTDIR=$2 ; shift 2 ;;
+ --samples) SAMPLES=$2 ; shift 2 ;;
+ --count) COUNT=$2 ; shift 2 ;;
+ --echos) ECHO=$2 ; shift 2 ;;
+ --size) SIZE=$2 ; shift 2 ;;
+ --nsubs) NSUBS=$2 ; shift 2 ;;
+ --npubs) NPUBS=$2 ; shift 2 ;;
+ --) COLLECT=CLIENTARG; shift ;;
+ ---) COLLECT=BROKERARG; shift;;
+ *) collect $1; shift ;;
+ esac
+done
+
+CLIENTS=${CLIENTARG:-$CLIENTS}
+BROKERS=${BROKERARG:-$BROKERS}
+test -z "$CLIENTS" && { echo "Must specify at least one client host."; exit 1; }
+test -z "$BROKERS" && { echo "Must specify at least one broker host."; exit 1; }
+
+export TESTDIR # For perfdist
+CLIENTS=($CLIENTS) # Convert to array
+BROKERS=($BROKERS)
+trap "rm -f $FILES" EXIT
+
+dosamples() {
+ FILE=`mktemp`
+ FILES="$FILES $FILE"
+ TABS=`echo "$HEADING" | sed s'/[^ ]//g'`
+ {
+ echo "\"$*\"$TABS"
+ echo "$HEADING"
+ for (( i=0; i<$SAMPLES; ++i)) ; do echo "`$*`" ; done
+ echo
+ } | tee $FILE
+}
+
+HEADING="pub sub total Mb"
+dosamples $SCRIPTDIR/perfdist --size $SIZE --count $COUNT --nsubs $NSUBS --npubs $NPUBS -s -- ${CLIENTS[*]} --- ${BROKERS[*]}
+HEADING="pub"
+dosamples ssh -A ${CLIENTS[0]} $TESTDIR/publish --routing-key perftest0 --size $SIZE --count $COUNT -s -b ${BROKERS[0]}
+HEADING="sub"
+dosamples ssh -A ${CLIENTS[0]} $TESTDIR/consume --queue perftest0 -s --count $COUNT -b ${BROKERS[0]}
+HEADING="min max avg"
+dosamples ssh -A ${CLIENTS[0]} $TESTDIR/echotest --count $ECHO -s -b ${BROKERS[0]}
+
+echo
+echo "Tab separated spreadsheet (also saved as benchmark.tab):"
+echo
+
+echo "benchmark -- ${CLIENTS[*]} --- ${BROKERS[*]} " | tee benchmark.tab
+paste $FILES | tee -a benchmark.tab
diff --git a/cpp/src/tests/cli_tests.py b/cpp/src/tests/cli_tests.py
new file mode 100755
index 0000000000..a65097d431
--- /dev/null
+++ b/cpp/src/tests/cli_tests.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import sys
+import os
+from qpid.testlib import TestBase010
+from qpid.datatypes import Message
+from qpid.queue import Empty
+from time import sleep
+
+class CliTests(TestBase010):
+
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
+
+ def remote_port(self):
+ return int(self.defines["remote-port"])
+
+ def cli_dir(self):
+ return self.defines["cli-dir"]
+
+ def makeQueue(self, qname, arguments):
+ ret = os.system(self.command(" add queue " + qname + " " + arguments))
+ self.assertEqual(ret, 0)
+ queues = self.qmf.getObjects(_class="queue")
+ for queue in queues:
+ if queue.name == qname:
+ return queue
+ assert False
+
+ def test_queue_params(self):
+ self.startQmf()
+ queue1 = self.makeQueue("test_queue_params1", "--limit-policy none")
+ queue2 = self.makeQueue("test_queue_params2", "--limit-policy reject")
+ queue3 = self.makeQueue("test_queue_params3", "--limit-policy flow-to-disk")
+ queue4 = self.makeQueue("test_queue_params4", "--limit-policy ring")
+ queue5 = self.makeQueue("test_queue_params5", "--limit-policy ring-strict")
+
+ LIMIT = "qpid.policy_type"
+ assert LIMIT not in queue1.arguments
+ self.assertEqual(queue2.arguments[LIMIT], "reject")
+ self.assertEqual(queue3.arguments[LIMIT], "flow_to_disk")
+ self.assertEqual(queue4.arguments[LIMIT], "ring")
+ self.assertEqual(queue5.arguments[LIMIT], "ring_strict")
+
+ queue6 = self.makeQueue("test_queue_params6", "--order fifo")
+ queue7 = self.makeQueue("test_queue_params7", "--order lvq")
+ queue8 = self.makeQueue("test_queue_params8", "--order lvq-no-browse")
+
+ LVQ = "qpid.last_value_queue"
+ LVQNB = "qpid.last_value_queue_no_browse"
+
+ assert LVQ not in queue6.arguments
+ assert LVQ in queue7.arguments
+ assert LVQ not in queue8.arguments
+
+ assert LVQNB not in queue6.arguments
+ assert LVQNB not in queue7.arguments
+ assert LVQNB in queue8.arguments
+
+ def test_qpid_config(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config"
+
+ ret = os.system(self.command(" add queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = os.system(self.command(" del queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+ def test_qpid_config_durable(self):
+ self.startQmf();
+ qmf = self.qmf
+ qname = "test_qpid_config"
+
+ ret = os.system(self.command(" add queue --durable " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, True)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = os.system(self.command(" del queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+ def test_qpid_config_altex(self):
+ self.startQmf();
+ qmf = self.qmf
+ exName = "testalt"
+ qName = "testqalt"
+ altName = "amq.direct"
+
+ ret = os.system(self.command(" add exchange topic %s --alternate-exchange=%s" % (exName, altName)))
+ self.assertEqual(ret, 0)
+
+ exchanges = qmf.getObjects(_class="exchange")
+ found = False
+ for exchange in exchanges:
+ if exchange.name == altName:
+ self.assertEqual(exchange.altExchange, None)
+
+ if exchange.name == exName:
+ found = True
+ if not exchange.altExchange:
+ self.fail("Alternate exchange not set")
+ self.assertEqual(exchange._altExchange_.name, altName)
+ self.assertEqual(found, True)
+
+ ret = os.system(self.command(" add queue %s --alternate-exchange=%s" % (qName, altName)))
+ self.assertEqual(ret, 0)
+
+ queues = qmf.getObjects(_class="queue")
+ found = False
+ for queue in queues:
+ if queue.name == qName:
+ found = True
+ if not queue.altExchange:
+ self.fail("Alternate exchange not set")
+ self.assertEqual(queue._altExchange_.name, altName)
+ self.assertEqual(found, True)
+
+ def test_qpid_route(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ command = self.cli_dir() + "/qpid-route dynamic add guest/guest@localhost:%d %s:%d amq.topic" %\
+ (self.broker.port, self.remote_host(), self.remote_port())
+ ret = os.system(command)
+ self.assertEqual(ret, 0)
+
+ links = qmf.getObjects(_class="link")
+ found = False
+ for link in links:
+ if link.port == self.remote_port():
+ found = True
+ self.assertEqual(found, True)
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def getAppHeader(self, msg, name):
+ headers = self.getProperty(msg, "application_headers")
+ if headers:
+ return headers[name]
+ return None
+
+ def command(self, arg = ""):
+ return self.cli_dir() + "/qpid-config -a localhost:%d" % self.broker.port + " " + arg
diff --git a/cpp/src/tests/client_test.cpp b/cpp/src/tests/client_test.cpp
index 204c2c4b71..2f5e8e5afe 100644
--- a/cpp/src/tests/client_test.cpp
+++ b/cpp/src/tests/client_test.cpp
@@ -7,9 +7,9 @@
* 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
@@ -32,21 +32,24 @@
#include "qpid/client/Connection.h"
#include "qpid/client/Message.h"
#include "qpid/client/Session.h"
-#include "qpid/framing/FrameSet.h"
-#include "qpid/framing/all_method_bodies.h"
+#include "qpid/client/SubscriptionManager.h"
+
using namespace qpid;
using namespace qpid::client;
using namespace qpid::framing;
using std::string;
+namespace qpid {
+namespace tests {
+
struct Args : public TestOptions {
uint msgSize;
bool verbose;
Args() : TestOptions("Simple test of Qpid c++ client; sends and receives a single message."), msgSize(26)
{
- addOptions()
+ addOptions()
("size", optValue(msgSize, "N"), "message size")
("verbose", optValue(verbose), "print out some status messages");
}
@@ -58,7 +61,7 @@ std::string generateData(uint size)
{
if (size < chars.length()) {
return chars.substr(0, size);
- }
+ }
std::string data;
for (uint i = 0; i < (size / chars.length()); i++) {
data += chars;
@@ -78,6 +81,10 @@ void print(const std::string& text, const Message& msg)
std::cout << std::endl;
}
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char** argv)
{
try {
@@ -92,7 +99,7 @@ int main(int argc, char** argv)
//Create and open a session on the connection through which
//most functionality is exposed:
Session session = connection.newSession();
- if (opts.verbose) std::cout << "Opened session." << std::endl;
+ if (opts.verbose) std::cout << "Opened session." << std::endl;
//'declare' the exchange and the queue, which will create them
@@ -113,35 +120,18 @@ int main(int argc, char** argv)
session.messageTransfer(arg::destination="MyExchange", arg::content=msgOut, arg::acceptMode=1);
if (opts.verbose) print("Published message: ", msgOut);
- //subscribe to the queue, add sufficient credit and then get
- //incoming 'frameset', check that its a message transfer and
- //then convert it to a message (see Dispatcher and
- //SubscriptionManager utilties for common reusable patterns at
- //a higher level)
- session.messageSubscribe(arg::queue="MyQueue", arg::destination="MyId");
- session.messageFlow(arg::destination="MyId", arg::unit=0, arg::value=1); //credit for one message
- session.messageFlow(arg::destination="MyId", arg::unit=1, arg::value=0xFFFFFFFF); //credit for infinite bytes
- if (opts.verbose) std::cout << "Subscribed to queue." << std::endl;
- FrameSet::shared_ptr incoming = session.get();
- if (incoming->isA<MessageTransferBody>()) {
- Message msgIn(*incoming);
- if (msgIn.getData() == msgOut.getData()) {
- if (opts.verbose) std::cout << "Received the exepected message." << std::endl;
- session.messageAccept(SequenceSet(msgIn.getId()));
- session.markCompleted(msgIn.getId(), true, true);
- } else {
- print("Received an unexepected message: ", msgIn);
- }
- } else {
- throw Exception("Unexpected command received");
- }
-
+ // Using the SubscriptionManager, get the message from the queue.
+ SubscriptionManager subs(session);
+ Message msgIn = subs.get("MyQueue");
+ if (msgIn.getData() == msgOut.getData())
+ if (opts.verbose) std::cout << "Received the exepected message." << std::endl;
+
//close the session & connection
session.close();
if (opts.verbose) std::cout << "Closed session." << std::endl;
- connection.close();
+ connection.close();
if (opts.verbose) std::cout << "Closed connection." << std::endl;
- return 0;
+ return 0;
} catch(const std::exception& e) {
std::cout << e.what() << std::endl;
}
diff --git a/cpp/src/tests/cluster.cmake b/cpp/src/tests/cluster.cmake
new file mode 100644
index 0000000000..9084bfb85b
--- /dev/null
+++ b/cpp/src/tests/cluster.cmake
@@ -0,0 +1,86 @@
+#
+# 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.
+#
+
+#
+# Cluster tests cmake fragment, to be included in CMakeLists.txt
+#
+
+add_executable (failover_soak failover_soak.cpp ForkedBroker.cpp ${platform_test_additions})
+target_link_libraries (failover_soak qpidclient)
+remember_location(failover_soak)
+
+set (cluster_test_SOURCES
+ cluster_test
+ unit_test
+ ClusterFixture
+ ForkedBroker
+ PartialFailure
+ ClusterFailover
+ InitialStatusMap
+ StoreStatus
+ )
+add_executable (cluster_test ${cluster_test_SOURCES} ${platform_test_additions})
+target_link_libraries (cluster_test ${qpid_test_boost_libs} qpidclient qpidbroker cluster_shared)
+remember_location(cluster_test)
+
+add_test (cluster_test ${CMAKE_CURRENT_SOURCE_DIR}/run_cluster_test${test_script_suffix})
+add_test (cluster_tests ${CMAKE_CURRENT_SOURCE_DIR}/run_cluster_tests${test_script_suffix})
+add_test (cluster_read_credit ${CMAKE_CURRENT_SOURCE_DIR}/cluster_read_credit${test_script_suffix})
+add_test (cluster_test_watchdog ${CMAKE_CURRENT_SOURCE_DIR}/test_watchdog${test_script_suffix})
+add_test (federated_cluster_test ${CMAKE_CURRENT_SOURCE_DIR}/federated_cluster_test${test_script_suffix})
+add_test (clustered_replication_test ${CMAKE_CURRENT_SOURCE_DIR}/clustered_replication_test${test_script_suffix})
+
+# FIXME aconway 2009-12-01: translate to cmake
+# # Clean up after cluster_test and start_cluster
+# CLEANFILES += cluster_test.acl cluster.ports
+
+# EXTRA_DIST += \
+# ais_check \
+# run_cluster_test \
+# cluster_read_credit \
+# test_watchdog \
+# start_cluster \
+# stop_cluster \
+# restart_cluster \
+# cluster_python_tests \
+# cluster_python_tests_failing.txt \
+# federated_cluster_test \
+# clustered_replication_test \
+# run_cluster_tests \
+# run_long_cluster_tests \
+# testlib.py \
+# cluster_tests.py \
+# long_cluster_tests.py \
+# cluster_tests.fail
+
+# LONG_TESTS += \
+# run_long_cluster_tests \
+# start_cluster \
+# cluster_python_tests \
+# stop_cluster
+
+# qpidtest_PROGRAMS += cluster_test
+
+# cluster_test_SOURCES = \
+
+# cluster_test_LDADD=$(lib_client) $(lib_broker) ../cluster.la -lboost_unit_test_framework
+
+# qpidtest_SCRIPTS += run_cluster_tests cluster_tests.py run_long_cluster_tests long_cluster_tests.py testlib.py cluster_tests.fail
+
+# endif
diff --git a/cpp/src/tests/cluster.mk b/cpp/src/tests/cluster.mk
index 8b83e927ec..20053788e4 100644
--- a/cpp/src/tests/cluster.mk
+++ b/cpp/src/tests/cluster.mk
@@ -1,21 +1,86 @@
-if CPG
+#
+# 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.
+#
+
+if HAVE_LIBCPG
+
#
# Cluster tests makefile fragment, to be included in Makefile.am
#
-lib_cluster = $(abs_builddir)/../libqpidcluster.la
-
# NOTE: Programs using the openais library must be run with gid=ais
# You should do "newgrp ais" before running the tests to run these.
#
# ais_check checks pre-requisites for cluster tests and runs them if ok.
-TESTS+=ais_check
-EXTRA_DIST+=ais_check start_cluster stop_cluster
+TESTS += \
+ run_cluster_test \
+ cluster_read_credit \
+ test_watchdog \
+ run_cluster_tests \
+ federated_cluster_test \
+ clustered_replication_test
+
+# Clean up after cluster_test and start_cluster
+CLEANFILES += cluster_test.acl cluster.ports
+
+EXTRA_DIST += \
+ ais_check \
+ run_cluster_test \
+ cluster_read_credit \
+ test_watchdog \
+ start_cluster \
+ stop_cluster \
+ restart_cluster \
+ cluster_python_tests \
+ cluster_python_tests_failing.txt \
+ federated_cluster_test \
+ clustered_replication_test \
+ run_cluster_tests \
+ run_long_cluster_tests \
+ testlib.py \
+ cluster_tests.py \
+ long_cluster_tests.py \
+ cluster_tests.fail
+
+LONG_TESTS += \
+ run_long_cluster_tests \
+ start_cluster \
+ cluster_python_tests \
+ stop_cluster
+
+qpidtest_PROGRAMS += cluster_test
+
+cluster_test_SOURCES = \
+ cluster_test.cpp \
+ unit_test.cpp \
+ ClusterFixture.cpp \
+ ClusterFixture.h \
+ ForkedBroker.h \
+ ForkedBroker.cpp \
+ PartialFailure.cpp \
+ ClusterFailover.cpp \
+ InitialStatusMap.cpp \
+ StoreStatus.cpp
+
+cluster_test_LDADD=$(lib_client) $(lib_broker) ../cluster.la -lboost_unit_test_framework
-check_PROGRAMS+=cluster_test
-cluster_test_SOURCES=unit_test.cpp cluster_test.cpp
-cluster_test_LDADD=$(lib_client) $(lib_cluster) -lboost_unit_test_framework
+qpidtest_SCRIPTS += run_cluster_tests cluster_tests.py run_long_cluster_tests long_cluster_tests.py testlib.py cluster_tests.fail
endif
diff --git a/cpp/src/tests/cluster_python_tests b/cpp/src/tests/cluster_python_tests
new file mode 100755
index 0000000000..1a3fa4aff8
--- /dev/null
+++ b/cpp/src/tests/cluster_python_tests
@@ -0,0 +1,5 @@
+#!/bin/sh
+#
+FAILING=`dirname $0`/cluster_python_tests_failing.txt
+source `dirname $0`/python_tests
+
diff --git a/cpp/src/tests/cluster_python_tests_failing.txt b/cpp/src/tests/cluster_python_tests_failing.txt
new file mode 100644
index 0000000000..53b609942d
--- /dev/null
+++ b/cpp/src/tests/cluster_python_tests_failing.txt
@@ -0,0 +1,31 @@
+tests_0-10.management.ManagementTest.test_purge_queue
+tests_0-10.management.ManagementTest.test_connection_close
+tests_0-10.dtx.DtxTests.test_bad_resume
+tests_0-10.dtx.DtxTests.test_commit_unknown
+tests_0-10.dtx.DtxTests.test_end
+tests_0-10.dtx.DtxTests.test_end_suspend_and_fail
+tests_0-10.dtx.DtxTests.test_end_unknown_xid
+tests_0-10.dtx.DtxTests.test_forget_xid_on_completion
+tests_0-10.dtx.DtxTests.test_get_timeout
+tests_0-10.dtx.DtxTests.test_get_timeout_unknown
+tests_0-10.dtx.DtxTests.test_implicit_end
+tests_0-10.dtx.DtxTests.test_invalid_commit_not_ended
+tests_0-10.dtx.DtxTests.test_invalid_commit_one_phase_false
+tests_0-10.dtx.DtxTests.test_invalid_commit_one_phase_true
+tests_0-10.dtx.DtxTests.test_invalid_prepare_not_ended
+tests_0-10.dtx.DtxTests.test_invalid_rollback_not_ended
+tests_0-10.dtx.DtxTests.test_prepare_unknown
+tests_0-10.dtx.DtxTests.test_recover
+tests_0-10.dtx.DtxTests.test_rollback_unknown
+tests_0-10.dtx.DtxTests.test_select_required
+tests_0-10.dtx.DtxTests.test_set_timeout
+tests_0-10.dtx.DtxTests.test_simple_commit
+tests_0-10.dtx.DtxTests.test_simple_prepare_commit
+tests_0-10.dtx.DtxTests.test_simple_prepare_rollback
+tests_0-10.dtx.DtxTests.test_simple_rollback
+tests_0-10.dtx.DtxTests.test_start_already_known
+tests_0-10.dtx.DtxTests.test_start_join
+tests_0-10.dtx.DtxTests.test_start_join_and_resume
+tests_0-10.dtx.DtxTests.test_suspend_resume
+tests_0-10.dtx.DtxTests.test_suspend_start_end_resume
+tests_0-10.message.MessageTests.test_ttl
diff --git a/cpp/src/tests/cluster_read_credit b/cpp/src/tests/cluster_read_credit
new file mode 100755
index 0000000000..370d4098c5
--- /dev/null
+++ b/cpp/src/tests/cluster_read_credit
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# 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.
+#
+
+# Regression test for http://issues.apache.org/jira/browse/QPID-2086
+
+srcdir=`dirname $0`
+. $srcdir/ais_check
+$srcdir/start_cluster 1 --cluster-read-max=2 || exit 1
+trap $srcdir/stop_cluster EXIT
+seq 1 10000 | ./sender --port `cat cluster.ports` --routing-key no-such-queue
diff --git a/cpp/src/tests/cluster_test.cpp b/cpp/src/tests/cluster_test.cpp
index 49c5264990..33f23aa5b3 100644
--- a/cpp/src/tests/cluster_test.cpp
+++ b/cpp/src/tests/cluster_test.cpp
@@ -20,98 +20,78 @@
#include "unit_test.h"
#include "ForkedBroker.h"
#include "BrokerFixture.h"
+#include "ClusterFixture.h"
-#include "qpid/cluster/Cpg.h"
-#include "qpid/cluster/Cluster.h"
-#include "qpid/framing/AMQBody.h"
#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionSettings.h"
+#include "qpid/client/ConnectionAccess.h"
#include "qpid/client/Session.h"
+#include "qpid/client/FailoverListener.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/cluster/Cluster.h"
+#include "qpid/cluster/Cpg.h"
+#include "qpid/cluster/UpdateClient.h"
+#include "qpid/framing/AMQBody.h"
#include "qpid/framing/Uuid.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/enum.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/log/Logger.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Thread.h"
#include <boost/bind.hpp>
-#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/assign.hpp>
#include <string>
#include <iostream>
+#include <fstream>
#include <iterator>
#include <vector>
+#include <set>
#include <algorithm>
-
-namespace qpid {
-namespace cluster {
-boost::intrusive_ptr<Cluster> getGlobalCluster(); // Defined in qpid/cluster/ClusterPlugin.cpp
-}} // namespace qpid::cluster
-
-
-QPID_AUTO_TEST_SUITE(CpgTestSuite)
+#include <iterator>
using namespace std;
using namespace qpid;
using namespace qpid::cluster;
using namespace qpid::framing;
using namespace qpid::client;
-using qpid::broker::Broker;
-using boost::ptr_vector;
-using qpid::cluster::Cluster;
-using qpid::cluster::getGlobalCluster;
+using namespace boost::assign;
+using broker::Broker;
+using boost::shared_ptr;
-/** Cluster fixture is a vector of ports for the replicas.
- * Replica 0 is in the current process, all others are forked as children.
- */
-struct ClusterFixture : public vector<uint16_t> {
- string name;
- Broker::Options opts;
- std::auto_ptr<BrokerFixture> broker0;
- boost::ptr_vector<ForkedBroker> forkedBrokers;
-
- ClusterFixture(size_t n);
- void add(size_t n) { for (size_t i=0; i < n; ++i) add(); }
- void add();
- void setup();
-};
+namespace qpid {
+namespace tests {
-ClusterFixture::ClusterFixture(size_t n) : name(Uuid(true).str()) {
- add(n);
- // Wait for all n members to join the cluster
- int retry=20; // TODO aconway 2008-07-16: nasty sleeps, clean this up.
- while (retry && getGlobalCluster()->size() != n) {
- ::sleep(1);
- --retry;
- }
- BOOST_CHECK_EQUAL(n, getGlobalCluster()->size());
-}
+QPID_AUTO_TEST_SUITE(cluster_test)
-void ClusterFixture::add() {
- std::ostringstream os;
- os << "broker" << size();
- std::string prefix = os.str();
+bool durableFlag = std::getenv("STORE_LIB") != 0;
- const char* argv[] = {
- "qpidd " __FILE__ ,
- "--load-module=../.libs/libqpidcluster.so",
- "--cluster-name", name.c_str(),
- "--auth=no", "--no-data-dir",
- "--log-prefix", prefix.c_str(),
- };
- size_t argc = sizeof(argv)/sizeof(argv[0]);
+void prepareArgs(ClusterFixture::Args& args, const bool durableFlag = false) {
+ ostringstream clusterLib;
+ clusterLib << getLibPath("CLUSTER_LIB");
+ args += "--auth", "no", "--no-module-dir", "--load-module", clusterLib.str();
+ if (durableFlag)
+ args += "--load-module", getLibPath("STORE_LIB"), "TMP_DATA_DIR";
+ else
+ args += "--no-data-dir";
+}
- if (size()) { // Not the first broker, fork.
- forkedBrokers.push_back(new ForkedBroker(argc, argv));
- push_back(forkedBrokers.back().getPort());
- }
- else { // First broker, run in this process.
- Broker::Options opts;
- Plugin::addOptions(opts); // Pick up cluster options.
- opts.parse(argc, argv, "", true); // Allow-unknown for --load-module
- broker0.reset(new BrokerFixture(opts));
- push_back(broker0->getPort());
- }
+ClusterFixture::Args prepareArgs(const bool durableFlag = false) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ return args;
}
-// For debugging: op << for CPG types.
+// Timeout for tests that wait for messages
+const sys::Duration TIMEOUT=sys::TIME_SEC/2;
+
ostream& operator<<(ostream& o, const cpg_name* n) {
- return o << qpid::cluster::Cpg::str(*n);
+ return o << Cpg::str(*n);
}
ostream& operator<<(ostream& o, const cpg_address& a) {
@@ -127,55 +107,557 @@ ostream& operator<<(ostream& o, const pair<T*, int>& array) {
return o;
}
-struct Callback : public Cpg::Handler {
- Callback(const string group_) : group(group_) {}
- string group;
- vector<string> delivered;
- vector<int> configChanges;
+template <class C> set<int> makeSet(const C& c) {
+ set<int> s;
+ copy(c.begin(), c.end(), inserter(s, s.begin()));
+ return s;
+}
- void deliver (
- cpg_handle_t /*handle*/,
- struct cpg_name *grp,
- uint32_t /*nodeid*/,
- uint32_t /*pid*/,
- void* msg,
- int msg_len)
- {
- BOOST_CHECK_EQUAL(group, Cpg::str(*grp));
- delivered.push_back(string((char*)msg,msg_len));
+class Sender {
+ public:
+ Sender(boost::shared_ptr<ConnectionImpl> ci, uint16_t ch) : connection(ci), channel(ch) {}
+ void send(const AMQBody& body, bool firstSeg, bool lastSeg, bool firstFrame, bool lastFrame) {
+ AMQFrame f(body);
+ f.setChannel(channel);
+ f.setFirstSegment(firstSeg);
+ f.setLastSegment(lastSeg);
+ f.setFirstFrame(firstFrame);
+ f.setLastFrame(lastFrame);
+ connection->handle(f);
}
- void configChange(
- cpg_handle_t /*handle*/,
- struct cpg_name *grp,
- struct cpg_address */*members*/, int nMembers,
- struct cpg_address */*left*/, int nLeft,
- struct cpg_address */*joined*/, int nJoined
- )
- {
- BOOST_CHECK_EQUAL(group, Cpg::str(*grp));
- configChanges.push_back(nMembers);
- BOOST_MESSAGE("configChange: "<<
- nLeft<<" left "<<
- nJoined<<" joined "<<
- nMembers<<" members.");
+ private:
+ boost::shared_ptr<ConnectionImpl> connection;
+ uint16_t channel;
+};
+
+int64_t getMsgSequence(const Message& m) {
+ return m.getMessageProperties().getApplicationHeaders().getAsInt64("qpid.msg_sequence");
+}
+
+Message ttlMessage(const string& data, const string& key, uint64_t ttl, bool durable = false) {
+ Message m(data, key);
+ m.getDeliveryProperties().setTtl(ttl);
+ if (durable) m.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ return m;
+}
+
+Message makeMessage(const string& data, const string& key, bool durable = false) {
+ Message m(data, key);
+ if (durable) m.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ return m;
+}
+
+vector<string> browse(Client& c, const string& q, int n) {
+ SubscriptionSettings browseSettings(
+ FlowControl::messageCredit(n),
+ ACCEPT_MODE_NONE,
+ ACQUIRE_MODE_NOT_ACQUIRED,
+ 0 // No auto-ack.
+ );
+ LocalQueue lq;
+ c.subs.subscribe(lq, q, browseSettings);
+ vector<string> result;
+ for (int i = 0; i < n; ++i) {
+ Message m;
+ if (!lq.get(m, TIMEOUT))
+ break;
+ result.push_back(m.getData());
}
+ c.subs.getSubscription(q).cancel();
+ return result;
+}
+
+ConnectionSettings aclSettings(int port, const std::string& id) {
+ ConnectionSettings settings;
+ settings.port = port;
+ settings.mechanism = "PLAIN";
+ settings.username = id;
+ settings.password = id;
+ return settings;
+}
+
+// An illegal frame body
+struct PoisonPill : public AMQBody {
+ virtual uint8_t type() const { return 0xFF; }
+ virtual void encode(Buffer& ) const {}
+ virtual void decode(Buffer& , uint32_t=0) {}
+ virtual uint32_t encodedSize() const { return 0; }
+
+ virtual void print(std::ostream&) const {};
+ virtual void accept(AMQBodyConstVisitor&) const {};
+
+ virtual AMQMethodBody* getMethod() { return 0; }
+ virtual const AMQMethodBody* getMethod() const { return 0; }
+
+ /** Match if same type and same class/method ID for methods */
+ static bool match(const AMQBody& , const AMQBody& ) { return false; }
+ virtual boost::intrusive_ptr<AMQBody> clone() const { return new PoisonPill; }
};
-QPID_AUTO_TEST_CASE(testForkedBroker) {
- // Verify the ForkedBroker works as expected.
- const char* argv[] = { "", "--auth=no", "--no-data-dir", "--log-prefix=testForkedBroker" };
- ForkedBroker broker(sizeof(argv)/sizeof(argv[0]), argv);
- Client c(broker.getPort());
- BOOST_CHECK_EQUAL("direct", c.session.exchangeQuery("amq.direct").getType());
+QPID_AUTO_TEST_CASE(testBadClientData) {
+ // Ensure that bad data on a client connection closes the
+ // connection but does not stop the broker.
+ ClusterFixture::Args args;
+ prepareArgs(args, false);
+ args += "--log-enable=critical"; // Supress expected errors
+ ClusterFixture cluster(2, args, -1);
+ Client c0(cluster[0]);
+ Client c1(cluster[1]);
+ boost::shared_ptr<client::ConnectionImpl> ci =
+ client::ConnectionAccess::getImpl(c0.connection);
+ AMQFrame poison(boost::intrusive_ptr<AMQBody>(new PoisonPill));
+ ci->handle(poison);
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(c0.session.queueQuery("q0"), TransportFailure);
+ }
+ Client c00(cluster[0]);
+ BOOST_CHECK_EQUAL(c00.session.queueQuery("q00").getQueue(), "");
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getQueue(), "");
+}
+
+QPID_AUTO_TEST_CASE(testAcl) {
+ ofstream policyFile("cluster_test.acl");
+ policyFile << "acl allow foo@QPID create queue name=foo" << endl
+ << "acl allow foo@QPID create queue name=foo2" << endl
+ << "acl deny foo@QPID create queue name=bar" << endl
+ << "acl allow all all" << endl;
+ policyFile.close();
+ char cwd[1024];
+ BOOST_CHECK(::getcwd(cwd, sizeof(cwd)));
+ ostringstream aclLib;
+ aclLib << getLibPath("ACL_LIB");
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ args += "--log-enable=critical"; // Supress expected errors
+ args += "--acl-file", string(cwd) + "/cluster_test.acl",
+ "--cluster-mechanism", "PLAIN",
+ "--cluster-username", "cluster",
+ "--cluster-password", "cluster",
+ "--load-module", aclLib.str();
+ ClusterFixture cluster(2, args, -1);
+
+ Client c0(aclSettings(cluster[0], "c0"), "c0");
+ Client c1(aclSettings(cluster[1], "c1"), "c1");
+ Client foo(aclSettings(cluster[1], "foo"), "foo");
+
+ foo.session.queueDeclare("foo", arg::durable=durableFlag);
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("foo").getQueue(), "foo");
+
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(foo.session.queueDeclare("bar", arg::durable=durableFlag), framing::NotAllowedException);
+ }
+ BOOST_CHECK(c0.session.queueQuery("bar").getQueue().empty());
+ BOOST_CHECK(c1.session.queueQuery("bar").getQueue().empty());
+
+ cluster.add();
+ Client c2(aclSettings(cluster[2], "c2"), "c2");
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(foo.session.queueDeclare("bar", arg::durable=durableFlag), framing::NotAllowedException);
+ }
+ BOOST_CHECK(c2.session.queueQuery("bar").getQueue().empty());
+}
+
+QPID_AUTO_TEST_CASE(testMessageTimeToLive) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(2, args, -1);
+ Client c0(cluster[0], "c0");
+ Client c1(cluster[1], "c1");
+ c0.session.queueDeclare("p", arg::durable=durableFlag);
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=ttlMessage("a", "q", 200, durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("b", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=ttlMessage("x", "p", 10000, durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("y", "p", durableFlag));
+ cluster.add();
+ Client c2(cluster[1], "c2");
+
+ BOOST_CHECK_EQUAL(browse(c0, "p", 1), list_of<string>("x"));
+ BOOST_CHECK_EQUAL(browse(c1, "p", 1), list_of<string>("x"));
+ BOOST_CHECK_EQUAL(browse(c2, "p", 1), list_of<string>("x"));
+
+ sys::usleep(200*1000);
+ BOOST_CHECK_EQUAL(browse(c0, "q", 1), list_of<string>("b"));
+ BOOST_CHECK_EQUAL(browse(c1, "q", 1), list_of<string>("b"));
+ BOOST_CHECK_EQUAL(browse(c2, "q", 1), list_of<string>("b"));
+}
+
+QPID_AUTO_TEST_CASE(testSequenceOptions) {
+ // Make sure the exchange qpid.msg_sequence property is properly replicated.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ FieldTable ftargs;
+ ftargs.setInt("qpid.msg_sequence", 1);
+ c0.session.queueDeclare(arg::queue="q", arg::durable=durableFlag);
+ c0.session.exchangeDeclare(arg::exchange="ex", arg::type="direct", arg::arguments=ftargs);
+ c0.session.exchangeBind(arg::exchange="ex", arg::queue="q", arg::bindingKey="k");
+ c0.session.messageTransfer(arg::content=makeMessage("1", "k", durableFlag), arg::destination="ex");
+ c0.session.messageTransfer(arg::content=makeMessage("2", "k", durableFlag), arg::destination="ex");
+ BOOST_CHECK_EQUAL(1, getMsgSequence(c0.subs.get("q", TIMEOUT)));
+ BOOST_CHECK_EQUAL(2, getMsgSequence(c0.subs.get("q", TIMEOUT)));
+
+ cluster.add();
+ Client c1(cluster[1]);
+ c1.session.messageTransfer(arg::content=makeMessage("3", "k", durableFlag), arg::destination="ex");
+ BOOST_CHECK_EQUAL(3, getMsgSequence(c1.subs.get("q", TIMEOUT)));
+}
+
+QPID_AUTO_TEST_CASE(testTxTransaction) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare(arg::queue="q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("A", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("B", "q", durableFlag));
+
+ // Start a transaction that will commit.
+ Session commitSession = c0.connection.newSession("commit");
+ SubscriptionManager commitSubs(commitSession);
+ commitSession.txSelect();
+ commitSession.messageTransfer(arg::content=makeMessage("a", "q", durableFlag));
+ commitSession.messageTransfer(arg::content=makeMessage("b", "q", durableFlag));
+ BOOST_CHECK_EQUAL(commitSubs.get("q", TIMEOUT).getData(), "A");
+
+ // Start a transaction that will roll back.
+ Session rollbackSession = c0.connection.newSession("rollback");
+ SubscriptionManager rollbackSubs(rollbackSession);
+ rollbackSession.txSelect();
+ rollbackSession.messageTransfer(arg::content=makeMessage("1", "q", durableFlag));
+ Message rollbackMessage = rollbackSubs.get("q", TIMEOUT);
+ BOOST_CHECK_EQUAL(rollbackMessage.getData(), "B");
+
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
+ // Add new member mid transaction.
+ cluster.add();
+ Client c1(cluster[1], "c1");
+
+ // More transactional work
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+ rollbackSession.messageTransfer(arg::content=makeMessage("2", "q", durableFlag));
+ commitSession.messageTransfer(arg::content=makeMessage("c", "q", durableFlag));
+ rollbackSession.messageTransfer(arg::content=makeMessage("3", "q", durableFlag));
+
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+
+ // Commit/roll back.
+ commitSession.txCommit();
+ rollbackSession.txRollback();
+ rollbackSession.messageRelease(rollbackMessage.getId());
+
+ // Verify queue status: just the comitted messages and dequeues should remain.
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 4u);
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "B");
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "a");
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "b");
+ BOOST_CHECK_EQUAL(c1.subs.get("q", TIMEOUT).getData(), "c");
+
+ commitSession.close();
+ rollbackSession.close();
+}
+
+QPID_AUTO_TEST_CASE(testUnacked) {
+ // Verify replication of unacknowledged messages.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ Message m;
+
+ // Create unacked message: acquired but not accepted.
+ SubscriptionSettings manualAccept(FlowControl::unlimited(), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_PRE_ACQUIRED, 0);
+ c0.session.queueDeclare("q1", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("11","q1", durableFlag));
+ LocalQueue q1;
+ c0.subs.subscribe(q1, "q1", manualAccept);
+ BOOST_CHECK_EQUAL(q1.get(TIMEOUT).getData(), "11"); // Acquired but not accepted
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q1").getMessageCount(), 0u); // Gone from queue
+
+ // Create unacked message: not acquired, accepted or completeed.
+ SubscriptionSettings manualAcquire(FlowControl::unlimited(), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_NOT_ACQUIRED, 0);
+ c0.session.queueDeclare("q2", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("21","q2", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("22","q2", durableFlag));
+ LocalQueue q2;
+ c0.subs.subscribe(q2, "q2", manualAcquire);
+ m = q2.get(TIMEOUT); // Not acquired or accepted, still on queue
+ BOOST_CHECK_EQUAL(m.getData(), "21");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 2u); // Not removed
+ c0.subs.getSubscription("q2").acquire(m); // Acquire manually
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 1u); // Removed
+ BOOST_CHECK_EQUAL(q2.get(TIMEOUT).getData(), "22"); // Not acquired or accepted, still on queue
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q2").getMessageCount(), 1u); // 1 not acquired.
+
+ // Create empty credit record: acquire and accept but don't complete.
+ SubscriptionSettings manualComplete(FlowControl::messageWindow(1), ACCEPT_MODE_EXPLICIT, ACQUIRE_MODE_PRE_ACQUIRED, 1, MANUAL_COMPLETION);
+ c0.session.queueDeclare("q3", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("31", "q3", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("32", "q3", durableFlag));
+ LocalQueue q3;
+ c0.subs.subscribe(q3, "q3", manualComplete);
+ Message m31=q3.get(TIMEOUT);
+ BOOST_CHECK_EQUAL(m31.getData(), "31"); // Automatically acquired & accepted but not completed.
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q3").getMessageCount(), 1u);
+
+ // Add new member while there are unacked messages.
+ cluster.add();
+ Client c1(cluster[1], "c1");
+
+ // Check queue counts
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q2").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q3").getMessageCount(), 1u);
+
+ // Complete the empty credit message, should unblock the message behind it.
+ BOOST_CHECK_THROW(q3.get(0), Exception);
+ c0.session.markCompleted(SequenceSet(m31.getId()), true);
+ BOOST_CHECK_EQUAL(q3.get(TIMEOUT).getData(), "32");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q3").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q3").getMessageCount(), 0u);
+
+ // Close the original session - unacked messages should be requeued.
+ c0.session.close();
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q1").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q2").getMessageCount(), 2u);
+
+ BOOST_CHECK_EQUAL(c1.subs.get("q1", TIMEOUT).getData(), "11");
+ BOOST_CHECK_EQUAL(c1.subs.get("q2", TIMEOUT).getData(), "21");
+ BOOST_CHECK_EQUAL(c1.subs.get("q2", TIMEOUT).getData(), "22");
+}
+
+// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
+void testUpdateTxState() {
+ // Verify that we update transaction state correctly to new members.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ // Do work in a transaction.
+ c0.session.txSelect();
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("1","q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("2","q", durableFlag));
+ Message m;
+ BOOST_CHECK(c0.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "1");
+
+ // New member, TX not comitted, c1 should see nothing.
+ cluster.add();
+ Client c1(cluster[1], "c1");
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 0u);
+
+ // After commit c1 shoudl see results of tx.
+ c0.session.txCommit();
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 1u);
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "2");
+
+ // Another transaction with both members active.
+ c0.session.messageTransfer(arg::content=makeMessage("3","q", durableFlag));
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 0u);
+ c0.session.txCommit();
+ BOOST_CHECK_EQUAL(c1.session.queueQuery(arg::queue="q").getMessageCount(), 1u);
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "3");
+}
+
+QPID_AUTO_TEST_CASE(testUpdateMessageBuilder) {
+ // Verify that we update a partially recieved message to a new member.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ Sender sender(ConnectionAccess::getImpl(c0.connection), c0.session.getChannel());
+
+ // Send first 2 frames of message.
+ MessageTransferBody transfer(
+ ProtocolVersion(), string(), // default exchange.
+ framing::message::ACCEPT_MODE_NONE,
+ framing::message::ACQUIRE_MODE_PRE_ACQUIRED);
+ sender.send(transfer, true, false, true, true);
+ AMQHeaderBody header;
+ header.get<DeliveryProperties>(true)->setRoutingKey("q");
+ if (durableFlag)
+ header.get<DeliveryProperties>(true)->setDeliveryMode(DELIVERY_MODE_PERSISTENT);
+ else
+ header.get<DeliveryProperties>(true)->setDeliveryMode(DELIVERY_MODE_NON_PERSISTENT);
+ sender.send(header, false, false, true, true);
+
+ // No reliable way to ensure the partial message has arrived
+ // before we start the new broker, so we sleep.
+ sys::usleep(2500);
+ cluster.add();
+
+ // Send final 2 frames of message.
+ sender.send(AMQContentBody("ab"), false, true, true, false);
+ sender.send(AMQContentBody("cd"), false, true, false, true);
+
+ // Verify message is enqued correctly on second member.
+ Message m;
+ Client c1(cluster[1], "c1");
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "abcd");
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection).size());
+}
+
+QPID_AUTO_TEST_CASE(testConnectionKnownHosts) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+ set<int> kb0 = knownBrokerPorts(c0.connection);
+ BOOST_CHECK_EQUAL(kb0.size(), 1u);
+ BOOST_CHECK_EQUAL(kb0, makeSet(cluster));
+
+ cluster.add();
+ Client c1(cluster[1], "c1");
+ set<int> kb1 = knownBrokerPorts(c1.connection);
+ kb0 = knownBrokerPorts(c0.connection, 2);
+ BOOST_CHECK_EQUAL(kb1.size(), 2u);
+ BOOST_CHECK_EQUAL(kb1, makeSet(cluster));
+ BOOST_CHECK_EQUAL(kb1,kb0);
+
+ cluster.add();
+ Client c2(cluster[2], "c2");
+ set<int> kb2 = knownBrokerPorts(c2.connection);
+ kb1 = knownBrokerPorts(c1.connection, 3);
+ kb0 = knownBrokerPorts(c0.connection, 3);
+ BOOST_CHECK_EQUAL(kb2.size(), 3u);
+ BOOST_CHECK_EQUAL(kb2, makeSet(cluster));
+ BOOST_CHECK_EQUAL(kb2,kb0);
+ BOOST_CHECK_EQUAL(kb2,kb1);
+
+ cluster.killWithSilencer(1,c1.connection,9);
+ kb0 = knownBrokerPorts(c0.connection, 2);
+ kb2 = knownBrokerPorts(c2.connection, 2);
+ BOOST_CHECK_EQUAL(kb0.size(), 2u);
+ BOOST_CHECK_EQUAL(kb0, kb2);
+}
+
+QPID_AUTO_TEST_CASE(testUpdateConsumers) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("p", arg::durable=durableFlag);
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.subs.subscribe(c0.lq, "q", FlowControl::zero());
+ LocalQueue lp;
+ c0.subs.subscribe(lp, "p", FlowControl::messageCredit(1));
+ c0.session.sync();
+
+ // Start new members
+ cluster.add(); // Local
+ Client c1(cluster[1], "c1");
+ cluster.add();
+ Client c2(cluster[2], "c2");
+
+ // Transfer messages
+ c0.session.messageTransfer(arg::content=makeMessage("aaa", "q", durableFlag));
+
+ c0.session.messageTransfer(arg::content=makeMessage("bbb", "p", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("ccc", "p", durableFlag));
+
+ // Activate the subscription, ensure message removed on all queues.
+ c0.subs.setFlowControl("q", FlowControl::unlimited());
+ Message m;
+ BOOST_CHECK(c0.lq.get(m, TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "aaa");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c2.session.queueQuery("q").getMessageCount(), 0u);
+
+ // Check second subscription's flow control: gets first message, not second.
+ BOOST_CHECK(lp.get(m, TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "bbb");
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("p").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("p").getMessageCount(), 1u);
+ BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 1u);
+
+ BOOST_CHECK(c0.subs.get(m, "p", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "ccc");
+
+ // Kill the subscribing member, ensure further messages are not removed.
+ cluster.killWithSilencer(0,c0.connection,9);
+ BOOST_REQUIRE_EQUAL(knownBrokerPorts(c1.connection, 2).size(), 2u);
+ for (int i = 0; i < 10; ++i) {
+ c1.session.messageTransfer(arg::content=makeMessage("xxx", "q", durableFlag));
+ BOOST_REQUIRE(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_REQUIRE_EQUAL(m.getData(), "xxx");
+ }
+}
+
+// Test that message data and delivery properties are updated properly.
+QPID_AUTO_TEST_CASE(testUpdateMessages) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ // Create messages with different delivery properties
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.exchangeBind(arg::exchange="amq.fanout", arg::queue="q");
+ c0.session.messageTransfer(arg::content=makeMessage("foo","q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("bar","q", durableFlag),
+ arg::destination="amq.fanout");
+
+ while (c0.session.queueQuery("q").getMessageCount() != 2)
+ sys::usleep(1000); // Wait for message to show up on broker 0.
+
+ // Add a new broker, it will catch up.
+ cluster.add();
+
+ // Do some work post-add
+ c0.session.queueDeclare("p", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("pfoo","p", durableFlag));
+
+ // Do some work post-join
+ BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 2).size(), 2u);
+ c0.session.messageTransfer(arg::content=makeMessage("pbar","p", durableFlag));
+
+ // Verify new brokers have state.
+ Message m;
+
+ Client c1(cluster[1], "c1");
+
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "foo");
+ BOOST_CHECK(m.getDeliveryProperties().hasExchange());
+ BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), "");
+ BOOST_CHECK(c1.subs.get(m, "q", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "bar");
+ BOOST_CHECK(m.getDeliveryProperties().hasExchange());
+ BOOST_CHECK_EQUAL(m.getDeliveryProperties().getExchange(), "amq.fanout");
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+
+ // Add another broker, don't wait for join - should be stalled till ready.
+ cluster.add();
+ Client c2(cluster[2], "c2");
+ BOOST_CHECK(c2.subs.get(m, "p", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "pfoo");
+ BOOST_CHECK(c2.subs.get(m, "p", TIMEOUT));
+ BOOST_CHECK_EQUAL(m.getData(), "pbar");
+ BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 0u);
}
QPID_AUTO_TEST_CASE(testWiringReplication) {
- ClusterFixture cluster(3);
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(3, args, -1);
Client c0(cluster[0]);
BOOST_CHECK(c0.session.queueQuery("q").getQueue().empty());
- BOOST_CHECK(c0.session.exchangeQuery("ex").getType().empty());
- c0.session.queueDeclare("q");
+ BOOST_CHECK(c0.session.exchangeQuery("ex").getType().empty());
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
c0.session.exchangeDeclare("ex", arg::type="direct");
c0.session.close();
c0.connection.close();
@@ -185,41 +667,45 @@ QPID_AUTO_TEST_CASE(testWiringReplication) {
Client c(cluster[i]);
BOOST_CHECK_EQUAL("q", c.session.queueQuery("q").getQueue());
BOOST_CHECK_EQUAL("direct", c.session.exchangeQuery("ex").getType());
- }
+ }
}
QPID_AUTO_TEST_CASE(testMessageEnqueue) {
// Enqueue on one broker, dequeue on another.
- ClusterFixture cluster(2);
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(2, args, -1);
Client c0(cluster[0]);
- c0.session.queueDeclare("q");
- c0.session.messageTransfer(arg::content=TransferContent("foo", "q"));
- c0.session.messageTransfer(arg::content=TransferContent("bar", "q"));
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
c0.session.close();
Client c1(cluster[1]);
Message msg;
- BOOST_CHECK(c1.subs.get(msg, "q", qpid::sys::TIME_SEC));
+ BOOST_CHECK(c1.subs.get(msg, "q", TIMEOUT));
BOOST_CHECK_EQUAL(string("foo"), msg.getData());
- BOOST_CHECK(c1.subs.get(msg, "q", qpid::sys::TIME_SEC));
+ BOOST_CHECK(c1.subs.get(msg, "q", TIMEOUT));
BOOST_CHECK_EQUAL(string("bar"), msg.getData());
}
QPID_AUTO_TEST_CASE(testMessageDequeue) {
// Enqueue on one broker, dequeue on two others.
- ClusterFixture cluster (3);
- Client c0(cluster[0]);
- c0.session.queueDeclare("q");
- c0.session.messageTransfer(arg::content=TransferContent("foo", "q"));
- c0.session.messageTransfer(arg::content=TransferContent("bar", "q"));
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(3, args, -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ c0.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
+ c0.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
Message msg;
// Dequeue on 2 others, ensure correct order.
- Client c1(cluster[1]);
+ Client c1(cluster[1], "c1");
BOOST_CHECK(c1.subs.get(msg, "q"));
BOOST_CHECK_EQUAL("foo", msg.getData());
-
- Client c2(cluster[2]);
+
+ Client c2(cluster[2], "c2");
BOOST_CHECK(c1.subs.get(msg, "q"));
BOOST_CHECK_EQUAL("bar", msg.getData());
@@ -230,21 +716,26 @@ QPID_AUTO_TEST_CASE(testMessageDequeue) {
}
QPID_AUTO_TEST_CASE(testDequeueWaitingSubscription) {
- ClusterFixture cluster(3);
- // First start a subscription.
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(3, args, -1);
Client c0(cluster[0]);
- c0.session.queueDeclare("q");
+ BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 3).size(), 3u); // Wait for brokers.
+
+ // First start a subscription.
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
c0.subs.subscribe(c0.lq, "q", FlowControl::messageCredit(2));
+
// Now send messages
Client c1(cluster[1]);
- c1.session.messageTransfer(arg::content=TransferContent("foo", "q"));
- c1.session.messageTransfer(arg::content=TransferContent("bar", "q"));
+ c1.session.messageTransfer(arg::content=makeMessage("foo", "q", durableFlag));
+ c1.session.messageTransfer(arg::content=makeMessage("bar", "q", durableFlag));
// Check they arrived
Message m;
- BOOST_CHECK(c0.lq.get(m, sys::TIME_SEC));
+ BOOST_CHECK(c0.lq.get(m, TIMEOUT));
BOOST_CHECK_EQUAL("foo", m.getData());
- BOOST_CHECK(c0.lq.get(m, sys::TIME_SEC));
+ BOOST_CHECK(c0.lq.get(m, TIMEOUT));
BOOST_CHECK_EQUAL("bar", m.getData());
// Queue should be empty on all cluster members.
@@ -254,5 +745,447 @@ QPID_AUTO_TEST_CASE(testDequeueWaitingSubscription) {
BOOST_CHECK_EQUAL(0u, c2.session.queueQuery("q").getMessageCount());
}
+QPID_AUTO_TEST_CASE(queueDurabilityPropagationToNewbie)
+{
+ /*
+ Start with a single broker.
+ Set up two queues: one durable, and one not.
+ Add a new broker to the cluster.
+ Make sure it has one durable and one non-durable queue.
+ */
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0]);
+ c0.session.queueDeclare("durable_queue", arg::durable=true);
+ c0.session.queueDeclare("non_durable_queue", arg::durable=false);
+ cluster.add();
+ Client c1(cluster[1]);
+ QueueQueryResult durable_query = c1.session.queueQuery ( "durable_queue" );
+ QueueQueryResult non_durable_query = c1.session.queueQuery ( "non_durable_queue" );
+ BOOST_CHECK_EQUAL(durable_query.getQueue(), std::string("durable_queue"));
+ BOOST_CHECK_EQUAL(non_durable_query.getQueue(), std::string("non_durable_queue"));
+
+ BOOST_CHECK_EQUAL ( durable_query.getDurable(), true );
+ BOOST_CHECK_EQUAL ( non_durable_query.getDurable(), false );
+}
+
+
+QPID_AUTO_TEST_CASE(testHeartbeatCancelledOnFailover)
+{
+
+ struct Sender : FailoverManager::Command
+ {
+ std::string queue;
+ std::string content;
+
+ Sender(const std::string& q, const std::string& c) : queue(q), content(c) {}
+
+ void execute(AsyncSession& session, bool)
+ {
+ session.messageTransfer(arg::content=makeMessage(content, queue, durableFlag));
+ }
+ };
+
+ struct Receiver : FailoverManager::Command, MessageListener, qpid::sys::Runnable
+ {
+ FailoverManager& mgr;
+ std::string queue;
+ std::string expectedContent;
+ qpid::client::Subscription subscription;
+ qpid::sys::Monitor lock;
+ bool ready, failed;
+
+ Receiver(FailoverManager& m, const std::string& q, const std::string& c) : mgr(m), queue(q), expectedContent(c), ready(false), failed(false) {}
+
+ void received(Message& message)
+ {
+ BOOST_CHECK_EQUAL(expectedContent, message.getData());
+ subscription.cancel();
+ }
+
+ void execute(AsyncSession& session, bool)
+ {
+ session.queueDeclare(arg::queue=queue, arg::durable=durableFlag);
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queue);
+ session.sync();
+ setReady();
+ subs.run();
+ //cleanup:
+ session.queueDelete(arg::queue=queue);
+ }
+
+ void run()
+ {
+ try {
+ mgr.execute(*this);
+ }
+ catch (const std::exception& e) {
+ BOOST_MESSAGE("Exception in mgr.execute: " << e.what());
+ failed = true;
+ }
+ }
+
+ void waitForReady()
+ {
+ qpid::sys::Monitor::ScopedLock l(lock);
+ while (!ready) {
+ lock.wait();
+ }
+ }
+
+ void setReady()
+ {
+ qpid::sys::Monitor::ScopedLock l(lock);
+ ready = true;
+ lock.notify();
+ }
+ };
+
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(2, args, -1);
+ ConnectionSettings settings;
+ settings.port = cluster[1];
+ settings.heartbeat = 1;
+ FailoverManager fmgr(settings);
+ Sender sender("my-queue", "my-data");
+ Receiver receiver(fmgr, "my-queue", "my-data");
+ qpid::sys::Thread runner(receiver);
+ receiver.waitForReady();
+ {
+ ScopedSuppressLogging allQuiet; // suppress connection closed messages
+ cluster.kill(1);
+ //sleep for 2 secs to allow the heartbeat task to fire on the now dead connection:
+ ::usleep(2*1000*1000);
+ }
+ fmgr.execute(sender);
+ runner.join();
+ BOOST_CHECK(!receiver.failed);
+ fmgr.close();
+}
+
+QPID_AUTO_TEST_CASE(testPolicyUpdate) {
+ //tests that the policys internal state is accurate on newly
+ //joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setSizePolicy(REJECT, 0, 2);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+ c1.session.messageTransfer(arg::content=makeMessage("one", "q", durableFlag));
+ cluster.add();
+ Client c2(cluster[1], "c2");
+ c2.session.messageTransfer(arg::content=makeMessage("two", "q", durableFlag));
+
+ BOOST_CHECK_THROW(c2.session.messageTransfer(arg::content=makeMessage("three", "q", durableFlag)), framing::ResourceLimitExceededException);
+
+ Message received;
+ BOOST_CHECK(c1.subs.get(received, "q"));
+ BOOST_CHECK_EQUAL(received.getData(), std::string("one"));
+ BOOST_CHECK(c1.subs.get(received, "q"));
+ BOOST_CHECK_EQUAL(received.getData(), std::string("two"));
+ BOOST_CHECK(!c1.subs.get(received, "q"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveQueueUpdate) {
+ //tests that exclusive queues are accurately replicated on newly
+ //joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ c1.session.queueDeclare("q", arg::exclusive=true, arg::autoDelete=true, arg::alternateExchange="amq.fanout");
+ cluster.add();
+ Client c2(cluster[1], "c2");
+ QueueQueryResult result = c2.session.queueQuery("q");
+ BOOST_CHECK_EQUAL(result.getQueue(), std::string("q"));
+ BOOST_CHECK(result.getExclusive());
+ BOOST_CHECK(result.getAutoDelete());
+ BOOST_CHECK(!result.getDurable());
+ BOOST_CHECK_EQUAL(result.getAlternateExchange(), std::string("amq.fanout"));
+ BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::exclusive=true, arg::passive=true), framing::ResourceLockedException);
+ c1.session.close();
+ c1.connection.close();
+ c2.session = c2.connection.newSession();
+ BOOST_CHECK_THROW(c2.session.queueDeclare(arg::queue="q", arg::passive=true), framing::NotFoundException);
+ }
+}
+
+/**
+ * Subscribes to specified queue and acquires up to the specified
+ * number of message but does not accept or release them. These
+ * message are therefore 'locked' by the clients session.
+ */
+Subscription lockMessages(Client& client, const std::string& queue, int count)
+{
+ LocalQueue q;
+ SubscriptionSettings settings(FlowControl::messageCredit(count));
+ settings.autoAck = 0;
+ Subscription sub = client.subs.subscribe(q, queue, settings);
+ client.session.messageFlush(sub.getName());
+ return sub;
+}
+
+/**
+ * check that the specified queue contains the expected set of
+ * messages (matched on content) for all nodes in the cluster
+ */
+void checkQueue(ClusterFixture& cluster, const std::string& queue, const std::vector<std::string>& messages)
+{
+ for (size_t i = 0; i < cluster.size(); i++) {
+ Client client(cluster[i], (boost::format("%1%_%2%") % "c" % (i+1)).str());
+ BOOST_CHECK_EQUAL(browse(client, queue, messages.size()), messages);
+ client.close();
+ }
+}
+
+void send(Client& client, const std::string& queue, int count, int start=1, const std::string& base="m",
+ const std::string& lvqKey="")
+{
+ for (int i = 0; i < count; i++) {
+ Message message = makeMessage((boost::format("%1%_%2%") % base % (i+start)).str(), queue, durableFlag);
+ if (!lvqKey.empty()) message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqKey);
+ client.session.messageTransfer(arg::content=message);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRingQueueUpdate) {
+ //tests that ring queues are accurately replicated on newly
+ //joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setSizePolicy(RING, 0, 5);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+ send(c1, "q", 5);
+ lockMessages(c1, "q", 1);
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+ //send one more message
+ send(c1, "q", 1, 6);
+ //release locked message
+ c1.close();
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("m_2")("m_3")("m_4")("m_5")("m_6"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRingQueueUpdate2) {
+ //tests that ring queues are accurately replicated on newly joined
+ //nodes; just like testRingQueueUpdate, but new node joins after
+ //the sixth message has been sent.
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setSizePolicy(RING, 0, 5);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+ send(c1, "q", 5);
+ lockMessages(c1, "q", 1);
+ //send sixth message
+ send(c1, "q", 1, 6);
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+ //release locked message
+ c1.close();
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("m_2")("m_3")("m_4")("m_5")("m_6"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testLvqUpdate) {
+ //tests that lvqs are accurately replicated on newly joined nodes
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setOrdering(LVQ);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+
+ send(c1, "q", 5, 1, "a", "a");
+ send(c1, "q", 2, 1, "b", "b");
+ send(c1, "q", 1, 1, "c", "c");
+ send(c1, "q", 1, 3, "b", "b");
+
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("a_5")("b_3")("c_1"));
+ }
+}
+
+
+QPID_AUTO_TEST_CASE(testBrowsedLvqUpdate) {
+ //tests that lvqs are accurately replicated on newly joined nodes
+ //if the lvq state has been affected by browsers
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ QueueOptions options;
+ options.setOrdering(LVQ);
+ c1.session.queueDeclare("q", arg::arguments=options, arg::durable=durableFlag);
+
+ send(c1, "q", 1, 1, "a", "a");
+ send(c1, "q", 2, 1, "b", "b");
+ send(c1, "q", 1, 1, "c", "c");
+ checkQueue(cluster, "q", list_of<string>("a_1")("b_2")("c_1"));
+ send(c1, "q", 4, 2, "a", "a");
+ send(c1, "q", 1, 3, "b", "b");
+
+ //add new node
+ cluster.add();
+ BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection, 2).size());//wait till joined
+
+ //check state of queue on both nodes
+ checkQueue(cluster, "q", list_of<string>("a_1")("b_2")("c_1")("a_5")("b_3"));
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRelease) {
+ //tests that releasing a messages that was unacked when one node
+ //joined works correctly
+ ClusterFixture::Args args;
+ args += "--log-enable", "critical";
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c1(cluster[0], "c1");
+ {
+ ScopedSuppressLogging allQuiet;
+ c1.session.queueDeclare("q", arg::durable=durableFlag);
+ for (int i = 0; i < 5; i++) {
+ c1.session.messageTransfer(arg::content=makeMessage((boost::format("%1%_%2%") % "m" % (i+1)).str(), "q", durableFlag));
+ }
+ //receive but don't ack a message
+ LocalQueue lq;
+ SubscriptionSettings lqSettings(FlowControl::messageCredit(1));
+ lqSettings.autoAck = 0;
+ Subscription lqSub = c1.subs.subscribe(lq, "q", lqSettings);
+ c1.session.messageFlush("q");
+ Message received;
+ BOOST_CHECK(lq.get(received));
+ BOOST_CHECK_EQUAL(received.getData(), std::string("m_1"));
+
+ //add new node
+ cluster.add();
+
+ lqSub.release(lqSub.getUnaccepted());
+
+ //check state of queue on both nodes
+ vector<string> expected = list_of<string>("m_1")("m_2")("m_3")("m_4")("m_5");
+ Client c3(cluster[0], "c3");
+ BOOST_CHECK_EQUAL(browse(c3, "q", 5), expected);
+ Client c2(cluster[1], "c2");
+ BOOST_CHECK_EQUAL(browse(c2, "q", 5), expected);
+ }
+}
+
+
+// Browse for 1 message with byte credit, return true if a message was
+// received false if not.
+bool browseByteCredit(Client& c, const string& q, int n, Message& m) {
+ SubscriptionSettings browseSettings(
+ FlowControl(1, n, false), // 1 message, n bytes credit, no window
+ ACCEPT_MODE_NONE,
+ ACQUIRE_MODE_NOT_ACQUIRED,
+ 0 // No auto-ack.
+ );
+ LocalQueue lq;
+ Subscription s = c.subs.subscribe(lq, q, browseSettings);
+ c.session.messageFlush(arg::destination=q, arg::sync=true);
+ c.session.sync();
+ c.subs.getSubscription(q).cancel();
+ return lq.get(m, 0); // No timeout, flush should push message thru.
+}
+
+// Ensure cluster update preserves exact message size, use byte credt as test.
+QPID_AUTO_TEST_CASE(testExactByteCredit) {
+ ClusterFixture cluster(1, prepareArgs(), -1);
+ Client c0(cluster[0], "c0");
+ c0.session.queueDeclare("q");
+ c0.session.messageTransfer(arg::content=Message("MyMessage", "q"));
+ cluster.add();
+
+ int size=36; // Size of message on broker: headers+body
+ Client c1(cluster[1], "c1");
+ Message m;
+
+ // Ensure we get the message with exact credit.
+ BOOST_CHECK(browseByteCredit(c0, "q", size, m));
+ BOOST_CHECK(browseByteCredit(c1, "q", size, m));
+ // and not with one byte less.
+ BOOST_CHECK(!browseByteCredit(c0, "q", size-1, m));
+ BOOST_CHECK(!browseByteCredit(c1, "q", size-1, m));
+}
+
+// Test that consumer positions are updated correctly.
+// Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=541927
+//
+QPID_AUTO_TEST_CASE(testUpdateConsumerPosition) {
+ ClusterFixture::Args args;
+ prepareArgs(args, durableFlag);
+ ClusterFixture cluster(1, args, -1);
+ Client c0(cluster[0], "c0");
+
+ c0.session.queueDeclare("q", arg::durable=durableFlag);
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+ // Set the acquire mode to 'not-acquired' the consumer moves along the queue
+ // but does not acquire (remove) messages.
+ settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED;
+ Subscription s = c0.subs.subscribe(c0.lq, "q", settings);
+ c0.session.messageTransfer(arg::content=makeMessage("1", "q", durableFlag));
+ BOOST_CHECK_EQUAL("1", c0.lq.get(TIMEOUT).getData());
+
+ // Add another member, send/receive another message and acquire
+ // the messages. With the bug, this creates an inconsistency
+ // because the browse position was not updated to the new member.
+ cluster.add();
+ c0.session.messageTransfer(arg::content=makeMessage("2", "q", durableFlag));
+ BOOST_CHECK_EQUAL("2", c0.lq.get(TIMEOUT).getData());
+ s.acquire(s.getUnacquired());
+ s.accept(s.getUnaccepted());
+
+ // In the bug we now have 0 messages on cluster[0] and 1 message on cluster[1]
+ // Subscribing on cluster[1] provokes an error that shuts down cluster[0]
+ Client c1(cluster[1], "c1");
+ Subscription s1 = c1.subs.subscribe(c1.lq, "q"); // Default auto-ack=1
+ Message m;
+ BOOST_CHECK(!c1.lq.get(m, TIMEOUT/10));
+ BOOST_CHECK_EQUAL(c1.session.queueQuery("q").getMessageCount(), 0u);
+ BOOST_CHECK_EQUAL(c0.session.queueQuery("q").getMessageCount(), 0u);
+}
QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/cluster_test_scripts/README.txt b/cpp/src/tests/cluster_test_scripts/README.txt
new file mode 100644
index 0000000000..e861a2f397
--- /dev/null
+++ b/cpp/src/tests/cluster_test_scripts/README.txt
@@ -0,0 +1,20 @@
+Cluster test scripts.
+
+A set of scripts to start and stop cluster and test clients on
+multiple hosts using ssh.
+
+Pre-requisites: You must be
+ - set up for password-free ssh access to the test hosts.
+ - a member of the ais group on all the test hosts.
+
+Configuration:
+
+Copy defaults.sh to config.sh and edit the values as necessary.
+
+Test scripts:
+
+Test scripts use the functions in functions.sh to start & monitor
+cluster and clients.
+A test script can collect other scripts.
+
+
diff --git a/cpp/src/tests/cluster_test_scripts/cluster_check b/cpp/src/tests/cluster_test_scripts/cluster_check
new file mode 100755
index 0000000000..5cc872a921
--- /dev/null
+++ b/cpp/src/tests/cluster_test_scripts/cluster_check
@@ -0,0 +1,17 @@
+#!/bin/sh
+# Check that all members of a cluster are running
+
+source config.sh
+
+HOSTS=(`cat $CLUSTER_HOME/hosts`)
+PORTS=(`cat $CLUSTER_HOME/ports`)
+
+for ((i=0; i<${#HOSTS[*]}; ++i)); do
+ host=${HOSTS[$i]}
+ port=${PORTS[$i]}
+ ssh $host "$QPIDD -cp $port" > /dev/null || {
+ ret=1
+ echo "ERROR: broker not running $host:$port"
+ }
+done
+exit $ret
diff --git a/cpp/src/tests/cluster_test_scripts/cluster_start b/cpp/src/tests/cluster_test_scripts/cluster_start
new file mode 100755
index 0000000000..bde582ef2b
--- /dev/null
+++ b/cpp/src/tests/cluster_test_scripts/cluster_start
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Start a cluster
+#
+# Arguments: NAME HOST [host...]
+# Start a cluster called NAME with N nodes running on the given HOSTs
+# repeat the host name to run multiple brokers on one host. Use dynamic
+# ports.
+#
+# Log files, data directories and hosts/ports files are all stored under
+# $HOME/cluster_test/$NAME
+#
+
+source config.sh
+
+CLUSTER_NAME=`date +"${USER}_%F_%T"`
+HOSTS=($BROKER_HOSTS)
+for ((i = 0; i < ${#HOSTS[*]}; ++i)) ; do
+ host=${HOSTS[$i]}
+ datadir=$CLUSTER_HOME/broker$i
+ log=$datadir/qpidd.log
+ ssh $host "rm -rf $datadir; mkdir -p $datadir" || {
+ echo "ERROR: can't make data dir $datadir"; exit 1
+ }
+ port=`ssh $host "echo $QPIDD -dp0 --cluster-name=$CLUSTER_NAME \
+ --data-dir=$datadir \
+ --log-to-file=$log --log-prefix=broker$i \
+ $QPIDD_OPTS | newgrp ais"` || {
+ error "ERROR: can't start broker $i on $host"; exit 1;
+ }
+ PORTS="$PORTS $port"
+done
+
+echo "$BROKER_HOSTS" > $CLUSTER_HOME/hosts
+echo "$PORTS" > $CLUSTER_HOME/ports
+
+`dirname $0`/cluster_check $NAME
diff --git a/cpp/src/tests/cluster_test_scripts/cluster_stop b/cpp/src/tests/cluster_test_scripts/cluster_stop
new file mode 100755
index 0000000000..d4543d2d4a
--- /dev/null
+++ b/cpp/src/tests/cluster_test_scripts/cluster_stop
@@ -0,0 +1,18 @@
+#!/bin/sh
+# Stop the cluster.
+
+source config.sh
+
+HOSTS=(`cat $CLUSTER_HOME/hosts`)
+PORTS=(`cat $CLUSTER_HOME/ports`)
+
+for ((i=0; i<${#HOSTS[*]}; ++i)); do
+ host=${HOSTS[$i]}
+ port=${PORTS[$i]}
+ ssh $host "$QPIDD -qp $port" > /dev/null || {
+ ret=1
+ echo "ERROR: stopping broker at $host:$port"
+ }
+done
+
+exit $ret
diff --git a/cpp/src/tests/cluster_test_scripts/config_example.sh b/cpp/src/tests/cluster_test_scripts/config_example.sh
new file mode 100755
index 0000000000..fd5b800df7
--- /dev/null
+++ b/cpp/src/tests/cluster_test_scripts/config_example.sh
@@ -0,0 +1,25 @@
+# Cluster configuration.
+
+# All output stored under $HOME/$CLUSTER_HOME.
+CLUSTER_HOME=$HOME/cluster_test
+
+# Hosts where brokers will be run. Repeat hostname to run multiple brokers on 1 host.
+BROKER_HOSTS="mrg22 mrg23 mrg24 mrg25 mrg26"
+
+# Hosts where clients will be run.
+CLIENT_HOSTS="$BROKER_HOSTS"
+
+# Paths to executables
+QPIDD=qpidd
+PERFTEST=perftest
+
+# Directory containing tests
+TESTDIR=/usr/bin
+
+# Options for qpidd, must be sufficient to load the cluster plugin.
+# Scripts will add --cluster-name, --daemon, --port and --log-to-file options here.
+QPIDD_OPTS=" \
+--auth=no \
+--log-enable=notice+ \
+--log-enable=debug+:cluster \
+"
diff --git a/cpp/src/tests/cluster_test_scripts/perftest b/cpp/src/tests/cluster_test_scripts/perftest
new file mode 100755
index 0000000000..72c8268835
--- /dev/null
+++ b/cpp/src/tests/cluster_test_scripts/perftest
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Run a distributed perftest against a cluster.
+# Args: npubs nsubs [perftest-options]
+
+source config.sh
+
+NPUBS=${1:-4} ; shift
+NSUBS=${1:-4} ; shift
+OPTS="--npubs $NPUBS --nsubs $NSUBS $*"
+
+CLIENTS=($CLIENT_HOSTS)
+BROKERS=(`cat $CLUSTER_HOME/hosts`)
+PORTS=(`cat $CLUSTER_HOME/ports`)
+
+start() {
+ client=${CLIENTS[i % ${#CLIENTS[*]}]}
+ broker=${BROKERS[i % ${#BROKERS[*]}]}
+ port=${PORTS[i % ${#PORTS[*]}]}
+ ssh -n $client $PERFTEST $OPTS $* -b $broker -p $port &
+ PIDS="$PIDS $!"
+}
+
+ssh ${CLIENTS[0]} $PERFTEST $OPTS --setup -b ${BROKERS[0]} -p${PORTS[0]}
+for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done
+for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done
+ssh ${CLIENTS[0]} $PERFTEST $OPTS --control -b ${BROKERS[0]} -p${PORTS[0]}
+
+for pid in $PIDS; do
+ wait $pid || echo "ERROR: client process $pid failed"
+done
+
+`dirname $0`/cluster_check
+
+
diff --git a/cpp/src/tests/cluster_tests.fail b/cpp/src/tests/cluster_tests.fail
new file mode 100644
index 0000000000..139597f9cb
--- /dev/null
+++ b/cpp/src/tests/cluster_tests.fail
@@ -0,0 +1,2 @@
+
+
diff --git a/cpp/src/tests/cluster_tests.py b/cpp/src/tests/cluster_tests.py
new file mode 100755
index 0000000000..178271e977
--- /dev/null
+++ b/cpp/src/tests/cluster_tests.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python
+
+# 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.
+#
+
+import os, signal, sys, time, imp, re
+from qpid import datatypes, messaging
+from qpid.brokertest import *
+from qpid.harness import Skipped
+from qpid.messaging import Message
+from threading import Thread
+from logging import getLogger
+
+log = getLogger("qpid.cluster_tests")
+
+# Import scripts as modules
+qpid_cluster=import_script(checkenv("QPID_CLUSTER_EXEC"))
+
+class ShortTests(BrokerTest):
+ """Short cluster functionality tests."""
+
+ def test_message_replication(self):
+ """Test basic cluster message replication."""
+ # Start a cluster, send some messages to member 0.
+ cluster = self.cluster(2)
+ s0 = cluster[0].connect().session()
+ s0.sender("q; {create:always}").send(Message("x"))
+ s0.sender("q; {create:always}").send(Message("y"))
+ s0.connection.close()
+
+ # Verify messages available on member 1.
+ s1 = cluster[1].connect().session()
+ m = s1.receiver("q", capacity=1).fetch(timeout=1)
+ s1.acknowledge()
+ self.assertEqual("x", m.content)
+ s1.connection.close()
+
+ # Start member 2 and verify messages available.
+ s2 = cluster.start().connect().session()
+ m = s2.receiver("q", capacity=1).fetch(timeout=1)
+ s2.acknowledge()
+ self.assertEqual("y", m.content)
+ s2.connection.close()
+
+ def test_store_direct_update_match(self):
+ """Verify that brokers stores an identical message whether they receive it
+ direct from clients or during an update, no header or other differences"""
+ cluster = self.cluster(0, args=["--load-module", self.test_store_lib])
+ cluster.start(args=["--test-store-dump", "direct.dump"])
+ # Try messages with various headers
+ cluster[0].send_message("q", Message(durable=True, content="foobar",
+ subject="subject",
+ reply_to="reply_to",
+ properties={"n":10}))
+ # Try messages of different sizes
+ for size in range(0,10000,100):
+ cluster[0].send_message("q", Message(content="x"*size, durable=True))
+ # Try sending via named exchange
+ c = cluster[0].connect_old()
+ s = c.session(str(qpid.datatypes.uuid4()))
+ s.exchange_bind(exchange="amq.direct", binding_key="foo", queue="q")
+ props = s.delivery_properties(routing_key="foo", delivery_mode=2)
+ s.message_transfer(
+ destination="amq.direct",
+ message=qpid.datatypes.Message(props, "content"))
+
+ # Now update a new member and compare their dumps.
+ cluster.start(args=["--test-store-dump", "updatee.dump"])
+ assert file("direct.dump").read() == file("updatee.dump").read()
+ os.remove("direct.dump")
+ os.remove("updatee.dump")
+
+class LongTests(BrokerTest):
+ """Tests that can run for a long time if -DDURATION=<minutes> is set"""
+ def duration(self):
+ d = self.config.defines.get("DURATION")
+ if d: return float(d)*60
+ else: return 3 # Default is to be quick
+
+ def test_failover(self):
+ """Test fail-over during continuous send-receive with errors"""
+
+ # Original cluster will all be killed so expect exit with failure
+ cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL)
+ for b in cluster: ErrorGenerator(b)
+
+ # Start sender and receiver threads
+ cluster[0].declare_queue("test-queue")
+ sender = NumberedSender(cluster[1], 1000) # Max queue depth
+ receiver = NumberedReceiver(cluster[2], sender)
+ receiver.start()
+ sender.start()
+
+ # Kill original brokers, start new ones for the duration.
+ endtime = time.time() + self.duration()
+ i = 0
+ while time.time() < endtime:
+ cluster[i].kill()
+ i += 1
+ b = cluster.start(expect=EXPECT_EXIT_FAIL)
+ ErrorGenerator(b)
+ time.sleep(1)
+ sender.stop()
+ receiver.stop(sender.sent)
+ for i in range(i, len(cluster)): cluster[i].kill()
+
+
+class StoreTests(BrokerTest):
+ """
+ Cluster tests that can only be run if there is a store available.
+ """
+ def args(self):
+ assert BrokerTest.store_lib
+ return ["--load-module", BrokerTest.store_lib]
+
+ def test_store_loaded(self):
+ """Ensure we are indeed loading a working store"""
+ broker = self.broker(self.args(), name="recoverme", expect=EXPECT_EXIT_FAIL)
+ m = Message("x", durable=True)
+ broker.send_message("q", m)
+ broker.kill()
+ broker = self.broker(self.args(), name="recoverme")
+ self.assertEqual("x", broker.get_message("q").content)
+
+ def test_kill_restart(self):
+ """Verify we can kill/resetart a broker with store in a cluster"""
+ cluster = self.cluster(1, self.args())
+ cluster.start("restartme", expect=EXPECT_EXIT_FAIL).kill()
+
+ # Send a message, retrieve from the restarted broker
+ cluster[0].send_message("q", "x")
+ m = cluster.start("restartme").get_message("q")
+ self.assertEqual("x", m.content)
+
+ def test_persistent_restart(self):
+ """Verify persistent cluster shutdown/restart scenarios"""
+ cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"])
+ a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False)
+ c = cluster.start("c", expect=EXPECT_EXIT_FAIL, wait=True)
+ a.send_message("q", Message("1", durable=True))
+ # Kill & restart one member.
+ c.kill()
+ self.assertEqual(a.get_message("q").content, "1")
+ a.send_message("q", Message("2", durable=True))
+ c = cluster.start("c", expect=EXPECT_EXIT_OK)
+ self.assertEqual(c.get_message("q").content, "2")
+ # Shut down the entire cluster cleanly and bring it back up
+ a.send_message("q", Message("3", durable=True))
+ qpid_cluster.main(["qpid-cluster", "-kf", a.host_port()])
+ a = cluster.start("a", wait=False)
+ b = cluster.start("b", wait=False)
+ c = cluster.start("c", wait=True)
+ self.assertEqual(a.get_message("q").content, "3")
+
+ def test_persistent_partial_failure(self):
+ # Kill 2 members, shut down the last cleanly then restart
+ # Ensure we use the clean database
+ cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"])
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_FAIL, wait=False)
+ c = cluster.start("c", expect=EXPECT_EXIT_OK, wait=True)
+ a.send_message("q", Message("4", durable=True))
+ a.kill()
+ b.kill()
+ self.assertEqual(c.get_message("q").content, "4")
+ c.send_message("q", Message("clean", durable=True))
+ qpid_cluster.main(["qpid-cluster", "-kf", c.host_port()])
+ a = cluster.start("a", wait=False)
+ b = cluster.start("b", wait=False)
+ c = cluster.start("c", wait=True)
+ self.assertEqual(a.get_message("q").content, "clean")
+
+ def test_wrong_cluster_id(self):
+ # Start a cluster1 broker, then try to restart in cluster2
+ cluster1 = self.cluster(0, args=self.args())
+ a = cluster1.start("a", expect=EXPECT_EXIT_OK)
+ a.terminate()
+ cluster2 = self.cluster(1, args=self.args())
+ try:
+ a = cluster2.start("a", expect=EXPECT_EXIT_FAIL)
+ self.fail("Expected exception")
+ except: pass
+
+ def test_wrong_shutdown_id(self):
+ # Start 2 members and shut down.
+ cluster = self.cluster(0, args=self.args()+["--cluster-size=2"])
+ a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_OK, wait=False)
+ self.assertEqual(0, qpid_cluster.main(["qpid_cluster", "-kf", a.host_port()]))
+ self.assertEqual(a.wait(), 0)
+ self.assertEqual(b.wait(), 0)
+
+ # Restart with a different member and shut down.
+ a = cluster.start("a", expect=EXPECT_EXIT_OK, wait=False)
+ c = cluster.start("c", expect=EXPECT_EXIT_OK, wait=False)
+ self.assertEqual(0, qpid_cluster.main(["qpid_cluster", "-kf", a.host_port()]))
+ self.assertEqual(a.wait(), 0)
+ self.assertEqual(c.wait(), 0)
+
+ # Mix members from both shutdown events, they should fail
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_FAIL, wait=False)
+
+ def test_total_failure(self):
+ # Verify we abort with sutiable error message if no clean stores.
+ cluster = self.cluster(0, args=self.args()+["--cluster-size=2"])
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_FAIL, wait=True)
+ a.kill()
+ b.kill()
+ a = cluster.start("a", expect=EXPECT_EXIT_FAIL, wait=False)
+ b = cluster.start("b", expect=EXPECT_EXIT_FAIL, wait=False)
+ assert a.wait() != 0
+ assert b.wait() != 0
+ msg = re.compile("critical.*no clean store")
+ assert msg.search(file(a.log).read())
+ assert msg.search(file(b.log).read())
diff --git a/cpp/src/tests/clustered_replication_test b/cpp/src/tests/clustered_replication_test
new file mode 100755
index 0000000000..174f93382d
--- /dev/null
+++ b/cpp/src/tests/clustered_replication_test
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Test reliability of the replication feature in the face of link
+# failures:
+source ./test_env.sh
+
+trap stop_brokers INT EXIT
+
+fail() {
+ echo $1
+ exit 1
+}
+
+stop_brokers() {
+ if [[ $PRIMARY1 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $PRIMARY1
+ unset PRIMARY1
+ fi
+ if [[ $PRIMARY2 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $PRIMARY2
+ unset PRIMARY2
+ fi
+ if [[ $DR1 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $DR1
+ unset DR1
+ fi
+ if [[ $DR2 ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $DR2
+ unset DR2
+ fi
+}
+
+if test -d $PYTHON_DIR; then
+ . $srcdir/ais_check
+
+ #todo: these cluster names need to be unique to prevent clashes
+ PRIMARY_CLUSTER=PRIMARY_$(hostname)_$$
+ DR_CLUSTER=DR_$(hostname)_$$
+
+ GENERAL_OPTS="--auth no --no-module-dir --no-data-dir --daemon --port 0 --log-to-stderr false"
+ PRIMARY_OPTS="--load-module $REPLICATING_LISTENER_LIB --create-replication-queue true --replication-queue REPLICATION_QUEUE --load-module $CLUSTER_LIB --cluster-name $PRIMARY_CLUSTER"
+ DR_OPTS="--load-module $REPLICATION_EXCHANGE_LIB --load-module $CLUSTER_LIB --cluster-name $DR_CLUSTER"
+
+ rm -f repl*.tmp #cleanup any files left from previous run
+
+ #start first node of primary cluster and set up test queue
+ echo Starting primary cluster
+ PRIMARY1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $PRIMARY_OPTS --log-to-file repl.primary.1.tmp) || fail "Could not start PRIMARY1"
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$PRIMARY1" add queue test-queue --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$PRIMARY1" add queue control-queue --generate-queue-events 1
+
+ #send 10 messages, consume 5 of them
+ for i in `seq 1 10`; do echo Message$i; done | ./sender --port $PRIMARY1
+ ./receiver --port $PRIMARY1 --messages 5 > /dev/null
+
+ #add new node to primary cluster, testing correct transfer of state:
+ echo Adding node to primary cluster
+ PRIMARY2=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $PRIMARY_OPTS --log-to-file repl.primary.2.tmp) || fail "Could not start PRIMARY2 "
+
+ #start DR cluster, set up test queue there and establish replication bridge
+ echo Starting DR cluster
+ DR1=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.1.tmp) || fail "Could not start DR1"
+ DR2=$(with_ais_group $QPIDD_EXEC $GENERAL_OPTS $DR_OPTS --log-to-file repl.dr.2.tmp) || fail "Could not start DR2"
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add queue test-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add queue control-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$DR1" add exchange replication REPLICATION_EXCHANGE
+ $PYTHON_COMMANDS/qpid-route queue add localhost:$DR2 localhost:$PRIMARY2 REPLICATION_EXCHANGE REPLICATION_QUEUE
+
+ #send more messages to primary
+ for i in `seq 11 20`; do echo Message$i; done | ./sender --port $PRIMARY1 --send-eos 1
+
+ #wait for replication events to all be processed:
+ echo Waiting for replication to complete
+ echo Done | ./sender --port $PRIMARY1 --routing-key control-queue --send-eos 1
+ ./receiver --queue control-queue --port $DR1 > /dev/null
+
+ #verify contents of test queue on dr cluster:
+ echo Verifying...
+ ./receiver --port $DR2 > repl.out.tmp
+ for i in `seq 6 20`; do echo Message$i; done | diff repl.out.tmp - || FAIL=1
+
+ if [[ $FAIL ]]; then
+ echo Clustered replication test failed: expectations not met!
+ exit 1
+ else
+ echo Clustered replication test passed
+ rm -f repl*.tmp
+ fi
+
+fi
diff --git a/cpp/src/tests/config.null b/cpp/src/tests/config.null
new file mode 100644
index 0000000000..565c7da435
--- /dev/null
+++ b/cpp/src/tests/config.null
@@ -0,0 +1 @@
+# empty config
diff --git a/cpp/src/tests/consume.cpp b/cpp/src/tests/consume.cpp
index fecf6bb315..69110d151f 100644
--- a/cpp/src/tests/consume.cpp
+++ b/cpp/src/tests/consume.cpp
@@ -7,9 +7,9 @@
* 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
@@ -34,32 +34,44 @@
using namespace qpid;
using namespace qpid::client;
using namespace qpid::sys;
-using std::string;
+using namespace std;
+
+namespace qpid {
+namespace tests {
-typedef std::vector<std::string> StringSet;
+typedef vector<string> StringSet;
struct Args : public qpid::TestOptions {
uint count;
uint ack;
string queue;
+ bool declare;
+ bool summary;
+ bool print;
+ bool durable;
- Args() : count(0), ack(1)
+ Args() : count(1000), ack(0), queue("publish-consume"),
+ declare(false), summary(false), print(false)
{
addOptions()
("count", optValue(count, "N"), "number of messages to publish")
("ack-frequency", optValue(ack, "N"), "ack every N messages (0 means use no-ack mode)")
- ("queue", optValue(queue, "<queue name>"), "queue to consume from");
+ ("queue", optValue(queue, "<queue name>"), "queue to consume from")
+ ("declare", optValue(declare), "declare the queue")
+ ("durable", optValue(durable), "declare the queue durable, use with declare")
+ ("print-data", optValue(print), "Print the recieved data at info level")
+ ("s,summary", optValue(summary), "Print undecorated rate.");
}
};
Args opts;
-struct Client
+struct Client
{
Connection connection;
Session session;
- Client()
+ Client()
{
opts.open(connection);
session = connection.newSession();
@@ -67,33 +79,44 @@ struct Client
void consume()
{
-
+ if (opts.declare)
+ session.queueDeclare(arg::queue=opts.queue, arg::durable=opts.durable);
SubscriptionManager subs(session);
- LocalQueue lq(AckPolicy(opts.ack));
- subs.setAcceptMode(opts.ack > 0 ? 0 : 1);
- subs.setFlowControl(opts.count, SubscriptionManager::UNLIMITED,
- false);
- subs.subscribe(lq, opts.queue);
+ LocalQueue lq;
+ SubscriptionSettings settings;
+ settings.acceptMode = opts.ack > 0 ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE;
+ settings.flowControl = FlowControl(opts.count, SubscriptionManager::UNLIMITED,false);
+ Subscription sub = subs.subscribe(lq, opts.queue, settings);
Message msg;
+ AbsTime begin=now();
for (size_t i = 0; i < opts.count; ++i) {
msg=lq.pop();
- std::cout << "Received: " << msg.getMessageProperties().getCorrelationId() << std::endl;
+ QPID_LOG(info, "Received: " << msg.getMessageProperties().getCorrelationId());
+ if (opts.print) QPID_LOG(info, "Data: " << msg.getData());
}
if (opts.ack != 0)
- subs.getAckPolicy().ackOutstanding(session); // Cumulative ack for final batch.
+ sub.accept(sub.getUnaccepted()); // Cumulative ack for final batch.
+ AbsTime end=now();
+ double secs(double(Duration(begin,end))/TIME_SEC);
+ if (opts.summary) cout << opts.count/secs << endl;
+ else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl;
}
- ~Client()
+ ~Client()
{
try{
session.close();
connection.close();
- } catch(const std::exception& e) {
- std::cout << e.what() << std::endl;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
}
}
};
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char** argv)
{
try {
@@ -101,8 +124,8 @@ int main(int argc, char** argv)
Client client;
client.consume();
return 0;
- } catch(const std::exception& e) {
- std::cout << e.what() << std::endl;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
}
return 1;
}
diff --git a/cpp/src/tests/datagen.cpp b/cpp/src/tests/datagen.cpp
new file mode 100644
index 0000000000..acbc07d63c
--- /dev/null
+++ b/cpp/src/tests/datagen.cpp
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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 <exception>
+#include <iostream>
+#include <stdlib.h>
+#include <time.h>
+#include "qpid/Options.h"
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options
+{
+ uint count;
+ uint minSize;
+ uint maxSize;
+ uint minChar;
+ uint maxChar;
+ bool help;
+
+ Args() : qpid::Options("Random data generator"),
+ count(1), minSize(8), maxSize(4096),
+ minChar(32), maxChar(126),//safely printable ascii chars
+ help(false)
+ {
+ addOptions()
+ ("count", qpid::optValue(count, "N"), "number of data strings to generate")
+ ("min-size", qpid::optValue(minSize, "N"), "minimum size of data string")
+ ("max-size", qpid::optValue(maxSize, "N"), "maximum size of data string")
+ ("min-char", qpid::optValue(minChar, "N"), "minimum char value used in data string")
+ ("max-char", qpid::optValue(maxChar, "N"), "maximum char value used in data string")
+ ("help", qpid::optValue(help), "print this usage statement");
+ }
+
+ bool parse(int argc, char** argv) {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (maxSize < minSize) throw qpid::Options::Exception("max-size must be greater than min-size");
+ if (maxChar < minChar) throw qpid::Options::Exception("max-char must be greater than min-char");
+
+ if (help) {
+ std::cerr << *this << std::endl << std::endl;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ }
+ return false;
+ }
+
+};
+
+uint random(uint min, uint max)
+{
+ return (rand() % (max-min+1)) + min;
+}
+
+std::string generateData(uint size, uint min, uint max)
+{
+ std::string data;
+ for (uint i = 0; i < size; i++) {
+ data += (char) random(min, max);
+ }
+ return data;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ if (opts.parse(argc, argv)) {
+ srand(time(0));
+ for (uint i = 0; i < opts.count; i++) {
+ std::cout << generateData(random(opts.minSize, opts.maxSize), opts.minChar, opts.maxChar) << std::endl;
+ }
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/cpp/src/tests/declare_queues.cpp b/cpp/src/tests/declare_queues.cpp
new file mode 100644
index 0000000000..bf85b9c04b
--- /dev/null
+++ b/cpp/src/tests/declare_queues.cpp
@@ -0,0 +1,101 @@
+/*
+ *
+ * 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 <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Exception.h>
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+
+using namespace qpid::client;
+
+using namespace std;
+
+int
+main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+ if ( argc != 6 )
+ {
+ cerr << "Usage: declare_queues host port durability queue_name_prefix n_queues\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int durability = atoi(argv[3]);
+ char const * queue_name_prefix = argv[4];
+ int n_queues = atoi(argv[5]);
+
+ FailoverManager connection(settings);
+
+ int max_fail = 13;
+ for ( int i = 0; i < n_queues; ++ i ) {
+ stringstream queue_name;
+ queue_name << queue_name_prefix << '_' << i;
+
+ bool queue_created = false;
+ int failure_count;
+
+ // Any non-transport failure is Bad.
+ try
+ {
+ while ( ! queue_created ) {
+ Session session = connection.connect().newSession();
+ // TransportFailures aren't too bad -- they might happen because
+ // we are doing a cluster failover test. But if we get too many,
+ // we will still bug out.
+ failure_count = 0;
+ try {
+ if ( durability )
+ session.queueDeclare(arg::queue=queue_name.str(), arg::durable=true);
+ else
+ session.queueDeclare(arg::queue=queue_name.str());
+ queue_created = true;
+ cout << "declare_queues: Created queue " << queue_name.str() << endl;
+ }
+ catch ( const qpid::TransportFailure& ) {
+ if ( ++ failure_count >= max_fail ) {
+ cerr << "declare_queues failed: too many transport errors.\n";
+ cerr << " host: " << settings.host
+ << " port: " << settings.port << endl;
+ return 1;
+ }
+ qpid::sys::sleep ( 1 );
+ }
+ }
+ }
+ catch ( const exception & error) {
+ cerr << "declare_queues failed:" << error.what() << endl;
+ cerr << " host: " << settings.host
+ << " port: " << settings.port << endl;
+ return 1;
+ }
+ }
+}
+
+
+
+
+
diff --git a/cpp/src/tests/dlclose_noop.c b/cpp/src/tests/dlclose_noop.c
index ba2fa75891..b78cf486d8 100644
--- a/cpp/src/tests/dlclose_noop.c
+++ b/cpp/src/tests/dlclose_noop.c
@@ -26,5 +26,5 @@
*/
#include <stdio.h>
-void* dlclose(void* handle) {}
+void* dlclose(void* handle) { return 0; }
diff --git a/cpp/src/tests/echotest.cpp b/cpp/src/tests/echotest.cpp
new file mode 100644
index 0000000000..ab26dcf3fd
--- /dev/null
+++ b/cpp/src/tests/echotest.cpp
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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 <qpid/client/Connection.h>
+#include <qpid/client/SubscriptionManager.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/sys/Time.h>
+
+#include <iostream>
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options,
+ public qpid::client::ConnectionSettings
+{
+ bool help;
+ uint count;
+ uint size;
+ bool summary;
+
+ Args() : qpid::Options("Simple latency test optins"), help(false), count(20), size(0), summary()
+ {
+ using namespace qpid;
+ addOptions()
+ ("help", optValue(help), "Print this usage statement")
+ ("count", optValue(count, "N"), "Number of messages to send")
+ ("size", optValue(count, "N"), "Size of messages")
+ ("broker,b", optValue(host, "HOST"), "Broker host to connect to")
+ ("port,p", optValue(port, "PORT"), "Broker port to connect to")
+ ("username", optValue(username, "USER"), "user name for broker log in.")
+ ("password", optValue(password, "PASSWORD"), "password for broker log in.")
+ ("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.")
+ ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay")
+ ("s,summary", optValue(summary), "Print only average latency.");
+ }
+};
+
+uint64_t current_time()
+{
+ Duration t(now());
+ return t;
+}
+
+class Listener : public MessageListener
+{
+ private:
+ Session session;
+ SubscriptionManager subscriptions;
+ uint counter;
+ const uint limit;
+ std::string queue;
+ Message request;
+ double total, min, max;
+ bool summary;
+
+ public:
+ Listener(Session& session, uint limit, bool summary);
+ void start(uint size);
+ void received(Message& message);
+};
+
+Listener::Listener(Session& s, uint l, bool summary_) :
+ session(s), subscriptions(s), counter(0), limit(l),
+ queue(session.getId().getName()), total(),
+ min(std::numeric_limits<double>::max()), max(), summary(summary_)
+{}
+
+void Listener::start(uint size)
+{
+ session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true);
+ request.getDeliveryProperties().setRoutingKey(queue);
+ subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE));
+
+ request.getDeliveryProperties().setTimestamp(current_time());
+ if (size) request.setData(std::string(size, 'X'));
+ async(session).messageTransfer(arg::content=request);
+ subscriptions.run();
+}
+
+void Listener::received(Message& response)
+{
+ //extract timestamp and compute latency:
+ uint64_t sentAt = response.getDeliveryProperties().getTimestamp();
+ uint64_t receivedAt = current_time();
+
+ double latency = ((double) (receivedAt - sentAt)) / TIME_MSEC;
+ if (!summary) cout << "Latency: " << latency << "ms" << endl;
+ min = std::min(latency, min);
+ max = std::max(latency, max);
+ total += latency;
+
+ if (++counter < limit) {
+ request.getDeliveryProperties().setTimestamp(current_time());
+ async(session).messageTransfer(arg::content=request);
+ } else {
+ subscriptions.cancel(queue);
+ if (summary) cout << min << "\t" << max << "\t" << total/limit << endl;
+ else cout << "min: " << min << " max: " << max << " average: " << total/limit << endl;
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ opts.parse(argc, argv);
+
+ if (opts.help) {
+ std::cout << opts << std::endl;
+ return 0;
+ }
+
+ Connection connection;
+ try {
+ connection.open(opts);
+ Session session = connection.newSession();
+ Listener listener(session, opts.count, opts.summary);
+ listener.start(opts.size);
+
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/cpp/src/tests/exception_test.cpp b/cpp/src/tests/exception_test.cpp
index 1cbe35fff4..4dac8ee965 100644
--- a/cpp/src/tests/exception_test.cpp
+++ b/cpp/src/tests/exception_test.cpp
@@ -7,9 +7,9 @@
* 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
@@ -20,12 +20,17 @@
*/
#include "unit_test.h"
+#include "test_tools.h"
#include "BrokerFixture.h"
#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/MessageListener.h"
#include "qpid/sys/Runnable.h"
#include "qpid/sys/Thread.h"
#include "qpid/framing/reply_exceptions.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(exception_test)
// FIXME aconway 2008-06-12: need to update our exception handling to
@@ -38,6 +43,7 @@ using namespace sys;
using namespace client;
using namespace framing;
+using qpid::broker::Broker;
using boost::bind;
using boost::function;
@@ -46,12 +52,15 @@ struct Catcher : public Runnable {
function<void ()> f;
bool caught;
Thread thread;
-
+
Catcher(function<void ()> f_) : f(f_), caught(false), thread(this) {}
~Catcher() { join(); }
-
+
void run() {
- try { f(); }
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f();
+ }
catch(const Ex& e) {
caught=true;
BOOST_MESSAGE(string("Caught expected exception: ")+e.what()+"["+typeid(e).name()+"]");
@@ -73,12 +82,21 @@ struct Catcher : public Runnable {
}
};
+QPID_AUTO_TEST_CASE(TestSessionBusy) {
+ SessionFixture f;
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.connection.newSession(f.session.getId().getName());
+ BOOST_FAIL("Expected SessionBusyException for " << f.session.getId().getName());
+ } catch (const SessionBusyException&) {} // FIXME aconway 2008-09-22: client is not throwing correct exception.
+}
+
QPID_AUTO_TEST_CASE(DisconnectedPop) {
ProxySessionFixture fix;
- ProxyConnection c(fix.broker->getPort());
+ ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT));
fix.session.queueDeclare(arg::queue="q");
fix.subs.subscribe(fix.lq, "q");
- Catcher<Exception> pop(bind(&LocalQueue::pop, boost::ref(fix.lq)));
+ Catcher<TransportFailure> pop(bind(&LocalQueue::pop, &fix.lq, sys::TIME_SEC));
fix.connection.proxy.close();
BOOST_CHECK(pop.join());
}
@@ -88,18 +106,22 @@ QPID_AUTO_TEST_CASE(DisconnectedListen) {
struct NullListener : public MessageListener {
void received(Message&) { BOOST_FAIL("Unexpected message"); }
} l;
- ProxyConnection c(fix.broker->getPort());
+ ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT));
fix.session.queueDeclare(arg::queue="q");
fix.subs.subscribe(l, "q");
- Thread t(fix.subs);
+
+ Catcher<TransportFailure> runner(bind(&SubscriptionManager::run, boost::ref(fix.subs)));
fix.connection.proxy.close();
- t.join();
- BOOST_CHECK_THROW(fix.session.close(), ConnectionException);
+ runner.join();
+ BOOST_CHECK_THROW(fix.session.queueDeclare(arg::queue="x"), TransportFailure);
}
QPID_AUTO_TEST_CASE(NoSuchQueueTest) {
ProxySessionFixture fix;
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
BOOST_CHECK_THROW(fix.subs.subscribe(fix.lq, "no such queue"), NotFoundException);
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/failover_soak.cpp b/cpp/src/tests/failover_soak.cpp
new file mode 100644
index 0000000000..ed5c9cbee5
--- /dev/null
+++ b/cpp/src/tests/failover_soak.cpp
@@ -0,0 +1,826 @@
+/*
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include <boost/assign.hpp>
+
+#include "qpid/framing/Uuid.h"
+
+#include <ForkedBroker.h>
+#include <qpid/client/Connection.h>
+
+
+
+
+
+using namespace std;
+using boost::assign::list_of;
+using namespace qpid::framing;
+using namespace qpid::client;
+
+
+namespace qpid {
+namespace tests {
+
+typedef vector<ForkedBroker *> brokerVector;
+
+typedef enum
+{
+ NO_STATUS,
+ RUNNING,
+ COMPLETED
+}
+childStatus;
+
+
+typedef enum
+{
+ NO_TYPE,
+ DECLARING_CLIENT,
+ SENDING_CLIENT,
+ RECEIVING_CLIENT
+}
+childType;
+
+
+ostream& operator<< ( ostream& os, const childType& ct ) {
+ switch ( ct ) {
+ case DECLARING_CLIENT: os << "Declaring Client"; break;
+ case SENDING_CLIENT: os << "Sending Client"; break;
+ case RECEIVING_CLIENT: os << "Receiving Client"; break;
+ default: os << "No Client"; break;
+ }
+
+ return os;
+}
+
+
+
+
+struct child
+{
+ child ( string & name, pid_t pid, childType type )
+ : name(name), pid(pid), retval(-999), status(RUNNING), type(type)
+ {
+ gettimeofday ( & startTime, 0 );
+ }
+
+
+ void
+ done ( int _retval )
+ {
+ retval = _retval;
+ status = COMPLETED;
+ gettimeofday ( & stopTime, 0 );
+ }
+
+
+ void
+ setType ( childType t )
+ {
+ type = t;
+ }
+
+
+ string name;
+ pid_t pid;
+ int retval;
+ childStatus status;
+ childType type;
+ struct timeval startTime,
+ stopTime;
+};
+
+
+
+
+struct children : public vector<child *>
+{
+
+ void
+ add ( string & name, pid_t pid, childType type )
+ {
+ push_back ( new child ( name, pid, type ) );
+ }
+
+
+ child *
+ get ( pid_t pid )
+ {
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ if ( pid == (*i)->pid )
+ return *i;
+
+ return 0;
+ }
+
+
+ void
+ exited ( pid_t pid, int retval )
+ {
+ child * kid = get ( pid );
+ if(! kid)
+ {
+ if ( verbosity > 1 )
+ {
+ cerr << "children::exited warning: Can't find child with pid "
+ << pid
+ << endl;
+ }
+ return;
+ }
+
+ kid->done ( retval );
+ }
+
+
+ int
+ unfinished ( )
+ {
+ int count = 0;
+
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ if ( COMPLETED != (*i)->status )
+ ++ count;
+
+ return count;
+ }
+
+
+ int
+ checkChildren ( )
+ {
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ if ( (COMPLETED == (*i)->status) && (0 != (*i)->retval) )
+ {
+ cerr << "checkChildren: error on child of type "
+ << (*i)->type
+ << endl;
+ return (*i)->retval;
+ }
+
+ return 0;
+ }
+
+
+ void
+ killEverybody ( )
+ {
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ kill ( (*i)->pid, 9 );
+ }
+
+
+
+ void
+ print ( )
+ {
+ cout << "--- status of all children --------------\n";
+ vector<child *>::iterator i;
+ for ( i = begin(); i != end(); ++ i )
+ cout << "child: " << (*i)->name
+ << " status: " << (*i)->status
+ << endl;
+ cout << "\n\n\n\n";
+ }
+
+ int verbosity;
+};
+
+
+children allMyChildren;
+
+
+void
+childExit ( int )
+{
+ int childReturnCode;
+ pid_t pid = waitpid ( 0, & childReturnCode, WNOHANG);
+
+ if ( pid > 0 )
+ allMyChildren.exited ( pid, childReturnCode );
+}
+
+
+
+int
+mrand ( int maxDesiredVal ) {
+ double zeroToOne = (double) rand() / (double) RAND_MAX;
+ return (int) (zeroToOne * (double) maxDesiredVal);
+}
+
+
+
+int
+mrand ( int minDesiredVal, int maxDesiredVal ) {
+ int interval = maxDesiredVal - minDesiredVal;
+ return minDesiredVal + mrand ( interval );
+}
+
+
+
+void
+makeClusterName ( string & s ) {
+ stringstream ss;
+ ss << "soakTestCluster_" << Uuid(true).str();
+ s = ss.str();
+}
+
+
+
+
+
+void
+printBrokers ( brokerVector & brokers )
+{
+ cout << "Broker List ------------ size: " << brokers.size() << "\n";
+ for ( brokerVector::iterator i = brokers.begin(); i != brokers.end(); ++ i) {
+ cout << "pid: "
+ << (*i)->getPID()
+ << " port: "
+ << (*i)->getPort()
+ << endl;
+ }
+ cout << "end Broker List ------------\n";
+}
+
+
+
+
+ForkedBroker * newbie = 0;
+int newbie_port = 0;
+
+
+
+bool
+wait_for_newbie ( )
+{
+ if ( ! newbie )
+ return true;
+
+ try
+ {
+ Connection connection;
+ connection.open ( "127.0.0.1", newbie_port );
+ connection.close();
+ newbie = 0; // He's no newbie anymore!
+ return true;
+ }
+ catch ( const std::exception& error )
+ {
+ std::cerr << "wait_for_newbie error: "
+ << error.what()
+ << endl;
+ return false;
+ }
+}
+
+bool endsWith(const char* str, const char* suffix) {
+ return (strlen(suffix) < strlen(str) && 0 == strcmp(str+strlen(str)-strlen(suffix), suffix));
+}
+
+
+void
+startNewBroker ( brokerVector & brokers,
+ char const * moduleOrDir,
+ string const clusterName,
+ int verbosity,
+ int durable )
+{
+ static int brokerId = 0;
+ stringstream path, prefix;
+ prefix << "soak-" << brokerId;
+ std::vector<std::string> argv = list_of<string>
+ ("qpidd")
+ ("--cluster-name")(clusterName)
+ ("--auth=no")
+ ("--mgmt-enable=no")
+ ("--log-prefix")(prefix.str())
+ ("--log-to-file")(prefix.str()+".log")
+ ("--log-enable=notice+")
+ ("TMP_DATA_DIR");
+
+ if (endsWith(moduleOrDir, "cluster.so")) {
+ // Module path specified, load only that module.
+ argv.push_back(string("--load-module=")+moduleOrDir);
+ argv.push_back("--no-module-dir");
+ if ( durable ) {
+ std::cerr << "failover_soak warning: durable arg hass no effect. Use \"dir\" option of \"moduleOrDir\".\n";
+ }
+ }
+ else {
+ // Module directory specified, load all modules in dir.
+ argv.push_back(string("--module-dir=")+moduleOrDir);
+ }
+
+ newbie = new ForkedBroker (argv);
+ newbie_port = newbie->getPort();
+ ForkedBroker * broker = newbie;
+
+ if ( verbosity > 0 )
+ std::cerr << "new broker created: pid == "
+ << broker->getPID()
+ << " log-prefix == "
+ << "soak-" << brokerId
+ << endl;
+ brokers.push_back ( broker );
+
+ ++ brokerId;
+}
+
+
+
+
+
+bool
+killFrontBroker ( brokerVector & brokers, int verbosity )
+{
+ cerr << "killFrontBroker: waiting for newbie sync...\n";
+ if ( ! wait_for_newbie() )
+ return false;
+ cerr << "killFrontBroker: newbie synced.\n";
+
+ if ( verbosity > 0 )
+ cout << "killFrontBroker pid: " << brokers[0]->getPID() << " on port " << brokers[0]->getPort() << endl;
+ try { brokers[0]->kill(9); }
+ catch ( const exception& error ) {
+ if ( verbosity > 0 )
+ {
+ cout << "error killing broker: "
+ << error.what()
+ << endl;
+ }
+
+ return false;
+ }
+ delete brokers[0];
+ brokers.erase ( brokers.begin() );
+ return true;
+}
+
+
+
+
+
+/*
+ * The optional delay is to avoid killing newbie brokers that have just
+ * been added and are still in the process of updating. This causes
+ * spurious, test-generated errors that scare everybody.
+ */
+void
+killAllBrokers ( brokerVector & brokers, int delay )
+{
+ if ( delay > 0 )
+ {
+ std::cerr << "Killing all brokers after delay of " << delay << endl;
+ sleep ( delay );
+ }
+
+ for ( uint i = 0; i < brokers.size(); ++ i )
+ try { brokers[i]->kill(9); }
+ catch ( const exception& error )
+ {
+ std::cerr << "killAllBrokers Warning: exception during kill on broker "
+ << i
+ << " "
+ << error.what()
+ << endl;
+ }
+}
+
+
+
+
+
+pid_t
+runDeclareQueuesClient ( brokerVector brokers,
+ char const * host,
+ char const * path,
+ int verbosity,
+ int durable,
+ char const * queue_prefix,
+ int n_queues
+ )
+{
+ string name("declareQueues");
+ int port = brokers[0]->getPort ( );
+
+ if ( verbosity > 1 )
+ cout << "startDeclareQueuesClient: host: "
+ << host
+ << " port: "
+ << port
+ << endl;
+ stringstream portSs;
+ portSs << port;
+
+ vector<const char*> argv;
+ argv.push_back ( "declareQueues" );
+ argv.push_back ( host );
+ argv.push_back ( portSs.str().c_str() );
+ if ( durable )
+ argv.push_back ( "1" );
+ else
+ argv.push_back ( "0" );
+
+ argv.push_back ( queue_prefix );
+
+ char n_queues_str[20];
+ sprintf ( n_queues_str, "%d", n_queues );
+ argv.push_back ( n_queues_str );
+
+ argv.push_back ( 0 );
+ pid_t pid = fork();
+
+ if ( ! pid ) {
+ execv ( path, const_cast<char * const *>(&argv[0]) );
+ perror ( "error executing declareQueues: " );
+ return 0;
+ }
+
+ allMyChildren.add ( name, pid, DECLARING_CLIENT );
+ return pid;
+}
+
+
+
+
+
+pid_t
+startReceivingClient ( brokerVector brokers,
+ char const * host,
+ char const * receiverPath,
+ char const * reportFrequency,
+ int verbosity,
+ char const * queue_name
+ )
+{
+ string name("receiver");
+ int port = brokers[0]->getPort ( );
+
+ if ( verbosity > 1 )
+ cout << "startReceivingClient: port " << port << endl;
+
+ // verbosity has to be > 1 to let clients talk.
+ int client_verbosity = (verbosity > 1 ) ? 1 : 0;
+
+ char portStr[100];
+ char verbosityStr[100];
+ sprintf(portStr, "%d", port);
+ sprintf(verbosityStr, "%d", client_verbosity);
+
+
+ vector<const char*> argv;
+ argv.push_back ( "resumingReceiver" );
+ argv.push_back ( host );
+ argv.push_back ( portStr );
+ argv.push_back ( reportFrequency );
+ argv.push_back ( verbosityStr );
+ argv.push_back ( queue_name );
+ argv.push_back ( 0 );
+
+ pid_t pid = fork();
+
+ if ( ! pid ) {
+ execv ( receiverPath, const_cast<char * const *>(&argv[0]) );
+ perror ( "error executing receiver: " );
+ return 0;
+ }
+
+ allMyChildren.add ( name, pid, RECEIVING_CLIENT );
+ return pid;
+}
+
+
+
+
+
+pid_t
+startSendingClient ( brokerVector brokers,
+ char const * host,
+ char const * senderPath,
+ char const * nMessages,
+ char const * reportFrequency,
+ int verbosity,
+ int durability,
+ char const * queue_name
+ )
+{
+ string name("sender");
+ int port = brokers[0]->getPort ( );
+
+ if ( verbosity > 1)
+ cout << "startSenderClient: port " << port << endl;
+ char portStr[100];
+ char verbosityStr[100];
+ //
+ // verbosity has to be > 1 to let clients talk.
+ int client_verbosity = (verbosity > 1 ) ? 1 : 0;
+
+ sprintf ( portStr, "%d", port);
+ sprintf ( verbosityStr, "%d", client_verbosity);
+
+ vector<const char*> argv;
+ argv.push_back ( "replayingSender" );
+ argv.push_back ( host );
+ argv.push_back ( portStr );
+ argv.push_back ( nMessages );
+ argv.push_back ( reportFrequency );
+ argv.push_back ( verbosityStr );
+ if ( durability )
+ argv.push_back ( "1" );
+ else
+ argv.push_back ( "0" );
+ argv.push_back ( queue_name );
+ argv.push_back ( 0 );
+
+ pid_t pid = fork();
+
+ if ( ! pid ) {
+ execv ( senderPath, const_cast<char * const *>(&argv[0]) );
+ perror ( "error executing sender: " );
+ return 0;
+ }
+
+ allMyChildren.add ( name, pid, SENDING_CLIENT );
+ return pid;
+}
+
+
+
+#define HUNKY_DORY 0
+#define BAD_ARGS 1
+#define CANT_FORK_DQ 2
+#define CANT_FORK_RECEIVER 3
+#define CANT_FORK_SENDER 4
+#define DQ_FAILED 5
+#define ERROR_ON_CHILD 6
+#define HANGING 7
+#define ERROR_KILLING_BROKER 8
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+// If you want durability, use the "dir" option of "moduleOrDir" .
+int
+main ( int argc, char const ** argv )
+{
+ if ( argc != 11 ) {
+ cerr << "Usage: "
+ << argv[0]
+ << "moduleOrDir declareQueuesPath senderPath receiverPath nMessages reportFrequency verbosity durable n_queues n_brokers"
+ << endl;
+ cerr << "\tverbosity is an integer, durable is 0 or 1\n";
+ return BAD_ARGS;
+ }
+ signal ( SIGCHLD, childExit );
+
+ int i = 1;
+ char const * moduleOrDir = argv[i++];
+ char const * declareQueuesPath = argv[i++];
+ char const * senderPath = argv[i++];
+ char const * receiverPath = argv[i++];
+ char const * nMessages = argv[i++];
+ char const * reportFrequency = argv[i++];
+ int verbosity = atoi(argv[i++]);
+ int durable = atoi(argv[i++]);
+ int n_queues = atoi(argv[i++]);
+ int n_brokers = atoi(argv[i++]);
+
+ char const * host = "127.0.0.1";
+ int maxBrokers = 50;
+
+ allMyChildren.verbosity = verbosity;
+
+ string clusterName;
+
+ srand ( getpid() );
+
+ makeClusterName ( clusterName );
+
+ brokerVector brokers;
+
+ if ( verbosity > 1 )
+ cout << "Starting initial cluster...\n";
+
+ for ( int i = 0; i < n_brokers; ++ i ) {
+ startNewBroker ( brokers,
+ moduleOrDir,
+ clusterName,
+ verbosity,
+ durable );
+ }
+
+
+ if ( verbosity > 0 )
+ printBrokers ( brokers );
+
+ // Get prefix for each queue name.
+ stringstream queue_prefix;
+ queue_prefix << "failover_soak_" << getpid();
+
+
+ // Run the declareQueues child.
+ int childStatus;
+ pid_t dqClientPid =
+ runDeclareQueuesClient ( brokers,
+ host,
+ declareQueuesPath,
+ verbosity,
+ durable,
+ queue_prefix.str().c_str(),
+ n_queues
+ );
+ if ( -1 == dqClientPid ) {
+ cerr << "END_OF_TEST ERROR_START_DECLARE_1\n";
+ return CANT_FORK_DQ;
+ }
+
+ // Don't continue until declareQueues is finished.
+ pid_t retval = waitpid ( dqClientPid, & childStatus, 0);
+ if ( retval != dqClientPid) {
+ cerr << "END_OF_TEST ERROR_START_DECLARE_2\n";
+ return DQ_FAILED;
+ }
+ allMyChildren.exited ( dqClientPid, childStatus );
+
+
+ /*
+ Start one receiving and one sending client for each queue.
+ */
+ for ( int i = 0; i < n_queues; ++ i ) {
+
+ stringstream queue_name;
+ queue_name << queue_prefix.str() << '_' << i;
+
+ // Receiving client ---------------------------
+ pid_t receivingClientPid =
+ startReceivingClient ( brokers,
+ host,
+ receiverPath,
+ reportFrequency,
+ verbosity,
+ queue_name.str().c_str() );
+ if ( -1 == receivingClientPid ) {
+ cerr << "END_OF_TEST ERROR_START_RECEIVER\n";
+ return CANT_FORK_RECEIVER;
+ }
+
+
+ // Sending client ---------------------------
+ pid_t sendingClientPid =
+ startSendingClient ( brokers,
+ host,
+ senderPath,
+ nMessages,
+ reportFrequency,
+ verbosity,
+ durable,
+ queue_name.str().c_str() );
+ if ( -1 == sendingClientPid ) {
+ cerr << "END_OF_TEST ERROR_START_SENDER\n";
+ return CANT_FORK_SENDER;
+ }
+ }
+
+
+ int minSleep = 2,
+ maxSleep = 4;
+
+
+ for ( int totalBrokers = n_brokers;
+ totalBrokers < maxBrokers;
+ ++ totalBrokers
+ )
+ {
+ if ( verbosity > 0 )
+ cout << totalBrokers << " brokers have been added to the cluster.\n\n\n";
+
+ // Sleep for a while. -------------------------
+ int sleepyTime = mrand ( minSleep, maxSleep );
+ if ( verbosity > 0 )
+ cout << "Sleeping for " << sleepyTime << " seconds.\n";
+ sleep ( sleepyTime );
+
+ // Kill the oldest broker. --------------------------
+ if ( ! killFrontBroker ( brokers, verbosity ) )
+ {
+ allMyChildren.killEverybody();
+ killAllBrokers ( brokers, 5 );
+ std::cerr << "END_OF_TEST ERROR_BROKER\n";
+ return ERROR_KILLING_BROKER;
+ }
+
+ // Sleep for a while. -------------------------
+ sleepyTime = mrand ( minSleep, maxSleep );
+ if ( verbosity > 1 )
+ cerr << "Sleeping for " << sleepyTime << " seconds.\n";
+ sleep ( sleepyTime );
+
+ // Start a new broker. --------------------------
+ if ( verbosity > 0 )
+ cout << "Starting new broker.\n\n";
+
+ startNewBroker ( brokers,
+ moduleOrDir,
+ clusterName,
+ verbosity,
+ durable );
+
+ if ( verbosity > 1 )
+ printBrokers ( brokers );
+
+ // If all children have exited, quit.
+ int unfinished = allMyChildren.unfinished();
+ if ( ! unfinished ) {
+ killAllBrokers ( brokers, 5 );
+
+ if ( verbosity > 1 )
+ cout << "failoverSoak: all children have exited.\n";
+ int retval = allMyChildren.checkChildren();
+ if ( verbosity > 1 )
+ std::cerr << "failoverSoak: checkChildren: " << retval << endl;
+
+ if ( retval )
+ {
+ std::cerr << "END_OF_TEST ERROR_CLIENT\n";
+ return ERROR_ON_CHILD;
+ }
+ else
+ {
+ std::cerr << "END_OF_TEST SUCCESSFUL\n";
+ return HUNKY_DORY;
+ }
+ }
+
+ // Even if some are still running, if there's an error, quit.
+ if ( allMyChildren.checkChildren() )
+ {
+ if ( verbosity > 0 )
+ cout << "failoverSoak: error on child.\n";
+ allMyChildren.killEverybody();
+ killAllBrokers ( brokers, 5 );
+ std::cerr << "END_OF_TEST ERROR_CLIENT\n";
+ return ERROR_ON_CHILD;
+ }
+
+ if ( verbosity > 1 ) {
+ std::cerr << "------- next kill-broker loop --------\n";
+ allMyChildren.print();
+ }
+ }
+
+ retval = allMyChildren.checkChildren();
+ if ( verbosity > 1 )
+ std::cerr << "failoverSoak: checkChildren: " << retval << endl;
+
+ if ( verbosity > 1 )
+ cout << "failoverSoak: maxBrokers reached.\n";
+
+ allMyChildren.killEverybody();
+ killAllBrokers ( brokers, 5 );
+
+ std::cerr << "END_OF_TEST SUCCESSFUL\n";
+
+ return retval ? ERROR_ON_CHILD : HUNKY_DORY;
+}
+
+
+
diff --git a/cpp/src/tests/fanout_perftest b/cpp/src/tests/fanout_perftest
index a71a748232..d8a7661f49 100755
--- a/cpp/src/tests/fanout_perftest
+++ b/cpp/src/tests/fanout_perftest
@@ -1,2 +1,22 @@
#!/bin/sh
+
+#
+# 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.
+#
+
exec `dirname $0`/run_perftest 10000 --mode fanout --npubs 16 --nsubs 16 --size 64
diff --git a/cpp/src/tests/federated_cluster_test b/cpp/src/tests/federated_cluster_test
new file mode 100755
index 0000000000..70bec5e703
--- /dev/null
+++ b/cpp/src/tests/federated_cluster_test
@@ -0,0 +1,152 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Test reliability of the replication feature in the face of link
+# failures:
+srcdir=`dirname $0`
+source ./test_env.sh
+
+trap stop_brokers EXIT
+
+fail() {
+ echo $1
+ exit 1
+}
+
+stop_brokers() {
+ if [[ $BROKER_A ]] ; then
+ ../qpidd --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+ fi
+ if [[ $NODE_1 ]] ; then
+ ../qpidd --no-module-dir -q --port $NODE_1
+ unset NODE_1
+ fi
+ if [[ $NODE_2 ]] ; then
+ ../qpidd --no-module-dir -q --port $NODE_2
+ unset NODE_2
+ fi
+ if [ -f cluster.ports ]; then
+ rm cluster.ports
+ fi
+}
+
+start_brokers() {
+ #start single node...
+ BROKER_A=`../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no --log-enable info+` || fail "BROKER_A failed to start"
+
+ #...and start cluster
+ $srcdir/start_cluster 2 || fail "Could not start cluster"
+ NODE_1=$(head -1 cluster.ports)
+ NODE_2=$(tail -1 cluster.ports)
+ test -n "$NODE_1" || fail "NODE_1 failed to start"
+ test -n "$NODE_2" || fail "NODE_2 failed to start"
+}
+
+setup() {
+ #create exchange on both cluster and single broker
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add exchange direct test-exchange
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" add exchange direct test-exchange
+
+ #create dynamic routes for test exchange
+ $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$NODE_2" "localhost:$BROKER_A" test-exchange
+ $PYTHON_COMMANDS/qpid-route dynamic add "localhost:$BROKER_A" "localhost:$NODE_2" test-exchange
+
+ #create test queue on cluster and bind it to the test exchange
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" add queue test-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$NODE_1" bind test-exchange test-queue to-cluster
+
+ #create test queue on single broker and bind it to the test exchange
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue test-queue
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" bind test-exchange test-queue from-cluster
+}
+
+run_test_pull_to_cluster_two_consumers() {
+ #start consumers on each of the two nodes of the cluster
+ ./receiver --port $NODE_1 --queue test-queue --credit-window 1 > fed1.out.tmp &
+ ./receiver --port $NODE_2 --queue test-queue --credit-window 1 > fed2.out.tmp &
+
+ #send stream of messages to test exchange on single broker
+ for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
+ ./sender --port $BROKER_A --exchange test-exchange --routing-key to-cluster --send-eos 2 < fed.in.tmp
+
+ #combine output of the two consumers, sort it and compare with the expected stream
+ wait
+ sort -g -k 2 fed1.out.tmp fed2.out.tmp > fed.out.tmp
+ diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
+
+ rm -f fed*.tmp #cleanup
+}
+
+run_test_pull_to_cluster() {
+ #send stream of messages to test exchange on single broker
+ for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
+ ./sender --port $BROKER_A --exchange test-exchange --routing-key to-cluster --send-eos 1 < fed.in.tmp
+
+ #consume from remaining node of the cluster
+ ./receiver --port $NODE_2 --queue test-queue > fed.out.tmp
+
+ #verify all messages are received
+ diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
+
+ rm -f fed*.tmp #cleanup
+}
+
+run_test_pull_from_cluster() {
+ #start consumer on single broker
+ ./receiver --port $BROKER_A --queue test-queue --credit-window 1 > fed.out.tmp &
+
+ #send stream of messages to test exchange on cluster
+ for i in `seq 1 1000`; do echo Message $i >> fed.in.tmp; done
+ ./sender --port $NODE_2 --exchange test-exchange --routing-key from-cluster --send-eos 1 < fed.in.tmp
+
+ #verify all messages are received
+ wait
+ diff fed.in.tmp fed.out.tmp || fail "federated link from cluster failed: expectations not met!"
+
+ rm -f fed*.tmp #cleanup
+}
+
+
+if test -d ${PYTHON_DIR}; then
+ . $srcdir/ais_check
+
+ rm -f fed*.tmp #cleanup any files left from previous run
+ start_brokers
+ echo "brokers started"
+ setup
+ echo "setup completed"
+ run_test_pull_to_cluster_two_consumers
+ echo "federated link to cluster verified"
+ run_test_pull_from_cluster
+ echo "federated link from cluster verified"
+ if [[ $TEST_NODE_FAILURE ]] ; then
+ #kill first cluster node and retest
+ kill -9 $(../qpidd --check --port $NODE_1) && unset NODE_1
+ echo "killed first cluster node; waiting for links to re-establish themselves..."
+ sleep 5
+ echo "retesting..."
+ run_test_pull_to_cluster
+ echo "federated link to cluster verified"
+ run_test_pull_from_cluster
+ echo "federated link from cluster verified"
+ fi
+fi
diff --git a/cpp/src/tests/federated_cluster_test_with_node_failure b/cpp/src/tests/federated_cluster_test_with_node_failure
new file mode 100755
index 0000000000..f144a676de
--- /dev/null
+++ b/cpp/src/tests/federated_cluster_test_with_node_failure
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+srcdir=`dirname $0`
+TEST_NODE_FAILURE=1 $srcdir/federated_cluster_test
diff --git a/cpp/src/tests/federated_topic_test b/cpp/src/tests/federated_topic_test
index dbb4a2a133..b1063c7e8c 100755
--- a/cpp/src/tests/federated_topic_test
+++ b/cpp/src/tests/federated_topic_test
@@ -1,4 +1,24 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Run the topic test on a federated setup
# Clean up old log files
@@ -8,6 +28,7 @@ rm -f subscriber_*.log
SUBSCRIBERS=2
MESSAGES=1000
BATCHES=1
+VERBOSE=1
while getopts "s:m:b:" opt ; do
case $opt in
@@ -22,23 +43,27 @@ while getopts "s:m:b:" opt ; do
done
MY_DIR=$(dirname $(which $0))
-PYTHON_DIR=${MY_DIR}/../../../python
+source ./test_env.sh
trap stop_brokers EXIT
-start_brokers() {
+start_broker() {
${MY_DIR}/../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no > qpidd.port
+}
+
+start_brokers() {
+ start_broker
PORT_A=`cat qpidd.port`
- ${MY_DIR}/../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no > qpidd.port
+ start_broker
PORT_B=`cat qpidd.port`
- ${MY_DIR}/../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no > qpidd.port
+ start_broker
PORT_C=`cat qpidd.port`
}
stop_brokers() {
- ${MY_DIR}/../qpidd -q --port $PORT_A
- ${MY_DIR}/../qpidd -q --port $PORT_B
- ${MY_DIR}/../qpidd -q --port $PORT_C
+ for p in $PORT_A $PORT_B $PORT_C; do
+ $QPIDD_EXEC --no-module-dir -q --port $p
+ done
}
subscribe() {
@@ -62,33 +87,43 @@ setup_routes() {
BROKER_A="localhost:$PORT_A"
BROKER_B="localhost:$PORT_B"
BROKER_C="localhost:$PORT_C"
- export PYTHONPATH=$PYTHON_DIR:$PYTHONPATH
- echo "Establishing routes for topic..."
- $PYTHON_DIR/commands/qpid-route add $BROKER_B $BROKER_A amq.topic topic_control B B
- $PYTHON_DIR/commands/qpid-route add $BROKER_C $BROKER_B amq.topic topic_control C C
- echo "linked A->B->C"
- $PYTHON_DIR/commands/qpid-route add $BROKER_B $BROKER_C amq.topic topic_control B B
- $PYTHON_DIR/commands/qpid-route add $BROKER_A $BROKER_B amq.topic topic_control A A
- echo "linked C->B->A"
-
- echo "Establishing routes for response queue..."
- $PYTHON_DIR/commands/qpid-route add $BROKER_B $BROKER_C amq.direct response B B
- $PYTHON_DIR/commands/qpid-route add $BROKER_A $BROKER_B amq.direct response A A
- echo "linked C->B->A"
+ if (($VERBOSE)); then
+ echo "Establishing routes for topic..."
+ fi
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_A amq.topic topic_control B B
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_C $BROKER_B amq.topic topic_control C C
+ if (($VERBOSE)); then
+ echo "linked A->B->C"
+ fi
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.topic topic_control B B
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.topic topic_control A A
+ if (($VERBOSE)); then
+ echo "linked C->B->A"
+ echo "Establishing routes for response queue..."
+ fi
+
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.direct response B B
+ $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.direct response A A
+ if (($VERBOSE)); then
+ echo "linked C->B->A"
+ for b in $BROKER_A $BROKER_B $BROKER_C; do
+ echo "Routes for $b"
+ $PYTHON_COMMANDS/qpid-route route list $b
+ done
+ fi
}
if test -d ${PYTHON_DIR} ; then
start_brokers
- echo "Running federated topic test against brokers on ports $PORT_A $PORT_B $PORT_C"
-
- setup_routes
+ if (($VERBOSE)); then
+ echo "Running federated topic test against brokers on ports $PORT_A $PORT_B $PORT_C"
+ fi
for ((i=$SUBSCRIBERS ; i--; )); do
subscribe $i &
done
- #sleep to give subscribers time to get initialised
- sleep 1
+ setup_routes
- publish 2>&1 || exit 1
+ publish || exit 1
fi
diff --git a/cpp/src/tests/federation.py b/cpp/src/tests/federation.py
index b92df89839..aa68e8198b 100755
--- a/cpp/src/tests/federation.py
+++ b/cpp/src/tests/federation.py
@@ -19,103 +19,65 @@
#
import sys
-from qpid.testlib import TestBase010, testrunner
-from qpid.management import managementChannel, managementClient
+from qpid.testlib import TestBase010
from qpid.datatypes import Message
from qpid.queue import Empty
from time import sleep
-def add_module(args=sys.argv[1:]):
- for a in args:
- if a.startswith("federation"):
- return False
- return True
-
-def scan_args(name, default=None, args=sys.argv[1:]):
- if (name in args):
- pos = args.index(name)
- return args[pos + 1]
- elif default:
- return default
- else:
- print "Please specify extra argument: %s" % name
- sys.exit(2)
-
-def extract_args(name, args):
- if (name in args):
- pos = args.index(name)
- del args[pos:pos+2]
- else:
- return None
-
-def remote_host():
- return scan_args("--remote-host", "localhost")
-
-def remote_port():
- return int(scan_args("--remote-port"))
+class FederationTests(TestBase010):
-class Helper:
- def __init__(self, parent):
- self.parent = parent
- self.session = parent.conn.session("Helper")
- self.mc = managementClient(self.session.spec)
- self.mch = self.mc.addChannel(self.session)
- self.mc.syncWaitForStable(self.mch)
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
- def shutdown (self):
- self.mc.removeChannel (self.mch)
+ def remote_port(self):
+ return int(self.defines["remote-port"])
- def get_objects(self, type):
- return self.mc.syncGetObjects(self.mch, type)
+ def verify_cleanup(self):
+ attempts = 0
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+ while total > 0:
+ attempts += 1
+ if attempts >= 10:
+ self.fail("Bridges and links didn't clean up")
+ return
+ sleep(1)
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
- def get_object(self, type, position = 1, expected = None):
- objects = self.get_objects(type)
- if not expected: expected = position
- self.assertEqual(len(objects), expected)
- return objects[(position - 1)]
+ def test_bridge_create_and_close(self):
+ self.startQmf();
+ qmf = self.qmf
-
- def call_method(self, object, method, args=None):
- res = self.mc.syncCallMethod(self.mch, object.id, object.classKey, method, args)
- self.assertEqual(res.status, 0)
- self.assertEqual(res.statusText, "OK")
- return res
-
- def assertEqual(self, a, b):
- self.parent.assertEqual(a, b)
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
-class FederationTests(TestBase010):
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.direct", "my-key", "", "", False, False, False, 0)
+ self.assertEqual(result.status, 0)
- def test_bridge_create_and_close(self):
- mgmt = Helper(self)
- broker = mgmt.get_object("broker")
-
- mgmt.call_method(broker, "connect", {"host":remote_host(), "port":remote_port()})
- link = mgmt.get_object("link")
-
- mgmt.call_method(link, "bridge", {"durable":0, "src":"amq.direct", "dest":"amq.direct", "key":"my-key"})
- bridge = mgmt.get_object("bridge")
-
- mgmt.call_method(bridge, "close")
- mgmt.call_method(link, "close")
+ bridge = qmf.getObjects(_class="bridge")[0]
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
- sleep(6)
- self.assertEqual(len(mgmt.get_objects("bridge")), 0)
- self.assertEqual(len(mgmt.get_objects("link")), 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
- mgmt.shutdown ()
+ self.verify_cleanup()
def test_pull_from_exchange(self):
session = self.session
- mgmt = Helper(self)
- broker = mgmt.get_object("broker")
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
- mgmt.call_method(broker, "connect", {"host":remote_host(), "port":remote_port()})
- link = mgmt.get_object("link")
-
- mgmt.call_method(link, "bridge", {"durable":0, "src":"amq.direct", "dest":"amq.fanout", "key":"my-key"})
- bridge = mgmt.get_object("bridge")
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, False, False, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
#setup queue to receive messages from local broker
session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
@@ -125,7 +87,7 @@ class FederationTests(TestBase010):
sleep(6)
#send messages to remote broker and confirm it is routed to local broker
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_pull_from_exchange")
for i in range(1, 11):
@@ -140,21 +102,64 @@ class FederationTests(TestBase010):
self.fail("Got unexpected message in queue: " + extra.body)
except Empty: None
- mgmt.call_method(bridge, "close")
- mgmt.call_method(link, "close")
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_push_to_exchange(self):
+ session = self.session
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, True, False, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+
+ #setup queue to receive messages from remote broker
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_push_to_exchange")
+ r_session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ r_session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(session=r_session, queue="fed1", destination="f1")
+ queue = r_session.incoming("f1")
sleep(6)
- self.assertEqual(len(mgmt.get_objects("bridge")), 0)
- self.assertEqual(len(mgmt.get_objects("link")), 0)
- mgmt.shutdown()
+ #send messages to local broker and confirm it is routed to remote broker
+ for i in range(1, 11):
+ dp = session.delivery_properties(routing_key="my-key")
+ session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
def test_pull_from_queue(self):
session = self.session
#setup queue on remote broker and add some messages
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_pull_from_queue")
- r_session.queue_declare(queue="my-bridge-queue", exclusive=True, auto_delete=True)
+ r_session.queue_declare(queue="my-bridge-queue", auto_delete=True)
for i in range(1, 6):
dp = r_session.delivery_properties(routing_key="my-bridge-queue")
r_session.message_transfer(message=Message(dp, "Message %d" % i))
@@ -165,16 +170,18 @@ class FederationTests(TestBase010):
self.subscribe(queue="fed1", destination="f1")
queue = session.incoming("f1")
- mgmt = Helper(self)
- broker = mgmt.get_object("broker")
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
- mgmt.call_method(broker, "connect", {"host":remote_host(), "port":remote_port()})
- link = mgmt.get_object("link")
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False, 1)
+ self.assertEqual(result.status, 0)
- mgmt.call_method(link, "bridge", {"durable":0, "src":"my-bridge-queue", "dest":"amq.fanout",
- "key":"", "tag":"", "excludes":"", "srcIsQueue":1})
- sleep(6)
- bridge = mgmt.get_object("bridge")
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(3)
#add some more messages (i.e. after bridge was created)
for i in range(6, 11):
@@ -192,37 +199,96 @@ class FederationTests(TestBase010):
self.fail("Got unexpected message in queue: " + extra.body)
except Empty: None
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
- mgmt.call_method(bridge, "close")
- mgmt.call_method(link, "close")
- sleep(6)
- self.assertEqual(len(mgmt.get_objects("bridge")), 0)
- self.assertEqual(len(mgmt.get_objects("link")), 0)
+ self.verify_cleanup()
- mgmt.shutdown ()
+ def test_tracing_automatic(self):
+ remoteUrl = "%s:%d" % (self.remote_host(), self.remote_port())
+ self.startQmf()
+ l_broker = self.qmf_broker
+ r_broker = self.qmf.addBroker(remoteUrl)
- def test_tracing(self):
+ l_brokerObj = self.qmf.getObjects(_class="broker", _broker=l_broker)[0]
+ r_brokerObj = self.qmf.getObjects(_class="broker", _broker=r_broker)[0]
+
+ l_res = l_brokerObj.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ r_res = r_brokerObj.connect(self.broker.host, self.broker.port, False, "PLAIN", "guest", "guest", "tcp")
+
+ self.assertEqual(l_res.status, 0)
+ self.assertEqual(r_res.status, 0)
+
+ l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0]
+ r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0]
+
+ l_res = l_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False, 0)
+ r_res = r_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False, 0)
+
+ self.assertEqual(l_res.status, 0)
+ self.assertEqual(r_res.status, 0)
+
+ count = 0
+ while l_link.state != "Operational" or r_link.state != "Operational":
+ count += 1
+ if count > 10:
+ self.fail("Fed links didn't become operational after 10 seconds")
+ sleep(1)
+ l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0]
+ r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0]
+ sleep(3)
+
+ #setup queue to receive messages from local broker
session = self.session
-
- mgmt = Helper(self)
- broker = mgmt.get_object("broker")
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.direct", binding_key="key")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ #setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_trace")
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="key")
+ r_session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i))
- mgmt.call_method(broker, "connect", {"host":remote_host(), "port":remote_port()})
- link = mgmt.get_object("link")
+ for i in range(1, 11):
+ try:
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ except Empty:
+ self.fail("Failed to find expected message containing 'Message %d'" % i)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ def test_tracing(self):
+ session = self.session
- mgmt.call_method(link, "bridge", {"durable":0, "src":"amq.direct", "dest":"amq.fanout", "key":"my-key",
- "tag":"my-bridge-id", "excludes":"exclude-me,also-exclude-me"})
- sleep(6)
- bridge = mgmt.get_object("bridge")
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "my-bridge-id",
+ "exclude-me,also-exclude-me", False, False, False, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
#setup queue to receive messages from local broker
session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
session.exchange_bind(queue="fed1", exchange="amq.fanout")
self.subscribe(queue="fed1", destination="f1")
queue = session.incoming("f1")
+ sleep(6)
#send messages to remote broker and confirm it is routed to local broker
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_tracing")
trace = [None, "exclude-me", "a,exclude-me,b", "also-exclude-me,c", "dont-exclude-me"]
@@ -244,13 +310,280 @@ class FederationTests(TestBase010):
self.fail("Got unexpected message in queue: " + extra.body)
except Empty: None
- mgmt.call_method(bridge, "close")
- mgmt.call_method(link, "close")
- sleep(6)
- self.assertEqual(len(mgmt.get_objects("bridge")), 0)
- self.assertEqual(len(mgmt.get_objects("link")), 0)
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_fanout(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_fanout")
+
+ session.exchange_declare(exchange="fed.fanout", type="fanout")
+ r_session.exchange_declare(exchange="fed.fanout", type="fanout")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.fanout", "fed.fanout", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties()
+ r_session.message_transfer(destination="fed.fanout", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_direct(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_direct")
+
+ session.exchange_declare(exchange="fed.direct", type="direct")
+ r_session.exchange_declare(exchange="fed.direct", type="direct")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.direct", "fed.direct", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.direct", binding_key="fd-key")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="fd-key")
+ r_session.message_transfer(destination="fed.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic")
+
+ session.exchange_declare(exchange="fed.topic", type="topic")
+ r_session.exchange_declare(exchange="fed.topic", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic", "fed.topic", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="ft-key.#")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one.two")
+ r_session.message_transfer(destination="fed.topic", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic_reorigin")
+
+ session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+
+ session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.topic_reorigin_2", binding_key="ft-key.one.#")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic_reorigin", "fed.topic_reorigin", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.topic_reorigin_2", "fed.topic_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic_reorigin", binding_key="ft-key.#")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one.two")
+ r_session.message_transfer(destination="fed.topic_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = bridge2.close()
+ self.assertEqual(result.status, 0)
+
+ # extra check: verify we don't leak bridge objects - keep the link
+ # around and verify the bridge count has gone to zero
+
+ attempts = 0
+ bridgeCount = len(qmf.getObjects(_class="bridge"))
+ while bridgeCount > 0:
+ attempts += 1
+ if attempts >= 5:
+ self.fail("Bridges didn't clean up")
+ return
+ sleep(1)
+ bridgeCount = len(qmf.getObjects(_class="bridge"))
+
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_direct_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_direct_reorigin")
+
+ session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+
+ session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
- mgmt.shutdown ()
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.direct_reorigin_2", binding_key="ft-key.two")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.direct_reorigin", "fed.direct_reorigin", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.direct_reorigin_2", "fed.direct_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.direct_reorigin", binding_key="ft-key.one")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one")
+ r_session.message_transfer(destination="fed.direct_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ # Extra test: don't explicitly close() bridge2. When the link is closed,
+ # it should clean up bridge2 automagically. verify_cleanup() will detect
+ # if bridge2 isn't cleaned up and will fail the test.
+ #
+ #result = bridge2.close()
+ #self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
def getProperty(self, msg, name):
for h in msg.headers:
@@ -261,16 +594,4 @@ class FederationTests(TestBase010):
headers = self.getProperty(msg, "application_headers")
if headers:
return headers[name]
- return None
-
-if __name__ == '__main__':
- args = sys.argv[1:]
- #need to remove the extra options from args as test runner doesn't recognise them
- extract_args("--remote-port", args)
- extract_args("--remote-host", args)
-
- if add_module():
- #add module(s) to run to testrunners args
- args.append("federation")
-
- if not testrunner.run(args): sys.exit(1)
+ return None
diff --git a/cpp/src/tests/find_prog.ps1 b/cpp/src/tests/find_prog.ps1
new file mode 100644
index 0000000000..5c482debbf
--- /dev/null
+++ b/cpp/src/tests/find_prog.ps1
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+# Locate the subdirectory where the specified program resides; the program
+# must have a directory and a file name, even if the directory is .
+param(
+ [string] $prog # program to look for somewhere below cwd
+)
+
+$dir = Split-Path $prog
+$exe = Split-Path $prog -leaf
+$sub = ""
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($try in $subs) {
+ $prog = "$dir\$try\$exe"
+ if (Test-Path $prog) {
+ $sub = $try
+ break
+ }
+}
diff --git a/cpp/src/tests/header_test.cpp b/cpp/src/tests/header_test.cpp
new file mode 100644
index 0000000000..c36b4f3bc3
--- /dev/null
+++ b/cpp/src/tests/header_test.cpp
@@ -0,0 +1,59 @@
+/*
+ *
+ * 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 <iostream>
+
+#include "TestOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace std;
+
+int main(int argc, char** argv)
+{
+ TestOptions opts;
+ try {
+ opts.parse(argc, argv);
+ Connection connection;
+ connection.open(opts.con);
+ Session session = connection.newSession();
+ std::string q("header_interop_test_queue");
+ session.queueDeclare(arg::queue=q);
+ double pi = 3.14159265;
+ float e = 2.71828f;
+ Message msg("", q);
+ msg.getMessageProperties().getApplicationHeaders().setDouble("pi", pi);
+ msg.getMessageProperties().getApplicationHeaders().setFloat("e", e);
+ session.messageTransfer(arg::content=msg);
+
+ session.close();
+ connection.close();
+
+ return 0;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
+ }
+ return 1;
+}
diff --git a/cpp/src/tests/header_test.py b/cpp/src/tests/header_test.py
new file mode 100755
index 0000000000..d5a2c16c01
--- /dev/null
+++ b/cpp/src/tests/header_test.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import qpid
+import sys
+import os
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import Message, RangedSet, uuid4
+from qpid.queue import Empty
+from math import fabs
+
+def getApplicationHeaders(msg):
+ for h in msg.headers:
+ if hasattr(h, 'application_headers'): return getattr(h, 'application_headers')
+ return None
+
+# Set parameters for login
+
+host="127.0.0.1"
+port=5672
+user="guest"
+password="guest"
+
+if len(sys.argv) > 1 :
+ host=sys.argv[1]
+if len(sys.argv) > 2 :
+ port=int(sys.argv[2])
+
+# Create a connection.
+socket = connect(host, port)
+connection = Connection (sock=socket)
+connection.start()
+session = connection.session(str(uuid4()))
+
+q = "header_interop_test_queue"
+session.queue_declare(queue=q)
+
+session.message_subscribe(queue=q, destination="received")
+queue = session.incoming("received")
+queue.start()
+
+msg = queue.get(timeout=10)
+pi = 3.14159265
+e = 2.71828
+
+headers = getApplicationHeaders(msg)
+pi_ = headers["pi"]
+e_ = headers["e"]
+session.close(timeout=10)
+
+failed = False
+
+if pi != pi_:
+ print "got incorrect value for pi: ", pi_, " expected:", pi
+ failed = True
+
+if fabs(e - e_) > 0.0001:
+ print "got incorrect value for e: ", e_, " expected:", e
+ failed = True
+
+if failed:
+ sys.exit(1)
+else:
+ print "Correct header values received."
+ sys.exit(0)
+
+
+
diff --git a/cpp/src/tests/interop_runner.cpp b/cpp/src/tests/interop_runner.cpp
deleted file mode 100644
index 8c6e0a6991..0000000000
--- a/cpp/src/tests/interop_runner.cpp
+++ /dev/null
@@ -1,251 +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 "qpid/Options.h"
-#include "qpid/ptr_map.h"
-#include "qpid/Exception.h"
-#include "qpid/client/Channel.h"
-#include "qpid/client/Connection.h"
-#include "qpid/client/ConnectionOptions.h"
-#include "qpid/client/Exchange.h"
-#include "qpid/client/MessageListener.h"
-#include "qpid/client/Queue.h"
-#include "qpid/sys/Thread.h"
-#include "qpid/sys/Time.h"
-#include <iostream>
-#include <memory>
-#include "BasicP2PTest.h"
-#include "BasicPubSubTest.h"
-#include "TestCase.h"
-#include <boost/ptr_container/ptr_map.hpp>
-
-/**
- * Framework for interop tests.
- *
- * [see http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification for details].
- */
-
-using namespace qpid::client;
-using namespace qpid::sys;
-using qpid::TestCase;
-using qpid::framing::FieldTable;
-using qpid::framing::ReplyTo;
-using namespace std;
-
-class DummyRun : public TestCase
-{
-public:
- DummyRun() {}
- void assign(const string&, FieldTable&, ConnectionOptions&) {}
- void start() {}
- void stop() {}
- void report(qpid::client::Message&) {}
-};
-
-string parse_next_word(const string& input, const string& delims, string::size_type& position);
-
-/**
- */
-class Listener : public MessageListener, private Runnable{
- typedef boost::ptr_map<string, TestCase> TestMap;
-
- Channel& channel;
- ConnectionOptions& options;
- TestMap tests;
- const string name;
- const string topic;
- TestCase* test;
- auto_ptr<Thread> runner;
- ReplyTo reportTo;
- string reportCorrelator;
-
- void shutdown();
- bool invite(const string& name);
- void run();
-
- void sendResponse(Message& response, ReplyTo replyTo);
- void sendResponse(Message& response, Message& request);
- void sendSimpleResponse(const string& type, Message& request);
- void sendReport();
-public:
- Listener(Channel& channel, ConnectionOptions& options);
- void received(Message& msg);
- void bindAndConsume();
- void registerTest(string name, TestCase* test);
-};
-
-struct TestSettings : ConnectionOptions
-{
- bool help;
-
- TestSettings() : help(false)
- {
- addOptions()
- ("help", qpid::optValue(help), "print this usage statement");
- }
-};
-
-int main(int argc, char** argv) {
- try {
- TestSettings options;
- options.parse(argc, argv);
- if (options.help) {
- cout << options;
- } else {
- Connection connection;
- connection.open(options.host, options.port, "guest", "guest", options.virtualhost);
-
- Channel channel;
- connection.openChannel(channel);
-
- Listener listener(channel, options);
- listener.registerTest("TC1_DummyRun", new DummyRun());
- listener.registerTest("TC2_BasicP2P", new qpid::BasicP2PTest());
- listener.registerTest("TC3_BasicPubSub", new qpid::BasicPubSubTest());
-
- listener.bindAndConsume();
-
- channel.run();
- connection.close();
- }
- } catch(const exception& error) {
- cout << error.what() << endl << "Type " << argv[0] << " --help for help" << endl;
- }
-}
-
-Listener::Listener(Channel& _channel, ConnectionOptions& _options) : channel(_channel), options(_options), name(options.clientid), topic("iop.control." + name)
-{}
-
-void Listener::registerTest(string name, TestCase* test)
-{
- tests.insert(name, test);
-}
-
-void Listener::bindAndConsume()
-{
- Queue control(name, true);
- channel.declareQueue(control);
- qpid::framing::FieldTable bindArgs;
- //replace these separate binds with a wildcard once that is supported on java broker
- channel.bind(Exchange::STANDARD_TOPIC_EXCHANGE, control, "iop.control", bindArgs);
- channel.bind(Exchange::STANDARD_TOPIC_EXCHANGE, control, topic, bindArgs);
-
- string tag;
- channel.consume(control, tag, this);
-}
-
-void Listener::sendSimpleResponse(const string& type, Message& request)
-{
- Message response;
- response.getHeaders().setString("CONTROL_TYPE", type);
- response.getHeaders().setString("CLIENT_NAME", name);
- response.getHeaders().setString("CLIENT_PRIVATE_CONTROL_KEY", topic);
- response.getMessageProperties().setCorrelationId(request.getMessageProperties().getCorrelationId());
- sendResponse(response, request);
-}
-
-void Listener::sendResponse(Message& response, Message& request)
-{
- sendResponse(response, request.getMessageProperties().getReplyTo());
-}
-
-void Listener::sendResponse(Message& response, ReplyTo replyTo)
-{
- string exchange = replyTo.getExchange();
- string routingKey = replyTo.getRoutingKey();
- channel.publish(response, exchange, routingKey);
-}
-
-void Listener::received(Message& message)
-{
- string type(message.getHeaders().getString("CONTROL_TYPE"));
-
- if (type == "INVITE") {
- string name(message.getHeaders().getString("TEST_NAME"));
- if (name.empty() || invite(name)) {
- sendSimpleResponse("ENLIST", message);
- } else {
- cout << "Can't take part in '" << name << "'" << endl;
- }
- } else if (type == "ASSIGN_ROLE") {
- test->assign(message.getHeaders().getString("ROLE"), message.getHeaders(), options);
- sendSimpleResponse("ACCEPT_ROLE", message);
- } else if (type == "START") {
- reportTo = message.getMessageProperties().getReplyTo();
- reportCorrelator = message.getMessageProperties().getCorrelationId();
- runner = auto_ptr<Thread>(new Thread(this));
- } else if (type == "STATUS_REQUEST") {
- reportTo = message.getMessageProperties().getReplyTo();
- reportCorrelator = message.getMessageProperties().getCorrelationId();
- test->stop();
- sendReport();
- } else if (type == "TERMINATE") {
- if (test) test->stop();
- shutdown();
- } else {
- cerr <<"ERROR!: Received unknown control message: " << type << endl;
- shutdown();
- }
-}
-
-void Listener::shutdown()
-{
- channel.close();
-}
-
-bool Listener::invite(const string& name)
-{
- TestMap::iterator i = tests.find(name);
- test = (i != tests.end()) ? qpid::ptr_map_ptr(i) : 0;
- return test;
-}
-
-void Listener::run()
-{
- //NB: this method will be called in its own thread
- //start test and when start returns...
- test->start();
- sendReport();
-}
-
-void Listener::sendReport()
-{
- Message report;
- report.getHeaders().setString("CONTROL_TYPE", "REPORT");
- test->report(report);
- report.getMessageProperties().setCorrelationId(reportCorrelator);
- sendResponse(report, reportTo);
-}
-
-string parse_next_word(const string& input, const string& delims, string::size_type& position)
-{
- string::size_type start = input.find_first_not_of(delims, position);
- if (start == string::npos) {
- return "";
- } else {
- string::size_type end = input.find_first_of(delims, start);
- if (end == string::npos) {
- end = input.length();
- }
- position = end;
- return input.substr(start, end - start);
- }
-}
diff --git a/cpp/src/tests/latencytest.cpp b/cpp/src/tests/latencytest.cpp
index e1a6f156a5..a205ef6c7c 100644
--- a/cpp/src/tests/latencytest.cpp
+++ b/cpp/src/tests/latencytest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -21,53 +21,64 @@
#include <algorithm>
+#include <limits>
#include <iostream>
#include <memory>
#include <sstream>
#include <vector>
-#include <unistd.h>
#include "TestOptions.h"
+#include "qpid/sys/Thread.h"
#include "qpid/client/Connection.h"
#include "qpid/client/Message.h"
#include "qpid/client/AsyncSession.h"
#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/Time.h"
using namespace qpid;
using namespace qpid::client;
using namespace qpid::sys;
using std::string;
+namespace qpid {
+namespace tests {
+
typedef std::vector<std::string> StringSet;
struct Args : public qpid::TestOptions {
uint size;
uint count;
uint rate;
+ bool sync;
uint reportFrequency;
uint timeLimit;
- uint queues;
+ uint concurrentConnections;
uint prefetch;
uint ack;
bool cumulative;
bool csv;
bool durable;
string base;
+ bool singleConnect;
- Args() : size(256), count(1000), rate(0), reportFrequency(100),
- timeLimit(0), queues(1),
+ Args() : size(256), count(1000), rate(0), reportFrequency(1000),
+ timeLimit(0), concurrentConnections(1),
prefetch(100), ack(0),
- durable(false), base("latency-test")
+ durable(false), base("latency-test"), singleConnect(false)
+
{
- addOptions()
+ addOptions()
("size", optValue(size, "N"), "message size")
- ("queues", optValue(queues, "N"), "number of queues")
+ ("concurrentTests", optValue(concurrentConnections, "N"), "number of concurrent test setups, will create another publisher,\
+ subcriber, queue, and connections")
+ ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.")
("count", optValue(count, "N"), "number of messages to send")
("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)")
- ("report-frequency", optValue(reportFrequency, "N"),
+ ("sync", optValue(sync), "send messages synchronously")
+ ("report-frequency", optValue(reportFrequency, "N"),
"number of milliseconds to wait between reports (ignored unless rate specified)")
- ("time-limit", optValue(timeLimit, "N"),
+ ("time-limit", optValue(timeLimit, "N"),
"test duration, in seconds")
("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)")
("ack", optValue(ack, "N"), "Ack frequency in messages (defaults to half the prefetch value)")
@@ -82,6 +93,7 @@ const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
Args opts;
double c_min, c_avg, c_max;
+Connection globalConnection;
uint64_t current_time()
{
@@ -89,7 +101,7 @@ uint64_t current_time()
return t;
}
-struct Stats
+struct Stats
{
Mutex lock;
uint count;
@@ -106,14 +118,15 @@ struct Stats
class Client : public Runnable
{
protected:
- Connection connection;
+ Connection* connection;
+ Connection localConnection;
AsyncSession session;
Thread thread;
string queue;
public:
Client(const string& q);
- virtual ~Client() {}
+ virtual ~Client();
void start();
void join();
@@ -122,7 +135,7 @@ public:
};
class Receiver : public Client, public MessageListener
-{
+{
SubscriptionManager mgr;
uint count;
Stats& stats;
@@ -143,6 +156,8 @@ class Sender : public Client
void sendByRate();
void sendByCount();
Receiver& receiver;
+ const string data;
+
public:
Sender(const string& queue, Receiver& receiver);
void test();
@@ -156,7 +171,7 @@ class Test
Receiver receiver;
Sender sender;
AbsTime begin;
-
+
public:
Test(const string& q) : queue(q), receiver(queue, stats), sender(queue, receiver), begin(now()) {}
void start();
@@ -167,8 +182,14 @@ public:
Client::Client(const string& q) : queue(q)
{
- opts.open(connection);
- session = connection.newSession();
+ if (opts.singleConnect){
+ connection = &globalConnection;
+ if (!globalConnection.isOpen()) opts.open(globalConnection);
+ }else{
+ connection = &localConnection;
+ opts.open(localConnection);
+ }
+ session = connection->newSession();
}
void Client::start()
@@ -185,8 +206,16 @@ void Client::run()
{
try{
test();
+ } catch(const std::exception& e) {
+ std::cout << "Error in receiver: " << e.what() << std::endl;
+ }
+}
+
+Client::~Client()
+{
+ try{
session.close();
- connection.close();
+ connection->close();
} catch(const std::exception& e) {
std::cout << "Error in receiver: " << e.what() << std::endl;
}
@@ -199,15 +228,17 @@ Receiver::Receiver(const string& q, Stats& s) : Client(q), mgr(session), count(0
if (msgCount) {
std::cout << "Warning: found " << msgCount << " msgs on " << queue << ". Purging..." << std::endl;
session.queuePurge(arg::queue=queue);
+ session.sync();
}
+ SubscriptionSettings settings;
if (opts.prefetch) {
- mgr.setAckPolicy(AckPolicy(opts.ack ? opts.ack : (opts.prefetch / 2)));
- mgr.setFlowControl(opts.prefetch, SubscriptionManager::UNLIMITED, true);
+ settings.autoAck = (opts.ack ? opts.ack : (opts.prefetch / 2));
+ settings.flowControl = FlowControl::messageWindow(opts.prefetch);
} else {
- mgr.setAcceptMode(1/*not-required*/);
- mgr.setFlowControl(SubscriptionManager::UNLIMITED, SubscriptionManager::UNLIMITED, false);
+ settings.acceptMode = ACCEPT_MODE_NONE;
+ settings.flowControl = FlowControl::unlimited();
}
- mgr.subscribe(*this, queue);
+ mgr.subscribe(*this, queue, settings);
}
void Receiver::test()
@@ -219,11 +250,9 @@ void Receiver::test()
void Receiver::received(Message& msg)
{
++count;
- uint64_t sentAt = msg.getDeliveryProperties().getTimestamp();
- //uint64_t sentAt = msg.getHeaders().getTimestamp("sent-at");// TODO: add support for uint64_t as a field table type
uint64_t receivedAt = current_time();
+ uint64_t sentAt = msg.getDeliveryProperties().getTimestamp();
- //std::cerr << "Latency: " << (receivedAt - sentAt) << std::endl;
stats.update(((double) (receivedAt - sentAt)) / TIME_MSEC);
if (!opts.rate && count >= opts.count) {
@@ -235,57 +264,70 @@ void Stats::update(double latency)
{
Mutex::ScopedLock l(lock);
count++;
- if (minLatency == 0 || minLatency > latency) minLatency = latency;
- if (maxLatency == 0 || maxLatency < latency) maxLatency = latency;
+ minLatency = std::min(minLatency, latency);
+ maxLatency = std::max(maxLatency, latency);
totalLatency += latency;
}
-Stats::Stats() : count(0), minLatency(0), maxLatency(0), totalLatency(0) {}
+Stats::Stats() : count(0), minLatency(std::numeric_limits<double>::max()), maxLatency(0), totalLatency(0) {}
void Stats::print()
{
static bool already_have_stats = false;
uint value;
- double aux_avg = (totalLatency / count);
if (opts.rate)
value = opts.rate;
else
value = opts.count;
Mutex::ScopedLock l(lock);
+ double aux_avg = (totalLatency / count);
if (!opts.cumulative) {
if (!opts.csv) {
- std::cout << "Latency(ms): min=" << minLatency << ", max=" <<
- maxLatency << ", avg=" << aux_avg;
+ if (count) {
+ std::cout << "Latency(ms): min=" << minLatency << ", max=" <<
+ maxLatency << ", avg=" << aux_avg;
+ } else {
+ std::cout << "Stalled: no samples for interval";
+ }
} else {
- std::cout << value << "," << minLatency << "," << maxLatency <<
+ if (count) {
+ std::cout << value << "," << minLatency << "," << maxLatency <<
"," << aux_avg;
+ } else {
+ std::cout << value << "," << minLatency << "," << maxLatency <<
+ ", Stalled";
+ }
}
} else {
- if (already_have_stats) {
- c_avg = (c_min + aux_avg) / 2;
- if (c_min > minLatency) c_min = minLatency;
- if (c_max < maxLatency) c_max = maxLatency;
+ if (count) {
+ if (already_have_stats) {
+ c_avg = (c_min + aux_avg) / 2;
+ if (c_min > minLatency) c_min = minLatency;
+ if (c_max < maxLatency) c_max = maxLatency;
+ } else {
+ c_avg = aux_avg;
+ c_min = minLatency;
+ c_max = maxLatency;
+ already_have_stats = true;
+ }
+ std::cout << value << "," << c_min << "," << c_max <<
+ "," << c_avg;
} else {
- c_avg = aux_avg;
- c_min = minLatency;
- c_max = maxLatency;
- already_have_stats = true;
+ std::cout << "Stalled: no samples for interval";
}
- std::cout << value << "," << c_min << "," << c_max <<
- "," << c_avg;
}
-
}
void Stats::reset()
{
Mutex::ScopedLock l(lock);
count = 0;
- totalLatency = maxLatency = minLatency = 0;
+ totalLatency = maxLatency = 0;
+ minLatency = std::numeric_limits<double>::max();
}
-Sender::Sender(const string& q, Receiver& receiver) : Client(q), receiver(receiver) {}
+Sender::Sender(const string& q, Receiver& receiver) : Client(q), receiver(receiver), data(generateData(opts.size)) {}
void Sender::test()
{
@@ -295,7 +337,7 @@ void Sender::test()
void Sender::sendByCount()
{
- Message msg(generateData(opts.size), queue);
+ Message msg(data, queue);
if (opts.durable) {
msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
}
@@ -303,45 +345,38 @@ void Sender::sendByCount()
for (uint i = 0; i < opts.count; i++) {
uint64_t sentAt(current_time());
msg.getDeliveryProperties().setTimestamp(sentAt);
- //msg.getHeaders().setTimestamp("sent-at", sentAt);//TODO add support for uint64_t to field tables
async(session).messageTransfer(arg::content=msg, arg::acceptMode=1);
+ if (opts.sync) session.sync();
}
session.sync();
}
void Sender::sendByRate()
{
- Message msg(generateData(opts.size), queue);
+ Message msg(data, queue);
if (opts.durable) {
msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
}
-
- //calculate interval (in micro secs) between messages to achieve desired rate
- uint64_t interval = (1000*1000)/opts.rate;
- uint64_t timeLimit(opts.timeLimit * TIME_SEC);
- uint64_t start(current_time());
-
+ uint64_t interval = TIME_SEC/opts.rate;
+ int64_t timeLimit = opts.timeLimit * TIME_SEC;
+ uint64_t sent = 0, missedRate = 0;
+ AbsTime start = now();
while (true) {
- uint64_t start_msg(current_time());
- msg.getDeliveryProperties().setTimestamp(start_msg);
- //msg.getHeaders().setTimestamp("sent-at", sentAt);//TODO add support for uint64_t to field tables
+ AbsTime sentAt=now();
+ msg.getDeliveryProperties().setTimestamp(Duration(sentAt));
async(session).messageTransfer(arg::content=msg, arg::acceptMode=1);
-
- uint64_t now = current_time();
-
- if (timeLimit != 0 && (now - start) > timeLimit) {
- session.sync();
- receiver.stop();
- break;
- }
-
- uint64_t timeTaken = (now - start_msg) / TIME_USEC;
- if (timeTaken < interval) {
- usleep(interval - timeTaken);
- } else if (timeTaken > interval &&
- !opts.csv && !opts.cumulative) { // Don't be so verbose in this case, we're piping the results to another program
- std::cout << "Could not achieve desired rate! (Took " << timeTaken
- << " microsecs to send message, aiming for " << interval << " microsecs)" << std::endl;
+ if (opts.sync) session.sync();
+ ++sent;
+ AbsTime waitTill(start, sent*interval);
+ Duration delay(sentAt, waitTill);
+ if (delay < 0)
+ ++missedRate;
+ else
+ sys::usleep(delay / TIME_USEC);
+ if (timeLimit != 0 && Duration(start, now()) > timeLimit) {
+ session.sync();
+ receiver.stop();
+ break;
}
}
}
@@ -350,7 +385,7 @@ string Sender::generateData(uint size)
{
if (size < chars.length()) {
return chars.substr(0, size);
- }
+ }
std::string data;
for (uint i = 0; i < (size / chars.length()); i++) {
data += chars;
@@ -360,43 +395,51 @@ string Sender::generateData(uint size)
}
-void Test::start()
-{
- receiver.start();
+void Test::start()
+{
+ receiver.start();
begin = AbsTime(now());
- sender.start();
+ sender.start();
}
-void Test::join()
-{
- sender.join();
- receiver.join();
+void Test::join()
+{
+ sender.join();
+ receiver.join();
AbsTime end = now();
Duration time(begin, end);
double msecs(time / TIME_MSEC);
if (!opts.csv) {
- std::cout << "Sent " << receiver.getCount() << " msgs through " << queue
+ std::cout << "Sent " << receiver.getCount() << " msgs through " << queue
<< " in " << msecs << "ms (" << (receiver.getCount() * 1000 / msecs) << " msgs/s) ";
}
stats.print();
std::cout << std::endl;
}
-void Test::report()
-{
+void Test::report()
+{
stats.print();
std::cout << std::endl;
stats.reset();
}
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char** argv)
{
try {
opts.parse(argc, argv);
if (opts.cumulative)
opts.csv = true;
- boost::ptr_vector<Test> tests(opts.queues);
- for (uint i = 0; i < opts.queues; i++) {
+
+ Connection localConnection;
+ AsyncSession session;
+
+ boost::ptr_vector<Test> tests(opts.concurrentConnections);
+ for (uint i = 0; i < opts.concurrentConnections; i++) {
std::ostringstream out;
out << opts.base << "-" << (i+1);
tests.push_back(new Test(out.str()));
@@ -406,7 +449,7 @@ int main(int argc, char** argv)
}
if (opts.rate && !opts.timeLimit) {
while (true) {
- usleep(opts.reportFrequency * 1000);
+ qpid::sys::usleep(opts.reportFrequency * 1000);
//print latency report:
for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) {
i->report();
diff --git a/cpp/src/tests/logging.cpp b/cpp/src/tests/logging.cpp
index 97bfed2436..5cb563c7d3 100644
--- a/cpp/src/tests/logging.cpp
+++ b/cpp/src/tests/logging.cpp
@@ -19,8 +19,14 @@
#include "test_tools.h"
#include "qpid/log/Logger.h"
#include "qpid/log/Options.h"
+#include "qpid/log/OstreamOutput.h"
#include "qpid/memory.h"
#include "qpid/Options.h"
+#if defined (_WIN32)
+# include "qpid/log/windows/SinkOptions.h"
+#else
+# include "qpid/log/posix/SinkOptions.h"
+#endif
#include <boost/test/floating_point_comparison.hpp>
#include <boost/format.hpp>
@@ -31,6 +37,9 @@
#include <time.h>
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(loggingTestSuite)
using namespace std;
@@ -77,6 +86,7 @@ QPID_AUTO_TEST_CASE(testStatementEnabled) {
// Verify that the singleton enables and disables static
// log statements.
Logger& l = Logger::instance();
+ ScopedSuppressLogging ls(l);
l.select(Selector(debug));
static Statement s=QPID_LOG_STATEMENT_INIT(debug);
BOOST_CHECK(!s.enabled);
@@ -99,7 +109,7 @@ struct TestOutput : public Logger::Output {
TestOutput(Logger& l) {
l.output(std::auto_ptr<Logger::Output>(this));
}
-
+
void log(const Statement& s, const string& m) {
msg.push_back(m);
stmt.push_back(s);
@@ -133,7 +143,7 @@ QPID_AUTO_TEST_CASE(testLoggerOutput) {
QPID_AUTO_TEST_CASE(testMacro) {
Logger& l=Logger::instance();
- l.clear();
+ ScopedSuppressLogging ls(l);
l.select(Selector(info));
TestOutput* out=new TestOutput(l);
QPID_LOG(info, "foo");
@@ -152,6 +162,7 @@ QPID_AUTO_TEST_CASE(testMacro) {
QPID_AUTO_TEST_CASE(testLoggerFormat) {
Logger& l = Logger::instance();
+ ScopedSuppressLogging ls(l);
l.select(Selector(critical));
TestOutput* out=new TestOutput(l);
@@ -165,23 +176,19 @@ QPID_AUTO_TEST_CASE(testLoggerFormat) {
l.format(Logger::FUNCTION);
QPID_LOG(critical, "foo");
-
+ BOOST_CHECK_EQUAL(string(BOOST_CURRENT_FUNCTION) + ": foo\n", out->last());
+
l.format(Logger::LEVEL);
QPID_LOG(critical, "foo");
BOOST_CHECK_EQUAL("critical foo\n", out->last());
-
- l.format(~0); // Everything
- QPID_LOG(critical, "foo");
- string re=".* critical -?\\[[0-9a-f]*] "+string(__FILE__)+":\\d+:void .*testLoggerFormat.*\\(\\): foo\n";
- BOOST_CHECK_REGEX(re, out->last());
}
QPID_AUTO_TEST_CASE(testOstreamOutput) {
Logger& l=Logger::instance();
- l.clear();
+ ScopedSuppressLogging ls(l);
l.select(Selector(error));
ostringstream os;
- l.output(os);
+ l.output(qpid::make_auto_ptr<Logger::Output>(new OstreamOutput(os)));
QPID_LOG(error, "foo");
QPID_LOG(error, "bar");
QPID_LOG(error, "baz");
@@ -191,6 +198,7 @@ QPID_AUTO_TEST_CASE(testOstreamOutput) {
#if 0 // This test requires manual intervention. Normally disabled.
QPID_AUTO_TEST_CASE(testSyslogOutput) {
Logger& l=Logger::instance();
+ Logger::StateSaver ls(l);
l.clear();
l.select(Selector(info));
l.syslog("qpid_test");
@@ -223,12 +231,12 @@ clock_t timeLoop(int times, int (*fp)()) {
// Overhead test disabled because it consumes a ton of CPU and takes
// forever under valgrind. Not friendly for regular test runs.
-//
+//
#if 0
QPID_AUTO_TEST_CASE(testOverhead) {
// Ensure that the ratio of CPU time for an incrementing loop
// with and without disabled log statements is in acceptable limits.
- //
+ //
int times=100000000;
clock_t noLog=timeLoop(times, count);
clock_t withLog=timeLoop(times, loggedCount);
@@ -237,9 +245,9 @@ QPID_AUTO_TEST_CASE(testOverhead) {
// NB: in initial tests the ratio was consistently below 1.5,
// 2.5 is reasonable and should avoid spurios failures
// due to machine load.
- //
- BOOST_CHECK_SMALL(ratio, 2.5);
-}
+ //
+ BOOST_CHECK_SMALL(ratio, 2.5);
+}
#endif // 0
Statement statement(
@@ -258,19 +266,26 @@ QPID_AUTO_TEST_CASE(testOptionsParse) {
"--log-enable", "error+:foo",
"--log-enable", "debug:bar",
"--log-enable", "info",
- "--log-output", "x",
- "--log-output", "y",
+ "--log-to-stderr", "no",
+ "--log-to-file", "logout",
"--log-level", "yes",
"--log-source", "1",
"--log-thread", "true",
"--log-function", "YES"
};
qpid::log::Options opts("");
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions sinks("test");
+#else
+ qpid::log::posix::SinkOptions sinks("test");
+#endif
opts.parse(ARGC(argv), const_cast<char**>(argv));
+ sinks = *opts.sinkOptions;
vector<string> expect=list_of("error+:foo")("debug:bar")("info");
BOOST_CHECK_EQUAL(expect, opts.selectors);
- expect=list_of("x")("y");
- BOOST_CHECK_EQUAL(expect, opts.outputs);
+ BOOST_CHECK(!sinks.logToStderr);
+ BOOST_CHECK(!sinks.logToStdout);
+ BOOST_CHECK(sinks.logFile == "logout");
BOOST_CHECK(opts.level);
BOOST_CHECK(opts.source);
BOOST_CHECK(opts.function);
@@ -278,10 +293,17 @@ QPID_AUTO_TEST_CASE(testOptionsParse) {
}
QPID_AUTO_TEST_CASE(testOptionsDefault) {
- Options opts("");
- vector<string> expect=list_of("stderr");
- BOOST_CHECK_EQUAL(expect, opts.outputs);
- expect=list_of("error+");
+ qpid::log::Options opts("");
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions sinks("test");
+#else
+ qpid::log::posix::SinkOptions sinks("test");
+#endif
+ sinks = *opts.sinkOptions;
+ BOOST_CHECK(sinks.logToStderr);
+ BOOST_CHECK(!sinks.logToStdout);
+ BOOST_CHECK(sinks.logFile.length() == 0);
+ vector<string> expect=list_of("notice+");
BOOST_CHECK_EQUAL(expect, opts.selectors);
BOOST_CHECK(opts.time && opts.level);
BOOST_CHECK(!(opts.source || opts.function || opts.thread));
@@ -306,47 +328,16 @@ QPID_AUTO_TEST_CASE(testSelectorFromOptions) {
BOOST_CHECK(s.isEnabled(critical, "foo"));
}
-QPID_AUTO_TEST_CASE(testOptionsFormat) {
- Logger l;
- {
- Options opts("");
- BOOST_CHECK_EQUAL(Logger::TIME|Logger::LEVEL, l.format(opts));
- const char* argv[]={
- 0,
- "--log-time", "no",
- "--log-level", "no",
- "--log-source", "1",
- "--log-thread", "1"
- };
- opts.parse(ARGC(argv), const_cast<char**>(argv));
- BOOST_CHECK_EQUAL(
- Logger::FILE|Logger::LINE|Logger::THREAD, l.format(opts));
- }
- {
- Options opts(""); // Clear.
- const char* argv[]={
- 0,
- "--log-level", "no",
- "--log-thread", "true",
- "--log-function", "YES",
- "--log-time", "YES"
- };
- opts.parse(ARGC(argv), const_cast<char**>(argv));
- BOOST_CHECK_EQUAL(
- Logger::THREAD|Logger::FUNCTION|Logger::TIME,
- l.format(opts));
- }
-}
-
-QPID_AUTO_TEST_CASE(testLoggerConfigure) {
+QPID_AUTO_TEST_CASE(testLoggerStateure) {
Logger& l=Logger::instance();
- l.clear();
- Options opts("test");
+ ScopedSuppressLogging ls(l);
+ qpid::log::Options opts("test");
const char* argv[]={
0,
- "--log-time", "no",
+ "--log-time", "no",
"--log-source", "yes",
- "--log-output", "logging.tmp",
+ "--log-to-stderr", "no",
+ "--log-to-file", "logging.tmp",
"--log-enable", "critical"
};
opts.parse(ARGC(argv), const_cast<char**>(argv));
@@ -363,15 +354,23 @@ QPID_AUTO_TEST_CASE(testLoggerConfigure) {
QPID_AUTO_TEST_CASE(testQuoteNonPrintable) {
Logger& l=Logger::instance();
- l.clear();
- Options opts("test");
- opts.outputs.clear();
- opts.outputs.push_back("logging.tmp");
+ ScopedSuppressLogging ls(l);
+ qpid::log::Options opts("test");
opts.time=false;
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions *sinks =
+ dynamic_cast<qpid::log::windows::SinkOptions *>(opts.sinkOptions.get());
+#else
+ qpid::log::posix::SinkOptions *sinks =
+ dynamic_cast<qpid::log::posix::SinkOptions *>(opts.sinkOptions.get());
+#endif
+ sinks->logToStderr = false;
+ sinks->logFile = "logging.tmp";
l.configure(opts);
+
char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff";
string str(s, sizeof(s));
- QPID_LOG(critical, str);
+ QPID_LOG(critical, str);
ifstream log("logging.tmp");
string line;
getline(log, line, '\0');
@@ -382,3 +381,5 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) {
}
QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/long_cluster_tests.py b/cpp/src/tests/long_cluster_tests.py
new file mode 100755
index 0000000000..f77837f0c4
--- /dev/null
+++ b/cpp/src/tests/long_cluster_tests.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+# 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.
+#
+
+import os, signal, sys, unittest
+from testlib import TestBaseCluster
+
+class LongClusterTests(TestBaseCluster):
+ """Long/Soak cluster tests with async store ability"""
+
+
+ def test_LongCluster_01_DummyTest(self):
+ """Dummy test - a placeholder for the first of the long/soak python cluster tests"""
+ pass
+
+# Start the test here
+
+if __name__ == '__main__':
+ if os.getenv("STORE_LIB") != None:
+ print "NOTE: Store enabled for the following tests:"
+ if not unittest.main(): sys.exit(1)
+
diff --git a/cpp/src/tests/multiq_perftest b/cpp/src/tests/multiq_perftest
index f6644e740c..10f9edd2a6 100755
--- a/cpp/src/tests/multiq_perftest
+++ b/cpp/src/tests/multiq_perftest
@@ -1,2 +1,22 @@
#!/bin/sh
+
+#
+# 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.
+#
+
exec `dirname $0`/run_perftest 10000 --mode shared --qt 16
diff --git a/cpp/src/tests/perfdist b/cpp/src/tests/perfdist
index 816d2d99f6..59548b23f7 100755
--- a/cpp/src/tests/perfdist
+++ b/cpp/src/tests/perfdist
@@ -1,4 +1,25 @@
#!/bin/bash
+
+#
+# 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.
+#
+
+
#
# Distributed perftest.
# Runs perftest clients on multiple hosts using ssh.
@@ -7,23 +28,24 @@
set -e
usage() {
cat <<EOF
-usage: $0 <perftest-args> -- <client-hosts ...>
-
-Run perftest with clients running on the listed hosts. Clients are
-assigned to hosts publishers first, then subscribers the host list is
-used round-robin if there are more clients than hosts. perftest-args should
-include a --host <brokerhost> flag.
+usage: $0 <perftest-args> -- <client-hosts ...> [ --- <broker hosts...> ]
+Client & broker hosts can also be set in env vars CLIENTS and BROKERS.
-Do not pass preftest action flags: --setup, --control, --publish, --subscribe.
-The script will pass them to the appropriate client processes.
+Run perftest clients on the client hosts against brokers on the broker
+hosts Clients are assigned to client hosts round robin: publishers
+first, then subscribers. If there are multiple brokers (for cluster
+tests) clients connect to them round robin.
-Note all perftest args must come before --.
+Broker hosts can be listed with -b in perftest-args or after ---
+at the end of the arguments.
Error: $*
EOF
exit 1
}
+TESTDIR=${TESTDIR:-$PWD} # Absolute path to test exes on all hosts.
+
collect() { eval $COLLECT=\""\$$COLLECT $*"\"; }
NPUBS=1
NSUBS=1
@@ -33,18 +55,33 @@ while test $# -gt 0; do
--publish|--subscribe|--setup|--control) usage "Don't pass perftest action flags: $1" ;;
--npubs) collect $1 $2; NPUBS=$2; shift 2 ;;
--nsubs) collect $1 $2; NSUBS=$2; shift 2 ;;
- --) COLLECT=HOSTS; shift ;;
+ -s|--summary) collect $1; QUIET=yes; shift 1 ;;
+ -b|--broker) BROKERS="$BROKERS $2"; shift 2;;
+ --) COLLECT=CLIENTARG; shift ;;
+ ---) COLLECT=BROKERARG; shift;;
*) collect $1; shift ;;
esac
done
-if [ -z "$HOSTS" ]; then usage "No hosts listed after --"; fi
-PATH="`dirname $0`:$PATH"
-PERFTEST="`which perftest` $ARGS" || usage "Can't find perftest executable"
-HOSTS=($HOSTS)
-start() { ssh ${HOSTS[i % ${#HOSTS[*]}]} $PERFTEST $*& }
+CLIENTS=${CLIENTARG:-$CLIENTS}
+if [ -z "$CLIENTS" ]; then usage "No client hosts listed after --"; fi
+BROKERS=${BROKERARG:-$BROKERS}
+if [ -z "$BROKERS" ]; then usage "No brokers specified"; fi
+
+PERFTEST="$TESTDIR/perftest $ARGS"
+
+CLIENTS=($CLIENTS)
+BROKERS=($BROKERS)
+start() {
+ CLIENT=${CLIENTS[i % ${#CLIENTS[*]}]}
+ BROKER=${BROKERS[i % ${#BROKERS[*]}]}
+ ARGS="$* --broker $BROKER"
+ cmd="ssh -n $CLIENT $PERFTEST $ARGS"
+ test -z "$QUIET" && echo "Client $i: $cmd"
+ $cmd &
+}
-$PERFTEST --setup
+$PERFTEST --setup -b ${BROKERS[0]}
for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done
for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done
-$PERFTEST --control
+$PERFTEST --control -b ${BROKERS[0]}
diff --git a/cpp/src/tests/perftest.cpp b/cpp/src/tests/perftest.cpp
index 9096854a6d..88d9fd15cb 100644
--- a/cpp/src/tests/perftest.cpp
+++ b/cpp/src/tests/perftest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -28,6 +28,7 @@
#include "qpid/client/Message.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
@@ -38,7 +39,6 @@
#include <sstream>
#include <numeric>
#include <algorithm>
-#include <unistd.h>
#include <math.h>
@@ -49,6 +49,9 @@ using namespace sys;
using boost::lexical_cast;
using boost::bind;
+namespace qpid {
+namespace tests {
+
enum Mode { SHARED, FANOUT, TOPIC };
const char* modeNames[] = { "shared", "fanout", "topic" };
@@ -75,6 +78,7 @@ struct Opts : public TestOptions {
// Queue policy
uint32_t queueMaxCount;
uint64_t queueMaxSize;
+ std::string baseName;
bool queueDurable;
// Publisher
@@ -92,22 +96,26 @@ struct Opts : public TestOptions {
// General
size_t qt;
+ bool singleConnect;
size_t iterations;
Mode mode;
bool summary;
uint32_t intervalSub;
uint32_t intervalPub;
size_t tx;
+ size_t txPub;
+ size_t txSub;
+ bool commitAsync;
static const std::string helpText;
-
+
Opts() :
TestOptions(helpText),
- setup(false), control(false), publish(false), subscribe(false),
+ setup(false), control(false), publish(false), subscribe(false), baseName("perftest"),
pubs(1), count(500000), size(1024), confirm(true), durable(false), uniqueData(false), syncPub(false),
subs(1), ack(0),
- qt(1), iterations(1), mode(SHARED), summary(false),
- intervalSub(0), intervalPub(0), tx(0)
+ qt(1),singleConnect(false), iterations(1), mode(SHARED), summary(false),
+ intervalSub(0), intervalPub(0), tx(0), txPub(0), txSub(0), commitAsync(false)
{
addOptions()
("setup", optValue(setup), "Create shared queues.")
@@ -131,19 +139,25 @@ struct Opts : public TestOptions {
("nsubs", optValue(subs, "N"), "Create N subscribers.")
("sub-ack", optValue(ack, "N"), "N>0: Subscriber acks batches of N.\n"
"N==0: Subscriber uses unconfirmed mode")
-
+
("qt", optValue(qt, "N"), "Create N queues or topics.")
+ ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.")
+
("iterations", optValue(iterations, "N"), "Desired number of iterations of the test.")
("summary,s", optValue(summary), "Summary output: pubs/sec subs/sec transfers/sec Mbytes/sec")
("queue-max-count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'")
("queue-max-size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'")
+ ("base-name", optValue(baseName, "NAME"), "base name used for queues or topics")
("queue-durable", optValue(queueDurable, "N"), "Make queue durable (implied if durable set)")
("interval_sub", optValue(intervalSub, "ms"), ">=0 delay between msg consume")
("interval_pub", optValue(intervalPub, "ms"), ">=0 delay between msg publish")
- ("tx", optValue(tx, "N"), "if non-zero, the transaction batch size");
+ ("tx", optValue(tx, "N"), "if non-zero, the transaction batch size for publishing and consuming")
+ ("pub-tx", optValue(txPub, "N"), "if non-zero, the transaction batch size for publishing")
+ ("async-commit", optValue(commitAsync, "yes|no"), "Don't wait for completion of commit")
+ ("sub-tx", optValue(txSub, "N"), "if non-zero, the transaction batch size for consuming");
}
// Computed values
@@ -160,7 +174,7 @@ struct Opts : public TestOptions {
count += subs - (count % subs);
cout << "WARNING: Adjusted --count to " << count
<< " the nearest multiple of --nsubs" << endl;
- }
+ }
totalPubs = pubs*qt;
totalSubs = subs*qt;
subQuota = (pubs*count)/subs;
@@ -180,6 +194,18 @@ struct Opts : public TestOptions {
break;
}
transfers=(totalPubs*count) + (totalSubs*subQuota);
+ if (tx) {
+ if (txPub) {
+ cerr << "WARNING: Using overriden tx value for publishers: " << txPub << std::endl;
+ } else {
+ txPub = tx;
+ }
+ if (txSub) {
+ cerr << "WARNING: Using overriden tx value for subscribers: " << txSub << std::endl;
+ } else {
+ txSub = tx;
+ }
+ }
}
};
@@ -196,25 +222,46 @@ const std::string Opts::helpText=
"Note the <other options> must be identical for all processes.\n";
Opts opts;
+Connection globalConnection;
+
+std::string fqn(const std::string& name)
+{
+ ostringstream fqn;
+ fqn << opts.baseName << "_" << name;
+ return fqn.str();
+}
struct Client : public Runnable {
- Connection connection;
+ Connection* connection;
+ Connection localConnection;
AsyncSession session;
Thread thread;
Client() {
- opts.open(connection);
- session = connection.newSession();
+ if (opts.singleConnect){
+ connection = &globalConnection;
+ if (!globalConnection.isOpen()) opts.open(globalConnection);
+ }else{
+ connection = &localConnection;
+ opts.open(localConnection);
+ }
+ session = connection->newSession();
}
~Client() {
- session.close();
- connection.close();
+ try {
+ if (connection->isOpen()) {
+ session.close();
+ connection->close();
+ }
+ } catch (const std::exception& e) {
+ std::cerr << "Error in shutdown: " << e.what() << std::endl;
+ }
}
};
struct Setup : public Client {
-
+
void queueInit(string name, bool durable=false, const framing::FieldTable& settings=framing::FieldTable()) {
session.queueDeclare(arg::queue=name, arg::durable=durable, arg::arguments=settings);
session.queuePurge(arg::queue=name);
@@ -222,18 +269,19 @@ struct Setup : public Client {
}
void run() {
- queueInit("pub_start");
- queueInit("pub_done");
- queueInit("sub_ready");
- queueInit("sub_done");
+ queueInit(fqn("pub_start"));
+ queueInit(fqn("pub_done"));
+ queueInit(fqn("sub_ready"));
+ queueInit(fqn("sub_done"));
+ if (opts.iterations > 1) queueInit(fqn("sub_iteration"));
if (opts.mode==SHARED) {
framing::FieldTable settings;//queue policy settings
settings.setInt("qpid.max_count", opts.queueMaxCount);
settings.setInt("qpid.max_size", opts.queueMaxSize);
for (size_t i = 0; i < opts.qt; ++i) {
ostringstream qname;
- qname << "perftest" << i;
- queueInit(qname.str(), opts.durable || opts.queueDurable, settings);
+ qname << opts.baseName << i;
+ queueInit(qname.str(), opts.durable || opts.queueDurable, settings);
}
}
}
@@ -258,7 +306,7 @@ class Stats {
public:
Stats() : sum(0) {}
-
+
// Functor to collect rates.
void operator()(const string& data) {
try {
@@ -269,7 +317,7 @@ class Stats {
throw Exception("Bad report: "+data);
}
}
-
+
double mean() const {
return sum/values.size();
}
@@ -286,7 +334,7 @@ class Stats {
}
return sqrt(ssq/(values.size()-1));
}
-
+
ostream& print(ostream& out) {
ostream_iterator<double> o(out, "\n");
copy(values.begin(), values.end(), o);
@@ -296,11 +344,11 @@ class Stats {
return out << endl;
}
};
-
+
// Manage control queues, collect and print reports.
struct Controller : public Client {
-
+
SubscriptionManager subs;
Controller() : subs(session) {}
@@ -309,7 +357,7 @@ struct Controller : public Client {
void process(size_t n, string queue,
boost::function<void (const string&)> msgFn)
{
- if (!opts.summary)
+ if (!opts.summary)
cout << "Processing " << n << " messages from "
<< queue << " " << flush;
LocalQueue lq;
@@ -325,8 +373,8 @@ struct Controller : public Client {
void process(size_t n, LocalQueue lq, string queue,
boost::function<void (const string&)> msgFn)
{
- session.messageFlow(queue, 0, n);
- if (!opts.summary)
+ session.messageFlow(queue, 0, n);
+ if (!opts.summary)
cout << "Processing " << n << " messages from "
<< queue << " " << flush;
for (size_t i = 0; i < n; ++i) {
@@ -341,20 +389,20 @@ struct Controller : public Client {
cout << "Sending " << data << " " << n << " times to " << queue
<< endl;
Message msg(data, queue);
- for (size_t i = 0; i < n; ++i)
+ for (size_t i = 0; i < n; ++i)
session.messageTransfer(arg::content=msg, arg::acceptMode=1);
}
void run() { // Controller
try {
// Wait for subscribers to be ready.
- process(opts.totalSubs, "sub_ready", bind(expect, _1, "ready"));
+ process(opts.totalSubs, fqn("sub_ready"), bind(expect, _1, "ready"));
LocalQueue pubDone;
LocalQueue subDone;
subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false);
- subs.subscribe(pubDone, "pub_done");
- subs.subscribe(subDone, "sub_done");
+ subs.subscribe(pubDone, fqn("pub_done"));
+ subs.subscribe(subDone, fqn("sub_done"));
double txrateTotal(0);
double mbytesTotal(0);
@@ -363,15 +411,18 @@ struct Controller : public Client {
for (size_t j = 0; j < opts.iterations; ++j) {
AbsTime start=now();
- send(opts.totalPubs, "pub_start", "start"); // Start publishers
+ send(opts.totalPubs, fqn("pub_start"), "start"); // Start publishers
+ if (j) {
+ send(opts.totalPubs, fqn("sub_iteration"), "next"); // Start subscribers on next iteration
+ }
Stats pubRates;
Stats subRates;
- process(opts.totalPubs, pubDone, "pub_done", boost::ref(pubRates));
- process(opts.totalSubs, subDone, "sub_done", boost::ref(subRates));
+ process(opts.totalPubs, pubDone, fqn("pub_done"), boost::ref(pubRates));
+ process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates));
- AbsTime end=now();
+ AbsTime end=now();
double time=secs(start, end);
double txrate=opts.transfers/time;
@@ -411,7 +462,6 @@ struct Controller : public Client {
}
catch (const std::exception& e) {
cout << "Controller exception: " << e.what() << endl;
- exit(1);
}
}
};
@@ -422,12 +472,12 @@ struct PublishThread : public Client {
string routingKey;
PublishThread() {};
-
+
PublishThread(string key, string dest=string()) {
destination=dest;
routingKey=key;
}
-
+
void run() { // Publisher
try {
string data;
@@ -445,7 +495,7 @@ struct PublishThread : public Client {
}
} else {
size_t msgSize=max(opts.size, sizeof(size_t));
- data = string(msgSize, 'X');
+ data = string(msgSize, 'X');
}
Message msg(data, routingKey);
@@ -453,19 +503,21 @@ struct PublishThread : public Client {
msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
- if (opts.tx) sync(session).txSelect();
+ if (opts.txPub){
+ session.txSelect();
+ }
SubscriptionManager subs(session);
LocalQueue lq;
- subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true);
- subs.subscribe(lq, "pub_start");
-
+ subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true);
+ subs.subscribe(lq, fqn("pub_start"));
+
for (size_t j = 0; j < opts.iterations; ++j) {
expect(lq.pop().getData(), "start");
AbsTime start=now();
for (size_t i=0; i<opts.count; i++) {
// Stamp the iteration into the message data, avoid
// any heap allocation.
- const_cast<std::string&>(msg.getData()).replace(offset, sizeof(size_t),
+ const_cast<std::string&>(msg.getData()).replace(offset, sizeof(size_t),
reinterpret_cast<const char*>(&i), sizeof(size_t));
if (opts.syncPub) {
sync(session).messageTransfer(
@@ -478,23 +530,31 @@ struct PublishThread : public Client {
arg::content=msg,
arg::acceptMode=1);
}
- if (opts.tx && ((i+1) % opts.tx == 0)) sync(session).txCommit();
- if (opts.intervalPub) ::usleep(opts.intervalPub*1000);
+ if (opts.txPub && ((i+1) % opts.txPub == 0)){
+ if (opts.commitAsync){
+ session.txCommit();
+ } else {
+ sync(session).txCommit();
+ }
+ }
+ if (opts.intervalPub)
+ qpid::sys::usleep(opts.intervalPub*1000);
}
if (opts.confirm) session.sync();
AbsTime end=now();
double time=secs(start,end);
-
+
// Send result to controller.
- Message report(lexical_cast<string>(opts.count/time), "pub_done");
+ Message report(lexical_cast<string>(opts.count/time), fqn("pub_done"));
session.messageTransfer(arg::content=report, arg::acceptMode=1);
- if (opts.tx) sync(session).txCommit();
+ if (opts.txPub){
+ sync(session).txCommit();
+ }
}
session.close();
}
catch (const std::exception& e) {
cout << "PublishThread exception: " << e.what() << endl;
- exit(1);
}
}
};
@@ -504,7 +564,7 @@ struct SubscribeThread : public Client {
string queue;
SubscribeThread() {}
-
+
SubscribeThread(string q) { queue = q; }
SubscribeThread(string key, string ex) {
@@ -529,31 +589,48 @@ struct SubscribeThread : public Client {
}
void run() { // Subscribe
- try {
- if (opts.tx) sync(session).txSelect();
+ try {
+ if (opts.txSub) sync(session).txSelect();
SubscriptionManager subs(session);
- LocalQueue lq(AckPolicy(opts.tx ? opts.tx : opts.ack));
- subs.setAcceptMode(opts.tx || opts.ack ? 0 : 1);
- subs.setFlowControl(opts.subQuota, SubscriptionManager::UNLIMITED,
- false);
- subs.subscribe(lq, queue);
+ SubscriptionSettings settings;
+ settings.autoAck = opts.txSub ? opts.txSub : opts.ack;
+ settings.acceptMode = (opts.txSub || opts.ack ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE);
+ settings.flowControl = FlowControl::messageCredit(opts.subQuota);
+ LocalQueue lq;
+ Subscription subscription = subs.subscribe(lq, queue, settings);
// Notify controller we are ready.
- session.messageTransfer(arg::content=Message("ready", "sub_ready"), arg::acceptMode=1);
- if (opts.tx) sync(session).txCommit();
-
+ session.messageTransfer(arg::content=Message("ready", fqn("sub_ready")), arg::acceptMode=1);
+ if (opts.txSub) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
+
+ LocalQueue iterationControl;
+ if (opts.iterations > 1) {
+ subs.subscribe(iterationControl, fqn("sub_iteration"), SubscriptionSettings(FlowControl::messageCredit(0)));
+ }
+
for (size_t j = 0; j < opts.iterations; ++j) {
if (j > 0) {
- //need to allocate some more credit
- session.messageFlow(queue, 0, opts.subQuota);
+ //need to wait here until all subs are done
+ session.messageFlow(fqn("sub_iteration"), 0, 1);
+ iterationControl.pop();
+
+ //need to allocate some more credit for subscription
+ session.messageFlow(queue, 0, opts.subQuota);
}
Message msg;
AbsTime start=now();
size_t expect=0;
for (size_t i = 0; i < opts.subQuota; ++i) {
msg=lq.pop();
- if (opts.tx && ((i+1) % opts.tx == 0)) sync(session).txCommit();
- if (opts.intervalSub) ::usleep(opts.intervalSub*1000);
- // TODO aconway 2007-11-23: check message order for.
+ if (opts.txSub && ((i+1) % opts.txSub == 0)) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
+ if (opts.intervalSub)
+ qpid::sys::usleep(opts.intervalSub*1000);
+ // TODO aconway 2007-11-23: check message order for.
// multiple publishers. Need an array of counters,
// one per publisher and a publisher ID in the
// message. Careful not to introduce a lot of overhead
@@ -568,29 +645,37 @@ struct SubscribeThread : public Client {
expect = n+1;
}
}
- if (opts.tx || opts.ack)
- lq.getAckPolicy().ackOutstanding(session); // Cumulative ack for final batch.
- if (opts.tx)
- sync(session).txCommit();
+ if (opts.txSub || opts.ack)
+ subscription.accept(subscription.getUnaccepted());
+ if (opts.txSub) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
AbsTime end=now();
// Report to publisher.
Message result(lexical_cast<string>(opts.subQuota/secs(start,end)),
- "sub_done");
+ fqn("sub_done"));
session.messageTransfer(arg::content=result, arg::acceptMode=1);
- if (opts.tx) sync(session).txCommit();
+ if (opts.txSub) sync(session).txCommit();
}
session.close();
}
catch (const std::exception& e) {
cout << "SubscribeThread exception: " << e.what() << endl;
- exit(1);
}
}
};
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char** argv) {
-
+ int exitCode = 0;
+ boost::ptr_vector<Client> subs(opts.subs);
+ boost::ptr_vector<Client> pubs(opts.pubs);
+
try {
opts.parse(argc, argv);
@@ -600,7 +685,7 @@ int main(int argc, char** argv) {
case TOPIC: exchange="amq.topic"; break;
case SHARED: break;
}
-
+
bool singleProcess=
(!opts.setup && !opts.control && !opts.publish && !opts.subscribe);
if (singleProcess)
@@ -608,13 +693,10 @@ int main(int argc, char** argv) {
if (opts.setup) Setup().run(); // Set up queues
- boost::ptr_vector<Client> subs(opts.subs);
- boost::ptr_vector<Client> pubs(opts.pubs);
-
// Start pubs/subs for each queue/topic.
for (size_t i = 0; i < opts.qt; ++i) {
ostringstream key;
- key << "perftest" << i; // Queue or topic name.
+ key << opts.baseName << i; // Queue or topic name.
if (opts.publish) {
size_t n = singleProcess ? opts.pubs : 1;
for (size_t j = 0; j < n; ++j) {
@@ -635,29 +717,25 @@ int main(int argc, char** argv) {
}
if (opts.control) Controller().run();
-
-
- // Wait for started threads.
- if (opts.publish) {
- for (boost::ptr_vector<Client>::iterator i=pubs.begin();
- i != pubs.end();
- ++i)
- i->thread.join();
- }
-
-
- if (opts.subscribe) {
- for (boost::ptr_vector<Client>::iterator i=subs.begin();
- i != subs.end();
- ++i)
- i->thread.join();
- }
- return 0;
}
catch (const std::exception& e) {
- cout << endl << e.what() << endl;
+ cout << endl << e.what() << endl;
+ exitCode = 1;
}
- return 1;
-}
-
+ // Wait for started threads.
+ if (opts.publish) {
+ for (boost::ptr_vector<Client>::iterator i=pubs.begin();
+ i != pubs.end();
+ ++i)
+ i->thread.join();
+ }
+
+ if (opts.subscribe) {
+ for (boost::ptr_vector<Client>::iterator i=subs.begin();
+ i != subs.end();
+ ++i)
+ i->thread.join();
+ }
+ return exitCode;
+}
diff --git a/cpp/src/tests/policy.acl b/cpp/src/tests/policy.acl
new file mode 100644
index 0000000000..ef46026555
--- /dev/null
+++ b/cpp/src/tests/policy.acl
@@ -0,0 +1 @@
+acl allow all all
diff --git a/cpp/src/tests/publish.cpp b/cpp/src/tests/publish.cpp
index b78f3fdf6d..3f456e7588 100644
--- a/cpp/src/tests/publish.cpp
+++ b/cpp/src/tests/publish.cpp
@@ -7,9 +7,9 @@
* 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
@@ -34,9 +34,12 @@
using namespace qpid;
using namespace qpid::client;
using namespace qpid::sys;
-using std::string;
+using namespace std;
-typedef std::vector<std::string> StringSet;
+namespace qpid {
+namespace tests {
+
+typedef vector<string> StringSet;
struct Args : public qpid::TestOptions {
uint size;
@@ -44,64 +47,80 @@ struct Args : public qpid::TestOptions {
bool durable;
string destination;
string routingKey;
+ bool summary;
+ bool id;
- Args() : size(256), count(1000), durable(true)
- {
+ Args() : size(256), count(1000), durable(true), routingKey("publish-consume"), summary(false), id(false) {
addOptions()
("size", optValue(size, "N"), "message size")
("count", optValue(count, "N"), "number of messages to publish")
("durable", optValue(durable, "yes|no"), "use durable messages")
("destination", optValue(destination, "<exchange name>"), "destination to publish to")
- ("routing-key", optValue(routingKey, "<key>"), "routing key to publish with");
+ ("routing-key", optValue(routingKey, "<key>"), "routing key to publish with")
+ ("summary,s", optValue(summary), "Output only the rate.")
+ ("id", optValue(id), "Add unique correlation ID");
}
};
Args opts;
-struct Client
+struct Client
{
Connection connection;
AsyncSession session;
- Client()
+ Client()
{
opts.open(connection);
session = connection.newSession();
}
- std::string id(uint i)
- {
- std::stringstream s;
- s << "msg" << i;
- return s.str();
+ // Cheap hex calculation, avoid expensive ostrstream and string
+ // creation to generate correlation ids in message loop.
+ char hex(char i) { return i<10 ? '0'+i : 'A'+i-10; }
+ void hex(char i, string& s) {
+ s[0]=hex(i>>24); s[1]=hex(i>>16); s[2]=hex(i>>8); s[3]=i;
}
void publish()
{
+ AbsTime begin=now();
Message msg(string(opts.size, 'X'), opts.routingKey);
+ string correlationId = "0000";
if (opts.durable)
msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
-
+
for (uint i = 0; i < opts.count; i++) {
- msg.getMessageProperties().setCorrelationId(id(i + 1));
+ if (opts.id) {
+ hex(i+1, correlationId);
+ msg.getMessageProperties().setCorrelationId(correlationId);
+ }
session.messageTransfer(arg::destination=opts.destination,
arg::content=msg,
arg::acceptMode=1);
}
session.sync();
+ AbsTime end=now();
+ double secs(double(Duration(begin,end))/TIME_SEC);
+ if (opts.summary) cout << opts.count/secs << endl;
+ else cout << "Time: " << secs << "s Rate: " << opts.count/secs << endl;
}
- ~Client()
+ ~Client()
{
try{
session.close();
connection.close();
- } catch(const std::exception& e) {
- std::cout << e.what() << std::endl;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
}
}
};
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char** argv)
{
try {
@@ -109,8 +128,8 @@ int main(int argc, char** argv)
Client client;
client.publish();
return 0;
- } catch(const std::exception& e) {
- std::cout << e.what() << std::endl;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
}
return 1;
}
diff --git a/cpp/src/tests/python_tests b/cpp/src/tests/python_tests
index 896692e41e..51c808a6c9 100755
--- a/cpp/src/tests/python_tests
+++ b/cpp/src/tests/python_tests
@@ -1,19 +1,29 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Run the python tests.
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping python tests, no python dir."; exit 0; }
QPID_PORT=${QPID_PORT:-5672}
PYTHON_TESTS=${PYTHON_TESTS:-$*}
-MY_DIR=`dirname \`which $0\``
-QPID_PYTHON_DIR=${QPID_PYTHON_DIR:-${MY_DIR}/../../../python}
-
-run() {
- SPEC=$1
- FAILING=$2
- ./run-tests --skip-self-test -v -s $SPEC -I $FAILING -b localhost:$QPID_PORT $PYTHON_TESTS || { echo "FAIL python tests for $SPEC"; exit 1; }
-}
+FAILING=${FAILING:-/dev/null}
-if test -d ${QPID_PYTHON_DIR} ; then
- cd ${QPID_PYTHON_DIR}
- run 0-10-errata cpp_failing_0-10.txt
-else
- echo "WARNING: No python tests. $QPID_PYTHON_DIR not found."
-fi
+python $QPID_PYTHON_TEST -b localhost:$QPID_PORT -I $FAILING $PYTHON_TESTS || exit 1
diff --git a/cpp/src/tests/python_tests.ps1 b/cpp/src/tests/python_tests.ps1
new file mode 100644
index 0000000000..ab456a1557
--- /dev/null
+++ b/cpp/src/tests/python_tests.ps1
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+
+# Run the python tests; intended to be run by run_test.ps1 which sets up
+# QPID_PORT
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping python tests as python libs not found"
+ exit 1
+}
+
+if (Test-Path env:FAILING) {
+ $fails = "-I $env:FAILING"
+}
+if (Test-Path env:PYTHON_TESTS) {
+ $tests = "$env:PYTHON_TESTS"
+}
+else {
+ $tests = "$args"
+}
+
+#cd $PYTHON_DIR
+$env:PYTHONPATH="$PYTHON_DIR;$env:PYTHONPATH"
+python $PYTHON_DIR/qpid-python-test -b localhost:$env:QPID_PORT $fails $tests
+exit $LASTEXITCODE
diff --git a/cpp/src/tests/qpid_ping.cpp b/cpp/src/tests/qpid_ping.cpp
new file mode 100644
index 0000000000..b046fdf54b
--- /dev/null
+++ b/cpp/src/tests/qpid_ping.cpp
@@ -0,0 +1,123 @@
+/*
+ *
+ * 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 "TestOptions.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/framing/Uuid.h"
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace qpid::sys;
+using namespace qpid::framing;
+using namespace qpid::client;
+using namespace qpid;
+
+namespace qpid {
+namespace tests {
+
+struct PingOptions : public qpid::TestOptions {
+ int timeout; // Timeout in seconds.
+ bool quiet; // No output
+ PingOptions() : timeout(1), quiet(false) {
+ addOptions()
+ ("timeout,t", optValue(timeout, "SECONDS"), "Max time to wait.")
+ ("quiet,q", optValue(quiet), "Don't print anything to stderr/stdout.");
+ }
+};
+
+PingOptions opts;
+
+class Ping : public Runnable {
+ Connection connection;
+ Thread thread;
+ Monitor lock;
+ enum { WAITING, SUCCESS, ERROR } status;
+
+ public:
+ Ping() : status(WAITING) {}
+
+ void run() {
+ try {
+ opts.open(connection);
+ if (!opts.quiet) cout << "Opened connection." << endl;
+ AsyncSession s = connection.newSession();
+ string qname(Uuid(true).str());
+ s.queueDeclare(arg::queue=qname,arg::autoDelete=true,arg::exclusive=true);
+ s.messageTransfer(arg::content=Message("hello", qname));
+ if (!opts.quiet) cout << "Sent message." << endl;
+ SubscriptionManager subs(s);
+ subs.get(qname);
+ if (!opts.quiet) cout << "Received message." << endl;
+ s.sync();
+ s.close();
+ connection.close();
+ Mutex::ScopedLock l(lock);
+ status = SUCCESS;
+ lock.notifyAll();
+ } catch (const exception& e) {
+ if (!opts.quiet)
+ cerr << "Unexpected exception: " << e.what() << endl;
+ Mutex::ScopedLock l(lock);
+ status = ERROR;
+ lock.notifyAll();
+ }
+ }
+
+ void start() { thread=Thread(this); }
+
+ bool wait() {
+ Mutex::ScopedLock l(lock);
+ AbsTime deadline(now(), opts.timeout*TIME_SEC);
+ while (status == WAITING && lock.wait(deadline))
+ ;
+ if (status == WAITING && !opts.quiet)
+ cerr << "Timed out after " << opts.timeout << " seconds." << endl;
+ if (status != WAITING) thread.join();
+ return status == SUCCESS;
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv) {
+ try {
+ opts.parse(argc, argv);
+ Ping ping;
+ ping.start();
+ if (!ping.wait()) exit(1);
+ if (!opts.quiet) cout << "Success!" << endl;
+ return 0;
+ } catch (const exception& e) {
+ if (!opts.quiet)
+ cerr << "Unexpected exception: " << e.what() << endl;
+ return 1;
+ }
+}
diff --git a/cpp/src/tests/qpid_stream.cpp b/cpp/src/tests/qpid_stream.cpp
new file mode 100644
index 0000000000..8195bf390e
--- /dev/null
+++ b/cpp/src/tests/qpid_stream.cpp
@@ -0,0 +1,170 @@
+/*
+ *
+ * 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 <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/sys/Runnable.h>
+#include <qpid/sys/Thread.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Options.h>
+#include <iostream>
+#include <string>
+
+using namespace qpid::messaging;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options
+{
+ std::string url;
+ std::string address;
+ uint rate;
+ bool durable;
+
+ Args() : url("amqp:tcp:127.0.0.1:5672"), address("test-queue"), rate(1000), durable(false)
+ {
+ addOptions()
+ ("url", qpid::optValue(url, "URL"), "Url to connect to.")
+ ("address", qpid::optValue(address, "ADDRESS"), "Address to stream messages through.")
+ ("rate", qpid::optValue(rate, "msgs/sec"), "Rate at which to stream messages.")
+ ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.");
+ }
+};
+
+Args opts;
+
+const std::string TIMESTAMP = "ts";
+
+uint64_t timestamp(const AbsTime& time)
+{
+ Duration t(time);
+ return t;
+}
+
+struct Client : Runnable
+{
+ virtual ~Client() {}
+ virtual void doWork(Session&) = 0;
+
+ void run()
+ {
+ try {
+ Connection connection = Connection::open(opts.url);
+ Session session = connection.newSession();
+ doWork(session);
+ session.close();
+ connection.close();
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ }
+
+ Thread thread;
+
+ void start() { thread = Thread(this); }
+ void join() { thread.join(); }
+};
+
+struct Publish : Client
+{
+ void doWork(Session& session)
+ {
+ Sender sender = session.createSender(opts.address);
+ Message msg;
+ uint64_t interval = TIME_SEC / opts.rate;
+ uint64_t sent = 0, missedRate = 0;
+ AbsTime start = now();
+ while (true) {
+ AbsTime sentAt = now();
+ msg.getHeaders()[TIMESTAMP] = timestamp(sentAt);
+ sender.send(msg);
+ ++sent;
+ AbsTime waitTill(start, sent*interval);
+ Duration delay(sentAt, waitTill);
+ if (delay < 0) {
+ ++missedRate;
+ } else {
+ qpid::sys::usleep(delay / TIME_USEC);
+ }
+ }
+ }
+};
+
+struct Consume : Client
+{
+ void doWork(Session& session)
+ {
+ Message msg;
+ uint64_t received = 0;
+ double minLatency = std::numeric_limits<double>::max();
+ double maxLatency = 0;
+ double totalLatency = 0;
+ Receiver receiver = session.createReceiver(opts.address);
+ while (receiver.fetch(msg)) {
+ session.acknowledge();//TODO: add batching option
+ ++received;
+ //calculate latency
+ uint64_t receivedAt = timestamp(now());
+ uint64_t sentAt = msg.getHeaders()[TIMESTAMP].asUint64();
+ double latency = ((double) (receivedAt - sentAt)) / TIME_MSEC;
+
+ //update avg, min & max
+ minLatency = std::min(minLatency, latency);
+ maxLatency = std::max(maxLatency, latency);
+ totalLatency += latency;
+
+ if (received % opts.rate == 0) {
+ std::cout << "count=" << received
+ << ", avg=" << (totalLatency/received)
+ << ", min=" << minLatency
+ << ", max=" << maxLatency << std::endl;
+ }
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ Publish publish;
+ Consume consume;
+ publish.start();
+ consume.start();
+ consume.join();
+ publish.join();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/cpp/src/tests/qrsh.cpp b/cpp/src/tests/qrsh.cpp
new file mode 100644
index 0000000000..0cb52b6b05
--- /dev/null
+++ b/cpp/src/tests/qrsh.cpp
@@ -0,0 +1,169 @@
+/*
+ *
+ * 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 <qpid/client/Connection.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/client/SubscriptionManager.h>
+
+#include <stdio.h>
+#include <cstdlib>
+#include <iostream>
+
+#include <sstream>
+
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+class ResponseListener : public MessageListener
+{
+ public :
+
+ int exitCode;
+
+ ResponseListener ( SubscriptionManager & subscriptions )
+ : exitCode(-1),
+ subscriptions ( subscriptions )
+ {
+ }
+
+ virtual void
+ received ( Message & message )
+ {
+ char first_word[1000];
+ sscanf ( message.getData().c_str(), "%s", first_word );
+
+ if ( ! strcmp ( first_word, "wait_response" ) )
+ {
+ // If we receive a message here, parse out the exit code.
+ sscanf ( message.getData().c_str(), "%*s%d", & exitCode );
+ subscriptions.cancel(message.getDestination());
+ }
+ else
+ if ( ! strcmp ( first_word, "get_response" ) )
+ {
+ // The remainder of the message is the file we requested.
+ fprintf ( stdout,
+ "%s",
+ message.getData().c_str() + strlen("get_response" )
+ );
+ subscriptions.cancel(message.getDestination());
+ }
+ }
+
+
+ private :
+
+ SubscriptionManager & subscriptions;
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+/*
+ * argv[1] host
+ * argv[2] port
+ * argv[3] server name
+ * argv[4] command name
+ * argv[5..N] args to the command
+ */
+int
+main ( int argc, char ** argv )
+{
+ const char* host = argv[1];
+ int port = atoi(argv[2]);
+
+
+ Connection connection;
+
+ try
+ {
+ connection.open ( host, port );
+ Session session = connection.newSession ( );
+
+ // Make a queue and bind it to fanout.
+ string myQueue = session.getId().getName();
+
+ session.queueDeclare ( arg::queue=myQueue,
+ arg::exclusive=true,
+ arg::autoDelete=true
+ );
+
+ session.exchangeBind ( arg::exchange="amq.fanout",
+ arg::queue=myQueue,
+ arg::bindingKey="my-key"
+ );
+
+ // Get ready to listen for the wait-response.
+ // or maybe a get-response.
+ // ( Although this may not be one of those types
+ // of command, get ready anyway.
+ SubscriptionManager subscriptions ( session );
+ ResponseListener responseListener ( subscriptions );
+ subscriptions.subscribe ( responseListener, myQueue );
+
+ bool response_command = false;
+ if(! strcmp("exec_wait", argv[4] ))
+ response_command = true;
+ else
+ if(! strcmp("exited", argv[4] ))
+ response_command = true;
+ else
+ if(! strcmp("get", argv[4] ))
+ response_command = true;
+
+ // Send the payload message.
+ // Skip "qrsh host_name port"
+ Message message;
+ stringstream ss;
+ for ( int i = 3; i < argc; ++ i )
+ ss << argv[i] << ' ';
+
+ message.setData ( ss.str() );
+
+ session.messageTransfer(arg::content=message,
+ arg::destination="amq.fanout");
+
+ if ( response_command )
+ subscriptions.run();
+
+ session.close();
+ connection.close();
+ return responseListener.exitCode;
+ }
+ catch ( exception const & e)
+ {
+ cerr << e.what() << endl;
+ }
+
+ return 1;
+}
+
+
+
diff --git a/cpp/src/tests/qrsh_run.cpp b/cpp/src/tests/qrsh_run.cpp
new file mode 100644
index 0000000000..cfdd0cef80
--- /dev/null
+++ b/cpp/src/tests/qrsh_run.cpp
@@ -0,0 +1,321 @@
+/*
+ *
+ * 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 <iostream>
+#include <sstream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+
+using namespace std;
+
+
+
+int
+main ( int argc, char ** argv )
+{
+ int exit_code = -1;
+ int fd[2];
+ int my_pid = getpid();
+ int child_pid;
+
+ pipe(fd);
+
+ char const * root_dir = argv[1]; // This arg is prepended by qrsh_server.
+ char const * child_name = argv[2]; // This arg comes from qrsh.
+ char const * child_path = argv[3]; // This arg comes from qrsh.
+
+ // This is the problem..
+ fprintf ( stderr, "MDEBUG qrsh_run: root_dir: |%s|\n", root_dir );
+ fprintf ( stderr, "MDEBUG qrsh_run: child_name: |%s|\n", child_name );
+ fprintf ( stderr, "MDEBUG qrsh_run: child_path: |%s|\n", child_path );
+
+ /*
+ * A named child is one for whom we will create a directory and
+ * store information. There are some magic names that are not
+ * real symbolic names -- but are instead the names of actions.
+ */
+
+ bool named_child = true;
+
+ if ( ! strcmp ( child_name, "exec" ) )
+ named_child = false;
+ else
+ if ( ! strcmp ( child_name, "exec_wait" ) )
+ named_child = false;
+ else
+ if ( ! strcmp ( child_name, "exited" ) )
+ named_child = false;
+ else
+ named_child = true;
+
+ stringstream child_dir_name;
+
+ if ( named_child )
+ {
+ child_dir_name << root_dir
+ << '/'
+ << child_name;
+
+ /*
+ * Make the child directory before forking, or there is
+ * a race in which the child might be trying to make its
+ * stdout and stderr files while we are tring to make
+ * the directory.
+ */
+ if ( -1 == mkdir ( child_dir_name.str().c_str(), 0777 ) )
+ {
+ fprintf ( stderr,
+ "qrsh_run error: Can't mkdir |%s|\n",
+ child_dir_name.str().c_str()
+ );
+ exit ( 1 );
+ }
+
+ }
+ else
+ /*
+ * If this is an 'exited' command that means we are
+ * waiting for a pre-existing child.
+ */
+ if ( ! strcmp ( child_name, "exited" ) )
+ {
+ int wait_pid = atoi(child_path);
+
+ // Find the child's symbolic name.
+ stringstream pid_to_name_file_name;
+ pid_to_name_file_name << root_dir
+ << '/'
+ << wait_pid;
+ FILE * fp = fopen ( pid_to_name_file_name.str().c_str(), "r" );
+ if (! fp)
+ {
+ fprintf ( stderr,
+ "qrsh_run %d error: Can't open pid2name file |%s|.\n",
+ my_pid,
+ pid_to_name_file_name.str().c_str()
+ );
+ exit(1);
+ }
+ char symbolic_name[1000];
+ strcpy ( symbolic_name, "qrsh_no_name" );
+ fscanf ( fp, "%s", symbolic_name );
+ fclose ( fp );
+
+ // Make the name of the child's exit code file.
+ stringstream exit_code_file_name;
+ exit_code_file_name << root_dir
+ << '/'
+ << symbolic_name
+ << "/exit_code";
+
+ struct stat stat_buf;
+ int file_does_not_exist = stat ( exit_code_file_name.str().c_str(), & stat_buf );
+
+ /*
+ * If the result of stat is zero, the file exists, which means that
+ * the command has exited. The question we are being asked here is
+ * "has it exited yet?"
+ */
+ if ( ! file_does_not_exist )
+ return 1;
+ else
+ if ( errno == ENOENT )
+ return 0;
+ else
+ return 2 ;
+ }
+
+
+ // We are not waiting on a pre-wxiting child: we have a
+ // new child to create.
+
+ child_pid = fork();
+
+ if ( child_pid == 0 )
+ {
+ // This code is executed in the child process.
+
+ // If it's a *named* child, then redirect its stdout and stderr.
+ if ( named_child )
+ {
+ stringstream stdout_path,
+ stderr_path;
+
+ // Redirect the child's stdout. -----------------
+ stdout_path << root_dir
+ << '/'
+ << child_name
+ << '/'
+ << "stdout";
+
+ int redirected_stdout = open ( stdout_path.str().c_str(),
+ O_WRONLY|O_CREAT|O_TRUNC,
+ S_IRWXU|S_IRWXG|S_IRWXO
+ );
+ if ( redirected_stdout < 0 )
+ {
+ perror ( "qrsh_run: error opening redirected_stdout: " );
+ fprintf ( stderr, "stdout path: |%s|\n", stdout_path.str().c_str() );
+ exit ( 1 );
+ }
+ if ( -1 == dup2 ( redirected_stdout, 1 ) )
+ {
+ perror ( "qrsh_run: dup2 (stdout) error: " );
+ exit(1);
+ }
+
+ // Redirect the child's stderr. -----------------
+ stderr_path << root_dir
+ << '/'
+ << child_name
+ << '/'
+ << "stderr";
+
+ int redirected_stderr = open ( stderr_path.str().c_str(),
+ O_WRONLY|O_CREAT|O_TRUNC,
+ S_IRWXU|S_IRWXG|S_IRWXO
+ );
+ if ( redirected_stderr < 0 )
+ {
+ perror ( "qrsh_run: error opening redirected_stderr: " );
+ fprintf ( stderr, "stderr path: |%s|\n", stderr_path.str().c_str() );
+ exit ( 1 );
+ }
+ if(-1 == dup2 ( redirected_stderr, 2 ) )
+ {
+ perror ( "qrsh_run: dup2 (stderr) error: " );
+ exit(1);
+ }
+ }
+
+ fprintf ( stderr, "MDEBUG ------------- qrsh_run argv -------------\n" );
+ for ( int i = 0; i < argc; ++ i )
+ fprintf ( stderr, "MDEBUG argv[%d] : |%s|\n", i, argv[i] );
+
+ execv ( child_path, argv + 2 );
+ perror ( "qrsh_run: execv error: " );
+ fprintf ( stderr, "on path |%s|\n", child_path );
+ exit ( 1 );
+ }
+ else
+ {
+ // This code is executed in the parent process.
+
+ if ( named_child )
+ {
+ // Write the name-to-pid mapping.
+ stringstream pid_file_name;
+ pid_file_name << child_dir_name.str()
+ << "/pid";
+
+ FILE * fp;
+ if ( ! (fp = fopen ( pid_file_name.str().c_str(), "w") ) )
+ {
+ fprintf ( stderr,
+ "qrsh_run %d error: Can't open file |%s|\n",
+ my_pid,
+ pid_file_name.str().c_str()
+ );
+ exit(1);
+ }
+ fprintf ( fp, "%d\n", child_pid );
+ fclose ( fp );
+
+
+ // Write the pid-to-name mapping.
+ stringstream name_to_pid_file_name;
+ name_to_pid_file_name << root_dir
+ << '/'
+ << child_pid;
+ if(! (fp = fopen ( name_to_pid_file_name.str().c_str(), "w")))
+ {
+ fprintf ( stderr,
+ "qrsh_run %d error: Can't open file |%s|\n",
+ my_pid,
+ name_to_pid_file_name.str().c_str()
+ );
+ exit(1);
+ }
+ fprintf ( fp, "%s\n", child_name );
+ fclose(fp);
+ }
+
+ pid_t awaited_pid;
+ while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) )
+ {
+ fprintf ( stderr,
+ "qrsh_run %d info: parent: waiting for child %d...\n",
+ my_pid,
+ child_pid
+ );
+ sleep(1);
+ }
+
+ if ( -1 == awaited_pid )
+ {
+ fprintf ( stderr, "qrsh_run error awaiting child!\n" );
+ exit ( 1 );
+ }
+
+ /*
+ * Write the exit code.
+ */
+ exit_code >>= 8;
+
+ if ( named_child )
+ {
+ if ( child_pid == awaited_pid )
+ {
+ stringstream exit_code_file_name;
+ exit_code_file_name << child_dir_name.str()
+ << "/exit_code";
+
+ FILE * fp;
+ if ( ! (fp = fopen ( exit_code_file_name.str().c_str(), "w") ) )
+ {
+ fprintf ( stderr,
+ "qrsh_run error: Can't open file |%s|\n",
+ exit_code_file_name.str().c_str()
+ );
+ exit(1);
+ }
+ fprintf ( fp, "%d\n", exit_code );
+ fclose ( fp );
+ }
+ }
+ }
+
+ fprintf ( stderr, "MDEBUG qrsh_run returning exit code %d\n", exit_code );
+ return exit_code;
+}
+
+
+
+
diff --git a/cpp/src/tests/qrsh_server.cpp b/cpp/src/tests/qrsh_server.cpp
new file mode 100644
index 0000000000..f1163ba479
--- /dev/null
+++ b/cpp/src/tests/qrsh_server.cpp
@@ -0,0 +1,1065 @@
+/*
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <dirent.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <qpid/client/Connection.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/client/SubscriptionManager.h>
+
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace std;
+
+
+namespace qpid {
+namespace tests {
+
+int
+mrand ( int max_desired_val )
+{
+ double zero_to_one = (double) rand() / (double) RAND_MAX;
+ return (int) (zero_to_one * (double) max_desired_val);
+}
+
+
+
+char *
+file2str ( char const * file_name )
+{
+ FILE * fp = fopen ( file_name, "r" );
+ if(! fp)
+ {
+ fprintf ( stderr, "file2str error: can't open file |%s|.\n", file_name );
+ return 0;
+ }
+
+ fseek ( fp, 0, SEEK_END );
+ size_t file_len = (size_t) ftell ( fp );
+ rewind ( fp );
+ char * content = (char *) malloc ( file_len + 1 );
+
+ if ( ! content )
+ {
+ fprintf ( stderr,
+ "file2str error: can't malloc %d bytes.\n",
+ (int)file_len
+ );
+ return 0;
+ }
+
+ size_t items_read = fread ( content, file_len, 1, fp );
+
+ if ( 1 != items_read )
+ {
+ fprintf ( stderr, "file2str error: read failed.\n" );
+ free ( content );
+ return 0;
+ }
+
+ fclose ( fp );
+ content[file_len] = 0;
+
+ return content;
+}
+
+
+
+
+
+class QrshServer : public MessageListener
+{
+ public:
+
+ QrshServer ( SubscriptionManager & subscriptions,
+ char const * name,
+ char const * qrsh_run_path,
+ char const * host,
+ int port
+ );
+
+ virtual void received ( Message & message);
+
+
+ private:
+
+ set<string> all_server_names;
+
+ stringstream data_dir;
+
+ SubscriptionManager & subscriptions;
+
+ // Is this message addressed to me?
+ bool myMessage ( Message const & message );
+
+ /* ----------------------------------------------
+ * Special Commands
+ * These are commands that the qrsh_server executes
+ * directly, rather than through a child process
+ * instance of qrsh_run.
+ */
+ void runCommand ( Message const & message );
+ void execute ( Message const & message );
+ void wait ( Message const & message );
+ void exited ( Message const & message );
+ void get ( Message const & message );
+ void rememberIntroduction ( Message const & message );
+ void getStraw ( Message const & message );
+ void addAlias ( Message const & message );
+
+ void start ( );
+ void sayHello ( );
+ void sayName ( );
+ // end Special Commands ------------------------
+
+
+ void saveCommand ( Message const & message );
+
+ void send ( string const & content );
+
+ void drawStraws ( );
+ void getNames ( );
+ void runSavedCommand ( );
+
+ char ** getArgs ( char const * s );
+ bool isProcessName ( char const * s );
+ int string_countWords ( char const * s );
+ char const * skipWord ( char const * s );
+
+
+ void string_replaceAll ( string & str,
+ string & target,
+ string & replacement
+ );
+
+
+ string name,
+ qrsh_run_path,
+ host;
+
+ vector<string *> aliases;
+
+ int port;
+
+ map < char *, int > abstract_name_map;
+
+ set < string > myFellowBrokers;
+
+ bool saidHello;
+
+ Message savedCommand;
+
+ vector < int > straws;
+ int myStraw;
+
+};
+
+
+
+QrshServer::QrshServer ( SubscriptionManager & subs,
+ char const * name,
+ char const * qrsh_run_path,
+ char const * host,
+ int port
+ )
+ : subscriptions ( subs ),
+ name ( name ),
+ qrsh_run_path ( qrsh_run_path ),
+ host ( host ),
+ port ( port ),
+ saidHello ( false ),
+ myStraw ( 0 )
+{
+ data_dir << "/tmp/qrsh_"
+ << getpid();
+
+ if(mkdir ( data_dir.str().c_str(), 0777 ) )
+ {
+ fprintf ( stderr,
+ "QrshServer::QrshServer error: can't mkdir |%s|\n",
+ data_dir.str().c_str()
+ );
+ exit ( 1 );
+ }
+}
+
+
+
+void
+QrshServer::saveCommand ( Message const & message )
+{
+ savedCommand = message;
+}
+
+
+
+void
+QrshServer::runSavedCommand ( )
+{
+ runCommand ( savedCommand );
+}
+
+
+
+void
+QrshServer::start ( )
+{
+ stringstream announcement_data;
+ announcement_data << "hello_my_name_is "
+ << name;
+
+ send ( announcement_data.str() );
+
+ saidHello = true;
+}
+
+
+
+
+void
+QrshServer::send ( string const & content )
+{
+ try
+ {
+ Message message;
+ message.setData ( content );
+
+ Connection connection;
+ connection.open ( host, port );
+ Session session = connection.newSession ( );
+ session.messageTransfer ( arg::content = message,
+ arg::destination = "amq.fanout"
+ );
+ session.close();
+ connection.close();
+ }
+ catch ( exception const & e )
+ {
+ fprintf ( stderr, "QrshServer::send error: |%s|\n", e.what() );
+ }
+}
+
+
+
+
+void
+QrshServer::sayHello ( )
+{
+ if ( saidHello )
+ return;
+
+ stringstream ss;
+
+ ss << "hello_my_name_is "
+ << name;
+
+ send ( ss.str() );
+ saidHello = true;
+}
+
+
+
+void
+QrshServer::sayName ( )
+{
+ fprintf ( stderr, "My name is: |%s|\n", name.c_str() );
+}
+
+
+
+
+void
+QrshServer::drawStraws ( )
+{
+ myStraw = mrand ( 1000000000 );
+ stringstream ss;
+ ss << "straw "
+ << name
+ << ' '
+ << myStraw;
+ send ( ss.str() );
+}
+
+
+
+void
+QrshServer::getStraw ( Message const & message )
+{
+ int straw;
+
+ char brokerName[1000];
+ sscanf ( message.getData().c_str(), "%*s%s", brokerName );
+
+ if ( ! strcmp ( brokerName, name.c_str() ) )
+ return;
+
+ sscanf ( message.getData().c_str(), "%*s%*s%d", & straw );
+ straws.push_back ( straw );
+
+ bool i_win = true;
+ int ties = 0;
+
+ if ( straws.size() >= myFellowBrokers.size() )
+ {
+ // All votes are in! Let's see if I win!
+ for ( unsigned int i = 0; i < straws.size(); ++ i )
+ {
+ if ( straws[i] == myStraw )
+ ++ ties;
+ else
+ if ( straws[i] > myStraw )
+ {
+ i_win = false;
+ break;
+ }
+ }
+
+ if ( i_win && (ties <= 0) )
+ {
+ myStraw = 0;
+ straws.clear();
+ runSavedCommand ( );
+ }
+ else
+ if ( i_win && (ties > 0) )
+ {
+ fprintf ( stderr, "MDEBUG oh no! drawStraws error: server %s tied with straw %d!\n", name.c_str(), straw );
+ }
+ }
+}
+
+
+
+
+/*
+ * "APB" command (all-points-bullitens (commands that are not addressed
+ * specifically to any server)) are handled directly, here.
+ * Because if I return simply "true", the normal command processing code
+ * will misinterpret the command.
+ */
+bool
+QrshServer::myMessage ( Message const & message )
+{
+ int const maxlen = 100;
+ char head[maxlen];
+ char first_word [ maxlen + 1 ];
+ strncpy ( head, message.getData().c_str(), maxlen );
+ sscanf ( head, "%s", first_word );
+
+ if ( ! strcmp ( name.c_str(), first_word ) )
+ {
+ return true;
+ }
+ else
+ {
+ // Is the given name one of my aliases?
+ char possibleAlias[1000];
+ if(1 == sscanf ( message.getData().c_str(), "%s", possibleAlias ))
+ {
+ for ( unsigned int i = 0; i < aliases.size(); ++ i )
+ {
+
+ if ( ! strcmp ( possibleAlias, aliases[i]->c_str() ))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ if ( ! strcmp ( first_word, "hello_my_name_is" ) )
+ {
+ rememberIntroduction ( message );
+ sayHello ( );
+ return false;
+ }
+ else
+ if ( ! strcmp ( first_word, "straw" ) )
+ {
+ getStraw ( message );
+ return false;
+ }
+ else
+ if ( ! strcmp ( first_word, "all" ) )
+ {
+ return true;
+ }
+ else
+ if ( ! strcmp ( first_word, "any" ) )
+ {
+ straws.clear();
+ usleep ( 200000 );
+ saveCommand ( message );
+ drawStraws ( );
+ return false;
+ }
+ else
+ return false;
+}
+
+
+
+
+void
+QrshServer::rememberIntroduction ( Message const & message )
+{
+ char brokerName [ 1000 ];
+ sscanf ( message.getData().c_str(), "%*s%s", brokerName );
+
+ if ( strcmp ( brokerName, name.c_str() ) )
+ myFellowBrokers.insert ( string ( brokerName ) );
+}
+
+
+
+
+void
+QrshServer::addAlias ( Message const & message )
+{
+ char alias[1000];
+ sscanf ( message.getData().c_str(), "%*s%*s%s", alias );
+ aliases.push_back ( new string(alias) );
+}
+
+
+
+
+void
+QrshServer::getNames ( )
+{
+ abstract_name_map.clear();
+
+ DIR * dir = opendir ( data_dir.str().c_str() );
+
+ if ( ! dir )
+ {
+ fprintf ( stderr,
+ "QrshServer::getNames error: could not open dir |%s|.\n",
+ data_dir.str().c_str()
+ );
+ return;
+ }
+
+ struct dirent * file;
+ while ( (file = readdir ( dir ) ) )
+ {
+ if ( '.' != file->d_name[0] )
+ {
+ stringstream pid_file_name;
+ pid_file_name << data_dir.str()
+ << '/'
+ << file->d_name
+ << "/pid";
+
+ int pid = 0;
+ FILE * fp;
+ if ( (fp = fopen ( pid_file_name.str().c_str(), "r" ) ) )
+ {
+ fscanf ( fp, "%d", & pid );
+ fclose ( fp );
+ abstract_name_map.insert(pair<char*, int>(strdup(file->d_name), pid));
+ }
+ else
+ {
+ /*
+ * Fail silently. The non-existence of this file
+ * is not necessarily an error.
+ */
+ }
+ }
+ }
+ closedir ( dir );
+}
+
+
+
+void
+QrshServer::string_replaceAll ( string & str,
+ string & target,
+ string & replacement
+ )
+{
+ int target_size = target.size();
+ int found_pos = 0;
+
+ while ( 0 <= (found_pos = str.find ( target ) ) )
+ str.replace ( found_pos, target_size, replacement );
+}
+
+
+
+
+bool
+QrshServer::isProcessName ( char const * str )
+{
+ getNames();
+ map<char *, int>::iterator it;
+ for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
+ {
+ if ( ! strcmp ( str, it->first ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+
+
+
+int
+QrshServer::string_countWords ( char const * s1 )
+{
+ int count = 0;
+ char const * s2 = s1 + 1;
+
+ if ( ! isspace(* s1) )
+ {
+ ++ count;
+ }
+
+ for ( ; * s2; ++ s1, ++ s2 )
+ {
+ // count space-to-word transitions.
+ if ( isspace(*s1) && (! isspace(*s2)) )
+ ++ count;
+ }
+
+ return count;
+}
+
+
+
+
+void
+QrshServer::execute ( Message const & message )
+{
+ // First, gather all the symbolic names we know.
+ getNames();
+
+ // Now make a copy of the command, that I can alter.
+ string command ( message.getData() );
+
+
+ // Replace each occurrence of every abstract name with its pid.
+ char pid_str[100];
+ map<char *, int>::iterator it;
+ for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
+ {
+ sprintf ( pid_str, "%d", it->second );
+ string target ( it->first ),
+ replacement ( pid_str );
+ string_replaceAll ( command, target, replacement );
+ }
+
+
+ char const * truncated_command = skipWord(skipWord(command.c_str()));
+
+ if ( truncated_command )
+ system ( truncated_command );
+}
+
+
+
+
+
+void
+QrshServer::get ( Message const & request_message )
+{
+ char * file_content;
+
+ /*
+ * Get the contents of the requested file.
+ */
+ char file_or_process_name[1000];
+ sscanf ( request_message.getData().c_str(), "%*s%*s%s", file_or_process_name );
+
+ if ( isProcessName ( file_or_process_name ) )
+ {
+ stringstream desired_file_name;
+ desired_file_name << data_dir.str()
+ << '/'
+ << file_or_process_name
+ << '/';
+ char requested_output_stream[1000];
+ if(1 != sscanf ( request_message.getData().c_str(),
+ "%*s%*s%*s%s",
+ requested_output_stream
+ )
+ )
+ {
+ fprintf ( stderr,
+ "QrshServer::get error: Can't read requested data file name from this message: |%s|\n",
+ request_message.getData().c_str()
+ );
+ return;
+ }
+ desired_file_name << requested_output_stream;
+ file_content = file2str ( desired_file_name.str().c_str() );
+ }
+ else
+ {
+ file_content = file2str ( file_or_process_name );
+ }
+
+ stringstream reply_data ;
+ reply_data << "get_response "
+ << file_content;
+ /*
+ * Send a response-message to the server who is waiting.
+ */
+ send ( reply_data.str() );
+}
+
+
+
+
+
+
+void
+QrshServer::exited ( Message const & message )
+{
+ int exit_code = -1;
+
+ // First, gather all the symbolic names we know.
+ getNames();
+
+ // Now make a copy of the command, that I can alter.
+ string edited_command ( message.getData() );
+
+ // Replace each occurrence of every abstract name with its pid.
+ char pid_str[100];
+ map<char *, int>::iterator it;
+ for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
+ {
+ sprintf ( pid_str, "%d", it->second );
+ string target ( it->first ),
+ replacement ( pid_str );
+ string_replaceAll ( edited_command, target, replacement );
+ }
+
+ // Skip the service name. That is not used by the child.
+ char const * truncated_command = skipWord(edited_command.c_str());
+
+ if ( truncated_command )
+ {
+ stringstream ss;
+ ss << qrsh_run_path
+ << ' '
+ << data_dir.str()
+ << ' '
+ << truncated_command;
+
+ int child_pid;
+ if ( ! (child_pid = fork() ) )
+ {
+ // This is the child.
+
+ char ** argv = getArgs ( ss.str().c_str() );
+ execv ( qrsh_run_path.c_str(), argv );
+
+ perror ( "qrsh_server: execv error: " );
+ exit ( 1 );
+ }
+ else
+ {
+ // This is the parent.
+ pid_t awaited_pid;
+ while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) )
+ {
+ fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" );
+ sleep(1);
+ }
+
+ if ( -1 == awaited_pid )
+ {
+ fprintf ( stderr, "qrsh_server error awaiting child!\n" );
+ exit ( 1 );
+ }
+
+ exit_code >>= 8;
+
+ stringstream data;
+ data << "wait_response "
+ << exit_code;
+
+ send ( data.str() );
+ }
+ }
+}
+
+
+
+
+void
+QrshServer::wait ( Message const & message )
+{
+ bool pre_existing = false;
+ if ( 3 == string_countWords ( message.getData().c_str() ) )
+ {
+ // The first word is the name of this service.
+ // The second word is "exec_wait".
+ // The third word is the symbolic name of the command to wait for.
+ // The fact that there are exactly three words means that this
+ // must be a command that has already been named and started --
+ // we just need to find its pid and wait on it.
+ pre_existing = true;
+ }
+
+
+ int exit_code = -1;
+
+ // First, gather all the symbolic names we know.
+ getNames();
+
+ // Now make a copy of the command, that I can alter.
+ string edited_command ( message.getData() );
+
+ // Replace each occurrence of every abstract name with its pid.
+ char pid_str[100];
+ map<char *, int>::iterator it;
+ for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
+ {
+ sprintf ( pid_str, "%d", it->second );
+ string target ( it->first ),
+ replacement ( pid_str );
+ string_replaceAll ( edited_command, target, replacement );
+ }
+
+ // Skip the service name. That is not used by the child.
+ char const * truncated_command = skipWord(edited_command.c_str());
+
+ if ( truncated_command )
+ {
+ stringstream ss;
+ ss << qrsh_run_path
+ << ' '
+ << data_dir.str()
+ << ' '
+ << truncated_command;
+
+ int child_pid;
+ if ( ! (child_pid = fork() ) )
+ {
+ // This is the child.
+
+ char ** argv = getArgs ( ss.str().c_str() );
+ execv ( qrsh_run_path.c_str(), argv );
+
+ perror ( "qrsh_server: execv error: " );
+ exit ( 1 );
+ }
+ else
+ {
+ // This is the parent.
+ pid_t awaited_pid;
+ while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) )
+ {
+ fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" );
+ sleep(1);
+ }
+
+ if ( -1 == awaited_pid )
+ {
+ fprintf ( stderr, "qrsh_server error awaiting child!\n" );
+ exit ( 1 );
+ }
+ }
+
+ exit_code >>= 8;
+
+ stringstream data;
+ data << "wait_response "
+ << exit_code;
+
+ send ( data.str() );
+ }
+}
+
+
+
+
+
+char const *
+QrshServer::skipWord ( char const * s )
+{
+ if(! (s && *s) )
+ return 0;
+
+ // skip past initial white space
+ while ( isspace(*s) )
+ {
+ ++ s;
+ if(! *s)
+ return 0;
+ }
+
+ // skip past first word
+ while ( ! isspace(*s) )
+ {
+ ++ s;
+ if(! *s)
+ return 0;
+ }
+
+ return s;
+}
+
+
+
+
+
+char **
+QrshServer::getArgs ( char const * str )
+{
+ char const * s = str;
+
+ char ** argv = 0;
+ vector<int> start_positions,
+ lengths;
+
+ int pos = 0;
+ int arg_len = 0;
+
+ int n_args = 0;
+ while ( 1 )
+ {
+ // advance over whitespace.
+ while ( isspace ( *s ) )
+ {
+ ++ s; ++ pos;
+ if(! *s)
+ {
+ goto done;
+ }
+ }
+
+ ++ n_args;
+ start_positions.push_back ( pos );
+ arg_len = 0;
+
+ // advance over non-whitespace.
+ while ( ! isspace ( *s ) )
+ {
+ ++ s; ++ pos; ++ arg_len;
+ if(! *s)
+ {
+ lengths.push_back ( arg_len );
+ arg_len = 0;
+ goto done;
+ }
+ }
+
+ lengths.push_back ( arg_len );
+ arg_len = 0;
+ }
+
+ done:
+
+ if ( arg_len > 0 )
+ lengths.push_back ( arg_len );
+
+ // Alloc the array.
+ argv = (char **) malloc ( sizeof(char *) * ( n_args + 1 ) );
+ argv[n_args] = 0; // mull-term the array.
+
+ for ( int i = 0; i < n_args; ++ i )
+ {
+ argv[i] = ( char *) malloc ( lengths[i] + 1 );
+ strncpy ( argv[i],
+ str + start_positions[i],
+ lengths[i]
+ );
+ argv[i][lengths[i]] = 0;
+ }
+
+ return argv;
+}
+
+
+
+void
+QrshServer::runCommand ( Message const & message )
+{
+ char const * s = message.getData().c_str();
+
+ /*
+ * Skip the first word, which is this server's name.
+ */
+ while ( isspace(*s) ) // go to start of first word.
+ ++ s;
+
+ while ( ! isspace(*s) ) // go to end of first word.
+ ++ s;
+
+ while ( isspace(*s) ) // go to start of second word.
+ ++ s;
+
+ char command_name[1000];
+ sscanf ( s, "%s", command_name );
+
+ if ( ! strcmp ( "get", command_name ) )
+ {
+ get ( message );
+ }
+ else
+ if ( ! strcmp ( "exited", command_name ) )
+ {
+ exited ( message );
+ }
+ else
+ if ( ! strcmp ( "exec_wait", command_name ) )
+ {
+ wait ( message );
+ }
+ else
+ if ( ! strcmp ( "exec", command_name ) )
+ {
+ execute ( message );
+ }
+ else
+ if ( ! strcmp ( "start", command_name ) )
+ {
+ start ( );
+ }
+ else
+ if ( ! strcmp ( "alias", command_name ) )
+ {
+ addAlias ( message );
+ }
+ else
+ if ( ! strcmp ( "sayName", command_name ) )
+ {
+ sayName ( );
+ }
+ else
+ {
+ /*
+ * If the command is not any of the "special" commands
+ * above, then it's a "normal" command.
+ * That means we run it with a child process instance of
+ * qrsh_run, which will save all its data in the qrsh dir.
+ */
+ stringstream ss;
+ ss << qrsh_run_path
+ << ' '
+ << data_dir.str()
+ << ' '
+ << s;
+
+ if ( ! fork() )
+ {
+ char ** argv = getArgs ( ss.str().c_str() );
+ execv ( qrsh_run_path.c_str(), argv );
+ perror ( "qrsh_server: execv error: " );
+ }
+ }
+}
+
+
+
+void
+QrshServer::received ( Message & message )
+{
+ if ( myMessage ( message ) )
+ runCommand ( message );
+}
+
+
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+/*
+ * fixme mick Mon Aug 3 10:29:26 EDT 2009
+ * argv[1] server name
+ * argv[2] qrsh exe path
+ * argv[3] host
+ * argv[4] port
+ */
+int
+main ( int /*argc*/, char** argv )
+{
+ const char* host = argv[3];
+ int port = atoi(argv[4]);
+ Connection connection;
+ Message msg;
+
+ srand ( getpid() );
+
+ try
+ {
+ connection.open ( host, port );
+ Session session = connection.newSession();
+
+
+ // Declare queues.
+ string myQueue = session.getId().getName();
+ session.queueDeclare ( arg::queue=myQueue,
+ arg::exclusive=true,
+ arg::autoDelete=true);
+
+ session.exchangeBind ( arg::exchange="amq.fanout",
+ arg::queue=myQueue,
+ arg::bindingKey="my-key");
+
+ // Create a server and subscribe it to my queue.
+ SubscriptionManager subscriptions ( session );
+ QrshServer server ( subscriptions,
+ argv[1], // server name
+ argv[2], // qrsh exe path
+ host,
+ port
+ );
+ subscriptions.subscribe ( server, myQueue );
+
+ // Receive messages until the subscription is cancelled
+ // by QrshServer::received()
+ subscriptions.run();
+
+ connection.close();
+ }
+ catch(const exception& error)
+ {
+ cout << error.what() << endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+
diff --git a/cpp/src/tests/qrsh_utils/10_all b/cpp/src/tests/qrsh_utils/10_all
new file mode 100755
index 0000000000..7b486ea672
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/10_all
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+echo "Asking all servers to say their names... "
+qrsh 127.0.0.1 5813 \
+ all sayName
+
+
+
+
diff --git a/cpp/src/tests/qrsh_utils/1_remote_run b/cpp/src/tests/qrsh_utils/1_remote_run
new file mode 100755
index 0000000000..5b9b307bba
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/1_remote_run
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 command_1 /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz
diff --git a/cpp/src/tests/qrsh_utils/2_forever b/cpp/src/tests/qrsh_utils/2_forever
new file mode 100755
index 0000000000..5528b0e4d8
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/2_forever
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 command_2 /home/mick/redhat/qrsh/qrsh_run/forever foo bar baz
diff --git a/cpp/src/tests/qrsh_utils/3_kill_it b/cpp/src/tests/qrsh_utils/3_kill_it
new file mode 100755
index 0000000000..afc7a03c9d
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/3_kill_it
@@ -0,0 +1,27 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+echo "Killing command 2... "
+./qrsh 127.0.0.1 5813 \
+ mrg23 exec kill -9 command_2
+
diff --git a/cpp/src/tests/qrsh_utils/4_wait_for_it b/cpp/src/tests/qrsh_utils/4_wait_for_it
new file mode 100755
index 0000000000..a4dc0da1ce
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/4_wait_for_it
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 exec_wait /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz
+echo "my_command returned an exit code of $?"
diff --git a/cpp/src/tests/qrsh_utils/5_exited b/cpp/src/tests/qrsh_utils/5_exited
new file mode 100755
index 0000000000..4fec1dcc79
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/5_exited
@@ -0,0 +1,64 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+path=/home/mick/redhat/qrsh/qrsh_run
+
+echo "Running command_3 ..."
+./qrsh 127.0.0.1 5813 \
+ mrg23 command_3 $path/my_command foo bar baz
+
+echo "Now I do some other stuff..."
+sleep 1
+echo "And then some more stuff..."
+sleep 1
+echo "and so on..."
+sleep 1
+
+echo "Now I'm waiting for command_3 ..."
+./qrsh 127.0.0.1 5813 \
+ mrg23 exited command_3
+echo "has command_3 exited: $? ."
+sleep 5
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 exited command_3
+echo "has command_3 exited: $? ."
+sleep 5
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 exited command_3
+echo "has command_3 exited: $? ."
+sleep 5
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 exited command_3
+echo "has command_3 exited: $? ."
+sleep 5
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 exited command_3
+echo "has command_3 exited: $? ."
+sleep 5
+
+
+
diff --git a/cpp/src/tests/qrsh_utils/6_get b/cpp/src/tests/qrsh_utils/6_get
new file mode 100755
index 0000000000..4b35ca98e6
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/6_get
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+echo "getting /tmp/foo ..."
+./qrsh 127.0.0.1 5813 \
+ mrg23 get /tmp/foo
+
+
+
diff --git a/cpp/src/tests/qrsh_utils/7_get_output b/cpp/src/tests/qrsh_utils/7_get_output
new file mode 100755
index 0000000000..59911089ec
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/7_get_output
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+echo "Run a command..."
+./qrsh 127.0.0.1 5813 \
+ mrg23 command_4 /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz
+
+echo "Wait for a while..."
+sleep 20
+
+echo "Get stderr output:"
+echo "------------- begin stderr ---------------"
+./qrsh 127.0.0.1 5813 \
+ mrg23 get command_4 stderr
+echo "------------- end stderr ---------------"
+echo " "
+echo " "
+echo " "
+echo "Get stdout output:"
+echo "------------- begin stdout ---------------"
+./qrsh 127.0.0.1 5813 \
+ mrg23 get command_4 stdout
+echo "------------- end stdout ---------------"
+
diff --git a/cpp/src/tests/qrsh_utils/8_any b/cpp/src/tests/qrsh_utils/8_any
new file mode 100755
index 0000000000..2a922ea0e0
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/8_any
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#! /bin/bash
+
+echo "asking any server to say his name ..."
+./qrsh 127.0.0.1 5813 \
+ any sayName
+sleep 1
+echo "asking any server to say his name ..."
+./qrsh 127.0.0.1 5813 \
+ any sayName
+sleep 1
+echo "asking any server to say his name ..."
+./qrsh 127.0.0.1 5813 \
+ any sayName
+sleep 1
+echo "asking any server to say his name ..."
+./qrsh 127.0.0.1 5813 \
+ any sayName
+sleep 1
+
+
+
+
diff --git a/cpp/src/tests/BasicP2PTest.h b/cpp/src/tests/qrsh_utils/9_alias
index b2611f0301..a4cfdfdf9a 100644..100755
--- a/cpp/src/tests/BasicP2PTest.h
+++ b/cpp/src/tests/qrsh_utils/9_alias
@@ -1,5 +1,3 @@
-#ifndef _BasicP2PTest_
-#define _BasicP2PTest_
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -11,7 +9,7 @@
* 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
@@ -19,28 +17,22 @@
* specific language governing permissions and limitations
* under the License.
*
- */
+ */
+
+#! /bin/bash
-#include <memory>
-#include <sstream>
+# Make a group of two of the servers, using "alias",
+# and send the group a command.
-#include "qpid/Exception.h"
-#include "qpid/client/Channel.h"
-#include "qpid/client/Message.h"
-#include "qpid/client/Connection.h"
-#include "qpid/client/MessageListener.h"
-#include "SimpleTestCaseBase.h"
+qrsh 127.0.0.1 5813 \
+ mrg22 alias group_1
+qrsh 127.0.0.1 5813 \
+ mrg23 alias group_1
+echo "Asking group_1 to say their names... "
+qrsh 127.0.0.1 5813 \
+ group_1 sayName
-namespace qpid {
-class BasicP2PTest : public SimpleTestCaseBase
-{
- class Receiver;
-public:
- void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options);
-};
-}
-#endif
diff --git a/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp b/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp
new file mode 100644
index 0000000000..386e2f73f0
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp
@@ -0,0 +1,52 @@
+/*
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+
+
+
+main ( int argc, char ** argv )
+{
+ fprintf ( stderr, "Hello, I am the Example Child!\n");
+ fprintf ( stderr, "my arguments %d are:\n", argc - 1 );
+ fprintf ( stdout, "And hello to stdout, too!\n");
+
+ int i;
+ for ( i = 1; i < argc; ++ i )
+ {
+ fprintf ( stderr, "arg %d: |%s|\n", i, argv[i] );
+ }
+
+ for ( i = 0; i < 15; ++ i )
+ {
+ fprintf ( stderr, "child sleeping...\n" );
+ sleep ( 1 );
+ }
+
+ fprintf ( stderr, "child exiting with code 13.\n" );
+
+ return 13;
+}
+
+
+
+
diff --git a/cpp/src/tests/qrsh_utils/qrsh_forever.cpp b/cpp/src/tests/qrsh_utils/qrsh_forever.cpp
new file mode 100644
index 0000000000..191a9bca11
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/qrsh_forever.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 <stdio.h>
+#include <unistd.h>
+
+
+
+main ( int argc, char ** argv )
+{
+ fprintf ( stderr, "Hello, I am the Forever Example Child!\n");
+ fprintf ( stderr, "my %d arguments are:\n", argc - 1 );
+
+ int i;
+ for ( i = 1; i < argc; ++ i )
+ fprintf ( stderr, "arg %d: |%s|\n", i, argv[i] );
+
+ for ( i = 0; i >= 0; ++ i )
+ {
+ fprintf ( stderr, "child sleeping forever %d ...\n" , i);
+ sleep ( 1 );
+ }
+
+ fprintf ( stderr, "child exiting with code 12.\n" );
+
+ return 12;
+}
+
+
+
+
diff --git a/cpp/src/tests/qrsh_utils/qsh_doc.txt b/cpp/src/tests/qrsh_utils/qsh_doc.txt
new file mode 100644
index 0000000000..ad5990b38b
--- /dev/null
+++ b/cpp/src/tests/qrsh_utils/qsh_doc.txt
@@ -0,0 +1,309 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+##############################################
+ qrsh: a Qpid-based remote shell utility
+
+ Last updated: 3 Aug 09 Mick Goulish
+##############################################
+
+
+
+=============================
+Overview
+=============================
+
+ You're writing a multi-box test, and you want to write a
+ shell script in which you start processes on other boxes
+ and kill them (or send arbitrary signals to them).
+
+ But ssh doesn't let you signal them, and bash isn't the
+ greatest language in the world for creating data structures
+ (like you need to associate the PIDs with box names and
+ executable names.)
+
+ Qsh is a utility implemented on Qpid that you can use from
+ within your bash script, or any other scripting language.
+ With it, you can:
+
+ 1. run any executable on any box in your cluster.
+
+ 2. don't worry about PIDs and box-names. You associate
+ your own abstract names with the executable instances,
+ and then use those names in the rest of your script.
+ I.e. "broker_1" "sender_3" etc.
+
+ 3. Launch the executable and wait until it returns, and
+ get its exit code.
+
+ 4. Launch your executable and do other stuff, then come
+ back later and see if it has exited.
+
+ 5. Get whatever it sent to stdout or stderr.
+
+ 6. Get the contents of any other file.
+
+ 7. send a command to all your boxes at once
+
+ 8. send a command to a randomly selected box.
+
+ 9. define groups of boxes, and send a command simultaneously
+ to all boxes in a given group.
+
+
+
+
+=============================
+Using It
+=============================
+
+ 1. You need to run a Qpid broker.
+
+ 2. You start a Qpid client ( which is called a qrsh_server )
+ on all the boxes you care about. And you give them all
+ names like "mrg13", "mrg14" etc. The names can be anything
+ you want, but I've always used one qrsh_server per box,
+ and given it the box name. ( However, you can run two on
+ one box, they won't collide. )
+
+ 3. After you start all servers, send a "start" command to any
+ one of them:
+
+ 4. The qrsh_servers use the fanout exchange to talk to each
+ other.
+
+ 5. In your script, you run an executable called "qrsh". It knows
+ how to talk to the servers, do what you want, and retrieve
+ the data you want.
+
+
+ example start script: (this does 4 servers on the same box)
+ -------------------------------------------------------------
+
+ echo "Starting server mrg22 ..."
+ ./qrsh_server mrg22 ./qrsh_run 127.0.0.1 5813 &
+
+ echo "Starting server mrg23 ..."
+ ./qrsh_server mrg23 ./qrsh_run 127.0.0.1 5813 &
+
+ echo "Starting server mrg24 ..."
+ ./qrsh_server mrg24 ./qrsh_run 127.0.0.1 5813 &
+
+ echo "Starting server mrg25 ..."
+ ./qrsh_server mrg25 ./qrsh_run 127.0.0.1 5813 &
+
+ echo "Issuing start command..."
+ sleep 2
+ ./qrsh 127.0.0.1 5813 mrg22 start
+ sleep 1
+
+ echo "Ready."
+
+ # end of script.
+
+
+
+
+
+
+=============================
+Qrsh Syntax
+=============================
+
+ qrsh host port server_name command_name arg*
+
+
+ "host" and "port" specify the Qpid server to connect to.
+
+ "server_name" can be anything you want. I always use the name
+ of the box that the server is running on.
+
+ "command_name" is the name that you choose to assign to
+ the process you are running. Each process that you decide
+ to name must have a unique name within this script.
+
+ Or it could be a reserved command name, that Qsh
+ interprets in a special way.
+
+ Reserved command names are:
+
+ exec
+ exec_wait
+ exited
+ get
+
+ "exec" means "interpret the rest of the command line as a
+ command to be executed by the designated server.
+
+ "exec_wait" means same as "exec", but wait for the command
+ to terminate, and return its exit code.
+
+ "exited" -- you provide 1 arg, which is an abstract
+ process name. qrsh returns 1 if that process has exited,
+ else 0.
+
+ "get" -- you provide one arg which is a path. qrsh returns
+ (by printing to stdout) the contents of that file.
+
+ "arg*" is zero or more arguments. They are interpreted
+ differently depending on whether you are using one of
+ the above reserved command names, or making up your own
+ abstract name for a command.
+
+
+
+
+=============================
+Examples
+=============================
+
+ 1. Run a process on a remote box.
+
+ qrsh mrg23 command_1 /usr/sbin/whatever foo bar baz
+
+ Returns immediately.
+
+
+
+ 2. Kill a process that you started earlier:
+
+ qrsh mrg23 exec kill -9 command_1
+
+ After the word "exec" put any command line you want.
+ The server you're sending this to will replace all abstract
+ names in the command with process IDs. ( In this example,
+ just the word "command_1" will be replaced. ) Then it will
+ execute the command.
+
+
+
+ 3. Execute a command, and wait for it to finish
+
+ qrsh mrg23 exec_wait command_name args
+
+
+
+ 4. Check on whether a command you issude earlier has exited.
+
+ ./qrsh mrg23 exited command_3
+
+ Returns 1 if it has exited, else 0.
+
+
+
+ 5. Get the contents of a file from the remote system:
+
+ ./qrsh mrg23 get /tmp/foo
+
+ Prints the contents to stdout.
+
+
+
+ 6. Send a command to all servers at once:
+
+ # This example causes them all to print thir names to stderr.
+ ./qrsh all sayName
+
+
+ 7. Define a group of servers and send a command to that group.
+
+ #! /bin/bash
+
+ # Make a group of two of the servers, using "alias",
+ # and send the group a command.
+
+ qrsh 127.0.0.1 5813 \
+ mrg22 alias group_1
+
+ qrsh 127.0.0.1 5813 \
+ mrg23 alias group_1
+
+ echo "Asking group_1 to say their names... "
+ qrsh 127.0.0.1 5813 \
+ group_1 sayName
+
+ # end of script.
+
+
+
+
+ 8. Execute a command and get its stdout and stderr contents.
+
+ #! /bin/bash
+
+ echo "Run a command..."
+ ./qrsh 127.0.0.1 5813 \
+ mrg23 command_4 my_command foo bar baz
+
+ echo "Wait for a while..."
+ sleep 10
+
+ echo "Get stderr output:"
+ echo "------------- begin stderr ---------------"
+ ./qrsh 127.0.0.1 5813 \
+ mrg23 get command_4 stderr
+ echo "------------- end stderr ---------------"
+ echo " "
+
+ echo " "
+ echo "Get stdout output:"
+ echo "------------- begin stdout ---------------"
+ ./qrsh 127.0.0.1 5813 \
+ mrg23 get command_4 stdout
+ echo "------------- end stdout ---------------"
+
+ # end of script.
+
+
+
+
+ 9. Send a command to one of your servers, selected
+ at random.
+
+ #! /bin/bash
+
+ # I do it multiple times here, so I can see
+ # that it really is selecting randomly.
+
+ echo "asking any server to say his name ..."
+ ./qrsh 127.0.0.1 5813 \
+ any sayName
+ sleep 1
+
+ echo "asking any server to say his name ..."
+ ./qrsh 127.0.0.1 5813 \
+ any sayName
+ sleep 1
+
+ echo "asking any server to say his name ..."
+ ./qrsh 127.0.0.1 5813 \
+ any sayName
+ sleep 1
+
+ echo "asking any server to say his name ..."
+ ./qrsh 127.0.0.1 5813 \
+ any sayName
+
+ # end of script.
+
+
+
+
diff --git a/cpp/src/tests/quick_perftest b/cpp/src/tests/quick_perftest
index 676436fdc7..4f7cf3cb54 100755
--- a/cpp/src/tests/quick_perftest
+++ b/cpp/src/tests/quick_perftest
@@ -1,2 +1,22 @@
#!/bin/sh
+
+#
+# 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.
+#
+
exec `dirname $0`/run_test ./perftest --summary --count 100
diff --git a/cpp/src/tests/quick_topictest b/cpp/src/tests/quick_topictest
index b1e63b9350..0a6b29b33f 100755
--- a/cpp/src/tests/quick_topictest
+++ b/cpp/src/tests/quick_topictest
@@ -1,6 +1,27 @@
#!/bin/sh
+
+#
+# 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.
+#
+
+
# Quick and quiet topic test for make check.
-test -z "$srcdir" && srcdir=.
+test -z "$srcdir" && srcdir=`dirname $0`
$srcdir/topictest -s2 -m2 -b1 > topictest.log 2>&1 || {
echo $0 FAILED:
cat topictest.log
diff --git a/cpp/src/tests/quick_topictest.ps1 b/cpp/src/tests/quick_topictest.ps1
new file mode 100644
index 0000000000..b2efbdd684
--- /dev/null
+++ b/cpp/src/tests/quick_topictest.ps1
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+# Quick and quiet topic test for make check.
+[string]$me = $myInvocation.InvocationName
+$srcdir = Split-Path $me
+& "$srcdir\topictest.ps1" -subscribers 2 -messages 2 -batches 1 > topictest.log 2>&1
+if (!$?) {
+ "$me FAILED:"
+ cat topictest.log
+ exit $LastExitCode
+}
+Remove-Item topictest.log
+exit 0
diff --git a/cpp/src/tests/quick_txtest b/cpp/src/tests/quick_txtest
new file mode 100755
index 0000000000..938e3805d8
--- /dev/null
+++ b/cpp/src/tests/quick_txtest
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+exec `dirname $0`/run_test ./txtest --queues 4 --tx-count 10 --quiet
diff --git a/cpp/src/tests/receiver.cpp b/cpp/src/tests/receiver.cpp
new file mode 100644
index 0000000000..e01954e31a
--- /dev/null
+++ b/cpp/src/tests/receiver.cpp
@@ -0,0 +1,139 @@
+/*
+ *
+ * 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 <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/SubscriptionManager.h>
+#include <qpid/client/SubscriptionSettings.h>
+#include "TestOptions.h"
+
+#include <iostream>
+#include <fstream>
+
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string queue;
+ uint messages;
+ bool ignoreDuplicates;
+ uint creditWindow;
+ uint ackFrequency;
+ bool browse;
+
+ Args() : queue("test-queue"), messages(0), ignoreDuplicates(false), creditWindow(0), ackFrequency(1), browse(false)
+ {
+ addOptions()
+ ("queue", qpid::optValue(queue, "QUEUE NAME"), "Queue from which to request messages")
+ ("messages", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely")
+ ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)")
+ ("credit-window", qpid::optValue(creditWindow, "N"), "Credit window (0 implies infinite window)")
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
+ ("browse", qpid::optValue(browse), "Browse rather than consuming");
+ }
+};
+
+const string EOS("eos");
+
+class Receiver : public MessageListener, public FailoverManager::Command
+{
+ public:
+ Receiver(const string& queue, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse);
+ void received(Message& message);
+ void execute(AsyncSession& session, bool isRetry);
+ private:
+ const string queue;
+ const uint count;
+ const bool skipDups;
+ SubscriptionSettings settings;
+ Subscription subscription;
+ uint processed;
+ uint lastSn;
+
+ bool isDuplicate(Message& message);
+};
+
+Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse) :
+ queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0)
+{
+ if (browse) settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED;
+ if (creditWindow) settings.flowControl = FlowControl::messageWindow(creditWindow);
+ settings.autoAck = ackFrequency;
+}
+
+void Receiver::received(Message& message)
+{
+ if (!(skipDups && isDuplicate(message))) {
+ bool eos = message.getData() == EOS;
+ if (!eos) std::cout << message.getData() << std::endl;
+ if (eos || ++processed == count) subscription.cancel();
+ }
+}
+
+bool Receiver::isDuplicate(Message& message)
+{
+ uint sn = message.getHeaders().getAsInt("sn");
+ if (lastSn < sn) {
+ lastSn = sn;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void Receiver::execute(AsyncSession& session, bool /*isRetry*/)
+{
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queue, settings);
+ subs.run();
+ if (settings.autoAck) {
+ subscription.accept(subscription.getUnaccepted());
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ FailoverManager connection(opts.con);
+ Receiver receiver(opts.queue, opts.messages, opts.ignoreDuplicates, opts.creditWindow, opts.ackFrequency, opts.browse);
+ connection.execute(receiver);
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cerr << "Failure: " << error.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/cpp/src/tests/reliable_replication_test b/cpp/src/tests/reliable_replication_test
new file mode 100755
index 0000000000..f57d11a263
--- /dev/null
+++ b/cpp/src/tests/reliable_replication_test
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Test reliability of the replication feature in the face of link
+# failures:
+
+source ./test_env.sh
+
+trap stop_brokers EXIT
+
+stop_brokers() {
+ if [[ $BROKER_A ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+ fi
+ if [[ $BROKER_B ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
+ unset BROKER_B
+ fi
+}
+
+setup() {
+ rm -f replication-source.log replication-dest.log
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable trace+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd-repl.port
+ BROKER_A=`cat qpidd-repl.port`
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd-repl.port
+ BROKER_B=`cat qpidd-repl.port`
+
+ echo "Testing replication from port $BROKER_A to port $BROKER_B"
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+
+ #create test queue (only replicate enqueues for this test):
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 1
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a
+}
+
+send() {
+ ./sender --port $BROKER_A --routing-key queue-a --send-eos 1 < replicated.expected
+}
+
+receive() {
+ rm -f replicated.actual
+ ./receiver --port $BROKER_B --queue queue-a > replicated.actual
+}
+
+bounce_link() {
+ echo "Destroying link..."
+ $PYTHON_COMMANDS/qpid-route link del "localhost:$BROKER_B" "localhost:$BROKER_A"
+ echo "Link destroyed; recreating route..."
+ sleep 2
+ $PYTHON_COMMANDS/qpid-route --ack 500 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+ echo "Route re-established"
+}
+
+if test -d ${PYTHON_DIR} && test -e $REPLICATING_LISTENER_LIB && test -e $REPLICATION_EXCHANGE_LIB ; then
+ setup
+ for i in `seq 1 100000`; do echo Message $i; done > replicated.expected
+ send &
+ receive &
+ for i in `seq 1 5`; do sleep 10; bounce_link; done;
+ wait
+ #check that received list is identical to sent list
+ diff replicated.actual replicated.expected || FAIL=1
+ if [[ $FAIL ]]; then
+ echo reliable replication test failed: expectations not met!
+ exit 1
+ else
+ echo replication reliable in the face of link failures
+ rm -f replication.actual replication.expected replication-source.log replication-dest.log qpidd-repl.port
+ fi
+fi
+
diff --git a/cpp/src/tests/replaying_sender.cpp b/cpp/src/tests/replaying_sender.cpp
new file mode 100644
index 0000000000..62912ea0fc
--- /dev/null
+++ b/cpp/src/tests/replaying_sender.cpp
@@ -0,0 +1,163 @@
+/*
+ *
+ * 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 <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageReplayTracker.h>
+#include <qpid/Exception.h>
+
+#include <iostream>
+#include <sstream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+class Sender : public FailoverManager::Command
+{
+ public:
+ Sender(const std::string& queue, uint count, uint reportFreq);
+ void execute(AsyncSession& session, bool isRetry);
+ uint getSent();
+
+ void setVerbosity ( int v ) { verbosity = v; }
+ void setPersistence ( int p ) { persistence = p; }
+
+ private:
+ MessageReplayTracker sender;
+ const uint count;
+ uint sent;
+ const uint reportFrequency;
+ Message message;
+ int verbosity;
+ int persistence;
+ string queueName;
+};
+
+Sender::Sender(const std::string& queue, uint count_, uint reportFreq )
+ : sender(10),
+ count(count_),
+ sent(0),
+ reportFrequency(reportFreq),
+ verbosity(0),
+ persistence(0),
+ queueName ( queue )
+{
+ message.getDeliveryProperties().setRoutingKey(queueName.c_str());
+}
+
+void Sender::execute(AsyncSession& session, bool isRetry)
+{
+ if (verbosity > 0)
+ std::cout << "replaying_sender " << (isRetry ? "first " : "re-") << "connect." << endl;
+ if (isRetry) sender.replay(session);
+ else sender.init(session);
+ while (sent < count) {
+ stringstream message_data;
+ message_data << ++sent;
+ message.setData(message_data.str());
+ message.getHeaders().setInt("sn", sent);
+ if ( persistence )
+ message.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+
+ sender.send(message);
+ if (count > reportFrequency && !(sent % reportFrequency)) {
+ if ( verbosity > 0 )
+ std::cout << "Sender sent "
+ << sent
+ << " of "
+ << count
+ << " on queue "
+ << queueName
+ << std::endl;
+ }
+ }
+ message.setData("That's all, folks!");
+ sender.send(message);
+
+ if ( verbosity > 0 )
+ std::cout << "SENDER COMPLETED\n";
+}
+
+uint Sender::getSent()
+{
+ return sent;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+
+ if ( argc != 8 )
+ {
+ std::cerr << "Usage: replaying_sender host port n_messages report_frequency verbosity persistence queue_name\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int n_messages = atoi(argv[3]);
+ int reportFrequency = atoi(argv[4]);
+ int verbosity = atoi(argv[5]);
+ int persistence = atoi(argv[6]);
+ char * queue_name = argv[7];
+
+ FailoverManager connection(settings);
+ Sender sender(queue_name, n_messages, reportFrequency );
+ sender.setVerbosity ( verbosity );
+ sender.setPersistence ( persistence );
+ try {
+ connection.execute ( sender );
+ if ( verbosity > 0 )
+ {
+ std::cout << "Sender finished. Sent "
+ << sender.getSent()
+ << " messages on queue "
+ << queue_name
+ << endl;
+ }
+ connection.close();
+ return 0;
+ }
+ catch(const std::exception& error)
+ {
+ cerr << "Sender (host: "
+ << settings.host
+ << " port: "
+ << settings.port
+ << " ) "
+ << " Failed: "
+ << error.what()
+ << std::endl;
+ }
+ return 1;
+}
diff --git a/cpp/src/tests/replication_test b/cpp/src/tests/replication_test
new file mode 100755
index 0000000000..691fd20b0c
--- /dev/null
+++ b/cpp/src/tests/replication_test
@@ -0,0 +1,182 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Run a test of the replication feature
+
+source ./test_env.sh
+
+trap stop_brokers INT TERM QUIT
+
+stop_brokers() {
+ if [ x$BROKER_A != x ]; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+ fi
+ if [ x$BROKER_B != x ]; then
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
+ unset BROKER_B
+ fi
+}
+
+if test -d ${PYTHON_DIR} && test -f "$REPLICATING_LISTENER_LIB" && test -f "$REPLICATION_EXCHANGE_LIB"; then
+ rm -f queue-*.repl replication-*.log #cleanup from any earlier runs
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable info+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd.port
+ BROKER_A=`cat qpidd.port`
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
+ BROKER_B=`cat qpidd.port`
+ echo "Running replication test between localhost:$BROKER_A and localhost:$BROKER_B"
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+
+ #create test queues
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-a --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-b --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-c --generate-queue-events 1
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 1
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-a
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-b
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-c
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e
+ #queue-d deliberately not declared on DR; this error case should be handled
+
+ #publish and consume from test queues on broker A:
+ i=1
+ while [ $i -le 10 ]; do
+ echo Message $i for A >> queue-a-input.repl
+ i=`expr $i + 1`
+ done
+ i=1
+ while [ $i -le 20 ]; do
+ echo Message $i for B >> queue-b-input.repl
+ i=`expr $i + 1`
+ done
+ i=1
+ while [ $i -le 15 ]; do
+ echo Message $i for C >> queue-c-input.repl
+ i=`expr $i + 1`
+ done
+
+ ./sender --port $BROKER_A --routing-key queue-a --send-eos 1 < queue-a-input.repl
+ ./sender --port $BROKER_A --routing-key queue-b --send-eos 1 < queue-b-input.repl
+ ./sender --port $BROKER_A --routing-key queue-c --send-eos 1 < queue-c-input.repl
+ echo dummy | ./sender --port $BROKER_A --routing-key queue-d --send-eos 1
+
+ ./receiver --port $BROKER_A --queue queue-a --messages 5 > /dev/null
+ ./receiver --port $BROKER_A --queue queue-b --messages 10 > /dev/null
+ ./receiver --port $BROKER_A --queue queue-c --messages 10 > /dev/null
+ ./receiver --port $BROKER_A --queue queue-d > /dev/null
+
+
+ # What we are doing is putting a message on the end of repliaction queue & waiting for it on remote side
+ # making sure all the messages have been flushed from the replication queue.
+ echo dummy | ./sender --port $BROKER_A --routing-key queue-e --send-eos 1
+ ./receiver --port $BROKER_B --queue queue-e --messages 1 > /dev/null
+
+ #shutdown broker A then check that broker Bs versions of the queues are as expected
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_A
+ unset BROKER_A
+
+ #validate replicated queues:
+ ./receiver --port $BROKER_B --queue queue-a > queue-a-backup.repl
+ ./receiver --port $BROKER_B --queue queue-b > queue-b-backup.repl
+ ./receiver --port $BROKER_B --queue queue-c > queue-c-backup.repl
+
+
+ tail -5 queue-a-input.repl > queue-a-expected.repl
+ tail -10 queue-b-input.repl > queue-b-expected.repl
+ diff queue-a-backup.repl queue-a-expected.repl || FAIL=1
+ diff queue-b-backup.repl queue-b-expected.repl || FAIL=1
+ diff queue-c-backup.repl queue-c-input.repl || FAIL=1
+
+ grep 'queue-d does not exist' replication-dest.log > /dev/null || echo "WARNING: Expected error to be logged!"
+
+ stop_brokers
+
+ # now check offsets working (enqueue based on position being set, not queue abs position)
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATING_LISTENER_LIB --replication-queue replication --create-replication-queue true --log-enable info+ --log-to-file replication-source.log --log-to-stderr 0 > qpidd.port
+ BROKER_A=`cat qpidd.port`
+
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
+ BROKER_B=`cat qpidd.port`
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-e --generate-queue-events 2
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_A" add queue queue-d --generate-queue-events 1
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-d
+
+ i=1
+ while [ $i -le 10 ]; do
+ echo Message $i for A >> queue-e-input.repl
+ i=`expr $i + 1`
+ done
+
+ ./sender --port $BROKER_A --routing-key queue-e --send-eos 1 < queue-e-input.repl
+ ./receiver --port $BROKER_A --queue queue-e --messages 10 > /dev/null
+
+ # What we are doing is putting a message on the end of repliaction queue & waiting for it on remote side
+ # making sure all the messages have been flushed from the replication queue.
+ echo dummy | ./sender --port $BROKER_A --routing-key queue-d --send-eos 1
+ ./receiver --port $BROKER_B --queue queue-d --messages 1 > /dev/null
+
+ # now check offsets working
+ $QPIDD_EXEC --no-module-dir -q --port $BROKER_B
+ unset BROKER_B
+ $QPIDD_EXEC --daemon --port 0 --no-data-dir --no-module-dir --auth no --load-module $REPLICATION_EXCHANGE_LIB --log-enable info+ --log-to-file replication-dest.log --log-to-stderr 0 > qpidd.port
+ BROKER_B=`cat qpidd.port`
+
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add queue queue-e
+ $PYTHON_COMMANDS/qpid-config -a "localhost:$BROKER_B" add exchange replication replication
+ $PYTHON_COMMANDS/qpid-route --ack 5 queue add "localhost:$BROKER_B" "localhost:$BROKER_A" replication replication
+ # now send another 15
+ i=11
+ while [ $i -le 15 ]; do
+ echo Message $i for A >> queue-e1-input.repl
+ i=`expr $i + 1`
+ done
+ ./sender --port $BROKER_A --routing-key queue-e --send-eos 1 < queue-e1-input.repl
+
+ ./receiver --port $BROKER_B --queue queue-e > queue-e-backup.repl
+ diff queue-e-backup.repl queue-e1-input.repl || FAIL=1
+
+ stop_brokers
+
+ if [ x$FAIL != x ]; then
+ echo replication test failed: expectations not met!
+ exit 1
+ else
+ echo queue state replicated as expected
+ rm -f queue-*.repl replication-*.log
+ fi
+
+else
+ echo "Skipping replication test, plugins not built or python utils not located"
+fi
+
diff --git a/cpp/src/tests/restart_cluster b/cpp/src/tests/restart_cluster
index e288805674..6a6abc8042 100755
--- a/cpp/src/tests/restart_cluster
+++ b/cpp/src/tests/restart_cluster
@@ -1,4 +1,24 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Re-start a cluster on the local host.
srcdir=`dirname $0`
diff --git a/cpp/src/tests/resuming_receiver.cpp b/cpp/src/tests/resuming_receiver.cpp
new file mode 100644
index 0000000000..abd62f003e
--- /dev/null
+++ b/cpp/src/tests/resuming_receiver.cpp
@@ -0,0 +1,192 @@
+/*
+ *
+ * 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 <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/SubscriptionManager.h>
+
+#include <iostream>
+#include <fstream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+
+
+namespace qpid {
+namespace tests {
+
+class Listener : public MessageListener,
+ public FailoverManager::Command,
+ public FailoverManager::ReconnectionStrategy
+{
+ public:
+ Listener ( int report_frequency = 1000,
+ int verbosity = 0,
+ char const * queue_name = "message_queue" );
+ void received(Message& message);
+ void execute(AsyncSession& session, bool isRetry);
+ void check();
+ void editUrlList(vector<Url>& urls);
+ private:
+ Subscription subscription;
+ uint count;
+ vector<int> received_twice;
+ uint lastSn;
+ bool gaps;
+ uint reportFrequency;
+ int verbosity;
+ bool done;
+ string queueName;
+};
+
+
+Listener::Listener ( int freq, int verbosity, char const * name )
+ : count(0),
+ lastSn(0),
+ gaps(false),
+ reportFrequency(freq),
+ verbosity(verbosity),
+ done(false),
+ queueName ( name )
+{}
+
+
+void Listener::received(Message & message)
+{
+ if (message.getData() == "That's all, folks!")
+ {
+ done = true;
+ if(verbosity > 0 )
+ {
+ cout << "Shutting down listener for "
+ << message.getDestination() << endl;
+
+ cout << "Listener received "
+ << count
+ << " messages ("
+ << received_twice.size()
+ << " received_twice)"
+ << endl;
+
+ }
+ subscription.cancel();
+ if ( verbosity > 0 )
+ cout << "LISTENER COMPLETED\n";
+
+ if ( ! gaps ) {
+ cout << "no gaps were detected\n";
+ cout << received_twice.size() << " messages were received twice.\n";
+ }
+ else {
+ cout << "gaps detected\n";
+ for ( unsigned int i = 0; i < received_twice.size(); ++ i )
+ cout << "received_twice "
+ << received_twice[i]
+ << endl;
+ }
+ } else {
+ uint sn = message.getHeaders().getAsInt("sn");
+ if (lastSn < sn) {
+ if (sn - lastSn > 1) {
+ cerr << "Error: gap in sequence between " << lastSn << " and " << sn << endl;
+ gaps = true;
+ }
+ lastSn = sn;
+ ++count;
+ if ( ! ( count % reportFrequency ) ) {
+ if ( verbosity > 0 )
+ cout << "Listener has received "
+ << count
+ << " messages on queue "
+ << queueName
+ << endl;
+ }
+ } else {
+ received_twice.push_back ( sn );
+ }
+ }
+}
+
+void Listener::check()
+{
+ if (gaps) throw Exception("Detected gaps in sequence; messages appear to have been lost.");
+}
+
+void Listener::execute(AsyncSession& session, bool isRetry) {
+ if (verbosity > 0)
+ cout << "resuming_receiver " << (isRetry ? "first " : "re-") << "connect." << endl;
+ if (!done) {
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queueName);
+ subs.run();
+ }
+}
+
+void Listener::editUrlList(vector<Url>& urls)
+{
+ /**
+ * A more realistic algorithm would be to search through the list
+ * for prefered hosts and ensure they come first in the list.
+ */
+ if (urls.size() > 1) rotate(urls.begin(), urls.begin() + 1, urls.end());
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+
+ if ( argc != 6 )
+ {
+ cerr << "Usage: resuming_receiver host port report_frequency verbosity queue_name\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int reportFrequency = atoi(argv[3]);
+ int verbosity = atoi(argv[4]);
+ char * queue_name = argv[5];
+
+ Listener listener ( reportFrequency, verbosity, queue_name );
+ FailoverManager connection(settings, &listener);
+
+ try {
+ connection.execute(listener);
+ connection.close();
+ listener.check();
+ return 0;
+ } catch(const exception& error) {
+ cerr << "Receiver failed: " << error.what() << endl;
+ }
+ return 1;
+}
+
+
+
diff --git a/cpp/src/tests/ring_queue_test b/cpp/src/tests/ring_queue_test
new file mode 100755
index 0000000000..553746eb49
--- /dev/null
+++ b/cpp/src/tests/ring_queue_test
@@ -0,0 +1,174 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+# Test script for validating the behaviour of a ring queue
+
+QUEUE_NAME=ring-queue
+LIMIT=100
+DURABLE=0
+MESSAGES=10000
+SENDERS=1
+RECEIVERS=1
+CONCURRENT=0
+BROKER_URL="-a ${QPID_BROKER:-localhost}:${QPID_PORT:-5672}"
+
+setup() {
+ if [[ $DURABLE -gt 0 ]]; then
+ EXTRA_ARGS=" --durable"
+ fi
+ qpid-config $BROKER_URL add queue $QUEUE_NAME --max-queue-count $LIMIT --limit-policy ring $EXTRA_ARGS
+}
+
+send() {
+ datagen --count $MESSAGES | tee sender_${QUEUE_NAME}_${1} | sender --durable $DURABLE --routing-key $QUEUE_NAME
+}
+
+receive() {
+ #TODO: allow a variety of receiver options to be passed in (ack-frequency, credit-window etc)
+ receiver --queue $QUEUE_NAME > receiver_${QUEUE_NAME}_${1}
+}
+
+cleanup() {
+ rm -f sender_${QUEUE_NAME}_* receiver_${QUEUE_NAME}_*
+ qpid-config $BROKER_URL del queue $QUEUE_NAME --force
+}
+
+log() {
+ echo $1
+}
+
+fail() {
+ echo $1
+ FAILED=1
+}
+
+validate() {
+ if [[ $RECEIVERS -eq 0 ]]; then
+ #queue should have $LIMIT messages on it, but need to send an eos also
+ sender --routing-key $QUEUE_NAME --send-eos 1 < /dev/null
+ received=$(receiver --queue $QUEUE_NAME --browse | wc -l)
+ if [[ received -eq $(( $LIMIT - 1)) ]]; then
+ log "queue contains $LIMIT messages as expected"
+ else
+ fail "queue does not contain the expected $LIMIT messages (received $received)"
+ fi
+ elif [[ $CONCURRENT -eq 0 ]]; then
+ #sum of length of all output files should be equal to $LIMIT - $RECEIVERS (1 eos message each)
+ received=$(cat receiver_${QUEUE_NAME}_* | wc -l)
+ expected=$(( $LIMIT - $RECEIVERS ))
+ if [[ $received -eq $expected ]]; then
+ log "received $LIMIT messages as expected"
+ else
+ fail "received $received messages, expected $expected"
+ fi
+ #if there were only a single sender and receiver (executed serially) we can check the
+ #actual received contents
+ if [[ $RECEIVERS -eq 1 ]] && [[ $SENDERS -eq 1 ]]; then
+ tail -n $(($LIMIT - 1)) sender_${QUEUE_NAME}_1 | diff - receiver_${QUEUE_NAME}_1 || FAILED=1
+ if [[ $FAILED -eq 1 ]]; then
+ fail "did not receive expected messages"
+ else
+ log "received messages matched expectations"
+ fi
+ fi
+ else
+ #multiple receivers, concurrent with senders; ring queue functionality cannot be validated in this case
+ if [[ $(cat receiver_${QUEUE_NAME}_* | wc -l) -le $(cat sender_${QUEUE_NAME}_* | wc -l) ]]; then
+ log "sent at least as many messages as were received"
+ else
+ #Note: if any receiver was browsing, this would be valid (would need to add 'sort | uniq')
+ # to pipeline above
+ fail "received more messages than were sent"
+ fi
+ fi
+
+ if [[ $FAILED ]]; then
+ echo $(basename $0): FAILED
+ exit 1
+ else
+ cleanup
+ fi
+}
+
+run_test() {
+ if [[ $CONCURRENT -eq 0 ]]; then
+ echo "Starting $SENDERS senders followed by $RECEIVERS receivers "
+ else
+ echo "Starting $SENDERS senders concurrently with $RECEIVERS receivers"
+ fi
+ for ((i=1; i <= $SENDERS; i++)); do
+ send $i &
+ sender_pids[$i]=$!
+ done
+ if [[ $CONCURRENT -eq 0 ]] && [[ $RECEIVERS -gt 0 ]]; then
+ wait
+ sender --routing-key $QUEUE_NAME --send-eos $RECEIVERS < /dev/null
+ fi
+ for ((i=1; i <= $RECEIVERS; i++)); do
+ receive $i &
+ done
+ if [[ $CONCURRENT -gt 0 ]]; then
+ for ((i=1; i <= $SENDERS; i++)); do
+ wait ${sender_pids[$i]}
+ done
+ sender --routing-key $QUEUE_NAME --send-eos $RECEIVERS < /dev/null
+ fi
+ wait
+}
+
+usage() {
+ cat <<EOF
+$(basename $0): Test script for validating the behaviour of a ring queue
+
+Options:
+ -q <queue> the name of the queue to use
+ -s <senders> the number of senders to start
+ -r <receivers> the number of receivers to start
+ -l <limit> the limit for the ring queue
+ -m <messages> the number of messages to send
+ -c if specified, receivers will run concurrently with senders
+ -d if specified the queue and messages will be durable
+EOF
+ exit 1
+}
+
+while getopts "s:r:m:u:dch" opt ; do
+ case $opt in
+ q) QUEUE_NAME=$OPTARG ;;
+ l) LIMIT=$OPTARG ;;
+ s) SENDERS=$OPTARG ;;
+ r) RECEIVERS=$OPTARG ;;
+ m) MESSAGES=$OPTARG ;;
+ d) DURABLE=1 ;;
+ c) CONCURRENT=1 ;;
+ h) usage;;
+ ?) usage;;
+ esac
+done
+
+if [[ $SENDERS -gt 0 ]]; then
+ setup
+ run_test
+ validate
+else
+ echo "Nothing can be done if there are no senders"
+fi
+
diff --git a/cpp/src/tests/run-unit-tests b/cpp/src/tests/run-unit-tests
index 464ce131f5..862a76c4f5 100755
--- a/cpp/src/tests/run-unit-tests
+++ b/cpp/src/tests/run-unit-tests
@@ -1,4 +1,24 @@
#!/bin/sh
+
+#
+# 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.
+#
+
#
# Library names (without path or .so) and CppUnit test paths can be
# specified on the command line or in env var UNIT_TESTS. For example:
diff --git a/cpp/src/tests/run_acl_tests b/cpp/src/tests/run_acl_tests
new file mode 100755
index 0000000000..7112f621a1
--- /dev/null
+++ b/cpp/src/tests/run_acl_tests
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Run the acl tests. $srcdir is set by the Makefile.
+source ./test_env.sh
+DATA_DIR=`pwd`/data_dir
+
+trap stop_brokers INT TERM QUIT
+
+start_brokers() {
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+}
+
+test_loading_acl_from_absolute_path(){
+ POLICY_FILE=$srcdir/policy.acl
+ rm -f temp.log
+ PORT=`../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no --load-module $ACL_LIB --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null`
+ ACL_FILE=`grep "notice Read ACL file" temp.log | sed 's/^.*Read ACL file //'`
+ $QPIDD_EXEC --no-module-dir -q --port $PORT
+ if test "$ACL_FILE" != "\"$POLICY_FILE\""; then
+ echo "unable to load policy file from an absolute path";
+ return 1;
+ fi
+ rm temp.log
+}
+
+if test -d ${PYTHON_DIR} ; then
+ rm -rf $DATA_DIR
+ mkdir -p $DATA_DIR
+ cp $srcdir/policy.acl $DATA_DIR
+ start_brokers
+ echo "Running acl tests using brokers on ports $LOCAL_PORT"
+ PYTHONPATH=$PYTHON_DIR:$srcdir
+ export PYTHONPATH
+ $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl || EXITCODE=1
+ stop_brokers || EXITCODE=1
+ test_loading_acl_from_absolute_path || EXITCODE=1
+ rm -rf $DATA_DIR
+ exit $EXITCODE
+fi
+
diff --git a/cpp/src/tests/run_cli_tests b/cpp/src/tests/run_cli_tests
new file mode 100755
index 0000000000..34ce3c4f1a
--- /dev/null
+++ b/cpp/src/tests/run_cli_tests
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Run the cli-utility tests.
+
+source ./test_env.sh
+CLI_DIR=$PYTHON_COMMANDS
+
+trap stop_brokers INT TERM QUIT
+
+start_brokers() {
+ ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > qpidd.port
+ REMOTE_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $REMOTE_PORT
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running CLI tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
+ $PYTHON_COMMANDS/qpid-python-test -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL CLI tests"; exit 1;
+ fi
+fi
+
diff --git a/cpp/src/tests/run_cluster_test b/cpp/src/tests/run_cluster_test
new file mode 100755
index 0000000000..c022eea1fe
--- /dev/null
+++ b/cpp/src/tests/run_cluster_test
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+
+# Run the tests
+srcdir=`dirname $0`
+. $srcdir/ais_check
+with_ais_group $srcdir/run_test ./cluster_test
diff --git a/cpp/src/tests/run_cluster_tests b/cpp/src/tests/run_cluster_tests
new file mode 100755
index 0000000000..6064f28751
--- /dev/null
+++ b/cpp/src/tests/run_cluster_tests
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+source ./test_env.sh
+source $srcdir/ais_check
+
+test -x $QPID_PYTHON_TEST || { echo Skipping test, $QPID_PYTHON_TEST not found; exit 0; }
+
+# Delete old cluster test data
+OUTDIR=brokertest.tmp
+rm -rf $OUTDIR
+mkdir -p $OUTDIR
+
+# Ignore tests requiring a store by default.
+CLUSTER_TESTS_IGNORE=${CLUSTER_TESTS_IGNORE:--i cluster_tests.StoreTests.* -I $srcdir/cluster_tests.fail}
+CLUSTER_TESTS=${CLUSTER_TESTS:-$*}
+
+with_ais_group $QPID_PYTHON_TEST -DOUTDIR=$OUTDIR -m cluster_tests $CLUSTER_TESTS_IGNORE $CLUSTER_TESTS || exit 1
+rm -rf $OUTDIR
+#exit 0
diff --git a/cpp/src/tests/run_failover_soak b/cpp/src/tests/run_failover_soak
new file mode 100755
index 0000000000..69551a51c2
--- /dev/null
+++ b/cpp/src/tests/run_failover_soak
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+source ./test_env.sh
+. $srcdir/ais_check
+
+host=127.0.0.1
+
+unset QPID_NO_MODULE_DIR # failover_soak uses --module-dir, dont want clash
+MODULES=${MODULES:-$moduledir}
+MESSAGES=${MESSAGES:-1000000}
+REPORT_FREQUENCY=${REPORT_FREQUENCY:-20000}
+VERBOSITY=${VERBOSITY:-10}
+DURABILITY=${DURABILITY:-0}
+N_QUEUES=${N_QUEUES:-1}
+N_BROKERS=${N_BROKERS:-3}
+
+rm -f soak-*.log
+exec ./failover_soak $MODULES ./declare_queues ./replaying_sender ./resuming_receiver $MESSAGES $REPORT_FREQUENCY $VERBOSITY $DURABILITY $N_QUEUES $N_BROKERS
+
diff --git a/cpp/src/tests/run_federation_tests b/cpp/src/tests/run_federation_tests
index 6142c1c37c..8d7954a533 100755
--- a/cpp/src/tests/run_federation_tests
+++ b/cpp/src/tests/run_federation_tests
@@ -1,7 +1,27 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Run the federation tests.
-MY_DIR=`dirname \`which $0\``
-PYTHON_DIR=${MY_DIR}/../../../python
+
+source ./test_env.sh
trap stop_brokers INT TERM QUIT
@@ -13,20 +33,17 @@ start_brokers() {
}
stop_brokers() {
- ../qpidd -q --port $LOCAL_PORT
- ../qpidd -q --port $REMOTE_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $REMOTE_PORT
}
if test -d ${PYTHON_DIR} ; then
start_brokers
echo "Running federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
- PYTHONPATH=${PYTHON_DIR}
- export PYTHONPATH
- ${MY_DIR}/federation.py -v -s ${MY_DIR}/../../../specs/amqp.0-10-qpid-errata.xml -b localhost:$LOCAL_PORT --remote-port $REMOTE_PORT
+ $QPID_PYTHON_TEST -m federation -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT $@
RETCODE=$?
stop_brokers
if test x$RETCODE != x0; then
echo "FAIL federation tests"; exit 1;
fi
fi
-
diff --git a/cpp/src/tests/run_federation_tests.ps1 b/cpp/src/tests/run_federation_tests.ps1
new file mode 100644
index 0000000000..dd0ea2fe5b
--- /dev/null
+++ b/cpp/src/tests/run_federation_tests.ps1
@@ -0,0 +1,80 @@
+#
+# 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.
+#
+
+# Run the federation tests.
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping federation tests as python libs not found"
+ exit 1
+}
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+$cmdline = "$prog --auth=no --no-module-dir --port=0 --log-to-file qpidd.log $args | foreach { set-content qpidd.port `$_ }"
+$cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+
+function start_brokers {
+ # Start 2 brokers, saving the port numbers in LOCAL_PORT, REMOTE_PORT.
+ . $srcdir\background.ps1 $cmdblock
+ while (!(Test-Path qpidd.port)) {
+ Start-Sleep 2
+ }
+ set-item -path env:LOCAL_PORT -value (get-content -path qpidd.port -totalcount 1)
+ Remove-Item qpidd.port
+ . $srcdir\background.ps1 $cmdblock
+ while (!(Test-Path qpidd.port)) {
+ Start-Sleep 2
+ }
+ set-item -path env:REMOTE_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+function stop_brokers {
+ Invoke-Expression "$prog -q --port $env:LOCAL_PORT" | Out-Default
+ Invoke-Expression "$prog -q --port $env:REMOTE_PORT" | Out-Default
+}
+
+trap {
+ &stop_brokers
+ break
+}
+
+&start_brokers
+"Running federation tests using brokers on ports $env:LOCAL_PORT $env:REMOTE_PORT"
+$env:PYTHONPATH=$PYTHON_DIR
+python $srcdir/federation.py -v -s $srcdir\..\..\..\specs\amqp.0-10-qpid-errata.xml -b localhost:$env:LOCAL_PORT --remote-port $env:REMOTE_PORT $args
+$RETCODE=$LASTEXITCODE
+&stop_brokers
+if ($RETCODE -ne 0) {
+ "FAIL federation tests"
+ exit 1
+}
diff --git a/cpp/src/tests/run_header_test b/cpp/src/tests/run_header_test
new file mode 100755
index 0000000000..07658343e7
--- /dev/null
+++ b/cpp/src/tests/run_header_test
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Simple test of encode/decode of a double in application headers
+# TODO: this should be expanded to cover a wider set of types and go
+# in both directions
+
+srcdir=`dirname $0`
+source ./test_env.sh
+
+test -f qpidd.port && QPID_PORT=`cat qpidd.port`
+
+if test -d ${PYTHON_DIR} ; then
+ ./header_test -p $QPID_PORT
+ $srcdir/header_test.py "localhost" $QPID_PORT
+else
+ echo "Skipping header test as python libs not found"
+fi
+
diff --git a/cpp/src/tests/run_header_test.ps1 b/cpp/src/tests/run_header_test.ps1
new file mode 100644
index 0000000000..7d3e43a30f
--- /dev/null
+++ b/cpp/src/tests/run_header_test.ps1
@@ -0,0 +1,47 @@
+#
+# 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.
+#
+
+# Simple test of encode/decode of a double in application headers
+# TODO: this should be expanded to cover a wider set of types and go
+# in both directions
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping header test as python libs not found"
+ exit 0
+}
+
+if (Test-Path qpidd.port) {
+ set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+# Test runs from the tests directory but the test executables are in a
+# subdirectory based on the build type. Look around for it before trying
+# to start it.
+. $srcdir\find_prog.ps1 .\header_test.exe
+if (!(Test-Path $prog)) {
+ "Cannot locate header_test.exe"
+ exit 1
+}
+
+Invoke-Expression "$prog -p $env:QPID_PORT" | Write-Output
+$env:PYTHONPATH="$PYTHON_DIR;$env:PYTHONPATH"
+Invoke-Expression "python $srcdir/header_test.py localhost $env:QPID_PORT" | Write-Output
+exit $LASTEXITCODE
diff --git a/cpp/src/tests/run_long_cluster_tests b/cpp/src/tests/run_long_cluster_tests
new file mode 100755
index 0000000000..cb9c6b219b
--- /dev/null
+++ b/cpp/src/tests/run_long_cluster_tests
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+srcdir=`dirname $0`
+$srcdir/run_cluster_tests long_cluster_tests
diff --git a/cpp/src/tests/run_perftest b/cpp/src/tests/run_perftest
index c1e66294c1..1a9b934641 100755
--- a/cpp/src/tests/run_perftest
+++ b/cpp/src/tests/run_perftest
@@ -1,4 +1,24 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Args: count [perftest options...]
# Run a perftest with count multiplied.
#
diff --git a/cpp/src/tests/run_ring_queue_test b/cpp/src/tests/run_ring_queue_test
new file mode 100755
index 0000000000..7ca870841e
--- /dev/null
+++ b/cpp/src/tests/run_ring_queue_test
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+#
+# 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.
+#
+#script to run a sequence of ring queue tests via make
+
+#setup path to find qpid-config and sender/receiver test progs
+source ./test_env.sh
+
+export PATH=$PWD:$srcdir:$PYTHON_COMMANDS:$PATH
+
+#set port to connect to via env var
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
+export QPID_PORT
+
+ring_queue_test -c -s 4 -r 4
+ring_queue_test -s 4 -r 0
+ring_queue_test -s 1 -r 1
+
+
diff --git a/cpp/src/tests/run_test b/cpp/src/tests/run_test
index 4d0da15d4c..4b227621bc 100755
--- a/cpp/src/tests/run_test
+++ b/cpp/src/tests/run_test
@@ -1,11 +1,30 @@
#!/bin/sh
+
+# 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.
+#
+
#
# Set up environment and run a test executable or script.
#
# Output nothing if test passes, show the output if it fails and
# leave output in <test>.log for examination.
#
-# If qpidd.port exists run test with QPID_PORT=`cat qpidd.port`
+# If qpidd.port exists and is not empty run test with QPID_PORT=`cat qpidd.port`
#
# If $VALGRIND if is set run under valgrind. If there are valgrind
# erros show valgrind output, also leave it in <test>.valgrind for
@@ -13,13 +32,14 @@
#
srcdir=`dirname $0`
-. $srcdir/vg_check
+source ./test_env.sh
+source $srcdir/vg_check
# Export variables from makefile.
-export VALGRIND srcdir
+export srcdir
# Set QPID_PORT if qpidd.port exists.
-test -f qpidd.port && QPID_PORT=`cat qpidd.port`
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
export QPID_PORT
# Avoid silly libtool error messages if these are not defined
@@ -32,27 +52,30 @@ export LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
VG_LOG="`basename $1`.vglog"
rm -f $VG_LOG*
-VALGRIND_OPTS="
---gen-suppressions=all
+# Use VALGRIND_OPTS="--gen-suppressions=all" to generated suppressions
+VALGRIND_OPTS="$VALGRIND_OPTS
--leak-check=full
--demangle=yes
--suppressions=$srcdir/.valgrind.supp
--num-callers=25
--log-file=$VG_LOG --
"
-# FIXME aconway 2008-07-16: removed --trace-children=yes, problems with cluster tests forking
-# qpidd libtool script. Investigate & restore --trace-children if possible.
-
ERROR=0
if grep -l "^# Generated by .*libtool" "$1" >/dev/null 2>&1; then
# This is a libtool "executable". Valgrind it if VALGRIND specified.
test -n "$VALGRIND" && VALGRIND="$VALGRIND $VALGRIND_OPTS"
# Hide output unless there's an error.
- libtool --mode=execute $VALGRIND "$@" 2>&1 || ERROR=1
+ $LIBTOOL --mode=execute $VALGRIND "$@" 2>&1 || ERROR=1
test -n "$VALGRIND" && { vg_check $VG_LOG* || ERROR=1 ; }
-else
+elif file $1 | grep -q text; then
# This is a non-libtool shell script, just execute it.
exec "$@"
+else
+ # This is a real executable, valgrind it.
+ test -n "$VALGRIND" && VALGRIND="$VALGRIND $VALGRIND_OPTS"
+ # Hide output unless there's an error.
+ $VALGRIND "$@" 2>&1 || ERROR=1
+ test -n "$VALGRIND" && { vg_check $VG_LOG* || ERROR=1 ; }
fi
exit $ERROR
diff --git a/cpp/src/tests/run_test.ps1 b/cpp/src/tests/run_test.ps1
new file mode 100644
index 0000000000..4deb07bf71
--- /dev/null
+++ b/cpp/src/tests/run_test.ps1
@@ -0,0 +1,73 @@
+#
+# 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.
+#
+
+$srcdir = Split-Path $myInvocation.InvocationName
+
+# Set up environment and run a test executable or script.
+$env:QPID_DATA_DIR = ""
+$env:BOOST_TEST_SHOW_PROGRESS = "yes"
+
+# The test exe is probably not in the current binary dir - it's usually
+# placed in a subdirectory based on the configuration built in Visual Studio.
+# So check around to see where it is - when located, set the QPID_LIB_DIR
+# and PATH to look in the corresponding configuration off the src directory,
+# one level up.
+$prog = $args[0]
+$is_script = $prog -match ".ps1$"
+if (Test-Path $prog) {
+ $env:QPID_LIB_DIR = ".."
+ $env:PATH += ";.."
+}
+else {
+ . $srcdir\find_prog.ps1 $prog
+ $args[0] = $prog
+ $env:QPID_LIB_DIR = "..\$sub"
+ $env:PATH += "$dir\$sub;..\$sub"
+}
+
+# If qpidd.port exists and is not empty run test with QPID_PORT set.
+if (Test-Path qpidd.port) {
+ set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+$si = new-object System.Diagnostics.ProcessStartInfo
+$si.WorkingDirectory = $pwd
+$si.UseShellExecute = $false
+$si.CreateNoWindow = $true
+$si.RedirectStandardOutput = $true
+if ($is_script) {
+ $si.FileName = (get-command powershell.exe).Definition
+ $si.Arguments = $args
+}
+else {
+ $si.FileName = $args[0]
+ if ($args.length -gt 1) {
+ $si.Arguments = $args[1..($args.length-1)]
+ }
+}
+$p = [System.Diagnostics.Process]::Start($si)
+$line = ""
+while (($line = $p.StandardOutput.ReadLine()) -ne $null) {
+ $line
+}
+# ReadToEnd() works, but doesn't show any output until the program exits.
+#$p.StandardOutput.ReadToEnd()
+$p.WaitForExit()
+$status = $p.ExitCode
+exit $status
diff --git a/cpp/src/tests/sender.cpp b/cpp/src/tests/sender.cpp
new file mode 100644
index 0000000000..4e845c42b4
--- /dev/null
+++ b/cpp/src/tests/sender.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.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageReplayTracker.h>
+#include <qpid/client/QueueOptions.h>
+#include <qpid/Exception.h>
+#include "TestOptions.h"
+
+#include <fstream>
+#include <iostream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string destination;
+ string key;
+ uint sendEos;
+ bool durable;
+ uint ttl;
+ string lvqMatchValue;
+ string lvqMatchFile;
+
+ Args() : key("test-queue"), sendEos(0), durable(false), ttl(0)
+ {
+ addOptions()
+ ("exchange", qpid::optValue(destination, "EXCHANGE"), "Exchange to send messages to")
+ ("routing-key", qpid::optValue(key, "KEY"), "Routing key to add to messages")
+ ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input")
+ ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.")
+ ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds")
+ ("lvq-match-value", qpid::optValue(lvqMatchValue, "KEY"), "The value to set for the LVQ match key property")
+ ("lvq-match-file", qpid::optValue(lvqMatchFile, "FILE"), "A file containing values to set for the LVQ match key property");
+ }
+};
+
+const string EOS("eos");
+
+class Sender : public FailoverManager::Command
+{
+ public:
+ Sender(const std::string& destination, const std::string& key, uint sendEos, bool durable, uint ttl,
+ const std::string& lvqMatchValue, const std::string& lvqMatchFile);
+ void execute(AsyncSession& session, bool isRetry);
+ private:
+ const std::string destination;
+ MessageReplayTracker sender;
+ Message message;
+ const uint sendEos;
+ uint sent;
+ std::ifstream lvqMatchValues;
+};
+
+Sender::Sender(const std::string& dest, const std::string& key, uint eos, bool durable, uint ttl, const std::string& lvqMatchValue, const std::string& lvqMatchFile) :
+ destination(dest), sender(10), message("", key), sendEos(eos), sent(0) , lvqMatchValues(lvqMatchFile.c_str())
+{
+ if (durable){
+ message.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+
+ if (ttl) {
+ message.getDeliveryProperties().setTtl(ttl);
+ }
+
+ if (!lvqMatchValue.empty()) {
+ message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqMatchValue);
+ }
+}
+
+void Sender::execute(AsyncSession& session, bool isRetry)
+{
+ if (isRetry) sender.replay(session);
+ else sender.init(session);
+ string data;
+ while (getline(std::cin, data)) {
+ message.setData(data);
+ message.getHeaders().setInt("sn", ++sent);
+ string matchKey;
+ if (lvqMatchValues && getline(lvqMatchValues, matchKey)) {
+ message.getHeaders().setString(QueueOptions::strLVQMatchProperty, matchKey);
+ }
+ sender.send(message, destination);
+ }
+ for (uint i = sendEos; i > 0; --i) {
+ message.setData(EOS);
+ sender.send(message, destination);
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ FailoverManager connection(opts.con);
+ Sender sender(opts.destination, opts.key, opts.sendEos, opts.durable, opts.ttl, opts.lvqMatchValue, opts.lvqMatchFile);
+ connection.execute(sender);
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << "Failed: " << error.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/cpp/src/tests/shared_perftest b/cpp/src/tests/shared_perftest
index 212ba22df4..cc192d25bd 100755
--- a/cpp/src/tests/shared_perftest
+++ b/cpp/src/tests/shared_perftest
@@ -1,2 +1,22 @@
#!/bin/sh
+
+#
+# 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.
+#
+
exec `dirname $0`/run_perftest 100000 --mode shared --npubs 16 --nsubs 16
diff --git a/cpp/src/tests/shlibtest.cpp b/cpp/src/tests/shlibtest.cpp
index 80320ea7be..5655eb7e64 100644
--- a/cpp/src/tests/shlibtest.cpp
+++ b/cpp/src/tests/shlibtest.cpp
@@ -18,11 +18,17 @@
*
*/
+namespace qpid {
+namespace tests {
+
int* loaderData = 0;
-extern "C" void callMe(int *i) { loaderData=i; }
+extern "C"
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+void callMe(int *i) { loaderData=i; }
struct OnUnload { ~OnUnload() { *loaderData=42; } };
OnUnload unloader; // For destructor.
-
-
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/ssl.mk b/cpp/src/tests/ssl.mk
new file mode 100644
index 0000000000..cb887c8fda
--- /dev/null
+++ b/cpp/src/tests/ssl.mk
@@ -0,0 +1,23 @@
+#
+# 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+=ssl_test
+EXTRA_DIST+=ssl_test
+clean-local:
+ rm -rf test_cert_db cert.password \ No newline at end of file
diff --git a/cpp/src/tests/ssl_test b/cpp/src/tests/ssl_test
new file mode 100755
index 0000000000..528833076e
--- /dev/null
+++ b/cpp/src/tests/ssl_test
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Run a simple test over SSL
+source ./test_env.sh
+
+CONFIG=$(dirname $0)/config.null
+CERT_DIR=`pwd`/test_cert_db
+CERT_PW_FILE=`pwd`/cert.password
+TEST_HOSTNAME=127.0.0.1
+COUNT=10
+
+trap cleanup EXIT
+
+error() { echo $*; exit 1; }
+
+create_certs() {
+ #create certificate and key databases with single, simple, self-signed certificate in it
+ mkdir ${CERT_DIR}
+ certutil -N -d ${CERT_DIR} -f ${CERT_PW_FILE}
+ certutil -S -d ${CERT_DIR} -n ${TEST_HOSTNAME} -s "CN=${TEST_HOSTNAME}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil
+}
+
+delete_certs() {
+ if [[ -e ${CERT_DIR} ]] ; then
+ rm -rf ${CERT_DIR}
+ fi
+}
+
+start_broker() {
+ PORT=`../qpidd --daemon --transport ssl --port 0 --ssl-port 0 --no-data-dir --no-module-dir --auth no --config $CONFIG --load-module $SSL_LIB --ssl-cert-db $CERT_DIR --ssl-cert-password-file $CERT_PW_FILE --ssl-cert-name $TEST_HOSTNAME`
+}
+
+stop_broker() {
+ if [[ $PORT ]] ; then
+ $QPIDD_EXEC --no-module-dir -q --port $PORT
+ fi
+}
+
+cleanup() {
+ stop_broker
+ delete_certs
+}
+
+CERTUTIL=$(type -p certutil)
+if [[ !(-x $CERTUTIL) ]] ; then
+ echo "No certutil, skipping ssl test";
+ exit 0;
+fi
+
+if [[ !(-e ${CERT_PW_FILE}) ]] ; then
+ echo password > ${CERT_PW_FILE}
+fi
+delete_certs
+create_certs || error "Could not create test certificate"
+
+start_broker || error "Could not start broker"
+echo "Running SSL test on port $PORT"
+export QPID_NO_MODULE_DIR=1
+export QPID_LOAD_MODULE=$SSLCONNECTOR_LIB
+export QPID_SSL_CERT_DB=${CERT_DIR}
+export QPID_SSL_CERT_PASSWORD_FILE=${CERT_PW_FILE}
+./perftest --count ${COUNT} --port ${PORT} -P ssl -b $TEST_HOSTNAME --summary
+
diff --git a/cpp/src/tests/start_broker b/cpp/src/tests/start_broker
index aabe12ad43..093c44051a 100755
--- a/cpp/src/tests/start_broker
+++ b/cpp/src/tests/start_broker
@@ -1,4 +1,24 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Start a test broker.
srcdir=`dirname $0`
-exec $srcdir/run_test ../qpidd --auth=no --no-module-dir --daemon --port=0 --log-output qpidd.log "$@" > qpidd.port
+exec $srcdir/run_test ../qpidd --auth=no --no-module-dir --daemon --port=0 --log-to-file qpidd.log "$@" > qpidd.port
diff --git a/cpp/src/tests/start_broker.ps1 b/cpp/src/tests/start_broker.ps1
new file mode 100644
index 0000000000..9263262b9f
--- /dev/null
+++ b/cpp/src/tests/start_broker.ps1
@@ -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.
+#
+
+# Get the directory where this script resides.
+function Get-ScriptPath
+ { Split-Path $myInvocation.ScriptName }
+
+# Start a test broker and capture it's port (from stdout) to qpidd.port
+# This script will exit immediately after spawning the broker process. To avoid
+# running more tests before the broker is initialized, wait for the qpidd.port
+# file to appear before exiting.
+if (Test-Path qpidd.port) {
+ Remove-Item qpidd.port
+}
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+$cmdline = "$prog --auth=no --no-module-dir --port=0 --log-to-file qpidd.log $args | foreach { set-content qpidd.port `$_ }"
+$cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+$srcdir = Get-ScriptPath
+. $srcdir\background.ps1 $cmdblock
+
+$wait_time = 0
+while (!(Test-Path qpidd.port) -and ($wait_time -lt 10)) {
+ Start-Sleep 2
+ $wait_time += 2
+}
+if (Test-Path qpidd.port) {
+ exit 0
+}
+"Time out waiting for broker to start"
+exit 1
diff --git a/cpp/src/tests/start_cluster b/cpp/src/tests/start_cluster
index 55f989a3e9..bc35a2eddc 100755
--- a/cpp/src/tests/start_cluster
+++ b/cpp/src/tests/start_cluster
@@ -1,26 +1,42 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Start a cluster of brokers on local host, put the list of ports for cluster members in cluster.ports
#
# Execute command with the ais group set.
-with_ais_group() {
- id -nG | grep '\<ais\>' >/dev/null || { echo "You are not a member of the ais group." 1>&2; exit 1; }
- echo $* | newgrp ais
-}
-
-test -f cluster.ports && { echo "cluster.ports file already exists" ; exit 1; }
-rm -f cluster*.log
-SIZE=$1; shift
-CLUSTER=`pwd` # Cluster name=pwd, avoid clashes.
-OPTS="-d --load-module ../.libs/libqpidcluster.so --cluster-name=$CLUSTER --no-data-dir --auth=no $*"
-
-if test "$SIZE" = "one"; then # Special case of singleton cluster, use default port.
- ../qpidd -q
- with_ais_group ../qpidd $OPTS || exit 1
-else
- for (( i=0; i<SIZE; ++i )); do
- PORT=`with_ais_group ../qpidd -p0 --log-output=cluster$i.log $OPTS` || exit 1
- echo $PORT >> cluster.ports
- done
-fi
+source ./test_env.sh
+. `dirname $0`/ais_check
+
+rm -f cluster*.log cluster.ports qpidd.port
+
+SIZE=${1:-3}; shift
+CLUSTER=$HOSTNAME.$$
+OPTS="-d --no-module-dir --load-module $CLUSTER_LIB --cluster-name=$CLUSTER --auth=no --log-enable notice+ --log-enable debug+:cluster $@"
+
+for (( i=0; i<SIZE; ++i )); do
+ DDIR=`mktemp -d /tmp/start_cluster.XXXXXXXXXX`
+ PORT=`with_ais_group ../qpidd -p0 --log-to-file=cluster$i.log $OPTS --data-dir=$DDIR` || exit 1
+ echo $PORT >> cluster.ports
+done
+
+head -n 1 cluster.ports > qpidd.port # First member's port for tests.
diff --git a/cpp/src/tests/start_cluster_hosts b/cpp/src/tests/start_cluster_hosts
new file mode 100755
index 0000000000..778b4248da
--- /dev/null
+++ b/cpp/src/tests/start_cluster_hosts
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+#
+# Start a cluster of brokers on local host, put the list of host port addresses
+# in cluster.ports
+#
+# Arguments: [-k] [-p port] HOST [HOST...]
+# -p port to start broker on, can be 0. Actual ports recorded in cluster.addr.
+# -k kill any qpidd processes owned by this user before starting.
+#
+# Start a broker on each named host. Name a host twice to start multiple brokers.
+#
+# You must be able to ssh to each host and be in group ais.
+# $QPIDD must be executable on each host.
+# Logs go to syslog on each host, with a unique prefix per broker.
+#
+
+QPIDD=${QPIDD:-$PWD/../qpidd}
+LIBQPIDCLUSTER=${LIBQPIDCLUSTER:-$PWD/../.libs/cluster.so}
+NAME=$USER # User name is default cluster name.
+RESTART=NO
+
+while getopts "kp:n:q:r" ARG ; do
+ case $ARG in
+ k) KILL=yes ;;
+ p) PORT="$OPTARG" ;;
+ n) NAME=$OPTARG ;;
+ q) QPIDD=$OPTARG ;;
+ l) LIBQPIDCLUSTER=$OPTARG ;;
+ r) RESTART=yes ;;
+ *) echo "Error parsing options: $ARG"; exit 1 ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+test -n "$PORT" && PORTOPT="-p $PORT"
+test "$KILL" = yes && KILL="$QPIDD --no-module-dir -q $PORTOPT ;"
+CLUSTER=${*:-$CLUSTER} # Use args or env
+test -z "$CLUSTER" && { echo Must specify at least one host; exit 1; }
+
+
+OPTS="-d $PORTOPT --load-module $LIBQPIDCLUSTER --cluster-name=$NAME --no-data-dir --auth=no --log-to-syslog --log-enable=info+"
+
+num=0
+for h in $CLUSTER; do
+ num=`expr $num + 1` # Give a unique log prefix to each node.
+ cmd="$KILL $QPIDD $OPTS --log-prefix $num.$h"
+ out=`echo "$cmd" | ssh $h newgrp ais` || { echo == $h error: $out ; exit 1; }
+ if [ "$PORT" = 0 ] ; then p=$out; else p=$PORT; fi
+ echo "$h $p"
+done
+
diff --git a/cpp/src/tests/stop_broker b/cpp/src/tests/stop_broker
index 6912795fca..248fd1fc5c 100755
--- a/cpp/src/tests/stop_broker
+++ b/cpp/src/tests/stop_broker
@@ -1,11 +1,31 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Stop the broker, check for errors.
#
QPID_PORT=`cat qpidd.port`
export QPID_PORT
rm -f qpidd.port
-../qpidd --quit || ERROR=1
+../qpidd --no-module-dir --quit || ERROR=1
# Check qpidd.log.
egrep 'warning\|error\|critical' qpidd.log && {
diff --git a/cpp/src/tests/stop_broker.ps1 b/cpp/src/tests/stop_broker.ps1
new file mode 100644
index 0000000000..4fdeb26e2b
--- /dev/null
+++ b/cpp/src/tests/stop_broker.ps1
@@ -0,0 +1,56 @@
+#
+# 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.
+#
+
+# Stop the broker, check for errors.
+Get-Content -path qpidd.port -totalCount 1 | Set-Variable -name qpid_port
+Remove-Item qpidd.port
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+
+# Piping the output makes the script wait for qpidd to finish.
+Invoke-Expression "$prog --quit --port $qpid_port" | Write-Output
+$stopped = $?
+
+# Check qpidd.log.
+filter bad_stuff {
+ $_ -match "( warning | error | critical )"
+}
+
+$qpidd_errors = $false
+Get-Content -path qpidd.log | where { bad_stuff } | Out-Default | Set-Variable -name qpidd_errors -value $true
+if ($qpidd_errors -eq $true) {
+ "WARNING: Suspicious broker log entries in qpidd.log, above."
+}
+if ($stopped -eq $true) {
+ exit 0
+}
+exit 1
diff --git a/cpp/src/tests/stop_cluster b/cpp/src/tests/stop_cluster
index 9bd05092de..d598a2255a 100755
--- a/cpp/src/tests/stop_cluster
+++ b/cpp/src/tests/stop_cluster
@@ -1,11 +1,31 @@
#!/bin/sh
+
+#
+# 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.
+#
+
# Stop brokers on ports listed in cluster.ports
PORTS=`cat cluster.ports`
for PORT in $PORTS ; do
- ../qpidd -qp $PORT || ERROR="$ERROR $PORT"
+ $QPIDD_EXEC --no-module-dir -qp $PORT || ERROR="$ERROR $PORT"
done
-rm -f cluster.ports
+rm -f cluster.ports qpidd.port
if [ -n "$ERROR" ]; then
echo "Errors stopping brokers on ports: $ERROR"
diff --git a/cpp/src/tests/test_env.sh.in b/cpp/src/tests/test_env.sh.in
new file mode 100644
index 0000000000..32c010b9be
--- /dev/null
+++ b/cpp/src/tests/test_env.sh.in
@@ -0,0 +1,68 @@
+#
+# 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.
+#
+
+absdir() { echo `cd $1 && pwd`; }
+
+# Environment variables substituted by configure/cmake.
+srcdir=@abs_srcdir@
+builddir=@abs_builddir@
+top_srcdir=@abs_top_srcdir@
+top_builddir=@abs_top_builddir@
+moduledir=$top_builddir/src@builddir_lib_suffix@
+testmoduledir=$builddir@builddir_lib_suffix@
+
+# Python paths and directories
+export PYTHON_DIR=$builddir/python
+export QPID_PYTHON_TEST=$PYTHON_DIR/commands/qpid-python-test
+if [ ! -d $PYTHON_DIR -a -d $top_srcdir/../python ]; then
+ export PYTHON_DIR=$top_srcdir/../python
+ export QPID_PYTHON_TEST=$PYTHON_DIR/qpid-python-test
+fi
+export PYTHON_COMMANDS=$PYTHON_DIR/commands
+export PYTHONPATH=$srcdir:$PYTHON_DIR:$PYTHON_COMMANDS:$PYTHONPATH
+export QPID_CONFIG_EXEC=$PYTHON_COMMANDS/qpid-config
+export QPID_ROUTE_EXEC=$PYTHON_COMMANDS/qpid-route
+export QPID_CLUSTER_EXEC=$PYTHON_COMMANDS/qpid-cluster
+
+# Executables
+export QPIDD_EXEC=$top_builddir/src/qpidd
+export QPID_WATCHDOG_EXEC=$top_builddir/src/qpidd_watchdog
+
+# Test executables
+export QPID_TEST_EXEC_DIR=$builddir
+export RECEIVER_EXEC=$QPID_TEST_EXEC_DIR/receiver
+export SENDER_EXEC=$QPID_TEST_EXEC_DIR/sender
+
+# Modules
+export TEST_STORE_LIB=$testmoduledir/test_store.so
+
+exportmodule() { test -f $moduledir/$2 && eval "export $1=$moduledir/$2"; }
+exportmodule ACL_LIB acl.so
+exportmodule CLUSTER_LIB cluster.so
+exportmodule REPLICATING_LISTENER_LIB replicating_listener.so
+exportmodule REPLICATION_EXCHANGE_LIB replication_exchange.so
+exportmodule SSLCONNECTOR_LIB sslconnector.so
+exportmodule SSL_LIB ssl.so
+exportmodule WATCHDOG_LIB watchdog.so
+exportmodule XML_LIB xml.so
+
+# Qpid options
+export QPID_NO_MODULE_DIR=1 # Don't accidentally load installed modules
+export QPID_DATA_DIR= # Default to no data dir, not ~/.qpidd
+
diff --git a/cpp/src/tests/test_store.cpp b/cpp/src/tests/test_store.cpp
new file mode 100644
index 0000000000..257e77b6b4
--- /dev/null
+++ b/cpp/src/tests/test_store.cpp
@@ -0,0 +1,178 @@
+/*
+ *
+ * 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
+ * Plug-in message store for tests.
+ *
+ * Add functionality as required, build up a comprehensive set of
+ * features to support persistent behavior tests.
+ *
+ * Current features special "action" messages can:
+ * - raise exception from enqueue.
+ * - force host process to exit.
+ * - do async completion after a delay.
+ */
+
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/log/Statement.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include <boost/cast.hpp>
+#include <boost/lexical_cast.hpp>
+#include <memory>
+#include <fstream>
+
+using namespace qpid;
+using namespace broker;
+using namespace std;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct TestStoreOptions : public Options {
+
+ string name;
+ string dump;
+
+ TestStoreOptions() : Options("Test Store Options") {
+ addOptions()
+ ("test-store-name", optValue(name, "NAME"), "Name of test store instance.")
+ ("test-store-dump", optValue(dump, "FILE"), "File to dump enqueued messages.")
+ ;
+ }
+};
+
+struct Completer : public Runnable {
+ boost::intrusive_ptr<PersistableMessage> message;
+ int usecs;
+ Completer(boost::intrusive_ptr<PersistableMessage> m, int u) : message(m), usecs(u) {}
+ void run() {
+ qpid::sys::usleep(usecs);
+ message->enqueueComplete();
+ delete this;
+ }
+};
+
+class TestStore : public NullMessageStore {
+ public:
+ TestStore(const TestStoreOptions& opts, Broker& broker_)
+ : options(opts), name(opts.name), broker(broker_)
+ {
+ QPID_LOG(info, "TestStore name=" << name << " dump=" << options.dump);
+ if (!options.dump.empty())
+ dump.reset(new ofstream(options.dump.c_str()));
+ }
+
+ ~TestStore() {
+ for_each(threads.begin(), threads.end(), boost::bind(&Thread::join, _1));
+ }
+
+ virtual bool isNull() const { return false; }
+
+ void enqueue(TransactionContext* ,
+ const boost::intrusive_ptr<PersistableMessage>& pmsg,
+ const PersistableQueue& )
+ {
+ Message* msg = dynamic_cast<Message*>(pmsg.get());
+ assert(msg);
+
+ // Dump the message if there is a dump file.
+ if (dump.get()) {
+ msg->getFrames().getMethod()->print(*dump);
+ *dump << endl << " ";
+ msg->getFrames().getHeaders()->print(*dump);
+ *dump << endl << " ";
+ *dump << msg->getFrames().getContentSize() << endl;
+ }
+
+ // Check the message for special instructions.
+ string data = msg->getFrames().getContent();
+ size_t i = string::npos;
+ size_t j = string::npos;
+ if (strncmp(data.c_str(), TEST_STORE_DO.c_str(), strlen(TEST_STORE_DO.c_str())) == 0
+ && (i = data.find(name+"[")) != string::npos
+ && (j = data.find("]", i)) != string::npos)
+ {
+ size_t start = i+name.size()+1;
+ string action = data.substr(start, j-start);
+
+ if (action == EXCEPTION) {
+ throw Exception(QPID_MSG("TestStore " << name << " throwing exception for: " << data));
+ }
+ else if (action == EXIT_PROCESS) {
+ // FIXME aconway 2009-04-10: this is a dubious way to
+ // close the process at best, it can cause assertions or seg faults
+ // rather than clean exit.
+ QPID_LOG(critical, "TestStore " << name << " forcing process exit for: " << data);
+ exit(0);
+ }
+ else if (strncmp(action.c_str(), ASYNC.c_str(), strlen(ASYNC.c_str())) == 0) {
+ std::string delayStr(action.substr(ASYNC.size()));
+ int delay = boost::lexical_cast<int>(delayStr);
+ threads.push_back(Thread(*new Completer(msg, delay)));
+ }
+ else {
+ QPID_LOG(error, "TestStore " << name << " unknown action " << action);
+ msg->enqueueComplete();
+ }
+ }
+ else
+ msg->enqueueComplete();
+ }
+
+ private:
+ static const string TEST_STORE_DO, EXCEPTION, EXIT_PROCESS, ASYNC;
+ TestStoreOptions options;
+ string name;
+ Broker& broker;
+ vector<Thread> threads;
+ std::auto_ptr<ofstream> dump;
+};
+
+const string TestStore::TEST_STORE_DO = "TEST_STORE_DO: ";
+const string TestStore::EXCEPTION = "exception";
+const string TestStore::EXIT_PROCESS = "exit_process";
+const string TestStore::ASYNC="async ";
+
+struct TestStorePlugin : public Plugin {
+
+ TestStoreOptions options;
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize (Plugin::Target& target)
+ {
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (!broker) return;
+ boost::shared_ptr<MessageStore> p(new TestStore(options, *broker));
+ broker->setStore (p);
+ }
+
+ void initialize(qpid::Plugin::Target&) {}
+};
+
+static TestStorePlugin pluginInstance;
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/test_tools.h b/cpp/src/tests/test_tools.h
index 32127b0442..bb2509fd32 100644
--- a/cpp/src/tests/test_tools.h
+++ b/cpp/src/tests/test_tools.h
@@ -18,15 +18,18 @@
* limitations under the License.
*
*/
+#include "qpid/log/Logger.h"
#include <limits.h> // Include before boost/test headers.
-
#include <boost/test/test_tools.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/regex.hpp>
#include <boost/assign/list_of.hpp>
#include <vector>
+#include <set>
#include <ostream>
+#include <sstream>
+#include <exception>
// Print a sequence
template <class T> std::ostream& seqPrint(std::ostream& o, const T& seq) {
@@ -34,7 +37,7 @@ template <class T> std::ostream& seqPrint(std::ostream& o, const T& seq) {
return o;
}
-// Compare sequences
+// Compare sequences
template <class T, class U>
bool seqEqual(const T& a, const U& b) {
typename T::const_iterator i = a.begin();
@@ -43,14 +46,17 @@ bool seqEqual(const T& a, const U& b) {
return (i == a.end()) && (j == b.end());
}
-// ostream and == operators so we can compare vectors and boost::assign::list_of
-// with BOOST_CHECK_EQUALS
+// ostream and == operators so we can compare vectors and sets with
+// boost::assign::list_of with BOOST_CHECK_EQUALS
namespace std { // In namespace std so boost can find them.
template <class T>
ostream& operator<<(ostream& o, const vector<T>& v) { return seqPrint(o, v); }
template <class T>
+ostream& operator<<(ostream& o, const set<T>& v) { return seqPrint(o, v); }
+
+template <class T>
ostream& operator<<(ostream& o, const boost::assign_detail::generic_list<T>& l) { return seqPrint(o, l); }
template <class T>
@@ -58,8 +64,17 @@ bool operator == (const vector<T>& a, const boost::assign_detail::generic_list<T
template <class T>
bool operator == (const boost::assign_detail::generic_list<T>& b, const vector<T>& a) { return seqEqual(a, b); }
+
+template <class T>
+bool operator == (const set<T>& a, const boost::assign_detail::generic_list<T>& b) { return seqEqual(a, b); }
+
+template <class T>
+bool operator == (const boost::assign_detail::generic_list<T>& b, const set<T>& a) { return seqEqual(a, b); }
}
+namespace qpid {
+namespace tests {
+
/** NB: order of parameters is regex first, in line with
* CHECK(expected, actual) convention.
*/
@@ -78,5 +93,30 @@ inline bool regexPredicate(const std::string& re, const std::string& text) {
/** Check if types of two objects (as given by typeinfo::name()) match. */
#define BOOST_CHECK_TYPEID_EQUAL(a,b) BOOST_CHECK_EQUAL(typeid(a).name(),typeid(b).name())
+/**
+ * Supress all logging in a scope, restore to previous configuration in destructor.
+ */
+struct ScopedSuppressLogging {
+ typedef qpid::log::Logger Logger;
+ ScopedSuppressLogging(Logger& l=Logger::instance()) : logger(l), opts(l.getOptions()) { l.clear(); }
+ ~ScopedSuppressLogging() { logger.configure(opts); }
+ Logger& logger;
+ qpid::log::Options opts;
+};
+
+inline std::string getLibPath(const char* envName, const char* defaultPath = 0) {
+ const char* p = std::getenv(envName);
+ if (p != 0)
+ return p;
+ if (defaultPath == 0) {
+ std::ostringstream msg;
+ msg << "Environment variable " << envName << " not set.";
+ throw std::runtime_error(msg.str());
+ }
+ return defaultPath;
+}
+
+}} // namespace qpid::tests
+
#endif /*!TEST_TOOLS_H*/
diff --git a/cpp/src/tests/test_watchdog b/cpp/src/tests/test_watchdog
new file mode 100755
index 0000000000..5afdaa5e24
--- /dev/null
+++ b/cpp/src/tests/test_watchdog
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+#
+# 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 watchdog plug-in
+
+source ./test_env.sh
+# Start a broker with watchdog, freeze it with kill -STOP, verify that it is killed.
+PORT=`$QPIDD_EXEC -dp0 --no-data-dir --auth=no --no-module-dir --load-module $WATCHDOG_LIB --log-to-file=qpidd_watchdog.log --watchdog-interval 1` || exit 1
+PID=`$QPIDD_EXEC --no-module-dir -cp $PORT` || exit 1
+kill -STOP $PID
+sleep 2
+
+if kill -0 $PID 2>/dev/null; then
+ echo "Hung process did not die."
+ kill $PID
+else
+ true
+fi
diff --git a/cpp/src/tests/test_wrap b/cpp/src/tests/test_wrap
new file mode 100755
index 0000000000..dd43c5a2e2
--- /dev/null
+++ b/cpp/src/tests/test_wrap
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Read the started broker port, set appropriate env vars
+# then run the program under test
+
+QPID_PORT=`cat qpidd.port`
+export QPID_PORT
+
+program=$1
+shift
+
+QPID_LOG_TO_FILE=`basename $program`.log
+export QPID_LOG_TO_FILE
+
+ERROR=0
+$program $* || ERROR=1
+
+# Check qpidd.log.
+egrep 'warning\|error\|critical' $QPID_LOG_TO_FILE && {
+ echo "WARNING: Suspicious broker log entries in $QPID_LOG_TO_FILE, above."
+}
+
+# Check valgrind log.
+#if test -n "$VALGRIND"; then
+# . `dirname $0`/vg_check $VG_LOG*
+# vg_check qpidd.vglog* || ERROR=1
+#fi
+
+exit $ERROR
diff --git a/cpp/src/tests/testlib.py b/cpp/src/tests/testlib.py
new file mode 100644
index 0000000000..fe57a84a81
--- /dev/null
+++ b/cpp/src/tests/testlib.py
@@ -0,0 +1,766 @@
+#
+# 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.
+#
+
+#
+# Support library for qpid python tests.
+#
+
+import os, re, signal, subprocess, time, unittest
+
+class TestBase(unittest.TestCase):
+ """
+ Base class for qpid tests. Provides broker start/stop/kill methods
+ """
+
+ """
+ The following environment vars control if and how the test is run, and determine where many of the helper
+ executables/libs are to be found.
+ """
+ _storeLib = os.getenv("STORE_LIB")
+ _storeEnable = _storeLib != None # Must be True for durability to be enabled during the test
+ _qpiddExec = os.getenv("QPIDD_EXEC", "/usr/sbin/qpidd")
+ _tempStoreDir = os.path.abspath(os.getenv("TMP_DATA_DIR", "/tmp/qpid"))
+
+ """Global message counter ensures unique messages"""
+ _msgCnt = 0
+
+ # --- Helper functions for parameter handling ---
+
+ def _paramBool(self, key, val, keyOnly = False):
+ if val == None:
+ return ""
+ if keyOnly:
+ if val:
+ return " --%s" % key
+ else:
+ return ""
+ else:
+ if val:
+ return " --%s yes" % key
+ else:
+ return " --%s no" % key
+
+ # --- Helper functions for message creation ---
+
+ def _makeMessage(self, msgSize):
+ msg = "Message-%04d" % self._msgCnt
+ self._msgCnt = self._msgCnt + 1
+ msgLen = len(msg)
+ if msgSize > msgLen:
+ for i in range(msgLen, msgSize):
+ if i == msgLen:
+ msg += "-"
+ else:
+ msg += chr(ord('a') + (i % 26))
+ return msg
+
+ def _makeMessageList(self, numMsgs, msgSize):
+ if msgSize == None:
+ msgSize = 12
+ msgs = ""
+ for m in range(0, numMsgs):
+ msgs += "%s\n" % self._makeMessage(msgSize)
+ return msgs
+
+ # --- Starting and stopping a broker ---
+
+ def startBroker(self, qpiddArgs, logFile = None):
+ """Start a single broker daemon, returns tuple (pid, port)"""
+ if self._qpiddExec == None:
+ raise Exception("Environment variable QPIDD is not set")
+ cmd = "%s --daemon --port=0 %s" % (self._qpiddExec, qpiddArgs)
+ portStr = os.popen(cmd).read()
+ if len(portStr) == 0:
+ err = "Broker daemon startup failed."
+ if logFile != None:
+ err += " See log file %s" % logFile
+ raise Exception(err)
+ port = int(portStr)
+ pidStr = os.popen("%s -p %d -c" % (self._qpiddExec, port)).read()
+ try:
+ pid = int(pidStr)
+ except:
+ raise Exception("Unable to get pid: \"%s -p %d -c\" returned %s" % (self._qpiddExec, port, pidStr))
+ #print "started broker: pid=%d, port=%d args: %s" % (pid, port, qpiddArgs)
+ return (pid, port)
+
+ def killBroker(self, nodeTuple, ignoreFailures = False):
+ """Kill a broker using kill -9"""
+ try:
+ os.kill(nodeTuple[self.PID], signal.SIGKILL)
+ try:
+ os.waitpid(nodeTuple[self.PID], 0)
+ except:
+ pass
+ #print "killed broker: port=%d pid=%d" % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ except:
+ if ignoreFailures:
+ print "WARNING: killBroker (port=%d pid=%d) failed - ignoring." % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ else:
+ raise
+
+ def stopBroker(self, nodeTuple, ignoreFailures = False):
+ """Stop a broker using qpidd -q"""
+ try:
+ ret = os.spawnl(os.P_WAIT, self._qpiddExec, self._qpiddExec, "--port=%d" % nodeTuple[self.PORT], "--quit", "--no-module-dir")
+ if ret != 0:
+ raise Exception("stopBroker(): port=%d: qpidd -q returned %d" % (nodeTuple[self.PORT], ret))
+ try:
+ os.waitpid(nodeTuple[self.PID], 0)
+ except:
+ pass
+ #print "stopped broker: port=%d pid=%d" % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ except:
+ if ignoreFailures:
+ print "WARNING: stopBroker (port=%d pid=%d) failed - ignoring." % (nodeTuple[self.PORT], nodeTuple[self.PID])
+ else:
+ raise
+
+
+
+class TestBaseCluster(TestBase):
+ """
+ Base class for cluster tests. Provides methods for starting and stopping clusters and cluster nodes.
+ """
+
+ """
+ The following environment vars control if and how the test is run, and determine where many of the helper
+ executables/libs are to be found.
+ """
+ _clusterLib = os.getenv("CLUSTER_LIB")
+ _clusterTestEnable = _clusterLib != None # Must be True for these cluster tests to run
+ _xmlLib = os.getenv("XML_LIB")
+ _xmlEnable = _xmlLib != None
+ _qpidConfigExec = os.getenv("QPID_CONFIG_EXEC", "/usr/bin/qpid-config")
+ _qpidRouteExec = os.getenv("QPID_ROUTE_EXEC", "/usr/bin/qpid-route")
+ _receiverExec = os.getenv("RECEIVER_EXEC", "/usr/libexec/qpid/test/receiver")
+ _senderExec = os.getenv("SENDER_EXEC", "/usr/libexec/qpid/test/sender")
+
+
+ """
+ _clusterDict is a dictionary of clusters:
+ key = cluster name (string)
+ val = dictionary of node numbers:
+ key = integer node number
+ val = tuple containing (pid, port)
+ For example, two clusters "TestCluster0" and "TestCluster1" containing several nodes would look as follows:
+ {"TestCluster0": {0: (pid0-0, port0-0), 1: (pid0-1, port0-1), ...}, "TestCluster1": {0: (pid1-0, port1-0), 1: (pid1-1, port1-1), ...}}
+ where pidm-n and portm-n are the int pid and port for TestCluster m node n respectively.
+ """
+ _clusterDict = {}
+
+ """Index for (pid, port) tuple"""
+ PID = 0
+ PORT = 1
+
+ def run(self, res):
+ """ Skip cluster testing if env var RUN_CLUSTER_TESTS is not defined."""
+ if not self._clusterTestEnable:
+ return
+ unittest.TestCase.run(self, res)
+
+ # --- Private helper / convenience functions ---
+
+ def _checkPids(self, clusterName = None):
+ for pid, port in self.getTupleList():
+ try:
+ os.kill(pid, 0)
+ except:
+ raise Exception("_checkPids(): Broker with pid %d expected but does not exist! (crashed?)" % pid)
+
+
+ # --- Starting cluster node(s) ---
+
+ def createClusterNode(self, nodeNumber, clusterName):
+ """Create a node and add it to the named cluster"""
+ if self._tempStoreDir == None:
+ raise Exception("Environment variable TMP_DATA_DIR is not set")
+ if self._clusterLib == None:
+ raise Exception("Environment variable LIBCLUSTER is not set")
+ name = "%s-%d" % (clusterName, nodeNumber)
+ dataDir = os.path.join(self._tempStoreDir, "cluster", name)
+ logFile = "%s.log" % dataDir
+ args = "--no-module-dir --load-module=%s --data-dir=%s --cluster-name=%s --auth=no --log-enable=notice+ --log-to-file=%s" % \
+ (self._clusterLib, dataDir, clusterName, logFile)
+ if self._storeEnable:
+ if self._storeLib == None:
+ raise Exception("Environment variable LIBSTORE is not set")
+ args += " --load-module %s" % self._storeLib
+ self._clusterDict[clusterName][nodeNumber] = self.startBroker(args, logFile)
+
+ def createCluster(self, clusterName, numberNodes = 0):
+ """Create a cluster containing an initial number of nodes"""
+ self._clusterDict[clusterName] = {}
+ for n in range(0, numberNodes):
+ self.createClusterNode(n, clusterName)
+
+ def waitForNodes(self, clusterName):
+ """Wait for all nodes to become active (ie finish cluster sync)"""
+ # TODO - connect to each known node in cluster
+ # Until this is done, wait a bit (hack)
+ time.sleep(1)
+
+ # --- Cluster and node status ---
+
+ def getTupleList(self, clusterName = None):
+ """Get list of (pid, port) tuples of all known cluster brokers"""
+ tList = []
+ for c, l in self._clusterDict.iteritems():
+ if clusterName == None or c == clusterName:
+ for t in l.itervalues():
+ tList.append(t)
+ return tList
+
+ def getNumBrokers(self):
+ """Get total number of brokers in all known clusters"""
+ return len(self.getTupleList())
+
+ def checkNumBrokers(self, expected = None, checkPids = True):
+ """Check that the total number of brokers in all known clusters is the expected value"""
+ if expected != None and self.getNumBrokers() != expected:
+ raise Exception("Unexpected number of brokers: expected %d, found %d" % (expected, self.getNumBrokers()))
+ if checkPids:
+ self._checkPids()
+
+ def getClusterTupleList(self, clusterName):
+ """Get list of (pid, port) tuples of all nodes in named cluster"""
+ if clusterName in self._clusterDict:
+ return self._clusterDict[clusterName].values()
+ return []
+
+ def getNumClusterBrokers(self, clusterName):
+ """Get total number of brokers in named cluster"""
+ return len(self.getClusterTupleList(clusterName))
+
+ def getNodeTuple(self, nodeNumber, clusterName):
+ """Get the (pid, port) tuple for the given cluster node"""
+ return self._clusterDict[clusterName][nodeNumber]
+
+ def checkNumClusterBrokers(self, clusterName, expected = None, checkPids = True, waitForNodes = True):
+ """Check that the total number of brokers in the named cluster is the expected value"""
+ if expected != None and self.getNumClusterBrokers(clusterName) != expected:
+ raise Exception("Unexpected number of brokers in cluster %s: expected %d, found %d" % \
+ (clusterName, expected, self.getNumClusterBrokers(clusterName)))
+ if checkPids:
+ self._checkPids(clusterName)
+ if waitForNodes:
+ self.waitForNodes(clusterName)
+
+ def clusterExists(self, clusterName):
+ """ Return True if clusterName exists, False otherwise"""
+ return clusterName in self._clusterDict.keys()
+
+ def clusterNodeExists(self, clusterName, nodeNumber):
+ """ Return True if nodeNumber in clusterName exists, False otherwise"""
+ if clusterName in self._clusterDict.keys():
+ return nodeNumber in self._clusterDict[nodeName]
+ return False
+
+ def createCheckCluster(self, clusterName, size):
+ """Create a cluster using the given name and size, then check the number of brokers"""
+ self.createCluster(clusterName, size)
+ self.checkNumClusterBrokers(clusterName, size)
+
+ # --- Kill cluster nodes using signal 9 ---
+
+ def killNode(self, nodeNumber, clusterName, updateDict = True, ignoreFailures = False):
+ """Kill the given node in the named cluster using kill -9"""
+ self.killBroker(self.getNodeTuple(nodeNumber, clusterName), ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName][nodeNumber])
+
+ def killCluster(self, clusterName, updateDict = True, ignoreFailures = False):
+ """Kill all nodes in the named cluster"""
+ for n in self._clusterDict[clusterName].iterkeys():
+ self.killNode(n, clusterName, False, ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName])
+
+ def killClusterCheck(self, clusterName):
+ """Kill the named cluster and check that the name is removed from the cluster dictionary"""
+ self.killCluster(clusterName)
+ if self.clusterExists(clusterName):
+ raise Exception("Unable to kill cluster %s; %d nodes still exist" % \
+ (clusterName, self.getNumClusterBrokers(clusterName)))
+
+ def killAllClusters(self, ignoreFailures = False):
+ """Kill all known clusters"""
+ for n in self._clusterDict.iterkeys():
+ self.killCluster(n, False, ignoreFailures)
+ self._clusterDict.clear()
+
+ def killAllClustersCheck(self, ignoreFailures = False):
+ """Kill all known clusters and check that the cluster dictionary is empty"""
+ self.killAllClusters(ignoreFailures)
+ self.checkNumBrokers(0)
+
+ # --- Stop cluster nodes using qpidd -q ---
+
+ def stopNode(self, nodeNumber, clusterName, updateDict = True, ignoreFailures = False):
+ """Stop the given node in the named cluster using qpidd -q"""
+ self.stopBroker(self.getNodeTuple(nodeNumber, clusterName), ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName][nodeNumber])
+
+ def stopAllClusters(self, ignoreFailures = False):
+ """Stop all known clusters"""
+ for n in self._clusterDict.iterkeys():
+ self.stopCluster(n, False, ignoreFailures)
+ self._clusterDict.clear()
+
+
+ def stopCluster(self, clusterName, updateDict = True, ignoreFailures = False):
+ """Stop all nodes in the named cluster"""
+ for n in self._clusterDict[clusterName].iterkeys():
+ self.stopNode(n, clusterName, False, ignoreFailures)
+ if updateDict:
+ del(self._clusterDict[clusterName])
+
+ def stopCheckCluster(self, clusterName, ignoreFailures = False):
+ """Stop the named cluster and check that the name is removed from the cluster dictionary"""
+ self.stopCluster(clusterName, True, ignoreFailures)
+ if self.clusterExists(clusterName):
+ raise Exception("Unable to kill cluster %s; %d nodes still exist" % (clusterName, self.getNumClusterBrokers(clusterName)))
+
+ def stopAllCheck(self, ignoreFailures = False):
+ """Kill all known clusters and check that the cluster dictionary is empty"""
+ self.stopAllClusters()
+ self.checkNumBrokers(0)
+
+ # --- qpid-config functions ---
+
+ def _qpidConfig(self, nodeNumber, clusterName, action):
+ """Configure some aspect of a qpid broker using the qpid_config executable"""
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ #print "%s -a localhost:%d %s" % (self._qpidConfigExec, port, action)
+ ret = os.spawnl(os.P_WAIT, self._qpidConfigExec, self._qpidConfigExec, "-a", "localhost:%d" % port, *action.split())
+ if ret != 0:
+ raise Exception("_qpidConfig(): cluster=\"%s\" nodeNumber=%d port=%d action=\"%s\" returned %d" % \
+ (clusterName, nodeNumber, port, action, ret))
+
+ def addExchange(self, nodeNumber, clusterName, exchangeType, exchangeName, durable = False, sequence = False, \
+ ive = False):
+ """Add a named exchange."""
+ action = "add exchange %s %s" % (exchangeType, exchangeName)
+ action += self._paramBool("durable", durable, True)
+ action += self._paramBool("sequence", sequence, True)
+ action += self._paramBool("ive", ive, True)
+ self._qpidConfig(nodeNumber, clusterName, action)
+
+ def deleteExchange(self, nodeNumber, clusterName, exchangeName):
+ """Delete a named exchange"""
+ self._qpidConfig(nodeNumber, clusterName, "del exchange %s" % exchangeName)
+
+ def addQueue(self, nodeNumber, clusterName, queueName, configArgs = None):
+ """Add a queue using qpid-config."""
+ action = "add queue %s" % queueName
+ if self._storeEnable:
+ action += " --durable"
+ if configArgs != None:
+ action += " %s" % configArgs
+ self._qpidConfig(nodeNumber, clusterName, action)
+
+ def delQueue(self, nodeNumber, clusterName, queueName):
+ """Delete a named queue using qpid-config."""
+ self._qpidConfig(nodeNumber, clusterName, "del queue %s" % queueName)
+
+ def bind(self, nodeNumber, clusterName, exchangeName, queueName, key):
+ """Create an exchange-queue binding using qpid-config."""
+ self._qpidConfig(nodeNumber, clusterName, "bind %s %s %s" % (exchangeName, queueName, key))
+
+ def unbind(self, nodeNumber, clusterName, exchangeName, queueName, key):
+ """Remove an exchange-queue binding using qpid-config."""
+ self._qpidConfig(nodeNumber, clusterName, "unbind %s %s %s" % (exchangeName, queueName, key))
+
+ # --- qpid-route functions (federation) ---
+
+ def brokerDict(self, nodeNumber, clusterName, host = "localhost", user = None, password = None):
+ """Returns a dictionary containing the broker info to be passed to route functions"""
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ return {"cluster": clusterName, "node":nodeNumber, "port":port, "host":host, "user":user, "password":password}
+
+ def _brokerStr(self, brokerDict):
+ """Set up a broker string in the format [user/password@]host:port"""
+ str = ""
+ if brokerDict["user"] !=None and brokerDict["password"] != None:
+ str = "%s@%s" % (brokerDict["user"], brokerDict["password"])
+ str += "%s:%d" % (brokerDict["host"], brokerDict["port"])
+ return str
+
+ def _qpidRoute(self, action):
+ """Set up a route using qpid-route"""
+ #print "%s %s" % (self._qpidRouteExec, action)
+ ret = os.spawnl(os.P_WAIT, self._qpidRouteExec, self._qpidRouteExec, *action.split())
+ if ret != 0:
+ raise Exception("_qpidRoute(): action=\"%s\" returned %d" % (action, ret))
+
+ def routeDynamicAdd(self, destBrokerDict, srcBrokerDict, exchangeName):
+ self._qpidRoute("dynamic add %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName))
+
+ def routeDynamicDelete(self, destBrokerDict, srcBrokerDict, exchangeName):
+ self._qpidRoute("dynamic del %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName))
+
+ def routeAdd(self, destBrokerDict, srcBrokerDict, exchangeName, routingKey):
+ self._qpidRoute("route add %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, routingKey))
+
+ def routeDelete(self, destBrokerDict, srcBrokerDict, exchangeName, routingKey):
+ self._qpidRoute("route del %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, routingKey))
+
+ def routeQueueAdd(self, destBrokerDict, srcBrokerDict, exchangeName, queueName):
+ self._qpidRoute("queue add %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, queueName))
+
+ def routeQueueDelete(self, destBrokerDict, srcBrokerDict, exchangeName, queueName):
+ self._qpidRoute("queue del %s %s %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict), exchangeName, queueName))
+
+ def routeLinkAdd(self, destBrokerDict, srcBrokerDict):
+ self._qpidRoute("link add %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict)))
+
+ def routeLinkDelete(self, destBrokerDict, srcBrokerDict):
+ self._qpidRoute("link del %s %s" % (self._brokerStr(destBrokerDict), self._brokerStr(srcBrokerDict)))
+
+ # --- Message send and receive functions ---
+
+ def _receiver(self, action):
+ if self._receiverExec == None:
+ raise Exception("Environment variable RECEIVER is not set")
+ cmd = "%s %s" % (self._receiverExec, action)
+ #print cmd
+ return subprocess.Popen(cmd.split(), stdout = subprocess.PIPE)
+
+ def _sender(self, action):
+ if self._senderExec == None:
+ raise Exception("Environment variable SENDER is not set")
+ cmd = "%s %s" % (self._senderExec, action)
+ #print cmd
+ return subprocess.Popen(cmd.split(), stdin = subprocess.PIPE)
+
+ def createReciever(self, nodeNumber, clusterName, queueName, numMsgs = None, receiverArgs = None):
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ action = "--port %d --queue %s" % (port, queueName)
+ if numMsgs != None:
+ action += " --messages %d" % numMsgs
+ if receiverArgs != None:
+ action += " %s" % receiverArgs
+ return self._receiver(action)
+
+ def createSender(self, nodeNumber, clusterName, exchangeName, routingKey, senderArgs = None):
+ port = self.getNodeTuple(nodeNumber, clusterName)[self.PORT]
+ action = "--port %d --exchange %s" % (port, exchangeName)
+ if routingKey != None and len(routingKey) > 0:
+ action += " --routing-key %s" % routingKey
+ if self._storeEnable:
+ action += " --durable yes"
+ if senderArgs != None:
+ action += " %s" % senderArgs
+ return self._sender(action)
+
+ def createBindDirectExchangeQueue(self, nodeNumber, clusterName, exchangeName, queueName):
+ self.addExchange(nodeNumber, clusterName, "direct", exchangeName)
+ self.addQueue(nodeNumber, clusterName, queueName)
+ self.bind(nodeNumber, clusterName, exchangeName, queueName, queueName)
+
+ def createBindTopicExchangeQueues(self, nodeNumber, clusterName, exchangeName, queueNameKeyList):
+ self.addExchange(nodeNumber, clusterName, "topic", exchangeName)
+ for queueName, key in queueNameKeyList.iteritems():
+ self.addQueue(nodeNumber, clusterName, queueName)
+ self.bind(nodeNumber, clusterName, exchangeName, queueName, key)
+
+ def createBindFanoutExchangeQueues(self, nodeNumber, clusterName, exchangeName, queueNameList):
+ self.addExchange(nodeNumber, clusterName, "fanout", exchangeName)
+ for queueName in queueNameList:
+ self.addQueue(nodeNumber, clusterName, queueName)
+ self.bind(nodeNumber, clusterName, exchangeName, queueName, "")
+
+ def sendMsgs(self, nodeNumber, clusterName, exchangeName, routingKey, numMsgs, msgSize = None, wait = True):
+ msgs = self._makeMessageList(numMsgs, msgSize)
+ sender = self.createSender(nodeNumber, clusterName, exchangeName, routingKey)
+ sender.stdin.write(msgs)
+ sender.stdin.close()
+ if wait:
+ sender.wait()
+ return msgs
+
+ def receiveMsgs(self, nodeNumber, clusterName, queueName, numMsgs, wait = True):
+ receiver = self.createReciever(nodeNumber, clusterName, queueName, numMsgs)
+ cnt = 0
+ msgs = ""
+ while cnt < numMsgs:
+ rx = receiver.stdout.readline()
+ if rx == "" and receiver.poll() != None: break
+ msgs += rx
+ cnt = cnt + 1
+ if wait:
+ receiver.wait()
+ return msgs
+
+
+ # --- Exchange-specific helper inner classes ---
+
+ class TestHelper:
+ """
+ This is a "virtual" superclass for test helpers, and is not useful on its own, but the
+ per-exchange subclasses are designed to keep track of the messages sent to and received
+ from queues which have bindings to that exchange type.
+ """
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
+
+ """Dictionary of queues and lists of messages sent to them."""
+ self._txMsgs = {}
+ """Dictionary of queues and lists of messages received from them."""
+ self._rxMsgs = {}
+ """List of node numbers currently in the cluster"""
+ self._nodes = []
+ """List of node numbers which have been killed and can therefore be recovered"""
+ self._deadNodes = []
+ """Last node to be used"""
+ self._lastNode = None
+
+ self._testBaseCluster = testBaseCluster
+ self._clusterName = clusterName
+ self._exchangeName = exchangeName
+ self._queueNameList = queueNameList
+ self._addQueues(queueNameList)
+ self._testBaseCluster.createCheckCluster(clusterName, numNodes)
+ self._nodes.extend(range(0, numNodes))
+
+ def _addQueues(self, queueNameList):
+ for qn in queueNameList:
+ if not qn in self._txMsgs:
+ self._txMsgs[qn] = []
+ if not qn in self._rxMsgs:
+ self._rxMsgs[qn] = []
+
+ def _bindQueue(self, queueName, bindingKey, nodeNumber = None):
+ """Bind a queue to an exchange using a binding key."""
+ if nodeNumber == None:
+ nodeNumber = self._nodes[0] # first available node
+ self._testBaseCluster.addQueue(nodeNumber, self._clusterName, queueName)
+ self._testBaseCluster.bind(nodeNumber, self._clusterName, self._exchangeName, queueName, bindingKey)
+
+ def _highestNodeNumber(self):
+ """Find the highest node number used so far between the current nodes and those stopped/killed."""
+ highestNode = self._nodes[-1]
+ if len(self._deadNodes) == 0:
+ return highestNode
+ highestDeadNode = self._deadNodes[-1]
+ if highestNode > highestDeadNode:
+ return highestNode
+ return highestDeadNode
+
+ def killCluster(self):
+ """Kill all nodes in the cluster"""
+ self._testBaseCluster.killCluster(self._clusterName)
+ self._testBaseCluster.checkNumClusterBrokers(self._clusterName, 0)
+ self._deadNodes.extend(self._nodes)
+ self._deadNodes.sort()
+ del self._nodes[:]
+
+ def restoreCluster(self, lastNode = None, restoreNodes = True):
+ """Restore a previously killed cluster"""
+ self._testBaseCluster.createCluster(self._clusterName)
+ if restoreNodes:
+ numNodes = len(self._deadNodes)
+ self.restoreNodes(lastNode)
+ self._testBaseCluster.checkNumClusterBrokers(self._clusterName, numNodes)
+
+ def addNodes(self, numberOfNodes = 1):
+ """Add a fixed number of nodes to the cluster."""
+ nodeStart = self._highestNodeNumber() + 1
+ for i in range(0, numberOfNodes):
+ nodeNumber = nodeStart + i
+ self._testBaseCluster.createClusterNode(nodeNumber, self._clusterName)
+ self._nodes.append(nodeNumber)
+ self._testBaseCluster.checkNumClusterBrokers(self._clusterName, len(self._nodes))
+ self._testBaseCluster.waitForNodes(self._clusterName)
+
+ def restoreNode(self, nodeNumber):
+ """Restore a cluster node that has been previously killed"""
+ if nodeNumber not in self._deadNodes:
+ raise Exception("restoreNode(): Node number %d not in dead node list %s" % (nodeNumber, self._deadNodes))
+ self._testBaseCluster.createClusterNode(nodeNumber, self._clusterName)
+ self._deadNodes.remove(nodeNumber)
+ self._nodes.append(nodeNumber)
+ self._nodes.sort()
+
+ def restoreNodes(self, lastNode = None):
+ """Restore all known cluster nodes that have been previously killed starting with a known last-used node"""
+ if len(self._nodes) == 0: # restore last-used node first
+ if lastNode == None:
+ lastNode = self._lastNode
+ self.restoreNode(lastNode)
+ while len(self._deadNodes) > 0:
+ self.restoreNode(self._deadNodes[0])
+ self._testBaseCluster.waitForNodes(self._clusterName)
+
+ def killNode(self, nodeNumber):
+ """Kill a cluster node (if it is in the _nodes list)."""
+ if nodeNumber not in self._nodes:
+ raise Exception("killNode(): Node number %d not in node list %s" % (nodeNumber, self._nodes))
+ self._testBaseCluster.killNode(nodeNumber, self._clusterName)
+ self._nodes.remove(nodeNumber)
+ self._deadNodes.append(nodeNumber)
+ self._deadNodes.sort()
+
+ def sendMsgs(self, routingKey, numMsgs, nodeNumber = None, msgSize = None, wait = True):
+ """Send a fixed number of messages using the given routing key."""
+ if nodeNumber == None:
+ nodeNumber = self._nodes[0] # Use first available node
+ msgs = self._testBaseCluster._makeMessageList(numMsgs, msgSize)
+ sender = self._testBaseCluster.createSender(nodeNumber, self._clusterName, self._exchangeName, routingKey)
+ sender.stdin.write(msgs)
+ sender.stdin.close()
+ if wait:
+ sender.wait()
+ self._lastNode = nodeNumber
+ return msgs.split()
+
+ # TODO - this i/f is messy: one mumMsgs can be given, but a list of queues
+ # so assuming numMsgs for each queue
+ # A mechanism is needed to specify a different numMsgs per queue
+ def receiveMsgs(self, numMsgs, nodeNumber = None, queueNameList = None, wait = True):
+ """Receive a fixed number of messages from a named queue. If numMsgs == None, get all remaining messages."""
+ if nodeNumber == None:
+ nodeNumber = self._nodes[0] # Use first available node
+ if queueNameList == None:
+ queueNameList = self._txMsgs.iterkeys()
+ for qn in queueNameList:
+ nm = numMsgs
+ if nm == None:
+ nm = len(self._txMsgs[qn]) - len(self._rxMsgs[qn]) # get all remaining messages
+ if nm > 0:
+ while nm > 0:
+ receiver = self._testBaseCluster.createReciever(nodeNumber, self._clusterName, qn, nm)
+ cnt = 0
+ while cnt < nm:
+ rx = receiver.stdout.readline().strip()
+ if rx == "":
+ if receiver.poll() != None: break
+ elif rx not in self._rxMsgs[qn]:
+ self._rxMsgs[qn].append(rx)
+ cnt = cnt + 1
+ nm = nm - cnt
+ if wait:
+ receiver.wait()
+ self._rxMsgs[qn].sort()
+ self._lastNode = nodeNumber
+
+ def receiveRemainingMsgs(self, nodeNumber = None, queueNameList = None, wait = True):
+ """Receive all remaining messages on named queue."""
+ self.receiveMsgs(None, nodeNumber, queueNameList, wait)
+
+ def checkMsgs(self):
+ """Return True if all expected messages have been received (ie the transmit and receive list are identical)."""
+ txMsgTot = 0
+ rxMsgTot = 0
+ for qn, txMsgList in self._txMsgs.iteritems():
+ rxMsgList = self._rxMsgs[qn]
+ txMsgTot = txMsgTot + len(txMsgList)
+ rxMsgTot = rxMsgTot + len(rxMsgList)
+ if len(txMsgList) != len(rxMsgList):
+ return False
+ for i, m in enumerate(txMsgList):
+ if m != rxMsgList[i]:
+ return False
+ if txMsgTot == 0 and rxMsgTot == 0:
+ print "WARNING: No messages were either sent or received"
+ return True
+
+ def finalizeTest(self):
+ """Recover all the remaining messages on all queues, then check that all expected messages were received."""
+ self.receiveRemainingMsgs()
+ self._testBaseCluster.stopAllCheck()
+ if not self.checkMsgs():
+ self.printMsgs()
+ self._testBaseCluster.fail("Send - receive message mismatch")
+
+ def printMsgs(self, txMsgs = True, rxMsgs = True):
+ """Print all messages transmitted and received."""
+ for qn, txMsgList in self._txMsgs.iteritems():
+ print "Queue: %s" % qn
+ if txMsgs:
+ print " txMsgList = %s" % txMsgList
+ if rxMsgs:
+ rxMsgList = self._rxMsgs[qn]
+ print " rxMsgList = %s" % rxMsgList
+
+
+ class DirectExchangeTestHelper(TestHelper):
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
+ TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList)
+ self._testBaseCluster.addExchange(0, clusterName, "direct", exchangeName)
+ for qn in queueNameList:
+ self._bindQueue(qn, qn)
+
+ def addQueues(self, queueNameList):
+ self._addQueues(queueNameList)
+ for qn in queueNameList:
+ self._bindQueue(qn, qn)
+
+ def sendMsgs(self, numMsgs, nodeNumber = None, queueNameList = None, msgSize = None, wait = True):
+ if queueNameList == None:
+ queueNameList = self._txMsgs.iterkeys()
+ for qn in queueNameList:
+ self._txMsgs[qn].extend(TestBaseCluster.TestHelper.sendMsgs(self, qn, numMsgs, nodeNumber, msgSize, wait))
+
+
+ class TopicExchangeTestHelper(TestHelper):
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameKeyList):
+ self._queueNameKeyList = queueNameKeyList
+ TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameKeyList.iterkeys())
+ self._testBaseCluster.addExchange(0, clusterName, "topic", exchangeName)
+ for qn, bk in queueNameKeyList.iteritems():
+ self._bindQueue(qn, bk)
+
+ def addQueues(self, queueNameKeyList):
+ self._addQueues(queueNameKeyList.iterkeys())
+ for qn, bk in queueNameKeyList.iteritems():
+ self._bindQueue(qn, bk)
+
+ def _prepareRegex(self, bk):
+ # This regex conversion is not very complete - there are other chars that should be escaped too
+ return "^%s$" % bk.replace(".", r"\.").replace("*", r"[^.]*").replace("#", ".*")
+
+ def sendMsgs(self, routingKey, numMsgs, nodeNumber = None, msgSize = None, wait = True):
+ msgList = TestBaseCluster.TestHelper.sendMsgs(self, routingKey, numMsgs, nodeNumber, msgSize, wait)
+ for qn, bk in self._queueNameKeyList.iteritems():
+ if re.match(self._prepareRegex(bk), routingKey):
+ self._txMsgs[qn].extend(msgList)
+
+
+ class FanoutExchangeTestHelper(TestHelper):
+
+ def __init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList):
+ TestBaseCluster.TestHelper.__init__(self, testBaseCluster, clusterName, numNodes, exchangeName, queueNameList)
+ self._testBaseCluster.addExchange(0, clusterName, "fanout", exchangeName)
+ for qn in queueNameList:
+ self._bindQueue(qn, "")
+
+ def addQueues(self, queueNameList):
+ self._addQueues(queueNameList)
+ for qn in queueNameList:
+ self._bindQueue(qn, "")
+
+ def sendMsgs(self, numMsgs, nodeNumber = None, msgSize = None, wait = True):
+ msgList = TestBaseCluster.TestHelper.sendMsgs(self, "", numMsgs, nodeNumber, msgSize, wait)
+ for ml in self._txMsgs.itervalues():
+ ml.extend(msgList)
+
diff --git a/cpp/src/tests/topic_listener.cpp b/cpp/src/tests/topic_listener.cpp
index 636618b6dc..aa8c19df99 100644
--- a/cpp/src/tests/topic_listener.cpp
+++ b/cpp/src/tests/topic_listener.cpp
@@ -7,9 +7,9 @@
* 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
@@ -23,7 +23,7 @@
* This file provides one half of a test and example of a pub-sub
* style of interaction. See topic_publisher.cpp for the other half,
* in which the logic for publishing is defined.
- *
+ *
* This file contains the listener logic. A listener will subscribe to
* a logical 'topic'. It will count the number of messages it receives
* and the time elapsed between the first one and the last one. It
@@ -36,7 +36,9 @@
#include "qpid/client/Connection.h"
#include "qpid/client/MessageListener.h"
#include "qpid/client/Session.h"
+#include "qpid/client/AsyncSession.h"
#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/SystemInfo.h"
#include "qpid/sys/Time.h"
#include "qpid/framing/FieldValue.h"
#include <iostream>
@@ -48,11 +50,14 @@ using namespace qpid::sys;
using namespace qpid::framing;
using namespace std;
+namespace qpid {
+namespace tests {
+
/**
* A message listener implementation in which the runtime logic is
* defined.
*/
-class Listener : public MessageListener{
+class Listener : public MessageListener{
Session session;
SubscriptionManager& mgr;
const string responseQueue;
@@ -60,12 +65,13 @@ class Listener : public MessageListener{
bool init;
int count;
AbsTime start;
-
+
void shutdown();
void report();
public:
Listener(const Session& session, SubscriptionManager& mgr, const string& reponseQueue, bool tx);
virtual void received(Message& msg);
+ Subscription subscription;
};
/**
@@ -88,6 +94,52 @@ struct Args : public qpid::TestOptions {
}
};
+Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) :
+ session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){}
+
+void Listener::received(Message& message){
+ if(!init){
+ start = now();
+ count = 0;
+ init = true;
+ cout << "Batch started." << endl;
+ }
+ string type = message.getHeaders().getAsString("TYPE");
+
+ if(string("TERMINATION_REQUEST") == type){
+ shutdown();
+ }else if(string("REPORT_REQUEST") == type){
+ subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point
+ cout <<"Batch ended, sending report." << endl;
+ //send a report:
+ report();
+ init = false;
+ }else if (++count % 1000 == 0){
+ cout <<"Received " << count << " messages." << endl;
+ }
+}
+
+void Listener::shutdown(){
+ mgr.stop();
+}
+
+void Listener::report(){
+ AbsTime finish = now();
+ Duration time(start, finish);
+ stringstream reportstr;
+ reportstr << "Received " << count << " messages in "
+ << time/TIME_MSEC << " ms.";
+ Message msg(reportstr.str(), responseQueue);
+ msg.getHeaders().setString("TYPE", "REPORT");
+ session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1);
+ if(transactional){
+ sync(session).txCommit();
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
/**
* The main routine creates a Listener instance and sets it up to
@@ -118,19 +170,20 @@ int main(int argc, char** argv){
//set up listener
SubscriptionManager mgr(session);
Listener listener(session, mgr, "response", args.transactional);
+ SubscriptionSettings settings;
if (args.prefetch) {
- mgr.setAckPolicy(AckPolicy(args.ack ? args.ack : (args.prefetch / 2)));
- mgr.setFlowControl(args.prefetch, SubscriptionManager::UNLIMITED, true);
+ settings.autoAck = (args.ack ? args.ack : (args.prefetch / 2));
+ settings.flowControl = FlowControl::messageCredit(args.prefetch);
} else {
- mgr.setAcceptMode(1/*-not-required*/);
- mgr.setFlowControl(SubscriptionManager::UNLIMITED, SubscriptionManager::UNLIMITED, false);
+ settings.acceptMode = ACCEPT_MODE_NONE;
+ settings.flowControl = FlowControl::unlimited();
}
- mgr.subscribe(listener, control);
+ listener.subscription = mgr.subscribe(listener, control, settings);
session.sync();
if( args.statusqueue.length() > 0 ) {
stringstream msg_str;
- msg_str << "topic_listener: " << (int)getpid();
+ msg_str << "topic_listener: " << qpid::sys::SystemInfo::getProcessId();
session.messageTransfer(arg::content=Message(msg_str.str(), args.statusqueue));
cout << "Ready status put on queue '" << args.statusqueue << "'" << endl;
}
@@ -138,7 +191,7 @@ int main(int argc, char** argv){
if (args.transactional) {
session.txSelect();
}
-
+
cout << "topic_listener: listening..." << endl;
mgr.run();
if (args.durable) {
@@ -154,47 +207,3 @@ int main(int argc, char** argv){
}
return 1;
}
-
-Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) :
- session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){}
-
-void Listener::received(Message& message){
- if(!init){
- start = now();
- count = 0;
- init = true;
- cout << "Batch started." << endl;
- }
- string type = message.getHeaders().getString("TYPE");
-
- if(string("TERMINATION_REQUEST") == type){
- shutdown();
- }else if(string("REPORT_REQUEST") == type){
- mgr.getAckPolicy().ackOutstanding(session);//acknowledge everything upto this point
- cout <<"Batch ended, sending report." << endl;
- //send a report:
- report();
- init = false;
- }else if (++count % 1000 == 0){
- cout <<"Received " << count << " messages." << endl;
- }
-}
-
-void Listener::shutdown(){
- mgr.stop();
-}
-
-void Listener::report(){
- AbsTime finish = now();
- Duration time(start, finish);
- stringstream reportstr;
- reportstr << "Received " << count << " messages in "
- << time/TIME_MSEC << " ms.";
- Message msg(reportstr.str(), responseQueue);
- msg.getHeaders().setString("TYPE", "REPORT");
- session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1);
- if(transactional){
- sync(session).txCommit();
- }
-}
-
diff --git a/cpp/src/tests/topic_perftest b/cpp/src/tests/topic_perftest
index 5d317610f3..cd440b2458 100755
--- a/cpp/src/tests/topic_perftest
+++ b/cpp/src/tests/topic_perftest
@@ -1,2 +1,22 @@
#!/bin/sh
+
+#
+# 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.
+#
+
exec `dirname $0`/run_perftest 10000 --mode topic --qt 16
diff --git a/cpp/src/tests/topic_publisher.cpp b/cpp/src/tests/topic_publisher.cpp
index f37ad2dc0e..3381132b1a 100644
--- a/cpp/src/tests/topic_publisher.cpp
+++ b/cpp/src/tests/topic_publisher.cpp
@@ -7,9 +7,9 @@
* 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
@@ -23,7 +23,7 @@
* This file provides one half of a test and example of a pub-sub
* style of interaction. See topic_listener.cpp for the other half, in
* which the logic for subscribers is defined.
- *
+ *
* This file contains the publisher logic. The publisher will send a
* number of messages to the exchange with the appropriate routing key
* for the logical 'topic'. Once it has done this it will then send a
@@ -40,7 +40,6 @@
#include "qpid/client/AsyncSession.h"
#include "qpid/client/SubscriptionManager.h"
#include "qpid/sys/Monitor.h"
-#include <unistd.h>
#include "qpid/sys/Time.h"
#include <cstdlib>
#include <iostream>
@@ -50,19 +49,22 @@ using namespace qpid::client;
using namespace qpid::sys;
using namespace std;
+namespace qpid {
+namespace tests {
+
/**
* The publishing logic is defined in this class. It implements
* message listener and can therfore be used to receive messages sent
* back by the subscribers.
*/
-class Publisher {
+class Publisher {
AsyncSession session;
SubscriptionManager mgr;
LocalQueue queue;
const string controlTopic;
const bool transactional;
const bool durable;
-
+
string generateData(int size);
public:
@@ -100,6 +102,64 @@ struct Args : public TestOptions {
}
};
+Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) :
+ session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d)
+{
+ mgr.subscribe(queue, "response");
+}
+
+int64_t Publisher::publish(int msgs, int listeners, int size){
+ Message msg(generateData(size), controlTopic);
+ if (durable) {
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+ AbsTime start = now();
+
+ for(int i = 0; i < msgs; i++){
+ session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1);
+ }
+ //send report request
+ Message reportRequest("", controlTopic);
+ reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST");
+ session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1);
+ if(transactional){
+ sync(session).txCommit();
+ }
+ //wait for a response from each listener (TODO, could log these)
+ for (int i = 0; i < listeners; i++) {
+ Message report = queue.pop();
+ }
+
+ if(transactional){
+ sync(session).txCommit();
+ }
+
+ AbsTime finish = now();
+ return Duration(start, finish);
+}
+
+string Publisher::generateData(int size){
+ string data;
+ for(int i = 0; i < size; i++){
+ data += ('A' + (i / 26));
+ }
+ return data;
+}
+
+void Publisher::terminate(){
+ //send termination request
+ Message terminationRequest("", controlTopic);
+ terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST");
+ session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1);
+ if(transactional){
+ session.txCommit();
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char** argv) {
try{
Args args;
@@ -121,11 +181,11 @@ int main(int argc, char** argv) {
Message m = statusQ.get();
if( m.getData().find("topic_listener: ", 0) == 0 ) {
cout << "Listener " << (i+1) << " of " << args.subscribers
- << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16)
- << ")" << endl;
+ << " is ready (pid " << m.getData().substr(16, m.getData().length() - 16)
+ << ")" << endl;
} else {
throw Exception(QPID_MSG("Unexpected message received on status queue: " << m.getData()));
- }
+ }
}
}
@@ -142,7 +202,7 @@ int main(int argc, char** argv) {
int64_t min(0);
int64_t sum(0);
for(int i = 0; i < batchSize; i++){
- if(i > 0 && args.delay) sleep(args.delay);
+ if(i > 0 && args.delay) qpid::sys::sleep(args.delay);
int64_t msecs =
publisher.publish(args.messages,
args.subscribers,
@@ -151,12 +211,12 @@ int main(int argc, char** argv) {
if(!min || msecs < min) min = msecs;
sum += msecs;
cout << "Completed " << (i+1) << " of " << batchSize
- << " in " << msecs << "ms" << endl;
+ << " in " << msecs << "ms" << endl;
}
publisher.terminate();
int64_t avg = sum / batchSize;
if(batchSize > 1){
- cout << batchSize << " batches completed. avg=" << avg <<
+ cout << batchSize << " batches completed. avg=" << avg <<
", max=" << max << ", min=" << min << endl;
}
session.close();
@@ -168,57 +228,3 @@ int main(int argc, char** argv) {
}
return 1;
}
-
-Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) :
- session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d)
-{
- mgr.subscribe(queue, "response");
-}
-
-int64_t Publisher::publish(int msgs, int listeners, int size){
- Message msg(generateData(size), controlTopic);
- if (durable) {
- msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
- }
- AbsTime start = now();
-
- for(int i = 0; i < msgs; i++){
- session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1);
- }
- //send report request
- Message reportRequest("", controlTopic);
- reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST");
- session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1);
- if(transactional){
- sync(session).txCommit();
- }
- //wait for a response from each listener (TODO, could log these)
- for (int i = 0; i < listeners; i++) {
- Message report = queue.pop();
- }
-
- if(transactional){
- sync(session).txCommit();
- }
-
- AbsTime finish = now();
- return Duration(start, finish);
-}
-
-string Publisher::generateData(int size){
- string data;
- for(int i = 0; i < size; i++){
- data += ('A' + (i / 26));
- }
- return data;
-}
-
-void Publisher::terminate(){
- //send termination request
- Message terminationRequest("", controlTopic);
- terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST");
- session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1);
- if(transactional){
- session.txCommit();
- }
-}
diff --git a/cpp/src/tests/topictest b/cpp/src/tests/topictest
index 6763d8552b..8fd680ee35 100755
--- a/cpp/src/tests/topictest
+++ b/cpp/src/tests/topictest
@@ -1,4 +1,24 @@
#!/bin/bash
+
+#
+# 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.
+#
+
# Run the C++ topic test
# Clean up old log files
diff --git a/cpp/src/tests/topictest.ps1 b/cpp/src/tests/topictest.ps1
new file mode 100644
index 0000000000..0d22cea657
--- /dev/null
+++ b/cpp/src/tests/topictest.ps1
@@ -0,0 +1,70 @@
+#
+# 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.
+#
+
+# Run the C++ topic test
+$srcdir = Split-Path $myInvocation.InvocationName
+
+# Parameters with default values: s (subscribers) m (messages) b (batches)
+# h (host) t (false; use transactions)
+param (
+ [int]$subscribers = 10,
+ [int]$messages = 2000,
+ [int]$batches = 10,
+ [string]$broker,
+ [switch] $t # transactional
+)
+
+# Clean up old log files
+Get-Item subscriber_*.log | Remove-Item
+
+if ($t) {
+ $transactional = "--transactional --durable"
+}
+
+# Find which subdir the exes are in
+. $srcdir\find_prog.ps1 .\topic_listener.exe
+
+function subscribe {
+ param ([int]$num)
+ "Start subscriber $num"
+ $LOG = "subscriber_$num.log"
+ $cmdline = ".\$sub\topic_listener $transactional > $LOG 2>&1
+ if (`$LastExitCode -ne 0) { Remove-Item $LOG }"
+ $cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+ . $srcdir\background.ps1 $cmdblock
+}
+
+function publish {
+ Invoke-Expression ".\$sub\topic_publisher --messages $messages --batches $batches --subscribers $subscribers $host $transactional" 2>&1
+}
+
+if ($broker.length) {
+ $broker = "-h$broker"
+}
+
+$i = $subscribers
+while ($i -gt 0) {
+ subscribe $i
+ $i--
+}
+
+# FIXME aconway 2007-03-27: Hack around startup race. Fix topic test.
+Start-Sleep 2
+publish
+exit $LastExitCode
diff --git a/cpp/src/tests/txjob.cpp b/cpp/src/tests/txjob.cpp
new file mode 100644
index 0000000000..a7a905c1b7
--- /dev/null
+++ b/cpp/src/tests/txjob.cpp
@@ -0,0 +1,102 @@
+/*
+ *
+ * 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 <iostream>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include "TestOptions.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid::client;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string workQueue;
+ string source;
+ string dest;
+ uint messages;
+ uint jobs;
+ bool quit;
+ bool declareQueues;
+
+ Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0),
+ quit(false), declareQueues(false)
+ {
+ addOptions()
+ ("messages", qpid::optValue(messages, "N"), "Number of messages to shift")
+ ("jobs", qpid::optValue(jobs, "N"), "Number of shift jobs to request")
+ ("source", qpid::optValue(source, "QUEUE NAME"), "source queue from which messages will be shifted")
+ ("dest", qpid::optValue(dest, "QUEUE NAME"), "dest queue to which messages will be shifted")
+ ("work-queue", qpid::optValue(workQueue, "QUEUE NAME"), "work queue from which to take instructions")
+ ("add-quit", qpid::optValue(quit), "add a 'quit' instruction to the queue (after any other jobs)")
+ ("declare-queues", qpid::optValue(declareQueues), "issue a declare for all queues");
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+//TODO: might be nice to make this capable of failover as well at some
+//point; for now its just for the setup phase.
+int main(int argc, char** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ Connection connection;
+ connection.open(opts.con);
+ Session session = connection.newSession();
+ if (opts.declareQueues) {
+ session.queueDeclare(arg::queue=opts.workQueue);
+ session.queueDeclare(arg::queue=opts.source);
+ session.queueDeclare(arg::queue=opts.dest);
+ }
+ for (uint i = 0; i < opts.jobs; ++i) {
+ Message job("transfer", opts.workQueue);
+ job.getHeaders().setString("src", opts.source);
+ job.getHeaders().setString("dest", opts.dest);
+ job.getHeaders().setInt("count", opts.messages);
+ async(session).messageTransfer(arg::content=job);
+ }
+
+ if (opts.quit) {
+ async(session).messageTransfer(arg::content=Message("quit", opts.workQueue));
+ }
+
+ session.sync();
+ session.close();
+
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+}
diff --git a/cpp/src/tests/txshift.cpp b/cpp/src/tests/txshift.cpp
new file mode 100644
index 0000000000..882d3716d8
--- /dev/null
+++ b/cpp/src/tests/txshift.cpp
@@ -0,0 +1,193 @@
+/*
+ *
+ * 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 <iostream>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include "TestOptions.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid::client;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string workQueue;
+ size_t workers;
+
+ Args() : workQueue("txshift-control"), workers(1)
+ {
+ addOptions()
+ ("workers", qpid::optValue(workers, "N"), "Number of separate worker sessions to start")
+ ("work-queue", qpid::optValue(workQueue, "NAME"), "work queue from which to take instructions");
+ }
+};
+
+struct Transfer : MessageListener
+{
+ std::string control;
+ std::string source;
+ std::string destination;
+ uint expected;
+ uint transfered;
+ SubscriptionSettings controlSettings;
+ Subscription controlSubscription;
+ SubscriptionSettings sourceSettings;
+ Subscription sourceSubscription;
+
+ Transfer(const std::string control_) : control(control_), expected(0), transfered(0) {}
+
+ void subscribeToSource(SubscriptionManager manager)
+ {
+ sourceSettings.autoAck = 0;//will accept once at the end of the batch
+ sourceSettings.flowControl = FlowControl::messageCredit(expected);
+ sourceSubscription = manager.subscribe(*this, source, sourceSettings);
+ QPID_LOG(info, "Subscribed to source: " << source << " expecting: " << expected);
+ }
+
+ void subscribeToControl(SubscriptionManager manager)
+ {
+ controlSettings.flowControl = FlowControl::messageCredit(1);
+ controlSubscription = manager.subscribe(*this, control, controlSettings);
+ QPID_LOG(info, "Subscribed to job queue");
+ }
+
+ void received(Message& message)
+ {
+ QPID_LOG(debug, "received: " << message.getData() << " for " << message.getDestination());
+ if (message.getDestination() == source) {
+ receivedFromSource(message);
+ } else if (message.getDestination() == control) {
+ receivedFromControl(message);
+ } else {
+ QPID_LOG(error, "Unexpected message: " << message.getData() << " to " << message.getDestination());
+ }
+ }
+
+ void receivedFromSource(Message& message)
+ {
+ QPID_LOG(debug, "transfering " << (transfered+1) << " of " << expected);
+ message.getDeliveryProperties().setRoutingKey(destination);
+ async(sourceSubscription.getSession()).messageTransfer(arg::content=message);
+ if (++transfered == expected) {
+ QPID_LOG(info, "completed job: " << transfered << " messages shifted from " <<
+ source << " to " << destination);
+ sourceSubscription.accept(sourceSubscription.getUnaccepted());
+ sourceSubscription.getSession().txCommit();
+ sourceSubscription.cancel();
+ //grant credit to allow broker to send us another control message
+ controlSubscription.grantMessageCredit(1);
+ }
+ }
+
+ void receivedFromControl(Message& message)
+ {
+ if (message.getData() == "transfer") {
+ source = message.getHeaders().getAsString("src");
+ destination = message.getHeaders().getAsString("dest");
+ expected = message.getHeaders().getAsInt("count");
+ transfered = 0;
+ QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " <<
+ source << " to " << destination);
+ subscribeToSource(controlSubscription.getSubscriptionManager());
+ } else if (message.getData() == "quit") {
+ QPID_LOG(info, "received quit request");
+ controlSubscription.cancel();
+ } else {
+ std::cerr << "Rejecting invalid message: " << message.getData() << std::endl;
+ controlSubscription.getSession().messageReject(SequenceSet(message.getId()));
+ }
+ }
+
+};
+
+struct Worker : FailoverManager::Command, Runnable
+{
+ FailoverManager& connection;
+ Transfer transfer;
+ Thread runner;
+
+ Worker(FailoverManager& c, const std::string& controlQueue) : connection(c), transfer(controlQueue) {}
+
+ void run()
+ {
+ connection.execute(*this);
+ }
+
+ void start()
+ {
+ runner = Thread(this);
+ }
+
+ void join()
+ {
+ runner.join();
+ }
+
+ void execute(AsyncSession& session, bool isRetry)
+ {
+ if (isRetry) QPID_LOG(info, "Retrying...");
+ session.txSelect();
+ SubscriptionManager subs(session);
+ transfer.subscribeToControl(subs);
+ subs.run();
+ session.txCommit();//commit accept of control messages
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ FailoverManager connection(opts.con);
+ connection.connect();
+ if (opts.workers == 1) {
+ Worker worker(connection, opts.workQueue);
+ worker.run();
+ } else {
+ boost::ptr_vector<Worker> workers;
+ for (size_t i = 0; i < opts.workers; i++) {
+ workers.push_back(new Worker(connection, opts.workQueue));
+ }
+ std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::start, _1));
+ std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::join, _1));
+ }
+
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+}
diff --git a/cpp/src/tests/txtest.cpp b/cpp/src/tests/txtest.cpp
index f0e70e83e9..d0ba2f1245 100644
--- a/cpp/src/tests/txtest.cpp
+++ b/cpp/src/tests/txtest.cpp
@@ -7,9 +7,9 @@
* 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
@@ -33,12 +33,17 @@
#include "qpid/client/SubscriptionManager.h"
#include "qpid/framing/Array.h"
#include "qpid/framing/Buffer.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Thread.h"
using namespace qpid;
using namespace qpid::client;
using namespace qpid::sys;
using std::string;
+namespace qpid {
+namespace tests {
+
typedef std::vector<std::string> StringSet;
struct Args : public qpid::TestOptions {
@@ -51,13 +56,14 @@ struct Args : public qpid::TestOptions {
uint txCount;
uint totalMsgCount;
bool dtx;
+ bool quiet;
- Args() : init(true), transfer(true), check(true),
- size(256), durable(true), queues(2),
+ Args() : init(true), transfer(true), check(true),
+ size(256), durable(true), queues(2),
base("tx-test"), msgsPerTx(1), txCount(1), totalMsgCount(10),
- dtx(false)
+ dtx(false), quiet(false)
{
- addOptions()
+ addOptions()
("init", optValue(init, "yes|no"), "Declare queues and populate one with the initial set of messages.")
("transfer", optValue(transfer, "yes|no"), "'Move' messages from one queue to another using transactions to ensure no message loss.")
@@ -69,7 +75,8 @@ struct Args : public qpid::TestOptions {
("messages-per-tx", optValue(msgsPerTx, "N"), "number of messages transferred per transaction")
("tx-count", optValue(txCount, "N"), "number of transactions per 'agent'")
("total-messages", optValue(totalMsgCount, "N"), "total number of messages in 'circulation'")
- ("dtx", optValue(dtx, "yes|no"), "use distributed transactions");
+ ("dtx", optValue(dtx, "yes|no"), "use distributed transactions")
+ ("quiet", optValue(quiet), "reduce output from test");
}
};
@@ -79,7 +86,7 @@ std::string generateData(uint size)
{
if (size < chars.length()) {
return chars.substr(0, size);
- }
+ }
std::string data;
for (uint i = 0; i < (size / chars.length()); i++) {
data += chars;
@@ -99,18 +106,18 @@ void generateSet(const std::string& base, uint count, StringSet& collection)
Args opts;
-struct Client
+struct Client
{
Connection connection;
AsyncSession session;
- Client()
+ Client()
{
opts.open(connection);
session = connection.newSession();
}
- ~Client()
+ ~Client()
{
try{
session.close();
@@ -126,38 +133,44 @@ struct Transfer : public Client, public Runnable
std::string src;
std::string dest;
Thread thread;
- unsigned long xid_cnt;
framing::Xid xid;
- Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid_cnt(0), xid(0x4c414e47, "", from) {}
+ Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid(0x4c414e47, "", from) {}
- void run()
+ void run()
{
try {
-
+
if (opts.dtx) session.dtxSelect();
else session.txSelect();
SubscriptionManager subs(session);
-
- LocalQueue lq(AckPolicy(0));//manual acking
- subs.setFlowControl(opts.msgsPerTx, SubscriptionManager::UNLIMITED, true);
- subs.subscribe(lq, src);
-
+
+ LocalQueue lq;
+ SubscriptionSettings settings(FlowControl::messageWindow(opts.msgsPerTx));
+ settings.autoAck = 0; // Disabled
+ Subscription sub = subs.subscribe(lq, src, settings);
+
for (uint t = 0; t < opts.txCount; t++) {
Message in;
Message out("", dest);
if (opts.dtx) {
- setNextXid(xid);
+ setNewXid(xid);
session.dtxStart(arg::xid=xid);
}
for (uint m = 0; m < opts.msgsPerTx; m++) {
in = lq.pop();
- out.setData(in.getData());
+ std::string& data = in.getData();
+ if (data.size() != opts.size) {
+ std::ostringstream oss;
+ oss << "Message size incorrect: size=" << in.getData().size() << "; expected " << opts.size;
+ throw std::runtime_error(oss.str());
+ }
+ out.setData(data);
out.getMessageProperties().setCorrelationId(in.getMessageProperties().getCorrelationId());
out.getDeliveryProperties().setDeliveryMode(in.getDeliveryProperties().getDeliveryMode());
session.messageTransfer(arg::content=out, arg::acceptMode=1);
}
- lq.getAckPolicy().ackOutstanding(session);
+ sub.accept(sub.getUnaccepted());
if (opts.dtx) {
session.dtxEnd(arg::xid=xid);
session.dtxPrepare(arg::xid=xid);
@@ -171,14 +184,13 @@ struct Transfer : public Client, public Runnable
}
}
- void setNextXid(framing::Xid& xid) {
- std::ostringstream oss;
- oss << std::setfill('0') << std::hex << "xid-" << std::setw(12) << (++xid_cnt);
- xid.setGlobalId(oss.str());
+ void setNewXid(framing::Xid& xid) {
+ framing::Uuid uuid(true);
+ xid.setGlobalId(uuid.str());
}
};
-struct Controller : public Client
+struct Controller : public Client
{
StringSet ids;
StringSet queues;
@@ -189,7 +201,7 @@ struct Controller : public Client
generateSet("msg", opts.totalMsgCount, ids);
}
- void init()
+ void init()
{
//declare queues
for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) {
@@ -217,7 +229,7 @@ struct Controller : public Client
StringSet::iterator next = i + 1;
if (next == queues.end()) next = queues.begin();
- std::cout << "Transfering from " << *i << " to " << *next << std::endl;
+ if (!opts.quiet) std::cout << "Transfering from " << *i << " to " << *next << std::endl;
agents.push_back(new Transfer(*i, *next));
agents.back().thread = Thread(agents.back());
}
@@ -227,11 +239,9 @@ struct Controller : public Client
}
}
- void check()
+ int check()
{
SubscriptionManager subs(session);
- subs.setFlowControl(SubscriptionManager::UNLIMITED, SubscriptionManager::UNLIMITED, false);
- subs.setAcceptMode(1/*not-required*/);
// Recover DTX transactions (if any)
if (opts.dtx) {
@@ -241,13 +251,13 @@ struct Controller : public Client
xidArr.collect(inDoubtXids);
if (inDoubtXids.size()) {
- std::cout << "Recovering DTX in-doubt transaction(s):" << std::endl;
+ if (!opts.quiet) std::cout << "Recovering DTX in-doubt transaction(s):" << std::endl;
framing::StructHelper decoder;
framing::Xid xid;
// abort even, commit odd transactions
for (unsigned i = 0; i < inDoubtXids.size(); i++) {
decoder.decode(xid, inDoubtXids[i]);
- std::cout << (i%2 ? " * aborting " : " * committing ");
+ if (!opts.quiet) std::cout << (i%2 ? " * aborting " : " * committing ");
xid.print(std::cout);
std::cout << std::endl;
if (i%2) {
@@ -262,9 +272,10 @@ struct Controller : public Client
StringSet drained;
//drain each queue and verify the correct set of messages are available
for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) {
- //subscribe, allocate credit and flush
- LocalQueue lq(AckPolicy(0));//manual acking
- subs.subscribe(lq, *i, *i);
+ //subscribe, allocate credit and flushn
+ LocalQueue lq;
+ SubscriptionSettings settings(FlowControl::unlimited(), ACCEPT_MODE_NONE);
+ subs.subscribe(lq, *i, settings);
session.messageFlush(arg::destination=*i);
session.sync();
@@ -275,7 +286,7 @@ struct Controller : public Client
drained.push_back(m.getMessageProperties().getCorrelationId());
++count;
}
- std::cout << "Drained " << count << " messages from " << *i << std::endl;
+ if (!opts.quiet) std::cout << "Drained " << count << " messages from " << *i << std::endl;
}
sort(ids.begin(), ids.end());
@@ -283,41 +294,47 @@ struct Controller : public Client
//check that drained == ids
StringSet missing;
- set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing));
+ set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing));
StringSet extra;
- set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra));
+ set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra));
if (missing.empty() && extra.empty()) {
- std::cout << "All expected messages were retrieved." << std::endl;
+ std::cout << "All expected messages were retrieved." << std::endl;
+ return 0;
} else {
if (!missing.empty()) {
std::cout << "The following ids were missing:" << std::endl;
for (StringSet::iterator i = missing.begin(); i != missing.end(); i++) {
- std::cout << " '" << *i << "'" << std::endl;
- }
+ std::cout << " '" << *i << "'" << std::endl;
+ }
}
if (!extra.empty()) {
std::cout << "The following extra ids were encountered:" << std::endl;
for (StringSet::iterator i = extra.begin(); i != extra.end(); i++) {
- std::cout << " '" << *i << "'" << std::endl;
- }
+ std::cout << " '" << *i << "'" << std::endl;
+ }
}
+ return 1;
}
}
};
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
int main(int argc, char** argv)
{
try {
opts.parse(argc, argv);
Controller controller;
- if (opts.init) controller.init();
+ if (opts.init) controller.init();
if (opts.transfer) controller.transfer();
- if (opts.check) controller.check();
+ if (opts.check) return controller.check();
return 0;
} catch(const std::exception& e) {
std::cout << e.what() << std::endl;
}
- return 1;
+ return 2;
}
diff --git a/cpp/src/tests/unit_test.h b/cpp/src/tests/unit_test.h
index b986c74afa..ed9623bcc0 100644
--- a/cpp/src/tests/unit_test.h
+++ b/cpp/src/tests/unit_test.h
@@ -51,18 +51,6 @@
#endif // Workarounds for BOOST_AUTO_TEST_CASE|SUITE|SUITE_END
-// Workaround for BOOST_AUTO_TEST_SUITE_EXPECTED_FAILURES
-//
-#if (BOOST_VERSION < 103500)
-
-// Keep the test function for compilation but do not not register it.
-// TODO aconway 2008-04-23: better workaround for expected failures.
-# define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \
- namespace { struct test_name { void test_method(); }; } \
- void test_name::test_method()
-
-#endif // Workaround for BOOST_AUTO_TEST_SUITE_EXPECTED_FAILURES
-
//
// Default definitions for latest version of boost.
//
@@ -75,10 +63,6 @@
# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name)
#endif
-#ifndef QPID_AUTO_TEST_CASE_EXPECTED_FAILURES
-# define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(name,n) BOOST_AUTO_TEST_SUITE_EXPECTED_FAILURES(name,n)
-#endif
-
#ifndef QPID_AUTO_TEST_SUITE_END
# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
#endif
diff --git a/cpp/src/tests/vg_check b/cpp/src/tests/vg_check
index e9a691abe6..c5a1e6d2d0 100644
--- a/cpp/src/tests/vg_check
+++ b/cpp/src/tests/vg_check
@@ -1,6 +1,7 @@
# Check for valgrind errors. Sourced by test scripts.
vg_failed() {
+ echo "Valgrind error log in $VG_LOG." 1>&2
cat $VG_LOG 1>&2
echo $1 1>&2
exit 1
@@ -12,10 +13,10 @@ vg_check()
test -f $VG_LOG || vg_failed Valgrind log file $VG_LOG missing.
# Ensure there is an ERROR SUMMARY line.
grep -E '^==[0-9]+== ERROR SUMMARY:' $VG_LOG > /dev/null || \
- vg_failed "No valgrind ERROR SUMMARY line in $$vg_failed."
+ vg_failed "No valgrind ERROR SUMMARY line in $VG_LOG."
# Ensure that the number of errors is 0.
grep -E '^==[0-9]+== ERROR SUMMARY: [^0]' $VG_LOG > /dev/null && \
- vg_failed "Valgrind reported errors in $vg_out; see above."
+ vg_failed "Valgrind reported errors in $VG_LOG; see above."
# Check for leaks.
grep -E '^==[0-9]+== +.* lost: [^0]' $VG_LOG && \
vg_failed "Found memory leaks (see log file, $VG_LOG); see above."
diff --git a/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp b/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
new file mode 100644
index 0000000000..a0b665db73
--- /dev/null
+++ b/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+// This file intends to prevent Windows from throwing up error boxes and
+// offering to debug when serious errors happen. The errors are displayed
+// on stderr instead. The purpose of this is to allow the tests to proceed
+// scripted and catch the text for logging. If this behavior is desired,
+// include this file with the executable being built. If the default
+// behaviors are desired, don't include this file in the build.
+
+#include <crtdbg.h>
+#include <windows.h>
+#include <iostream>
+
+namespace {
+
+// Instead of popping up a window for exceptions, just print something out
+LONG _stdcall UnhandledExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo)
+{
+ DWORD dwExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
+
+ if (dwExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ std::cerr << "\nERROR: ACCESS VIOLATION\n" << std::endl;
+ else
+ std::cerr << "\nERROR: UNHANDLED EXCEPTION\n" << std::endl;
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+struct redirect_errors_to_stderr {
+ redirect_errors_to_stderr ();
+};
+
+static redirect_errors_to_stderr block;
+
+redirect_errors_to_stderr::redirect_errors_to_stderr()
+{
+ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+
+ // Prevent the system from displaying the critical-error-handler
+ // and can't-open-file message boxes.
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+ SetErrorMode(SEM_NOOPENFILEERRORBOX);
+
+ // And this will catch all unhandled exceptions.
+ SetUnhandledExceptionFilter (&UnhandledExceptionFilter);
+}
+
+} // namespace