diff options
Diffstat (limited to 'RC9/qpid/cpp/src/tests')
130 files changed, 17275 insertions, 0 deletions
diff --git a/RC9/qpid/cpp/src/tests/.valgrind.supp b/RC9/qpid/cpp/src/tests/.valgrind.supp new file mode 100644 index 0000000000..7ac34fde5d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/.valgrind.supp @@ -0,0 +1,203 @@ +{ + Benign error in libcpg. + Memcheck:Param + socketcall.sendmsg(msg.msg_iov[i]) + obj:*/libpthread-2.5.so + obj:*/libcpg.so.2.0.0 +} + +{ + Uninitialised value problem in _dl_relocate (F7, F8) + Memcheck:Cond + fun:_dl_relocate_object + fun:*dl_* +} + +{ + False "possibly leaked" in boost program_options - global std::string var. + Memcheck:Leak + fun:_Znwj + fun:_ZNSs4_Rep9_S_createEjjRKSaIcE + obj:/usr/lib/libstdc++.so.6.0.8 + fun:_ZNSsC1EPKcRKSaIcE + obj:/usr/lib/libboost_program_options.so.1.33.1 +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:_Znwm + fun:_ZN5boost15program_options??options_description* +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:_Znwm + fun:_ZN5boost9unit_test9test_case* +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:calloc + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 + fun:_ZN4qpid3sys5Shlib4loadEPKc + fun:_Z9testShlibv + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv + obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0 + fun:_ZN5boost17execution_monitor7executeEbi + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi + fun:_ZN5boost9unit_test9test_case3runEv + fun:_ZN5boost9unit_test10test_suite6do_runEv + fun:_ZN5boost9unit_test9test_case3runEv + fun:main +} + +{ + boost 103200 -- we think Boost is responsible for these leaks. + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6broker5Timer5startEv + fun:_ZN4qpid6broker5TimerC1Ev + fun:_ZN4qpid6broker10DtxManagerC1Ev + fun:_ZN4qpid6broker6BrokerC1ERKNS1_7OptionsE + fun:_ZN4qpid6broker6Broker6createERKNS1_7OptionsE + fun:_ZN15SessionFixtureTI15ProxyConnectionEC2Ev + fun:_Z14testQueueQueryv + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv + obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0 + fun:_ZN5boost17execution_monitor7executeEbi + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi + fun:_ZN5boost9unit_test9test_case3runEv + fun:_ZN5boost9unit_test10test_suite6do_runEv + fun:_ZN5boost9unit_test9test_case3runEv + fun:main +} + +{ + INVESTIGATE + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6client9Connector4initEv + fun:_ZN4qpid6client14ConnectionImpl4openERKSsiS3_S3_S3_ +} + +{ + INVESTIGATE + Memcheck:Param + write(buf) + obj:/lib64/tls/libc-2.3.4.so + fun:_ZNK4qpid3sys6Socket5writeEPKvm + fun:_ZN4qpid3sys8AsynchIO9writeableERNS0_14DispatchHandleE +} + +{ + "Conditional jump or move depends on uninitialised value(s)" from Xerces parser + Memcheck:Cond + fun:_ZN11xercesc_2_717XMLUTF8Transcoder13transcodeFromEPKhjPtjRjPh + fun:_ZN11xercesc_2_79XMLReader14xcodeMoreCharsEPtPhj + fun:_ZN11xercesc_2_79XMLReader17refreshCharBufferEv +} + +{ + boost 103200 -- mgoulish -- fix this, sometime + Memcheck:Leak + fun:* + fun:* + obj:* + fun:* + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ +} + +{ + boost 103200 -- mgoulish -- fix this, sometime + Memcheck:Leak + fun:* + fun:* + fun:* + fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_ +} + +{ + INVESTIGATE + Memcheck:Param + socketcall.sendto(msg) + fun:send + fun:get_mapping + fun:__nscd_get_map_ref + fun:nscd_gethst_r + fun:__nscd_gethostbyname_r + fun:gethostbyname_r@@GLIBC_2.2.5 + fun:gethostbyname + fun:_ZNK4qpid3sys6Socket7connectERKSsi +} + +{ + INVESTIGATE + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6broker5Timer5startEv + fun:_ZN4qpid6broker5TimerC1Ev + fun:_ZN4qpid6broker10DtxManagerC1Ev + fun:_ZN4qpid6broker6BrokerC1ERKNS1_7OptionsE + fun:_ZN4qpid6broker6Broker6createERKNS1_7OptionsE + fun:_ZN20ClientSessionFixtureC1Ev + fun:_Z14testQueueQueryv + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv + obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0 + fun:_ZN5boost17execution_monitor7executeEbi + fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi + fun:_ZN5boost9unit_test9test_case3runEv + fun:_ZN5boost9unit_test10test_suite6do_runEv + fun:_ZN5boost9unit_test9test_case3runEv + fun:main +} + +{ + INVESTIGATE + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_ZN4qpid6client9Connector4initEv +} + +{ + MICK -- FIX + Memcheck:Leak + fun:_Znam + fun:_ZN4qpid7Options5parseEiPPcRKSsb +} + +{ + MICK -- FIX + Memcheck:Leak + fun:malloc + fun:strdup + fun:_ZN4qpid7Options5parseEiPPcRKSsb +} + +{ + CPG error - seems benign. + Memcheck:Param + socketcall.sendmsg(msg.msg_iov[i]) + 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 +} + diff --git a/RC9/qpid/cpp/src/tests/AccumulatedAckTest.cpp b/RC9/qpid/cpp/src/tests/AccumulatedAckTest.cpp new file mode 100644 index 0000000000..028ce71907 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/AccumulatedAckTest.cpp @@ -0,0 +1,232 @@ + +/* + * + * 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/AccumulatedAck.h" +#include "unit_test.h" +#include <iostream> +#include <list> + +using std::list; +using namespace qpid::framing; + + +bool covers(const AccumulatedAck& ack, int i) +{ + return ack.covers(SequenceNumber(i)); +} + +void update(AccumulatedAck& ack, int start, int end) +{ + ack.update(SequenceNumber(start), SequenceNumber(end)); +} + +QPID_AUTO_TEST_SUITE(AccumulatedAckTestSuite) + +QPID_AUTO_TEST_CASE(testGeneral) +{ + AccumulatedAck ack(0); + ack.clear(); + update(ack, 3,3); + update(ack, 7,7); + update(ack, 9,9); + update(ack, 1,2); + update(ack, 4,5); + update(ack, 6,6); + + for(int i = 1; i <= 7; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(covers(ack, 9)); + + BOOST_CHECK(!covers(ack, 8)); + BOOST_CHECK(!covers(ack, 10)); + + ack.consolidate(); + + for(int i = 1; i <= 7; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(covers(ack, 9)); + + BOOST_CHECK(!covers(ack, 8)); + BOOST_CHECK(!covers(ack, 10)); +} + +QPID_AUTO_TEST_CASE(testCovers) +{ + AccumulatedAck ack(5); + update(ack, 7, 7); + update(ack, 9, 9); + + BOOST_CHECK(covers(ack, 1)); + BOOST_CHECK(covers(ack, 2)); + BOOST_CHECK(covers(ack, 3)); + BOOST_CHECK(covers(ack, 4)); + BOOST_CHECK(covers(ack, 5)); + BOOST_CHECK(covers(ack, 7)); + BOOST_CHECK(covers(ack, 9)); + + BOOST_CHECK(!covers(ack, 6)); + BOOST_CHECK(!covers(ack, 8)); + BOOST_CHECK(!covers(ack, 10)); +} + +QPID_AUTO_TEST_CASE(testUpdateFromCompletionData) +{ + AccumulatedAck ack(0); + SequenceNumber mark(2); + SequenceNumberSet ranges; + ranges.addRange(SequenceNumber(5), SequenceNumber(8)); + ranges.addRange(SequenceNumber(10), SequenceNumber(15)); + ranges.addRange(SequenceNumber(9), SequenceNumber(9)); + ranges.addRange(SequenceNumber(3), SequenceNumber(4)); + + ack.update(mark, ranges); + + for(int i = 0; i <= 15; i++) { + BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 16)); + BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue()); +} + +QPID_AUTO_TEST_CASE(testCase1) +{ + AccumulatedAck ack(3); + update(ack, 1,2); + for(int i = 1; i <= 3; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 4)); +} + +QPID_AUTO_TEST_CASE(testCase2) +{ + AccumulatedAck ack(3); + update(ack, 3,6); + for(int i = 1; i <= 6; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 7)); +} + +QPID_AUTO_TEST_CASE(testCase3) +{ + AccumulatedAck ack(3); + update(ack, 4,6); + for(int i = 1; i <= 6; i++) { + BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 7)); +} + +QPID_AUTO_TEST_CASE(testCase4) +{ + AccumulatedAck ack(3); + update(ack, 5,6); + for(int i = 1; i <= 6; i++) { + if (i == 4) BOOST_CHECK(!covers(ack, i)); + else BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 7)); +} + +QPID_AUTO_TEST_CASE(testConsolidation1) +{ + AccumulatedAck ack(3); + update(ack, 7,7); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 8,9); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 1,2); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 4,5); + BOOST_CHECK_EQUAL((uint32_t) 5, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 6,6); + BOOST_CHECK_EQUAL((uint32_t) 9, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size()); + + for(int i = 1; i <= 9; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 10)); +} + +QPID_AUTO_TEST_CASE(testConsolidation2) +{ + AccumulatedAck ack(0); + update(ack, 10,12); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 7,9); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + BOOST_CHECK_EQUAL((uint32_t) 7, ack.ranges.front().start.getValue()); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 5,7); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + BOOST_CHECK_EQUAL((uint32_t) 5, ack.ranges.front().start.getValue()); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 3,4); + BOOST_CHECK_EQUAL((uint32_t) 0, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 1, ack.ranges.size()); + BOOST_CHECK_EQUAL((uint32_t) 3, ack.ranges.front().start.getValue()); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 1,2); + BOOST_CHECK_EQUAL((uint32_t) 12, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size()); + + for(int i = 1; i <= 12; i++) BOOST_CHECK(covers(ack, i)); + BOOST_CHECK(!covers(ack, 13)); +} + +QPID_AUTO_TEST_CASE(testConsolidation3) +{ + AccumulatedAck ack(0); + update(ack, 10,12); + update(ack, 6,7); + update(ack, 3,4); + update(ack, 1,15); + BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue()); + BOOST_CHECK_EQUAL((size_t) 0, ack.ranges.size()); +} + +QPID_AUTO_TEST_CASE(testConsolidation4) +{ + AccumulatedAck ack(0); + ack.update(SequenceNumber(0), SequenceNumber(2)); + ack.update(SequenceNumber(5), SequenceNumber(8)); + ack.update(SequenceNumber(10), SequenceNumber(15)); + ack.update(SequenceNumber(9), SequenceNumber(9)); + ack.update(SequenceNumber(3), SequenceNumber(4)); + + for(int i = 0; i <= 15; i++) { + BOOST_CHECK(covers(ack, i)); + } + BOOST_CHECK(!covers(ack, 16)); + BOOST_CHECK_EQUAL((uint32_t) 15, ack.mark.getValue()); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/Array.cpp b/RC9/qpid/cpp/src/tests/Array.cpp new file mode 100644 index 0000000000..c779cbe901 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Array.cpp @@ -0,0 +1,79 @@ +/* + * + * 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 "qpid/framing/Array.h" +#include "qpid/framing/FieldValue.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ArrayTestSuite) + +using namespace qpid::framing; + +void populate(std::vector<std::string>& data, int count = 10) +{ + for (int i = 0; i < count; i++) { + std::stringstream out; + out << "item-" << i; + data.push_back(out.str()); + } +} + +QPID_AUTO_TEST_CASE(testEncodeDecode) +{ + std::vector<std::string> data; + populate(data); + + Array a(data); + + char buff[200]; + Buffer wbuffer(buff, 200); + a.encode(wbuffer); + + Array b; + Buffer rbuffer(buff, 200); + b.decode(rbuffer); + BOOST_CHECK_EQUAL(a, b); + + std::vector<std::string> data2; + b.collect(data2); + //BOOST_CHECK_EQUAL(data, data2); + BOOST_CHECK(data == data2); +} + +QPID_AUTO_TEST_CASE(testArrayAssignment) +{ + std::vector<std::string> data; + populate(data); + Array b; + { + Array a(data); + b = a; + BOOST_CHECK_EQUAL(a, b); + } + std::vector<std::string> data2; + b.collect(data2); + //BOOST_CHECK_EQUAL(data, data2); + BOOST_CHECK(data == data2); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/AsyncCompletion.cpp b/RC9/qpid/cpp/src/tests/AsyncCompletion.cpp new file mode 100644 index 0000000000..e33b2dc35d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/AsyncCompletion.cpp @@ -0,0 +1,100 @@ +/* + * + * 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" + +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. + */ + +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) { + AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore; + SessionFixture fix; + fix.broker->setStore(store); // Broker will delete store. + 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_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/AtomicValue.cpp b/RC9/qpid/cpp/src/tests/AtomicValue.cpp new file mode 100644 index 0000000000..05083ad177 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/AtomicValue.cpp @@ -0,0 +1,49 @@ +/* + * + * 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/sys/AtomicValue.h" + +QPID_AUTO_TEST_SUITE(AtomicValueTestSuite) + +QPID_AUTO_TEST_CASE(test) { + qpid::sys::AtomicValue<int> x(0); + BOOST_CHECK_EQUAL(++x, 1); + BOOST_CHECK_EQUAL(--x,0); + BOOST_CHECK_EQUAL(x+=5,5); + BOOST_CHECK_EQUAL(x-=10,-5); + BOOST_CHECK_EQUAL(x.fetchAndAdd(7), -5); + BOOST_CHECK_EQUAL(x.get(),2); + BOOST_CHECK_EQUAL(x.fetchAndSub(3), 2); + BOOST_CHECK_EQUAL(x.get(),-1); + + BOOST_CHECK_EQUAL(x.valueCompareAndSwap(-1,10), -1); + BOOST_CHECK_EQUAL(x.get(), 10); + BOOST_CHECK_EQUAL(x.valueCompareAndSwap(5, 6), 10); + BOOST_CHECK_EQUAL(x.get(), 10); + + BOOST_CHECK(!x.boolCompareAndSwap(5, 6)); + BOOST_CHECK_EQUAL(x.get(), 10); + BOOST_CHECK(x.boolCompareAndSwap(10, 6)); + BOOST_CHECK_EQUAL(x.get(), 6); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/BasicP2PTest.cpp b/RC9/qpid/cpp/src/tests/BasicP2PTest.cpp new file mode 100644 index 0000000000..f4a4cce7f2 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicP2PTest.cpp @@ -0,0 +1,66 @@ +/* + * + * 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/RC9/qpid/cpp/src/tests/BasicP2PTest.h b/RC9/qpid/cpp/src/tests/BasicP2PTest.h new file mode 100644 index 0000000000..b2611f0301 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicP2PTest.h @@ -0,0 +1,46 @@ +#ifndef _BasicP2PTest_ +#define _BasicP2PTest_ +/* + * + * 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 "qpid/client/MessageListener.h" +#include "SimpleTestCaseBase.h" + + +namespace qpid { + +class BasicP2PTest : public SimpleTestCaseBase +{ + class Receiver; +public: + void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options); +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/BasicPubSubTest.cpp b/RC9/qpid/cpp/src/tests/BasicPubSubTest.cpp new file mode 100644 index 0000000000..1e9ff364f1 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicPubSubTest.cpp @@ -0,0 +1,121 @@ +/* + * + * 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/RC9/qpid/cpp/src/tests/BasicPubSubTest.h b/RC9/qpid/cpp/src/tests/BasicPubSubTest.h new file mode 100644 index 0000000000..242d2847d7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BasicPubSubTest.h @@ -0,0 +1,51 @@ +#ifndef _BasicPubSubTest_ +#define _BasicPubSubTest_ +/* + * + * 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 "qpid/client/MessageListener.h" +#include "SimpleTestCaseBase.h" +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/format.hpp> + + +namespace qpid { + +using namespace qpid::client; + +class BasicPubSubTest : public SimpleTestCaseBase +{ + class Receiver; + class MultiReceiver; +public: + void assign(const std::string& role, framing::FieldTable& params, ConnectionOptions& options); +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/Blob.cpp b/RC9/qpid/cpp/src/tests/Blob.cpp new file mode 100644 index 0000000000..c40e43b96e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Blob.cpp @@ -0,0 +1,128 @@ +/* + * 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/RC9/qpid/cpp/src/tests/BrokerFixture.h b/RC9/qpid/cpp/src/tests/BrokerFixture.h new file mode 100644 index 0000000000..2a4faa2fd4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/BrokerFixture.h @@ -0,0 +1,130 @@ +#ifndef TESTS_BROKERFIXTURE_H +#define TESTS_BROKERFIXTURE_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "SocketProxy.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/log/Logger.h" +#include "qpid/log/Options.h" +#include "qpid/sys/Thread.h" +#include <boost/noncopyable.hpp> + +/** + * A fixture with an in-process broker. + */ +struct BrokerFixture : private boost::noncopyable { + typedef qpid::broker::Broker Broker; + typedef boost::intrusive_ptr<Broker> BrokerPtr; + + BrokerPtr broker; + 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.workerThreads=1; + opts.dataDir=""; + opts.auth=false; + broker = Broker::create(opts); + // 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(qpid::broker::Broker::TCP_TRANSPORT); + brokerThread = qpid::sys::Thread(*broker); + }; + + ~BrokerFixture() { + broker->shutdown(); + brokerThread.join(); + } + + /** Open a connection to the broker. */ + void open(qpid::client::Connection& c) { + c.open("localhost", broker->getPort(qpid::broker::Broker::TCP_TRANSPORT)); + } + + 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); } +}; + +/** A local client connection via a socket proxy. */ +struct ProxyConnection : public qpid::client::Connection { + SocketProxy proxy; + ProxyConnection(int brokerPort) : proxy(brokerPort) { + open("localhost", proxy.getPort()); + } + ~ProxyConnection() { close(); } +}; + +/** Convenience class to create and open a connection and session + * and some related useful objects. + */ +template <class ConnectionType=LocalConnection, class SessionType=qpid::client::Session> +struct ClientT { + ConnectionType connection; + SessionType session; + qpid::client::SubscriptionManager subs; + qpid::client::LocalQueue lq; + ClientT(uint16_t port, const std::string& name=std::string()) + : connection(port), session(connection.newSession(name)), subs(session) {} + + ~ClientT() { connection.close(); } +}; + +typedef ClientT<> Client; + +/** + * A BrokerFixture and ready-connected BrokerFixture::Client all in one. + */ +template <class ConnectionType, class SessionType=qpid::client::Session> +struct SessionFixtureT : BrokerFixture, ClientT<ConnectionType,SessionType> { + + SessionFixtureT(Broker::Options opts=Broker::Options()) : + BrokerFixture(opts), + ClientT<ConnectionType,SessionType>(broker->getPort(qpid::broker::Broker::TCP_TRANSPORT)) + {} + +}; + +typedef SessionFixtureT<LocalConnection> SessionFixture; +typedef SessionFixtureT<ProxyConnection> ProxySessionFixture; + + +#endif /*!TESTS_BROKERFIXTURE_H*/ diff --git a/RC9/qpid/cpp/src/tests/ClientSessionTest.cpp b/RC9/qpid/cpp/src/tests/ClientSessionTest.cpp new file mode 100644 index 0000000000..5d047dcd0e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ClientSessionTest.cpp @@ -0,0 +1,452 @@ +/* + * + * 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/client/SubscriptionManager.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/client/Session.h" +#include "qpid/framing/TransferContent.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> + +QPID_AUTO_TEST_SUITE(ClientSessionTest) + +using namespace qpid::client; +using namespace qpid::framing; +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; + + +struct DummyListener : public sys::Runnable, public MessageListener { + std::vector<Message> messages; + string name; + uint expected; + SubscriptionManager submgr; + + DummyListener(Session& session, const string& n, uint ex) : + name(n), expected(ex), submgr(session) {} + + void run() + { + submgr.subscribe(*this, name); + submgr.run(); + } + + void received(Message& msg) + { + messages.push_back(msg); + if (--expected == 0) { + submgr.stop(); + } + } +}; + +struct SimpleListener : public MessageListener +{ + Monitor lock; + std::vector<Message> messages; + + void received(Message& msg) + { + Monitor::ScopedLock l(lock); + messages.push_back(msg); + lock.notifyAll(); + } + + void waitFor(const uint n) + { + Monitor::ScopedLock l(lock); + while (messages.size() < n) { + lock.wait(); + } + } +}; + +struct ClientSessionFixture : public ProxySessionFixture +{ + 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="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("amq.fanout", result.getAlternateExchange()); +} + +QPID_AUTO_TEST_CASE(testDispatcher) +{ + ClientSessionFixture fix; + fix.session =fix.connection.newSession(); + 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-queue", count); + listener.run(); + 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()); +} + +QPID_AUTO_TEST_CASE(testDispatcherThread) +{ + ClientSessionFixture fix; + fix.session =fix.connection.newSession(); + size_t count = 10; + 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")); + } + t.join(); + 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()); +} + +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(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&) {} +} + +QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testSuspendResume, 1) +{ + 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=TransferContent("my-message", "my-queue")); + FrameSet::shared_ptr msg = fix.session.get(); + BOOST_CHECK_EQUAL(string("my-message"), msg->getContent()); +} + + +QPID_AUTO_TEST_CASE(testSendToSelf) { + ClientSessionFixture fix; + SimpleListener mylistener; + fix.session.queueDeclare(arg::queue="myq", arg::exclusive=true, arg::autoDelete=true); + fix.subs.subscribe(mylistener, "myq"); + sys::Thread runner(fix.subs);//start dispatcher thread + string data("msg"); + Message msg(data, "myq"); + const uint count=10; + for (uint i = 0; i < count; ++i) { + fix.session.messageTransfer(arg::content=msg); + } + mylistener.waitFor(count); + fix.subs.cancel("myq"); + fix.subs.stop(); + runner.join(); + fix.session.close(); + BOOST_CHECK_EQUAL(mylistener.messages.size(), count); + for (uint j = 0; j < count; ++j) { + BOOST_CHECK_EQUAL(mylistener.messages[j].getData(), data); + } +} + +QPID_AUTO_TEST_CASE(testLocalQueue) { + ClientSessionFixture fix; + fix.session.queueDeclare(arg::queue="lq", arg::exclusive=true, arg::autoDelete=true); + LocalQueue lq; + fix.subs.subscribe(lq, "lq", FlowControl(2, FlowControl::UNLIMITED, false)); + fix.session.messageTransfer(arg::content=Message("foo0", "lq")); + fix.session.messageTransfer(arg::content=Message("foo1", "lq")); + fix.session.messageTransfer(arg::content=Message("foo2", "lq")); + BOOST_CHECK_EQUAL("foo0", lq.pop().getData()); + BOOST_CHECK_EQUAL("foo1", lq.pop().getData()); + BOOST_CHECK(lq.empty()); // Credit exhausted. + fix.subs.getSubscription("lq").setFlowControl(FlowControl::unlimited()); + BOOST_CHECK_EQUAL("foo2", lq.pop().getData()); +} + +struct DelayedTransfer : sys::Runnable +{ + ClientSessionFixture& fixture; + + DelayedTransfer(ClientSessionFixture& f) : fixture(f) {} + + void run() + { + sleep(1); + fixture.session.messageTransfer(arg::content=Message("foo2", "getq")); + } +}; + +QPID_AUTO_TEST_CASE(testGet) { + ClientSessionFixture fix; + fix.session.queueDeclare(arg::queue="getq", arg::exclusive=true, arg::autoDelete=true); + fix.session.messageTransfer(arg::content=Message("foo0", "getq")); + fix.session.messageTransfer(arg::content=Message("foo1", "getq")); + Message got; + BOOST_CHECK(fix.subs.get(got, "getq", TIME_SEC)); + BOOST_CHECK_EQUAL("foo0", got.getData()); + BOOST_CHECK(fix.subs.get(got, "getq", TIME_SEC)); + BOOST_CHECK_EQUAL("foo1", got.getData()); + BOOST_CHECK(!fix.subs.get(got, "getq")); + DelayedTransfer sender(fix); + 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()); + t.join(); +} + +QPID_AUTO_TEST_CASE(testOpenFailure) { + BrokerFixture b; + Connection c; + string host("unknowable-host"); + try { + c.open(host); + } catch (const Exception&) { + BOOST_CHECK(!c.isOpen()); + } + b.open(c); + BOOST_CHECK(c.isOpen()); + c.close(); + BOOST_CHECK(!c.isOpen()); +} + +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); + 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); + } + + ::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)); + } + for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::start, _1)); + 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_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/ConnectionOptions.h b/RC9/qpid/cpp/src/tests/ConnectionOptions.h new file mode 100644 index 0000000000..0130842668 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ConnectionOptions.h @@ -0,0 +1,54 @@ +#ifndef QPID_CLIENT_CONNECTIONOPTIONS_H +#define QPID_CLIENT_CONNECTIONOPTIONS_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/client/ConnectionSettings.h" +#include "qpid/Options.h" + +/** + * Options parser for ConnectionOptions. + */ +struct ConnectionOptions : public qpid::Options, + public qpid::client::ConnectionSettings +{ + ConnectionOptions() : qpid::Options("Connection Settings") + { + using namespace qpid; + addOptions() + ("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.") + ("max-frame-size", optValue(maxFrameSize, "N"), "the maximum frame size to request.") + ("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"); + } +}; + +#endif /*!QPID_CLIENT_CONNECTIONOPTIONS_H*/ diff --git a/RC9/qpid/cpp/src/tests/ConsoleTest.cpp b/RC9/qpid/cpp/src/tests/ConsoleTest.cpp new file mode 100644 index 0000000000..1d55b13f3c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ConsoleTest.cpp @@ -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. + * + */ + +#include "qpid/console/Package.h" +#include "qpid/console/ClassKey.h" +#include "unit_test.h" + +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() + + diff --git a/RC9/qpid/cpp/src/tests/DeliveryRecordTest.cpp b/RC9/qpid/cpp/src/tests/DeliveryRecordTest.cpp new file mode 100644 index 0000000000..47c7157749 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/DeliveryRecordTest.cpp @@ -0,0 +1,62 @@ + +/* + * + * 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/broker/DeliveryRecord.h" +#include "unit_test.h" +#include <iostream> +#include <memory> +#include <boost/format.hpp> + +using namespace qpid::broker; +using namespace qpid::sys; +using namespace qpid::framing; +using boost::dynamic_pointer_cast; +using std::list; + +QPID_AUTO_TEST_SUITE(DeliveryRecordTestSuite) + +QPID_AUTO_TEST_CASE(testSort) +{ + list<SequenceNumber> ids; + ids.push_back(SequenceNumber(6)); + ids.push_back(SequenceNumber(2)); + ids.push_back(SequenceNumber(4)); + ids.push_back(SequenceNumber(5)); + ids.push_back(SequenceNumber(1)); + ids.push_back(SequenceNumber(3)); + + list<DeliveryRecord> records; + for (list<SequenceNumber>::iterator i = ids.begin(); i != ids.end(); i++) { + 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)); + } +} + + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/DispatcherTest.cpp b/RC9/qpid/cpp/src/tests/DispatcherTest.cpp new file mode 100644 index 0000000000..7631956acc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/DispatcherTest.cpp @@ -0,0 +1,128 @@ +/* + * + * 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/sys/Poller.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Thread.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include <iostream> +#include <boost/bind.hpp> + +using namespace std; +using namespace qpid::sys; + +int writeALot(int fd, const string& s) { + int bytesWritten = 0; + do { + errno = 0; + int lastWrite = ::write(fd, s.c_str(), s.size()); + if ( lastWrite >= 0) { + bytesWritten += lastWrite; + } + } while (errno != EAGAIN); + return bytesWritten; +} + +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; +} + +int64_t writtenBytes = 0; +int64_t readBytes = 0; + +void writer(DispatchHandle& h, int fd, const string& s) { + writtenBytes += writeALot(fd, s); + h.rewatch(); +} + +void reader(DispatchHandle& h, int fd) { + readBytes += readALot(fd); + h.rewatch(); +} + +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); + Thread dt(d); + Thread dt1(d1); + //Thread dt2(d2); + //Thread dt3(d3); + + // Setup sender and receiver + int sv[2]; + int rc = ::socketpair(AF_LOCAL, 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)); + + rh.watch(poller); + wh.watch(poller); + + // wait 2 minutes then shutdown + sleep(60); + + poller->shutdown(); + dt.join(); + dt1.join(); + //dt2.join(); + //dt3.join(); + + cout << "Wrote: " << writtenBytes << "\n"; + cout << "Read: " << readBytes << "\n"; + + return 0; +} diff --git a/RC9/qpid/cpp/src/tests/DtxWorkRecordTest.cpp b/RC9/qpid/cpp/src/tests/DtxWorkRecordTest.cpp new file mode 100644 index 0000000000..c7c1b460ff --- /dev/null +++ b/RC9/qpid/cpp/src/tests/DtxWorkRecordTest.cpp @@ -0,0 +1,189 @@ +/* + * + * 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/broker/DtxWorkRecord.h" +#include "unit_test.h" +#include <iostream> +#include <vector> +#include "TxMocks.h" + +using namespace qpid::broker; +using boost::static_pointer_cast; + +QPID_AUTO_TEST_SUITE(DtxWorkRecordTestSuite) + +QPID_AUTO_TEST_CASE(testOnePhaseCommit){ + MockTransactionalStore store; + store.expectBegin().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectCommit(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + 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); + + work.commit(true); + + store.check(); + BOOST_CHECK(store.isCommitted()); + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnOnePhaseCommit){ + MockTransactionalStore store; + store.expectBegin().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + 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); + work.add(bufferC); + + work.commit(true); + + BOOST_CHECK(store.isAborted()); + store.check(); + + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testTwoPhaseCommit){ + MockTransactionalStore store; + store.expectBegin2PC().expectPrepare().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectCommit(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + 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); + + BOOST_CHECK(work.prepare()); + BOOST_CHECK(store.isPrepared()); + work.commit(false); + store.check(); + BOOST_CHECK(store.isCommitted()); + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnTwoPhaseCommit){ + MockTransactionalStore store; + store.expectBegin2PC().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + 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); + work.add(bufferC); + + BOOST_CHECK(!work.prepare()); + BOOST_CHECK(store.isAborted()); + store.check(); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testRollback){ + MockTransactionalStore store; + store.expectBegin2PC().expectPrepare().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + 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); + + BOOST_CHECK(work.prepare()); + BOOST_CHECK(store.isPrepared()); + work.rollback(); + store.check(); + BOOST_CHECK(store.isAborted()); + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/ExchangeTest.cpp b/RC9/qpid/cpp/src/tests/ExchangeTest.cpp new file mode 100644 index 0000000000..0946d3115d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ExchangeTest.cpp @@ -0,0 +1,284 @@ +/* + * + * 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/Exception.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/FanOutExchange.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid/framing/reply_exceptions.h" +#include "unit_test.h" +#include <iostream> +#include "MessageUtils.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace qpid; + +QPID_AUTO_TEST_SUITE(ExchangeTestSuite) + +QPID_AUTO_TEST_CASE(testMe) +{ + Queue::shared_ptr queue(new Queue("queue", true)); + Queue::shared_ptr queue2(new Queue("queue2", true)); + + TopicExchange topic("topic"); + topic.bind(queue, "abc", 0); + topic.bind(queue2, "abc", 0); + + DirectExchange direct("direct"); + direct.bind(queue, "abc", 0); + direct.bind(queue2, "abc", 0); + + queue.reset(); + queue2.reset(); + + intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "key", "id")); + DeliverableMessage msg(msgPtr); + topic.route(msg, "abc", 0); + direct.route(msg, "abc", 0); + +} + +QPID_AUTO_TEST_CASE(testIsBound) +{ + Queue::shared_ptr a(new Queue("a", true)); + 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"); + 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)); + BOOST_CHECK(fanout.isBound(c, 0, 0)); + BOOST_CHECK(!fanout.isBound(d, 0, 0)); + + DirectExchange direct("direct"); + 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)); + BOOST_CHECK(direct.isBound(a, &k3, 0)); + BOOST_CHECK(!direct.isBound(a, &k2, 0)); + BOOST_CHECK(direct.isBound(b, 0, 0)); + BOOST_CHECK(direct.isBound(b, &k2, 0)); + BOOST_CHECK(direct.isBound(c, &k1, 0)); + BOOST_CHECK(!direct.isBound(d, 0, 0)); + BOOST_CHECK(!direct.isBound(d, &k1, 0)); + BOOST_CHECK(!direct.isBound(d, &k2, 0)); + BOOST_CHECK(!direct.isBound(d, &k3, 0)); + + TopicExchange topic("topic"); + 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)); + BOOST_CHECK(topic.isBound(a, &k3, 0)); + BOOST_CHECK(!topic.isBound(a, &k2, 0)); + BOOST_CHECK(topic.isBound(b, 0, 0)); + BOOST_CHECK(topic.isBound(b, &k2, 0)); + BOOST_CHECK(topic.isBound(c, &k1, 0)); + BOOST_CHECK(!topic.isBound(d, 0, 0)); + BOOST_CHECK(!topic.isBound(d, &k1, 0)); + BOOST_CHECK(!topic.isBound(d, &k2, 0)); + BOOST_CHECK(!topic.isBound(d, &k3, 0)); + + HeadersExchange headers("headers"); + FieldTable args1; + args1.setString("x-match", "all"); + args1.setString("a", "A"); + args1.setInt("b", 1); + FieldTable args2; + args2.setString("x-match", "any"); + args2.setString("a", "A"); + args2.setInt("b", 1); + FieldTable args3; + args3.setString("x-match", "any"); + args3.setString("c", "C"); + args3.setInt("b", 6); + + headers.bind(a, "", &args1); + 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)); + BOOST_CHECK(!headers.isBound(a, 0, &args2)); + BOOST_CHECK(headers.isBound(b, 0, 0)); + BOOST_CHECK(headers.isBound(b, 0, &args2)); + BOOST_CHECK(headers.isBound(c, 0, &args1)); + BOOST_CHECK(!headers.isBound(d, 0, 0)); + BOOST_CHECK(!headers.isBound(d, 0, &args1)); + BOOST_CHECK(!headers.isBound(d, 0, &args2)); + BOOST_CHECK(!headers.isBound(d, 0, &args3)); +} + +QPID_AUTO_TEST_CASE(testDeleteGetAndRedeclare) +{ + ExchangeRegistry exchanges; + exchanges.declare("my-exchange", "direct", false, FieldTable()); + exchanges.destroy("my-exchange"); + try { + 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()); +} + +intrusive_ptr<Message> cmessage(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>()); + 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() diff --git a/RC9/qpid/cpp/src/tests/FieldTable.cpp b/RC9/qpid/cpp/src/tests/FieldTable.cpp new file mode 100644 index 0000000000..6b364723cf --- /dev/null +++ b/RC9/qpid/cpp/src/tests/FieldTable.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/framing/Array.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include <alloca.h> + +#include "unit_test.h" + +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(FieldTableTestSuite) + +QPID_AUTO_TEST_CASE(testMe) +{ + FieldTable ft; + ft.setString("A", "BCDE"); + BOOST_CHECK(string("BCDE") == ft.getAsString("A")); + + char buff[100]; + Buffer wbuffer(buff, 100); + wbuffer.put(ft); + + Buffer rbuffer(buff, 100); + FieldTable ft2; + rbuffer.get(ft2); + BOOST_CHECK(string("BCDE") == ft2.getAsString("A")); + +} + +QPID_AUTO_TEST_CASE(testAssignment) +{ + FieldTable a; + FieldTable b; + + a.setString("A", "BBBB"); + a.setInt("B", 1234); + b = a; + a.setString("A", "CCCC"); + + 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")); + + FieldTable d; + { + FieldTable c; + c = a; + + char* buff = static_cast<char*>(::alloca(c.encodedSize())); + Buffer wbuffer(buff, c.encodedSize()); + wbuffer.put(c); + + Buffer rbuffer(buff, c.encodedSize()); + rbuffer.get(d); + BOOST_CHECK_EQUAL(c, d); + BOOST_CHECK(string("CCCC") == c.getAsString("A")); + BOOST_CHECK(IntegerValue(1234) == *c.get("B")); + } + BOOST_CHECK(string("CCCC") == d.getAsString("A")); + BOOST_CHECK(IntegerValue(1234) == *d.get("B")); +} + + +QPID_AUTO_TEST_CASE(testNestedValues) +{ + char buff[100]; + { + FieldTable a; + FieldTable b; + std::vector<std::string> items; + items.push_back("one"); + items.push_back("two"); + Array c(items); + + a.setString("id", "A"); + b.setString("id", "B"); + a.setTable("B", b); + a.setArray("C", c); + + + 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]); + } +} + +QPID_AUTO_TEST_CASE(testFloatAndDouble) +{ + char buff[100]; + float f = 5.672; + 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() diff --git a/RC9/qpid/cpp/src/tests/FieldValue.cpp b/RC9/qpid/cpp/src/tests/FieldValue.cpp new file mode 100644 index 0000000000..448f068107 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/FieldValue.cpp @@ -0,0 +1,90 @@ +/* + * 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/FieldValue.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(FieldValueTestSuite) + +using namespace qpid::framing; + +Str16Value s("abc"); +IntegerValue i(42); +//DecimalValue d(1234,2); +//FieldTableValue ft; +//EmptyValue e; + +QPID_AUTO_TEST_CASE(testStr16ValueEquals) +{ + + 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); + BOOST_CHECK(s.get<std::string>() == "abc"); + BOOST_CHECK_THROW(s.get<int>(), InvalidConversionException); +// BOOST_CHECK(s != ft); + +} + +QPID_AUTO_TEST_CASE(testIntegerValueEquals) +{ + BOOST_CHECK(IntegerValue(42) == i); + BOOST_CHECK(IntegerValue(5) != i); + BOOST_CHECK(i != s); + BOOST_CHECK(i.convertsTo<std::string>() == false); + BOOST_CHECK(i.convertsTo<int>() == true); + BOOST_CHECK_THROW(i.get<std::string>(), InvalidConversionException); + BOOST_CHECK(i.get<int>() == 42); +// BOOST_CHECK(i != ft); +} + +#if 0 +QPID_AUTO_TEST_CASE(testDecimalValueEquals) +{ + BOOST_CHECK(DecimalValue(1234, 2) == d); + BOOST_CHECK(DecimalValue(12345, 2) != d); + BOOST_CHECK(DecimalValue(1234, 3) != d); + BOOST_CHECK(d != s); +} + +QPID_AUTO_TEST_CASE(testFieldTableValueEquals) +{ + ft.getValue().setString("foo", "FOO"); + ft.getValue().setInt("magic", 7); + + 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"); + BOOST_CHECK(ft != f2); + f2.getValue().setInt("magic", 7); + BOOST_CHECK_EQUAL(ft,f2); + BOOST_CHECK(ft == f2); + f2.getValue().setString("foo", "BAR"); + BOOST_CHECK(ft != f2); + BOOST_CHECK(ft != i); +} +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/ForkedBroker.h b/RC9/qpid/cpp/src/tests/ForkedBroker.h new file mode 100644 index 0000000000..bf9e9265c4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ForkedBroker.h @@ -0,0 +1,122 @@ +#ifndef TESTS_FORKEDBROKER_H + + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" +#include "qpid/broker/Broker.h" +#include <boost/lexical_cast.hpp> +#include <string> +#include <stdio.h> +#include <sys/wait.h> + +/** + * 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. + * + * This useful for tests that need to start multiple brokers where + * 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); + } + + ~ForkedBroker() { + try { kill(); } catch(const std::exception& e) { + QPID_LOG(error, QPID_MSG("Killing forked broker: " << e.what())); + } + } + + void kill(int sig=SIGINT) { + 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) + throw ErrnoException("wait for forked process failed"); + if (WEXITSTATUS(status) != 0) + throw qpid::Exception(QPID_MSG("Forked broker exited with: " << WEXITSTATUS(status))); + } + + uint16_t getPort() { return port; } + pid_t getPID() { return pid; } + + private: + + template <class F> struct OnExit { + F fn; + OnExit(F f) : fn(f) {} + ~OnExit() { fn(); } + }; + + void init(const std::vector<const char*>& args) { + 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 = "../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("--log-enable=error+"); // Keep quiet except for errors. + args2.push_back(0); + execv(prog, const_cast<char* const*>(&args2[0])); + throw ErrnoException("execv failed"); + } + } + + pid_t pid; + int port; +}; + +#endif /*!TESTS_FORKEDBROKER_H*/ diff --git a/RC9/qpid/cpp/src/tests/Frame.cpp b/RC9/qpid/cpp/src/tests/Frame.cpp new file mode 100644 index 0000000000..11905911fa --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Frame.cpp @@ -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. + * + */ +#include "qpid/framing/Frame.h" + +#include <boost/lexical_cast.hpp> +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(FrameTestSuite) + +using namespace std; +using namespace qpid::framing; +using namespace boost; + +QPID_AUTO_TEST_CASE(testContentBody) { + Frame f(42, AMQContentBody("foobar")); + AMQBody* body=f.getBody(); + BOOST_CHECK(dynamic_cast<AMQContentBody*>(body)); + Buffer b(f.encodedSize(); + f.encode(b); + b.flip(); + Frame g; + g.decode(b); + AMQContentBody* content=dynamic_cast<AMQContentBody*>(g.getBody()); + BOOST_REQUIRE(content); + BOOST_CHECK_EQUAL(content->getData(), "foobar"); +} + +QPID_AUTO_TEST_CASE(testMethodBody) { + FieldTable args; + args.setString("foo", "bar"); + Frame f( + 42, QueueDeclareBody(ProtocolVersion(), 1, "q", "altex", + true, false, true, false, true, args)); + BOOST_CHECK_EQUAL(f.getChannel(), 42); + Buffer b(f.encodedSize(); + f.encode(b); + b.flip(); + Frame g; + g.decode(b); + BOOST_CHECK_EQUAL(f.getChannel(), g.getChannel()); + QueueDeclareBody* declare=dynamic_cast<QueueDeclareBody*>(g.getBody()); + BOOST_REQUIRE(declare); + BOOST_CHECK_EQUAL(declare->getAlternateExchange(), "altex"); + BOOST_CHECK_EQUAL(lexical_cast<string>(*f.getBody()), lexical_cast<string>(*g.getBody())); +} + +QPID_AUTO_TEST_CASE(testLoop) { + // Run in a loop so heap profiler can spot any allocations. + Buffer b(1024); + for (int i = 0; i < 100; ++i) { + Frame ctor(2, AccessRequestOkBody(ProtocolVersion(), 42)); + Frame assign(3); + assign.body = AccessRequestOkBody(ProtocolVersion(), 42); + assign.encode(b); + b.flip(); + Frame g; + g.decode(b); + BOOST_REQUIRE(dynamic_cast<AccessRequestOkBody*>(g.getBody())->getTicket() == 42); + } +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/FramingTest.cpp b/RC9/qpid/cpp/src/tests/FramingTest.cpp new file mode 100644 index 0000000000..f82507c0a7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/FramingTest.cpp @@ -0,0 +1,151 @@ +/* + * + * 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/Connector.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/reply_exceptions.h" +#include "unit_test.h" + +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> + +#include <memory> +#include <sstream> +#include <typeinfo> + +using namespace qpid; +using namespace qpid::framing; +using namespace std; + +template <class T> +std::string tostring(const T& x) +{ + std::ostringstream out; + out << x; + return out.str(); +} + +QPID_AUTO_TEST_SUITE(FramingTestSuite) + +QPID_AUTO_TEST_CASE(testMessageTransferBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + MessageTransferBody in(version, "my-exchange", 1, 1); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + MessageTransferBody out(version); + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testConnectionSecureBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + std::string s = "security credential"; + ConnectionSecureBody in(version, s); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + ConnectionSecureBody out(version); + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testConnectionRedirectBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + std::string a = "hostA"; + std::string b = "hostB"; + 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); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testQueueDeclareBody) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + QueueDeclareBody in(version, "name", "dlq", true, false, true, false, FieldTable()); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + QueueDeclareBody out(version); + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testConnectionRedirectBodyFrame) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + std::string a = "hostA"; + std::string b = "hostB"; + 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)); + in.setChannel(999); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + AMQFrame out; + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_CASE(testMessageCancelBodyFrame) +{ + char buffer[1024]; + ProtocolVersion version(highestProtocolVersion); + Buffer wbuff(buffer, sizeof(buffer)); + AMQFrame in(in_place<MessageCancelBody>(version, "tag")); + in.setChannel(999); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + AMQFrame out; + out.decode(rbuff); + BOOST_CHECK_EQUAL(tostring(in), tostring(out)); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/HeaderTest.cpp b/RC9/qpid/cpp/src/tests/HeaderTest.cpp new file mode 100644 index 0000000000..33bf705e65 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/HeaderTest.cpp @@ -0,0 +1,110 @@ +/* + * + * 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/amqp_framing.h" +#include "qpid/framing/FieldValue.h" +#include "unit_test.h" + +using namespace qpid::framing; +using namespace std; + +QPID_AUTO_TEST_SUITE(HeaderTestSuite) + +QPID_AUTO_TEST_CASE(testGenericProperties) +{ + AMQHeaderBody body; + body.get<MessageProperties>(true)->getApplicationHeaders().setString( + "A", "BCDE"); + char buff[100]; + Buffer wbuffer(buff, 100); + body.encode(wbuffer); + + Buffer rbuffer(buff, 100); + AMQHeaderBody body2; + body2.decode(rbuffer, body.encodedSize()); + MessageProperties* props = + body2.get<MessageProperties>(true); + BOOST_CHECK_EQUAL( + string("BCDE"), + props->getApplicationHeaders().get("A")->get<string>()); +} + +QPID_AUTO_TEST_CASE(testMessageProperties) +{ + AMQFrame out(in_place<AMQHeaderBody>()); + MessageProperties* props1 = + out.castBody<AMQHeaderBody>()->get<MessageProperties>(true); + + props1->setContentLength(42); + props1->setMessageId(Uuid(true)); + props1->setCorrelationId("correlationId"); + props1->setReplyTo(ReplyTo("ex","key")); + props1->setContentType("contentType"); + props1->setContentEncoding("contentEncoding"); + props1->setUserId("userId"); + props1->setAppId("appId"); + + char buff[10000]; + Buffer wbuffer(buff, 10000); + out.encode(wbuffer); + + Buffer rbuffer(buff, 10000); + AMQFrame in; + in.decode(rbuffer); + MessageProperties* props2 = + in.castBody<AMQHeaderBody>()->get<MessageProperties>(true); + + BOOST_CHECK_EQUAL(props1->getContentLength(), props2->getContentLength()); + BOOST_CHECK_EQUAL(props1->getMessageId(), props2->getMessageId()); + BOOST_CHECK_EQUAL(props1->getCorrelationId(), props2->getCorrelationId()); + BOOST_CHECK_EQUAL(props1->getContentType(), props2->getContentType()); + BOOST_CHECK_EQUAL(props1->getContentEncoding(), props2->getContentEncoding()); + BOOST_CHECK_EQUAL(props1->getUserId(), props2->getUserId()); + BOOST_CHECK_EQUAL(props1->getAppId(), props2->getAppId()); + +} + +QPID_AUTO_TEST_CASE(testDeliveryProperies) +{ + AMQFrame out(in_place<AMQHeaderBody>()); + DeliveryProperties* props1 = + out.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); + + props1->setDiscardUnroutable(true); + props1->setExchange("foo"); + + char buff[10000]; + Buffer wbuffer(buff, 10000); + out.encode(wbuffer); + + Buffer rbuffer(buff, 10000); + AMQFrame in; + in.decode(rbuffer); + DeliveryProperties* props2 = + in.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); + + BOOST_CHECK(props2->getDiscardUnroutable()); + BOOST_CHECK_EQUAL(string("foo"), props2->getExchange()); + BOOST_CHECK(!props2->hasTimestamp()); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/HeadersExchangeTest.cpp b/RC9/qpid/cpp/src/tests/HeadersExchangeTest.cpp new file mode 100644 index 0000000000..46933f955a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/HeadersExchangeTest.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/Exception.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include "unit_test.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(HeadersExchangeTestSuite) + +QPID_AUTO_TEST_CASE(testMatchAll) +{ + FieldTable b, m, n; + b.setString("x-match", "all"); + b.setString("foo", "FOO"); + b.setInt("n", 42); + m.setString("foo", "FOO"); + m.setInt("n", 42); + BOOST_CHECK(HeadersExchange::match(b, m)); + + // 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)); + + // Fail mismatch, missing value + n.setInt("n", 42); + n.setString("extra", "x"); + BOOST_CHECK(!HeadersExchange::match(b, n)); +} + +QPID_AUTO_TEST_CASE(testMatchAny) +{ + FieldTable b, m, n; + b.setString("x-match", "any"); + b.setString("foo", "FOO"); + b.setInt("n", 42); + m.setString("foo", "FOO"); + BOOST_CHECK(!HeadersExchange::match(b, n)); + BOOST_CHECK(HeadersExchange::match(b, m)); + m.setInt("n", 42); + BOOST_CHECK(HeadersExchange::match(b, m)); +} + +QPID_AUTO_TEST_CASE(testMatchEmptyValue) +{ + FieldTable b, m; + b.setString("x-match", "all"); + b.set("foo", FieldTable::ValuePtr()); + b.set("n", FieldTable::ValuePtr()); + BOOST_CHECK(!HeadersExchange::match(b, m)); + m.setString("foo", "blah"); + m.setInt("n", 123); +} + +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) +{ + FieldTable b, m; + b.setString("foo", "FOO"); + m.setString("foo", "FOO"); + BOOST_CHECK(!HeadersExchange::match(b, m)); +} + +QPID_AUTO_TEST_CASE(testBindNoXMatch) +{ + HeadersExchange exchange("test"); + Queue::shared_ptr queue; + std::string key; + FieldTable args; + try { + //just checking this doesn't cause assertion etc + exchange.bind(queue, key, &args); + } catch(qpid::Exception&) { + //expected + } +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/IncompleteMessageList.cpp b/RC9/qpid/cpp/src/tests/IncompleteMessageList.cpp new file mode 100644 index 0000000000..925cdbf43e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/IncompleteMessageList.cpp @@ -0,0 +1,128 @@ +/* + * + * 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 "qpid/broker/Message.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/IncompleteMessageList.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(IncompleteMessageListTestSuite) + +using namespace qpid::broker; +using namespace qpid::framing; + +struct Checker +{ + std::list<SequenceNumber> ids; + + Checker() { } + + Checker(uint start, uint end) { + for (uint i = start; i <= end; i++) { + ids.push_back(i); + } + } + + Checker& expect(const SequenceNumber& id) { + ids.push_back(id); + return *this; + } + + 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) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + } + //process and ensure they are all passed to completion listener + list.process(Checker(1, 5), false); + //process again and ensure none are resent to listener + list.process(Checker(), false); +} + +QPID_AUTO_TEST_CASE(testProcessWithIncomplete) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + boost::intrusive_ptr<Message> middle; + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + if (i == 2) { + //mark a message in the middle as incomplete + msg->enqueueAsync(); + middle = msg; + } + } + //process and ensure only message upto incomplete message are passed to listener + 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); +} + + +struct MockStore : public NullMessageStore +{ + Queue::shared_ptr queue; + boost::intrusive_ptr<Message> msg; + + void flush(const qpid::broker::PersistableQueue& q) { + BOOST_CHECK_EQUAL(queue.get(), &q); + msg->enqueueComplete(); + } +}; + +QPID_AUTO_TEST_CASE(testSyncProcessWithIncomplete) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + MockStore store; + store.queue = Queue::shared_ptr(new Queue("mock-queue")); + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + if (i == 2) { + //mark a message in the middle as incomplete + msg->enqueueAsync(store.queue, &store); + store.msg = msg; + } + } + //process with sync bit specified and ensure that all messages are passed to listener + list.process(Checker(1, 5), true); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/InlineAllocator.cpp b/RC9/qpid/cpp/src/tests/InlineAllocator.cpp new file mode 100644 index 0000000000..fe6eaefaf4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/InlineAllocator.cpp @@ -0,0 +1,63 @@ +/* + * + * 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/InlineAllocator.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(InlineAllocatorTestSuite) + +using namespace qpid; +using namespace std; + +QPID_AUTO_TEST_CASE(testAllocate) { + InlineAllocator<std::allocator<char>, 2> alloc; + + char* p = alloc.allocate(1); + BOOST_CHECK(p == (char*)&alloc); + alloc.deallocate(p,1); + + p = alloc.allocate(2); + BOOST_CHECK(p == (char*)&alloc); + alloc.deallocate(p,2); + + p = alloc.allocate(3); + BOOST_CHECK(p != (char*)&alloc); + alloc.deallocate(p,3); +} + +QPID_AUTO_TEST_CASE(testAllocateFull) { + InlineAllocator<std::allocator<char>, 1> alloc; + + 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() diff --git a/RC9/qpid/cpp/src/tests/InlineVector.cpp b/RC9/qpid/cpp/src/tests/InlineVector.cpp new file mode 100644 index 0000000000..bcd36e47b4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/InlineVector.cpp @@ -0,0 +1,119 @@ +/* + * + * 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/InlineVector.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(InlineVectorTestSuite) + +using namespace qpid; +using namespace std; + +typedef InlineVector<int, 3> Vec; + +bool isInline(const Vec& v) { + return (const char*)&v <= (const char*)(&v[0]) && + (const char*)(&v[0]) < (const char*)&v+sizeof(v); +} + +QPID_AUTO_TEST_CASE(testCtor) { + { + Vec v; + BOOST_CHECK(isInline(v)); + BOOST_CHECK(v.empty()); + } + { + Vec v(3, 42); + BOOST_CHECK(isInline(v)); + BOOST_CHECK_EQUAL(3u, v.size()); + BOOST_CHECK_EQUAL(v[0], 42); + BOOST_CHECK_EQUAL(v[2], 42); + + Vec u(v); + BOOST_CHECK(isInline(u)); + BOOST_CHECK_EQUAL(3u, u.size()); + BOOST_CHECK_EQUAL(u[0], 42); + BOOST_CHECK_EQUAL(u[2], 42); + } + + { + Vec v(4, 42); + + BOOST_CHECK_EQUAL(v.size(), 4u); + BOOST_CHECK(!isInline(v)); + Vec u(v); + BOOST_CHECK_EQUAL(u.size(), 4u); + BOOST_CHECK(!isInline(u)); + } +} + +QPID_AUTO_TEST_CASE(testInsert) { + { + Vec v; + v.push_back(1); + BOOST_CHECK_EQUAL(v.size(), 1u); + BOOST_CHECK_EQUAL(v.back(), 1); + BOOST_CHECK(isInline(v)); + + v.insert(v.begin(), 2); + BOOST_CHECK_EQUAL(v.size(), 2u); + BOOST_CHECK_EQUAL(v.back(), 1); + BOOST_CHECK(isInline(v)); + + v.push_back(3); + BOOST_CHECK(isInline(v)); + + v.push_back(4); + + BOOST_CHECK(!isInline(v)); + } + { + Vec v(3,42); + v.insert(v.begin(), 9); + BOOST_CHECK_EQUAL(v.size(), 4u); + BOOST_CHECK(!isInline(v)); + } + { + Vec v(3,42); + v.insert(v.begin()+1, 9); + BOOST_CHECK(!isInline(v)); + BOOST_CHECK_EQUAL(v.size(), 4u); + } +} + +QPID_AUTO_TEST_CASE(testAssign) { + Vec v(3,42); + Vec u; + u = v; + BOOST_CHECK(isInline(u)); + u.push_back(4); + BOOST_CHECK(!isInline(u)); + v = u; + BOOST_CHECK(!isInline(v)); +} + +QPID_AUTO_TEST_CASE(testResize) { + Vec v; + v.resize(5); + BOOST_CHECK(!isInline(v)); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/Makefile.am b/RC9/qpid/cpp/src/tests/Makefile.am new file mode 100644 index 0000000000..3a608b2bae --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Makefile.am @@ -0,0 +1,245 @@ +# +# 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 + +abs_builddir=@abs_builddir@ +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 + +# +# Initialize variables that are incremented with += +# +check_PROGRAMS= +check_LTLIBRARIES= +TESTS= +EXTRA_DIST= +CLEANFILES= + +# +# 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 do +# rm -f unit_test; make unit_test unit_test_OBJECTS="unit_test.o SelectedTest.o" +# + +TESTS+=unit_test +check_PROGRAMS+=unit_test +unit_test_LDADD=-lboost_unit_test_framework -lboost_regex \ + $(lib_client) $(lib_broker) $(lib_console) + +unit_test_SOURCES= unit_test.cpp unit_test.h \ + BrokerFixture.h SocketProxy.h \ + exception_test.cpp \ + RefCounted.cpp \ + SessionState.cpp Blob.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 \ + RangeSet.cpp \ + AtomicValue.cpp \ + QueueTest.cpp \ + AccumulatedAckTest.cpp \ + DtxWorkRecordTest.cpp \ + DeliveryRecordTest.cpp \ + ExchangeTest.cpp \ + HeadersExchangeTest.cpp \ + MessageTest.cpp \ + QueueRegistryTest.cpp \ + QueuePolicyTest.cpp \ + FramingTest.cpp \ + HeaderTest.cpp \ + SequenceNumberTest.cpp \ + TimerTest.cpp \ + TopicExchangeTest.cpp \ + TxBufferTest.cpp \ + TxPublishTest.cpp \ + MessageBuilderTest.cpp \ + ConnectionOptions.h \ + ForkedBroker.h \ + ManagementTest.cpp \ + MessageReplayTracker.cpp \ + ConsoleTest.cpp + +if HAVE_XML +unit_test_SOURCES+= XmlClientSessionTest.cpp +endif + + +# 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 + + +check_LTLIBRARIES += libshlibtest.la +libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir) +libshlibtest_la_SOURCES = shlibtest.cpp + +include cluster.mk +if SSL +include ssl.mk +endif + +# +# Other test programs +# +check_PROGRAMS+=perftest +perftest_SOURCES=perftest.cpp test_tools.h TestOptions.h ConnectionOptions.h +perftest_LDADD=$(lib_client) + +check_PROGRAMS+=txtest +txtest_SOURCES=txtest.cpp TestOptions.h ConnectionOptions.h +txtest_LDADD=$(lib_client) + +check_PROGRAMS+=latencytest +latencytest_SOURCES=latencytest.cpp TestOptions.h ConnectionOptions.h +latencytest_LDADD=$(lib_client) + +check_PROGRAMS+=echotest +echotest_SOURCES=echotest.cpp TestOptions.h ConnectionOptions.h +echotest_LDADD=$(lib_client) + +check_PROGRAMS+=client_test +client_test_SOURCES=client_test.cpp TestOptions.h ConnectionOptions.h +client_test_LDADD=$(lib_client) + +check_PROGRAMS+=topic_listener +topic_listener_SOURCES=topic_listener.cpp TestOptions.h ConnectionOptions.h +topic_listener_LDADD=$(lib_client) + +check_PROGRAMS+=topic_publisher +topic_publisher_SOURCES=topic_publisher.cpp TestOptions.h ConnectionOptions.h +topic_publisher_LDADD=$(lib_client) + +check_PROGRAMS+=publish +publish_SOURCES=publish.cpp TestOptions.h ConnectionOptions.h +publish_LDADD=$(lib_client) + +check_PROGRAMS+=consume +consume_SOURCES=consume.cpp TestOptions.h ConnectionOptions.h +consume_LDADD=$(lib_client) + +check_PROGRAMS+=header_test +header_test_SOURCES=header_test.cpp TestOptions.h ConnectionOptions.h +header_test_LDADD=$(lib_client) + +check_PROGRAMS+=failover_soak +failover_soak_SOURCES=failover_soak.cpp ForkedBroker.h +failover_soak_LDADD=$(lib_client) + +check_PROGRAMS+=declare_queues +declare_queues_SOURCES=declare_queues.cpp +declare_queues_LDADD=$(lib_client) + +check_PROGRAMS+=replaying_sender +replaying_sender_SOURCES=replaying_sender.cpp +replaying_sender_LDADD=$(lib_client) + +check_PROGRAMS+=resuming_receiver +resuming_receiver_SOURCES=resuming_receiver.cpp +resuming_receiver_LDADD=$(lib_client) + +check_PROGRAMS+=txshift +txshift_SOURCES=txshift.cpp TestOptions.h ConnectionOptions.h +txshift_LDADD=$(lib_client) + +check_PROGRAMS+=txjob +txjob_SOURCES=txjob.cpp TestOptions.h ConnectionOptions.h +txjob_LDADD=$(lib_client) + +check_PROGRAMS+=receiver +receiver_SOURCES=receiver.cpp TestOptions.h ConnectionOptions.h +receiver_LDADD=$(lib_client) + +check_PROGRAMS+=sender +sender_SOURCES=sender.cpp TestOptions.h ConnectionOptions.h +sender_LDADD=$(lib_client) + + +TESTS_ENVIRONMENT = VALGRIND=$(VALGRIND) srcdir=$(srcdir) 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 + +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 + +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) + +# 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/RC9/qpid/cpp/src/tests/ManagementTest.cpp b/RC9/qpid/cpp/src/tests/ManagementTest.cpp new file mode 100644 index 0000000000..a3d29ec22c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ManagementTest.cpp @@ -0,0 +1,85 @@ +/* + * + * 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 "unit_test.h" + +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_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/MessageBuilderTest.cpp b/RC9/qpid/cpp/src/tests/MessageBuilderTest.cpp new file mode 100644 index 0000000000..313a91c053 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageBuilderTest.cpp @@ -0,0 +1,224 @@ +/* + * + * 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/broker/Message.h" +#include "qpid/broker/MessageBuilder.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/TypeFilter.h" +#include "unit_test.h" +#include <list> + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +class MockMessageStore : public NullMessageStore +{ + enum Op {STAGE=1, APPEND=2}; + + uint64_t id; + intrusive_ptr<PersistableMessage> expectedMsg; + string expectedData; + std::list<Op> ops; + + void checkExpectation(Op actual) + { + BOOST_CHECK_EQUAL(ops.front(), actual); + ops.pop_front(); + } + + public: + MockMessageStore() : id(0), expectedMsg(0) {} + + void expectStage(PersistableMessage& msg) + { + expectedMsg = &msg; + ops.push_back(STAGE); + } + + void expectAppendContent(PersistableMessage& msg, const string& data) + { + expectedMsg = &msg; + expectedData = data; + ops.push_back(APPEND); + } + + void stage(const intrusive_ptr<PersistableMessage>& msg) + { + checkExpectation(STAGE); + BOOST_CHECK_EQUAL(expectedMsg, msg); + msg->setPersistenceId(++id); + } + + void appendContent(const 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); + } + + 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) +{ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK_EQUAL(exchange, builder.getMessage()->getExchangeName()); + BOOST_CHECK_EQUAL(key, builder.getMessage()->getRoutingKey()); + BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); +} + +QPID_AUTO_TEST_CASE(test1ContentFrame) +{ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string data("abcdefg"); + 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)); + 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<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(header); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(content); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); +} + +QPID_AUTO_TEST_CASE(test2ContentFrames) +{ + MessageBuilder builder(0, 0); + 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)); + method.setEof(false); + header.setBof(false); + header.setEof(false); + content1.setBof(false); + content1.setEof(false); + content2.setBof(false); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + builder.handle(content1); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(content2); + BOOST_CHECK(builder.getMessage()); + BOOST_CHECK(builder.getMessage()->getFrames().isComplete()); +} + +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)); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + + store.expectStage(*builder.getMessage()); + builder.handle(content1); + BOOST_CHECK(store.expectationsMet()); + BOOST_CHECK_EQUAL((uint64_t) 1, builder.getMessage()->getPersistenceId()); + + store.expectAppendContent(*builder.getMessage(), data2); + builder.handle(content2); + BOOST_CHECK(store.expectationsMet()); + //were the content frames dropped? + BOOST_CHECK(!builder.getMessage()->isContentLoaded()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/MessageReplayTracker.cpp b/RC9/qpid/cpp/src/tests/MessageReplayTracker.cpp new file mode 100644 index 0000000000..a5121cdeb7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageReplayTracker.cpp @@ -0,0 +1,99 @@ +/* + * + * 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" + +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() + + diff --git a/RC9/qpid/cpp/src/tests/MessageTest.cpp b/RC9/qpid/cpp/src/tests/MessageTest.cpp new file mode 100644 index 0000000000..f9292ee53e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageTest.cpp @@ -0,0 +1,90 @@ +/* + * + * 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/broker/Message.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/Uuid.h" + +#include "unit_test.h" + +#include <iostream> +#include <alloca.h> + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(MessageTestSuite) + +QPID_AUTO_TEST_CASE(testEncodeDecode) +{ + string exchange = "MyExchange"; + string routingKey = "MyRoutingKey"; + Uuid messageId(true); + string data1("abcdefg"); + string data2("hijklmn"); + + 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)); + + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().append(content1); + msg->getFrames().append(content2); + + MessageProperties* mProps = msg->getFrames().getHeaders()->get<MessageProperties>(true); + mProps->setContentLength(data1.size() + data2.size()); + mProps->setMessageId(messageId); + FieldTable applicationHeaders; + applicationHeaders.setString("abc", "xyz"); + mProps->setApplicationHeaders(applicationHeaders); + DeliveryProperties* dProps = msg->getFrames().getHeaders()->get<DeliveryProperties>(true); + dProps->setRoutingKey(routingKey); + dProps->setDeliveryMode(PERSISTENT); + BOOST_CHECK(msg->isPersistent()); + + 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); + msg->decodeContent(rbuffer); + BOOST_CHECK_EQUAL(exchange, msg->getExchangeName()); + BOOST_CHECK_EQUAL(routingKey, msg->getRoutingKey()); + 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().getAsString("abc")); + BOOST_CHECK_EQUAL((uint8_t) PERSISTENT, msg->getProperties<DeliveryProperties>()->getDeliveryMode()); + BOOST_CHECK(msg->isPersistent()); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/RC9/qpid/cpp/src/tests/MessageUtils.h b/RC9/qpid/cpp/src/tests/MessageUtils.h new file mode 100644 index 0000000000..81508e534e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/MessageUtils.h @@ -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. + * + */ + +#include "qpid/broker/Message.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/Uuid.h" + +using namespace qpid; +using namespace broker; +using namespace framing; + +struct MessageUtils +{ + static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="", + const Uuid& messageId=Uuid(true), uint64_t contentSize = 0) + { + boost::intrusive_ptr<Message> msg(new Message()); + + AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + + msg->getFrames().append(method); + msg->getFrames().append(header); + MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true); + props->setContentLength(contentSize); + props->setMessageId(messageId); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; + } + + static void addContent(boost::intrusive_ptr<Message> msg, const string& data) + { + AMQFrame content(in_place<AMQContentBody>(data)); + msg->getFrames().append(content); + } +}; diff --git a/RC9/qpid/cpp/src/tests/PollerTest.cpp b/RC9/qpid/cpp/src/tests/PollerTest.cpp new file mode 100644 index 0000000000..fcb1d0dadf --- /dev/null +++ b/RC9/qpid/cpp/src/tests/PollerTest.cpp @@ -0,0 +1,164 @@ +/* + * + * 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. + * + */ + +/** + * Use socketpair to test the poller + */ + +#include "qpid/sys/Poller.h" + +#include <string> +#include <iostream> +#include <memory> +#include <exception> + +#include <assert.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +using namespace std; +using namespace qpid::sys; + +int writeALot(int fd, const string& s) { + int bytesWritten = 0; + do { + errno = 0; + int lastWrite = ::write(fd, s.c_str(), s.size()); + if ( lastWrite >= 0) { + bytesWritten += lastWrite; + } + } while (errno != EAGAIN); + return bytesWritten; +} + +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) +{ + try + { + int sv[2]; + int rc = ::socketpair(AF_LOCAL, 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++) + testString += testString; + + // Read as much as we can from socket 0 + int bytesRead = readALot(sv[0]); + assert(bytesRead == 0); + cout << "Read(0): " << bytesRead << " bytes\n"; + + // 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 + Poller::Event event = poller->wait(); + assert(event.handle == &h0); + assert(event.dir == Poller::OUT); + + // 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); + event = poller->wait(); + assert(event.handle == &h1); + assert(event.dir == Poller::INOUT); + + 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 + event = poller->wait(); + assert(event.handle == &h0); + assert(event.dir == Poller::OUT); + + // Now both the handles should be disabled + event = poller->wait(500000000); + assert(event.handle == 0); + + // Test shutdown + poller->shutdown(); + event = poller->wait(); + assert(event.handle == 0); + assert(event.dir == Poller::SHUTDOWN); + + event = poller->wait(); + assert(event.handle == 0); + assert(event.dir == Poller::SHUTDOWN); + + poller->delFd(h1); + poller->delFd(h0); + + return 0; + } catch (exception& e) { + cout << "Caught exception " << e.what() << "\n"; + } +} + + diff --git a/RC9/qpid/cpp/src/tests/QueueOptionsTest.cpp b/RC9/qpid/cpp/src/tests/QueueOptionsTest.cpp new file mode 100644 index 0000000000..bac369bfec --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueueOptionsTest.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/framing/Array.h" +#include "qpid/client/QueueOptions.h" +#include <alloca.h> + +#include "unit_test.h" + +using namespace qpid::client; + + +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() diff --git a/RC9/qpid/cpp/src/tests/QueuePolicyTest.cpp b/RC9/qpid/cpp/src/tests/QueuePolicyTest.cpp new file mode 100644 index 0000000000..6c650169c7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueuePolicyTest.cpp @@ -0,0 +1,274 @@ + /* + * + * 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 "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; + +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) +{ + std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy(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); + } + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on enqueuing sixth message"); + } catch (const ResourceLimitExceededException&) {} + + policy->dequeued(msg); + policy->tryEnqueue(msg); + + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on enqueuing sixth message (after dequeue)"); + } catch (const ResourceLimitExceededException&) {} +} + +QPID_AUTO_TEST_CASE(testSize) +{ + std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy(0, 50)); + QueuedMessage msg = createMessage(10); + + for (size_t i = 0; i < 5; i++) { + policy->tryEnqueue(msg); + } + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on aggregate size exceeding 50. " << *policy); + } catch (const ResourceLimitExceededException&) {} + + policy->dequeued(msg); + policy->tryEnqueue(msg); + + try { + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on aggregate size exceeding 50 (after dequeue). " << *policy); + } catch (const ResourceLimitExceededException&) {} +} + +QPID_AUTO_TEST_CASE(testBoth) +{ + std::auto_ptr<QueuePolicy> policy(QueuePolicy::createQueuePolicy(5, 50)); + try { + QueuedMessage msg = createMessage(51); + policy->tryEnqueue(msg); + 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]); + } + //size = 45 at this point, count = 5 + try { + QueuedMessage msg = createMessage(5); + policy->tryEnqueue(msg); + BOOST_FAIL("Policy did not fail on count exceeding 6. " << *policy); + } catch (const ResourceLimitExceededException&) {} + try { + QueuedMessage msg = createMessage(10); + policy->tryEnqueue(msg); + 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); + } 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(101, 303)); + FieldTable settings; + a->update(settings); + std::auto_ptr<QueuePolicy> b(QueuePolicy::createQueuePolicy(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(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)); +} + +QPID_AUTO_TEST_CASE(testStrictRingPolicy) +{ + FieldTable args; + std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy(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(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_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/QueueRegistryTest.cpp b/RC9/qpid/cpp/src/tests/QueueRegistryTest.cpp new file mode 100644 index 0000000000..7ad4e0b89d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueueRegistryTest.cpp @@ -0,0 +1,94 @@ +/* + * 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/broker/QueueRegistry.h" +#include "unit_test.h" +#include <string> + +using namespace qpid::broker; + +QPID_AUTO_TEST_SUITE(QueueRegistryTest) + +QPID_AUTO_TEST_CASE(testDeclare) +{ + std::string foo("foo"); + std::string bar("bar"); + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + qc = reg.declare(foo, false, 0, 0); + Queue::shared_ptr q = qc.first; + BOOST_CHECK(q); + BOOST_CHECK(qc.second); // New queue + BOOST_CHECK_EQUAL(foo, q->getName()); + + qc = reg.declare(foo, false, 0, 0); + BOOST_CHECK_EQUAL(q, qc.first); + BOOST_CHECK(!qc.second); + + qc = reg.declare(bar, false, 0, 0); + q = qc.first; + BOOST_CHECK(q); + BOOST_CHECK_EQUAL(true, qc.second); + BOOST_CHECK_EQUAL(bar, q->getName()); +} + +QPID_AUTO_TEST_CASE(testDeclareTmp) +{ + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + qc = reg.declare(std::string(), false, 0, 0); + BOOST_CHECK(qc.second); + BOOST_CHECK_EQUAL(std::string("tmp_1"), qc.first->getName()); +} + +QPID_AUTO_TEST_CASE(testFind) +{ + std::string foo("foo"); + std::string bar("bar"); + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + BOOST_CHECK(reg.find(foo) == 0); + + reg.declare(foo, false, 0, 0); + reg.declare(bar, false, 0, 0); + Queue::shared_ptr q = reg.find(bar); + BOOST_CHECK(q); + BOOST_CHECK_EQUAL(bar, q->getName()); +} + +QPID_AUTO_TEST_CASE(testDestroy) +{ + std::string foo("foo"); + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + qc = reg.declare(foo, false, 0, 0); + reg.destroy(foo); + // Queue is gone from the registry. + BOOST_CHECK(reg.find(foo) == 0); + // Queue is not actually destroyed till we drop our reference. + BOOST_CHECK_EQUAL(foo, qc.first->getName()); + // We shoud be the only reference. + BOOST_CHECK_EQUAL(1L, qc.first.use_count()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/QueueTest.cpp b/RC9/qpid/cpp/src/tests/QueueTest.cpp new file mode 100644 index 0000000000..f1771e26cd --- /dev/null +++ b/RC9/qpid/cpp/src/tests/QueueTest.cpp @@ -0,0 +1,501 @@ + /* + * + * 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/Exception.h" +#include "qpid/broker/Broker.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/framing/MessageTransferBody.h" +#include "qpid/client/QueueOptions.h" +#include <iostream> +#include "boost/format.hpp" + +using boost::intrusive_ptr; +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +class TestConsumer : public virtual Consumer{ +public: + typedef boost::shared_ptr<TestConsumer> shared_ptr; + + intrusive_ptr<Message> last; + bool received; + TestConsumer(bool acquire = true):Consumer(acquire), received(false) {}; + + virtual bool deliver(QueuedMessage& msg){ + last = msg.payload; + received = true; + return true; + }; + void notify() {} + OwnershipToken* getSession() { return 0; } +}; + +class FailOnDeliver : public Deliverable +{ + Message msg; +public: + void deliverTo(const boost::shared_ptr<Queue>& queue) + { + throw Exception(QPID_MSG("Invalid delivery to " << queue->getName())); + } + Message& getMessage() { return msg; } +}; + +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>()); + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; +} + +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::shared_ptr c1(new TestConsumer()); + queue->consume(c1); + + + //Test basic delivery: + intrusive_ptr<Message> msg1 = create_message("e", "A"); + msg1->enqueueAsync();//this is done on enqueue which is not called from process + queue->process(msg1); + sleep(2); + + BOOST_CHECK(!c1->received); + msg1->enqueueComplete(); + + received = queue->get().payload; + 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 = create_message("e", "A"); + msg1->enqueueAsync();//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()); + msg1->enqueueComplete(); + compval=1; + BOOST_CHECK_EQUAL(compval, queue->getMessageCount()); +} + +QPID_AUTO_TEST_CASE(testConsumers){ + Queue::shared_ptr queue(new Queue("my_queue", true)); + + //Test adding consumers: + 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 = 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()); + + queue->deliver(msg2); + BOOST_CHECK(queue->dispatch(c2)); + 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()); + + //Test cancellation: + queue->cancel(c1); + BOOST_CHECK_EQUAL(uint32_t(1), queue->getConsumerCount()); + queue->cancel(c2); + BOOST_CHECK_EQUAL(uint32_t(0), queue->getConsumerCount()); +} + +QPID_AUTO_TEST_CASE(testRegistry){ + //Test use of queues in registry: + QueueRegistry registry; + 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")); +} + +QPID_AUTO_TEST_CASE(testDequeue){ + 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"); + 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()); + + received = queue->get().payload; + BOOST_CHECK_EQUAL(msg2.get(), received.get()); + BOOST_CHECK_EQUAL(uint32_t(1), queue->getMessageCount()); + + TestConsumer::shared_ptr consumer(new TestConsumer()); + queue->consume(consumer); + queue->dispatch(consumer); + if (!consumer->received) + sleep(2); + + 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) +{ + //test the recording of bindings, and use of those to allow a queue to be unbound + string key("my-key"); + FieldTable args; + + Queue::shared_ptr queue(new Queue("my-queue", true)); + ExchangeRegistry exchanges; + //establish bindings from exchange->queue and notify the queue as it is bound: + Exchange::shared_ptr exchange1 = exchanges.declare("my-exchange-1", "direct").first; + exchange1->bind(queue, key, &args); + queue->bound(exchange1->getName(), key, args); + + Exchange::shared_ptr exchange2 = exchanges.declare("my-exchange-2", "fanout").first; + exchange2->bind(queue, key, &args); + queue->bound(exchange2->getName(), key, args); + + Exchange::shared_ptr exchange3 = exchanges.declare("my-exchange-3", "topic").first; + exchange3->bind(queue, key, &args); + queue->bound(exchange3->getName(), key, args); + + //delete one of the exchanges: + exchanges.destroy(exchange2->getName()); + exchange2.reset(); + + //unbind the queue from all exchanges it knows it has been bound to: + queue->unbind(exchanges, queue); + + //ensure the remaining exchanges don't still have the queue bound to them: + FailOnDeliver deliverable; + exchange1->route(deliverable, key, &args); + exchange3->route(deliverable, key, &args); +} + +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()); + +} + +class TestMessageStoreOC : public NullMessageStore +{ + public: + + virtual void dequeue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& /*msg*/, + const PersistableQueue& /*queue*/) + { + } + + virtual void enqueue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& /*msg*/, + const PersistableQueue& /* queue */) + { + } + + TestMessageStoreOC() : NullMessageStore() {} + ~TestMessageStoreOC(){} +}; + + +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(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()); + +} + +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(); + 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_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/README b/RC9/qpid/cpp/src/tests/README new file mode 100644 index 0000000000..0f4edee493 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/README @@ -0,0 +1,54 @@ += Running Qpid C++ tests = + +General philosophy is that "make check" run all tests by default, but +developers can run tests selectively as explained below. + +== Valgrind == + +By default we run tests under valgrind to detect memory errors if valgrind +is present. ./configure --disable-valgrind will disable it. + +Default valgrind options are specified in .valgrindrc-default, which a +checked-in file. The actual options used are in .valgrindrc which is a +local file. Normally it is a copy of valgrindrc-default but you can +modify at will. + +Supressed errors are listed in .valgrind.supp. If you want to change +suppressions for local testing, just modify .valgrindrc to point to a +different file. Do NOT add suppressions to .valgrindrc.supp unless +they are known problems outside of Qpid that can't reasonably be +worked around in Qpid. + + +== Unit Tests == +Unit tests use the boost test framework, and are compiled to rogram + unit_test +There are several options to control how test results are displayed, +see + http://www.boost.org/doc/libs/1_35_0/libs/test/doc/components/utf/parameters/index.html + +NOTE: some unit tests are written as CppUnit plug-ins, we are moving away +from CppUnit so new tests should use the boost framework. + +CppUnit tests are run by the script run-unit-tests. + +== System Tests == + +System tests are self contained AMQP client executables or scripts. +They are listed in the TESTS make variable, which can be over-ridden. + +The ./start_broker "test" launches the broker, ./stop_broker" stops it. +Tests in between assume the broker is running. + +./python_tests: runs ../python/run_tests. This is the main set of +system testss for the broker. + +Other C++ client test executables and scripts under client/test are +system tests for the client. + +By setting TESTS in a make command you can run a different subset of tests +against an already-running broker. + + + + diff --git a/RC9/qpid/cpp/src/tests/RangeSet.cpp b/RC9/qpid/cpp/src/tests/RangeSet.cpp new file mode 100644 index 0000000000..9c602de78d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/RangeSet.cpp @@ -0,0 +1,141 @@ +/* + * + * 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/RangeSet.h" + +using namespace std; +using namespace qpid; + +QPID_AUTO_TEST_SUITE(RangeSetTestSuite) + +typedef qpid::Range<int> TestRange; +typedef qpid::RangeSet<int> TestRangeSet; + +QPID_AUTO_TEST_CASE(testEmptyRange) { + TestRange r; + BOOST_CHECK(r.empty()); + BOOST_CHECK(!r.contains(0)); + // BOOST_CHECK(r.contiguous(0)); +} + +QPID_AUTO_TEST_CASE(testRangeSetAddPoint) { + TestRangeSet r; + BOOST_CHECK(r.empty()); + r += 3; + BOOST_CHECK_MESSAGE(r.contains(3), r); + 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(TestRange(3,6)), r); + r += 4; + BOOST_CHECK_MESSAGE(r.contains(TestRange(3,6)), r); +} + +QPID_AUTO_TEST_CASE(testRangeSetAddRange) { + TestRangeSet r; + r += TestRange(0,3); + BOOST_CHECK(r.contains(TestRange(0,3))); + r += TestRange(4,6); + BOOST_CHECK_MESSAGE(r.contains(TestRange(4,6)), r); + r += 3; + BOOST_CHECK_MESSAGE(r.contains(TestRange(0,6)), r); + BOOST_CHECK(r.front() == 0); + BOOST_CHECK(r.back() == 6); +} + +QPID_AUTO_TEST_CASE(testRangeSetAddSet) { + TestRangeSet r; + TestRangeSet s = TestRangeSet(0,3)+TestRange(5,10); + r += s; + BOOST_CHECK_EQUAL(r,s); + r += TestRangeSet(3,5) + TestRange(7,12) + 15; + BOOST_CHECK_EQUAL(r, TestRangeSet(0,12) + 15); + + r.clear(); + BOOST_CHECK(r.empty()); + r += TestRange::makeClosed(6,10); + BOOST_CHECK_EQUAL(r, TestRangeSet(6,11)); + r += TestRangeSet(2,6)+8; + BOOST_CHECK_EQUAL(r, TestRangeSet(2,11)); +} + +QPID_AUTO_TEST_CASE(testRangeSetIterate) { + TestRangeSet r; + (((r += 1) += 10) += TestRange(4,7)) += 2; + BOOST_MESSAGE(r); + std::vector<int> actual; + std::copy(r.begin(), r.end(), std::back_inserter(actual)); + std::vector<int> expect = boost::assign::list_of(1)(2)(4)(5)(6)(10); + BOOST_CHECK_EQUAL(expect, actual); +} + +QPID_AUTO_TEST_CASE(testRangeSetRemove) { + // points + BOOST_CHECK_EQUAL(TestRangeSet(0,5)-3, TestRangeSet(0,3)+TestRange(4,5)); + BOOST_CHECK_EQUAL(TestRangeSet(1,5)-5, TestRangeSet(1,5)); + BOOST_CHECK_EQUAL(TestRangeSet(1,5)-0, TestRangeSet(1,5)); + + TestRangeSet r(TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,25)); + + // TestRanges + BOOST_CHECK_EQUAL(r-TestRange(0,5), TestRangeSet(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(10,15), TestRangeSet(0,5)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(20,25), TestRangeSet(0,5)+TestRange(10,15)); + + BOOST_CHECK_EQUAL(r-TestRange(-5, 30), TestRangeSet()); + + BOOST_CHECK_EQUAL(r-TestRange(-5, 7), TestRangeSet(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(8,19), TestRangeSet(0,5)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15)); + BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15)); + + BOOST_CHECK_EQUAL(r-TestRange(-5, 5), TestRangeSet(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(10,19), TestRangeSet(0,5)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(18,25), TestRangeSet(0,5)+TestRange(10,15)); + BOOST_CHECK_EQUAL(r-TestRange(23,25), TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,23)); + + BOOST_CHECK_EQUAL(r-TestRange(-3, 3), TestRangeSet(3,5)+TestRange(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(3, 7), TestRangeSet(0,2)+TestRange(10,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(3, 12), TestRangeSet(0,3)+TestRange(12,15)+TestRange(20,25)); + BOOST_CHECK_EQUAL(r-TestRange(3, 22), TestRangeSet(12,15)+TestRange(22,25)); + BOOST_CHECK_EQUAL(r-TestRange(12, 22), TestRangeSet(0,5)+TestRange(10,11)+TestRange(22,25)); + + // Sets + BOOST_CHECK_EQUAL(r-(TestRangeSet(-1,6)+TestRange(11,14)+TestRange(23,25)), + TestRangeSet(10,11)+TestRange(14,15)+TestRange(20,23)); +} + +QPID_AUTO_TEST_CASE(testRangeContaining) { + TestRangeSet r; + (((r += 1) += TestRange(3,5)) += 7); + BOOST_CHECK_EQUAL(r.rangeContaining(0), TestRange(0,0)); + BOOST_CHECK_EQUAL(r.rangeContaining(1), TestRange(1,2)); + BOOST_CHECK_EQUAL(r.rangeContaining(2), TestRange(2,2)); + BOOST_CHECK_EQUAL(r.rangeContaining(3), TestRange(3,5)); + BOOST_CHECK_EQUAL(r.rangeContaining(4), TestRange(3,5)); + BOOST_CHECK_EQUAL(r.rangeContaining(5), TestRange(5,5)); + BOOST_CHECK_EQUAL(r.rangeContaining(6), TestRange(6,6)); + BOOST_CHECK_EQUAL(r.rangeContaining(7), TestRange(7,8)); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/RefCounted.cpp b/RC9/qpid/cpp/src/tests/RefCounted.cpp new file mode 100644 index 0000000000..8c679a3d2e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/RefCounted.cpp @@ -0,0 +1,50 @@ +/* + * + * 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/RefCounted.h" +#include <boost/intrusive_ptr.hpp> + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(RefCountedTestSuiteTestSuite) + +using boost::intrusive_ptr; +using namespace std; +using namespace qpid; + +struct CountMe : public RefCounted { + static int instances; + CountMe() { ++instances; } + ~CountMe() { --instances; } +}; + +int CountMe::instances=0; + +QPID_AUTO_TEST_CASE(testRefCounted) { + BOOST_CHECK_EQUAL(0, CountMe::instances); + intrusive_ptr<CountMe> p(new CountMe()); + BOOST_CHECK_EQUAL(1, CountMe::instances); + intrusive_ptr<CountMe> q(p); + BOOST_CHECK_EQUAL(1, CountMe::instances); + q=0; + BOOST_CHECK_EQUAL(1, CountMe::instances); + p=0; + BOOST_CHECK_EQUAL(0, CountMe::instances); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/SequenceNumberTest.cpp b/RC9/qpid/cpp/src/tests/SequenceNumberTest.cpp new file mode 100644 index 0000000000..e4c6d066ef --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SequenceNumberTest.cpp @@ -0,0 +1,205 @@ +/* + * + * 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 <iostream> +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceNumberSet.h" + +using namespace qpid::framing; + + +void checkDifference(SequenceNumber& a, SequenceNumber& b, int gap) +{ + BOOST_CHECK_EQUAL(gap, a - b); + BOOST_CHECK_EQUAL(-gap, b - a); + + //increment until b wraps around + for (int i = 0; i < (gap + 2); i++, ++a, ++b) { + BOOST_CHECK_EQUAL(gap, a - b); + } + //keep incrementing until a also wraps around + for (int i = 0; i < (gap + 2); i++, ++a, ++b) { + BOOST_CHECK_EQUAL(gap, a - b); + } + //let b catch up and overtake + for (int i = 0; i < (gap*2); i++, ++b) { + BOOST_CHECK_EQUAL(gap - i, a - b); + BOOST_CHECK_EQUAL(i - gap, b - a); + } +} + +void checkComparison(SequenceNumber& a, SequenceNumber& b, int gap) +{ + //increment until b wraps around + for (int i = 0; i < (gap + 2); i++) { + BOOST_CHECK(++a < ++b);//test prefix + } + //keep incrementing until a also wraps around + for (int i = 0; i < (gap + 2); i++) { + BOOST_CHECK(a++ < b++);//test postfix + } + //let a 'catch up' + for (int i = 0; i < gap; i++) { + a++; + } + BOOST_CHECK(a == b); + BOOST_CHECK(++a > b); +} + + +QPID_AUTO_TEST_SUITE(SequenceNumberTestSuite) + +QPID_AUTO_TEST_CASE(testIncrementPostfix) +{ + SequenceNumber a; + SequenceNumber b; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); + + SequenceNumber c = a++; + BOOST_CHECK(a > b); + BOOST_CHECK(b < a); + BOOST_CHECK(a != b); + BOOST_CHECK(c < a); + BOOST_CHECK(a != c); + + b++; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); + BOOST_CHECK(c < b); + BOOST_CHECK(b != c); +} + +QPID_AUTO_TEST_CASE(testIncrementPrefix) +{ + SequenceNumber a; + SequenceNumber b; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); + + SequenceNumber c = ++a; + BOOST_CHECK(a > b); + BOOST_CHECK(b < a); + BOOST_CHECK(a != b); + BOOST_CHECK(a == c); + + ++b; + BOOST_CHECK(!(a > b)); + BOOST_CHECK(!(b < a)); + BOOST_CHECK(a == b); +} + +QPID_AUTO_TEST_CASE(testWrapAround) +{ + const uint32_t max = 0xFFFFFFFF; + SequenceNumber a(max - 10); + SequenceNumber b(max - 5); + checkComparison(a, b, 5); + + const uint32_t max_signed = 0x7FFFFFFF; + SequenceNumber c(max_signed - 10); + SequenceNumber d(max_signed - 5); + checkComparison(c, d, 5); +} + +QPID_AUTO_TEST_CASE(testCondense) +{ + SequenceNumberSet set; + for (uint i = 0; i < 6; i++) { + set.push_back(SequenceNumber(i)); + } + set.push_back(SequenceNumber(7)); + for (uint i = 9; i < 13; i++) { + set.push_back(SequenceNumber(i)); + } + set.push_back(SequenceNumber(13)); + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(0), SequenceNumber(5)); + expected.addRange(SequenceNumber(7), SequenceNumber(7)); + expected.addRange(SequenceNumber(9), SequenceNumber(13)); + BOOST_CHECK_EQUAL(expected, actual); +} + +QPID_AUTO_TEST_CASE(testCondenseSingleRange) +{ + SequenceNumberSet set; + for (uint i = 0; i < 6; i++) { + set.push_back(SequenceNumber(i)); + } + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(0), SequenceNumber(5)); + BOOST_CHECK_EQUAL(expected, actual); +} + +QPID_AUTO_TEST_CASE(testCondenseSingleItem) +{ + SequenceNumberSet set; + set.push_back(SequenceNumber(1)); + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(1), SequenceNumber(1)); + BOOST_CHECK_EQUAL(expected, actual); +} + +QPID_AUTO_TEST_CASE(testDifference) +{ + SequenceNumber a; + SequenceNumber b; + + for (int i = 0; i < 10; i++, ++a) { + BOOST_CHECK_EQUAL(i, a - b); + BOOST_CHECK_EQUAL(-i, b - a); + } + + b = a; + + for (int i = 0; i < 10; i++, ++b) { + BOOST_CHECK_EQUAL(-i, a - b); + BOOST_CHECK_EQUAL(i, b - a); + } +} + +QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround1) +{ + const uint32_t max = 0xFFFFFFFF; + SequenceNumber a(max - 5); + SequenceNumber b(max - 10); + checkDifference(a, b, 5); +} + +QPID_AUTO_TEST_CASE(testDifferenceWithWrapAround2) +{ + const uint32_t max_signed = 0x7FFFFFFF; + SequenceNumber c(max_signed - 5); + SequenceNumber d(max_signed - 10); + checkDifference(c, d, 5); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/SequenceSet.cpp b/RC9/qpid/cpp/src/tests/SequenceSet.cpp new file mode 100644 index 0000000000..ba2f1391a1 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SequenceSet.cpp @@ -0,0 +1,140 @@ +/* + * + * 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/framing/SequenceSet.h" +#include "unit_test.h" +#include <list> + +QPID_AUTO_TEST_SUITE(SequenceSetTestSuite) + +using namespace qpid::framing; + +struct RangeExpectations +{ + typedef std::pair<SequenceNumber, SequenceNumber> Range; + typedef std::list<Range> Ranges; + + Ranges ranges; + + RangeExpectations& expect(const SequenceNumber& start, const SequenceNumber& end) { + ranges.push_back(Range(start, end)); + return *this; + } + + void operator()(const SequenceNumber& start, const SequenceNumber& end) { + BOOST_CHECK(!ranges.empty()); + if (!ranges.empty()) { + BOOST_CHECK_EQUAL(start, ranges.front().first); + BOOST_CHECK_EQUAL(end, ranges.front().second); + ranges.pop_front(); + } + } + + void check(SequenceSet& set) { + set.for_each(*this); + BOOST_CHECK(ranges.empty()); + } +}; + +QPID_AUTO_TEST_CASE(testAdd) { + SequenceSet s; + s.add(2); + s.add(8,8); + s.add(3,5); + + for (uint32_t i = 0; i <= 1; i++) + BOOST_CHECK(!s.contains(i)); + + for (uint32_t i = 2; i <= 5; i++) + BOOST_CHECK(s.contains(i)); + + for (uint32_t i = 6; i <= 7; i++) + BOOST_CHECK(!s.contains(i)); + + BOOST_CHECK(s.contains(8)); + + for (uint32_t i = 9; i <= 10; i++) + BOOST_CHECK(!s.contains(i)); + + RangeExpectations().expect(2, 5).expect(8, 8).check(s); + + SequenceSet t; + t.add(6, 10); + t.add(s); + + for (uint32_t i = 0; i <= 1; i++) + BOOST_CHECK(!t.contains(i)); + + for (uint32_t i = 2; i <= 10; i++) + BOOST_CHECK_MESSAGE(t.contains(i), t << " contains " << i); + + RangeExpectations().expect(2, 10).check(t); +} + +QPID_AUTO_TEST_CASE(testAdd2) { + SequenceSet s; + s.add(7,6); + s.add(4,4); + s.add(3,10); + s.add(2); + RangeExpectations().expect(2, 10).check(s); +} + +QPID_AUTO_TEST_CASE(testRemove) { + SequenceSet s; + SequenceSet t; + s.add(0, 10); + t.add(0, 10); + + s.remove(7); + s.remove(3, 5); + s.remove(9, 10); + + t.remove(s); + + for (uint32_t i = 0; i <= 2; i++) { + BOOST_CHECK(s.contains(i)); + BOOST_CHECK(!t.contains(i)); + } + + for (uint32_t i = 3; i <= 5; i++) { + BOOST_CHECK(!s.contains(i)); + BOOST_CHECK(t.contains(i)); + } + + BOOST_CHECK(s.contains(6)); + BOOST_CHECK(!t.contains(6)); + + BOOST_CHECK(!s.contains(7)); + BOOST_CHECK(t.contains(7)); + + BOOST_CHECK(s.contains(8)); + BOOST_CHECK(!t.contains(8)); + + for (uint32_t i = 9; i <= 10; i++) { + BOOST_CHECK(!s.contains(i)); + BOOST_CHECK(t.contains(i)); + } + + RangeExpectations().expect(0, 2).expect(6, 6).expect(8, 8).check(s); + RangeExpectations().expect(3, 5).expect(7, 7).expect(9, 10).check(t); +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/RC9/qpid/cpp/src/tests/SessionState.cpp b/RC9/qpid/cpp/src/tests/SessionState.cpp new file mode 100644 index 0000000000..2db25f9fe8 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SessionState.cpp @@ -0,0 +1,300 @@ +/* + * + * 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 "qpid/SessionState.h" +#include "qpid/Exception.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/SessionFlushBody.h" + +#include <boost/bind.hpp> +#include <algorithm> +#include <functional> +#include <numeric> + +QPID_AUTO_TEST_SUITE(SessionStateTestSuite) + +using namespace std; +using namespace boost; +using namespace qpid::framing; + +// ================================================================ +// Utility functions. + +// Apply f to [begin, end) and accumulate the result +template <class Iter, class T, class F> +T applyAccumulate(Iter begin, Iter end, T seed, const F& f) { + return std::accumulate(begin, end, seed, bind(std::plus<T>(), _1, bind(f, _2))); +} + +// Create a frame with a one-char string. +AMQFrame& frame(char s) { + static AMQFrame frame; + frame.setBody(AMQContentBody(string(&s, 1))); + return frame; +} + +// Simple string representation of a frame. +string str(const AMQFrame& f) { + if (f.getMethod()) return "C"; // Command or Control + const AMQContentBody* c = dynamic_cast<const AMQContentBody*>(f.getBody()); + if (c) return c->getData(); // Return data for content frames. + return "H"; // Must be a header. +} +// Make a string from a range of frames. +string str(const boost::iterator_range<vector<AMQFrame>::const_iterator>& frames) { + string (*strFrame)(const AMQFrame&) = str; + return applyAccumulate(frames.begin(), frames.end(), string(), ptr_fun(strFrame)); +} +// Make a transfer command frame. +AMQFrame transferFrame(bool hasContent) { + AMQFrame t(in_place<MessageTransferBody>()); + t.setFirstFrame(true); + t.setLastFrame(true); + t.setFirstSegment(true); + t.setLastSegment(!hasContent); + return t; +} +// Make a content frame +AMQFrame contentFrame(string content, bool isLast=true) { + AMQFrame f(in_place<AMQContentBody>(content)); + f.setFirstFrame(true); + f.setLastFrame(true); + f.setFirstSegment(false); + f.setLastSegment(isLast); + return f; +} +AMQFrame contentFrameChar(char content, bool isLast=true) { + return contentFrame(string(1, content), isLast); +} + +// Send frame & return size of frame. +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. +size_t transfer1(qpid::SessionState& s, string content) { + return send(s,transferFrame(true)) + send(s,contentFrame(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())); + if (!content.empty()) { + char last = content[content.size()-1]; + content.resize(content.size()-1); + size += applyAccumulate(content.begin(), content.end(), 0, + bind(&send, ref(s), + bind(contentFrameChar, _1, false))); + size += send(s, contentFrameChar(last, true)); + } + return size; +} + +// Send multiple transfers with single-byte content. +size_t transfers(qpid::SessionState& s, string content) { + return applyAccumulate(content.begin(), content.end(), 0, + bind(transfer1Char, ref(s), _1)); +} + +size_t contentFrameSize(size_t n=1) { return AMQFrame(in_place<AMQContentBody>()).encodedSize() + n; } +size_t transferFrameSize() { return AMQFrame(in_place<MessageTransferBody>()).encodedSize(); } + +// ==== qpid::SessionState test classes + +using qpid::SessionId; +using qpid::SessionPoint; + + +QPID_AUTO_TEST_CASE(testSendGetReplyList) { + qpid::SessionState s; + s.setTimeout(1); + s.senderGetCommandPoint(); + transfer1(s, "abc"); + transfers(s, "def"); + 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"); +} + +QPID_AUTO_TEST_CASE(testNeedFlush) { + qpid::SessionState::Configuration c; + // sync after 2 1-byte transfers or equivalent bytes. + c.replayFlushLimit = 2*(transferFrameSize()+contentFrameSize()); + qpid::SessionState s(SessionId(), c); + s.setTimeout(1); + s.senderGetCommandPoint(); + transfers(s, "a"); + BOOST_CHECK(!s.senderNeedFlush()); + transfers(s, "b"); + BOOST_CHECK(s.senderNeedFlush()); + s.senderRecordFlush(); + BOOST_CHECK(!s.senderNeedFlush()); + transfers(s, "c"); + BOOST_CHECK(!s.senderNeedFlush()); + transfers(s, "d"); + BOOST_CHECK(s.senderNeedFlush()); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint())), "CaCbCcCd"); +} + +QPID_AUTO_TEST_CASE(testPeerConfirmed) { + qpid::SessionState::Configuration c; + // sync after 2 1-byte transfers or equivalent bytes. + c.replayFlushLimit = 2*(transferFrameSize()+contentFrameSize()); + qpid::SessionState s(SessionId(), c); + s.setTimeout(1); + s.senderGetCommandPoint(); + transfers(s, "ab"); + BOOST_CHECK(s.senderNeedFlush()); + transfers(s, "cd"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCcCd"); + s.senderConfirmed(SessionPoint(3)); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(3,0))), "Cd"); + BOOST_CHECK(!s.senderNeedFlush()); + + // Multi-frame transfer. + transfer1(s, "efg"); + transfers(s, "xy"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(3,0))), "CdCefgCxCy"); + BOOST_CHECK(s.senderNeedFlush()); + + s.senderConfirmed(SessionPoint(4)); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CefgCxCy"); + BOOST_CHECK(s.senderNeedFlush()); + + 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()); +} + +QPID_AUTO_TEST_CASE(testPeerCompleted) { + qpid::SessionState s; + s.setTimeout(1); + s.senderGetCommandPoint(); + // Completion implies confirmation + transfers(s, "abc"); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(0,0))), "CaCbCc"); + SequenceSet set(SequenceSet() + 0 + 1); + s.senderCompleted(set); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(2,0))), "Cc"); + + 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); + BOOST_CHECK_EQUAL(str(s.senderExpected(SessionPoint(4,0))), "CeCf"); +} + +QPID_AUTO_TEST_CASE(testReceive) { + // Advance expected/received correctly + qpid::SessionState s; + 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); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), point); + BOOST_CHECK(s.receiverRecord(contentFrame("", false))); + point.offset += contentFrameSize(0); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), point); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), point); + BOOST_CHECK(s.receiverRecord(contentFrame("", true))); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(2)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(2)); + + // Idempotence barrier, rewind expected & receive some duplicates. + s.receiverSetCommandPoint(SessionPoint(1)); + BOOST_CHECK(!s.receiverRecord(transferFrame(false))); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(2)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(2)); + BOOST_CHECK(s.receiverRecord(transferFrame(false))); + BOOST_CHECK_EQUAL(s.receiverGetExpected(), SessionPoint(3)); + BOOST_CHECK_EQUAL(s.receiverGetReceived(), SessionPoint(3)); +} + +QPID_AUTO_TEST_CASE(testCompleted) { + // completed & unknownCompleted + qpid::SessionState s; + s.receiverSetCommandPoint(SessionPoint()); + s.receiverRecord(transferFrame(false)); + s.receiverRecord(transferFrame(false)); + s.receiverRecord(transferFrame(false)); + s.receiverCompleted(1); + BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), SequenceSet(SequenceSet()+1)); + s.receiverCompleted(0); + BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), + SequenceSet(SequenceSet() + qpid::Range<SequenceNumber>(0,2))); + s.receiverKnownCompleted(SequenceSet(SequenceSet()+1)); + BOOST_CHECK_EQUAL(s.receiverGetUnknownComplete(), SequenceSet(SequenceSet()+2)); + // TODO aconway 2008-04-30: missing tests for known-completed. +} + +QPID_AUTO_TEST_CASE(testNeedKnownCompleted) { + size_t flushInterval= 2*(transferFrameSize()+contentFrameSize())+1; + qpid::SessionState::Configuration c(flushInterval); + qpid::SessionState s(qpid::SessionId(), c); + s.senderGetCommandPoint(); + transfers(s, "a"); + SequenceSet set(SequenceSet() + 0); + s.senderCompleted(set); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "b"); + set += 1; + s.senderCompleted(set); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "c"); + set += 2; + s.senderCompleted(set); + BOOST_CHECK(s.senderNeedKnownCompleted()); + s.senderRecordKnownCompleted(); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "de"); + set += 3; + set += 4; + s.senderCompleted(set); + BOOST_CHECK(!s.senderNeedKnownCompleted()); + + transfers(s, "f"); + set += 2; + s.senderCompleted(set); + BOOST_CHECK(s.senderNeedKnownCompleted()); + s.senderRecordKnownCompleted(); + BOOST_CHECK(!s.senderNeedKnownCompleted()); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/Shlib.cpp b/RC9/qpid/cpp/src/tests/Shlib.cpp new file mode 100644 index 0000000000..426a052c9f --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Shlib.cpp @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_tools.h" +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ShlibTestSuite) + +using namespace qpid::sys; +typedef void (*CallMe)(int*); + +QPID_AUTO_TEST_CASE(testShlib) { + Shlib sh(".libs/libshlibtest.so"); + // Double cast to avoid ISO warning. + CallMe callMe=sh.getSymbol<CallMe>("callMe"); + BOOST_REQUIRE(callMe != 0); + int unloaded=0; + callMe(&unloaded); + sh.unload(); + BOOST_CHECK_EQUAL(42, unloaded); + try { + sh.getSymbol("callMe"); + BOOST_FAIL("Expected exception"); + } + catch (const qpid::Exception&) {} +} + +QPID_AUTO_TEST_CASE(testAutoShlib) { + int unloaded = 0; + { + AutoShlib sh(".libs/libshlibtest.so"); + CallMe callMe=sh.getSymbol<CallMe>("callMe"); + BOOST_REQUIRE(callMe != 0); + callMe(&unloaded); + } + BOOST_CHECK_EQUAL(42, unloaded); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.cpp b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.cpp new file mode 100644 index 0000000000..2739734731 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.cpp @@ -0,0 +1,87 @@ +/* + * + * 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/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.h b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.h new file mode 100644 index 0000000000..0c1052d0c2 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SimpleTestCaseBase.h @@ -0,0 +1,89 @@ +#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/RC9/qpid/cpp/src/tests/SocketProxy.h b/RC9/qpid/cpp/src/tests/SocketProxy.h new file mode 100644 index 0000000000..9722359d82 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/SocketProxy.h @@ -0,0 +1,143 @@ +#ifndef SOCKETPROXY_H +#define SOCKETPROXY_H + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "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> + +/** + * A simple socket proxy that forwards to another socket. + * Used between client & local broker to simulate network failures. + */ +class SocketProxy : private qpid::sys::Runnable +{ + 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() + { + client.connect(host, connectPort); + thread = qpid::sys::Thread(static_cast<qpid::sys::Runnable*>(this)); + } + + ~SocketProxy() { close(); } + + /** Simulate a network disconnect. */ + void close() { + { + qpid::sys::Mutex::ScopedLock l(lock); + if (closed) return; + closed=true; + } + poller.shutdown(); + if (thread.id() != qpid::sys::Thread::current().id()) + thread.join(); + client.close(); + } + + /** Simulate lost packets, drop data from client */ + void dropClientData(bool drop=true) { dropClient=drop; } + + /** Simulate lost packets, drop data from server */ + void dropServerData(bool drop=true) { dropServer=drop; } + + bool isClosed() const { + qpid::sys::Mutex::ScopedLock l(lock); + return closed; + } + + uint16_t getPort() const { return port; } + + private: + static void throwErrno(const std::string& msg) { + throw qpid::Exception(msg+":"+qpid::sys::strError(errno)); + } + static void throwIf(bool condition, const std::string& msg) { + if (condition) throw qpid::Exception(msg); + } + + void run() { + std::auto_ptr<qpid::sys::Socket> server; + try { + qpid::sys::PollerHandle listenerHandle(listener); + poller.addFd(listenerHandle, qpid::sys::Poller::INPUT); + 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::INPUT); + poller.addFd(serverHandle, qpid::sys::Poller::INPUT); + 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)); + 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"); + } + } + } + catch (const std::exception& e) { + QPID_LOG(debug, "SocketProxy::run exception: " << e.what()); + } + try { + 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; + uint16_t port; + qpid::sys::Thread thread; + bool dropClient, dropServer; +}; + +#endif diff --git a/RC9/qpid/cpp/src/tests/StringUtils.cpp b/RC9/qpid/cpp/src/tests/StringUtils.cpp new file mode 100644 index 0000000000..6a19119288 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/StringUtils.cpp @@ -0,0 +1,77 @@ +/* + * + * 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/StringUtils.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(StringUtilsTestSuite) + +using namespace qpid; +using std::string; + +QPID_AUTO_TEST_CASE(testSplit_general) +{ + std::vector<std::string> results = split("a bbbb, car,d123,,,e", ", "); + BOOST_CHECK_EQUAL(5u, results.size()); + BOOST_CHECK_EQUAL(string("a"), results[0]); + BOOST_CHECK_EQUAL(string("bbbb"), results[1]); + BOOST_CHECK_EQUAL(string("car"), results[2]); + BOOST_CHECK_EQUAL(string("d123"), results[3]); + BOOST_CHECK_EQUAL(string("e"), results[4]); +} + +QPID_AUTO_TEST_CASE(testSplit_noDelims) +{ + std::vector<std::string> results = split("abc", ", "); + BOOST_CHECK_EQUAL(1u, results.size()); + BOOST_CHECK_EQUAL(string("abc"), results[0]); +} + +QPID_AUTO_TEST_CASE(testSplit_delimAtEnd) +{ + std::vector<std::string> results = split("abc def,,", ", "); + BOOST_CHECK_EQUAL(2u, results.size()); + BOOST_CHECK_EQUAL(string("abc"), results[0]); + BOOST_CHECK_EQUAL(string("def"), results[1]); +} + +QPID_AUTO_TEST_CASE(testSplit_delimAtStart) +{ + std::vector<std::string> results = split(",,abc def", ", "); + BOOST_CHECK_EQUAL(2u, results.size()); + BOOST_CHECK_EQUAL(string("abc"), results[0]); + BOOST_CHECK_EQUAL(string("def"), results[1]); +} + +QPID_AUTO_TEST_CASE(testSplit_onlyDelims) +{ + std::vector<std::string> results = split(",, , ", ", "); + BOOST_CHECK_EQUAL(0u, results.size()); +} + +QPID_AUTO_TEST_CASE(testSplit_empty) +{ + std::vector<std::string> results = split("", ", "); + BOOST_CHECK_EQUAL(0u, results.size()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TestCase.h b/RC9/qpid/cpp/src/tests/TestCase.h new file mode 100644 index 0000000000..ba3330c951 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TestCase.h @@ -0,0 +1,64 @@ +#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/RC9/qpid/cpp/src/tests/TestMessageStore.h b/RC9/qpid/cpp/src/tests/TestMessageStore.h new file mode 100644 index 0000000000..be1ed57349 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TestMessageStore.h @@ -0,0 +1,58 @@ +#ifndef _tests_TestMessageStore_h +#define _tests_TestMessageStore_h + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "qpid/broker/NullMessageStore.h" +#include <vector> + +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; + +typedef std::pair<string, boost::intrusive_ptr<PersistableMessage> > msg_queue_pair; + +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*/) + { + dequeued.push_back(msg); + } + + void enqueue(TransactionContext*, + const boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) + { + msg->enqueueComplete(); + enqueued.push_back(msg_queue_pair(queue.getName(), msg)); + } + + TestMessageStore() : NullMessageStore() {} + ~TestMessageStore(){} +}; + +#endif diff --git a/RC9/qpid/cpp/src/tests/TestOptions.h b/RC9/qpid/cpp/src/tests/TestOptions.h new file mode 100644 index 0000000000..a400fe5ecb --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TestOptions.h @@ -0,0 +1,79 @@ +#ifndef _TestOptions_ +#define _TestOptions_ +/* + * + * 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/log/Options.h" +#include "qpid/Url.h" +#include "qpid/log/Logger.h" +#include "qpid/client/Connection.h" +#include "ConnectionOptions.h" + +#include <iostream> +#include <exception> + +namespace qpid { + +struct TestOptions : public qpid::Options +{ + TestOptions(const std::string& helpText_=std::string(), + const std::string& argv0=std::string()) + : Options("Test Options"), help(false), log(argv0), helpText(helpText_) + { + addOptions() + ("help", optValue(help), "print this usage statement"); + add(con); + add(log); + } + + /** As well as parsing, throw help message if requested. */ + void parse(int argc, char** argv) { + try { + qpid::Options::parse(argc, argv); + } catch (const std::exception& e) { + std::ostringstream msg; + msg << *this << std::endl << std::endl << e.what() << std::endl; + throw qpid::Options::Exception(msg.str()); + } + qpid::log::Logger::instance().configure(log); + if (help) { + std::ostringstream msg; + msg << *this << std::endl << std::endl << helpText << std::endl; + throw qpid::Options::Exception(msg.str()); + } + } + + /** Open a connection using option values */ + void open(qpid::client::Connection& connection) { + connection.open(con); + } + + + bool help; + ConnectionOptions con; + qpid::log::Options log; + std::string helpText; +}; + +} + +#endif diff --git a/RC9/qpid/cpp/src/tests/TimerTest.cpp b/RC9/qpid/cpp/src/tests/TimerTest.cpp new file mode 100644 index 0000000000..50712ff79c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TimerTest.cpp @@ -0,0 +1,120 @@ + +/* + * + * 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/broker/Timer.h" +#include "qpid/sys/Monitor.h" +#include "unit_test.h" +#include <math.h> +#include <iostream> +#include <memory> +#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; + +class Counter +{ + Mutex lock; + uint counter; + public: + Counter() : counter(0) {} + uint next() + { + Mutex::ScopedLock l(lock); + return ++counter; + } +}; + +class TestTask : public TimerTask +{ + const AbsTime start; + const Duration expected; + AbsTime end; + bool fired; + uint position; + Monitor monitor; + Counter& counter; + + public: + TestTask(Duration timeout, Counter& _counter) + : TimerTask(timeout), start(now()), expected(timeout), end(start), fired(false), counter(_counter) {} + + void fire() + { + Monitor::ScopedLock l(monitor); + fired = true; + position = counter.next(); + end = now(); + monitor.notify(); + } + + void check(uint expected_position, uint64_t tolerance = 500 * TIME_MSEC) + { + Monitor::ScopedLock l(monitor); + BOOST_CHECK(fired); + BOOST_CHECK_EQUAL(expected_position, position); + Duration actual(start, end); + uint64_t difference = abs(expected - actual); + std::string msg(boost::lexical_cast<std::string>(boost::format("tolerance = %1%, difference = %2%") % tolerance % difference)); + BOOST_CHECK_MESSAGE(difference < tolerance, msg); + } + + void wait(Duration d) + { + Monitor::ScopedLock l(monitor); + monitor.wait(AbsTime(now(), d)); + } +}; + +class DummyRunner : public Runnable +{ + public: + void run() {} +}; + +QPID_AUTO_TEST_SUITE(TimerTestSuite) + +QPID_AUTO_TEST_CASE(testGeneral) +{ + Counter counter; + Timer timer; + intrusive_ptr<TestTask> task1(new TestTask(Duration(3 * TIME_SEC), counter)); + 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); + dynamic_pointer_cast<TestTask>(task4)->check(2); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TopicExchangeTest.cpp b/RC9/qpid/cpp/src/tests/TopicExchangeTest.cpp new file mode 100644 index 0000000000..af4263de34 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TopicExchangeTest.cpp @@ -0,0 +1,167 @@ +/* + * 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/broker/TopicExchange.h" +#include "unit_test.h" +#include "test_tools.h" + +using namespace qpid::broker; + +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))) + + +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); +} + +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) +{ + 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(".")); +} + + +QPID_AUTO_TEST_CASE(testStar) +{ + 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")); +} + +QPID_AUTO_TEST_CASE(testHash) +{ + 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")); +} + +QPID_AUTO_TEST_CASE(testMixed) +{ + 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")); +} + +QPID_AUTO_TEST_CASE(testCombo) +{ + 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")); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TxBufferTest.cpp b/RC9/qpid/cpp/src/tests/TxBufferTest.cpp new file mode 100644 index 0000000000..3d6a12cacc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TxBufferTest.cpp @@ -0,0 +1,176 @@ +/* + * + * 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/broker/TxBuffer.h" +#include "unit_test.h" +#include <iostream> +#include <vector> +#include "TxMocks.h" + +using namespace qpid::broker; +using boost::static_pointer_cast; + +QPID_AUTO_TEST_SUITE(TxBufferTestSuite) + +QPID_AUTO_TEST_CASE(testCommitLocal) +{ + MockTransactionalStore store; + store.expectBegin().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectPrepare().expectCommit().expectCommit();//opB enlisted twice to test relative order + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectPrepare().expectCommit(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opB));//opB enlisted twice + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(buffer.commitLocal(&store)); + store.check(); + BOOST_CHECK(store.isCommitted()); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnCommitLocal) +{ + MockTransactionalStore store; + store.expectBegin().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail + opC->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(!buffer.commitLocal(&store)); + BOOST_CHECK(store.isAborted()); + store.check(); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testPrepare) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectPrepare(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(buffer.prepare(0)); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testFailOnPrepare) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare(); + MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + BOOST_CHECK(!buffer.prepare(0)); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testRollback) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + buffer.rollback(); + opA->check(); + opB->check(); + opC->check(); +} + +QPID_AUTO_TEST_CASE(testBufferIsClearedAfterRollback) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + + buffer.rollback(); + buffer.commit();//second call should not reach ops + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_CASE(testBufferIsClearedAfterCommit) +{ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectCommit(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + + buffer.commit(); + buffer.rollback();//second call should not reach ops + opA->check(); + opB->check(); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/TxMocks.h b/RC9/qpid/cpp/src/tests/TxMocks.h new file mode 100644 index 0000000000..fe103c5fe5 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TxMocks.h @@ -0,0 +1,229 @@ +/* + * + * 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. + * + */ +#ifndef _tests_TxMocks_h +#define _tests_TxMocks_h + + +#include "qpid/Exception.h" +#include "qpid/broker/TransactionalStore.h" +#include "qpid/broker/TxOp.h" +#include <iostream> +#include <vector> + +using namespace qpid::broker; +using boost::static_pointer_cast; +using std::string; + +template <class T> void assertEqualVector(std::vector<T>& expected, std::vector<T>& actual){ + unsigned int i = 0; + while(i < expected.size() && i < actual.size()){ + BOOST_CHECK_EQUAL(expected[i], actual[i]); + i++; + } + if (i < expected.size()) { + throw qpid::Exception(QPID_MSG("Missing " << expected[i])); + } else if (i < actual.size()) { + throw qpid::Exception(QPID_MSG("Extra " << actual[i])); + } + BOOST_CHECK_EQUAL(expected.size(), actual.size()); +} + +class TxOpConstants{ +protected: + const string PREPARE; + const string COMMIT; + const string ROLLBACK; + + TxOpConstants() : PREPARE("PREPARE"), COMMIT("COMMIT"), ROLLBACK("ROLLBACK") {} +}; + +class MockTxOp : public TxOp, public TxOpConstants{ + std::vector<string> expected; + std::vector<string> actual; + bool failOnPrepare; + 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(){ + 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 << ", "; + std::cout << *i; + } + std::cout << std::endl; + } + + 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 << ", "; + std::cout << *i; + } + std::cout << std::endl; + } + + bool prepare(TransactionContext*) throw(){ + actual.push_back(PREPARE); + return !failOnPrepare; + } + void commit() throw(){ + actual.push_back(COMMIT); + } + void rollback() throw(){ + if(!debugName.empty()) std::cout << std::endl << "MockTxOp[" << debugName << "]::rollback()" << std::endl; + actual.push_back(ROLLBACK); + } + MockTxOp& expectPrepare(){ + expected.push_back(PREPARE); + return *this; + } + MockTxOp& expectCommit(){ + expected.push_back(COMMIT); + return *this; + } + MockTxOp& expectRollback(){ + expected.push_back(ROLLBACK); + return *this; + } + void check(){ + assertEqualVector(expected, actual); + } + + void accept(TxOpConstVisitor&) const {} + + ~MockTxOp(){} +}; + +class MockTransactionalStore : public TransactionalStore{ + const string BEGIN; + const string BEGIN2PC; + const string PREPARE; + const string COMMIT; + 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: + TestTransactionContext(MockTransactionalStore* _store) : store(_store) {} + void prepare(){ + if(!store->isOpen()) throw "txn already completed"; + store->state = PREPARED; + } + + void commit(){ + 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"; + } + + 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(){ + actual.push_back(BEGIN); + std::auto_ptr<TransactionContext> txn(new TestTransactionContext(this)); + return txn; + } + void prepare(TPCTransactionContext& ctxt){ + actual.push_back(PREPARE); + dynamic_cast<TestTransactionContext&>(ctxt).prepare(); + } + void commit(TransactionContext& ctxt){ + actual.push_back(COMMIT); + dynamic_cast<TestTransactionContext&>(ctxt).commit(); + } + void abort(TransactionContext& ctxt){ + actual.push_back(ABORT); + dynamic_cast<TestTransactionContext&>(ctxt).abort(); + } + MockTransactionalStore& expectBegin(){ + expected.push_back(BEGIN); + return *this; + } + MockTransactionalStore& expectBegin2PC(){ + expected.push_back(BEGIN2PC); + return *this; + } + MockTransactionalStore& expectPrepare(){ + expected.push_back(PREPARE); + return *this; + } + MockTransactionalStore& expectCommit(){ + expected.push_back(COMMIT); + return *this; + } + MockTransactionalStore& expectAbort(){ + expected.push_back(ABORT); + return *this; + } + 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(){} +}; + +#endif diff --git a/RC9/qpid/cpp/src/tests/TxPublishTest.cpp b/RC9/qpid/cpp/src/tests/TxPublishTest.cpp new file mode 100644 index 0000000000..9e9715c987 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/TxPublishTest.cpp @@ -0,0 +1,94 @@ +/* + * + * 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/broker/NullMessageStore.h" +#include "qpid/broker/RecoveryManager.h" +#include "qpid/broker/TxPublish.h" +#include "unit_test.h" +#include <iostream> +#include <list> +#include <vector> +#include "MessageUtils.h" +#include "TestMessageStore.h" + +using std::list; +using std::pair; +using std::vector; +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; + +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")), + 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); + //ensure messages are enqueued in store + t.op.prepare(0); + BOOST_CHECK_EQUAL((size_t) 2, t.store.enqueued.size()); + BOOST_CHECK_EQUAL(string("queue1"), t.store.enqueued[0].first); + 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()); +} + +QPID_AUTO_TEST_CASE(testCommit) +{ + TxPublishTest t; + + //ensure messages are delivered to queue + t.op.prepare(0); + t.op.commit(); + 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(t.msg, msg_dequeue); + + BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount()); + BOOST_CHECK_EQUAL(t.msg, t.queue2->get().payload); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/Url.cpp b/RC9/qpid/cpp/src/tests/Url.cpp new file mode 100644 index 0000000000..f3b42a7208 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Url.cpp @@ -0,0 +1,67 @@ +/* + * + * 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/Url.h" +#include <boost/assign.hpp> + +using namespace std; +using namespace qpid; +using namespace boost::assign; + +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(TestParseTcp) { + URL_CHECK_STR("amqp:tcp:host:42"); + URL_CHECK_STR("amqp:tcp:host-._~%ff%23:42"); // unreserved chars and pct encoded hex. + + // 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_CHECK_INVALID("amqp::badHost!#$#"); + URL_CHECK_INVALID("amqp::host:badPort"); +} + +QPID_AUTO_TEST_CASE(TestParseExample) { + URL_CHECK_STR("amqp:example:x"); + URL_CHECK_INVALID("amqp:example:badExample"); +} + +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() diff --git a/RC9/qpid/cpp/src/tests/Uuid.cpp b/RC9/qpid/cpp/src/tests/Uuid.cpp new file mode 100644 index 0000000000..ee86d75a26 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/Uuid.cpp @@ -0,0 +1,79 @@ +/* + * + * 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/framing/Uuid.h" +#include "qpid/framing/Buffer.h" + +#include "unit_test.h" + +#include <set> +#include <alloca.h> + +QPID_AUTO_TEST_SUITE(UuidTestSuite) + +using namespace std; +using namespace qpid::framing; + +struct UniqueSet : public std::set<Uuid> { + void operator()(const Uuid& uuid) { + BOOST_REQUIRE(find(uuid) == end()); + insert(uuid); + } +}; + +QPID_AUTO_TEST_CASE(testUuidCtor) { + // Uniqueness + boost::array<Uuid,1000> uuids; + for_each(uuids.begin(), uuids.end(), mem_fun_ref(&Uuid::generate)); + UniqueSet unique; + for_each(uuids.begin(), uuids.end(), unique); +} + +boost::array<uint8_t, 16> sample = {{'\x1b', '\x4e', '\x28', '\xba', '\x2f', '\xa1', '\x11', '\xd2', '\x88', '\x3f', '\xb9', '\xa7', '\x61', '\xbd', '\xe3', '\xfb'}}; +const string sampleStr("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb"); + +QPID_AUTO_TEST_CASE(testUuidIstream) { + Uuid uuid; + istringstream in(sampleStr); + in >> uuid; + BOOST_CHECK(!in.fail()); + BOOST_CHECK(uuid == sample); +} + +QPID_AUTO_TEST_CASE(testUuidOstream) { + Uuid uuid(sample.c_array()); + ostringstream out; + out << uuid; + BOOST_CHECK(out.good()); + BOOST_CHECK_EQUAL(out.str(), sampleStr); +} + +QPID_AUTO_TEST_CASE(testUuidEncodeDecode) { + char* buff = static_cast<char*>(::alloca(Uuid::size())); + Buffer wbuf(buff, Uuid::size()); + Uuid uuid(sample.c_array()); + uuid.encode(wbuf); + + Buffer rbuf(buff, Uuid::size()); + Uuid decoded; + decoded.decode(rbuf); + BOOST_CHECK_EQUAL(string(sample.begin(), sample.end()), + string(decoded.begin(), decoded.end())); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/XmlClientSessionTest.cpp b/RC9/qpid/cpp/src/tests/XmlClientSessionTest.cpp new file mode 100644 index 0000000000..98558f0a76 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/XmlClientSessionTest.cpp @@ -0,0 +1,221 @@ +/* + * + * Licensed to the Apachef 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/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/framing/reply_exceptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/LocalQueue.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" + +#include <boost/optional.hpp> +#include <boost/lexical_cast.hpp> + +#include <vector> + +QPID_AUTO_TEST_SUITE(XmlClientSessionTest) + +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; + +Shlib shlib("../.libs/xml.so"); + +class SubscribedLocalQueue : public LocalQueue { + private: + SubscriptionManager& subscriptions; + public: + SubscribedLocalQueue(SubscriptionManager& subs) : subscriptions(subs) {} + Message get () { return pop(); } + virtual ~SubscribedLocalQueue() {} +}; + + +struct SimpleListener : public MessageListener +{ + Monitor lock; + std::vector<Message> messages; + + void received(Message& msg) + { + Monitor::ScopedLock l(lock); + messages.push_back(msg); + lock.notifyAll(); + } + + void waitFor(const uint n) + { + Monitor::ScopedLock l(lock); + while (messages.size() < n) { + lock.wait(); + } + } +}; + +struct ClientSessionFixture : public ProxySessionFixture +{ + void declareSubscribe(const string& q="odd_blue", + const string& dest="xml") + { + session.queueDeclare(queue=q); + session.messageSubscribe(queue=q, destination=dest, acquireMode=1); + session.messageFlow(destination=dest, unit=0, value=0xFFFFFFFF);//messages + session.messageFlow(destination=dest, unit=1, value=0xFFFFFFFF);//bytes + } +}; + +// ########### START HERE #################################### + + + +QPID_AUTO_TEST_CASE(testXmlBinding) { + 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"); + + message.getHeaders().setString("color", "blue"); + string m = "<message><id>1</id></message>"; + message.setData(m); + + f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml"); + + 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; + + 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-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); + + Message sent1("<>colour>blue</colour>", "by-colour"); + f.session.messageTransfer(arg::content=sent1, arg::destination="xml"); + + BOOST_CHECK_EQUAL(1, 1); +} + + +//### 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() + diff --git a/RC9/qpid/cpp/src/tests/acl.py b/RC9/qpid/cpp/src/tests/acl.py new file mode 100755 index 0000000000..671b2fe247 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/acl.py @@ -0,0 +1,459 @@ +#!/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, testrunner +from qmf.console import Session +from qpid.datatypes import Message + +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 get_broker_port(): + return scan_args("--port", "5672") + +def get_session(user, passwd): + socket = connect('127.0.0.1', int(get_broker_port())) + connection = Connection (sock=socket, username=user, password=passwd) + connection.start() + return connection.session(str(uuid4())) + +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 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_all(self): + """ + Test the deny all mode + """ + aclf = ACLFile() + aclf.write('acl allow guest@QPID all all\n') + aclf.write('acl allow bob@QPID create queue\n') + aclf.write('acl deny all all') + aclf.close() + + self.reload_acl() + + session = 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_all(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() + + self.reload_acl() + + session = 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) + + + + #===================================== + # ACL queue tests + #===================================== + + def test_queue_acl(self): + """ + Test various modes for queue acl + """ + 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\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 allow all all') + aclf.close() + + self.reload_acl() + + session = 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 = get_session('bob','bob') + + try: + session.queue_declare(queue="q2", exclusive='true') + self.fail("ACL should deny queue create request with name=q2 exclusive=true"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + 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 = 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 = 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 = 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"); + + #===================================== + # ACL exchange tests + #===================================== + + def test_exchange_acl(self): + """ + Test various modes for exchange acl + """ + 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\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() + + self.reload_acl() + + session = get_session('bob','bob') + + 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 = get_session('bob','bob') + + 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 = get_session('bob','bob') + + try: + session.exchange_declare(exchange='myXml', type='direct') + session.queue_declare(queue='q1') + 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 queue query request for q3"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + 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 = get_session('bob','bob') + + try: + session.exchange_bind(exchange='myXml', 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='myXml', queue='q1', binding_key='x'"); + 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 = get_session('bob','bob') + + try: + session.exchange_unbind(exchange='myXml', 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='myXml', queue='q1', binding_key='x'"); + + 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 = 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"); + + + #===================================== + # ACL consume tests + #===================================== + + def test_consume_acl(self): + """ + Test various consume acl + """ + aclf = ACLFile() + aclf.write('acl deny bob@QPID consume queue name=q1 durable=true\n') + aclf.write('acl deny bob@QPID consume queue name=q2 exclusive=true\n') + aclf.write('acl allow all all') + aclf.close() + + self.reload_acl() + + session = get_session('bob','bob') + + + try: + session.queue_declare(queue='q1', durable='true') + session.queue_declare(queue='q2', exclusive='true') + session.queue_declare(queue='q3', durable='true') + 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 message subscriber request for queue='q1'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = get_session('bob','bob') + + try: + session.message_subscribe(queue='q2', destination='myq1') + self.fail("ACL should deny message subscriber request for queue='q2'"); + except qpid.session.SessionException, e: + self.assertEqual(530,e.args[0].error_code) + session = 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 create message subscribe"); + + + #===================================== + # ACL publish tests + #===================================== + + def test_publish_acl(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() + + self.reload_acl() + + session = get_session('bob','bob') + + try: + session.exchange_declare(exchange='myEx', type='topic') + except qpid.session.SessionException, e: + if (530 == e.args[0].error_code): + self.fail("ACL should allow exchange create request for myEx with any parameter"); + + 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 = 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 = get_session('bob','bob') + + try: + 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"); + + +if __name__ == '__main__': + args = sys.argv[1:] + #need to remove the extra options from args as test runner doesn't recognize them + extract_args("--port", args) + args.append("acl") + + if not testrunner.run(args): sys.exit(1) diff --git a/RC9/qpid/cpp/src/tests/ais_check b/RC9/qpid/cpp/src/tests/ais_check new file mode 100755 index 0000000000..5687110165 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ais_check @@ -0,0 +1,56 @@ +#!/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` + +# Check AIS requirements and run tests if found. +id -nG | grep '\<ais\>' >/dev/null || \ + NOGROUP="You are not a member of the ais group." +ps -u root | grep 'aisexec\|corosync' >/dev/null || \ + NOAISEXEC="The aisexec or corosync 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: + + $NOGROUP + $NOAISEXEC + + ========================================================== + +EOF + exit 0; # A warning, not a failure. +fi + +# 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."; exit 1; } + echo $* | newgrp ais +} + +# Run the tests +srcdir=`dirname $0` +with_ais_group $srcdir/run_test ./cluster_test || ERROR=1 +exit $ERROR + diff --git a/RC9/qpid/cpp/src/tests/ais_test.cpp b/RC9/qpid/cpp/src/tests/ais_test.cpp new file mode 100644 index 0000000000..00c61242e4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ais_test.cpp @@ -0,0 +1,23 @@ +/* + * + * 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. + * + */ + +// Defines test_main function to link with actual unit test code. +#define BOOST_AUTO_TEST_MAIN // Boost 1.33 +#define BOOST_TEST_MAIN +#include "unit_test.h" + diff --git a/RC9/qpid/cpp/src/tests/allSegmentTypes.h b/RC9/qpid/cpp/src/tests/allSegmentTypes.h new file mode 100644 index 0000000000..e942250c89 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/allSegmentTypes.h @@ -0,0 +1,128 @@ +#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/RC9/qpid/cpp/src/tests/amqp_0_10/Map.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/Map.cpp new file mode 100644 index 0000000000..efde967050 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/Map.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 "unit_test.h" +#include "qpid/amqp_0_10/Map.h" +#include "qpid/amqp_0_10/Array.h" +#include "qpid/amqp_0_10/Struct32.h" +#include "qpid/amqp_0_10/UnknownType.h" +#include "qpid/amqp_0_10/Codec.h" +#include <iostream> + +using namespace qpid::amqp_0_10; +using namespace std; + +QPID_AUTO_TEST_SUITE(MapTestSuite) + + QPID_AUTO_TEST_CASE(testGetSet) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK(v.get<Str8>()); + BOOST_CHECK(!v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo"); + + v = uint8_t(42); + BOOST_CHECK(!v.get<Str8>()); + BOOST_CHECK(v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42); + + v = uint16_t(12); + BOOST_CHECK(v.get<uint16_t>()); + BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12); +} + +template <class R> struct TestVisitor : public MapValue::Visitor<R> { + template <class T> R operator()(const T&) const { throw MapValue::BadTypeException(); } + R operator()(const R& r) const { return r; } +}; + +QPID_AUTO_TEST_CASE(testVisit) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Str8>()), "foo"); + v = Uint16(42); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42); + try { + v.apply_visitor(TestVisitor<bool>()); + BOOST_FAIL("Expecting exception"); + } + catch(const MapValue::BadTypeException&) {} +} + + +QPID_AUTO_TEST_CASE(testEncodeMapValue) { + MapValue mv; + std::string data; + mv = Str8("hello"); + Codec::encode(back_inserter(data))(mv); + BOOST_CHECK_EQUAL(data.size(), Codec::size(mv)); + MapValue mv2; + Codec::decode(data.begin())(mv2); + BOOST_CHECK_EQUAL(mv2.getCode(), 0x85); + BOOST_REQUIRE(mv2.get<Str8>()); + BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "hello"); +} + +QPID_AUTO_TEST_CASE(testEncode) { + Map map; + std::string data; + map["A"] = true; + map["b"] = Str8("hello"); + Codec::encode(back_inserter(data))(map); + BOOST_CHECK_EQUAL(Codec::size(map), data.size()); + Map map2; + Codec::decode(data.begin())(map2); + BOOST_CHECK_EQUAL(map.size(), 2u); + BOOST_CHECK(map["A"].get<bool>()); + BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "hello"); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp new file mode 100644 index 0000000000..9d4fcb8935 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp @@ -0,0 +1,49 @@ +/* + * + * 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/amqp_0_10/ProxyTemplate.h" +#include <boost/any.hpp> + +QPID_AUTO_TEST_SUITE(ProxyTemplateTestSuite) + +using namespace qpid::amqp_0_10; + +struct ToAny { + template <class T> + boost::any operator()(const T& t) { return boost::any(t); } +}; + +struct AnyProxy : public ProxyTemplate<ToAny, boost::any> {}; + +QPID_AUTO_TEST_CASE(testAnyProxy) { + AnyProxy p; + boost::any a=p.connectionTune(1,2,3,4); + BOOST_CHECK_EQUAL(a.type().name(), typeid(connection::Tune).name()); + connection::Tune* tune=boost::any_cast<connection::Tune>(&a); + BOOST_REQUIRE(tune); + BOOST_CHECK_EQUAL(tune->channelMax, 1u); + BOOST_CHECK_EQUAL(tune->maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune->heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune->heartbeatMax, 4u); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/apply.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/apply.cpp new file mode 100644 index 0000000000..5a67c28c79 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/apply.cpp @@ -0,0 +1,99 @@ +/* + * + * 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/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/ApplyControl.h" + +QPID_AUTO_TEST_SUITE(VisitorTestSuite) + +using namespace qpid::amqp_0_10; + +struct GetCode : public ApplyFunctor<uint8_t> { + template <class T> uint8_t operator()(const T&) const { return T::CODE; } +}; + +struct SetChannelMax : ApplyFunctor<void> { + template <class T> void operator()(T&) const { BOOST_FAIL(""); } + void operator()(connection::Tune& t) const { t.channelMax=42; } +}; + +struct TestFunctor { + typedef bool result_type; + bool operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + return true; + } + template <class T> + bool operator()(const T&) { return false; } +}; + +QPID_AUTO_TEST_CASE(testApply) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + + // boost oddity - without the cast we get undefined symbol errors. + BOOST_CHECK_EQUAL(apply(GetCode(), *p), (uint8_t)connection::Tune::CODE); + + TestFunctor tf; + BOOST_CHECK(apply(tf, *p)); + + connection::Start start; + p = &start; + BOOST_CHECK(!apply(tf, *p)); + + apply(SetChannelMax(), tune); + BOOST_CHECK_EQUAL(tune.channelMax, 42); +} + +struct VoidTestFunctor { + typedef void result_type; + + int code; + VoidTestFunctor() : code() {} + + void operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + code=connection::Tune::CODE; + } + template <class T> + void operator()(const T&) { code=0xFF; } +}; + +QPID_AUTO_TEST_CASE(testApplyVoid) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + VoidTestFunctor tf; + apply(tf, *p); + BOOST_CHECK_EQUAL(uint8_t(connection::Tune::CODE), tf.code); + + connection::Start start; + p = &start; + apply(tf, *p); + BOOST_CHECK_EQUAL(0xFF, tf.code); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/handlers.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/handlers.cpp new file mode 100644 index 0000000000..428643c802 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/handlers.cpp @@ -0,0 +1,125 @@ +/* + * + * 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/Exception.h" +#include "qpid/amqp_0_10/Unit.h" +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/CommandHolder.h" +#include "qpid/amqp_0_10/handlers.h" +#include "qpid/amqp_0_10/specification.h" + +QPID_AUTO_TEST_SUITE(handler_tests) + +using namespace qpid::amqp_0_10; +using namespace std; + +string called; // Set by called handler function + +// Note on handlers: +// +// Control and Command handlers are separate, both behave the same way, +// so substitute "control or command" for command in the following. +// +// Command handlers derive from CommandHandler and implement functions +// for all the commands they handle. Handling an unimplemented command +// will raise NotImplementedException. +// +// Using virtual inheritance from CommandHandler allows multiple +// handlers to be aggregated into one with multiple inheritance, +// See test code for example. +// +// E.g. the existing broker model would have two control handlers: +// - ConnectionHandler: ControlHandler for connection controls. +// - SessionHandler: ControlHandler for session controls. +// It would have class-command handlers for each AMQP class: +// - QueueHandler, MessageHandler etc.. handle each class. +// And an aggregate handler in place of BrokerAdapter +// - BrokerCommandHandler: public QueueHandler, MessageHandler ... +// +// In other applications (e.g. cluster) any combination of commands +// can be handled by a given handler. It _might_ simplify the code +// to collaps ConnectionHandler and SessionHandler into a single +// ControlHandler (or it might not.) + +struct TestExecutionHandler : public virtual CommandHandler { + void executionSync() { called = "executionSync"; } + // ... etc. for all execution commands +}; + +struct TestMessageHandler : public virtual CommandHandler { + void messageCancel(const Str8&) { called="messageCancel"; } + // ... etc. +}; + +// Aggregate handler for all recognised commands. +struct TestCommandHandler : + public TestExecutionHandler, + public TestMessageHandler + // ... etc. handlers for all command classes. +{}; // Nothing to do. + + +// Sample unit handler, written as a static_visitor. +// Note it could equally be written with if/else statements +// in handle. +// +struct TestUnitHandler : public boost::static_visitor<void> { + TestCommandHandler handler; + void handle(const Unit& u) { u.applyVisitor(*this); } + + void operator()(const Body&) { called="Body"; } + void operator()(const Header&) { called="Header"; } + void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); } + void operator()(const CommandHolder& c) { c.invoke(handler); } +}; + +QPID_AUTO_TEST_CASE(testHandlers) { + TestUnitHandler handler; + Unit u; + + u = Body(); + handler.handle(u); + BOOST_CHECK_EQUAL("Body", called); + + u = Header(); + handler.handle(u); + BOOST_CHECK_EQUAL("Header", called); + + // in_place<Foo>(...) is equivalent to Foo(...) but + // constructs Foo directly in the holder, avoiding + // a copy. + + u = CommandHolder(in_place<execution::Sync>()); + handler.handle(u); + BOOST_CHECK_EQUAL("executionSync", called); + + u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array())); + try { + handler.handle(u); + } catch (const qpid::Exception&) {} + + u = CommandHolder(in_place<message::Cancel>(Str8())); + handler.handle(u); + BOOST_CHECK_EQUAL("messageCancel", called); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/amqp_0_10/serialize.cpp b/RC9/qpid/cpp/src/tests/amqp_0_10/serialize.cpp new file mode 100644 index 0000000000..0cfeb8d28d --- /dev/null +++ b/RC9/qpid/cpp/src/tests/amqp_0_10/serialize.cpp @@ -0,0 +1,429 @@ +/* + * + * 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 "allSegmentTypes.h" + +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/Buffer.h" + +#include "qpid/amqp_0_10/Packer.h" +#include "qpid/amqp_0_10/built_in_types.h" +#include "qpid/amqp_0_10/Codec.h" +#include "qpid/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/Struct32.h" +#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 <boost/test/test_case_template.hpp> +#include <boost/type_traits/is_arithmetic.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/optional.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/mpl/back_inserter.hpp> +#include <boost/mpl/copy.hpp> +#include <boost/mpl/empty_sequence.hpp> +#include <boost/current_function.hpp> +#include <iterator> +#include <string> +#include <sstream> +#include <iostream> +#include <netinet/in.h> + +// Missing operators needed for tests. +namespace boost { +template <class T, size_t N> +std::ostream& operator<<(std::ostream& out, const array<T,N>& a) { + std::ostream_iterator<T> o(out, " "); + std::copy(a.begin(), a.end(), o); + return out; +} +} // boost + +QPID_AUTO_TEST_SUITE(SerializeTestSuite) + +using namespace std; +namespace mpl=boost::mpl; +using namespace qpid::amqp_0_10; +using qpid::framing::in_place; + +template <class A, class B> struct concat2 { typedef typename mpl::copy<B, typename mpl::back_inserter<A> >::type type; }; +template <class A, class B, class C> struct concat3 { typedef typename concat2<A, typename concat2<B, C>::type>::type type; }; +template <class A, class B, class C, class D> struct concat4 { typedef typename concat2<A, typename concat3<B, C, D>::type>::type type; }; + +typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes; +typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, Bin64, Bin72>::type BinTypes; +typedef mpl::vector<Double, Float>::type FloatTypes; +typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> FixedSizeClassTypes; +typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes; + +typedef concat4<IntegralTypes, BinTypes, FloatTypes, FixedSizeClassTypes>::type FixedSizeTypes; +typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes; + +// TODO aconway 2008-02-20: should test 64 bit integrals for order also. +QPID_AUTO_TEST_CASE(testNetworkByteOrder) { + string data; + + uint32_t l = 0x11223344; + Codec::encode(std::back_inserter(data))(l); + uint32_t enc=reinterpret_cast<const uint32_t&>(*data.data()); + uint32_t l2 = ntohl(enc); + BOOST_CHECK_EQUAL(l, l2); + + data.clear(); + uint16_t s = 0x1122; + Codec::encode(std::back_inserter(data))(s); + uint32_t s2 = ntohs(*reinterpret_cast<const uint32_t*>(data.data())); + BOOST_CHECK_EQUAL(s, s2); +} + +QPID_AUTO_TEST_CASE(testSetLimit) { + typedef Codec::Encoder<back_insert_iterator<string> > Encoder; + string data; + Encoder encode(back_inserter(data), 3); + encode('1')('2')('3'); + try { + encode('4'); + BOOST_FAIL("Expected exception"); + } catch (...) {} // FIXME aconway 2008-04-03: catch proper exception + BOOST_CHECK_EQUAL(data, "123"); +} + +QPID_AUTO_TEST_CASE(testScopedLimit) { + typedef Codec::Encoder<back_insert_iterator<string> > Encoder; + string data; + Encoder encode(back_inserter(data), 10); + encode(Str8("123")); // 4 bytes + { + Encoder::ScopedLimit l(encode, 3); + encode('a')('b')('c'); + try { + encode('d'); + BOOST_FAIL("Expected exception"); + } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception + } + BOOST_CHECK_EQUAL(data, "\003123abc"); + encode('x')('y')('z'); + try { + encode('!'); + BOOST_FAIL("Expected exception"); + } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception + BOOST_CHECK_EQUAL(data.size(), 10u); +} + +// Assign test values to the various types. +void testValue(bool& b) { b = true; } +void testValue(Bit&) { } +template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type testValue(T& n) { n=42; } +void testValue(CharUtf32& c) { c = 43; } +void testValue(long long& l) { l = 0x012345; } +void testValue(Datetime& dt) { dt = qpid::sys::now(); } +void testValue(Uuid& uuid) { uuid=Uuid(true); } +template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; } +void testValue(SequenceNo& s) { s = 42; } +template <size_t N> void testValue(Bin<N>& a) { a.assign(42); } +template <class T, class S, int Unique> void testValue(SerializableString<T, S, Unique>& s) { + char msg[]="foobar"; + s.assign(msg, msg+sizeof(msg)); +} +void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; } +void testValue(Str8& s) { s = "foobar"; } +void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = uint16_t(42); } + +//typedef mpl::vector<Str8, Str16>::type TestTypes; +/*BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes) +{ + string data; + T t; + testValue(t); + Codec::encode(std::back_inserter(data))(t); + + BOOST_CHECK_EQUAL(Codec::size(t), data.size()); + + T t2; + Codec::decode(data.begin())(t2); + BOOST_CHECK_EQUAL(t,t2); +} +*/ + +struct TestMe { + bool encoded, decoded; + char value; + TestMe(char v) : encoded(), decoded(), value(v) {} + template <class S> void encode(S& s) const { + const_cast<TestMe*>(this)->encoded=true; s(value); + } + template <class S> void decode(S& s) { decoded=true; s(value); } + template <class S> void serialize(S& s) { s.split(*this); } +}; + +QPID_AUTO_TEST_CASE(testSplit) { + string data; + TestMe t1('x'); + Codec::encode(std::back_inserter(data))(t1); + BOOST_CHECK(t1.encoded); + BOOST_CHECK(!t1.decoded); + BOOST_CHECK_EQUAL(data, "x"); + + TestMe t2('y'); + Codec::decode(data.begin())(t2); + BOOST_CHECK(!t2.encoded); + BOOST_CHECK(t2.decoded); + BOOST_CHECK_EQUAL(t2.value, 'x'); +} + +QPID_AUTO_TEST_CASE(testControlEncodeDecode) { + string data; + Control::Holder h(in_place<connection::Tune>(1,2,3,4)); + Codec::encode(std::back_inserter(data))(h); + + BOOST_CHECK_EQUAL(data.size(), Codec::size(h)); + + Codec::Decoder<string::iterator> decode(data.begin()); + Control::Holder h2; + decode(h2); + + BOOST_REQUIRE(h2.get()); + BOOST_CHECK_EQUAL(h2.get()->getClassCode(), connection::CODE); + BOOST_CHECK_EQUAL(h2.get()->getCode(), uint8_t(connection::Tune::CODE)); + connection::Tune& tune=static_cast<connection::Tune&>(*h2.get()); + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); +} + +QPID_AUTO_TEST_CASE(testStruct32) { + message::DeliveryProperties dp; + dp.priority=message::MEDIUM; + dp.routingKey="foo"; + Struct32 s(dp); + string data; + Codec::encode(back_inserter(data))(s); + + uint32_t structSize; // Starts with size + Codec::decode(data.begin())(structSize); + BOOST_CHECK_EQUAL(structSize, Codec::size(dp) + 2); // +2 for code + BOOST_CHECK_EQUAL(structSize, data.size()-4); // encoded body + + BOOST_CHECK_EQUAL(data.size(), Codec::size(s)); + Struct32 s2; + Codec::decode(data.begin())(s2); + message::DeliveryProperties* dp2 = s2.getIf<message::DeliveryProperties>(); + BOOST_REQUIRE(dp2); + BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM); + BOOST_CHECK_EQUAL(dp2->routingKey, "foo"); +} + +QPID_AUTO_TEST_CASE(testStruct32Unknown) { + // Verify we can recode an unknown struct unchanged. + Struct32 s; + string data; + Codec::encode(back_inserter(data))(uint32_t(10)); + data.append(10, 'X'); + Codec::decode(data.begin())(s); + string data2; + Codec::encode(back_inserter(data2))(s); + BOOST_CHECK_EQUAL(data.size(), data2.size()); + BOOST_CHECK_EQUAL(data, data2); +} + +struct DummyPacked { + static const uint8_t PACK=1; + boost::optional<char> i, j; + char k; + Bit l,m; + DummyPacked(char a=0, char b=0, char c=0) : i(a), j(b), k(c), l(), m() {} + template <class S> void serialize(S& s) { s(i)(j)(k)(l)(m); } +}; + +Packer<DummyPacked> serializable(DummyPacked& d) { return Packer<DummyPacked>(d); } + +QPID_AUTO_TEST_CASE(testPackBits) { + DummyPacked d('a','b','c'); + BOOST_CHECK_EQUAL(packBits(d), 7u); + d.j = boost::none; + BOOST_CHECK_EQUAL(packBits(d), 5u); + d.m = true; + BOOST_CHECK_EQUAL(packBits(d), 0x15u); +} + + +QPID_AUTO_TEST_CASE(testPacked) { + string data; + + Codec::encode(back_inserter(data))('a')(boost::optional<char>('b'))(boost::optional<char>())('c'); + BOOST_CHECK_EQUAL(data, "abc"); + data.clear(); + + DummyPacked dummy('a','b','c'); + + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data.size(), 4u); + BOOST_CHECK_EQUAL(data, string("\007abc")); + data.clear(); + + dummy.i = boost::none; + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data, string("\6bc")); + data.clear(); + + const char* missing = "\5xy"; + Codec::decode(missing)(dummy); + BOOST_CHECK(dummy.i); + BOOST_CHECK_EQUAL(*dummy.i, 'x'); + BOOST_CHECK(!dummy.j); + BOOST_CHECK_EQUAL(dummy.k, 'y'); +} + +QPID_AUTO_TEST_CASE(testUnitControl) { + string data; + Control::Holder h(in_place<connection::Tune>(1,2,3,4)); + Codec::encode(std::back_inserter(data))(h); + + Unit unit(FrameHeader(FIRST_FRAME|LAST_FRAME, CONTROL)); + Codec::decode(data.begin())(unit); + + BOOST_REQUIRE(unit.get<ControlHolder>()); + + string data2; + Codec::encode(back_inserter(data2))(unit); + + BOOST_CHECK_EQUAL(data, data2); +} + +QPID_AUTO_TEST_CASE(testArray) { + ArrayDomain<char> a; + a.resize(3, 'x'); + string data; + Codec::encode(back_inserter(data))(a); + + ArrayDomain<char> b; + Codec::decode(data.begin())(b); + BOOST_CHECK_EQUAL(b.size(), 3u); + string data3; + Codec::encode(back_inserter(data3))(a); + BOOST_CHECK_EQUAL(data, data3); + + Array x; + Codec::decode(data.begin())(x); + BOOST_CHECK_EQUAL(x.size(), 3u); + BOOST_CHECK_EQUAL(x[0].size(), 1u); + BOOST_CHECK_EQUAL(*x[0].begin(), 'x'); + BOOST_CHECK_EQUAL(*x[2].begin(), 'x'); + + string data2; + Codec::encode(back_inserter(data2))(x); + BOOST_CHECK_EQUAL(data,data2); +} + +QPID_AUTO_TEST_CASE(testStruct) { + string data; + + message::DeliveryProperties dp; + BOOST_CHECK(!dp.discardUnroutable); + dp.immediate = true; + dp.redelivered = false; + dp.priority = message::MEDIUM; + dp.exchange = "foo"; + + Codec::encode(back_inserter(data))(dp); + // Skip 4 bytes size, little-endian decode for pack bits. + uint16_t encodedBits=uint8_t(data[5]); + encodedBits <<= 8; + encodedBits += uint8_t(data[4]); + BOOST_CHECK_EQUAL(encodedBits, packBits(dp)); + + data.clear(); + Struct32 h(dp); + Codec::encode(back_inserter(data))(h); + + Struct32 h2; + Codec::decode(data.begin())(h2); + BOOST_CHECK_EQUAL(h2.getClassCode(), Uint8(message::DeliveryProperties::CLASS_CODE)); + BOOST_CHECK_EQUAL(h2.getCode(), Uint8(message::DeliveryProperties::CODE)); + message::DeliveryProperties* dp2 = + dynamic_cast<message::DeliveryProperties*>(h2.get()); + BOOST_CHECK(dp2); + BOOST_CHECK(!dp2->discardUnroutable); + BOOST_CHECK(dp2->immediate); + BOOST_CHECK(!dp2->redelivered); + BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM); + BOOST_CHECK_EQUAL(dp2->exchange, "foo"); +} + +struct RecodeUnit { + template <class T> + void operator() (const T& t) { + BOOST_MESSAGE(BOOST_CURRENT_FUNCTION << " called with: " << t); + using qpid::framing::Buffer; + using qpid::framing::AMQFrame; + + session::Header sh; + BOOST_CHECK_EQUAL(Codec::size(sh), 2u); + + // Encode unit. + Unit u(t); + string data; + Codec::encode(back_inserter(data))(u.getHeader())(u); + data.push_back(char(0xCE)); // Preview end-of-frame + + // Decode AMQFrame + Buffer buf(&data[0], data.size()); + AMQFrame f; + f.decode(buf); + BOOST_MESSAGE("AMQFrame decoded: " << f); + // Encode AMQFrame + string data2(f.size(), ' '); + Buffer buf2(&data2[0], data.size()); + f.encode(buf2); + + // Verify encoded by unit == encoded by AMQFrame + BOOST_CHECK_MESSAGE(data == data2, BOOST_CURRENT_FUNCTION); + + // Decode unit + // FIXME aconway 2008-04-15: must set limit to decode a header. + Codec::Decoder<string::iterator> decode(data2.begin(), data2.size()-1); + + FrameHeader h; + decode(h); + BOOST_CHECK_EQUAL(u.getHeader(), h); + Unit u2(h); + decode(u2); + + // Re-encode unit + string data3; + Codec::encode(back_inserter(data3))(u2.getHeader())(u2); + data3.push_back(char(0xCE)); // Preview end-of-frame + + BOOST_CHECK_MESSAGE(data3 == data2, BOOST_CURRENT_FUNCTION); + } +}; + +QPID_AUTO_TEST_CASE(testSerializeAllSegmentTypes) { + RecodeUnit recode; + allSegmentTypes(recode); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/benchmark b/RC9/qpid/cpp/src/tests/benchmark new file mode 100755 index 0000000000..c075837847 --- /dev/null +++ b/RC9/qpid/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/RC9/qpid/cpp/src/tests/client_test.cpp b/RC9/qpid/cpp/src/tests/client_test.cpp new file mode 100644 index 0000000000..204c2c4b71 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/client_test.cpp @@ -0,0 +1,149 @@ +/* + * + * 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 provides a simple test (and example) of basic + * functionality including declaring an exchange and a queue, binding + * these together, publishing a message and receiving that message + * asynchronously. + */ + +#include <iostream> + +#include "TestOptions.h" +#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" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; +using std::string; + +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() + ("size", optValue(msgSize, "N"), "message size") + ("verbose", optValue(verbose), "print out some status messages"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +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; + } + data += chars.substr(0, size % chars.length()); + return data; +} + +void print(const std::string& text, const Message& msg) +{ + std::cout << text; + if (msg.getData().size() > 16) { + std::cout << msg.getData().substr(0, 16) << "..."; + } else { + std::cout << msg.getData(); + } + std::cout << std::endl; +} + +int main(int argc, char** argv) +{ + try { + Args opts; + opts.parse(argc, argv); + + //Connect to the broker: + Connection connection; + opts.open(connection); + if (opts.verbose) std::cout << "Opened connection." << std::endl; + + //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; + + + //'declare' the exchange and the queue, which will create them + //as they don't exist + session.exchangeDeclare(arg::exchange="MyExchange", arg::type="direct"); + if (opts.verbose) std::cout << "Declared exchange." << std::endl; + session.queueDeclare(arg::queue="MyQueue", arg::autoDelete=true, arg::exclusive=true); + if (opts.verbose) std::cout << "Declared queue." << std::endl; + + //now bind the queue to the exchange + session.exchangeBind(arg::exchange="MyExchange", arg::queue="MyQueue", arg::bindingKey="MyKey"); + if (opts.verbose) std::cout << "Bound queue to exchange." << std::endl; + + //create and send a message to the exchange using the routing + //key we bound our queue with: + Message msgOut(generateData(opts.msgSize)); + msgOut.getDeliveryProperties().setRoutingKey("MyKey"); + 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"); + } + + //close the session & connection + session.close(); + if (opts.verbose) std::cout << "Closed session." << std::endl; + connection.close(); + if (opts.verbose) std::cout << "Closed connection." << std::endl; + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/cluster.mk b/RC9/qpid/cpp/src/tests/cluster.mk new file mode 100644 index 0000000000..1f027ccb4c --- /dev/null +++ b/RC9/qpid/cpp/src/tests/cluster.mk @@ -0,0 +1,41 @@ +# +# 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 +# + +# 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 + +check_PROGRAMS+=cluster_test +cluster_test_SOURCES=unit_test.cpp cluster_test.cpp +cluster_test_LDADD=$(lib_client) ../cluster.la -lboost_unit_test_framework + +unit_test_LDADD+=../cluster.la + +endif diff --git a/RC9/qpid/cpp/src/tests/cluster_test.cpp b/RC9/qpid/cpp/src/tests/cluster_test.cpp new file mode 100644 index 0000000000..f4a38ae861 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/cluster_test.cpp @@ -0,0 +1,648 @@ +/* + * + * 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/DumpClient.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 <string> +#include <iostream> +#include <iterator> +#include <vector> +#include <set> +#include <algorithm> +#include <iterator> + +namespace std { // ostream operators in std:: namespace +template <class T> +ostream& operator<<(ostream& o, const std::set<T>& s) { return seqPrint(o, s); } +} + + +QPID_AUTO_TEST_SUITE(cluster) + +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; + +/** 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; +} + +/** 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> { + string name; + std::auto_ptr<BrokerFixture> localBroker; + int localIndex; + std::vector<shared_ptr<ForkedBroker> > forkedBrokers; + + public: + /** @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, int localIndex=0); + void add(size_t n) { for (size_t i=0; i < n; ++i) add(); } + void add(); // Add a broker. + void addLocal(); // Add a local broker. + void setup(); + + bool hasLocal() const { return localIndex >= 0 && size_t(localIndex) < size(); } + + /** Kill a forked broker with sig, or shutdown localBroker if n==0. */ + void kill(size_t n, int sig=SIGINT) { + if (n == size_t(localIndex)) + localBroker->broker->shutdown(); + else + forkedBrokers[n]->kill(sig); + } + + /** Kill a broker and suppress errors from connection. */ + void killWithSilencer(size_t n, client::Connection& c, int sig=SIGINT) { + ScopedSuppressLogging sl; + kill(n,sig); + try { c.close(); } catch(...) {} + } +}; + +ClusterFixture::ClusterFixture(size_t n, int localIndex_) : name(Uuid(true).str()), localIndex(localIndex_) { + add(n); +} + +void ClusterFixture::add() { + if (size() != size_t(localIndex)) { // fork a broker process. + std::ostringstream os; os << "fork" << size(); + std::string prefix = os.str(); + const char* argv[] = { + "qpidd " __FILE__ , + "--no-module-dir", + "--load-module=../.libs/cluster.so", + "--cluster-name", name.c_str(), + "--auth=no", "--no-data-dir", + "--log-prefix", prefix.c_str(), + }; + size_t argc = sizeof(argv)/sizeof(argv[0]); + forkedBrokers.push_back(shared_ptr<ForkedBroker>(new ForkedBroker(argc, argv))); + push_back(forkedBrokers.back()->getPort()); + } + else { // Run in this process + addLocal(); + } +} + +void ClusterFixture::addLocal() { + assert(int(size()) == localIndex || localIndex == -1); + localIndex = size(); + const char* argv[] = { + "qpidd " __FILE__ , + "--load-module=../.libs/cluster.so", + "--cluster-name", name.c_str(), + "--auth=no", "--no-data-dir" + }; + size_t argc = sizeof(argv)/sizeof(argv[0]); + ostringstream os; os << "local" << localIndex; + qpid::log::Logger::instance().setPrefix(os.str()); + localBroker.reset(new BrokerFixture(parseOpts(argc, argv))); + push_back(localBroker->getPort()); + forkedBrokers.push_back(shared_ptr<ForkedBroker>()); +} + +ostream& operator<<(ostream& o, const cpg_name* n) { + return o << qpid::cluster::Cpg::str(*n); +} + +ostream& operator<<(ostream& o, const cpg_address& a) { + return o << "(" << a.nodeid <<","<<a.pid<<","<<a.reason<<")"; +} + +template <class T> +ostream& operator<<(ostream& o, const pair<T*, int>& array) { + o << "{ "; + ostream_iterator<cpg_address> i(o, " "); + copy(array.first, array.first+array.second, i); + o << "}"; + return o; +} + +template <class C> set<uint16_t> makeSet(const C& c) { + set<uint16_t> s; + std::copy(c.begin(), c.end(), std::inserter(s, s.begin())); + return s; +} + +template <class T> std::set<uint16_t> knownBrokerPorts(T& source, int n=-1) { + vector<Url> urls = source.getKnownBrokers(); + if (n >= 0 && unsigned(n) != urls.size()) { + BOOST_MESSAGE("knownBrokerPorts waiting for " << n << ": " << urls); + // Retry up to 10 secs in .1 second intervals. + for (size_t retry=100; urls.size() != unsigned(n) && retry != 0; --retry) { + ::usleep(1000*100); // 0.1 secs + urls = source.getKnownBrokers(); + } + } + BOOST_MESSAGE("knownBrokerPorts expecting " << n << ": " << urls); + set<uint16_t> s; + for (vector<Url>::const_iterator i = urls.begin(); i != urls.end(); ++i) + s.insert((*i)[0].get<TcpAddress>()->port); + return s; +} + +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); + } + + private: + boost::shared_ptr<ConnectionImpl> connection; + uint16_t channel; +}; + +int64_t getMsgSequence(const Message& m) { + return m.getMessageProperties().getApplicationHeaders().getAsInt64("qpid.msg_sequence"); +} + +QPID_AUTO_TEST_CASE(testSequenceOptions) { + // Make sure the exchange qpid.msg_sequence property is properly replicated. + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + FieldTable args; + args.setInt("qpid.msg_sequence", 1); // FIXME aconway 2008-11-11: works with "qpid.sequence_counter"?? + c0.session.queueDeclare(arg::queue="q"); + c0.session.exchangeDeclare(arg::exchange="ex", arg::type="direct", arg::arguments=args); + c0.session.exchangeBind(arg::exchange="ex", arg::queue="q", arg::bindingKey="k"); + c0.session.messageTransfer(arg::content=Message("1", "k"), arg::destination="ex"); + c0.session.messageTransfer(arg::content=Message("2", "k"), arg::destination="ex"); + BOOST_CHECK_EQUAL(1, getMsgSequence(c0.subs.get("q", TIME_SEC))); + BOOST_CHECK_EQUAL(2, getMsgSequence(c0.subs.get("q", TIME_SEC))); + + cluster.add(); + Client c1(cluster[1]); + c1.session.messageTransfer(arg::content=Message("3", "k"), arg::destination="ex"); + BOOST_CHECK_EQUAL(3, getMsgSequence(c1.subs.get("q", TIME_SEC))); +} + +QPID_AUTO_TEST_CASE(testUnsupported) { + ScopedSuppressLogging sl; + ClusterFixture cluster(1); + Client c1(cluster[0], "c1"); + BOOST_CHECK_THROW(c1.session.dtxSelect(), FramingErrorException); + Client c2(cluster[0], "c2"); + Message m; + m.getDeliveryProperties().setTtl(1); + BOOST_CHECK_THROW(c2.session.messageTransfer(arg::content=m), Exception); +} + +QPID_AUTO_TEST_CASE(testTxTransaction) { + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + c0.session.queueDeclare(arg::queue="q"); + c0.session.messageTransfer(arg::content=Message("A", "q")); + c0.session.messageTransfer(arg::content=Message("B", "q")); + + // Start a transaction that will commit. + Session commitSession = c0.connection.newSession("commit"); + SubscriptionManager commitSubs(commitSession); + commitSession.txSelect(); + commitSession.messageTransfer(arg::content=Message("a", "q")); + commitSession.messageTransfer(arg::content=Message("b", "q")); + BOOST_CHECK_EQUAL(commitSubs.get("q", TIME_SEC).getData(), "A"); + + // Start a transaction that will roll back. + Session rollbackSession = c0.connection.newSession("rollback"); + SubscriptionManager rollbackSubs(rollbackSession); + rollbackSession.txSelect(); + rollbackSession.messageTransfer(arg::content=Message("1", "q")); + Message rollbackMessage = rollbackSubs.get("q", TIME_SEC); + 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=Message("2", "q")); + commitSession.messageTransfer(arg::content=Message("c", "q")); + rollbackSession.messageTransfer(arg::content=Message("3", "q")); + + 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", TIME_SEC).getData(), "B"); + BOOST_CHECK_EQUAL(c1.subs.get("q", TIME_SEC).getData(), "a"); + BOOST_CHECK_EQUAL(c1.subs.get("q", TIME_SEC).getData(), "b"); + BOOST_CHECK_EQUAL(c1.subs.get("q", TIME_SEC).getData(), "c"); +} + +QPID_AUTO_TEST_CASE(testUnacked) { + // Verify replication of unacknowledged messages. + ClusterFixture cluster(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"); + c0.session.messageTransfer(arg::content=Message("11","q1")); + LocalQueue q1; + c0.subs.subscribe(q1, "q1", manualAccept); + BOOST_CHECK_EQUAL(q1.get(TIME_SEC).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"); + c0.session.messageTransfer(arg::content=Message("21","q2")); + c0.session.messageTransfer(arg::content=Message("22","q2")); + LocalQueue q2; + c0.subs.subscribe(q2, "q2", manualAcquire); + m = q2.get(TIME_SEC); // 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(TIME_SEC).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"); + c0.session.messageTransfer(arg::content=Message("31", "q3")); + c0.session.messageTransfer(arg::content=Message("32", "q3")); + LocalQueue q3; + c0.subs.subscribe(q3, "q3", manualComplete); + Message m31=q3.get(TIME_SEC); + 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(TIME_SEC).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", TIME_SEC).getData(), "11"); + BOOST_CHECK_EQUAL(c1.subs.get("q2", TIME_SEC).getData(), "21"); + BOOST_CHECK_EQUAL(c1.subs.get("q2", TIME_SEC).getData(), "22"); +} + +QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(testDumpTxState, 1) { + // Verify that we dump transaction state correctly to new members. + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + + // Do work in a transaction. + c0.session.txSelect(); + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("1","q")); + c0.session.messageTransfer(arg::content=Message("2","q")); + Message m; + BOOST_CHECK(c0.subs.get(m, "q", TIME_SEC)); + 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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "2"); + + // Another transaction with both members active. + c0.session.messageTransfer(arg::content=Message("3","q")); + 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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "3"); +} + +QPID_AUTO_TEST_CASE(testDumpMessageBuilder) { + // Verify that we dump a partially recieved message to a new member. + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + c0.session.queueDeclare("q"); + Sender sender(ConnectionAccess::getImpl(c0.connection), c0.session.getChannel()); + + // Send first 2 frames of message. + MessageTransferBody transfer( + ProtocolVersion(), std::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"); + 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. + ::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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "abcd"); + BOOST_CHECK_EQUAL(2u, knownBrokerPorts(c1.connection).size()); +} + +QPID_AUTO_TEST_CASE(testConnectionKnownHosts) { + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + set<uint16_t> kb0 = knownBrokerPorts(c0.connection); + BOOST_CHECK_EQUAL(kb0.size(), 1u); + BOOST_CHECK_EQUAL(kb0, makeSet(cluster)); + + cluster.add(); + Client c1(cluster[1], "c1"); + set<uint16_t> 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<uint16_t> 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(DumpConsumers) { + ClusterFixture cluster(1, 1); + + Client c0(cluster[0], "c0"); + c0.session.queueDeclare("p"); + c0.session.queueDeclare("q"); + 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=Message("aaa", "q")); + + c0.session.messageTransfer(arg::content=Message("bbb", "p")); + c0.session.messageTransfer(arg::content=Message("ccc", "p")); + + // Activate the subscription, ensure message removed on all queues. + c0.subs.setFlowControl("q", FlowControl::unlimited()); + Message m; + BOOST_CHECK(c0.lq.get(m, TIME_SEC)); + 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, TIME_SEC)); + 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", TIME_SEC)); + 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=Message("xxx", "q")); + BOOST_REQUIRE(c1.subs.get(m, "q", TIME_SEC)); + BOOST_REQUIRE_EQUAL(m.getData(), "xxx"); + } +} + +QPID_AUTO_TEST_CASE(testCatchupSharedState) { + ClusterFixture cluster(1); + Client c0(cluster[0], "c0"); + + // Create some shared state. + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("foo","q")); + c0.session.messageTransfer(arg::content=Message("bar","q")); + while (c0.session.queueQuery("q").getMessageCount() != 2) + ::usleep(1000); // Wait for message to show up on broker 0. + + // Add a new broker, it should catch up. + cluster.add(); + + // Do some work post-add + c0.session.queueDeclare("p"); + c0.session.messageTransfer(arg::content=Message("pfoo","p")); + + // Do some work post-join + BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 2).size(), 2u); + c0.session.messageTransfer(arg::content=Message("pbar","p")); + + // Verify new brokers have state. + Message m; + + Client c1(cluster[1], "c1"); + + BOOST_CHECK(c1.subs.get(m, "q", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "foo"); + BOOST_CHECK(c1.subs.get(m, "q", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "bar"); + 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", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "pfoo"); + BOOST_CHECK(c2.subs.get(m, "p", TIME_SEC)); + BOOST_CHECK_EQUAL(m.getData(), "pbar"); + BOOST_CHECK_EQUAL(c2.session.queueQuery("p").getMessageCount(), 0u); +} + +QPID_AUTO_TEST_CASE(testWiringReplication) { + ClusterFixture cluster(3); + Client c0(cluster[0]); + BOOST_CHECK(c0.session.queueQuery("q").getQueue().empty()); + BOOST_CHECK(c0.session.exchangeQuery("ex").getType().empty()); + c0.session.queueDeclare("q"); + c0.session.exchangeDeclare("ex", arg::type="direct"); + c0.session.close(); + c0.connection.close(); + // Verify all brokers get wiring update. + for (size_t i = 0; i < cluster.size(); ++i) { + BOOST_MESSAGE("i == "<< i); + 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); + Client c0(cluster[0]); + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("foo", "q")); + c0.session.messageTransfer(arg::content=Message("bar", "q")); + c0.session.close(); + Client c1(cluster[1]); + Message msg; + BOOST_CHECK(c1.subs.get(msg, "q", qpid::sys::TIME_SEC)); + BOOST_CHECK_EQUAL(string("foo"), msg.getData()); + BOOST_CHECK(c1.subs.get(msg, "q", qpid::sys::TIME_SEC)); + 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"); + c0.session.queueDeclare("q"); + c0.session.messageTransfer(arg::content=Message("foo", "q")); + c0.session.messageTransfer(arg::content=Message("bar", "q")); + + Message msg; + + // Dequeue on 2 others, ensure correct order. + Client c1(cluster[1], "c1"); + BOOST_CHECK(c1.subs.get(msg, "q")); + BOOST_CHECK_EQUAL("foo", msg.getData()); + + Client c2(cluster[2], "c2"); + BOOST_CHECK(c1.subs.get(msg, "q")); + BOOST_CHECK_EQUAL("bar", msg.getData()); + + // Queue should be empty on all cluster members. + BOOST_CHECK_EQUAL(0u, c0.session.queueQuery("q").getMessageCount()); + BOOST_CHECK_EQUAL(0u, c1.session.queueQuery("q").getMessageCount()); + BOOST_CHECK_EQUAL(0u, c2.session.queueQuery("q").getMessageCount()); +} + +QPID_AUTO_TEST_CASE(testDequeueWaitingSubscription) { + ClusterFixture cluster(3); + Client c0(cluster[0]); + BOOST_REQUIRE_EQUAL(knownBrokerPorts(c0.connection, 3).size(), 3u); // Wait for brokers. + + // First start a subscription. + c0.session.queueDeclare("q"); + c0.subs.subscribe(c0.lq, "q", FlowControl::messageCredit(2)); + + // Now send messages + Client c1(cluster[1]); + c1.session.messageTransfer(arg::content=Message("foo", "q")); + c1.session.messageTransfer(arg::content=Message("bar", "q")); + + // Check they arrived + Message m; + BOOST_CHECK(c0.lq.get(m, sys::TIME_SEC)); + BOOST_CHECK_EQUAL("foo", m.getData()); + BOOST_CHECK(c0.lq.get(m, sys::TIME_SEC)); + BOOST_CHECK_EQUAL("bar", m.getData()); + + // Queue should be empty on all cluster members. + Client c2(cluster[2]); + BOOST_CHECK_EQUAL(0u, c0.session.queueQuery("q").getMessageCount()); + BOOST_CHECK_EQUAL(0u, c1.session.queueQuery("q").getMessageCount()); + BOOST_CHECK_EQUAL(0u, c2.session.queueQuery("q").getMessageCount()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/config.null b/RC9/qpid/cpp/src/tests/config.null new file mode 100644 index 0000000000..565c7da435 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/config.null @@ -0,0 +1 @@ +# empty config diff --git a/RC9/qpid/cpp/src/tests/consume.cpp b/RC9/qpid/cpp/src/tests/consume.cpp new file mode 100644 index 0000000000..4d74b8ae57 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/consume.cpp @@ -0,0 +1,119 @@ +/* + * + * 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 <algorithm> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#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 qpid::sys; +using namespace std; + +typedef vector<string> StringSet; + +struct Args : public qpid::TestOptions { + uint count; + uint ack; + string queue; + bool declare; + bool summary; + + Args() : count(1000), ack(0), queue("publish-consume"), + declare(false), summary(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") + ("declare", optValue(declare), "declare the queue") + ("s,summary", optValue(summary), "Print undecorated rate."); + } +}; + +Args opts; + +struct Client +{ + Connection connection; + Session session; + + Client() + { + opts.open(connection); + session = connection.newSession(); + } + + void consume() + { + if (opts.declare) + session.queueDeclare(opts.queue); + SubscriptionManager subs(session); + 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(); + QPID_LOG(info, "Received: " << msg.getMessageProperties().getCorrelationId()); + } + if (opts.ack != 0) + 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() + { + try{ + session.close(); + connection.close(); + } catch(const exception& e) { + cout << e.what() << endl; + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Client client; + client.consume(); + return 0; + } catch(const exception& e) { + cout << e.what() << endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/declare_queues.cpp b/RC9/qpid/cpp/src/tests/declare_queues.cpp new file mode 100644 index 0000000000..7f61bde12a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/declare_queues.cpp @@ -0,0 +1,69 @@ +/* + * + * 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/Exception.h> + +#include <cstdlib> +#include <iostream> + +using namespace qpid::client; + +using namespace std; + +int main(int argc, char ** argv) +{ + ConnectionSettings settings; + if ( argc != 3 ) + { + cerr << "Usage: declare_queues host port\n"; + return 1; + } + + settings.host = argv[1]; + settings.port = atoi(argv[2]); + + FailoverManager connection(settings); + try { + bool complete = false; + while (!complete) { + Session session = connection.connect().newSession(); + try { + session.queueDeclare(arg::queue="message_queue"); + complete = true; + } catch (const qpid::TransportFailure&) {} + } + connection.close(); + return 0; + } catch (const exception& error) { + cerr << "declare_queues failed:" << error.what() << endl; + cerr << " host: " << settings.host + << " port: " << settings.port << endl; + return 1; + } + +} + + + + + diff --git a/RC9/qpid/cpp/src/tests/dlclose_noop.c b/RC9/qpid/cpp/src/tests/dlclose_noop.c new file mode 100644 index 0000000000..ba2fa75891 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/dlclose_noop.c @@ -0,0 +1,30 @@ +/* + * + * 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. + * + */ + +/* + * Loaded via LD_PRELOAD this will turn dlclose into a no-op. + * + * Allows valgrind to generate useful reports from programs that + * dynamically unload libraries before exit, such as CppUnit's + * DllPlugInTester. + * + */ + +#include <stdio.h> +void* dlclose(void* handle) {} + diff --git a/RC9/qpid/cpp/src/tests/echotest.cpp b/RC9/qpid/cpp/src/tests/echotest.cpp new file mode 100644 index 0000000000..7cbf3e7df4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/echotest.cpp @@ -0,0 +1,150 @@ +/* + * + * 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/Session.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; + +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; + } +} + +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/RC9/qpid/cpp/src/tests/exception_test.cpp b/RC9/qpid/cpp/src/tests/exception_test.cpp new file mode 100644 index 0000000000..e420bf2f0b --- /dev/null +++ b/RC9/qpid/cpp/src/tests/exception_test.cpp @@ -0,0 +1,121 @@ +/* + * + * 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/client/SubscriptionManager.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/framing/reply_exceptions.h" + +QPID_AUTO_TEST_SUITE(exception_test) + +// FIXME aconway 2008-06-12: need to update our exception handling to +// 0-10 handling and extend this test to provoke all the exceptional +// conditions we know of and verify the correct exception is thrown. + +using namespace std; +using namespace qpid; +using namespace sys; +using namespace client; +using namespace framing; + +using qpid::broker::Broker; +using boost::bind; +using boost::function; + +template <class Ex> +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 { + 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()+"]"); + } + catch(const std::exception& e) { + BOOST_ERROR(string("Bad exception: ")+e.what()+"["+typeid(e).name()+"] expected: "+typeid(Ex).name()); + } + catch(...) { + BOOST_ERROR(string("Bad exception: unknown")); + } + } + + bool join() { + if (thread.id()) { + thread.join(); + thread=Thread(); + } + return caught; + } +}; + +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(Broker::TCP_TRANSPORT)); + fix.session.queueDeclare(arg::queue="q"); + fix.subs.subscribe(fix.lq, "q"); + Catcher<TransportFailure> pop(bind(&LocalQueue::pop, &fix.lq, sys::TIME_SEC)); + fix.connection.proxy.close(); + BOOST_CHECK(pop.join()); +} + +QPID_AUTO_TEST_CASE(DisconnectedListen) { + ProxySessionFixture fix; + struct NullListener : public MessageListener { + void received(Message&) { BOOST_FAIL("Unexpected message"); } + } l; + ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT)); + fix.session.queueDeclare(arg::queue="q"); + fix.subs.subscribe(l, "q"); + + Catcher<TransportFailure> runner(bind(&SubscriptionManager::run, boost::ref(fix.subs))); + fix.connection.proxy.close(); + runner.join(); + BOOST_CHECK_THROW(fix.session.close(), 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() diff --git a/RC9/qpid/cpp/src/tests/failover_soak.cpp b/RC9/qpid/cpp/src/tests/failover_soak.cpp new file mode 100644 index 0000000000..6149e845e4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/failover_soak.cpp @@ -0,0 +1,654 @@ +/* + * + * 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 <string> +#include <iostream> +#include <sstream> +#include <vector> + +#include <ForkedBroker.h> + + + + +using namespace std; + + +typedef vector<ForkedBroker *> brokerVector; + +typedef enum +{ + NO_STATUS, + RUNNING, + COMPLETED +} +childStatus; + + + +struct child +{ + child ( string & name, pid_t pid ) + : name(name), pid(pid), retval(-999), status(RUNNING) + { + gettimeofday ( & startTime, 0 ); + } + + + void + done ( int _retval ) + { + retval = _retval; + status = COMPLETED; + gettimeofday ( & stopTime, 0 ); + } + + + string name; + pid_t pid; + int retval; + childStatus status; + struct timeval startTime, + stopTime; +}; + + + + +struct children : public vector<child *> +{ + void + add ( string & name, pid_t pid ) + { + push_back(new child ( name, pid )); + } + + + 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 > 0 ) + { + 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) ) + 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"; + } + + + /* + Only call this if you already know there is at least + one child still running. Supply a time in seconds. + If it has been at least that long since a shild stopped + running, we judge the system to have hung. + */ + bool + hanging ( int hangTime ) + { + struct timeval now, + duration; + gettimeofday ( &now, 0 ); + + vector<child *>::iterator i; + for ( i = begin(); i != end(); ++ i ) + { + timersub ( & now, &((*i)->startTime), & duration ); + if ( duration.tv_sec >= hangTime ) + return true; + } + + return false; + } + + + int verbosity; +}; + + + +children allMyChildren; + + + + +void +childExit ( int signalNumber ) +{ + signalNumber ++; // Now maybe the compiler willleave me alone? + 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, int & num ) { + num = mrand(1000); + stringstream ss; + ss << "soakTestCluster_" << num; + 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"; +} + + + + + +void +startNewBroker ( brokerVector & brokers, + char const * srcRoot, + char const * moduleDir, + string const clusterName ) +{ + static int brokerId = 0; + stringstream path, prefix, module; + module << moduleDir << "/cluster.so"; + path << srcRoot << "/qpidd"; + prefix << "soak-" << brokerId++; + + const char * const argv[] = + { + "qpidd", + "-p0", + "--load-module=cluster.so", + "--cluster-name", + clusterName.c_str(), + "--auth=no", + "--no-data-dir", + "--no-module-dir", + "--mgmt-enable=no", + "--log-prefix", prefix.str().c_str(), + 0 + }; + + size_t argc = sizeof(argv)/sizeof(argv[0]); + brokers.push_back ( new ForkedBroker ( argc, argv ) ); +} + + + + + +void +killFrontBroker ( brokerVector & brokers, int verbosity ) +{ + 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; + } + delete brokers[0]; + brokers.erase ( brokers.begin() ); +} + + + + + +void +killAllBrokers ( brokerVector & brokers ) +{ + for ( uint i = 0; i < brokers.size(); ++ i ) + try { brokers[i]->kill(9); } + catch ( ... ) { } +} + + + + + +pid_t +runDeclareQueuesClient ( brokerVector brokers, + char const * host, + char const * path, + int verbosity + ) +{ + string name("declareQueues"); + int port = brokers[0]->getPort ( ); + + if ( verbosity > 0 ) + 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() ); + argv.push_back ( 0 ); + pid_t pid = fork(); + + if ( ! pid ) { + execv ( path, const_cast<char * const *>(&argv[0]) ); + perror ( "error executing dq: " ); + return 0; + } + + allMyChildren.add ( name, pid ); + return pid; +} + + + + + +pid_t +startReceivingClient ( brokerVector brokers, + char const * host, + char const * receiverPath, + char const * reportFrequency, + int verbosity + ) +{ + string name("receiver"); + int port = brokers[0]->getPort ( ); + + if ( verbosity > 0 ) + cout << "startReceivingClient: port " << port << endl; + char portStr[100]; + char verbosityStr[100]; + sprintf(portStr, "%d", port); + sprintf(verbosityStr, "%d", 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 ( 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 ); + return pid; +} + + + + + +pid_t +startSendingClient ( brokerVector brokers, + char const * host, + char const * senderPath, + char const * nMessages, + char const * reportFrequency, + int verbosity + ) +{ + string name("sender"); + int port = brokers[0]->getPort ( ); + + if ( verbosity ) + cout << "startSenderClient: port " << port << endl; + char portStr[100]; + char verbosityStr[100]; + + sprintf ( portStr, "%d", port); + sprintf ( verbosityStr, "%d", 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 ); + 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 ); + return pid; +} + + + +#define HUNKY_DORY 0 +#define BAD_ARGS 1 +#define CANT_FORK_DQ 2 +#define CANT_FORK_RECEIVER 3 +#define DQ_FAILED 4 +#define ERROR_ON_CHILD 5 +#define HANGING 6 + + +int +main ( int argc, char const ** argv ) +{ + if ( argc < 9 ) { + cerr << "Usage: failoverSoak srcRoot moduleDir host senderPath receiverPath nMessages verbosity\n"; + cerr << " ( argc was " << argc << " )\n"; + return BAD_ARGS; + } + + signal ( SIGCHLD, childExit ); + + char const * srcRoot = argv[1]; + char const * moduleDir = argv[2]; + char const * host = argv[3]; + char const * declareQueuesPath = argv[4]; + char const * senderPath = argv[5]; + char const * receiverPath = argv[6]; + char const * nMessages = argv[7]; + char const * reportFrequency = argv[8]; + int verbosity = atoi(argv[9]); + + int maxBrokers = 50; + + allMyChildren.verbosity = verbosity; + + int clusterNum; + string clusterName; + + srand ( getpid() ); + + makeClusterName ( clusterName, clusterNum ); + + brokerVector brokers; + + if ( verbosity > 0 ) + cout << "Starting initial cluster...\n"; + + int nBrokers = 3; + for ( int i = 0; i < nBrokers; ++ i ) { + startNewBroker ( brokers, + srcRoot, + moduleDir, + clusterName ); + } + + + if ( verbosity > 0 ) + printBrokers ( brokers ); + + // Run the declareQueues child. + int childStatus; + pid_t dqClientPid = + runDeclareQueuesClient ( brokers, host, declareQueuesPath, verbosity ); + if ( -1 == dqClientPid ) { + cerr << "failoverSoak error: Couldn't fork declareQueues.\n"; + return CANT_FORK_DQ; + } + + // Don't continue until declareQueues is finished. + pid_t retval = waitpid ( dqClientPid, & childStatus, 0); + if ( retval != dqClientPid) { + cerr << "failoverSoak error: waitpid on declareQueues returned value " << retval << endl; + return DQ_FAILED; + } + allMyChildren.exited ( dqClientPid, childStatus ); + + + + // Start the receiving client. + pid_t receivingClientPid = + startReceivingClient ( brokers, + host, + receiverPath, + reportFrequency, + verbosity ); + if ( -1 == receivingClientPid ) { + cerr << "failoverSoak error: Couldn't fork receiver.\n"; + return CANT_FORK_RECEIVER; + } + + + // Start the sending client. + pid_t sendingClientPid = + startSendingClient ( brokers, + host, + senderPath, + nMessages, + reportFrequency, + verbosity ); + if ( -1 == sendingClientPid ) { + cerr << "failoverSoak error: Couldn't fork sender.\n"; + return CANT_FORK_RECEIVER; + } + + + int minSleep = 3, + maxSleep = 6; + + + for ( int totalBrokers = 3; + 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. -------------------------- + killFrontBroker ( brokers, verbosity ); + + // Sleep for a while. ------------------------- + sleepyTime = mrand ( minSleep, maxSleep ); + if ( verbosity > 0 ) + cerr << "Sleeping for " << sleepyTime << " seconds.\n"; + sleep ( sleepyTime ); + + // Start a new broker. -------------------------- + if ( verbosity > 0 ) + cout << "Starting new broker.\n\n"; + + startNewBroker ( brokers, + srcRoot, + moduleDir, + clusterName ); + + if ( verbosity > 0 ) + printBrokers ( brokers ); + + // If all children have exited, quit. + int unfinished = allMyChildren.unfinished(); + if ( ! unfinished ) { + killAllBrokers ( brokers ); + + if ( verbosity > 0 ) + cout << "failoverSoak: all children have exited.\n"; + int retval = allMyChildren.checkChildren(); + if ( verbosity > 0 ) + std::cerr << "failoverSoak: checkChildren: " << retval << endl; + return retval ? ERROR_ON_CHILD : 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 ); + return ERROR_ON_CHILD; + } + + // If one is hanging, quit. + if ( allMyChildren.hanging ( 120 ) ) + { + if ( verbosity > 0 ) + cout << "failoverSoak: child hanging.\n"; + allMyChildren.killEverybody(); + killAllBrokers ( brokers ); + return HANGING; + } + + if ( verbosity > 0 ) { + std::cerr << "------- next kill-broker loop --------\n"; + allMyChildren.print(); + } + } + + retval = allMyChildren.checkChildren(); + if ( verbosity > 0 ) + std::cerr << "failoverSoak: checkChildren: " << retval << endl; + + if ( verbosity > 0 ) + cout << "failoverSoak: maxBrokers reached.\n"; + + allMyChildren.killEverybody(); + killAllBrokers ( brokers ); + + return retval ? ERROR_ON_CHILD : HUNKY_DORY; +} + + + diff --git a/RC9/qpid/cpp/src/tests/fanout_perftest b/RC9/qpid/cpp/src/tests/fanout_perftest new file mode 100755 index 0000000000..d8a7661f49 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/fanout_perftest @@ -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_perftest 10000 --mode fanout --npubs 16 --nsubs 16 --size 64 diff --git a/RC9/qpid/cpp/src/tests/federated_topic_test b/RC9/qpid/cpp/src/tests/federated_topic_test new file mode 100755 index 0000000000..21d8411eaf --- /dev/null +++ b/RC9/qpid/cpp/src/tests/federated_topic_test @@ -0,0 +1,130 @@ +#!/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 +rm -f subscriber_*.log + +# Defaults values +SUBSCRIBERS=2 +MESSAGES=1000 +BATCHES=1 +VERBOSE=1 + +while getopts "s:m:b:" opt ; do + case $opt in + s) SUBSCRIBERS=$OPTARG ;; + m) MESSAGES=$OPTARG ;; + b) BATCHES=$OPTARG ;; + ?) + echo "Usage: %0 [-s <subscribers>] [-m <messages.] [-b <batches>]" + exit 1 + ;; + esac +done + +MY_DIR=$(dirname $(which $0)) +PYTHON_DIR=${MY_DIR}/../../../python + +trap stop_brokers EXIT + +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` + start_broker + PORT_B=`cat qpidd.port` + start_broker + PORT_C=`cat qpidd.port` +} + +stop_brokers() { + for p in $PORT_A $PORT_B $PORT_C; do + ${MY_DIR}/../qpidd -q --port $p + done +} + +subscribe() { + #which broker should we connect to? + if (( $1 % 2 )); then + MY_PORT=$PORT_C; + else + MY_PORT=$PORT_A; + fi + + echo Subscriber $1 connecting on $MY_PORT + LOG="subscriber_$1.log" + ${MY_DIR}/topic_listener -p $MY_PORT > $LOG 2>&1 && rm -f $LOG +} + +publish() { + ${MY_DIR}/topic_publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS -p $PORT_A +} + +setup_routes() { + BROKER_A="localhost:$PORT_A" + BROKER_B="localhost:$PORT_B" + BROKER_C="localhost:$PORT_C" + export PYTHONPATH=$PYTHON_DIR + if (($VERBOSE)); then + echo "Establishing routes for topic..." + fi + $PYTHON_DIR/commands/qpid-route route add $BROKER_B $BROKER_A amq.topic topic_control B B + $PYTHON_DIR/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_DIR/commands/qpid-route route add $BROKER_B $BROKER_C amq.topic topic_control B B + $PYTHON_DIR/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_DIR/commands/qpid-route route add $BROKER_B $BROKER_C amq.direct response B B + $PYTHON_DIR/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_DIR/commands/qpid-route route list $b + done + fi +} + +if test -d ${PYTHON_DIR} ; then + start_brokers + 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 + + setup_routes + + publish || exit 1 +fi diff --git a/RC9/qpid/cpp/src/tests/federation.py b/RC9/qpid/cpp/src/tests/federation.py new file mode 100755 index 0000000000..ad82964007 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/federation.py @@ -0,0 +1,505 @@ +#!/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 +from qpid.testlib import TestBase010, testrunner +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): + + def test_bridge_create_and_close(self): + self.startQmf(); + qmf = self.qmf + + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), 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.direct", "my-key", "", "", False, False, False) + self.assertEqual(result.status, 0) + + bridge = qmf.getObjects(_class="bridge")[0] + result = bridge.close() + self.assertEqual(result.status, 0) + + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_pull_from_exchange(self): + session = self.session + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), 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, False, False) + 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_session = r_conn.session("test_pull_from_exchange") + + for i in range(1, 11): + dp = r_session.delivery_properties(routing_key="my-key") + r_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) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_push_to_exchange(self): + session = self.session + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), 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) + self.assertEqual(result.status, 0) + + bridge = qmf.getObjects(_class="bridge")[0] + + #setup queue to receive messages from remote broker + r_conn = self.connect(host=remote_host(), port=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) + + #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) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + 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_session = r_conn.session("test_pull_from_queue") + 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)) + + #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") + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + self.assertEqual(result.status, 0) + + link = qmf.getObjects(_class="link")[0] + result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False) + self.assertEqual(result.status, 0) + + bridge = qmf.getObjects(_class="bridge")[0] + sleep(3) + + #add some more messages (i.e. after bridge was created) + for i in range(6, 11): + dp = r_session.delivery_properties(routing_key="my-bridge-queue") + r_session.message_transfer(message=Message(dp, "Message %d" % i)) + + 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 + + result = bridge.close() + self.assertEqual(result.status, 0) + result = link.close() + self.assertEqual(result.status, 0) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_tracing_automatic(self): + remoteUrl = "%s:%d" % (remote_host(), remote_port()) + self.startQmf() + l_broker = self.qmf_broker + r_broker = self.qmf.addBroker(remoteUrl) + + 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(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp") + r_res = r_brokerObj.connect(testrunner.host, testrunner.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) + r_res = r_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False) + + 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 + 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=remote_host(), port=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)) + + 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 + + self.startQmf() + qmf = self.qmf + broker = qmf.getObjects(_class="broker")[0] + result = broker.connect(remote_host(), 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) + 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_session = r_conn.session("test_tracing") + + trace = [None, "exclude-me", "a,exclude-me,b", "also-exclude-me,c", "dont-exclude-me"] + body = ["yes", "first-bad", "second-bad", "third-bad", "yes"] + for b, t in zip(body, trace): + headers = {} + if (t): headers["x-qpid.trace"]=t + dp = r_session.delivery_properties(routing_key="my-key") + mp = r_session.message_properties(application_headers=headers) + r_session.message_transfer(destination="amq.direct", message=Message(dp, mp, b)) + + for e in ["my-bridge-id", "dont-exclude-me,my-bridge-id"]: + msg = queue.get(timeout=5) + self.assertEqual("yes", msg.body) + self.assertEqual(e, self.getAppHeader(msg, "x-qpid.trace")) + + 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) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + def test_dynamic_fanout(self): + session = self.session + r_conn = self.connect(host=remote_host(), port=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(remote_host(), 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) + 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) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + + def test_dynamic_direct(self): + session = self.session + r_conn = self.connect(host=remote_host(), port=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(remote_host(), 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) + 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) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + + def test_dynamic_topic(self): + session = self.session + r_conn = self.connect(host=remote_host(), port=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(remote_host(), 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) + 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) + + sleep(3) + self.assertEqual(len(qmf.getObjects(_class="bridge")), 0) + self.assertEqual(len(qmf.getObjects(_class="link")), 0) + + + 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 + + +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) diff --git a/RC9/qpid/cpp/src/tests/header_test.cpp b/RC9/qpid/cpp/src/tests/header_test.cpp new file mode 100644 index 0000000000..ba9ffacc9b --- /dev/null +++ b/RC9/qpid/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.71828; + 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/RC9/qpid/cpp/src/tests/header_test.py b/RC9/qpid/cpp/src/tests/header_test.py new file mode 100755 index 0000000000..d5a2c16c01 --- /dev/null +++ b/RC9/qpid/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/RC9/qpid/cpp/src/tests/interop_runner.cpp b/RC9/qpid/cpp/src/tests/interop_runner.cpp new file mode 100644 index 0000000000..8c6e0a6991 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/interop_runner.cpp @@ -0,0 +1,251 @@ +/* + * + * 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/RC9/qpid/cpp/src/tests/latencytest.cpp b/RC9/qpid/cpp/src/tests/latencytest.cpp new file mode 100644 index 0000000000..6895964133 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/latencytest.cpp @@ -0,0 +1,432 @@ +/* + * + * 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 <algorithm> +#include <limits> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.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; + +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 prefetch; + uint ack; + bool cumulative; + bool csv; + bool durable; + string base; + + Args() : size(256), count(1000), rate(0), reportFrequency(1000), + timeLimit(0), queues(1), + prefetch(100), ack(0), + durable(false), base("latency-test") + { + addOptions() + + ("size", optValue(size, "N"), "message size") + ("queues", optValue(queues, "N"), "number of queues") + ("count", optValue(count, "N"), "number of messages to send") + ("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)") + ("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"), + "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)") + ("durable", optValue(durable, "yes|no"), "use durable messages") + ("csv", optValue(csv), "print stats in csv format (rate,min,max,avg)") + ("cumulative", optValue(cumulative), "cumulative stats in csv format") + ("queue-base-name", optValue(base, "<name>"), "base name for queues"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +Args opts; +double c_min, c_avg, c_max; + +uint64_t current_time() +{ + Duration t(now()); + return t; +} + +struct Stats +{ + Mutex lock; + uint count; + double minLatency; + double maxLatency; + double totalLatency; + + Stats(); + void update(double l); + void print(); + void reset(); +}; + +class Client : public Runnable +{ +protected: + Connection connection; + AsyncSession session; + Thread thread; + string queue; + +public: + Client(const string& q); + virtual ~Client() {} + + void start(); + void join(); + void run(); + virtual void test() = 0; +}; + +class Receiver : public Client, public MessageListener +{ + SubscriptionManager mgr; + uint count; + Stats& stats; + +public: + Receiver(const string& queue, Stats& stats); + void test(); + void received(Message& msg); + Stats getStats(); + uint getCount() { return count; } + void stop() { mgr.stop(); mgr.cancel(queue); } +}; + + +class Sender : public Client +{ + string generateData(uint size); + void sendByRate(); + void sendByCount(); + Receiver& receiver; + const string data; +public: + Sender(const string& queue, Receiver& receiver); + void test(); +}; + + +class Test +{ + const string queue; + Stats stats; + Receiver receiver; + Sender sender; + AbsTime begin; + +public: + Test(const string& q) : queue(q), receiver(queue, stats), sender(queue, receiver), begin(now()) {} + void start(); + void join(); + void report(); +}; + + +Client::Client(const string& q) : queue(q) +{ + opts.open(connection); + session = connection.newSession(); +} + +void Client::start() +{ + thread = Thread(this); +} + +void Client::join() +{ + thread.join(); +} + +void Client::run() +{ + try{ + test(); + session.close(); + connection.close(); + } catch(const std::exception& e) { + std::cout << "Error in receiver: " << e.what() << std::endl; + } +} + +Receiver::Receiver(const string& q, Stats& s) : Client(q), mgr(session), count(0), stats(s) +{ + session.queueDeclare(arg::queue=queue, arg::durable=opts.durable, arg::autoDelete=true); + uint msgCount = session.queueQuery(arg::queue=queue).get().getMessageCount(); + if (msgCount) { + std::cout << "Warning: found " << msgCount << " msgs on " << queue << ". Purging..." << std::endl; + session.queuePurge(arg::queue=queue); + } + SubscriptionSettings settings; + if (opts.prefetch) { + settings.autoAck = (opts.ack ? opts.ack : (opts.prefetch / 2)); + settings.flowControl = FlowControl::messageWindow(opts.prefetch); + } else { + settings.acceptMode = ACCEPT_MODE_NONE; + settings.flowControl = FlowControl::unlimited(); + } + mgr.subscribe(*this, queue, settings); +} + +void Receiver::test() +{ + mgr.run(); + mgr.cancel(queue); +} + +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(); + + //std::cerr << "Latency: " << (receivedAt - sentAt) << std::endl; + stats.update(((double) (receivedAt - sentAt)) / TIME_MSEC); + + if (!opts.rate && count >= opts.count) { + mgr.stop(); + } +} + +void Stats::update(double latency) +{ + Mutex::ScopedLock l(lock); + count++; + minLatency = std::min(minLatency, latency); + maxLatency = std::max(maxLatency, latency); + totalLatency += latency; +} + +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; + + 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; + } else { + std::cout << value << "," << minLatency << "," << maxLatency << + "," << aux_avg; + } + } 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; + } else { + c_avg = aux_avg; + c_min = minLatency; + c_max = maxLatency; + already_have_stats = true; + } + std::cout << value << "," << c_min << "," << c_max << + "," << c_avg; + } + +} + +void Stats::reset() +{ + Mutex::ScopedLock l(lock); + count = 0; + totalLatency = maxLatency = 0; + minLatency = std::numeric_limits<double>::max(); +} + +Sender::Sender(const string& q, Receiver& receiver) : Client(q), receiver(receiver), data(generateData(opts.size)) {} + +void Sender::test() +{ + if (opts.rate) sendByRate(); + else sendByCount(); +} + +void Sender::sendByCount() +{ + Message msg(data, queue); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + for (uint i = 0; i < opts.count; i++) { + uint64_t sentAt(current_time()); + msg.getDeliveryProperties().setTimestamp(sentAt); + async(session).messageTransfer(arg::content=msg, arg::acceptMode=1); + if (opts.sync) session.sync(); + } + session.sync(); +} + +void Sender::sendByRate() +{ + 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()); + + while (true) { + uint64_t start_msg(current_time()); + msg.getDeliveryProperties().setTimestamp(start_msg); + async(session).messageTransfer(arg::content=msg, arg::acceptMode=1); + if (opts.sync) session.sync(); + + 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) { + qpid::sys::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; + } + } +} + +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; + } + data += chars.substr(0, size % chars.length()); + return data; +} + + +void Test::start() +{ + receiver.start(); + begin = AbsTime(now()); + sender.start(); +} + +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 + << " in " << msecs << "ms (" << (receiver.getCount() * 1000 / msecs) << " msgs/s) "; + } + stats.print(); + std::cout << std::endl; +} + +void Test::report() +{ + stats.print(); + std::cout << std::endl; + stats.reset(); +} + +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++) { + std::ostringstream out; + out << opts.base << "-" << (i+1); + tests.push_back(new Test(out.str())); + } + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->start(); + } + if (opts.rate && !opts.timeLimit) { + while (true) { + qpid::sys::usleep(opts.reportFrequency * 1000); + //print latency report: + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->report(); + } + } + } else { + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->join(); + } + } + + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/logging.cpp b/RC9/qpid/cpp/src/tests/logging.cpp new file mode 100644 index 0000000000..051722e7c8 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/logging.cpp @@ -0,0 +1,366 @@ +/* + * + * 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 "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) +#else +# include "qpid/log/posix/SinkOptions.h" +#endif + +#include <boost/test/floating_point_comparison.hpp> +#include <boost/format.hpp> +#include "unit_test.h" + +#include <exception> +#include <fstream> +#include <time.h> + + +QPID_AUTO_TEST_SUITE(loggingTestSuite) + +using namespace std; +using namespace boost; +using namespace qpid::log; + +QPID_AUTO_TEST_CASE(testStatementInit) { + Statement s=QPID_LOG_STATEMENT_INIT(debug); int line=__LINE__; + BOOST_CHECK(!s.enabled); + BOOST_CHECK_EQUAL(string(__FILE__), s.file); + BOOST_CHECK_EQUAL(line, s.line); + BOOST_CHECK_EQUAL(debug, s.level); +} + + +QPID_AUTO_TEST_CASE(testSelector_enable) { + Selector s; + // Simple enable + s.enable(debug,"foo"); + BOOST_CHECK(s.isEnabled(debug,"foo")); + BOOST_CHECK(!s.isEnabled(error,"foo")); + BOOST_CHECK(!s.isEnabled(error,"bar")); + + // Substring match + BOOST_CHECK(s.isEnabled(debug, "bazfoobar")); + BOOST_CHECK(!s.isEnabled(debug, "bazbar")); + + // Different levels for different substrings. + s.enable(info, "bar"); + BOOST_CHECK(s.isEnabled(debug, "foobar")); + BOOST_CHECK(s.isEnabled(info, "foobar")); + BOOST_CHECK(!s.isEnabled(debug, "bar")); + BOOST_CHECK(!s.isEnabled(info, "foo")); + + // Enable-strings + s.enable("notice:blob"); + BOOST_CHECK(s.isEnabled(notice, "blob")); + s.enable("error+:oops"); + BOOST_CHECK(s.isEnabled(error, "oops")); + BOOST_CHECK(s.isEnabled(critical, "oops")); +} + +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); + static Statement::Initializer init(s); + BOOST_CHECK(s.enabled); + + static Statement s2=QPID_LOG_STATEMENT_INIT(warning); + static Statement::Initializer init2(s2); + BOOST_CHECK(!s2.enabled); + + l.select(Selector(warning)); + BOOST_CHECK(!s.enabled); + BOOST_CHECK(s2.enabled); +} + +struct TestOutput : public Logger::Output { + vector<string> msg; + vector<Statement> stmt; + + 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); + } + string last() { return msg.back(); } +}; + +using boost::assign::list_of; + +QPID_AUTO_TEST_CASE(testLoggerOutput) { + Logger l; + l.clear(); + l.select(Selector(debug)); + Statement s=QPID_LOG_STATEMENT_INIT(debug); + + TestOutput* out=new TestOutput(l); + + // Verify message is output. + l.log(s, "foo"); + vector<string> expect=list_of("foo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + + // Verify multiple outputs + TestOutput* out2=new TestOutput(l); + l.log(Statement(), "baz"); + expect.push_back("baz\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + expect.erase(expect.begin()); + BOOST_CHECK_EQUAL(expect, out2->msg); +} + +QPID_AUTO_TEST_CASE(testMacro) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + l.select(Selector(info)); + TestOutput* out=new TestOutput(l); + QPID_LOG(info, "foo"); + vector<string> expect=list_of("foo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + BOOST_CHECK_EQUAL(__FILE__, out->stmt.front().file); + + // Not enabled: + QPID_LOG(debug, "bar"); + BOOST_CHECK_EQUAL(expect, out->msg); + + QPID_LOG(info, 42 << " bingo"); + expect.push_back("42 bingo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); +} + +QPID_AUTO_TEST_CASE(testLoggerFormat) { + Logger& l = Logger::instance(); + ScopedSuppressLogging ls(l); + l.select(Selector(critical)); + TestOutput* out=new TestOutput(l); + + l.format(Logger::FILE); + QPID_LOG(critical, "foo"); + BOOST_CHECK_EQUAL(out->last(), string(__FILE__)+": foo\n"); + + l.format(Logger::FILE|Logger::LINE); + QPID_LOG(critical, "foo"); + BOOST_CHECK_REGEX(string(__FILE__)+":\\d+: foo\n", out->last()); + + l.format(Logger::FUNCTION); + QPID_LOG(critical, "foo"); + BOOST_CHECK_REGEX("void .*testLoggerFormat.*\\(\\): foo\n", out->last()); + + l.format(Logger::LEVEL); + QPID_LOG(critical, "foo"); + BOOST_CHECK_EQUAL("critical foo\n", out->last()); +} + +QPID_AUTO_TEST_CASE(testOstreamOutput) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + l.select(Selector(error)); + ostringstream os; + l.output(qpid::make_auto_ptr<Logger::Output>(new OstreamOutput(os))); + QPID_LOG(error, "foo"); + QPID_LOG(error, "bar"); + QPID_LOG(error, "baz"); + BOOST_CHECK_EQUAL("foo\nbar\nbaz\n", os.str()); +} + +#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"); + QPID_LOG(info, "Testing QPID"); + BOOST_ERROR("Manually verify that /var/log/messages contains a recent line 'Testing QPID'"); +} +#endif // 0 + +int count() { + static int n = 0; + return n++; +} + +int loggedCount() { + static int n = 0; + QPID_LOG(debug, "counting: " << n); + return n++; +} + + +using namespace qpid::sys; + +// Measure CPU time. +clock_t timeLoop(int times, int (*fp)()) { + clock_t start=clock(); + while (times-- > 0) + (*fp)(); + return clock() - start; +} + +// 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); + double ratio=double(withLog)/double(noLog); + + // 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); +} +#endif // 0 + +Statement statement( + Level level, const char* file="", int line=0, const char* fn=0) +{ + Statement s={0, file, line, fn, level}; + return s; +} + + +#define ARGC(argv) (sizeof(argv)/sizeof(char*)) + +QPID_AUTO_TEST_CASE(testOptionsParse) { + const char* argv[]={ + 0, + "--log-enable", "error+:foo", + "--log-enable", "debug:bar", + "--log-enable", "info", + "--log-to-stderr", "no", + "--log-to-file", "logout", + "--log-level", "yes", + "--log-source", "1", + "--log-thread", "true", + "--log-function", "YES" + }; + qpid::log::Options opts(""); + qpid::log::posix::SinkOptions sinks("test"); + 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); + 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); + BOOST_CHECK(opts.thread); +} + +QPID_AUTO_TEST_CASE(testOptionsDefault) { + Options opts(""); + qpid::log::posix::SinkOptions sinks("test"); + 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)); +} + +QPID_AUTO_TEST_CASE(testSelectorFromOptions) { + const char* argv[]={ + 0, + "--log-enable", "error+:foo", + "--log-enable", "debug:bar", + "--log-enable", "info" + }; + qpid::log::Options opts(""); + opts.parse(ARGC(argv), const_cast<char**>(argv)); + vector<string> expect=list_of("error+:foo")("debug:bar")("info"); + BOOST_CHECK_EQUAL(expect, opts.selectors); + Selector s(opts); + BOOST_CHECK(!s.isEnabled(warning, "x")); + BOOST_CHECK(!s.isEnabled(debug, "x")); + BOOST_CHECK(s.isEnabled(debug, "bar")); + BOOST_CHECK(s.isEnabled(error, "foo")); + BOOST_CHECK(s.isEnabled(critical, "foo")); +} + +QPID_AUTO_TEST_CASE(testLoggerStateure) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + Options opts("test"); + const char* argv[]={ + 0, + "--log-time", "no", + "--log-source", "yes", + "--log-to-stderr", "no", + "--log-to-file", "logging.tmp", + "--log-enable", "critical" + }; + opts.parse(ARGC(argv), const_cast<char**>(argv)); + l.configure(opts); + QPID_LOG(critical, "foo"); int srcline=__LINE__; + ifstream log("logging.tmp"); + string line; + getline(log, line); + string expect=(format("critical %s:%d: foo")%__FILE__%srcline).str(); + BOOST_CHECK_EQUAL(expect, line); + log.close(); + unlink("logging.tmp"); +} + +QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { + Logger& l=Logger::instance(); + ScopedSuppressLogging ls(l); + Options opts("test"); + opts.time=false; + qpid::log::posix::SinkOptions *sinks = + dynamic_cast<qpid::log::posix::SinkOptions *>(opts.sinkOptions.get()); + 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); + ifstream log("logging.tmp"); + string line; + getline(log, line, '\0'); + string expect="critical null\\x00tab\tspace newline\nret\r\\x80\\x99\\xFF\\x00\n"; + BOOST_CHECK_EQUAL(expect, line); + log.close(); + unlink("logging.tmp"); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/RC9/qpid/cpp/src/tests/multiq_perftest b/RC9/qpid/cpp/src/tests/multiq_perftest new file mode 100755 index 0000000000..10f9edd2a6 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/multiq_perftest @@ -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_perftest 10000 --mode shared --qt 16 diff --git a/RC9/qpid/cpp/src/tests/perfdist b/RC9/qpid/cpp/src/tests/perfdist new file mode 100755 index 0000000000..59548b23f7 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/perfdist @@ -0,0 +1,87 @@ +#!/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. +# + +set -e +usage() { +cat <<EOF +usage: $0 <perftest-args> -- <client-hosts ...> [ --- <broker hosts...> ] +Client & broker hosts can also be set in env vars CLIENTS and BROKERS. + +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. + +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 +COLLECT=ARGS +while test $# -gt 0; do + case $1 in + --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 ;; + -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 + +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 -b ${BROKERS[0]} +for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done +for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done +$PERFTEST --control -b ${BROKERS[0]} diff --git a/RC9/qpid/cpp/src/tests/perftest.cpp b/RC9/qpid/cpp/src/tests/perftest.cpp new file mode 100644 index 0000000000..be9ffd846e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/perftest.cpp @@ -0,0 +1,701 @@ +/* + * + * 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/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Completion.h" +#include "qpid/client/Message.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Time.h" + +#include <boost/lexical_cast.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include <iostream> +#include <sstream> +#include <numeric> +#include <algorithm> +#include <unistd.h> +#include <math.h> + + +using namespace std; +using namespace qpid; +using namespace client; +using namespace sys; +using boost::lexical_cast; +using boost::bind; + +enum Mode { SHARED, FANOUT, TOPIC }; +const char* modeNames[] = { "shared", "fanout", "topic" }; + +// istream/ostream ops so Options can read/display Mode. +istream& operator>>(istream& in, Mode& mode) { + string s; + in >> s; + int i = find(modeNames, modeNames+3, s) - modeNames; + if (i >= 3) throw Exception("Invalid mode: "+s); + mode = Mode(i); + return in; +} + +ostream& operator<<(ostream& out, Mode mode) { + return out << modeNames[mode]; +} + + +struct Opts : public TestOptions { + + // Actions + bool setup, control, publish, subscribe; + + // Queue policy + uint32_t queueMaxCount; + uint64_t queueMaxSize; + bool queueDurable; + + // Publisher + size_t pubs; + size_t count ; + size_t size; + bool confirm; + bool durable; + bool uniqueData; + bool syncPub; + + // Subscriber + size_t subs; + size_t ack; + + // General + size_t qt; + 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), + 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), txPub(0), txSub(0), commitAsync(false) + { + addOptions() + ("setup", optValue(setup), "Create shared queues.") + ("control", optValue(control), "Run test, print report.") + ("publish", optValue(publish), "Publish messages.") + ("subscribe", optValue(subscribe), "Subscribe for messages.") + + ("mode", optValue(mode, "shared|fanout|topic"), "Test mode." + "\nshared: --qt queues, --npubs publishers and --nsubs subscribers per queue.\n" + "\nfanout: --npubs publishers, --nsubs subscribers, fanout exchange." + "\ntopic: --qt topics, --npubs publishers and --nsubs subscribers per topic.\n") + + ("npubs", optValue(pubs, "N"), "Create N publishers.") + ("count", optValue(count, "N"), "Each publisher sends N messages.") + ("size", optValue(size, "BYTES"), "Size of messages in bytes.") + ("pub-confirm", optValue(confirm, "yes|no"), "Publisher use confirm-mode.") + ("durable", optValue(durable, "yes|no"), "Publish messages as durable.") + ("unique-data", optValue(uniqueData, "yes|no"), "Make data for each message unique.") + ("sync-publish", optValue(syncPub, "yes|no"), "Wait for confirmation of each message before sending the next one.") + + ("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.") + ("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'") + ("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 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 + size_t totalPubs; + size_t totalSubs; + size_t transfers; + size_t subQuota; + + void parse(int argc, char** argv) { + TestOptions::parse(argc, argv); + switch (mode) { + case SHARED: + if (count % subs) { + 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; + break; + case FANOUT: + if (qt != 1) cerr << "WARNING: Fanout mode, ignoring --qt=" + << qt << endl; + qt=1; + totalPubs = pubs; + totalSubs = subs; + subQuota = totalPubs*count; + break; + case TOPIC: + totalPubs = pubs*qt; + totalSubs = subs*qt; + subQuota = pubs*count; + 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; + } + } + } +}; + +const std::string Opts::helpText= +"There are two ways to use perftest: single process or multi-process.\n\n" +"If none of the --setup, --publish, --subscribe or --control options\n" +"are given perftest will run a single-process test.\n" +"For a multi-process test first run:\n" +" perftest --setup <other options>\n" +"and wait for it to complete. The remaining process should run concurrently::\n" +"Run --npubs times: perftest --publish <other options>\n" +"Run --nsubs times: perftest --subscribe <other options>\n" +"Run once: perftest --control <other options>\n" +"Note the <other options> must be identical for all processes.\n"; + +Opts opts; + +struct Client : public Runnable { + Connection connection; + AsyncSession session; + Thread thread; + + Client() { + opts.open(connection); + session = connection.newSession(); + } + + ~Client() { + try { + 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); + session.sync(); + } + + void run() { + queueInit("pub_start"); + queueInit("pub_done"); + queueInit("sub_ready"); + queueInit("sub_done"); + 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); + } + } + } +}; + +void expect(string actual, string expect) { + if (expect != actual) + throw Exception("Expecting "+expect+" but received "+actual); + +} + +double secs(Duration d) { return double(d)/TIME_SEC; } +double secs(AbsTime start, AbsTime finish) { + return secs(Duration(start,finish)); +} + + +// Collect rates & print stats. +class Stats { + vector<double> values; + double sum; + + public: + Stats() : sum(0) {} + + // Functor to collect rates. + void operator()(const string& data) { + try { + double d=lexical_cast<double>(data); + values.push_back(d); + sum += d; + } catch (const std::exception&) { + throw Exception("Bad report: "+data); + } + } + + double mean() const { + return sum/values.size(); + } + + double stdev() const { + if (values.size() <= 1) return 0; + double avg = mean(); + double ssq = 0; + for (vector<double>::const_iterator i = values.begin(); + i != values.end(); ++i) { + double x=*i; + x -= avg; + ssq += x*x; + } + return sqrt(ssq/(values.size()-1)); + } + + ostream& print(ostream& out) { + ostream_iterator<double> o(out, "\n"); + copy(values.begin(), values.end(), o); + out << "Average: " << mean(); + if (values.size() > 1) + out << " (std.dev. " << stdev() << ")"; + return out << endl; + } +}; + + +// Manage control queues, collect and print reports. +struct Controller : public Client { + + SubscriptionManager subs; + + Controller() : subs(session) {} + + /** Process messages from queue by applying a functor. */ + void process(size_t n, string queue, + boost::function<void (const string&)> msgFn) + { + if (!opts.summary) + cout << "Processing " << n << " messages from " + << queue << " " << flush; + LocalQueue lq; + subs.setFlowControl(n, SubscriptionManager::UNLIMITED, false); + subs.subscribe(lq, queue); + for (size_t i = 0; i < n; ++i) { + if (!opts.summary) cout << "." << flush; + msgFn(lq.pop().getData()); + } + if (!opts.summary) cout << " done." << endl; + } + + void process(size_t n, LocalQueue lq, string queue, + boost::function<void (const string&)> msgFn) + { + session.messageFlow(queue, 0, n); + if (!opts.summary) + cout << "Processing " << n << " messages from " + << queue << " " << flush; + for (size_t i = 0; i < n; ++i) { + if (!opts.summary) cout << "." << flush; + msgFn(lq.pop().getData()); + } + if (!opts.summary) cout << " done." << endl; + } + + void send(size_t n, string queue, string data) { + if (!opts.summary) + cout << "Sending " << data << " " << n << " times to " << queue + << endl; + Message msg(data, queue); + 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")); + + LocalQueue pubDone; + LocalQueue subDone; + subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false); + subs.subscribe(pubDone, "pub_done"); + subs.subscribe(subDone, "sub_done"); + + double txrateTotal(0); + double mbytesTotal(0); + double pubRateTotal(0); + double subRateTotal(0); + + for (size_t j = 0; j < opts.iterations; ++j) { + AbsTime start=now(); + send(opts.totalPubs, "pub_start", "start"); // Start publishers + + Stats pubRates; + Stats subRates; + + process(opts.totalPubs, pubDone, "pub_done", boost::ref(pubRates)); + process(opts.totalSubs, subDone, "sub_done", boost::ref(subRates)); + + AbsTime end=now(); + + double time=secs(start, end); + double txrate=opts.transfers/time; + double mbytes=(txrate*opts.size)/(1024*1024); + + if (!opts.summary) { + cout << endl << "Total " << opts.transfers << " transfers of " + << opts.size << " bytes in " + << time << " seconds." << endl; + cout << endl << "Publish transfers/sec: " << endl; + pubRates.print(cout); + cout << endl << "Subscribe transfers/sec: " << endl; + subRates.print(cout); + cout << endl + << "Total transfers/sec: " << txrate << endl + << "Total Mbytes/sec: " << mbytes << endl; + } + else { + cout << pubRates.mean() << "\t" + << subRates.mean() << "\t" + << txrate << "\t" + << mbytes << endl; + } + + txrateTotal += txrate; + mbytesTotal += mbytes; + pubRateTotal += pubRates.mean(); + subRateTotal += subRates.mean(); + } + if (opts.iterations > 1) { + cout << "Averages: "<< endl + << (pubRateTotal / opts.iterations) << "\t" + << (subRateTotal / opts.iterations) << "\t" + << (txrateTotal / opts.iterations) << "\t" + << (mbytesTotal / opts.iterations) << endl; + } + } + catch (const std::exception& e) { + cout << "Controller exception: " << e.what() << endl; + } + } +}; + + +struct PublishThread : public Client { + string destination; + string routingKey; + + PublishThread() {}; + + PublishThread(string key, string dest=string()) { + destination=dest; + routingKey=key; + } + + void run() { // Publisher + try { + string data; + size_t offset(0); + if (opts.uniqueData) { + offset = 5; + data += "data:";//marker (requested for latency testing tool scripts) + data += string(sizeof(size_t), 'X');//space for seq no + data += session.getId().str(); + if (opts.size > data.size()) { + data += string(opts.size - data.size(), 'X'); + } else if(opts.size < data.size()) { + cout << "WARNING: Increased --size to " << data.size() + << " to honour --unique-data" << endl; + } + } else { + size_t msgSize=max(opts.size, sizeof(size_t)); + data = string(msgSize, 'X'); + } + + Message msg(data, routingKey); + if (opts.durable) + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + + + if (opts.txPub){ + session.txSelect(); + } + SubscriptionManager subs(session); + LocalQueue lq; + subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true); + subs.subscribe(lq, "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), + reinterpret_cast<const char*>(&i), sizeof(size_t)); + if (opts.syncPub) { + sync(session).messageTransfer( + arg::destination=destination, + arg::content=msg, + arg::acceptMode=1); + } else { + session.messageTransfer( + arg::destination=destination, + arg::content=msg, + arg::acceptMode=1); + } + if (opts.txPub && ((i+1) % opts.txPub == 0)){ + if (opts.commitAsync){ + session.txCommit(); + } else { + sync(session).txCommit(); + } + } + if (opts.intervalPub) ::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"); + session.messageTransfer(arg::content=report, arg::acceptMode=1); + if (opts.txPub){ + sync(session).txCommit(); + } + } + session.close(); + } + catch (const std::exception& e) { + cout << "PublishThread exception: " << e.what() << endl; + } + } +}; + +struct SubscribeThread : public Client { + + string queue; + + SubscribeThread() {} + + SubscribeThread(string q) { queue = q; } + + SubscribeThread(string key, string ex) { + queue=session.getId().str(); // Unique name. + session.queueDeclare(arg::queue=queue, + arg::exclusive=true, + arg::autoDelete=true, + arg::durable=opts.durable); + session.exchangeBind(arg::queue=queue, + arg::exchange=ex, + arg::bindingKey=key); + } + + void verify(bool cond, const char* test, uint32_t expect, uint32_t actual) { + if (!cond) { + Message error( + QPID_MSG("Sequence error: expected n" << test << expect << " but got " << actual), + "sub_done"); + session.messageTransfer(arg::content=error, arg::acceptMode=1); + throw Exception(error.getData()); + } + } + + void run() { // Subscribe + try { + if (opts.txSub) sync(session).txSelect(); + SubscriptionManager subs(session); + 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.txSub) { + if (opts.commitAsync) session.txCommit(); + else sync(session).txCommit(); + } + + for (size_t j = 0; j < opts.iterations; ++j) { + if (j > 0) { + //need to allocate some more credit + 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.txSub && ((i+1) % opts.txSub == 0)) { + if (opts.commitAsync) session.txCommit(); + else sync(session).txCommit(); + } + if (opts.intervalSub) ::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 + // here, e.g. no std::map, std::string etc. + // + // For now verify order only for a single publisher. + size_t offset = opts.uniqueData ? 5 /*marker is 'data:'*/ : 0; + size_t n = *reinterpret_cast<const size_t*>(msg.getData().data() + offset); + if (opts.pubs == 1) { + if (opts.subs == 1 || opts.mode == FANOUT) verify(n==expect, "==", expect, n); + else verify(n>=expect, ">=", expect, n); + expect = n+1; + } + } + 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"); + session.messageTransfer(arg::content=result, arg::acceptMode=1); + if (opts.txSub) sync(session).txCommit(); + } + session.close(); + } + catch (const std::exception& e) { + cout << "SubscribeThread exception: " << e.what() << endl; + } + } +}; + +int main(int argc, char** argv) { + + try { + opts.parse(argc, argv); + + string exchange; + switch (opts.mode) { + case FANOUT: exchange="amq.fanout"; break; + case TOPIC: exchange="amq.topic"; break; + case SHARED: break; + } + + bool singleProcess= + (!opts.setup && !opts.control && !opts.publish && !opts.subscribe); + if (singleProcess) + opts.setup = opts.control = opts.publish = opts.subscribe = true; + + 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. + if (opts.publish) { + size_t n = singleProcess ? opts.pubs : 1; + for (size_t j = 0; j < n; ++j) { + pubs.push_back(new PublishThread(key.str(), exchange)); + pubs.back().thread=Thread(pubs.back()); + } + } + if (opts.subscribe) { + size_t n = singleProcess ? opts.subs : 1; + for (size_t j = 0; j < n; ++j) { + if (opts.mode==SHARED) + subs.push_back(new SubscribeThread(key.str())); + else + subs.push_back(new SubscribeThread(key.str(),exchange)); + subs.back().thread=Thread(subs.back()); + } + } + } + + 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; + } + return 1; +} + + diff --git a/RC9/qpid/cpp/src/tests/policy.acl b/RC9/qpid/cpp/src/tests/policy.acl new file mode 100644 index 0000000000..ef46026555 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/policy.acl @@ -0,0 +1 @@ +acl allow all all diff --git a/RC9/qpid/cpp/src/tests/publish.cpp b/RC9/qpid/cpp/src/tests/publish.cpp new file mode 100644 index 0000000000..34c2b8fefc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/publish.cpp @@ -0,0 +1,128 @@ +/* + * + * 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 <algorithm> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace std; + +typedef vector<string> StringSet; + +struct Args : public qpid::TestOptions { + uint size; + uint count; + bool durable; + string destination; + string routingKey; + bool summary; + bool id; + + 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") + ("summary,s", optValue(summary), "Output only the rate.") + ("id", optValue(id), "Add unique correlation ID"); + } +}; + +Args opts; + +struct Client +{ + Connection connection; + AsyncSession session; + + Client() + { + opts.open(connection); + session = connection.newSession(); + } + + // 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++) { + 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() + { + try{ + session.close(); + connection.close(); + } catch(const exception& e) { + cout << e.what() << endl; + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Client client; + client.publish(); + return 0; + } catch(const exception& e) { + cout << e.what() << endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/python_tests b/RC9/qpid/cpp/src/tests/python_tests new file mode 100755 index 0000000000..30bb8259a4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/python_tests @@ -0,0 +1,39 @@ +#!/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. +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; } +} + +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 diff --git a/RC9/qpid/cpp/src/tests/quick_perftest b/RC9/qpid/cpp/src/tests/quick_perftest new file mode 100755 index 0000000000..4f7cf3cb54 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/quick_perftest @@ -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 ./perftest --summary --count 100 diff --git a/RC9/qpid/cpp/src/tests/quick_topictest b/RC9/qpid/cpp/src/tests/quick_topictest new file mode 100755 index 0000000000..5e7d85849f --- /dev/null +++ b/RC9/qpid/cpp/src/tests/quick_topictest @@ -0,0 +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. +# + + +# Quick and quiet topic test for make check. +test -z "$srcdir" && srcdir=. +$srcdir/topictest -s2 -m2 -b1 > topictest.log 2>&1 || { + echo $0 FAILED: + cat topictest.log + exit 1 +} +rm topictest.log diff --git a/RC9/qpid/cpp/src/tests/quick_txtest b/RC9/qpid/cpp/src/tests/quick_txtest new file mode 100755 index 0000000000..938e3805d8 --- /dev/null +++ b/RC9/qpid/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/RC9/qpid/cpp/src/tests/receiver.cpp b/RC9/qpid/cpp/src/tests/receiver.cpp new file mode 100644 index 0000000000..1b0b6b2548 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/receiver.cpp @@ -0,0 +1,129 @@ +/* + * + * 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/SubscriptionManager.h> +#include "TestOptions.h" + +#include <iostream> +#include <fstream> + + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + +struct Args : public qpid::TestOptions +{ + string queue; + uint messages; + bool ignoreDuplicates; + uint creditWindow; + uint ackFrequency; + + Args() : queue("test-queue"), messages(0), ignoreDuplicates(false), creditWindow(0), ackFrequency(1) + { + 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)"); + } +}; + +const string EOS("eos"); + +class Receiver : public MessageListener, public FailoverManager::Command +{ + public: + Receiver(const string& queue, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency); + 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) : + queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0) +{ + 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(); +} + +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); + connection.execute(receiver); + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cerr << "Failure: " << error.what() << std::endl; + } + return 1; +} + + + diff --git a/RC9/qpid/cpp/src/tests/replaying_sender.cpp b/RC9/qpid/cpp/src/tests/replaying_sender.cpp new file mode 100644 index 0000000000..7e148e277f --- /dev/null +++ b/RC9/qpid/cpp/src/tests/replaying_sender.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 <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; + +class Sender : public FailoverManager::Command +{ + public: + Sender(const std::string& queue, uint count, uint reportFreq); + void execute(AsyncSession& session, bool isRetry); + uint getSent(); + + int verbosity; + + private: + MessageReplayTracker sender; + const uint count; + uint sent; + const uint reportFrequency; + Message message; + +}; + +Sender::Sender(const std::string& queue, uint count_, uint reportFreq ) : sender(10), count(count_), sent(0), reportFrequency(reportFreq) +{ + message.getDeliveryProperties().setRoutingKey(queue); +} + +void Sender::execute(AsyncSession& session, bool isRetry) +{ + 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); + sender.send(message); + if (count > reportFrequency && !(sent % reportFrequency)) { + if ( verbosity > 0 ) + std::cout << "sent " << sent << " of " << count << std::endl; + } + } + message.setData("That's all, folks!"); + sender.send(message); + + if ( verbosity > 0 ) + std::cout << "SENDER COMPLETED\n"; +} + +uint Sender::getSent() +{ + return sent; +} + +int main(int argc, char ** argv) +{ + ConnectionSettings settings; + + if ( argc != 6 ) + { + std::cerr << "Usage: replaying_sender host port n_messages report_frequency verbosity\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]); + + FailoverManager connection(settings); + Sender sender("message_queue", n_messages, reportFrequency ); + sender.verbosity = verbosity; + try { + connection.execute ( sender ); + if ( verbosity > 0 ) + { + std::cout << "Sender finished. Sent " + << sender.getSent() + << " messages." + << 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/RC9/qpid/cpp/src/tests/restart_cluster b/RC9/qpid/cpp/src/tests/restart_cluster new file mode 100755 index 0000000000..6a6abc8042 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/restart_cluster @@ -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. +# + +# Re-start a cluster on the local host. + +srcdir=`dirname $0` +$srcdir/stop_cluster +exec $srcdir/start_cluster "$@" +#!/bin/sh +# Re-start a cluster on the local host. + +srcdir=`dirname $0` +$srcdir/stop_cluster +exec $srcdir/start_cluster "$@" +#!/bin/sh +# Re-start a cluster on the local host. + +srcdir=`dirname $0` +$srcdir/stop_cluster +exec $srcdir/start_cluster "$@" diff --git a/RC9/qpid/cpp/src/tests/resuming_receiver.cpp b/RC9/qpid/cpp/src/tests/resuming_receiver.cpp new file mode 100644 index 0000000000..f49a115e1e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/resuming_receiver.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/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; + + +class Listener : public MessageListener, + public FailoverManager::Command, + public FailoverManager::ReconnectionStrategy +{ + public: + Listener ( int report_frequency = 1000, int verbosity = 0 ); + void received(Message& message); + void execute(AsyncSession& session, bool isRetry); + void check(); + void editUrlList(std::vector<Url>& urls); + private: + Subscription subscription; + uint count; + uint received_twice; + uint lastSn; + bool gaps; + uint reportFrequency; + int verbosity; +}; + + +Listener::Listener(int freq, int verbosity) + : count(0), + received_twice(0), + lastSn(0), + gaps(false), + reportFrequency(freq), + verbosity(verbosity) +{} + + +void Listener::received(Message & message) +{ + if (message.getData() == "That's all, folks!") + { + if(verbosity > 0 ) + { + std::cout << "Shutting down listener for " + << message.getDestination() << std::endl; + + std::cout << "Listener received " + << count + << " messages (" + << received_twice + << " received_twice)" + << endl; + } + subscription.cancel(); + if ( verbosity > 0 ) + std::cout << "LISTENER COMPLETED\n"; + } else { + uint sn = message.getHeaders().getAsInt("sn"); + if (lastSn < sn) { + if (sn - lastSn > 1) { + std::cerr << "Error: gap in sequence between " << lastSn << " and " << sn << std::endl; + gaps = true; + } + lastSn = sn; + ++count; + if ( ! ( count % reportFrequency ) ) { + if ( verbosity > 0 ) + std::cout << "Listener has received " + << count + << " messages.\n"; + } + } else { + ++received_twice; + } + } +} + +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 (isRetry) { + // std::cout << "Resuming from " << count << std::endl; + } + SubscriptionManager subs(session); + subscription = subs.subscribe(*this, "message_queue"); + subs.run(); +} + +void Listener::editUrlList(std::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) std::rotate(urls.begin(), urls.begin() + 1, urls.end()); +} + +int main(int argc, char ** argv) +{ + ConnectionSettings settings; + + if ( argc != 5 ) + { + std::cerr << "Usage: resuming_receiver host port report_frequency verbosity\n"; + return 1; + } + + settings.host = argv[1]; + settings.port = atoi(argv[2]); + int reportFrequency = atoi(argv[3]); + int verbosity = atoi(argv[4]); + + Listener listener(reportFrequency, verbosity); + FailoverManager connection(settings, &listener); + + try { + connection.execute(listener); + connection.close(); + listener.check(); + return 0; + } catch(const std::exception& error) { + std::cerr << "Receiver failed: " << error.what() << std::endl; + } + return 1; +} + + + diff --git a/RC9/qpid/cpp/src/tests/run-unit-tests b/RC9/qpid/cpp/src/tests/run-unit-tests new file mode 100755 index 0000000000..862a76c4f5 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run-unit-tests @@ -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. +# + +# +# 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: +# +# Selected test classes: +# ./run-unit-tests ValueTest ClientChannelTest +# +# Individual test method +# ./run-unit-tests ValueTest :ValueTest::testStringValueEquals +# +# Build and run selected tests: +# make check TESTS=run-unit-tests UNIT_TESTS=ClientChannelTest +# + +for u in $* $UNIT_TESTS ; do + case $u in + :*) TEST_ARGS="$TEST_ARGS $u" ;; # A test path. + *) TEST_ARGS="$TEST_ARGS .libs/$u.so" ;; # A test library. + esac +done +test -z "$TEST_ARGS" && TEST_ARGS=".libs/*Test.so" + +test -z "$srcdir" && srcdir=. + +# libdlclose_noop prevents unloading symbols needed for valgrind output. +export LD_PRELOAD=.libs/libdlclose_noop.so +source $srcdir/run_test DllPlugInTester -c -b $TEST_ARGS diff --git a/RC9/qpid/cpp/src/tests/run_acl_tests b/RC9/qpid/cpp/src/tests/run_acl_tests new file mode 100755 index 0000000000..8d583c1895 --- /dev/null +++ b/RC9/qpid/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. +PYTHON_DIR=$srcdir/../../../python +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 ../.libs/acl.so --acl-file policy.acl --auth no > qpidd.port + LOCAL_PORT=`cat qpidd.port` +} + +stop_brokers() { + ../qpidd -q --port $LOCAL_PORT +} + +test_loading_acl_from_absolute_path(){ + POLICY_FILE=$PWD/$srcdir/policy.acl + ../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no --load-module ../.libs/acl.so --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null + PORT=`grep "Listening on TCP port" temp.log | awk '{print $8}'` + ACL_FILE=`grep "notice Read ACL file" temp.log | awk '{print $7}'` + ../qpidd -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 + export PYTHONPATH + $srcdir/acl.py -v -s $srcdir/../../../specs/amqp.0-10-qpid-errata.xml -b localhost:$LOCAL_PORT --port $LOCAL_PORT || EXITCODE=1 + stop_brokers || EXITCODE=1 + test_loading_acl_from_absolute_path || EXITCODE=1 + rm -rf $DATA_DIR + exit $EXITCODE +fi + diff --git a/RC9/qpid/cpp/src/tests/run_failover_soak b/RC9/qpid/cpp/src/tests/run_failover_soak new file mode 100755 index 0000000000..9dddf59cf1 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_failover_soak @@ -0,0 +1,56 @@ +#!/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. +# + +# Check AIS requirements and run tests if found. +id -ng | grep '\<ais\>' >/dev/null || \ + NOGROUP="The ais group is not your primary 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: + + $NOGROUP + $NOAISEXEC + + ========================================================== + +EOF + exit 0; # A warning, not a failure. +fi + + +host=127.0.0.1 + +src_root=.. +module_dir=$src_root/.libs +n_messages=300000 +report_frequency=10000 +verbosity=1 + + +exec `dirname $0`/failover_soak $src_root $module_dir $host ./declare_queues ./replaying_sender ./resuming_receiver $n_messages $report_frequency $verbosity + diff --git a/RC9/qpid/cpp/src/tests/run_federation_tests b/RC9/qpid/cpp/src/tests/run_federation_tests new file mode 100755 index 0000000000..28bcc012cc --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_federation_tests @@ -0,0 +1,52 @@ +#!/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 + +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 -q --port $LOCAL_PORT + ../qpidd -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 $@ + RETCODE=$? + stop_brokers + if test x$RETCODE != x0; then + echo "FAIL federation tests"; exit 1; + fi +fi + diff --git a/RC9/qpid/cpp/src/tests/run_header_test b/RC9/qpid/cpp/src/tests/run_header_test new file mode 100755 index 0000000000..39d4a24f84 --- /dev/null +++ b/RC9/qpid/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` +PYTHON_DIR=$srcdir/../../../python +test -f qpidd.port && QPID_PORT=`cat qpidd.port` + +if test -d ${PYTHON_DIR} ; then + ./header_test -p $QPID_PORT + export PYTHONPATH=$PYTHON_DIR:$PYTHONPATH + $srcdir/header_test.py "localhost" $QPID_PORT +else + echo "Skipping header test as python libs not found" +fi + diff --git a/RC9/qpid/cpp/src/tests/run_perftest b/RC9/qpid/cpp/src/tests/run_perftest new file mode 100755 index 0000000000..1a9b934641 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_perftest @@ -0,0 +1,28 @@ +#!/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. +# +MULTIPLIER=3 +COUNT=`expr $1 \* $MULTIPLIER` +shift +exec `dirname $0`/run_test ./perftest --summary --count $COUNT "$@" diff --git a/RC9/qpid/cpp/src/tests/run_test b/RC9/qpid/cpp/src/tests/run_test new file mode 100755 index 0000000000..062e9e137e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/run_test @@ -0,0 +1,78 @@ +#!/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 $VALGRIND if is set run under valgrind. If there are valgrind +# erros show valgrind output, also leave it in <test>.valgrind for +# examination. +# + +srcdir=`dirname $0` +. $srcdir/vg_check + +# Export variables from makefile. +export VALGRIND srcdir + +# Set QPID_PORT if qpidd.port exists. +test -f qpidd.port && QPID_PORT=`cat qpidd.port` +export QPID_PORT + +# Avoid silly libtool error messages if these are not defined +test -z "$LC_ALL" && LC_ALL= +test -z "$LC_CTYPE" && LC_CTYPE= +test -z "$LC_COLLATE" && LC_COLLATE= +test -z "$LC_MESSAGES" && LC_MESSAGES= +export LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + +VG_LOG="`basename $1`.vglog" +rm -f $VG_LOG* + +VALGRIND_OPTS=" +--gen-suppressions=all +--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 + test -n "$VALGRIND" && { vg_check $VG_LOG* || ERROR=1 ; } +else + # This is a non-libtool shell script, just execute it. + exec "$@" +fi + +exit $ERROR + diff --git a/RC9/qpid/cpp/src/tests/sender.cpp b/RC9/qpid/cpp/src/tests/sender.cpp new file mode 100644 index 0000000000..a02b713d86 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/sender.cpp @@ -0,0 +1,100 @@ +/* + * + * 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 "TestOptions.h" + +#include <iostream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; + +using namespace std; + +struct Args : public qpid::TestOptions +{ + string destination; + string key; + uint sendEos; + + Args() : key("test-queue"), sendEos(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"); + } +}; + +const string EOS("eos"); + +class Sender : public FailoverManager::Command +{ + public: + Sender(const std::string& destination, const std::string& key, uint sendEos); + void execute(AsyncSession& session, bool isRetry); + private: + MessageReplayTracker sender; + Message message; + const uint sendEos; + uint sent; +}; + +Sender::Sender(const std::string& destination, const std::string& key, uint eos) : + sender(10), message(destination, key), sendEos(eos), sent(0) {} + +void Sender::execute(AsyncSession& session, bool isRetry) +{ + if (isRetry) sender.replay(session); + else sender.init(session); + string data; + while (std::cin >> data) { + message.setData(data); + message.getHeaders().setInt("sn", ++sent); + sender.send(message); + } + for (uint i = sendEos; i > 0; --i) { + message.setData(EOS); + sender.send(message); + } +} + +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); + connection.execute(sender); + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << "Failed: " << error.what() << std::endl; + } + return 1; +} diff --git a/RC9/qpid/cpp/src/tests/shared_perftest b/RC9/qpid/cpp/src/tests/shared_perftest new file mode 100755 index 0000000000..cc192d25bd --- /dev/null +++ b/RC9/qpid/cpp/src/tests/shared_perftest @@ -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_perftest 100000 --mode shared --npubs 16 --nsubs 16 diff --git a/RC9/qpid/cpp/src/tests/shlibtest.cpp b/RC9/qpid/cpp/src/tests/shlibtest.cpp new file mode 100644 index 0000000000..80320ea7be --- /dev/null +++ b/RC9/qpid/cpp/src/tests/shlibtest.cpp @@ -0,0 +1,28 @@ +/* + * 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. + * + */ + +int* loaderData = 0; +extern "C" void callMe(int *i) { loaderData=i; } + +struct OnUnload { ~OnUnload() { *loaderData=42; } }; +OnUnload unloader; // For destructor. + + + diff --git a/RC9/qpid/cpp/src/tests/ssl.mk b/RC9/qpid/cpp/src/tests/ssl.mk new file mode 100644 index 0000000000..cb887c8fda --- /dev/null +++ b/RC9/qpid/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/RC9/qpid/cpp/src/tests/ssl_test b/RC9/qpid/cpp/src/tests/ssl_test new file mode 100755 index 0000000000..047db93d20 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/ssl_test @@ -0,0 +1,71 @@ +#!/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 + +CONFIG=$(dirname $0)/config.null +CERT_DIR=`pwd`/test_cert_db +CERT_PW_FILE=`pwd`/cert.password +HOSTNAME=`hostname` +COUNT=10000 + +trap stop_broker 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 ${HOSTNAME} -s "CN=${HOSTNAME}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil +} + +start_broker() { + ../qpidd --daemon --transport ssl --port 0 --ssl-port 0 --no-data-dir --no-module-dir --auth no --config $CONFIG --load-module ../.libs/ssl.so --ssl-cert-db $CERT_DIR --ssl-cert-password-file $CERT_PW_FILE > qpidd.port + PORT=`cat qpidd.port` +} + +stop_broker() { + if [[ $PORT ]] ; then + ../qpidd -q --port $PORT + fi +} +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 +if [[ !(-e ${CERT_DIR}) ]] ; then + create_certs || error "Could not create test certificate" +fi + +start_broker || error "Could not start broker" +echo "Running SSL test on port $PORT" +export QPID_NO_MODULE_DIR=1 +export QPID_LOAD_MODULE=../.libs/sslconnector.so +export QPID_SSL_CERT_DB=${CERT_DIR} +export QPID_SSL_CERT_PASSWORD_FILE=${CERT_PW_FILE} +./perftest --count ${COUNT} --port ${PORT} -P ssl -b $HOSTNAME --summary + diff --git a/RC9/qpid/cpp/src/tests/start_broker b/RC9/qpid/cpp/src/tests/start_broker new file mode 100755 index 0000000000..093c44051a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/start_broker @@ -0,0 +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-to-file qpidd.log "$@" > qpidd.port diff --git a/RC9/qpid/cpp/src/tests/start_cluster b/RC9/qpid/cpp/src/tests/start_cluster new file mode 100755 index 0000000000..ee306edf14 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/start_cluster @@ -0,0 +1,46 @@ +#!/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/cluster.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 --log-to-file=cluster.log || exit 1 +else + for (( i=0; i<SIZE; ++i )); do + PORT=`with_ais_group ../qpidd -p0 --log-to-file=cluster$i.log $OPTS` || exit 1 + echo $PORT >> cluster.ports + done +fi + diff --git a/RC9/qpid/cpp/src/tests/start_cluster_hosts b/RC9/qpid/cpp/src/tests/start_cluster_hosts new file mode 100755 index 0000000000..7680da01ad --- /dev/null +++ b/RC9/qpid/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 -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/RC9/qpid/cpp/src/tests/stop_broker b/RC9/qpid/cpp/src/tests/stop_broker new file mode 100755 index 0000000000..2f45309a2b --- /dev/null +++ b/RC9/qpid/cpp/src/tests/stop_broker @@ -0,0 +1,41 @@ +#!/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 + +# Check qpidd.log. +egrep 'warning\|error\|critical' qpidd.log && { + echo "WARNING: Suspicious broker log entries in qpidd.log, 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/RC9/qpid/cpp/src/tests/stop_cluster b/RC9/qpid/cpp/src/tests/stop_cluster new file mode 100755 index 0000000000..b3f0e7395e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/stop_cluster @@ -0,0 +1,33 @@ +#!/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" +done +rm -f cluster.ports + +if [ -n "$ERROR" ]; then + echo "Errors stopping brokers on ports: $ERROR" + exit 1 +fi diff --git a/RC9/qpid/cpp/src/tests/test_tools.h b/RC9/qpid/cpp/src/tests/test_tools.h new file mode 100644 index 0000000000..37a6594f8a --- /dev/null +++ b/RC9/qpid/cpp/src/tests/test_tools.h @@ -0,0 +1,94 @@ +#ifndef TEST_TOOLS_H +#define TEST_TOOLS_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/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 <ostream> + +// Print a sequence +template <class T> std::ostream& seqPrint(std::ostream& o, const T& seq) { + std::copy(seq.begin(), seq.end(), std::ostream_iterator<typename T::value_type>(o, " ")); + return o; +} + +// Compare sequences +template <class T, class U> +bool seqEqual(const T& a, const U& b) { + typename T::const_iterator i = a.begin(); + typename U::const_iterator j = b.begin(); + while (i != a.end() && j != b.end() && *i == *j) { ++i; ++j; } + return (i == a.end()) && (j == b.end()); +} + +// ostream and == operators so we can compare vectors and 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 boost::assign_detail::generic_list<T>& l) { return seqPrint(o, l); } + +template <class T> +bool operator == (const vector<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 vector<T>& a) { return seqEqual(a, b); } +} + +/** NB: order of parameters is regex first, in line with + * CHECK(expected, actual) convention. + */ +inline bool regexPredicate(const std::string& re, const std::string& text) { + return boost::regex_match(text, boost::regex(re)); +} + +/** Check for regular expression match. You must #include <boost/regex.hpp> */ +#if (BOOST_VERSION < 103300) + #define BOOST_CHECK_REGEX(re, text) +#else + #define BOOST_CHECK_REGEX(re, text) \ + BOOST_CHECK_PREDICATE(regexPredicate, (re)(text)) +#endif + +/** 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; +}; + + +#endif /*!TEST_TOOLS_H*/ + diff --git a/RC9/qpid/cpp/src/tests/topic_listener.cpp b/RC9/qpid/cpp/src/tests/topic_listener.cpp new file mode 100644 index 0000000000..7bdc2c32de --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topic_listener.cpp @@ -0,0 +1,202 @@ +/* + * + * 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 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 + * recognises two types of 'special' message that tell it to (a) send + * a report containing this information, (b) shutdown (i.e. stop + * listening). + */ + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/FieldValue.h" +#include <iostream> +#include <sstream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace qpid::framing; +using namespace std; + +/** + * A message listener implementation in which the runtime logic is + * defined. + */ +class Listener : public MessageListener{ + Session session; + SubscriptionManager& mgr; + const string responseQueue; + const bool transactional; + 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; +}; + +/** + * A utility class for managing the options passed in. + */ +struct Args : public qpid::TestOptions { + int ack; + bool transactional; + bool durable; + int prefetch; + string statusqueue; + + Args() : ack(0), transactional(false), durable(false), prefetch(0) { + addOptions() + ("ack", optValue(ack, "MODE"), "Ack frequency in messages (defaults to half the prefetch value)") + ("transactional", optValue(transactional), "Use transactions") + ("durable", optValue(durable), "subscribers should use durable queues") + ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)") + ("status-queue", optValue(statusqueue, "QUEUE-NAME"), "Message queue to put status messages on"); + } +}; + + +/** + * The main routine creates a Listener instance and sets it up to + * consume from a private queue bound to the exchange with the + * appropriate topic name. + */ +int main(int argc, char** argv){ + try{ + Args args; + args.parse(argc, argv); + if(args.help) + cout << args << endl; + else { + Connection connection; + args.open(connection); + AsyncSession session = connection.newSession(); + + //declare exchange, queue and bind them: + session.queueDeclare(arg::queue="response"); + std::string control = "control_" + session.getId().str(); + if (args.durable) { + session.queueDeclare(arg::queue=control, arg::durable=true); + } else { + session.queueDeclare(arg::queue=control, arg::exclusive=true, arg::autoDelete=true); + } + session.exchangeBind(arg::exchange="amq.topic", arg::queue=control, arg::bindingKey="topic_control"); + + //set up listener + SubscriptionManager mgr(session); + Listener listener(session, mgr, "response", args.transactional); + SubscriptionSettings settings; + if (args.prefetch) { + settings.autoAck = (args.ack ? args.ack : (args.prefetch / 2)); + settings.flowControl = FlowControl::messageCredit(args.prefetch); + } else { + settings.acceptMode = ACCEPT_MODE_NONE; + settings.flowControl = FlowControl::unlimited(); + } + listener.subscription = mgr.subscribe(listener, control, settings); + session.sync(); + + if( args.statusqueue.length() > 0 ) { + stringstream msg_str; + msg_str << "topic_listener: " << (int)getpid(); + session.messageTransfer(arg::content=Message(msg_str.str(), args.statusqueue)); + cout << "Ready status put on queue '" << args.statusqueue << "'" << endl; + } + + if (args.transactional) { + session.txSelect(); + } + + cout << "topic_listener: listening..." << endl; + mgr.run(); + if (args.durable) { + session.queueDelete(arg::queue=control); + } + session.close(); + cout << "closing connection" << endl; + connection.close(); + } + return 0; + } catch (const std::exception& error) { + cout << "topic_listener: " << error.what() << endl; + } + 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().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(); + } +} + diff --git a/RC9/qpid/cpp/src/tests/topic_perftest b/RC9/qpid/cpp/src/tests/topic_perftest new file mode 100755 index 0000000000..cd440b2458 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topic_perftest @@ -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_perftest 10000 --mode topic --qt 16 diff --git a/RC9/qpid/cpp/src/tests/topic_publisher.cpp b/RC9/qpid/cpp/src/tests/topic_publisher.cpp new file mode 100644 index 0000000000..f37ad2dc0e --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topic_publisher.cpp @@ -0,0 +1,224 @@ +/* + * + * 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 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 + * request that each subscriber report back with the number of message + * it has received and the time that elapsed between receiving the + * first one and receiving the report request. Once the expected + * number of reports are received, it sends out a request that each + * subscriber shutdown. + */ + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#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> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace std; + +/** + * 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 { + AsyncSession session; + SubscriptionManager mgr; + LocalQueue queue; + const string controlTopic; + const bool transactional; + const bool durable; + + string generateData(int size); + +public: + Publisher(const AsyncSession& session, const string& controlTopic, bool tx, bool durable); + int64_t publish(int msgs, int listeners, int size); + void terminate(); +}; + +/** + * A utility class for managing the options passed in to the test + */ +struct Args : public TestOptions { + int messages; + int subscribers; + bool transactional; + bool durable; + int batches; + int delay; + int size; + string statusqueue; + + Args() : messages(1000), subscribers(1), + transactional(false), durable(false), + batches(1), delay(0), size(256) + { + addOptions() + ("messages", optValue(messages, "N"), "how many messages to send") + ("subscribers", optValue(subscribers, "N"), "how many subscribers to expect reports from") + ("transactional", optValue(transactional), "client should use transactions") + ("durable", optValue(durable), "messages should be durable") + ("batches", optValue(batches, "N"), "how many batches to run") + ("delay", optValue(delay, "SECONDS"), "Causes a delay between each batch") + ("size", optValue(size, "BYTES"), "size of the published messages") + ("status-queue", optValue(statusqueue, "QUEUE-NAME"), "Message queue to read status messages from"); + } +}; + +int main(int argc, char** argv) { + try{ + Args args; + args.parse(argc, argv); + if(args.help) + cout << args << endl; + else { + Connection connection; + args.open(connection); + AsyncSession session = connection.newSession(); + + // If status-queue is defined, wait for all expected listeners to join in before we start + if( args.statusqueue.length() > 0 ) { + cout << "Waiting for " << args.subscribers << " listeners..." << endl; + SubscriptionManager statusSubs(session); + LocalQueue statusQ; + statusSubs.subscribe(statusQ, args.statusqueue); + for (int i = 0; i < args.subscribers; i++) { + 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; + } else { + throw Exception(QPID_MSG("Unexpected message received on status queue: " << m.getData())); + } + } + } + + if (args.transactional) { + session.txSelect(); + } + session.queueDeclare(arg::queue="response"); + session.exchangeBind(arg::exchange="amq.direct", arg::queue="response", arg::bindingKey="response"); + + Publisher publisher(session, "topic_control", args.transactional, args.durable); + + int batchSize(args.batches); + int64_t max(0); + int64_t min(0); + int64_t sum(0); + for(int i = 0; i < batchSize; i++){ + if(i > 0 && args.delay) sleep(args.delay); + int64_t msecs = + publisher.publish(args.messages, + args.subscribers, + args.size) / TIME_MSEC; + if(!max || msecs > max) max = msecs; + if(!min || msecs < min) min = msecs; + sum += msecs; + cout << "Completed " << (i+1) << " of " << batchSize + << " in " << msecs << "ms" << endl; + } + publisher.terminate(); + int64_t avg = sum / batchSize; + if(batchSize > 1){ + cout << batchSize << " batches completed. avg=" << avg << + ", max=" << max << ", min=" << min << endl; + } + session.close(); + connection.close(); + } + return 0; + }catch(exception& error) { + cout << error.what() << endl; + } + 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/RC9/qpid/cpp/src/tests/topictest b/RC9/qpid/cpp/src/tests/topictest new file mode 100755 index 0000000000..8fd680ee35 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/topictest @@ -0,0 +1,61 @@ +#!/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 +rm -f subscriber_*.log + +# Defaults values +SUBSCRIBERS=10 +MESSAGES=2000 +BATCHES=10 + +while getopts "s:m:b:h:t" opt ; do + case $opt in + s) SUBSCRIBERS=$OPTARG ;; + m) MESSAGES=$OPTARG ;; + b) BATCHES=$OPTARG ;; + h) HOST=-h$OPTARG ;; + t) TRANSACTIONAL="--transactional --durable" ;; + ?) + echo "Usage: %0 [-s <subscribers>] [-m <messages.] [-b <batches>]" + exit 1 + ;; + esac +done + +subscribe() { + echo Start subscriber $1 + LOG="subscriber_$1.log" + ./topic_listener $TRANSACTIONAL > $LOG 2>&1 && rm -f $LOG +} + +publish() { + ./topic_publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS $HOST $TRANSACTIONAL +} + +for ((i=$SUBSCRIBERS ; i--; )); do + subscribe $i & +done +# FIXME aconway 2007-03-27: Hack around startup race. Fix topic test. +sleep 2 +publish 2>&1 || exit 1 diff --git a/RC9/qpid/cpp/src/tests/txjob.cpp b/RC9/qpid/cpp/src/tests/txjob.cpp new file mode 100644 index 0000000000..94db96a666 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/txjob.cpp @@ -0,0 +1,95 @@ +/* + * + * 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; + +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"); + } +}; + +//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/RC9/qpid/cpp/src/tests/txshift.cpp b/RC9/qpid/cpp/src/tests/txshift.cpp new file mode 100644 index 0000000000..5db08d7a53 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/txshift.cpp @@ -0,0 +1,185 @@ +/* + * + * 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; + +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(); + } +}; + +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)); + } + for_each(workers.begin(), workers.end(), boost::bind(&Worker::start, _1)); + 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/RC9/qpid/cpp/src/tests/txtest.cpp b/RC9/qpid/cpp/src/tests/txtest.cpp new file mode 100644 index 0000000000..0c8ce90648 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/txtest.cpp @@ -0,0 +1,329 @@ +/* + * + * 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 <algorithm> +#include <iomanip> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/AsyncSession.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/framing/Array.h" +#include "qpid/framing/Buffer.h" +#include "qpid/sys/uuid.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using std::string; + +typedef std::vector<std::string> StringSet; + +struct Args : public qpid::TestOptions { + bool init, transfer, check;//actions + uint size; + bool durable; + uint queues; + string base; + uint msgsPerTx; + uint txCount; + uint totalMsgCount; + bool dtx; + bool quiet; + + Args() : init(true), transfer(true), check(true), + size(256), durable(true), queues(2), + base("tx-test"), msgsPerTx(1), txCount(1), totalMsgCount(10), + dtx(false), quiet(false) + { + 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.") + ("check", optValue(check, "yes|no"), "Check that the initial messages are all still available.") + ("size", optValue(size, "N"), "message size") + ("durable", optValue(durable, "yes|no"), "use durable messages") + ("queues", optValue(queues, "N"), "number of queues") + ("queue-base-name", optValue(base, "<name>"), "base name for queues") + ("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") + ("quiet", optValue(quiet), "reduce output from test"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +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; + } + data += chars.substr(0, size % chars.length()); + return data; +} + +void generateSet(const std::string& base, uint count, StringSet& collection) +{ + for (uint i = 0; i < count; i++) { + std::ostringstream out; + out << base << "-" << (i+1); + collection.push_back(out.str()); + } +} + +Args opts; + +struct Client +{ + Connection connection; + AsyncSession session; + + Client() + { + opts.open(connection); + session = connection.newSession(); + } + + ~Client() + { + try{ + session.close(); + connection.close(); + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + } +}; + +struct Transfer : public Client, public Runnable +{ + std::string src; + std::string dest; + Thread thread; + uuid_t uuid; + char uuidStr[37]; // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + trailing \0 + framing::Xid xid; + + Transfer(const std::string& to, const std::string& from) : src(to), dest(from), xid(0x4c414e47, "", from) {} + + void run() + { + try { + + if (opts.dtx) session.dtxSelect(); + else session.txSelect(); + SubscriptionManager subs(session); + + 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) { + setNewXid(xid); + session.dtxStart(arg::xid=xid); + } + for (uint m = 0; m < opts.msgsPerTx; m++) { + in = lq.pop(); + out.setData(in.getData()); + out.getMessageProperties().setCorrelationId(in.getMessageProperties().getCorrelationId()); + out.getDeliveryProperties().setDeliveryMode(in.getDeliveryProperties().getDeliveryMode()); + session.messageTransfer(arg::content=out, arg::acceptMode=1); + } + sub.accept(sub.getUnaccepted()); + if (opts.dtx) { + session.dtxEnd(arg::xid=xid); + session.dtxPrepare(arg::xid=xid); + session.dtxCommit(arg::xid=xid); + } else { + session.txCommit(); + } + } + } catch(const std::exception& e) { + std::cout << "Transfer interrupted: " << e.what() << std::endl; + } + } + + void setNewXid(framing::Xid& xid) { + ::uuid_generate(uuid); + ::uuid_unparse(uuid, uuidStr); + xid.setGlobalId(uuidStr); + } +}; + +struct Controller : public Client +{ + StringSet ids; + StringSet queues; + + Controller() + { + generateSet(opts.base, opts.queues, queues); + generateSet("msg", opts.totalMsgCount, ids); + } + + void init() + { + //declare queues + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + session.queueDeclare(arg::queue=*i, arg::durable=opts.durable); + session.sync(); + } + + Message msg(generateData(opts.size), *queues.begin()); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + //publish messages + for (StringSet::iterator i = ids.begin(); i != ids.end(); i++) { + msg.getMessageProperties().setCorrelationId(*i); + session.messageTransfer(arg::content=msg, arg::acceptMode=1); + } + } + + void transfer() + { + boost::ptr_vector<Transfer> agents(opts.queues); + //launch transfer agents + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + StringSet::iterator next = i + 1; + if (next == queues.end()) next = queues.begin(); + + 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()); + } + + for (boost::ptr_vector<Transfer>::iterator i = agents.begin(); i != agents.end(); i++) { + i->thread.join(); + } + } + + int check() + { + SubscriptionManager subs(session); + + // Recover DTX transactions (if any) + if (opts.dtx) { + std::vector<std::string> inDoubtXids; + framing::DtxRecoverResult dtxRes = session.dtxRecover().get(); + const framing::Array& xidArr = dtxRes.getInDoubt(); + xidArr.collect(inDoubtXids); + + if (inDoubtXids.size()) { + 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]); + if (!opts.quiet) std::cout << (i%2 ? " * aborting " : " * committing "); + xid.print(std::cout); + std::cout << std::endl; + if (i%2) { + session.dtxRollback(arg::xid=xid); + } else { + session.dtxCommit(arg::xid=xid); + } + } + } + } + + 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 flushn + LocalQueue lq; + SubscriptionSettings settings(FlowControl::unlimited(), ACCEPT_MODE_NONE); + subs.subscribe(lq, *i, settings); + session.messageFlush(arg::destination=*i); + session.sync(); + + uint count(0); + while (!lq.empty()) { + Message m = lq.pop(); + //add correlation ids of received messages to drained + drained.push_back(m.getMessageProperties().getCorrelationId()); + ++count; + } + if (!opts.quiet) std::cout << "Drained " << count << " messages from " << *i << std::endl; + } + + sort(ids.begin(), ids.end()); + sort(drained.begin(), drained.end()); + + //check that drained == ids + StringSet 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)); + + if (missing.empty() && extra.empty()) { + 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; + } + } + 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; + } + } + return 1; + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Controller controller; + if (opts.init) controller.init(); + if (opts.transfer) controller.transfer(); + if (opts.check) return controller.check(); + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 2; +} diff --git a/RC9/qpid/cpp/src/tests/unit_test.cpp b/RC9/qpid/cpp/src/tests/unit_test.cpp new file mode 100644 index 0000000000..00c61242e4 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/unit_test.cpp @@ -0,0 +1,23 @@ +/* + * + * 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. + * + */ + +// Defines test_main function to link with actual unit test code. +#define BOOST_AUTO_TEST_MAIN // Boost 1.33 +#define BOOST_TEST_MAIN +#include "unit_test.h" + diff --git a/RC9/qpid/cpp/src/tests/unit_test.h b/RC9/qpid/cpp/src/tests/unit_test.h new file mode 100644 index 0000000000..df3ebfb1fe --- /dev/null +++ b/RC9/qpid/cpp/src/tests/unit_test.h @@ -0,0 +1,86 @@ +#ifndef QPIPD_TEST_UNIT_TEST_H_ +#define QPIPD_TEST_UNIT_TEST_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. + * + */ + +// Workaround so we can build against boost 1.33 and boost 1.34. +// Remove when we no longer need to support 1.33. +// +#include <boost/version.hpp> +#include <limits.h> // Must be inclued beofre boost/test headers. + +// #include the correct header file. +// +#if (BOOST_VERSION < 103400) +# include <boost/test/auto_unit_test.hpp> +#else +# include <boost/test/unit_test.hpp> +#endif // BOOST_VERSION + +// Workarounds for BOOST_AUTO_TEST_CASE|SUITE|SUITE_END +// +#if (BOOST_VERSION < 103300) + +# define QPID_AUTO_TEST_SUITE(name) +# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_UNIT_TEST(name) +# define QPID_AUTO_TEST_SUITE_END() + +#elif (BOOST_VERSION < 103400) +// Note the trailing ';' +# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name); +# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END(); + +#endif // Workarounds for BOOST_AUTO_TEST_CASE|SUITE|SUITE_END + +// Workaround for BOOST_AUTO_TEST_SUITE_EXPECTED_FAILURES +// +#if (BOOST_VERSION < 103600) + +// 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. +// + +#ifndef QPID_AUTO_TEST_SUITE +# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name) +#endif + +#ifndef QPID_AUTO_TEST_CASE +# 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_CASE_EXPECTED_FAILURES(name,n) +#endif + +#ifndef QPID_AUTO_TEST_SUITE_END +# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() +#endif + +#endif // !QPIPD_TEST_UNIT_TEST_H_ diff --git a/RC9/qpid/cpp/src/tests/vg_check b/RC9/qpid/cpp/src/tests/vg_check new file mode 100644 index 0000000000..c5a1e6d2d0 --- /dev/null +++ b/RC9/qpid/cpp/src/tests/vg_check @@ -0,0 +1,24 @@ +# 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 +} + +vg_check() +{ + test -z "$1" || VG_LOG=$1 + 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_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_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." + true +} |