summaryrefslogtreecommitdiff
path: root/qpid/cpp/src/tests
diff options
context:
space:
mode:
authorRobert Gemmell <robbie@apache.org>2015-06-25 10:22:51 +0000
committerRobert Gemmell <robbie@apache.org>2015-06-25 10:22:51 +0000
commit32ae758bc2e8fd962b66a4ab6341b14009f1907e (patch)
tree2f4d8174813284a6ea58bb6b7f6520aa92287476 /qpid/cpp/src/tests
parent116d91ad7825a98af36a869fc751206fbce0c59f (diff)
parentf7e896076143de4572b4f1f67ef0765125f2498d (diff)
downloadqpid-python-32ae758bc2e8fd962b66a4ab6341b14009f1907e.tar.gz
NO-JIRA: create branch for qpid-cpp 0.34 RC process
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/qpid-cpp-0.34-rc@1687469 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/cpp/src/tests')
-rw-r--r--qpid/cpp/src/tests/.valgrind.supp179
-rw-r--r--qpid/cpp/src/tests/AccumulatedAckTest.cpp237
-rw-r--r--qpid/cpp/src/tests/Acl.cpp166
-rw-r--r--qpid/cpp/src/tests/AclHost.cpp166
-rw-r--r--qpid/cpp/src/tests/Address.cpp135
-rw-r--r--qpid/cpp/src/tests/Array.cpp84
-rw-r--r--qpid/cpp/src/tests/AsyncCompletion.cpp153
-rw-r--r--qpid/cpp/src/tests/AtomicValue.cpp54
-rw-r--r--qpid/cpp/src/tests/Blob.cpp21
-rw-r--r--qpid/cpp/src/tests/BrokerFixture.h168
-rw-r--r--qpid/cpp/src/tests/BrokerMgmtAgent.cpp387
-rw-r--r--qpid/cpp/src/tests/BrokerMgmtAgent.xml38
-rw-r--r--qpid/cpp/src/tests/BrokerOptions.cpp79
-rw-r--r--qpid/cpp/src/tests/CMakeLists.txt402
-rw-r--r--qpid/cpp/src/tests/ClientMessage.cpp46
-rw-r--r--qpid/cpp/src/tests/ClientMessageTest.cpp51
-rw-r--r--qpid/cpp/src/tests/ClientSessionTest.cpp663
-rw-r--r--qpid/cpp/src/tests/ConnectionOptions.h62
-rw-r--r--qpid/cpp/src/tests/DeliveryRecordTest.cpp67
-rw-r--r--qpid/cpp/src/tests/DispatcherTest.cpp240
-rw-r--r--qpid/cpp/src/tests/DtxWorkRecordTest.cpp193
-rw-r--r--qpid/cpp/src/tests/ExchangeTest.cpp267
-rw-r--r--qpid/cpp/src/tests/FieldTable.cpp215
-rw-r--r--qpid/cpp/src/tests/FieldValue.cpp98
-rw-r--r--qpid/cpp/src/tests/Frame.cpp84
-rw-r--r--qpid/cpp/src/tests/FrameDecoder.cpp78
-rw-r--r--qpid/cpp/src/tests/FramingTest.cpp168
-rw-r--r--qpid/cpp/src/tests/HeaderTest.cpp114
-rw-r--r--qpid/cpp/src/tests/HeadersExchangeTest.cpp194
-rw-r--r--qpid/cpp/src/tests/InlineAllocator.cpp68
-rw-r--r--qpid/cpp/src/tests/InlineVector.cpp128
-rw-r--r--qpid/cpp/src/tests/ManagementTest.cpp97
-rw-r--r--qpid/cpp/src/tests/MessageReplayTracker.cpp104
-rw-r--r--qpid/cpp/src/tests/MessageTest.cpp89
-rw-r--r--qpid/cpp/src/tests/MessageUtils.h117
-rw-r--r--qpid/cpp/src/tests/MessagingFixture.h352
-rw-r--r--qpid/cpp/src/tests/MessagingLogger.cpp149
-rw-r--r--qpid/cpp/src/tests/MessagingSessionTests.cpp1495
-rw-r--r--qpid/cpp/src/tests/MessagingThreadTests.cpp144
-rw-r--r--qpid/cpp/src/tests/PollableCondition.cpp109
-rw-r--r--qpid/cpp/src/tests/PollerTest.cpp262
-rw-r--r--qpid/cpp/src/tests/ProxyTest.cpp56
-rw-r--r--qpid/cpp/src/tests/Qmf2.cpp422
-rw-r--r--qpid/cpp/src/tests/QueueDepth.cpp105
-rw-r--r--qpid/cpp/src/tests/QueueFlowLimitTest.cpp457
-rw-r--r--qpid/cpp/src/tests/QueueOptionsTest.cpp85
-rw-r--r--qpid/cpp/src/tests/QueuePolicyTest.cpp300
-rw-r--r--qpid/cpp/src/tests/QueueRegistryTest.cpp91
-rw-r--r--qpid/cpp/src/tests/QueueTest.cpp629
-rw-r--r--qpid/cpp/src/tests/README.txt37
-rw-r--r--qpid/cpp/src/tests/RangeSet.cpp154
-rw-r--r--qpid/cpp/src/tests/RefCounted.cpp55
-rw-r--r--qpid/cpp/src/tests/RetryList.cpp111
-rw-r--r--qpid/cpp/src/tests/Selector.cpp442
-rw-r--r--qpid/cpp/src/tests/SequenceNumberTest.cpp209
-rw-r--r--qpid/cpp/src/tests/SequenceSet.cpp187
-rw-r--r--qpid/cpp/src/tests/SessionState.cpp303
-rw-r--r--qpid/cpp/src/tests/Shlib.cpp67
-rw-r--r--qpid/cpp/src/tests/Statistics.cpp131
-rw-r--r--qpid/cpp/src/tests/Statistics.h111
-rw-r--r--qpid/cpp/src/tests/StringUtils.cpp81
-rw-r--r--qpid/cpp/src/tests/SystemInfo.cpp36
-rw-r--r--qpid/cpp/src/tests/TestMessageStore.h63
-rw-r--r--qpid/cpp/src/tests/TestOptions.h79
-rw-r--r--qpid/cpp/src/tests/TimerTest.cpp176
-rw-r--r--qpid/cpp/src/tests/TopicExchangeTest.cpp408
-rw-r--r--qpid/cpp/src/tests/TransactionObserverTest.cpp147
-rw-r--r--qpid/cpp/src/tests/TxBufferTest.cpp188
-rw-r--r--qpid/cpp/src/tests/TxMocks.h236
-rw-r--r--qpid/cpp/src/tests/Url.cpp116
-rw-r--r--qpid/cpp/src/tests/Uuid.cpp150
-rw-r--r--qpid/cpp/src/tests/Variant.cpp834
-rw-r--r--qpid/cpp/src/tests/XmlClientSessionTest.cpp301
-rwxr-xr-xqpid/cpp/src/tests/acl.py3959
-rw-r--r--qpid/cpp/src/tests/ais_test.cpp23
-rwxr-xr-xqpid/cpp/src/tests/allhosts79
-rw-r--r--qpid/cpp/src/tests/assertions.py194
-rw-r--r--qpid/cpp/src/tests/background.ps155
-rw-r--r--qpid/cpp/src/tests/brokertest.py752
-rwxr-xr-xqpid/cpp/src/tests/cli_tests.py477
-rw-r--r--qpid/cpp/src/tests/config.null21
-rw-r--r--qpid/cpp/src/tests/consume.cpp131
-rw-r--r--qpid/cpp/src/tests/datagen.cpp103
-rw-r--r--qpid/cpp/src/tests/declare_queues.cpp101
-rw-r--r--qpid/cpp/src/tests/dlclose_noop.c30
-rwxr-xr-xqpid/cpp/src/tests/dynamic_log_hires_timestamp75
-rwxr-xr-xqpid/cpp/src/tests/dynamic_log_level_test90
-rw-r--r--qpid/cpp/src/tests/echotest.cpp157
-rw-r--r--qpid/cpp/src/tests/exception_test.cpp125
-rw-r--r--qpid/cpp/src/tests/failing-amqp0-10-python-tests32
-rw-r--r--qpid/cpp/src/tests/failing-amqp1.0-python-tests25
-rwxr-xr-xqpid/cpp/src/tests/fanout_perftest22
-rwxr-xr-xqpid/cpp/src/tests/federated_topic_test128
-rwxr-xr-xqpid/cpp/src/tests/federation.py2793
-rwxr-xr-xqpid/cpp/src/tests/federation_sys.py977
-rw-r--r--qpid/cpp/src/tests/find_prog.ps136
-rwxr-xr-xqpid/cpp/src/tests/ha_test.py403
-rw-r--r--qpid/cpp/src/tests/ha_test_max_queues.cpp67
-rwxr-xr-xqpid/cpp/src/tests/ha_tests.py1634
-rw-r--r--qpid/cpp/src/tests/header_test.cpp59
-rwxr-xr-xqpid/cpp/src/tests/header_test.py86
-rw-r--r--qpid/cpp/src/tests/headers_federation.py99
-rwxr-xr-xqpid/cpp/src/tests/idle_timeout_tests.py95
-rw-r--r--qpid/cpp/src/tests/install_env.sh.in26
-rwxr-xr-xqpid/cpp/src/tests/interlink_tests.py336
-rwxr-xr-xqpid/cpp/src/tests/interop_tests.py220
-rwxr-xr-xqpid/cpp/src/tests/ipv6_test120
-rw-r--r--qpid/cpp/src/tests/legacystore/.valgrind.supp35
-rw-r--r--qpid/cpp/src/tests/legacystore/.valgrindrc7
-rw-r--r--qpid/cpp/src/tests/legacystore/CMakeLists.txt133
-rw-r--r--qpid/cpp/src/tests/legacystore/MessageUtils.h105
-rw-r--r--qpid/cpp/src/tests/legacystore/TestFramework.cpp30
-rw-r--r--qpid/cpp/src/tests/legacystore/TestFramework.h37
-rw-r--r--qpid/cpp/src/tests/legacystore/clean.sh32
-rw-r--r--qpid/cpp/src/tests/legacystore/federation/Makefile.am46
-rwxr-xr-xqpid/cpp/src/tests/legacystore/federation/federation_tests_env.sh313
-rwxr-xr-xqpid/cpp/src/tests/legacystore/federation/run_federation_sys_tests96
-rwxr-xr-xqpid/cpp/src/tests/legacystore/federation/run_long_federation_sys_tests24
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp140
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp558
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp239
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h882
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp460
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp353
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp320
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp416
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp47
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp346
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp402
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp886
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp438
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp163
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp106
-rwxr-xr-xqpid/cpp/src/tests/legacystore/jrnl/chk_jdata32
-rwxr-xr-xqpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl59
-rwxr-xr-xqpid/cpp/src/tests/legacystore/jrnl/jhexdump41
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp207
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp100
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp178
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp146
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp113
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp206
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp178
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp147
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv74
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp226
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h66
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp87
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h66
-rwxr-xr-xqpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py838
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp77
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h80
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp439
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h121
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv234
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp57
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp93
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h62
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp179
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h110
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp201
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h100
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp185
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h81
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp169
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h99
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp218
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h68
-rwxr-xr-xqpid/cpp/src/tests/legacystore/jrnl/prof32
-rwxr-xr-xqpid/cpp/src/tests/legacystore/jrnl/run-journal-tests47
-rw-r--r--qpid/cpp/src/tests/legacystore/jrnl/tests.odsbin0 -> 91064 bytes
-rw-r--r--qpid/cpp/src/tests/legacystore/persistence.py574
-rw-r--r--qpid/cpp/src/tests/legacystore/python_tests/__init__.py24
-rw-r--r--qpid/cpp/src/tests/legacystore/python_tests/client_persistence.py239
-rw-r--r--qpid/cpp/src/tests/legacystore/python_tests/resize.py170
-rw-r--r--qpid/cpp/src/tests/legacystore/python_tests/store_test.py417
-rw-r--r--qpid/cpp/src/tests/legacystore/run_long_python_tests21
-rwxr-xr-xqpid/cpp/src/tests/legacystore/run_python_tests43
-rw-r--r--qpid/cpp/src/tests/legacystore/run_short_python_tests21
-rw-r--r--qpid/cpp/src/tests/legacystore/system_test.sh51
-rw-r--r--qpid/cpp/src/tests/legacystore/unit_test.cpp28
-rw-r--r--qpid/cpp/src/tests/legacystore/unit_test.h69
-rw-r--r--qpid/cpp/src/tests/linearstore/CMakeLists.txt29
-rwxr-xr-xqpid/cpp/src/tests/linearstore/linearstoredirsetup.sh55
-rw-r--r--qpid/cpp/src/tests/linearstore/python_tests/__init__.py23
-rw-r--r--qpid/cpp/src/tests/linearstore/python_tests/client_persistence.py239
-rw-r--r--qpid/cpp/src/tests/linearstore/python_tests/store_test.py417
-rw-r--r--qpid/cpp/src/tests/linearstore/run_long_python_tests21
-rwxr-xr-xqpid/cpp/src/tests/linearstore/run_python_tests42
-rw-r--r--qpid/cpp/src/tests/linearstore/run_short_python_tests21
-rwxr-xr-xqpid/cpp/src/tests/linearstore/tx-test-soak.sh275
-rw-r--r--qpid/cpp/src/tests/logging.cpp512
-rw-r--r--qpid/cpp/src/tests/misc.py119
-rw-r--r--qpid/cpp/src/tests/msg_group_test.cpp641
-rwxr-xr-xqpid/cpp/src/tests/multiq_perftest22
-rwxr-xr-xqpid/cpp/src/tests/perfdist87
-rwxr-xr-xqpid/cpp/src/tests/ping_broker134
-rw-r--r--qpid/cpp/src/tests/policies.py209
-rw-r--r--qpid/cpp/src/tests/policy.acl20
-rw-r--r--qpid/cpp/src/tests/publish.cpp135
-rwxr-xr-xqpid/cpp/src/tests/python_tests34
-rw-r--r--qpid/cpp/src/tests/python_tests.ps142
-rwxr-xr-xqpid/cpp/src/tests/qpid-analyze-trace258
-rwxr-xr-xqpid/cpp/src/tests/qpid-build-rinstall28
-rw-r--r--qpid/cpp/src/tests/qpid-client-test.cpp139
-rwxr-xr-xqpid/cpp/src/tests/qpid-cluster-benchmark64
-rwxr-xr-xqpid/cpp/src/tests/qpid-cpp-benchmark363
-rwxr-xr-xqpid/cpp/src/tests/qpid-ctrl120
-rw-r--r--qpid/cpp/src/tests/qpid-latency-test.cpp480
-rw-r--r--qpid/cpp/src/tests/qpid-perftest.cpp760
-rw-r--r--qpid/cpp/src/tests/qpid-ping.cpp94
-rw-r--r--qpid/cpp/src/tests/qpid-receive.cpp299
-rw-r--r--qpid/cpp/src/tests/qpid-send.cpp465
-rwxr-xr-xqpid/cpp/src/tests/qpid-src-rinstall31
-rw-r--r--qpid/cpp/src/tests/qpid-stream.cpp193
-rw-r--r--qpid/cpp/src/tests/qpid-topic-listener.cpp209
-rw-r--r--qpid/cpp/src/tests/qpid-topic-publisher.cpp230
-rw-r--r--qpid/cpp/src/tests/qpid-txtest.cpp342
-rw-r--r--qpid/cpp/src/tests/qpid-txtest2.cpp363
-rw-r--r--qpid/cpp/src/tests/qpidd-empty.conf22
-rwxr-xr-xqpid/cpp/src/tests/qpidd-p046
-rwxr-xr-xqpid/cpp/src/tests/qpidd_qmfv2_tests.py278
-rw-r--r--qpid/cpp/src/tests/queue_flow_limit_tests.py376
-rw-r--r--qpid/cpp/src/tests/queue_redirect.py317
-rwxr-xr-xqpid/cpp/src/tests/quick_perftest22
-rwxr-xr-xqpid/cpp/src/tests/quick_topictest30
-rw-r--r--qpid/cpp/src/tests/quick_topictest.ps130
-rwxr-xr-xqpid/cpp/src/tests/quick_txtest22
-rw-r--r--qpid/cpp/src/tests/receiver.cpp140
-rw-r--r--qpid/cpp/src/tests/reject_release.py65
-rw-r--r--qpid/cpp/src/tests/replaying_sender.cpp165
-rw-r--r--qpid/cpp/src/tests/resuming_receiver.cpp193
-rwxr-xr-xqpid/cpp/src/tests/ring_queue_test174
-rwxr-xr-xqpid/cpp/src/tests/rsynchosts57
-rwxr-xr-xqpid/cpp/src/tests/run_acl_tests166
-rw-r--r--qpid/cpp/src/tests/run_acl_tests.ps199
-rwxr-xr-xqpid/cpp/src/tests/run_cli_tests81
-rwxr-xr-xqpid/cpp/src/tests/run_federation_sys_tests71
-rwxr-xr-xqpid/cpp/src/tests/run_federation_tests61
-rw-r--r--qpid/cpp/src/tests/run_federation_tests.ps183
-rwxr-xr-xqpid/cpp/src/tests/run_ha_tests29
-rwxr-xr-xqpid/cpp/src/tests/run_header_test31
-rw-r--r--qpid/cpp/src/tests/run_header_test.ps148
-rw-r--r--qpid/cpp/src/tests/run_headers_federation_tests49
-rwxr-xr-xqpid/cpp/src/tests/run_interlink_tests26
-rw-r--r--qpid/cpp/src/tests/run_long_federation_sys_tests24
-rwxr-xr-xqpid/cpp/src/tests/run_msg_group_tests62
-rw-r--r--qpid/cpp/src/tests/run_msg_group_tests.ps171
-rwxr-xr-xqpid/cpp/src/tests/run_msg_group_tests_soak63
-rwxr-xr-xqpid/cpp/src/tests/run_paged_queue_tests50
-rwxr-xr-xqpid/cpp/src/tests/run_perftest28
-rwxr-xr-xqpid/cpp/src/tests/run_queue_flow_limit_tests27
-rwxr-xr-xqpid/cpp/src/tests/run_queue_redirect56
-rwxr-xr-xqpid/cpp/src/tests/run_ring_queue_test36
-rw-r--r--qpid/cpp/src/tests/run_store_tests.ps1132
-rwxr-xr-xqpid/cpp/src/tests/run_test191
-rw-r--r--qpid/cpp/src/tests/run_test.ps1162
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed169
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex283
-rwxr-xr-xqpid/cpp/src/tests/sasl_no_dir106
-rwxr-xr-xqpid/cpp/src/tests/sasl_test_setup.sh42
-rw-r--r--qpid/cpp/src/tests/sasl_version.cpp48
-rw-r--r--qpid/cpp/src/tests/sender.cpp157
-rwxr-xr-xqpid/cpp/src/tests/shared_perftest22
-rw-r--r--qpid/cpp/src/tests/shlibtest.cpp34
-rwxr-xr-xqpid/cpp/src/tests/ssl_test331
-rwxr-xr-xqpid/cpp/src/tests/store.py214
-rwxr-xr-xqpid/cpp/src/tests/swig_python_tests66
-rw-r--r--qpid/cpp/src/tests/test.xquery6
-rw-r--r--qpid/cpp/src/tests/test_env.ps1.in77
-rw-r--r--qpid/cpp/src/tests/test_env.sh.in100
-rw-r--r--qpid/cpp/src/tests/test_env_common.sh28
-rw-r--r--qpid/cpp/src/tests/test_store.cpp339
-rw-r--r--qpid/cpp/src/tests/test_tools.h106
-rwxr-xr-xqpid/cpp/src/tests/topic_perftest22
-rwxr-xr-xqpid/cpp/src/tests/topictest61
-rw-r--r--qpid/cpp/src/tests/topictest.ps173
-rw-r--r--qpid/cpp/src/tests/txjob.cpp102
-rw-r--r--qpid/cpp/src/tests/txshift.cpp193
-rw-r--r--qpid/cpp/src/tests/unit_test.cpp23
-rw-r--r--qpid/cpp/src/tests/unit_test.h74
-rw-r--r--qpid/cpp/src/tests/vg_check43
-rw-r--r--qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp78
283 files changed, 55802 insertions, 0 deletions
diff --git a/qpid/cpp/src/tests/.valgrind.supp b/qpid/cpp/src/tests/.valgrind.supp
new file mode 100644
index 0000000000..1a24a9178e
--- /dev/null
+++ b/qpid/cpp/src/tests/.valgrind.supp
@@ -0,0 +1,179 @@
+{
+ Leak in TCPConnector: https://bugzilla.redhat.com/show_bug.cgi?id=520600
+ Memcheck:Leak
+ fun:_vgrZU_libcZdsoZa_calloc
+ fun:_dl_allocate_tls
+ fun:*
+ fun:*
+ fun:*
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+}
+
+{
+ Leak in TCPConnector: https://bugzilla.redhat.com/show_bug.cgi?id=520600
+ Memcheck:Leak
+ fun:_vgrZU_libcZdsoZa_calloc
+ fun:_dl_allocate_tls
+ fun:*
+ fun:*
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+}
+
+{
+ Reported on FC5 and RHEL5 when md5 sasl libs are installed
+ Memcheck:Leak
+ fun:*
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:*
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+
+{
+ 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
+}
+
+{
+ 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
+}
+
+{
+ 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
+}
+
+{
+ Known leak in boost.thread 1.33.1. Wildcards for 64/32 bit diffs.
+ Memcheck:Leak
+ fun:*
+ obj:/usr/*/libboost_thread.so.1.33.1
+ fun:_ZN5boost6detail3tss3setEPv
+}
+
+{
+ Shows up on RHEL5: believed benign
+ Memcheck:Cond
+ fun:__strcpy_chk
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+
+{
+ Seems like a use after delete issue in boost unit_test
+ Memcheck:Addr8
+ fun:_ZN5boost9unit_test14framework_implD1Ev
+ fun:exit
+ fun:(below main)
+}
+
+{
+ Seems like a use after delete issue in boost unit_test
+ Memcheck:Addr4
+ fun:_ZN5boost9unit_test14framework_implD1Ev
+ fun:exit
+ fun:(below main)
+}
+
diff --git a/qpid/cpp/src/tests/AccumulatedAckTest.cpp b/qpid/cpp/src/tests/AccumulatedAckTest.cpp
new file mode 100644
index 0000000000..c736a519d2
--- /dev/null
+++ b/qpid/cpp/src/tests/AccumulatedAckTest.cpp
@@ -0,0 +1,237 @@
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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;
+
+
+namespace qpid {
+namespace tests {
+
+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()
+
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Acl.cpp b/qpid/cpp/src/tests/Acl.cpp
new file mode 100644
index 0000000000..9c3de0de62
--- /dev/null
+++ b/qpid/cpp/src/tests/Acl.cpp
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright (c) 2014 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/acl/AclLexer.h"
+#include <boost/assign.hpp>
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::acl;
+using namespace boost::assign;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(AclTestSuite)
+
+#define OBJ_ENUMS(e, s) \
+ BOOST_CHECK_EQUAL(AclHelper::getObjectTypeStr((e)),(s)); \
+ BOOST_CHECK_EQUAL(AclHelper::getObjectType((s)),(e))
+
+QPID_AUTO_TEST_CASE(TestLexerObjectEnums) {
+ BOOST_CHECK_EQUAL(OBJECTSIZE, 7);
+ OBJ_ENUMS(OBJ_QUEUE, "queue");
+ OBJ_ENUMS(OBJ_EXCHANGE, "exchange");
+ OBJ_ENUMS(OBJ_BROKER, "broker");
+ OBJ_ENUMS(OBJ_LINK, "link");
+ OBJ_ENUMS(OBJ_METHOD, "method");
+ OBJ_ENUMS(OBJ_QUERY, "query");
+ OBJ_ENUMS(OBJ_CONNECTION, "connection");
+ int maxLen = 0;
+ for (int i=0; i<acl::OBJECTSIZE; i++) {
+ int thisLen = AclHelper::getObjectTypeStr( ObjectType(i) ).length();
+ if (thisLen > maxLen)
+ maxLen = thisLen;
+ }
+ BOOST_CHECK_EQUAL(maxLen, acl::OBJECTTYPE_STR_WIDTH);
+}
+
+#define ACT_ENUMS(e, s) \
+ BOOST_CHECK_EQUAL(AclHelper::getActionStr((e)),(s)); \
+ BOOST_CHECK_EQUAL(AclHelper::getAction((s)),(e))
+
+QPID_AUTO_TEST_CASE(TestLexerActionEnums) {
+ BOOST_CHECK_EQUAL(ACTIONSIZE, 12);
+ ACT_ENUMS(ACT_CONSUME, "consume");
+ ACT_ENUMS(ACT_PUBLISH, "publish");
+ ACT_ENUMS(ACT_CREATE, "create");
+ ACT_ENUMS(ACT_ACCESS, "access");
+ ACT_ENUMS(ACT_BIND, "bind");
+ ACT_ENUMS(ACT_UNBIND, "unbind");
+ ACT_ENUMS(ACT_DELETE, "delete");
+ ACT_ENUMS(ACT_PURGE, "purge");
+ ACT_ENUMS(ACT_UPDATE, "update");
+ ACT_ENUMS(ACT_MOVE, "move");
+ ACT_ENUMS(ACT_REDIRECT, "redirect");
+ ACT_ENUMS(ACT_REROUTE, "reroute");
+ int maxLen = 0;
+ for (int i=0; i<acl::ACTIONSIZE; i++) {
+ int thisLen = AclHelper::getActionStr( Action(i) ).length();
+ if (thisLen > maxLen)
+ maxLen = thisLen;
+ }
+ BOOST_CHECK_EQUAL(maxLen, acl::ACTION_STR_WIDTH);
+}
+
+#define PROP_ENUMS(e, s) \
+ BOOST_CHECK_EQUAL(AclHelper::getPropertyStr((e)),(s)); \
+ BOOST_CHECK_EQUAL(AclHelper::getProperty((s)),(e))
+
+QPID_AUTO_TEST_CASE(TestLexerPropertyEnums) {
+ BOOST_CHECK_EQUAL(PROPERTYSIZE, 21);
+ PROP_ENUMS(PROP_NAME, "name");
+ PROP_ENUMS(PROP_DURABLE, "durable");
+ PROP_ENUMS(PROP_OWNER, "owner");
+ PROP_ENUMS(PROP_ROUTINGKEY, "routingkey");
+ PROP_ENUMS(PROP_AUTODELETE, "autodelete");
+ PROP_ENUMS(PROP_EXCLUSIVE, "exclusive");
+ PROP_ENUMS(PROP_TYPE, "type");
+ PROP_ENUMS(PROP_ALTERNATE, "alternate");
+ PROP_ENUMS(PROP_QUEUENAME, "queuename");
+ PROP_ENUMS(PROP_EXCHANGENAME, "exchangename");
+ PROP_ENUMS(PROP_SCHEMAPACKAGE, "schemapackage");
+ PROP_ENUMS(PROP_SCHEMACLASS, "schemaclass");
+ PROP_ENUMS(PROP_POLICYTYPE, "policytype");
+ PROP_ENUMS(PROP_PAGING, "paging");
+ PROP_ENUMS(PROP_HOST, "host");
+ PROP_ENUMS(PROP_MAXPAGES, "maxpages");
+ PROP_ENUMS(PROP_MAXPAGEFACTOR, "maxpagefactor");
+ PROP_ENUMS(PROP_MAXQUEUESIZE, "maxqueuesize");
+ PROP_ENUMS(PROP_MAXQUEUECOUNT, "maxqueuecount");
+ PROP_ENUMS(PROP_MAXFILESIZE, "maxfilesize");
+ PROP_ENUMS(PROP_MAXFILECOUNT, "maxfilecount");
+
+}
+
+#define SPECPROP_ENUMS(e, s) \
+ BOOST_CHECK_EQUAL(AclHelper::getPropertyStr((e)),(s)); \
+ BOOST_CHECK_EQUAL(AclHelper::getSpecProperty((s)),(e))
+
+QPID_AUTO_TEST_CASE(TestLexerSpecPropertyEnums) {
+ BOOST_CHECK_EQUAL(SPECPROPSIZE, 27);
+ SPECPROP_ENUMS(SPECPROP_NAME, "name");
+ SPECPROP_ENUMS(SPECPROP_DURABLE, "durable");
+ SPECPROP_ENUMS(SPECPROP_OWNER, "owner");
+ SPECPROP_ENUMS(SPECPROP_ROUTINGKEY, "routingkey");
+ SPECPROP_ENUMS(SPECPROP_AUTODELETE, "autodelete");
+ SPECPROP_ENUMS(SPECPROP_EXCLUSIVE, "exclusive");
+ SPECPROP_ENUMS(SPECPROP_TYPE, "type");
+ SPECPROP_ENUMS(SPECPROP_ALTERNATE, "alternate");
+ SPECPROP_ENUMS(SPECPROP_QUEUENAME, "queuename");
+ SPECPROP_ENUMS(SPECPROP_EXCHANGENAME, "exchangename");
+ SPECPROP_ENUMS(SPECPROP_SCHEMAPACKAGE, "schemapackage");
+ SPECPROP_ENUMS(SPECPROP_SCHEMACLASS, "schemaclass");
+ SPECPROP_ENUMS(SPECPROP_POLICYTYPE, "policytype");
+ SPECPROP_ENUMS(SPECPROP_PAGING, "paging");
+ SPECPROP_ENUMS(SPECPROP_HOST, "host");
+ SPECPROP_ENUMS(SPECPROP_MAXQUEUESIZELOWERLIMIT, "queuemaxsizelowerlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXQUEUESIZEUPPERLIMIT, "queuemaxsizeupperlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXQUEUECOUNTLOWERLIMIT, "queuemaxcountlowerlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXQUEUECOUNTUPPERLIMIT, "queuemaxcountupperlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXFILESIZELOWERLIMIT, "filemaxsizelowerlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXFILESIZEUPPERLIMIT, "filemaxsizeupperlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXFILECOUNTLOWERLIMIT, "filemaxcountlowerlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXFILECOUNTUPPERLIMIT, "filemaxcountupperlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXPAGESLOWERLIMIT, "pageslowerlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXPAGESUPPERLIMIT, "pagesupperlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXPAGEFACTORLOWERLIMIT, "pagefactorlowerlimit");
+ SPECPROP_ENUMS(SPECPROP_MAXPAGEFACTORUPPERLIMIT, "pagefactorupperlimit");
+
+ BOOST_CHECK_EQUAL(AclHelper::getSpecProperty("maxqueuesize"), SPECPROP_MAXQUEUESIZEUPPERLIMIT);
+ BOOST_CHECK_EQUAL(AclHelper::getSpecProperty("maxqueuecount"), SPECPROP_MAXQUEUECOUNTUPPERLIMIT);
+}
+
+#define RESULT_ENUMS(e, s) \
+ BOOST_CHECK_EQUAL(AclHelper::getAclResultStr((e)),(s)); \
+ BOOST_CHECK_EQUAL(AclHelper::getAclResult((s)),(e))
+
+QPID_AUTO_TEST_CASE(TestLexerResultEnums) {
+ BOOST_CHECK_EQUAL(RESULTSIZE, 4);
+ RESULT_ENUMS(ALLOW, "allow");
+ RESULT_ENUMS(ALLOWLOG, "allow-log");
+ RESULT_ENUMS(DENY, "deny");
+ RESULT_ENUMS(DENYLOG, "deny-log");
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/AclHost.cpp b/qpid/cpp/src/tests/AclHost.cpp
new file mode 100644
index 0000000000..7d60c5a63d
--- /dev/null
+++ b/qpid/cpp/src/tests/AclHost.cpp
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright (c) 2014 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/AclHost.h"
+#include "qpid/sys/SocketAddress.h"
+#include <boost/assign.hpp>
+
+using namespace std;
+using namespace qpid;
+using namespace boost::assign;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(AclHostTestSuite)
+
+#define ACLURL_CHECK_INVALID(STR) BOOST_CHECK_THROW(AclHost(STR), AclHost::Invalid)
+
+#define SENSE_IP_VERSIONS() \
+ bool haveIPv4(true); \
+ try { \
+ sys::SocketAddress sa("1.1.1.1", ""); \
+ sa.firstAddress(); \
+} catch (qpid::Exception) { \
+ haveIPv4 = false; \
+} \
+ bool haveIPv6(true); \
+ try { \
+ sys::SocketAddress sa("::1", ""); \
+ sa.firstAddress(); \
+} catch (qpid::Exception) { \
+ haveIPv6 = false; \
+} \
+(void) haveIPv4; \
+(void) haveIPv6;
+
+QPID_AUTO_TEST_CASE(TestParseTcpIPv4) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv4) {
+ BOOST_CHECK_EQUAL(AclHost("1.1.1.1").str(), "(1.1.1.1,1.1.1.1)");
+ BOOST_CHECK_EQUAL(AclHost("1.1.1.1,2.2.2.2").str(), "(1.1.1.1,2.2.2.2)");
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestParseTcpIPv6) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv6) {
+ BOOST_CHECK_EQUAL(AclHost("[::1]").str(), "([::1],[::1])");
+ BOOST_CHECK_EQUAL(AclHost("[::1],::5").str(), "([::1],[::5])");
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestParseAll) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv4 || haveIPv6) {
+ BOOST_CHECK_EQUAL(AclHost("").str(), "(all)");
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestInvalidMixedIpFamilies) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv4 && haveIPv6) {
+ ACLURL_CHECK_INVALID("1.1.1.1,[::1]");
+ ACLURL_CHECK_INVALID("[::1],1.1.1.1");
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestMalformedIPv4) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv4) {
+ ACLURL_CHECK_INVALID("1.1.1.1.1");
+ ACLURL_CHECK_INVALID("1.1.1.777");
+ ACLURL_CHECK_INVALID("1.1.1.1abcd");
+ ACLURL_CHECK_INVALID("1.1.1.*");
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestRangeWithInvertedSizeOrder) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv4) {
+ ACLURL_CHECK_INVALID("1.1.1.100,1.1.1.1");
+ }
+ if (haveIPv6) {
+ ACLURL_CHECK_INVALID("[FF::1],[::1]");
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestSingleHostResolvesMultipleAddresses) {
+ SENSE_IP_VERSIONS();
+ AclHost XX("localhost");
+}
+
+QPID_AUTO_TEST_CASE(TestMatchSingleAddresses) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv4) {
+ AclHost host1("1.1.1.1");
+ BOOST_CHECK(host1.match("1.1.1.1") == true);
+ BOOST_CHECK(host1.match("1.2.1.1") == false);
+ }
+ if (haveIPv6) {
+ AclHost host2("FF::1");
+ BOOST_CHECK(host2.match("00FF:0000::1") == true);
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestMatchIPv4Range) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv4) {
+ AclHost host1("192.168.0.0,192.168.255.255");
+ BOOST_CHECK(host1.match("128.1.1.1") == false);
+ BOOST_CHECK(host1.match("192.167.255.255") == false);
+ BOOST_CHECK(host1.match("192.168.0.0") == true);
+ BOOST_CHECK(host1.match("192.168.0.1") == true);
+ BOOST_CHECK(host1.match("192.168.1.0") == true);
+ BOOST_CHECK(host1.match("192.168.255.254") == true);
+ BOOST_CHECK(host1.match("192.168.255.255") == true);
+ BOOST_CHECK(host1.match("192.169.0.0") == false);
+ if (haveIPv6) {
+ BOOST_CHECK(host1.match("::1") == false);
+ }
+ }
+}
+
+QPID_AUTO_TEST_CASE(TestMatchIPv6Range) {
+ SENSE_IP_VERSIONS();
+ if (haveIPv6) {
+ AclHost host1("::10,::1:0");
+ BOOST_CHECK(host1.match("::1") == false);
+ BOOST_CHECK(host1.match("::f") == false);
+ BOOST_CHECK(host1.match("::10") == true);
+ BOOST_CHECK(host1.match("::11") == true);
+ BOOST_CHECK(host1.match("::ffff") == true);
+ BOOST_CHECK(host1.match("::1:0") == true);
+ BOOST_CHECK(host1.match("::1:1") == false);
+ if (haveIPv4) {
+ BOOST_CHECK(host1.match("192.169.0.0") == false);
+ }
+ AclHost host2("[fc00::],[fc00::ff]");
+ BOOST_CHECK(host2.match("fc00::") == true);
+ BOOST_CHECK(host2.match("fc00::1") == true);
+ BOOST_CHECK(host2.match("fc00::ff") == true);
+ BOOST_CHECK(host2.match("fc00::100") == false);
+
+ }
+}
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Address.cpp b/qpid/cpp/src/tests/Address.cpp
new file mode 100644
index 0000000000..0fd3585958
--- /dev/null
+++ b/qpid/cpp/src/tests/Address.cpp
@@ -0,0 +1,135 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include "qpid/messaging/Address.h"
+#include "qpid/types/Variant.h"
+
+#include "unit_test.h"
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(AddressSuite)
+
+QPID_AUTO_TEST_CASE(testParseNameOnly)
+{
+ Address address("my-topic");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+}
+
+QPID_AUTO_TEST_CASE(testParseSubject)
+{
+ Address address("my-topic/my-subject");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my-subject"), address.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptions)
+{
+ Address address("my-topic; {a:bc, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOptions()["a"]);
+ BOOST_CHECK_EQUAL(101, static_cast<int>(address.getOptions()["x"]));
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"]);
+
+ // Test asString() and asInt64() once here
+
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOptions()["a"].asString());
+ BOOST_CHECK_EQUAL(static_cast<uint16_t>(101), address.getOptions()["x"].asInt64());
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"].asString());
+}
+
+QPID_AUTO_TEST_CASE(testParseSubjectAndOptions)
+{
+ Address address("my-topic/my-subject; {a:bc, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my-subject"), address.getSubject());
+
+ BOOST_CHECK_EQUAL(std::string("bc"), address.getOptions()["a"]);
+ BOOST_CHECK_EQUAL(101, static_cast<int>(address.getOptions()["x"]));
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"]);
+}
+
+QPID_AUTO_TEST_CASE(testParseNestedOptions)
+{
+ Address address("my-topic; {a:{p:202, q:'another string'}, x:101, y:'a string'}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ BOOST_CHECK_EQUAL(202, static_cast<int>(address.getOptions()["a"].asMap()["p"]));
+ BOOST_CHECK_EQUAL(std::string("another string"), address.getOptions()["a"].asMap()["q"]);
+ BOOST_CHECK_EQUAL(std::string("a string"), address.getOptions()["y"]);
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithList)
+{
+ Address address("my-topic; {a:[202, 'another string'], x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant::List& list = address.getOptions()["a"].asList();
+ Variant::List::const_iterator i = list.begin();
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL((uint16_t) 202, i->asInt64());
+ BOOST_CHECK(++i != list.end());
+ BOOST_CHECK_EQUAL(std::string("another string"), i->asString());
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithEmptyList)
+{
+ Address address("my-topic; {a:[], x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant::List& list = address.getOptions()["a"].asList();
+ BOOST_CHECK_EQUAL(list.size(), (size_t) 0);
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithEmptyMap)
+{
+ Address address("my-topic; {a:{}, x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant::Map& map = address.getOptions()["a"].asMap();
+ BOOST_CHECK_EQUAL(map.size(), (size_t) 0);
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testParseQuotedNameAndSubject)
+{
+ Address address("'my topic with / in it'/'my subject with ; in it'");
+ BOOST_CHECK_EQUAL(std::string("my topic with / in it"), address.getName());
+ BOOST_CHECK_EQUAL(std::string("my subject with ; in it"), address.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testParseOptionsWithEmptyStringAsValue)
+{
+ Address address("my-topic; {a:'', x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant a = address.getOptions()["a"];
+ BOOST_CHECK_EQUAL(VAR_STRING, a.getType());
+ std::string aVal = a;
+ BOOST_CHECK(aVal.size() == 0);
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}}
diff --git a/qpid/cpp/src/tests/Array.cpp b/qpid/cpp/src/tests/Array.cpp
new file mode 100644
index 0000000000..8ce7615162
--- /dev/null
+++ b/qpid/cpp/src/tests/Array.cpp
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include <sstream>
+#include "qpid/framing/Array.h"
+#include "qpid/framing/FieldValue.h"
+
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+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;
+ std::transform(b.begin(), b.end(), std::back_inserter(data2), Array::get<std::string, Array::ValuePtr>);
+ //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;
+ std::transform(b.begin(), b.end(), std::back_inserter(data2), Array::get<std::string, Array::ValuePtr>);
+ //BOOST_CHECK_EQUAL(data, data2);
+ BOOST_CHECK(data == data2);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/AsyncCompletion.cpp b/qpid/cpp/src/tests/AsyncCompletion.cpp
new file mode 100644
index 0000000000..dc43f10156
--- /dev/null
+++ b/qpid/cpp/src/tests/AsyncCompletion.cpp
@@ -0,0 +1,153 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/sys/BlockingQueue.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/QueueQueryResult.h"
+#include "qpid/client/TypedResult.h"
+
+using namespace std;
+using namespace qpid;
+using namespace client;
+using namespace framing;
+
+namespace qpid { namespace broker {
+class TransactionContext;
+class PersistableQueue;
+}}
+
+using broker::PersistableMessage;
+using broker::NullMessageStore;
+using broker::TransactionContext;
+using broker::PersistableQueue;
+using sys::TIME_SEC;
+using boost::intrusive_ptr;
+
+/** @file
+ * Unit tests for async completion.
+ * Using a dummy store, verify that the broker indicates async completion of
+ * message enqueues at the correct time.
+ */
+
+namespace qpid {
+namespace tests {
+
+class AsyncCompletionMessageStore : public NullMessageStore {
+ public:
+ sys::BlockingQueue<boost::intrusive_ptr<PersistableMessage> > enqueued;
+
+ AsyncCompletionMessageStore() : NullMessageStore() {}
+ ~AsyncCompletionMessageStore(){}
+
+ void enqueue(TransactionContext*,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& )
+ {
+ enqueued.push(msg);
+ }
+};
+
+QPID_AUTO_TEST_SUITE(AsyncCompletionTestSuite)
+
+/**
+ * Send a sync after a bunch of incomplete messages, verify the sync completes
+ * only when all the messages are complete.
+ */
+QPID_AUTO_TEST_CASE(testWaitTillComplete) {
+ SessionFixture fix;
+ AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore;
+ boost::shared_ptr<qpid::broker::MessageStore> p;
+ p.reset(store);
+ fix.broker->setStore(p);
+ AsyncSession s = fix.session;
+
+ static const int count = 3;
+
+ s.queueDeclare("q", arg::durable=true);
+ Completion transfers[count];
+ for (int i = 0; i < count; ++i) {
+ Message msg(boost::lexical_cast<string>(i), "q");
+ msg.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+ transfers[i] = s.messageTransfer(arg::content=msg);
+ }
+
+ // Get hold of the broker-side messages.
+ typedef vector<intrusive_ptr<PersistableMessage> > BrokerMessages;
+ BrokerMessages enqueued;
+ for (int j = 0; j < count; ++j)
+ enqueued.push_back(store->enqueued.pop(TIME_SEC));
+
+ // Send a sync, make sure it does not complete till all messages are complete.
+ // In reverse order for fun.
+ Completion sync = s.executionSync(arg::sync=true);
+ for (int k = count-1; k >= 0; --k) {
+ BOOST_CHECK(!transfers[k].isComplete()); // Should not be complete yet.
+ BOOST_CHECK(!sync.isComplete()); // Should not be complete yet.
+ enqueued[k]->enqueueComplete();
+ }
+ sync.wait(); // Should complete now, all messages are completed.
+}
+
+/**
+ * Send a sync after all messages are complete, verify it completes immediately.
+ */
+QPID_AUTO_TEST_CASE(testSyncAfterComplete) {
+ SessionFixture fix;
+ AsyncCompletionMessageStore* store = new AsyncCompletionMessageStore;
+ boost::shared_ptr<qpid::broker::MessageStore> p;
+ p.reset(store);
+ fix.broker->setStore(p);
+ AsyncSession s = fix.session;
+
+ static const int count = 3;
+
+ s.queueDeclare("q", arg::durable=true);
+ // Transfer and complete all the messages
+ for (int i = 0; i < count; ++i) {
+ Message msg(boost::lexical_cast<string>(i), "q");
+ msg.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+ Completion transfer = s.messageTransfer(arg::content=msg, arg::sync=true);
+ intrusive_ptr<PersistableMessage> enqueued = store->enqueued.pop(TIME_SEC);
+ enqueued->enqueueComplete();
+ transfer.wait();
+ }
+ // Send a sync, make sure it completes immediately
+ Completion sync = s.executionSync(arg::sync=true);
+ sync.wait(); // Should complete now, all messages are completed.
+}
+
+QPID_AUTO_TEST_CASE(testGetResult) {
+ SessionFixture fix;
+ AsyncSession s = fix.session;
+
+ s.queueDeclare("q", arg::durable=true);
+ TypedResult<QueueQueryResult> tr = s.queueQuery("q");
+ QueueQueryResult qq = tr.get();
+ BOOST_CHECK_EQUAL(qq.getQueue(), "q");
+ BOOST_CHECK_EQUAL(qq.getMessageCount(), 0U);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/AtomicValue.cpp b/qpid/cpp/src/tests/AtomicValue.cpp
new file mode 100644
index 0000000000..d855d993a7
--- /dev/null
+++ b/qpid/cpp/src/tests/AtomicValue.cpp
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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"
+
+namespace qpid {
+namespace tests {
+
+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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Blob.cpp b/qpid/cpp/src/tests/Blob.cpp
new file mode 100644
index 0000000000..9878d92fe4
--- /dev/null
+++ b/qpid/cpp/src/tests/Blob.cpp
@@ -0,0 +1,21 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
diff --git a/qpid/cpp/src/tests/BrokerFixture.h b/qpid/cpp/src/tests/BrokerFixture.h
new file mode 100644
index 0000000000..474b9d747f
--- /dev/null
+++ b/qpid/cpp/src/tests/BrokerFixture.h
@@ -0,0 +1,168 @@
+#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 "qpid/broker/Broker.h"
+#include "qpid/broker/BrokerOptions.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/LocalQueue.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+#include "qpid/sys/Thread.h"
+#include <boost/noncopyable.hpp>
+
+namespace qpid {
+namespace tests {
+
+/**
+ * A fixture with an in-process broker.
+ */
+struct BrokerFixture : private boost::noncopyable {
+ typedef qpid::broker::Broker Broker;
+ typedef boost::intrusive_ptr<Broker> BrokerPtr;
+ typedef qpid::broker::BrokerOptions BrokerOptions;
+ typedef std::vector<std::string> Args;
+
+ BrokerPtr broker;
+ BrokerOptions opts;
+ uint16_t port;
+ qpid::sys::Thread brokerThread;
+
+ BrokerFixture(const Args& args=Args(), const BrokerOptions& opts0=BrokerOptions(),
+ bool isExternalPort_=false, uint16_t externalPort_=0) :
+ opts(opts0)
+ {
+ init(args, isExternalPort_, externalPort_);
+ }
+
+ BrokerFixture(const BrokerOptions& opts0,
+ bool isExternalPort_=false, uint16_t externalPort_=0) :
+ opts(opts0)
+ {
+ init(Args(), isExternalPort_, externalPort_);
+ }
+
+ void shutdownBroker() {
+ if (broker) {
+ broker->shutdown();
+ brokerThread.join();
+ broker = BrokerPtr();
+ }
+ }
+
+ ~BrokerFixture() { shutdownBroker(); }
+
+ /** Open a connection to the broker. */
+ void open(qpid::client::Connection& c) {
+ c.open("localhost", getPort());
+ }
+
+ uint16_t getPort() { return port; }
+
+ private:
+ void init(const Args& args, bool isExternalPort=false, uint16_t externalPort=0)
+ {
+ // 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.deselectors.clear();
+ logOpts.selectors.push_back("error+");
+ qpid::log::Logger::instance().configure(logOpts);
+ }
+ // Default options, may be over-ridden when we parse args.
+ opts.port=0;
+ opts.listenInterfaces.push_back("127.0.0.1");
+ opts.workerThreads=1;
+ opts.dataDir="";
+ opts.auth=false;
+
+ // Argument parsing
+ if (args.size() > 0) {
+ std::vector<const char*> argv(args.size());
+ for (size_t i = 0; i<args.size(); ++i)
+ argv[i] = args[i].c_str();
+ Plugin::addOptions(opts);
+ opts.parse(argv.size(), &argv[0]);
+ }
+ 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->accept();
+ if (isExternalPort) port = externalPort;
+ else port = broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+ brokerThread = qpid::sys::Thread(*broker);
+ };
+};
+
+/** Connection that opens in its constructor */
+struct LocalConnection : public qpid::client::Connection {
+ LocalConnection(uint16_t port) { open("localhost", port); }
+ LocalConnection(const qpid::client::ConnectionSettings& s) { open(s); }
+ ~LocalConnection() { close(); }
+};
+
+/** 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;
+ std::string name;
+
+ ClientT(uint16_t port, const std::string& name_=std::string(), int timeout=0)
+ : connection(port), session(connection.newSession(name_,timeout)), subs(session), name(name_) {}
+ ClientT(const qpid::client::ConnectionSettings& settings, const std::string& name_=std::string(), int timeout=0)
+ : connection(settings), session(connection.newSession(name_, timeout)), subs(session), name(name_) {}
+
+ ~ClientT() { close(); }
+ void close() { session.close(); 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(const BrokerOptions& opts=BrokerOptions()) :
+ BrokerFixture(BrokerFixture::Args(), opts),
+ ClientT<ConnectionType,SessionType>(getPort())
+ {}
+
+};
+
+typedef SessionFixtureT<LocalConnection> SessionFixture;
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_BROKERFIXTURE_H*/
diff --git a/qpid/cpp/src/tests/BrokerMgmtAgent.cpp b/qpid/cpp/src/tests/BrokerMgmtAgent.cpp
new file mode 100644
index 0000000000..bad7e768a6
--- /dev/null
+++ b/qpid/cpp/src/tests/BrokerMgmtAgent.cpp
@@ -0,0 +1,387 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "MessagingFixture.h"
+#include "qpid/management/Buffer.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/log/Logger.h"
+#include "qpid/log/Options.h"
+
+#include "qmf/org/apache/qpid/broker/mgmt/test/TestObject.h"
+
+#include <iomanip>
+
+
+using qpid::management::Mutex;
+using qpid::management::Manageable;
+using qpid::management::Buffer;
+using namespace qpid::messaging;
+using namespace qpid::types;
+
+
+
+namespace qpid {
+namespace tests {
+
+namespace _qmf = qmf::org::apache::qpid::broker::mgmt::test;
+namespace {
+
+typedef boost::shared_ptr<_qmf::TestObject> TestObjectPtr;
+typedef std::vector<TestObjectPtr> TestObjectVector;
+
+// Instantiates a broker and its internal management agent. Provides
+// factories for constructing Receivers for object indication messages.
+//
+class AgentFixture
+{
+ MessagingFixture *mFix;
+
+ public:
+ AgentFixture( unsigned int pubInterval=10,
+ bool qmfV2=false,
+ qpid::broker::Broker::Options opts = qpid::broker::Broker::Options())
+ {
+ opts.enableMgmt=true;
+ opts.qmf1Support=!qmfV2;
+ opts.qmf2Support=qmfV2;
+ opts.mgmtPubInterval=pubInterval;
+ mFix = new MessagingFixture(opts, true);
+
+ _qmf::TestObject::registerSelf(getBrokerAgent());
+ };
+ ~AgentFixture()
+ {
+ delete mFix;
+ };
+ ::qpid::management::ManagementAgent *getBrokerAgent() { return mFix->broker->getManagementAgent(); }
+ Receiver createV1DataIndRcvr( const std::string package, const std::string klass )
+ {
+ return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
+ "node: {type: queue, "
+ "x-bindings: [{exchange: qpid.management, "
+ "key: 'console.obj.1.0.")
+ + package + std::string(".") + klass
+ + std::string("'}]}}"));
+ };
+ Receiver createV2DataIndRcvr( const std::string package, const std::string klass )
+ {
+ std::string p(package);
+ std::replace(p.begin(), p.end(), '.', '_');
+ std::string k(klass);
+ std::replace(k.begin(), k.end(), '.', '_');
+
+ return mFix->session.createReceiver(std::string("kqueue; {create: always, delete: always, "
+ "node: {type: queue, "
+ "x-bindings: [{exchange: qmf.default.topic, "
+ "key: 'agent.ind.data.")
+ + p + std::string(".") + k
+ + std::string("'}]}}"));
+ };
+};
+
+
+// A "management object" that supports the TestObject
+//
+class TestManageable : public qpid::management::Manageable
+{
+ management::ManagementObject::shared_ptr mgmtObj;
+ const std::string key;
+ public:
+ TestManageable(management::ManagementAgent *agent, std::string _key)
+ : key(_key)
+ {
+ _qmf::TestObject::shared_ptr tmp(new _qmf::TestObject(agent, this));
+
+ // seed it with some default values...
+ tmp->set_string1(key);
+ tmp->set_bool1(true);
+ qpid::types::Variant::Map vMap;
+ vMap["one"] = qpid::types::Variant(1);
+ vMap["two"] = qpid::types::Variant("two");
+ vMap["three"] = qpid::types::Variant("whatever");
+ tmp->set_map1(vMap);
+
+ mgmtObj = tmp;
+ };
+ ~TestManageable() { mgmtObj.reset(); }
+ management::ManagementObject::shared_ptr GetManagementObject() const { return mgmtObj; };
+ static void validateTestObjectProperties(_qmf::TestObject& to)
+ {
+ // verify the default values are as expected. We don't check 'string1',
+ // as it is the object key, and is unique for each object (no default value).
+ BOOST_CHECK(to.get_bool1() == true);
+ BOOST_CHECK(to.get_map1().size() == 3);
+ qpid::types::Variant::Map mappy = to.get_map1();
+ BOOST_CHECK(1 == (unsigned int)mappy["one"]);
+ BOOST_CHECK(mappy["two"].asString() == std::string("two"));
+ BOOST_CHECK(mappy["three"].asString() == std::string("whatever"));
+ };
+};
+
+
+// decode a V1 Content Indication message
+//
+void decodeV1ObjectUpdates(const Message& inMsg, TestObjectVector& objs, const size_t objLen)
+{
+ const size_t MAX_BUFFER_SIZE=65536;
+ char tmp[MAX_BUFFER_SIZE];
+
+ objs.clear();
+
+ BOOST_CHECK(inMsg.getContent().size() <= MAX_BUFFER_SIZE);
+
+ ::memcpy(tmp, inMsg.getContent().data(), inMsg.getContent().size());
+ Buffer buf(tmp, inMsg.getContent().size());
+
+ while (buf.available() > 8) { // 8 == qmf v1 header size
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'A');
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'M');
+ BOOST_CHECK_EQUAL(buf.getOctet(), '2');
+ BOOST_CHECK_EQUAL(buf.getOctet(), 'c'); // opcode == content indication
+ // @@todo: kag: how do we skip 'i' entries???
+ buf.getLong(); // ignore sequence
+
+ std::string str1; // decode content body as string
+ buf.getRawData(str1, objLen);
+
+ TestObjectPtr fake(new _qmf::TestObject(0,0));
+ fake->readProperties( str1 );
+ objs.push_back(fake);
+ }
+}
+
+
+// decode a V2 Content Indication message
+//
+void decodeV2ObjectUpdates(const qpid::messaging::Message& inMsg, TestObjectVector& objs)
+{
+ objs.clear();
+
+ BOOST_CHECK_EQUAL(inMsg.getContentType(), std::string("amqp/list"));
+
+ const ::qpid::types::Variant::Map& m = inMsg.getProperties();
+ Variant::Map::const_iterator iter = m.find(std::string("qmf.opcode"));
+ BOOST_CHECK(iter != m.end());
+ BOOST_CHECK_EQUAL(iter->second.asString(), std::string("_data_indication"));
+
+ Variant::List vList;
+ ::qpid::amqp_0_10::ListCodec::decode(inMsg.getContent(), vList);
+
+ for (Variant::List::iterator lIter = vList.begin(); lIter != vList.end(); lIter++) {
+ TestObjectPtr fake(new _qmf::TestObject(0,0));
+ fake->readTimestamps(lIter->asMap());
+ fake->mapDecodeValues((lIter->asMap())["_values"].asMap());
+ objs.push_back(fake);
+ }
+}
+}
+
+QPID_AUTO_TEST_SUITE(BrokerMgmtAgent)
+
+// verify that an object that is added to the broker's management database is
+// published correctly. Furthermore, verify that it is published once after
+// it has been deleted.
+//
+QPID_AUTO_TEST_CASE(v1ObjPublish)
+{
+ AgentFixture* fix = new AgentFixture(3);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // create a manageable test object
+ TestManageable *tm = new TestManageable(agent, std::string("obj1"));
+ uint32_t objLen = tm->GetManagementObject()->writePropertiesSize();
+
+ Receiver r1 = fix->createV1DataIndRcvr("org.apache.qpid.broker.mgmt.test", "#");
+
+ agent->addObject(tm->GetManagementObject(), 1);
+
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
+
+ TestObjectVector objs;
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ BOOST_CHECK(0 == mappy["_delete_ts"].asUint64()); // not deleted
+ }
+
+ // destroy the object
+
+ tm->GetManagementObject()->resourceDestroy();
+
+ // wait for the deleted object to be published
+
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV1ObjectUpdates(m1, objs, objLen);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+
+ r1.close();
+ delete fix;
+ delete tm;
+}
+
+// Repeat the previous test, but with V2-based object support
+//
+QPID_AUTO_TEST_CASE(v2ObjPublish)
+{
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ TestManageable *tm = new TestManageable(agent, std::string("obj2"));
+
+ Receiver r1 = fix->createV2DataIndRcvr(tm->GetManagementObject()->getPackageName(), "#");
+
+ agent->addObject(tm->GetManagementObject(), "testobj-1");
+
+ // wait for the object to be published
+ Message m1;
+ BOOST_CHECK(r1.fetch(m1, Duration::SECOND * 6));
+
+ TestObjectVector objs;
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ BOOST_CHECK(0 == mappy["_delete_ts"].asUint64());
+ }
+
+ // destroy the object
+
+ tm->GetManagementObject()->resourceDestroy();
+
+ // wait for the deleted object to be published
+
+ bool isDeleted = false;
+ while (!isDeleted && r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0)
+ isDeleted = true;
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+
+ r1.close();
+ delete fix;
+ delete tm;
+}
+
+// See QPID-2997
+QPID_AUTO_TEST_CASE(v2RapidRestoreObj)
+{
+ AgentFixture* fix = new AgentFixture(3, true);
+ management::ManagementAgent* agent;
+ agent = fix->getBrokerAgent();
+
+ // two objects, same ObjID
+ TestManageable *tm1 = new TestManageable(agent, std::string("obj2"));
+ TestManageable *tm2 = new TestManageable(agent, std::string("obj2"));
+
+ Receiver r1 = fix->createV2DataIndRcvr(tm1->GetManagementObject()->getPackageName(), "#");
+
+ // add, then immediately delete and re-add a copy of the object
+ agent->addObject(tm1->GetManagementObject(), "testobj-1");
+ tm1->GetManagementObject()->resourceDestroy();
+ agent->addObject(tm2->GetManagementObject(), "testobj-1");
+
+ // expect: a delete notification, then an update notification
+ TestObjectVector objs;
+ bool isDeleted = false;
+ bool isAdvertised = false;
+ size_t count = 0;
+ Message m1;
+ while (r1.fetch(m1, Duration::SECOND * 6)) {
+
+ decodeV2ObjectUpdates(m1, objs);
+ BOOST_CHECK(objs.size() > 0);
+
+ for (TestObjectVector::iterator oIter = objs.begin(); oIter != objs.end(); oIter++) {
+ count++;
+ TestManageable::validateTestObjectProperties(**oIter);
+
+ qpid::types::Variant::Map mappy;
+ (*oIter)->writeTimestamps(mappy);
+ if (mappy["_delete_ts"].asUint64() != 0) {
+ isDeleted = true;
+ BOOST_CHECK(isAdvertised == false); // delete must be first
+ } else {
+ isAdvertised = true;
+ BOOST_CHECK(isDeleted == true); // delete must be first
+ }
+ }
+ }
+
+ BOOST_CHECK(isDeleted);
+ BOOST_CHECK(isAdvertised);
+ BOOST_CHECK(count == 2);
+
+ r1.close();
+ delete fix;
+ delete tm1;
+ delete tm2;
+}
+
+QPID_AUTO_TEST_SUITE_END()
+}
+}
+
+
diff --git a/qpid/cpp/src/tests/BrokerMgmtAgent.xml b/qpid/cpp/src/tests/BrokerMgmtAgent.xml
new file mode 100644
index 0000000000..202b8debf3
--- /dev/null
+++ b/qpid/cpp/src/tests/BrokerMgmtAgent.xml
@@ -0,0 +1,38 @@
+<schema package="org.apache.qpid.broker.mgmt.test">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+ <!--
+ ===============================================================
+ TestObject
+ ===============================================================
+ -->
+ <class name="TestObject">
+
+ A test object defined for the BrokerMgmtAgent unit test.
+
+ <property name="string1" type="lstr" access="RW" index="y"/>
+ <property name="bool1" type="bool" access="RW"/>
+ <property name="map1" type="map" access="RW"/>
+
+ </class>
+
+</schema>
+
diff --git a/qpid/cpp/src/tests/BrokerOptions.cpp b/qpid/cpp/src/tests/BrokerOptions.cpp
new file mode 100644
index 0000000000..b36d96916a
--- /dev/null
+++ b/qpid/cpp/src/tests/BrokerOptions.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.
+ *
+ */
+
+/** Unit tests for various broker configuration options **/
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "MessagingFixture.h"
+
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(BrokerOptionsTestSuite)
+
+using namespace qpid::broker;
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace qpid;
+
+QPID_AUTO_TEST_CASE(testDisabledTimestamp)
+{
+ // by default, there should be no timestamp added by the broker
+ MessagingFixture fix;
+
+ Sender sender = fix.session.createSender("test-q; {create:always, delete:sender}");
+ messaging::Message msg("hi");
+ sender.send(msg);
+
+ Receiver receiver = fix.session.createReceiver("test-q");
+ messaging::Message in;
+ BOOST_CHECK(receiver.fetch(in, Duration::IMMEDIATE));
+ Variant::Map props = in.getProperties();
+ BOOST_CHECK(props.find("x-amqp-0-10.timestamp") == props.end());
+}
+
+QPID_AUTO_TEST_CASE(testEnabledTimestamp)
+{
+ // when enabled, the 0.10 timestamp is added by the broker
+ Broker::Options opts;
+ opts.timestampRcvMsgs = true;
+ MessagingFixture fix(opts, true);
+
+ Sender sender = fix.session.createSender("test-q; {create:always, delete:sender}");
+ messaging::Message msg("one");
+ sender.send(msg);
+
+ Receiver receiver = fix.session.createReceiver("test-q");
+ messaging::Message in;
+ BOOST_CHECK(receiver.fetch(in, Duration::IMMEDIATE));
+ Variant::Map props = in.getProperties();
+ BOOST_CHECK(props.find("x-amqp-0-10.timestamp") != props.end());
+ BOOST_CHECK(props["x-amqp-0-10.timestamp"]);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}}
diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt
new file mode 100644
index 0000000000..20f98204a4
--- /dev/null
+++ b/qpid/cpp/src/tests/CMakeLists.txt
@@ -0,0 +1,402 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Make sure that everything get built before the tests
+# Need to create a var with all the necessary top level targets
+
+# If we're linking Boost for DLLs, turn that on for the unit test too.
+if (QPID_LINK_BOOST_DYNAMIC)
+ add_definitions(-DBOOST_TEST_DYN_LINK)
+endif (QPID_LINK_BOOST_DYNAMIC)
+
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+
+# Using the Boost DLLs triggers warning 4275 on Visual Studio
+# (non dll-interface class used as base for dll-interface class).
+# This is ok, so suppress the warning.
+# Also, boost lengthy names trigger warning 4503, decorated name length exceeded
+# and using getenv() triggers insecure CRT warnings which we can silence in the
+# test environment.
+if (MSVC)
+ add_definitions( /wd4275 /wd4503 /D_CRT_SECURE_NO_WARNINGS)
+endif (MSVC)
+
+# Macro to make it easier to remember where the tests are built
+macro(remember_location testname)
+ set (${testname}_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${testname}${CMAKE_EXECUTABLE_SUFFIX})
+endmacro(remember_location)
+
+# If we're using GCC allow variadic macros (even though they're c99 not c++01)
+if (CMAKE_COMPILER_IS_GNUCXX)
+ add_definitions(-Wno-variadic-macros)
+endif (CMAKE_COMPILER_IS_GNUCXX)
+
+# Windows uses some process-startup calls to ensure that errors, etc. don't
+# result in error boxes being thrown up. Since it's expected that most test
+# runs will be in scripts, the default is to force these outputs to stderr
+# instead of windows. If you want to remove this code, build without the
+# QPID_WINDOWS_DEFAULT_TEST_OUTPUTS ON.
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ option(QPID_WINDOWS_DEFAULT_TEST_OUTPUTS "Use default error-handling on Windows tests" OFF)
+ if (NOT QPID_WINDOWS_DEFAULT_TEST_OUTPUTS)
+ set(platform_test_additions windows/DisableWin32ErrorWindows.cpp)
+ endif (NOT QPID_WINDOWS_DEFAULT_TEST_OUTPUTS)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+# Some generally useful utilities that just happen to be built in the test area
+add_executable (qpid-receive qpid-receive.cpp Statistics.cpp ${platform_test_additions})
+target_link_libraries (qpid-receive qpidmessaging qpidtypes qpidcommon)
+remember_location(qpid-receive)
+
+add_executable (qpid-send qpid-send.cpp Statistics.cpp ${platform_test_additions})
+target_link_libraries (qpid-send qpidmessaging qpidtypes qpidcommon)
+remember_location(qpid-send)
+
+install (TARGETS
+ qpid-receive qpid-send
+ RUNTIME DESTINATION ${QPID_INSTALL_BINDIR})
+
+add_executable (qpid-perftest qpid-perftest.cpp ${platform_test_additions})
+target_link_libraries (qpid-perftest qpidclient qpidcommon ${Boost_PROGRAM_OPTIONS_LIBRARY})
+remember_location(qpid-perftest)
+
+add_executable (qpid-latency-test qpid-latency-test.cpp ${platform_test_additions})
+target_link_libraries (qpid-latency-test qpidclient qpidcommon)
+remember_location(qpid-latency-test)
+
+add_executable (qpid-client-test qpid-client-test.cpp ${platform_test_additions})
+target_link_libraries (qpid-client-test qpidclient qpidcommon)
+remember_location(qpid-client-test)
+
+add_executable (qpid-ping qpid-ping.cpp ${platform_test_additions})
+target_link_libraries (qpid-ping qpidmessaging qpidtypes qpidcommon)
+remember_location(qpid-ping)
+
+add_executable (qpid-topic-listener qpid-topic-listener.cpp ${platform_test_additions})
+target_link_libraries (qpid-topic-listener qpidclient qpidcommon)
+remember_location(qpid-topic-listener)
+
+add_executable (qpid-topic-publisher qpid-topic-publisher.cpp ${platform_test_additions})
+target_link_libraries (qpid-topic-publisher qpidclient qpidcommon)
+remember_location(qpid-topic-publisher)
+
+add_executable (receiver receiver.cpp ${platform_test_additions})
+target_link_libraries (receiver qpidclient qpidcommon)
+remember_location(receiver)
+
+# This is bizarre - using both messaging and client libraries
+add_executable (sender sender.cpp Statistics.cpp ${platform_test_additions})
+target_link_libraries (sender qpidmessaging qpidtypes qpidclient qpidcommon)
+remember_location(sender)
+
+add_executable (qpid-txtest qpid-txtest.cpp ${platform_test_additions})
+target_link_libraries (qpid-txtest qpidclient qpidcommon qpidtypes)
+#qpid_txtest_SOURCES=qpid-txtest.cpp TestOptions.h ConnectionOptions.h
+remember_location(qpid-txtest)
+
+add_executable (qpid-txtest2 qpid-txtest2.cpp ${platform_test_additions})
+target_link_libraries (qpid-txtest2 qpidmessaging qpidtypes qpidcommon)
+remember_location(qpid-txtest2)
+
+install (TARGETS
+ qpid-perftest qpid-latency-test qpid-client-test
+ qpid-ping
+ qpid-topic-listener qpid-topic-publisher receiver sender
+ qpid-txtest qpid-txtest2
+ RUNTIME DESTINATION ${QPID_INSTALL_TESTDIR})
+
+# Only build test code if testing is turned on
+if (BUILD_TESTING)
+
+# Create the environment scripts for tests
+set (abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
+set (abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
+set (abs_top_srcdir ${CMAKE_SOURCE_DIR})
+set (abs_top_builddir ${CMAKE_BINARY_DIR})
+set (builddir_lib_suffix "")
+
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ configure_file (${CMAKE_CURRENT_SOURCE_DIR}/test_env.ps1.in
+ ${CMAKE_CURRENT_BINARY_DIR}/test_env.ps1 @ONLY)
+else (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ configure_file (${CMAKE_CURRENT_SOURCE_DIR}/test_env.sh.in
+ ${CMAKE_CURRENT_BINARY_DIR}/test_env.sh @ONLY)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+# Copy qpidd-p0 script to build directory so tests can find it.
+configure_file (${CMAKE_CURRENT_SOURCE_DIR}/qpidd-p0 ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
+
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ set (ENV{OUTDIR} ${EXECUTABLE_OUTPUT_PATH})
+ set (test_script_suffix ".ps1")
+ set (shell "powershell")
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+set(test_wrap ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_test${test_script_suffix} -buildDir ${CMAKE_BINARY_DIR})
+set(python_wrap ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_test${test_script_suffix} -buildDir ${CMAKE_BINARY_DIR} -python)
+
+if (BUILD_TESTING_UNITTESTS)
+
+#
+# Unit test program
+#
+# Unit tests are built as a single program to reduce valgrind overhead
+# when running the tests. If you want to build a subset of the tests run
+# ccmake and set unit_tests_to_build to the set you want to build.
+
+# Like this to work with cmake 2.4 on Unix
+set (qpid_test_boost_libs
+ ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_SYSTEM_LIBRARY})
+
+set(all_unit_tests
+ AccumulatedAckTest
+ Acl
+ AclHost
+ Array
+ AsyncCompletion
+ AtomicValue
+ ClientMessage
+ ClientMessageTest
+ ClientSessionTest
+ DeliveryRecordTest
+ DtxWorkRecordTest
+ exception_test
+ ExchangeTest
+ FieldTable
+ FieldValue
+ FrameDecoder
+ FramingTest
+ HeadersExchangeTest
+ HeaderTest
+ InlineAllocator
+ InlineVector
+ logging
+ ManagementTest
+ MessageReplayTracker
+ MessageTest
+ MessagingLogger
+ MessagingSessionTests
+ PollableCondition
+ ProxyTest
+ QueueDepth
+ QueueFlowLimitTest
+ QueueOptionsTest
+ QueuePolicyTest
+ QueueRegistryTest
+ QueueTest
+ RangeSet
+ RefCounted
+ RetryList
+ Selector
+ SequenceNumberTest
+ SequenceSet
+ SessionState
+ Shlib
+ StringUtils
+ SystemInfo
+ TimerTest
+ TopicExchangeTest
+ TxBufferTest
+ TransactionObserverTest
+ Url
+ Uuid
+ Variant
+ ${xml_tests}
+ )
+
+set(unit_tests_to_build
+ ""
+ CACHE STRING "Which unit tests to build"
+ )
+
+mark_as_advanced(unit_tests_to_build)
+
+# If no unit_test specifically set then use all unit tests
+if (unit_tests_to_build)
+set(actual_unit_tests ${unit_tests_to_build})
+else()
+set(actual_unit_tests ${all_unit_tests})
+endif()
+
+add_executable (unit_test unit_test
+ ${actual_unit_tests} ${platform_test_additions})
+target_link_libraries (unit_test
+ ${qpid_test_boost_libs}
+ qpidmessaging qpidtypes qpidbroker qpidclient qpidcommon)
+set_target_properties (unit_test PROPERTIES COMPILE_DEFINITIONS _IN_QPID_BROKER)
+remember_location(unit_test)
+
+add_test (unit_test ${test_wrap} -boostTest -- ${unit_test_LOCATION})
+
+endif (BUILD_TESTING_UNITTESTS)
+
+add_library (shlibtest MODULE shlibtest.cpp)
+
+if (BUILD_SASL)
+ add_custom_command(
+ OUTPUT sasl_config/qpidd.conf sasl_config/qpidd.sasldb
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/sasl_test_setup.sh)
+
+ add_custom_target(
+ sasl_config ALL
+ DEPENDS sasl_config/qpidd.conf sasl_config/qpidd.sasldb)
+endif (BUILD_SASL)
+
+#
+# Other test programs
+#
+add_executable (echotest echotest.cpp ${platform_test_additions})
+target_link_libraries (echotest qpidclient qpidcommon)
+remember_location(echotest)
+
+add_executable (publish publish.cpp ${platform_test_additions})
+target_link_libraries (publish qpidclient qpidcommon)
+remember_location(publish)
+
+add_executable (consume consume.cpp ${platform_test_additions})
+target_link_libraries (consume qpidclient qpidcommon)
+remember_location(consume)
+
+add_executable (header_test header_test.cpp ${platform_test_additions})
+target_link_libraries (header_test qpidclient qpidcommon)
+remember_location(header_test)
+
+add_executable (declare_queues declare_queues.cpp ${platform_test_additions})
+target_link_libraries (declare_queues qpidclient qpidcommon)
+remember_location(declare_queues)
+
+add_executable (replaying_sender replaying_sender.cpp ${platform_test_additions})
+target_link_libraries (replaying_sender qpidclient qpidcommon)
+remember_location(replaying_sender)
+
+add_executable (resuming_receiver resuming_receiver.cpp ${platform_test_additions})
+target_link_libraries (resuming_receiver qpidclient qpidcommon)
+remember_location(resuming_receiver)
+
+add_executable (txshift txshift.cpp ${platform_test_additions})
+target_link_libraries (txshift qpidclient qpidcommon)
+remember_location(txshift)
+
+add_executable (txjob txjob.cpp ${platform_test_additions})
+target_link_libraries (txjob qpidclient qpidcommon)
+remember_location(txjob)
+
+add_executable (datagen datagen.cpp ${platform_test_additions})
+target_link_libraries (datagen qpidclient qpidcommon)
+remember_location(datagen)
+
+add_executable (msg_group_test msg_group_test.cpp ${platform_test_additions})
+target_link_libraries (msg_group_test qpidmessaging qpidtypes qpidcommon)
+remember_location(msg_group_test)
+
+add_executable (ha_test_max_queues ha_test_max_queues.cpp ${platform_test_additions})
+target_link_libraries (ha_test_max_queues qpidclient qpidcommon)
+remember_location(ha_test_max_queues)
+
+if (BUILD_SASL)
+ add_executable (sasl_version sasl_version.cpp ${platform_test_additions})
+ remember_location(sasl_version)
+endif (BUILD_SASL)
+
+set (python_src ${CMAKE_SOURCE_DIR}/../python)
+if (EXISTS ${python_src})
+ set (python_bld ${CMAKE_CURRENT_BINARY_DIR}/python)
+ # This will not pick up added or deleted python files
+ # In that case you need to rerun CMake
+ file(GLOB_RECURSE python_files ${python_src}/*.py)
+
+ add_custom_command(
+ OUTPUT ${python_bld}
+ DEPENDS ${python_files}
+ COMMAND ${PYTHON_EXECUTABLE}
+ setup.py
+ build --build-base=${python_bld}/build
+ install --prefix=${python_bld} --install-lib=${python_bld} --install-scripts=${python_bld}/commands
+ WORKING_DIRECTORY ${python_src}
+ )
+
+ add_custom_target(
+ python_bld ALL
+ DEPENDS ${python_bld}
+ )
+endif (EXISTS ${python_src})
+
+if (BUILD_SASL)
+ add_test (sasl_fed ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/sasl_fed${test_script_suffix})
+ add_test (sasl_fed_ex_dynamic ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/sasl_fed_ex${test_script_suffix} dynamic)
+ add_test (sasl_fed_ex_link ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/sasl_fed_ex${test_script_suffix} link)
+ add_test (sasl_fed_ex_queue ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/sasl_fed_ex${test_script_suffix} queue)
+ add_test (sasl_fed_ex_route ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/sasl_fed_ex${test_script_suffix} route)
+ add_test (sasl_no_dir ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/sasl_no_dir${test_script_suffix})
+ if (BUILD_SSL)
+ add_test(ssl_test ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/ssl_test${test_script_suffix})
+ endif (BUILD_SSL)
+endif (BUILD_SASL)
+add_test (qpid-client-test ${test_wrap} -startBroker -- ${qpid-client-test_LOCATION})
+add_test (quick_perftest ${test_wrap} -startBroker -- ${qpid-perftest_LOCATION} --summary --count 100)
+add_test (quick_topictest ${test_wrap} -startBroker -- ${CMAKE_CURRENT_SOURCE_DIR}/quick_topictest${test_script_suffix})
+add_test (quick_txtest ${test_wrap} -startBroker -- ${qpid-txtest_LOCATION} --queues 4 --tx-count 10 --quiet)
+add_test (quick_txtest2 ${test_wrap} -startBroker -- ${qpid-txtest2_LOCATION} --queues 4 --tx-count 10 --quiet)
+add_test (msg_group_tests ${test_wrap} -startBroker -- ${CMAKE_CURRENT_SOURCE_DIR}/run_msg_group_tests${test_script_suffix})
+add_test (run_header_test ${test_wrap} -startBroker -- ${CMAKE_CURRENT_SOURCE_DIR}/run_header_test${test_script_suffix})
+add_test (python_tests ${test_wrap} -startBroker -- ${CMAKE_CURRENT_SOURCE_DIR}/python_tests${test_script_suffix})
+if (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+ # paged queue not yet implemented for windows
+ add_test (paged_queue_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_paged_queue_tests${test_script_suffix})
+endif (NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
+
+if (BUILD_AMQP)
+ add_test (interop_tests ${python_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/interop_tests.py)
+endif (BUILD_AMQP)
+
+add_test (ha_tests ${python_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/ha_tests.py)
+add_test (qpidd_qmfv2_tests ${python_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/qpidd_qmfv2_tests.py)
+if (BUILD_AMQP)
+ add_test (interlink_tests ${python_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/interlink_tests.py)
+ add_test (idle_timeout_tests ${python_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/idle_timeout_tests.py)
+endif (BUILD_AMQP)
+add_test (swig_python_tests ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/swig_python_tests${test_script_suffix})
+add_test (ipv6_test ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/ipv6_test${test_script_suffix})
+add_test (federation_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_federation_tests${test_script_suffix})
+add_test (federation_sys_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_federation_sys_tests${test_script_suffix})
+add_test (queue_flow_limit_tests
+ ${test_wrap}
+ -startBroker -brokerOptions "--default-flow-stop-threshold=80 --default-flow-resume-threshold=70"
+ -- ${CMAKE_CURRENT_SOURCE_DIR}/run_queue_flow_limit_tests${test_script_suffix})
+if (BUILD_ACL)
+ add_test (acl_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_acl_tests${test_script_suffix})
+endif (BUILD_ACL)
+add_test (cli_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_cli_tests${test_script_suffix})
+add_test (dynamic_log_level_test ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_log_level_test${test_script_suffix})
+add_test (dynamic_log_hires_timestamp ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_log_hires_timestamp${test_script_suffix})
+if (BUILD_MSSQL)
+ add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL)
+endif (BUILD_MSSQL)
+if (BUILD_MSCLFS)
+ add_test (store_tests_clfs ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL-CLFS)
+endif (BUILD_MSCLFS)
+add_test (queue_redirect ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_queue_redirect${test_script_suffix})
+
+add_library(test_store MODULE test_store.cpp)
+target_link_libraries (test_store qpidbroker qpidcommon)
+set_target_properties (test_store PROPERTIES PREFIX "" COMPILE_DEFINITIONS _IN_QPID_BROKER)
+
+add_library (dlclose_noop MODULE dlclose_noop.c)
+
+endif (BUILD_TESTING)
diff --git a/qpid/cpp/src/tests/ClientMessage.cpp b/qpid/cpp/src/tests/ClientMessage.cpp
new file mode 100644
index 0000000000..994c46552c
--- /dev/null
+++ b/qpid/cpp/src/tests/ClientMessage.cpp
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include "qpid/messaging/Message.h"
+
+#include "unit_test.h"
+
+using namespace qpid::messaging;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClientMessageSuite)
+
+QPID_AUTO_TEST_CASE(testCopyConstructor)
+{
+ Message m("my-data");
+ m.setSubject("my-subject");
+ m.getProperties()["a"] = "ABC";
+ Message c(m);
+ BOOST_CHECK_EQUAL(m.getContent(), c.getContent());
+ BOOST_CHECK_EQUAL(m.getSubject(), c.getSubject());
+ BOOST_CHECK_EQUAL(m.getProperties()["a"], c.getProperties()["a"]);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ClientMessageTest.cpp b/qpid/cpp/src/tests/ClientMessageTest.cpp
new file mode 100644
index 0000000000..f925f1c234
--- /dev/null
+++ b/qpid/cpp/src/tests/ClientMessageTest.cpp
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+/**@file Unit tests for the client::Message class. */
+
+#include "unit_test.h"
+#include "qpid/client/Message.h"
+
+using namespace qpid::client;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ClientMessageTestSuite)
+
+QPID_AUTO_TEST_CASE(MessageCopyAssign) {
+ // Verify that message has normal copy semantics.
+ Message m("foo");
+ BOOST_CHECK_EQUAL("foo", m.getData());
+ Message c(m);
+ BOOST_CHECK_EQUAL("foo", c.getData());
+ Message a;
+ BOOST_CHECK_EQUAL("", a.getData());
+ a = m;
+ BOOST_CHECK_EQUAL("foo", a.getData());
+ a.setData("a");
+ BOOST_CHECK_EQUAL("a", a.getData());
+ c.setData("c");
+ BOOST_CHECK_EQUAL("c", c.getData());
+ BOOST_CHECK_EQUAL("foo", m.getData());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ClientSessionTest.cpp b/qpid/cpp/src/tests/ClientSessionTest.cpp
new file mode 100644
index 0000000000..f35524c0c0
--- /dev/null
+++ b/qpid/cpp/src/tests/ClientSessionTest.cpp
@@ -0,0 +1,663 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/QueueOptions.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Time.h"
+#include "qpid/client/Session.h"
+#include "qpid/client/Message.h"
+#include "qpid/framing/reply_exceptions.h"
+
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/format.hpp>
+
+#include <vector>
+
+namespace qpid {
+namespace tests {
+
+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::BrokerOptions;
+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 SessionFixture
+{
+ ClientSessionFixture(const BrokerOptions& opts = BrokerOptions()) : SessionFixture(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=Message(boost::lexical_cast<string>(i), "my-queue"));
+ DummyListener listener(fix.session, "my-queue", count);
+ listener.run();
+ BOOST_CHECK_EQUAL(count, listener.messages.size());
+ for (size_t i = 0; i < count; ++i)
+ BOOST_CHECK_EQUAL(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=Message(boost::lexical_cast<string>(i), "my-queue"));
+ }
+ t.join();
+ BOOST_CHECK_EQUAL(count, listener.messages.size());
+ for (size_t i = 0; i < count; ++i)
+ BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData());
+}
+
+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(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()
+ {
+ qpid::sys::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) {
+ BrokerOptions opts;
+ opts.queueCleanInterval = 1*TIME_SEC;
+ opts.queueFlowStopRatio = 0;
+ opts.queueFlowResumeRatio = 0;
+ ClientSessionFixture fix(opts);
+ FieldTable args;
+ args.setInt("qpid.max_count",10);
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+
+ for (uint i = 0; i < 10; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ if (i % 2) m.getDeliveryProperties().setTtl(500);
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 10u);
+ qpid::sys::sleep(2);
+ BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 5u);
+ fix.session.messageTransfer(arg::content=Message("Message_11", "my-queue"));//ensure policy is also updated
+}
+
+QPID_AUTO_TEST_CASE(testExpirationOnPop) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ for (uint i = 0; i < 10; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ if (i % 2) m.getDeliveryProperties().setTtl(200);
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ qpid::sys::usleep(300* 1000);
+
+ for (uint i = 0; i < 10; i++) {
+ if (i % 2) continue;
+ Message m;
+ BOOST_CHECK(fix.subs.get(m, "my-queue", TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRelease) {
+ ClientSessionFixture fix;
+
+ const uint count=10;
+ for (uint i = 0; i < count; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ fix.subs.setAutoStop(false);
+ fix.subs.start();
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+
+ SimpleListener l1;
+ Subscription s1 = fix.subs.subscribe(l1, "my-queue", settings);
+ l1.waitFor(count);
+ s1.cancel();
+
+ for (uint i = 0; i < count; i++) {
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l1.messages[i].getData());
+ }
+ s1.release(s1.getUnaccepted());
+
+ //check that released messages are redelivered
+ settings.autoAck = 1;
+ SimpleListener l2;
+ Subscription s2 = fix.subs.subscribe(l2, "my-queue", settings);
+ l2.waitFor(count);
+ for (uint i = 0; i < count; i++) {
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), l2.messages[i].getData());
+ }
+
+ fix.subs.stop();
+ fix.subs.wait();
+ fix.session.close();
+}
+
+QPID_AUTO_TEST_CASE(testCompleteOnAccept) {
+ ClientSessionFixture fix;
+ const uint count = 8;
+ const uint chunk = 4;
+ for (uint i = 0; i < count; i++) {
+ Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ fix.session.messageTransfer(arg::content=m);
+ }
+
+ SubscriptionSettings settings;
+ settings.autoAck = 0;
+ settings.completionMode = COMPLETE_ON_ACCEPT;
+ settings.flowControl = FlowControl::messageWindow(chunk);
+
+ LocalQueue q;
+ Subscription s = fix.subs.subscribe(q, "my-queue", settings);
+ fix.session.messageFlush(arg::destination=s.getName());
+ SequenceSet accepted;
+ for (uint i = 0; i < chunk; i++) {
+ Message m;
+ BOOST_CHECK(q.get(m));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ accepted.add(m.getId());
+ }
+ Message m;
+ BOOST_CHECK(!q.get(m));
+
+ s.accept(accepted);
+ //need to reallocate credit as we have flushed it all out
+ s.setFlowControl(FlowControl::messageWindow(chunk));
+ fix.session.messageFlush(arg::destination=s.getName());
+ accepted.clear();
+
+ for (uint i = chunk; i < count; i++) {
+ Message m;
+ BOOST_CHECK(q.get(m));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), m.getData());
+ accepted.add(m.getId());
+ }
+ fix.session.messageAccept(accepted);
+}
+
+namespace
+{
+struct Publisher : qpid::sys::Runnable
+{
+ AsyncSession session;
+ Message message;
+ uint count;
+ Thread thread;
+
+ Publisher(Connection& con, Message m, uint c) : session(con.newSession()), message(m), count(c) {}
+
+ void start()
+ {
+ thread = Thread(*this);
+ }
+
+ void join()
+ {
+ thread.join();
+ }
+
+ void run()
+ {
+ for (uint i = 0; i < count; i++) {
+ session.messageTransfer(arg::content=message);
+ }
+ session.sync();
+ session.close();
+ }
+};
+}
+
+QPID_AUTO_TEST_CASE(testConcurrentSenders)
+{
+ //Ensure concurrent publishing sessions on a connection don't
+ //cause assertions, deadlocks or other undesirables:
+ BrokerFixture fix;
+ Connection connection;
+ ConnectionSettings settings;
+ settings.maxFrameSize = 1024;
+ settings.port = fix.broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+ connection.open(settings);
+ AsyncSession session = connection.newSession();
+ Message message(string(512, 'X'));
+
+ boost::ptr_vector<Publisher> publishers;
+ for (size_t i = 0; i < 5; i++) {
+ publishers.push_back(new Publisher(connection, message, 100));
+ }
+ std::for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::start, _1));
+ std::for_each(publishers.begin(), publishers.end(), boost::bind(&Publisher::join, _1));
+ connection.close();
+}
+
+
+QPID_AUTO_TEST_CASE(testExclusiveSubscribe)
+{
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="myq", arg::exclusive=true, arg::autoDelete=true);
+ SubscriptionSettings settings;
+ settings.exclusive = true;
+ LocalQueue q;
+ fix.subs.subscribe(q, "myq", settings, "first");
+ //attempt to create new subscriber should fail
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.subs.subscribe(q, "myq", "second"), ResourceLockedException);
+ ;
+
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveBinding) {
+ FieldTable options;
+ options.setString("qpid.exclusive-binding", "anything");
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="queue-1", arg::exclusive=true, arg::autoDelete=true);
+ fix.session.queueDeclare(arg::queue="queue-2", arg::exclusive=true, arg::autoDelete=true);
+ fix.session.exchangeBind(arg::exchange="amq.direct", arg::queue="queue-1", arg::bindingKey="my-key", arg::arguments=options);
+ fix.session.messageTransfer(arg::destination="amq.direct", arg::content=Message("message1", "my-key"));
+ fix.session.exchangeBind(arg::exchange="amq.direct", arg::queue="queue-2", arg::bindingKey="my-key", arg::arguments=options);
+ fix.session.messageTransfer(arg::destination="amq.direct", arg::content=Message("message2", "my-key"));
+
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "queue-1"));
+ BOOST_CHECK_EQUAL("message1", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "queue-1"));
+
+ BOOST_CHECK(fix.subs.get(got, "queue-2"));
+ BOOST_CHECK_EQUAL("message2", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "queue-2"));
+}
+
+QPID_AUTO_TEST_CASE(testResubscribeWithLocalQueue) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="some-queue", arg::exclusive=true, arg::autoDelete=true);
+ LocalQueue p, q;
+ fix.subs.subscribe(p, "some-queue");
+ fix.subs.cancel("some-queue");
+ fix.subs.subscribe(q, "some-queue");
+
+ fix.session.messageTransfer(arg::content=Message("some-data", "some-queue"));
+ fix.session.messageFlush(arg::destination="some-queue");
+
+ Message got;
+ BOOST_CHECK(!p.get(got));
+
+ BOOST_CHECK(q.get(got));
+ BOOST_CHECK_EQUAL("some-data", got.getData());
+ BOOST_CHECK(!q.get(got));
+}
+
+QPID_AUTO_TEST_CASE(testReliableDispatch) {
+ ClientSessionFixture fix;
+ std::string queue("a-queue");
+ fix.session.queueDeclare(arg::queue=queue, arg::autoDelete=true);
+
+ ConnectionSettings settings;
+ settings.port = fix.broker->getPort(qpid::broker::Broker::TCP_TRANSPORT);
+
+ Connection c1;
+ c1.open(settings);
+ Session s1 = c1.newSession();
+ SubscriptionManager subs1(s1);
+ LocalQueue q1;
+ subs1.subscribe(q1, queue, FlowControl());//first subscriber has no credit
+
+ Connection c2;
+ c2.open(settings);
+ Session s2 = c2.newSession();
+ SubscriptionManager subs2(s2);
+ LocalQueue q2;
+ subs2.subscribe(q2, queue);//second subscriber has credit
+
+ fix.session.messageTransfer(arg::content=Message("my-message", queue));
+
+ //check that the second consumer gets the message
+ Message got;
+ BOOST_CHECK(q2.get(got, 1*TIME_SEC));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+
+ c1.close();
+ c2.close();
+}
+
+QPID_AUTO_TEST_CASE(testSessionCloseOnInvalidSession) {
+ Session session;
+ session.close();
+}
+
+QPID_AUTO_TEST_CASE(testLVQVariedSize) {
+ ClientSessionFixture fix;
+ std::string queue("my-lvq");
+ QueueOptions args;
+ args.setOrdering(LVQ_NO_BROWSE);
+ fix.session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+
+ std::string key;
+ args.getLVQKey(key);
+
+ for (size_t i = 0; i < 10; i++) {
+ std::ostringstream data;
+ size_t size = 100 - ((i % 10) * 10);
+ data << std::string(size, 'x');
+
+ Message m(data.str(), queue);
+ m.getHeaders().setString(key, "abc");
+ fix.session.messageTransfer(arg::content=m);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSessionManagerSetFlowControl) {
+ ClientSessionFixture fix;
+ std::string name("dummy");
+ LocalQueue queue;
+ SubscriptionSettings settings;
+ settings.flowControl = FlowControl();
+ fix.session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true);
+ fix.subs.subscribe(queue, name, settings);
+ fix.session.messageTransfer(arg::content=Message("my-message", name));
+ fix.subs.setFlowControl(name, 1, FlowControl::UNLIMITED, false);
+ fix.session.messageFlush(name);
+ Message got;
+ BOOST_CHECK(queue.get(got, 0));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+}
+
+QPID_AUTO_TEST_CASE(testGetThenSubscribe) {
+ ClientSessionFixture fix;
+ std::string name("myqueue");
+ fix.session.queueDeclare(arg::queue=name, arg::exclusive=true, arg::autoDelete=true);
+ fix.session.messageTransfer(arg::content=Message("one", name));
+ fix.session.messageTransfer(arg::content=Message("two", name));
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, name));
+ BOOST_CHECK_EQUAL("one", got.getData());
+
+ DummyListener listener(fix.session, name, 1);
+ listener.run();
+ BOOST_CHECK_EQUAL(1u, listener.messages.size());
+ if (!listener.messages.empty()) {
+ BOOST_CHECK_EQUAL("two", listener.messages[0].getData());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testSessionIsValid) {
+ ClientSessionFixture fix;
+ BOOST_CHECK(fix.session.isValid());
+ Session session;
+ BOOST_CHECK(!session.isValid());
+}
+
+QPID_AUTO_TEST_CASE(testExpirationNotAltered) {
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ Message m("my-message", "my-queue");
+ m.getDeliveryProperties().setTtl(60000);
+ m.getDeliveryProperties().setExpiration(12345);
+ fix.session.messageTransfer(arg::content=m);
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "my-queue"));
+ BOOST_CHECK_EQUAL("my-message", got.getData());
+ BOOST_CHECK_EQUAL(12345u, got.getDeliveryProperties().getExpiration());
+}
+
+QPID_AUTO_TEST_CASE(testGetConnectionFromSession) {
+ ClientSessionFixture fix;
+ FieldTable options;
+ options.setInt("no-local", 1);
+ fix.session.queueDeclare(arg::queue="a", arg::exclusive=true, arg::autoDelete=true, arg::arguments=options);
+ fix.session.queueDeclare(arg::queue="b", arg::exclusive=true, arg::autoDelete=true);
+
+ Connection c = fix.session.getConnection();
+ Session s = c.newSession();
+ //If this new session was created as expected on the same connection as
+ //fix.session, then the no-local behaviour means that queue 'a'
+ //will not enqueue messages from this new session but queue 'b'
+ //will.
+ s.messageTransfer(arg::content=Message("a", "a"));
+ s.messageTransfer(arg::content=Message("b", "b"));
+
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "b"));
+ BOOST_CHECK_EQUAL("b", got.getData());
+ BOOST_CHECK(!fix.subs.get(got, "a"));
+}
+
+
+QPID_AUTO_TEST_CASE(testQueueDeleted)
+{
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue");
+ LocalQueue queue;
+ fix.subs.subscribe(queue, "my-queue");
+
+ ScopedSuppressLogging sl;
+ fix.session.queueDelete(arg::queue="my-queue");
+ BOOST_CHECK_THROW(queue.get(1*qpid::sys::TIME_SEC), qpid::framing::ResourceDeletedException);
+}
+
+QPID_AUTO_TEST_CASE(testTtl)
+{
+ const uint64_t ms = 1000ULL; // convert sec to ms
+ const uint64_t us = 1000ULL * 1000ULL; // convert sec to us
+
+ ClientSessionFixture fix;
+ fix.session.queueDeclare(arg::queue="ttl-test", arg::exclusive=true, arg::autoDelete=true);
+ Message msg1 = Message("AAA", "ttl-test");
+ uint64_t ttl = 2 * ms; // 2 sec
+ msg1.getDeliveryProperties().setTtl(ttl);
+ Connection c = fix.session.getConnection();
+ Session s = c.newSession();
+ s.messageTransfer(arg::content=msg1);
+
+ Message msg2 = Message("BBB", "ttl-test");
+ ttl = 10 * ms; // 10 sec
+ msg2.getDeliveryProperties().setTtl(ttl);
+ s.messageTransfer(arg::content=msg2);
+
+ qpid::sys::usleep(5 * us); // 5 sec
+
+ // Message "AAA" should be expired and never be delivered
+ // Check "BBB" has ttl somewhere between 1 and 5 secs
+ Message got;
+ BOOST_CHECK(fix.subs.get(got, "ttl-test"));
+ BOOST_CHECK_EQUAL("BBB", got.getData());
+ BOOST_CHECK(got.getDeliveryProperties().getTtl() > 1 * ms);
+ BOOST_CHECK(got.getDeliveryProperties().getTtl() < ttl - (5 * ms));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ConnectionOptions.h b/qpid/cpp/src/tests/ConnectionOptions.h
new file mode 100644
index 0000000000..fe945e9ddd
--- /dev/null
+++ b/qpid/cpp/src/tests/ConnectionOptions.h
@@ -0,0 +1,62 @@
+#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"
+
+namespace qpid {
+
+/**
+ * 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|ssl|rdma"), "Protocol to use for broker connection")
+ ("virtualhost,v", optValue(virtualhost, "VHOST"), "virtual host")
+ ("username", optValue(username, "USER"), "user name for broker log in.")
+ ("password", optValue(password, "PASSWORD"), "password for broker log in.")
+ ("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.")
+ ("locale", optValue(locale, "LOCALE"), "locale to use.")
+ ("max-channels", optValue(maxChannels, "N"), "the maximum number of channels the client requires.")
+ ("heartbeat", optValue(heartbeat, "N"), "Desired heartbeat interval in seconds.")
+ ("max-frame-size", optValue(maxFrameSize, "N"), "the maximum frame size to request.")
+ ("bounds-multiplier", optValue(bounds, "N"),
+ "bound size of write queue (as a multiple of the max frame size).")
+ ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay")
+ ("service", optValue(service, "SERVICE-NAME"), "SASL service name.")
+ ("min-ssf", optValue(minSsf, "N"), "Minimum acceptable strength for SASL security layer")
+ ("max-ssf", optValue(maxSsf, "N"), "Maximum acceptable strength for SASL security layer");
+ }
+};
+
+} // namespace qpid
+
+#endif /*!QPID_CLIENT_CONNECTIONOPTIONS_H*/
diff --git a/qpid/cpp/src/tests/DeliveryRecordTest.cpp b/qpid/cpp/src/tests/DeliveryRecordTest.cpp
new file mode 100644
index 0000000000..37b3095f81
--- /dev/null
+++ b/qpid/cpp/src/tests/DeliveryRecordTest.cpp
@@ -0,0 +1,67 @@
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/DeliveryRecord.h"
+#include "qpid/broker/Queue.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;
+
+namespace qpid {
+namespace tests {
+
+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(QueueCursor(CONSUMER), framing::SequenceNumber(), SequenceNumber(), Queue::shared_ptr(), "tag", Consumer::shared_ptr(), 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->getId() == ++expected);
+ }
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/DispatcherTest.cpp b/qpid/cpp/src/tests/DispatcherTest.cpp
new file mode 100644
index 0000000000..7312fe8d2e
--- /dev/null
+++ b/qpid/cpp/src/tests/DispatcherTest.cpp
@@ -0,0 +1,240 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/DispatchHandle.h"
+#include "qpid/sys/posix/PrivatePosix.h"
+#include "qpid/sys/Thread.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <iostream>
+#include <boost/bind.hpp>
+
+using namespace std;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+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();
+}
+
+void rInterrupt(DispatchHandle&) {
+ cerr << "R";
+}
+
+void wInterrupt(DispatchHandle&) {
+ cerr << "W";
+}
+
+DispatchHandle::Callback rcb = rInterrupt;
+DispatchHandle::Callback wcb = wInterrupt;
+
+DispatchHandleRef *volatile rh = 0;
+DispatchHandleRef *volatile wh = 0;
+
+volatile bool stopWait = false;
+volatile bool phase1finished = false;
+
+timer_t timer;
+
+void stop_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) {
+ stopWait = true;
+}
+
+void timer_handler(int /*signo*/, siginfo_t* /*info*/, void* /*context*/) {
+ static int count = 0;
+ if (count++ < 10) {
+ rh->call(rcb);
+ wh->call(wcb);
+ } else {
+ phase1finished = true;
+ assert(::timer_delete(timer) == 0);
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ // Create poller
+ Poller::shared_ptr poller(new Poller);
+
+ // Create dispatcher thread
+ Thread dt(*poller);
+ Thread dt1(*poller);
+ Thread dt2(*poller);
+ Thread dt3(*poller);
+
+ // Setup sender and receiver
+ int sv[2];
+ int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ assert(rc >= 0);
+
+ // Set non-blocking
+ rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+
+ rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+
+ // Make up a large string
+ string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;";
+ for (int i = 0; i < 8; i++)
+ testString += testString;
+
+ IOHandle f0(sv[0]);
+ IOHandle f1(sv[1]);
+
+ rh = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0);
+ wh = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0);
+
+ rh->startWatch(poller);
+ wh->startWatch(poller);
+
+ // Set up a regular itimer interupt
+ // We assume that this thread will handle the signals whilst sleeping
+ // as the Poller threads have signal handling blocked
+
+ // Signal handling
+ struct ::sigaction sa;
+ sa.sa_sigaction = timer_handler;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ ::sigemptyset(&sa.sa_mask);
+ rc = ::sigaction(SIGRTMIN, &sa,0);
+ assert(rc == 0);
+
+ ::sigevent se;
+ ::memset(&se, 0, sizeof(se)); // Clear to make valgrind happy (this *is* the neatest way to do this portably - sigh)
+ se.sigev_notify = SIGEV_SIGNAL;
+ se.sigev_signo = SIGRTMIN;
+ rc = ::timer_create(CLOCK_REALTIME, &se, &timer);
+ assert(rc == 0);
+
+ itimerspec ts = {
+ /*.it_value = */ {2, 0}, // s, ns
+ /*.it_interval = */ {2, 0}}; // s, ns
+
+ rc = ::timer_settime(timer, 0, &ts, 0);
+ assert(rc == 0);
+
+ // wait
+ while (!phase1finished) {
+ ::sleep(1);
+ }
+
+ // Now test deleting/creating DispatchHandles in tight loop, so that we are likely to still be using the
+ // attached PollerHandles after deleting the DispatchHandle
+ DispatchHandleRef* t = wh;
+ wh = 0;
+ delete t;
+ t = rh;
+ rh = 0;
+ delete t;
+
+ sa.sa_sigaction = stop_handler;
+ rc = ::sigaction(SIGRTMIN, &sa,0);
+ assert(rc == 0);
+
+ itimerspec nts = {
+ /*.it_value = */ {30, 0}, // s, ns
+ /*.it_interval = */ {30, 0}}; // s, ns
+
+ rc = ::timer_create(CLOCK_REALTIME, &se, &timer);
+ assert(rc == 0);
+ rc = ::timer_settime(timer, 0, &nts, 0);
+ assert(rc == 0);
+
+ DispatchHandleRef* rh1;
+ DispatchHandleRef* wh1;
+
+ struct timespec w = {0, 1000000};
+ while (!stopWait) {
+ rh1 = new DispatchHandleRef(f0, boost::bind(reader, _1, sv[0]), 0, 0);
+ wh1 = new DispatchHandleRef(f1, 0, boost::bind(writer, _1, sv[1], testString), 0);
+ rh1->startWatch(poller);
+ wh1->startWatch(poller);
+
+ ::nanosleep(&w, 0);
+
+ delete wh1;
+ delete rh1;
+ }
+
+ rc = ::timer_delete(timer);
+ assert(rc == 0);
+
+ poller->shutdown();
+ dt.join();
+ dt1.join();
+ dt2.join();
+ dt3.join();
+
+ cout << "\nWrote: " << writtenBytes << "\n";
+ cout << "Read: " << readBytes << "\n";
+
+ return 0;
+}
diff --git a/qpid/cpp/src/tests/DtxWorkRecordTest.cpp b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp
new file mode 100644
index 0000000000..bcb3fc14a1
--- /dev/null
+++ b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp
@@ -0,0 +1,193 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/DtxWorkRecord.h"
+#include "unit_test.h"
+#include <iostream>
+#include <vector>
+#include "TxMocks.h"
+
+using namespace qpid::broker;
+using boost::static_pointer_cast;
+
+namespace qpid {
+namespace tests {
+
+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();
+
+ boost::intrusive_ptr<DtxBuffer> bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ boost::intrusive_ptr<DtxBuffer> 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();
+
+ boost::intrusive_ptr<DtxBuffer> bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ boost::intrusive_ptr<DtxBuffer> bufferB(new DtxBuffer());
+ bufferB->enlist(static_pointer_cast<TxOp>(opB));
+ bufferB->markEnded();
+ boost::intrusive_ptr<DtxBuffer> 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();
+
+ boost::intrusive_ptr<DtxBuffer> bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ boost::intrusive_ptr<DtxBuffer> 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();
+
+ boost::intrusive_ptr<DtxBuffer> bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ boost::intrusive_ptr<DtxBuffer> bufferB(new DtxBuffer());
+ bufferB->enlist(static_pointer_cast<TxOp>(opB));
+ bufferB->markEnded();
+ boost::intrusive_ptr<DtxBuffer> 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();
+
+ boost::intrusive_ptr<DtxBuffer> bufferA(new DtxBuffer());
+ bufferA->enlist(static_pointer_cast<TxOp>(opA));
+ bufferA->markEnded();
+ boost::intrusive_ptr<DtxBuffer> 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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ExchangeTest.cpp b/qpid/cpp/src/tests/ExchangeTest.cpp
new file mode 100644
index 0000000000..df0684e832
--- /dev/null
+++ b/qpid/cpp/src/tests/ExchangeTest.cpp
@@ -0,0 +1,267 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 std::string;
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace qpid;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ExchangeTestSuite)
+
+QPID_AUTO_TEST_CASE(testMe)
+{
+ 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();
+
+ DeliverableMessage msg(MessageUtils::createMessage("exchange", "abc"), 0);
+ topic.route(msg);
+ direct.route(msg);
+}
+
+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, "other", &args3);//need to use different binding key to correctly identify second binding
+ 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, 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, false, FieldTable());
+ BOOST_CHECK_EQUAL(string("direct"), response.first->getType());
+}
+
+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, false, args);
+
+ DeliverableMessage msg1(MessageUtils::createMessage("e", "abc"), 0);
+ DeliverableMessage msg2(MessageUtils::createMessage("e", "abc"), 0);
+ DeliverableMessage msg3(MessageUtils::createMessage("e", "abc"), 0);
+
+ direct.route(msg1);
+ direct.route(msg2);
+ direct.route(msg3);
+
+ BOOST_CHECK_EQUAL(1, msg1.getMessage().getAnnotation("qpid.msg_sequence").asInt64());
+ BOOST_CHECK_EQUAL(2, msg2.getMessage().getAnnotation("qpid.msg_sequence").asInt64());
+ BOOST_CHECK_EQUAL(3, msg3.getMessage().getAnnotation("qpid.msg_sequence").asInt64());
+
+ FanOutExchange fanout("fanout1", false, false, args);
+ HeadersExchange header("headers1", false, false, args);
+ TopicExchange topic ("topic1", false, false, args);
+
+ // check other exchanges, that they preroute
+ DeliverableMessage msg4(MessageUtils::createMessage("e", "abc"), 0);
+ DeliverableMessage msg5(MessageUtils::createMessage("e", "abc"), 0);
+ DeliverableMessage msg6(MessageUtils::createMessage("e", "abc"), 0);
+
+ fanout.route(msg4);
+ BOOST_CHECK_EQUAL(1, msg4.getMessage().getAnnotation("qpid.msg_sequence").asInt64());
+
+ header.route(msg5);
+ BOOST_CHECK_EQUAL(1, msg5.getMessage().getAnnotation("qpid.msg_sequence").asInt64());
+
+ topic.route(msg6);
+ BOOST_CHECK_EQUAL(1, msg6.getMessage().getAnnotation("qpid.msg_sequence").asInt64());
+ direct.encode(buffer);
+ }
+ {
+
+ ExchangeRegistry exchanges;
+ buffer.reset();
+ DirectExchange::shared_ptr exch_dec = Exchange::decode(exchanges, buffer);
+
+ DeliverableMessage msg1(MessageUtils::createMessage("e", "abc"), 0);
+ exch_dec->route(msg1);
+
+ BOOST_CHECK_EQUAL(4, msg1.getMessage().getAnnotation("qpid.msg_sequence").asInt64());
+
+ }
+ delete [] buff;
+}
+
+QPID_AUTO_TEST_CASE(testIVEOption)
+{
+ FieldTable args;
+ args.setInt("qpid.ive",1);
+ DirectExchange direct("direct1", false, false, args);
+ FanOutExchange fanout("fanout1", false, false, args);
+ HeadersExchange header("headers1", false, false, args);
+ TopicExchange topic ("topic1", false, false, args);
+
+ qpid::types::Variant::Map properties;
+ properties["routing-key"] = "abc";
+ properties["a"] = "abc";
+ Message msg1 = MessageUtils::createMessage(properties, "my-message", "direct1");
+ DeliverableMessage dmsg1(msg1, 0);
+
+ FieldTable args2;
+ args2.setString("x-match", "any");
+ args2.setString("a", "abc");
+
+ direct.route(dmsg1);
+ fanout.route(dmsg1);
+ header.route(dmsg1);
+ topic.route(dmsg1);
+ 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(direct.bind(queue, "abc", 0));
+ BOOST_CHECK(fanout.bind(queue1, "abc", 0));
+ BOOST_CHECK(header.bind(queue2, "", &args2));
+ BOOST_CHECK(topic.bind(queue3, "abc", 0));
+
+ BOOST_CHECK_EQUAL(1u,queue->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue1->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue2->getMessageCount());
+ BOOST_CHECK_EQUAL(1u,queue3->getMessageCount());
+
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp
new file mode 100644
index 0000000000..c040f1d433
--- /dev/null
+++ b/qpid/cpp/src/tests/FieldTable.cpp
@@ -0,0 +1,215 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <algorithm>
+#include "qpid/framing/Array.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/List.h"
+
+#include "unit_test.h"
+
+using namespace qpid::framing;
+
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FieldTableTestSuite)
+
+QPID_AUTO_TEST_CASE(testMe)
+{
+ FieldTable ft;
+ ft.setString("A", "BCDE");
+ BOOST_CHECK(string("BCDE") == ft.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;
+
+ std::vector<char> buff(c.encodedSize());
+ Buffer wbuffer(&buff[0], c.encodedSize());
+ wbuffer.put(c);
+
+ Buffer rbuffer(&buff[0], 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)
+{
+ double d = 1.2345;
+ uint32_t u = 101;
+ char buff[1000];
+ {
+ FieldTable a;
+ FieldTable b;
+ std::vector<std::string> items;
+ items.push_back("one");
+ items.push_back("two");
+ Array c(items);
+ List list;
+ list.push_back(List::ValuePtr(new Str16Value("red")));
+ list.push_back(List::ValuePtr(new Unsigned32Value(u)));
+ list.push_back(List::ValuePtr(new Str8Value("yellow")));
+ list.push_back(List::ValuePtr(new DoubleValue(d)));
+
+ a.setString("id", "A");
+ b.setString("id", "B");
+ a.setTable("B", b);
+ a.setArray("C", c);
+ a.set("my-list", FieldTable::ValuePtr(new ListValue(list)));
+
+
+ Buffer wbuffer(buff, 100);
+ wbuffer.put(a);
+ }
+ {
+ Buffer rbuffer(buff, 100);
+ FieldTable a;
+ FieldTable b;
+ Array c;
+ rbuffer.get(a);
+ BOOST_CHECK(string("A") == a.getAsString("id"));
+ a.getTable("B", b);
+ BOOST_CHECK(string("B") == b.getAsString("id"));
+ a.getArray("C", c);
+ std::vector<std::string> items;
+ std::transform(c.begin(), c.end(), std::back_inserter(items), Array::get<std::string, Array::ValuePtr>);
+ BOOST_CHECK((uint) 2 == items.size());
+ BOOST_CHECK(string("one") == items[0]);
+ BOOST_CHECK(string("two") == items[1]);
+
+ List list;
+ BOOST_CHECK(a.get("my-list")->get<List>(list));
+ List::const_iterator i = list.begin();
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("red"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(u, (uint32_t) (*i)->get<int>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(std::string("yellow"), (*i)->get<std::string>());
+
+ i++;
+ BOOST_CHECK(i != list.end());
+ BOOST_CHECK_EQUAL(d, (*i)->get<double>());
+
+ i++;
+ BOOST_CHECK(i == list.end());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testFloatAndDouble)
+{
+ char buff[100];
+ float f = 5.672f;
+ double d = 56.720001;
+ {
+ FieldTable a;
+ a.setString("string", "abc");
+ a.setInt("int", 5672);
+ a.setFloat("float", f);
+ a.setDouble("double", d);
+
+ Buffer wbuffer(buff, 100);
+ wbuffer.put(a);
+ }
+ {
+ Buffer rbuffer(buff, 100);
+ FieldTable a;
+ rbuffer.get(a);
+ BOOST_CHECK_EQUAL(string("abc"), a.getAsString("string"));
+ BOOST_CHECK_EQUAL(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_EQUAL(f2, f);
+
+ double d2;
+ BOOST_CHECK(!a.getDouble("string", d2));
+ BOOST_CHECK(!a.getDouble("int", d2));
+ BOOST_CHECK(a.getDouble("double", d2));
+ BOOST_CHECK_EQUAL(d2, d);
+ }
+}
+
+QPID_AUTO_TEST_CASE(test64GetAndSetConverts)
+{
+ FieldTable args;
+ args.setInt64("a",100);
+ args.setInt64("b",-(int64_t) ((int64_t) 1<<34));
+
+ args.setUInt64("c",1u);
+ args.setUInt64("d",(uint64_t) ((uint64_t) 1<<34));
+ BOOST_CHECK_EQUAL(1u, args.getAsUInt64("c"));
+ BOOST_CHECK_EQUAL(100u, args.getAsUInt64("a"));
+ BOOST_CHECK_EQUAL(1, args.getAsInt64("c"));
+ BOOST_CHECK_EQUAL(100, args.getAsInt64("a"));
+ BOOST_CHECK_EQUAL(-(int64_t) ((int64_t) 1<<34), args.getAsInt64("b"));
+ BOOST_CHECK_EQUAL((uint64_t) ((uint64_t) 1<<34), args.getAsUInt64("d"));
+ BOOST_CHECK_EQUAL((int64_t) ((int64_t) 1<<34), args.getAsInt64("d"));
+
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FieldValue.cpp b/qpid/cpp/src/tests/FieldValue.cpp
new file mode 100644
index 0000000000..9cea48e3cf
--- /dev/null
+++ b/qpid/cpp/src/tests/FieldValue.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 "qpid/framing/FieldValue.h"
+
+#include "unit_test.h"
+#include <boost/test/floating_point_comparison.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FieldValueTestSuite)
+
+using namespace qpid::framing;
+
+Str16Value s("abc");
+IntegerValue i(42);
+FloatValue f((float)42.42);
+DoubleValue df(123.123);
+
+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);
+
+}
+
+QPID_AUTO_TEST_CASE(testIntegerValueEquals)
+{
+ BOOST_CHECK(i.get<int>() == 42);
+ 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<float>() == true);
+ BOOST_CHECK(i.convertsTo<int>() == true);
+ BOOST_CHECK_THROW(i.get<std::string>(), InvalidConversionException);
+ BOOST_CHECK_EQUAL(i.get<float>(), 42.0);
+}
+
+QPID_AUTO_TEST_CASE(testFloatValueEquals)
+{
+ BOOST_CHECK(f.convertsTo<float>() == true);
+ BOOST_CHECK(FloatValue((float)42.42) == f);
+ BOOST_CHECK_CLOSE(double(f.get<float>()), 42.42, 0.001);
+ // Check twice, regression test for QPID-6470 where the value was corrupted during get.
+ BOOST_CHECK(FloatValue((float)42.42) == f);
+ BOOST_CHECK_CLOSE(f.get<double>(), 42.42, 0.001);
+
+ // Float to double conversion
+ BOOST_CHECK(f.convertsTo<double>() == true);
+ BOOST_CHECK_CLOSE(f.get<double>(), 42.42, 0.001);
+
+ // Double value
+ BOOST_CHECK(f.convertsTo<float>() == true);
+ BOOST_CHECK(f.convertsTo<double>() == true);
+ BOOST_CHECK_CLOSE(double(df.get<float>()), 123.123, 0.001);
+ BOOST_CHECK_CLOSE(df.get<double>(), 123.123, 0.001);
+
+ // Invalid conversions should fail.
+ BOOST_CHECK(!f.convertsTo<std::string>());
+ BOOST_CHECK(!f.convertsTo<int>());
+ BOOST_CHECK_THROW(f.get<std::string>(), InvalidConversionException);
+ BOOST_CHECK_THROW(f.get<int>(), InvalidConversionException);
+
+ // getFloatingPointValue: check twice, regression test for QPID-6470
+ BOOST_CHECK_CLOSE((double(f.getFloatingPointValue<float,sizeof(float)>())), 42.42, 0.001);
+ BOOST_CHECK_CLOSE((double(f.getFloatingPointValue<float,sizeof(float)>())), 42.42, 0.001);
+ BOOST_CHECK_CLOSE((df.getFloatingPointValue<double,sizeof(double)>()), 123.123, 0.001);
+ // getFloatingPointValue should *not* convert float/double, require exact type.
+ BOOST_CHECK_THROW((f.getFloatingPointValue<double,sizeof(double)>()), InvalidConversionException);
+ BOOST_CHECK_THROW((double(df.getFloatingPointValue<float,sizeof(float)>())), InvalidConversionException);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Frame.cpp b/qpid/cpp/src/tests/Frame.cpp
new file mode 100644
index 0000000000..cfcfde04a7
--- /dev/null
+++ b/qpid/cpp/src/tests/Frame.cpp
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/framing/Frame.h"
+
+#include <boost/lexical_cast.hpp>
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FrameTestSuite)
+
+using namespace std;
+using namespace qpid::framing;
+
+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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FrameDecoder.cpp b/qpid/cpp/src/tests/FrameDecoder.cpp
new file mode 100644
index 0000000000..9eeff2a41e
--- /dev/null
+++ b/qpid/cpp/src/tests/FrameDecoder.cpp
@@ -0,0 +1,78 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "unit_test.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FrameDecoder.h"
+#include "qpid/framing/AMQContentBody.h"
+#include "qpid/framing/Buffer.h"
+#include <string>
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(FrameDecoderTest)
+
+using namespace std;
+using namespace qpid::framing;
+
+
+string makeData(int size) {
+ string data;
+ data.resize(size);
+ for (int i =0; i < size; ++i)
+ data[i] = 'a' + (i%26);
+ return data;
+}
+string encodeFrame(string data) {
+ AMQFrame f((AMQContentBody(data)));
+ string encoded;
+ encoded.resize(f.encodedSize());
+ Buffer b(&encoded[0], encoded.size());
+ f.encode(b);
+ return encoded;
+}
+
+string getData(const AMQFrame& frame) {
+ const AMQContentBody* content = dynamic_cast<const AMQContentBody*>(frame.getBody());
+ BOOST_CHECK(content);
+ return content->getData();
+}
+
+QPID_AUTO_TEST_CASE(testByteFragments) {
+ string data = makeData(42);
+ string encoded = encodeFrame(data);
+ FrameDecoder decoder;
+ for (size_t i = 0; i < encoded.size()-1; ++i) {
+ Buffer buf(&encoded[i], 1);
+ BOOST_CHECK(!decoder.decode(buf));
+ }
+ Buffer buf(&encoded[encoded.size()-1], 1);
+ BOOST_CHECK(decoder.decode(buf));
+ BOOST_CHECK_EQUAL(data, getData(decoder.getFrame()));
+}
+
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/FramingTest.cpp b/qpid/cpp/src/tests/FramingTest.cpp
new file mode 100644
index 0000000000..2392b6fec4
--- /dev/null
+++ b/qpid/cpp/src/tests/FramingTest.cpp
@@ -0,0 +1,168 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "qpid/framing/FieldValue.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;
+
+namespace qpid {
+namespace tests {
+
+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((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((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_CASE(badStrings) {
+ char data[(65535 + 2) + (255 + 1)];
+ Buffer b(data, sizeof(data));
+ BOOST_CHECK_THROW(b.putShortString(std::string(256, 'X')),
+ Exception);
+ BOOST_CHECK_THROW(b.putMediumString(std::string(65536, 'X')),
+ Exception);
+ b.putShortString(std::string(255, 'X'));
+ b.putMediumString(std::string(65535, 'X'));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/HeaderTest.cpp b/qpid/cpp/src/tests/HeaderTest.cpp
new file mode 100644
index 0000000000..4b16f3c793
--- /dev/null
+++ b/qpid/cpp/src/tests/HeaderTest.cpp
@@ -0,0 +1,114 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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;
+
+namespace qpid {
+namespace tests {
+
+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((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((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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/HeadersExchangeTest.cpp b/qpid/cpp/src/tests/HeadersExchangeTest.cpp
new file mode 100644
index 0000000000..3e68b84bc3
--- /dev/null
+++ b/qpid/cpp/src/tests/HeadersExchangeTest.cpp
@@ -0,0 +1,194 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/broker/Message.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include "MessageUtils.h"
+#include "unit_test.h"
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+using namespace qpid::types;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(HeadersExchangeTestSuite)
+
+QPID_AUTO_TEST_CASE(testMatchAll)
+{
+ FieldTable b;
+ b.setString("x-match", "all");
+ b.setString("foo", "FOO");
+ b.setInt("n", 42);
+
+ Variant::Map m;
+ const int32_t int_n(42);
+ m["foo"] = "FOO";
+ m["n"] = int_n;
+ BOOST_CHECK(HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+
+ // Ignore extras.
+ m["extra"] = "x";
+ BOOST_CHECK(HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+
+ // Fail mismatch, wrong value.
+ m["foo"] = "NotFoo";
+ BOOST_CHECK(!HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+
+ // Fail mismatch, missing value
+ Variant::Map n;
+ n["n"] = int_n;
+ n["extra"] = "x";
+ BOOST_CHECK(!HeadersExchange::match(b, MessageUtils::createMessage(n, "", "", true)));
+}
+
+QPID_AUTO_TEST_CASE(testMatchAny)
+{
+ FieldTable b;
+ b.setString("x-match", "any");
+ b.setString("foo", "FOO");
+ b.setInt("n", 42);
+
+ Variant::Map n;
+ Variant::Map m;
+ m["foo"] = "FOO";
+ BOOST_CHECK(!HeadersExchange::match(b, MessageUtils::createMessage(n, "", "", true)));
+ BOOST_CHECK(HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+ const int32_t int_n(42);
+ m["n"] = int_n;
+ BOOST_CHECK(HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+}
+
+QPID_AUTO_TEST_CASE(testMatchEmptyValue)
+{
+ FieldTable b;
+ b.setString("x-match", "all");
+ b.set("foo", FieldTable::ValuePtr());
+ b.set("n", FieldTable::ValuePtr());
+ Variant::Map m;
+ BOOST_CHECK(!HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+}
+
+QPID_AUTO_TEST_CASE(testMatchEmptyArgs)
+{
+ FieldTable b;
+ Variant::Map m;
+ m["foo"] = "FOO";
+ Message msg = MessageUtils::createMessage(m, "", "", true);
+
+ b.setString("x-match", "all");
+ BOOST_CHECK(HeadersExchange::match(b, msg));
+ b.setString("x-match", "any");
+ BOOST_CHECK(!HeadersExchange::match(b, msg));
+}
+
+
+QPID_AUTO_TEST_CASE(testMatchNoXMatch)
+{
+ FieldTable b;
+ b.setString("foo", "FOO");
+ Variant::Map m;
+ m["foo"] = "FOO";
+ BOOST_CHECK(!HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+}
+
+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_CASE(testMatchSizedIntUint)
+{
+ typedef std::list<Variant::Map> vml;
+
+ const int8_t i8(1);
+ const int16_t i16(1);
+ const int32_t i32(1);
+ const int64_t i64(1);
+ const uint8_t u8(1);
+ const uint16_t u16(1);
+ const uint32_t u32(1);
+ const uint64_t u64(1);
+
+ Variant::Map mi8, mi16, mi32, mi64;
+ Variant::Map mu8, mu16, mu32, mu64;
+
+ mi8["bk"] = i8;
+ mi16["bk"] = i16;
+ mi32["bk"] = i32;
+ mi64["bk"] = i64;
+ mu8["bk"] = u8;
+ mu16["bk"] = u16;
+ mu32["bk"] = u32;
+ mu64["bk"] = u64;
+
+ vml mMap;
+ mMap.push_back(mi8);
+ mMap.push_back(mi16);
+ mMap.push_back(mi32);
+ mMap.push_back(mi64);
+ mMap.push_back(mu8);
+ mMap.push_back(mu16);
+ mMap.push_back(mu32);
+ mMap.push_back(mu64);
+
+ for (vml::iterator bVal=mMap.begin(); bVal!=mMap.end(); ++bVal) {
+ FieldTable b;
+ qpid::amqp_0_10::translate(*bVal, b);
+ b.setString("x-match", "all");
+ for (vml::iterator mVal=mMap.begin(); mVal!=mMap.end(); ++mVal) {
+ BOOST_CHECK(HeadersExchange::match(b, MessageUtils::createMessage(*mVal, "", "", true)));
+ }
+ }
+}
+
+// TODO: Headers exchange match on single
+
+QPID_AUTO_TEST_CASE(testMatchFloatDouble)
+{
+ const double iFloat(1.0);
+ Variant::Map m;
+ m["bk"] = iFloat;
+
+ FieldTable b;
+ qpid::amqp_0_10::translate(m, b);
+ b.setString("x-match", "all");
+ BOOST_CHECK(HeadersExchange::match(b, MessageUtils::createMessage(m, "", "", true)));
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/InlineAllocator.cpp b/qpid/cpp/src/tests/InlineAllocator.cpp
new file mode 100644
index 0000000000..a4c4d64cea
--- /dev/null
+++ b/qpid/cpp/src/tests/InlineAllocator.cpp
@@ -0,0 +1,68 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/InlineAllocator.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/InlineVector.cpp b/qpid/cpp/src/tests/InlineVector.cpp
new file mode 100644
index 0000000000..ba5165886d
--- /dev/null
+++ b/qpid/cpp/src/tests/InlineVector.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/InlineVector.h"
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(InlineVectorTestSuite)
+
+using namespace qpid;
+using namespace std;
+
+typedef InlineVector<int, 3> Vec;
+
+bool isInline(const Vec& v) {
+ // If nothing, give it the benefit of the doubt;
+ // can't take address of nothing.
+ if (v.size() <= 0)
+ return true;
+ return (const char*)&v <= (const char*)(&v[0]) &&
+ (const char*)(&v[0]) < (const char*)&v+sizeof(v);
+}
+
+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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ManagementTest.cpp b/qpid/cpp/src/tests/ManagementTest.cpp
new file mode 100644
index 0000000000..98ef591fae
--- /dev/null
+++ b/qpid/cpp/src/tests/ManagementTest.cpp
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ManagementTestSuite)
+
+using namespace qpid::framing;
+using namespace qpid::management;
+
+QPID_AUTO_TEST_CASE(testObjectIdSerializeStream) {
+ std::string text("0-10-4-2500-80000000000()");
+ std::stringstream input(text);
+
+ ObjectId oid(input);
+
+ std::stringstream output;
+ output << oid;
+
+ BOOST_CHECK_EQUAL(text, output.str());
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdSerializeString) {
+ std::string text("0-10-4-2500-80000000000()");
+
+ ObjectId oid(text);
+
+ std::stringstream output;
+ output << oid;
+
+ BOOST_CHECK_EQUAL(text, output.str());
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdEncode) {
+ qpid::types::Variant::Map oidMap;
+
+ ObjectId oid(1, 2, 3, 9999);
+ oid.setV2Key("testkey");
+ oid.setAgentName("myAgent");
+
+ std::stringstream out1;
+ out1 << oid;
+
+ BOOST_CHECK_EQUAL(out1.str(), "1-2-3-myAgent-9999(testkey)");
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdAttach) {
+ AgentAttachment agent;
+ ObjectId oid(&agent, 10, 20);
+ oid.setV2Key("GabbaGabbaHey");
+ oid.setAgentName("MrSmith");
+
+ std::stringstream out1;
+ out1 << oid;
+
+ BOOST_CHECK_EQUAL(out1.str(), "10-20-0-MrSmith-0(GabbaGabbaHey)");
+
+ agent.setBanks(30, 40);
+ std::stringstream out2;
+ out2 << oid;
+
+ BOOST_CHECK_EQUAL(out2.str(), "10-20-30-MrSmith-0(GabbaGabbaHey)");
+}
+
+QPID_AUTO_TEST_CASE(testObjectIdCreate) {
+ ObjectId oid("some-agent-name", "an-object-name");
+
+ BOOST_CHECK_EQUAL(oid.getAgentName(), "some-agent-name");
+ BOOST_CHECK_EQUAL(oid.getV2Key(), "an-object-name");
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessageReplayTracker.cpp b/qpid/cpp/src/tests/MessageReplayTracker.cpp
new file mode 100644
index 0000000000..c0778247f0
--- /dev/null
+++ b/qpid/cpp/src/tests/MessageReplayTracker.cpp
@@ -0,0 +1,104 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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"
+
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessageReplayTrackerTests)
+
+using namespace qpid::client;
+using namespace qpid::sys;
+using std::string;
+
+class ReplayBufferChecker
+{
+ public:
+
+ ReplayBufferChecker(uint from, uint to) : end(to), i(from) {}
+
+ void operator()(const Message& m)
+ {
+ if (i > end) BOOST_FAIL("Extra message found: " + m.getData());
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i++)).str(), m.getData());
+ }
+ private:
+ const uint end;
+ uint i;
+
+};
+
+QPID_AUTO_TEST_CASE(testReplay)
+{
+ SessionFixture 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)
+{
+ SessionFixture fix;
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+
+ MessageReplayTracker tracker(10);
+ tracker.init(fix.session);
+ for (uint i = 0; i < 5; i++) {
+ Message message((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
+ tracker.send(message);
+ }
+ fix.session.sync();//ensures all messages are complete
+ tracker.checkCompletion();
+ tracker.replay(fix.session);
+ Message received;
+ for (uint i = 0; i < 5; i++) {
+ BOOST_CHECK(fix.subs.get(received, "my-queue"));
+ BOOST_CHECK_EQUAL((boost::format("Message_%1%") % (i+1)).str(), received.getData());
+ }
+ BOOST_CHECK(!fix.subs.get(received, "my-queue"));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessageTest.cpp b/qpid/cpp/src/tests/MessageTest.cpp
new file mode 100644
index 0000000000..a6c5157b47
--- /dev/null
+++ b/qpid/cpp/src/tests/MessageTest.cpp
@@ -0,0 +1,89 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/Protocol.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 "MessageUtils.h"
+
+#include "unit_test.h"
+
+#include <iostream>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessageTestSuite)
+
+QPID_AUTO_TEST_CASE(testEncodeDecode)
+{
+ string exchange = "MyExchange";
+ string routingKey = "MyRoutingKey";
+ uint64_t ttl(60);
+ Uuid messageId(true);
+ string data("abcdefghijklmn");
+
+ qpid::types::Variant::Map properties;
+ properties["routing-key"] = routingKey;
+ properties["ttl"] = ttl;
+ properties["durable"] = true;
+ properties["message-id"] = qpid::types::Uuid(messageId.data());
+ properties["abc"] = "xyz";
+ Message msg = MessageUtils::createMessage(properties, data);
+
+ std::vector<char> bytes(msg.getPersistentContext()->encodedSize());
+ qpid::framing::Buffer buffer(&bytes[0], bytes.size());
+ msg.getPersistentContext()->encode(buffer);
+ buffer.reset();
+ ProtocolRegistry registry(std::set<std::string>(), 0);
+ msg = registry.decode(buffer);
+
+ BOOST_CHECK_EQUAL(routingKey, msg.getRoutingKey());
+ BOOST_CHECK_EQUAL((uint64_t) data.size(), msg.getContent().size());
+ BOOST_CHECK_EQUAL(data, msg.getContent());
+ //BOOST_CHECK_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId());
+ BOOST_CHECK_EQUAL(string("xyz"), msg.getPropertyAsString("abc"));
+ BOOST_CHECK(msg.isPersistent());
+}
+
+QPID_AUTO_TEST_CASE(testMessageProperties)
+{
+ string data("abcdefghijklmn");
+
+ qpid::types::Variant::Map properties;
+ properties["abc"] = "xyz";
+ Message msg = MessageUtils::createMessage(properties, data);
+
+ // Regression test that looking up a property doesn't return a prefix
+ BOOST_CHECK_EQUAL(msg.getProperty("abcdef").getType(), qpid::types::VAR_VOID);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h
new file mode 100644
index 0000000000..f05b0d8b20
--- /dev/null
+++ b/qpid/cpp/src/tests/MessageUtils.h
@@ -0,0 +1,117 @@
+#ifndef TESTS_MESSAGEUTILS_H
+#define TESTS_MESSAGEUTILS_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/Message.h"
+#include "qpid/broker/amqp_0_10/MessageTransfer.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/types/Variant.h"
+#include "qpid/amqp_0_10/Codecs.h"
+
+using namespace qpid;
+using namespace broker;
+using namespace framing;
+
+namespace qpid {
+namespace tests {
+
+struct MessageUtils
+{
+ static Message createMessage(const qpid::types::Variant::Map& properties,
+ const std::string& content="",
+ const std::string& destination = "",
+ bool replaceHeaders = false
+ )
+ {
+ boost::intrusive_ptr<broker::amqp_0_10::MessageTransfer> msg(new broker::amqp_0_10::MessageTransfer());
+
+ AMQFrame method(( MessageTransferBody(ProtocolVersion(), destination, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ if (content.size()) {
+ msg->getFrames().getHeaders()->get<MessageProperties>(true)->setContentLength(content.size());
+ AMQFrame data((AMQContentBody(content)));
+ msg->getFrames().append(data);
+ }
+ if (!replaceHeaders) {
+ for (qpid::types::Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ if (i->first == "routing-key" && !i->second.isVoid()) {
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(i->second);
+ } else if (i->first == "message-id" && !i->second.isVoid()) {
+ qpid::types::Uuid id = i->second;
+ qpid::framing::Uuid id2(id.data());
+ msg->getFrames().getHeaders()->get<MessageProperties>(true)->setMessageId(id2);
+ } else if (i->first == "ttl" && !i->second.isVoid()) {
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setTtl(i->second);
+ } else if (i->first == "priority" && !i->second.isVoid()) {
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setPriority(i->second);
+ } else if (i->first == "durable" && !i->second.isVoid()) {
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(i->second.asBool() ? 2 : 1);
+ } else {
+ msg->getFrames().getHeaders()->get<MessageProperties>(true)->getApplicationHeaders().setString(i->first, i->second);
+ }
+ }
+ } else {
+ framing::FieldTable newHeaders;
+ qpid::amqp_0_10::translate(properties, newHeaders);
+ msg->getFrames().getHeaders()->get<MessageProperties>(true)->getApplicationHeaders() = newHeaders;
+ }
+ return Message(msg, msg);
+ }
+
+
+ static Message createMessage(const std::string& exchange="", const std::string& routingKey="",
+ uint64_t ttl = 0, bool durable = false, const Uuid& messageId=Uuid(true),
+ const std::string& content="")
+ {
+ boost::intrusive_ptr<broker::amqp_0_10::MessageTransfer> msg(new broker::amqp_0_10::MessageTransfer());
+
+ AMQFrame method(( MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ props->setContentLength(content.size());
+ props->setMessageId(messageId);
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ if (durable)
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(2);
+ if (ttl)
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setTtl(ttl);
+ if (content.size()) {
+ AMQFrame data((AMQContentBody(content)));
+ msg->getFrames().append(data);
+ }
+ if (ttl) msg->computeExpiration();
+ return Message(msg, msg);
+ }
+};
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_MESSAGEUTILS_H*/
diff --git a/qpid/cpp/src/tests/MessagingFixture.h b/qpid/cpp/src/tests/MessagingFixture.h
new file mode 100644
index 0000000000..165aefeeec
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingFixture.h
@@ -0,0 +1,352 @@
+#ifndef TESTS_MESSAGINGFIXTURE_H
+#define TESTS_MESSAGINGFIXTURE_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 "BrokerFixture.h"
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/types/Variant.h"
+
+#include <boost/format.hpp>
+
+namespace qpid {
+namespace tests {
+
+using qpid::types::Variant;
+
+struct BrokerAdmin
+{
+ qpid::client::Connection connection;
+ qpid::client::Session session;
+
+ BrokerAdmin(uint16_t port)
+ {
+ connection.open("localhost", port);
+ session = connection.newSession();
+ }
+
+ void createQueue(const std::string& name)
+ {
+ session.queueDeclare(qpid::client::arg::queue=name);
+ }
+
+ void deleteQueue(const std::string& name)
+ {
+ session.queueDelete(qpid::client::arg::queue=name);
+ }
+
+ void createExchange(const std::string& name, const std::string& type)
+ {
+ session.exchangeDeclare(qpid::client::arg::exchange=name, qpid::client::arg::type=type);
+ }
+
+ void deleteExchange(const std::string& name)
+ {
+ session.exchangeDelete(qpid::client::arg::exchange=name);
+ }
+
+ bool checkQueueExists(const std::string& name)
+ {
+ return session.queueQuery(name).getQueue() == name;
+ }
+
+ bool checkExchangeExists(const std::string& name, std::string& type)
+ {
+ qpid::framing::ExchangeQueryResult result = session.exchangeQuery(name);
+ type = result.getType();
+ return !result.getNotFound();
+ }
+
+ void send(qpid::client::Message& message, const std::string& exchange=std::string())
+ {
+ session.messageTransfer(qpid::client::arg::destination=exchange, qpid::client::arg::content=message);
+ }
+
+ ~BrokerAdmin()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct MessagingFixture : public BrokerFixture
+{
+ messaging::Connection connection;
+ messaging::Session session;
+ BrokerAdmin admin;
+
+ MessagingFixture(const BrokerOptions& opts = BrokerOptions(), bool mgmtEnabled=false) :
+ BrokerFixture(opts, mgmtEnabled),
+ connection(open(broker->getPort(Broker::TCP_TRANSPORT))),
+ session(connection.createSession()),
+ admin(broker->getPort(Broker::TCP_TRANSPORT))
+ {
+ }
+
+ static messaging::Connection open(uint16_t port)
+ {
+ messaging::Connection connection(
+ (boost::format("amqp:tcp:localhost:%1%") % (port)).str());
+ connection.open();
+ return connection;
+
+ }
+
+ /** Open a connection to the broker. */
+ qpid::messaging::Connection newConnection()
+ {
+ qpid::messaging::Connection connection(
+ (boost::format("amqp:tcp:localhost:%1%") % (broker->getPort(qpid::broker::Broker::TCP_TRANSPORT))).str());
+ return connection;
+ }
+
+ void ping(const qpid::messaging::Address& address)
+ {
+ messaging::Receiver r = session.createReceiver(address);
+ messaging::Sender s = session.createSender(address);
+ messaging::Message out(framing::Uuid(true).str());
+ s.send(out);
+ messaging::Message in;
+ BOOST_CHECK(r.fetch(in, 5*messaging::Duration::SECOND));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ r.close();
+ s.close();
+ }
+
+ ~MessagingFixture()
+ {
+ session.close();
+ connection.close();
+ }
+};
+
+struct QueueFixture : MessagingFixture
+{
+ std::string queue;
+
+ QueueFixture(const std::string& name = "test-queue") : queue(name)
+ {
+ admin.createQueue(queue);
+ }
+
+ ~QueueFixture()
+ {
+ admin.deleteQueue(queue);
+ }
+
+};
+
+struct TopicFixture : MessagingFixture
+{
+ std::string topic;
+
+ TopicFixture(const std::string& name = "test-topic", const std::string& type="fanout") : topic(name)
+ {
+ admin.createExchange(topic, type);
+ }
+
+ ~TopicFixture()
+ {
+ admin.deleteExchange(topic);
+ }
+
+};
+
+struct MultiQueueFixture : MessagingFixture
+{
+ typedef std::vector<std::string>::const_iterator const_iterator;
+ std::vector<std::string> queues;
+
+ MultiQueueFixture(const std::vector<std::string>& names = boost::assign::list_of<std::string>("q1")("q2")("q3")) : queues(names)
+ {
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.createQueue(*i);
+ }
+ }
+
+ ~MultiQueueFixture()
+ {
+ connection.close();
+ for (const_iterator i = queues.begin(); i != queues.end(); ++i) {
+ admin.deleteQueue(*i);
+ }
+ }
+
+};
+
+inline std::vector<std::string> fetch(messaging::Receiver& receiver, int count, messaging::Duration timeout=messaging::Duration::SECOND*5)
+{
+ std::vector<std::string> data;
+ messaging::Message message;
+ for (int i = 0; i < count && receiver.fetch(message, timeout); i++) {
+ data.push_back(message.getContent());
+ }
+ return data;
+}
+
+
+inline void send(messaging::Sender& sender, uint count = 1, uint start = 1,
+ const std::string& base = "Message")
+{
+ for (uint i = start; i < start + count; ++i) {
+ sender.send(messaging::Message((boost::format("%1%_%2%") % base % i).str()));
+ }
+}
+
+inline void receive(messaging::Receiver& receiver, uint count = 1, uint start = 1,
+ const std::string& base = "Message",
+ messaging::Duration timeout=messaging::Duration::SECOND*5)
+{
+ for (uint i = start; i < start + count; ++i) {
+ BOOST_CHECK_EQUAL(receiver.fetch(timeout).getContent(), (boost::format("%1%_%2%") % base % i).str());
+ }
+}
+
+
+class MethodInvoker
+{
+ public:
+ MethodInvoker(messaging::Session session) :
+ replyTo("#; {create:always, node:{x-declare:{auto-delete:true}}}"),
+ sender(session.createSender("qmf.default.direct/broker")),
+ receiver(session.createReceiver(replyTo)) {}
+
+ void createExchange(const std::string& name, const std::string& type, bool durable=false)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="exchange";
+ params["properties"] = Variant::Map();
+ params["properties"].asMap()["exchange-type"] = type;
+ params["properties"].asMap()["durable"] = durable;
+ methodRequest("create", params);
+ }
+
+ void deleteExchange(const std::string& name)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="exchange";
+ methodRequest("delete", params);
+ }
+
+ void createQueue(const std::string& name, bool durable=false, bool autodelete=false,
+ const Variant::Map& options=Variant::Map())
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="queue";
+ params["properties"] = options;
+ params["properties"].asMap()["durable"] = durable;
+ params["properties"].asMap()["auto-delete"] = autodelete;
+ methodRequest("create", params);
+ }
+
+ void deleteQueue(const std::string& name)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="queue";
+ methodRequest("delete", params);
+ }
+
+ void bind(const std::string& exchange, const std::string& queue, const std::string& key,
+ const Variant::Map& options=Variant::Map())
+ {
+ Variant::Map params;
+ params["name"]=(boost::format("%1%/%2%/%3%") % (exchange) % (queue) % (key)).str();
+ params["type"]="binding";
+ params["properties"] = options;
+ methodRequest("create", params);
+ }
+
+ void unbind(const std::string& exchange, const std::string& queue, const std::string& key)
+ {
+ Variant::Map params;
+ params["name"]=(boost::format("%1%/%2%/%3%") % (exchange) % (queue) % (key)).str();
+ params["type"]="binding";
+ methodRequest("delete", params);
+ }
+
+ void methodRequest(
+ const std::string& method,
+ const Variant::Map& inParams, Variant::Map* outParams = 0,
+ const std::string& objectName="org.apache.qpid.broker:broker:amqp-broker")
+ {
+ Variant::Map content;
+ Variant::Map objectId;
+ objectId["_object_name"] = objectName;;
+ content["_object_id"] = objectId;
+ content["_method_name"] = method;
+ content["_arguments"] = inParams;
+
+ messaging::Message request;
+ request.setReplyTo(replyTo);
+ request.getProperties()["x-amqp-0-10.app-id"] = "qmf2";
+ request.getProperties()["qmf.opcode"] = "_method_request";
+ encode(content, request);
+
+ sender.send(request);
+
+ messaging::Message response;
+ if (receiver.fetch(response, messaging::Duration::SECOND*5)) {
+ if (response.getProperties()["x-amqp-0-10.app-id"] == "qmf2") {
+ std::string opcode = response.getProperties()["qmf.opcode"];
+ if (opcode == "_method_response") {
+ if (outParams) {
+ Variant::Map m;
+ decode(response, m);
+ *outParams = m["_arguments"].asMap();
+ }
+ } else if (opcode == "_exception") {
+ Variant::Map m;
+ decode(response, m);
+ throw Exception(QPID_MSG("Error: " << m["_values"]));
+ } else {
+ throw Exception(QPID_MSG("Invalid response received, unexpected opcode: " << opcode));
+ }
+ } else {
+ throw Exception(QPID_MSG("Invalid response received, not a qmfv2 message: app-id="
+ << response.getProperties()["x-amqp-0-10.app-id"]));
+ }
+ } else {
+ throw Exception(QPID_MSG("No response received"));
+ }
+ }
+ private:
+ messaging::Address replyTo;
+ messaging::Sender sender;
+ messaging::Receiver receiver;
+};
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_MESSAGINGFIXTURE_H*/
diff --git a/qpid/cpp/src/tests/MessagingLogger.cpp b/qpid/cpp/src/tests/MessagingLogger.cpp
new file mode 100644
index 0000000000..195a33db12
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingLogger.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.
+ *
+ */
+
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/exceptions.h"
+#include "qpid/messaging/Logger.h"
+
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+
+#include <vector>
+
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessagingLoggerSuite)
+
+class StringLogger : public qpid::messaging::LoggerOutput {
+ std::string& outString;
+
+ void log(qpid::messaging::Level /*level*/, bool user, const char* /*file*/, int /*line*/, const char* /*function*/, const std::string& message){
+ if (user) outString += "User ";
+ outString += message;
+ }
+
+public:
+ StringLogger(std::string& os) :
+ outString(os)
+ {}
+};
+
+#define SETUP_LOGGING(logger, ...) \
+do {\
+ const char* args[]={"", __VA_ARGS__, 0};\
+ qpid::messaging::Logger::configure((sizeof (args)/sizeof (char*))-1, args);\
+ logOutput.clear();\
+ qpid::messaging::Logger::setOutput(logger);\
+} while (0)
+#define LOG_LEVEL(level)\
+ QPID_LOG(level, #level " level output")
+#define LOG_ALL_LOGGING_LEVELS \
+do { \
+ LOG_LEVEL(trace); \
+ LOG_LEVEL(debug); \
+ LOG_LEVEL(info); \
+ LOG_LEVEL(notice); \
+ LOG_LEVEL(warning); \
+ LOG_LEVEL(critical); \
+} while (0)
+#define LOG_USER_LEVEL(level)\
+ qpid::messaging::Logger::log(qpid::messaging::level, __FILE__, __LINE__, __FUNCTION__, #level " message")
+#define LOG_ALL_USER_LOGGING_LEVELS \
+do { \
+ LOG_USER_LEVEL(trace); \
+ LOG_USER_LEVEL(debug); \
+ LOG_USER_LEVEL(info); \
+ LOG_USER_LEVEL(notice); \
+ LOG_USER_LEVEL(warning); \
+ LOG_USER_LEVEL(critical); \
+} while (0)
+
+std::string logOutput;
+
+QPID_AUTO_TEST_CASE(testLoggerLevels)
+{
+ StringLogger logger(logOutput);
+
+ SETUP_LOGGING(logger, "--log-enable", "debug");
+ LOG_ALL_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "debug level output\ncritical level output\n");
+
+ SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice");
+ LOG_ALL_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "trace level output\ndebug level output\ninfo level output\nwarning level output\ncritical level output\n");
+
+ SETUP_LOGGING(logger, "--log-enable", "info-");
+ LOG_ALL_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "trace level output\ndebug level output\ninfo level output\ncritical level output\n");
+
+ SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice+");
+ LOG_ALL_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "trace level output\ndebug level output\ninfo level output\ncritical level output\n");
+}
+
+QPID_AUTO_TEST_CASE(testUserLoggerLevels)
+{
+ StringLogger logger(logOutput);
+
+ SETUP_LOGGING(logger, "--log-enable", "debug");
+ LOG_ALL_USER_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "User debug message\nUser critical message\n");
+
+ SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice");
+ LOG_ALL_USER_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "User trace message\nUser debug message\nUser info message\nUser warning message\nUser critical message\n");
+
+ SETUP_LOGGING(logger, "--log-enable", "info-");
+ LOG_ALL_USER_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "User trace message\nUser debug message\nUser info message\nUser critical message\n");
+
+ SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice+");
+ LOG_ALL_USER_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "User trace message\nUser debug message\nUser info message\nUser critical message\n");
+
+ SETUP_LOGGING(logger, "--log-disable", "trace+");
+ LOG_ALL_LOGGING_LEVELS;
+ LOG_ALL_USER_LOGGING_LEVELS;
+ BOOST_CHECK_EQUAL(logOutput, "critical level output\nUser critical message\n");
+}
+
+QPID_AUTO_TEST_CASE(testLoggerUsage)
+{
+ qpid::messaging::Logger::configure(0, 0, "blah");
+ std::string u = qpid::messaging::Logger::usage();
+
+ BOOST_CHECK(!u.empty());
+ BOOST_CHECK( u.find("--blah-log-enable")!=u.npos );
+}
+
+QPID_AUTO_TEST_CASE(testLoggerException)
+{
+ const char* args[]={"", "--blah-log-enable", "illegal", 0};
+ BOOST_CHECK_THROW(qpid::messaging::Logger::configure(3, args, "blah"), qpid::messaging::MessagingException);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+}}
diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp
new file mode 100644
index 0000000000..d01dd69999
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp
@@ -0,0 +1,1495 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "MessagingFixture.h"
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Session.h"
+#include "qpid/framing/ExchangeQueryResult.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Time.h"
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <string>
+#include <vector>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(MessagingSessionTests)
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace qpid;
+using qpid::broker::BrokerOptions;
+using qpid::framing::Uuid;
+
+
+QPID_AUTO_TEST_CASE(testSimpleSendReceive)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ out.setSubject("test-subject");
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::SECOND * 5);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(in.getSubject(), out.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testSyncSendReceive)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ sender.send(out, true);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::IMMEDIATE);
+ fix.session.acknowledge(true);
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+}
+
+QPID_AUTO_TEST_CASE(testSendReceiveHeaders)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ for (uint i = 0; i < 10; ++i) {
+ out.getProperties()["a"] = i;
+ out.setProperty("b", i + 100);
+ sender.send(out);
+ }
+ uint8_t v1(255u);
+ int8_t v2(-120);
+ out.getProperties()["c"] = v1;
+ out.getProperties()["d"] = v2;
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in;
+ for (uint i = 0; i < 10; ++i) {
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND * 5));
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(in.getProperties()["a"].asUint32(), i);
+ BOOST_CHECK_EQUAL(in.getProperties()["b"].asUint32(), i + 100);
+ fix.session.acknowledge();
+ }
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND * 5));
+ Variant& c = in.getProperties()["c"];
+ BOOST_CHECK_EQUAL(c.getType(), VAR_UINT8);
+ BOOST_CHECK_EQUAL(c.asUint8(), v1);
+ Variant& d = in.getProperties()["d"];
+ BOOST_CHECK_EQUAL(d.getType(), VAR_INT8);
+ BOOST_CHECK_EQUAL(d.asInt8(), v2);
+}
+
+QPID_AUTO_TEST_CASE(testSenderError)
+{
+ MessagingFixture fix;
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress"), qpid::messaging::NotFound);
+ fix.session = fix.connection.createSession();
+ BOOST_CHECK_THROW(fix.session.createSender("NonExistentAddress; {create:receiver}"),
+ qpid::messaging::NotFound);
+}
+
+QPID_AUTO_TEST_CASE(testReceiverError)
+{
+ MessagingFixture fix;
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress"), qpid::messaging::NotFound);
+ fix.session = fix.connection.createSession();
+ BOOST_CHECK_THROW(fix.session.createReceiver("NonExistentAddress; {create:sender}"),
+ qpid::messaging::NotFound);
+}
+
+QPID_AUTO_TEST_CASE(testSimpleTopic)
+{
+ TopicFixture fix;
+
+ Sender sender = fix.session.createSender(fix.topic);
+ Message msg("one");
+ sender.send(msg);
+ Receiver sub1 = fix.session.createReceiver(fix.topic);
+ sub1.setCapacity(10u);
+ msg.setContent("two");
+ sender.send(msg);
+ Receiver sub2 = fix.session.createReceiver(fix.topic);
+ sub2.setCapacity(10u);
+ msg.setContent("three");
+ sender.send(msg);
+ Receiver sub3 = fix.session.createReceiver(fix.topic);
+ sub3.setCapacity(10u);
+ msg.setContent("four");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub2, 2), boost::assign::list_of<std::string>("three")("four"));
+ sub2.close();
+
+ msg.setContent("five");
+ sender.send(msg);
+ BOOST_CHECK_EQUAL(fetch(sub1, 4), boost::assign::list_of<std::string>("two")("three")("four")("five"));
+ BOOST_CHECK_EQUAL(fetch(sub3, 2), boost::assign::list_of<std::string>("four")("five"));
+ Message in;
+ BOOST_CHECK(!sub2.fetch(in, Duration::IMMEDIATE));//TODO: or should this raise an error?
+
+
+ //TODO: check pending messages...
+}
+
+QPID_AUTO_TEST_CASE(testNextReceiver)
+{
+ MultiQueueFixture fix;
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Receiver r = fix.session.createReceiver(fix.queues[i]);
+ r.setCapacity(10u);
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Sender s = fix.session.createSender(fix.queues[i]);
+ Message msg((boost::format("Message_%1%") % (i+1)).str());
+ s.send(msg);
+ }
+
+ for (uint i = 0; i < fix.queues.size(); i++) {
+ Message msg;
+ BOOST_CHECK(fix.session.nextReceiver().fetch(msg, Duration::SECOND));
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testMapMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::Map content;
+ content["abc"] = "def";
+ content["pi"] = 3.14f;
+ Variant utf8("A utf 8 string");
+ utf8.setEncoding("utf8");
+ content["utf8"] = utf8;
+ Variant utf16("\x00\x61\x00\x62\x00\x63");
+ utf16.setEncoding("utf16");
+ content["utf16"] = utf16;
+ encode(content, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::Map view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view["abc"].asString(), "def");
+ BOOST_CHECK_EQUAL(view["pi"].asFloat(), 3.14f);
+ BOOST_CHECK_EQUAL(view["utf8"].asString(), utf8.asString());
+ BOOST_CHECK_EQUAL(view["utf8"].getEncoding(), utf8.getEncoding());
+ BOOST_CHECK_EQUAL(view["utf16"].asString(), utf16.asString());
+ BOOST_CHECK_EQUAL(view["utf16"].getEncoding(), utf16.getEncoding());
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testMapMessageWithInitial)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::Map imap;
+ imap["abc"] = "def";
+ imap["pi"] = 3.14f;
+ encode(imap, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::Map view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view["abc"].asString(), "def");
+ BOOST_CHECK_EQUAL(view["pi"].asFloat(), 3.14f);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testListMessage)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::List content;
+ content.push_back(Variant("abc"));
+ content.push_back(Variant(1234));
+ content.push_back(Variant("def"));
+ content.push_back(Variant(56.789));
+ encode(content, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::List view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view.size(), content.size());
+ BOOST_CHECK_EQUAL(view.front().asString(), "abc");
+ BOOST_CHECK_EQUAL(view.back().asDouble(), 56.789);
+
+ Variant::List::const_iterator i = view.begin();
+ BOOST_CHECK(i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "abc");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asInt64(), 1234);
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "def");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asDouble(), 56.789);
+ BOOST_CHECK(++i == view.end());
+
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testListMessageWithInitial)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out;
+ Variant::List ilist;
+ ilist.push_back(Variant("abc"));
+ ilist.push_back(Variant(1234));
+ ilist.push_back(Variant("def"));
+ ilist.push_back(Variant(56.789));
+ encode(ilist, out);
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ Variant::List view;
+ decode(in, view);
+ BOOST_CHECK_EQUAL(view.size(), ilist.size());
+ BOOST_CHECK_EQUAL(view.front().asString(), "abc");
+ BOOST_CHECK_EQUAL(view.back().asDouble(), 56.789);
+
+ Variant::List::const_iterator i = view.begin();
+ BOOST_CHECK(i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "abc");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asInt64(), 1234);
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asString(), "def");
+ BOOST_CHECK(++i != view.end());
+ BOOST_CHECK_EQUAL(i->asDouble(), 56.789);
+ BOOST_CHECK(++i == view.end());
+
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testReject)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message m1("reject-me");
+ sender.send(m1);
+ Message m2("accept-me");
+ sender.send(m2);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(5 * Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), m1.getContent());
+ fix.session.reject(in);
+ in = receiver.fetch(5 * Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), m2.getContent());
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testAvailable)
+{
+ MultiQueueFixture fix;
+
+ Receiver r1 = fix.session.createReceiver(fix.queues[0]);
+ r1.setCapacity(100);
+
+ Receiver r2 = fix.session.createReceiver(fix.queues[1]);
+ r2.setCapacity(100);
+
+ Sender s1 = fix.session.createSender(fix.queues[0]);
+ Sender s2 = fix.session.createSender(fix.queues[1]);
+
+ for (uint i = 0; i < 10; ++i) {
+ s1.send(Message((boost::format("A_%1%") % (i+1)).str()));
+ }
+ for (uint i = 0; i < 5; ++i) {
+ s2.send(Message((boost::format("B_%1%") % (i+1)).str()));
+ }
+ qpid::sys::sleep(1);//is there any avoid an arbitrary sleep while waiting for messages to be dispatched?
+ for (uint i = 0; i < 5; ++i) {
+ BOOST_CHECK_EQUAL(fix.session.getReceivable(), 15u - 2*i);
+ BOOST_CHECK_EQUAL(r1.getAvailable(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str());
+ BOOST_CHECK_EQUAL(r2.getAvailable(), 5u - i);
+ BOOST_CHECK_EQUAL(r2.fetch().getContent(), (boost::format("B_%1%") % (i+1)).str());
+ fix.session.acknowledge();
+ }
+ for (uint i = 5; i < 10; ++i) {
+ BOOST_CHECK_EQUAL(fix.session.getReceivable(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.getAvailable(), 10u - i);
+ BOOST_CHECK_EQUAL(r1.fetch().getContent(), (boost::format("A_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testUnsettledAcks)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ for (uint i = 0; i < 10; ++i) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ for (uint i = 0; i < 10; ++i) {
+ BOOST_CHECK_EQUAL(receiver.fetch().getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+ BOOST_CHECK_EQUAL(fix.session.getUnsettledAcks(), 0u);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(fix.session.getUnsettledAcks(), 10u);
+ fix.session.sync();
+ BOOST_CHECK_EQUAL(fix.session.getUnsettledAcks(), 0u);
+}
+
+QPID_AUTO_TEST_CASE(testUnsettledSend)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ send(sender, 10);
+ //Note: this test relies on 'inside knowledge' of the sender
+ //implementation and the fact that the simple test case makes it
+ //possible to predict when completion information will be sent to
+ //the client. TODO: is there a better way of testing this?
+ BOOST_CHECK_EQUAL(sender.getUnsettled(), 10u);
+ fix.session.sync();
+ BOOST_CHECK_EQUAL(sender.getUnsettled(), 0u);
+
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ receive(receiver, 10);
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testBrowse)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ send(sender, 10);
+ Receiver browser1 = fix.session.createReceiver(fix.queue + "; {mode:browse}");
+ receive(browser1, 10);
+ Receiver browser2 = fix.session.createReceiver(fix.queue + "; {mode:browse}");
+ receive(browser2, 10);
+ Receiver releaser1 = fix.session.createReceiver(fix.queue);
+ Message m1 = releaser1.fetch(messaging::Duration::SECOND*5);
+ BOOST_CHECK(!m1.getRedelivered());
+ fix.session.release(m1);
+ Receiver releaser2 = fix.session.createReceiver(fix.queue);
+ Message m2 = releaser2.fetch(messaging::Duration::SECOND*5);
+ BOOST_CHECK(m2.getRedelivered());
+ fix.session.release(m2);
+ Receiver consumer = fix.session.createReceiver(fix.queue);
+ receive(consumer, 10);
+ fix.session.acknowledge();
+}
+
+struct QueueCreatePolicyFixture : public MessagingFixture
+{
+ qpid::messaging::Address address;
+
+ QueueCreatePolicyFixture(const std::string& a) : address(a) {}
+
+ void test()
+ {
+ ping(address);
+ BOOST_CHECK(admin.checkQueueExists(address.getName()));
+ }
+
+ ~QueueCreatePolicyFixture()
+ {
+ admin.deleteQueue(address.getName());
+ }
+};
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueAlways)
+{
+ QueueCreatePolicyFixture fix("#; {create:always, node:{type:queue}}");
+ fix.test();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueReceiver)
+{
+ QueueCreatePolicyFixture fix("#; {create:receiver, node:{type:queue}}");
+ Receiver r = fix.session.createReceiver(fix.address);
+ fix.test();
+ r.close();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyQueueSender)
+{
+ QueueCreatePolicyFixture fix("#; {create:sender, node:{type:queue}}");
+ Sender s = fix.session.createSender(fix.address);
+ fix.test();
+ s.close();
+}
+
+struct ExchangeCreatePolicyFixture : public MessagingFixture
+{
+ qpid::messaging::Address address;
+ const std::string exchangeType;
+
+ ExchangeCreatePolicyFixture(const std::string& a, const std::string& t) :
+ address(a), exchangeType(t) {}
+
+ void test()
+ {
+ ping(address);
+ std::string actualType;
+ BOOST_CHECK(admin.checkExchangeExists(address.getName(), actualType));
+ BOOST_CHECK_EQUAL(exchangeType, actualType);
+ }
+
+ ~ExchangeCreatePolicyFixture()
+ {
+ admin.deleteExchange(address.getName());
+ }
+};
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopic)
+{
+ ExchangeCreatePolicyFixture fix("#; {create:always, node:{type:topic}}",
+ "topic");
+ fix.test();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopicReceiverFanout)
+{
+ ExchangeCreatePolicyFixture fix("#/my-subject; {create:receiver, node:{type:topic, x-declare:{type:fanout}}}", "fanout");
+ Receiver r = fix.session.createReceiver(fix.address);
+ fix.test();
+ r.close();
+}
+
+QPID_AUTO_TEST_CASE(testCreatePolicyTopicSenderDirect)
+{
+ ExchangeCreatePolicyFixture fix("#/my-subject; {create:sender, node:{type:topic, x-declare:{type:direct}}}", "direct");
+ Sender s = fix.session.createSender(fix.address);
+ fix.test();
+ s.close();
+}
+
+struct DeletePolicyFixture : public MessagingFixture
+{
+ enum Mode {RECEIVER, SENDER, ALWAYS, NEVER};
+
+ std::string getPolicy(Mode mode)
+ {
+ switch (mode) {
+ case SENDER:
+ return "{delete:sender}";
+ case RECEIVER:
+ return "{delete:receiver}";
+ case ALWAYS:
+ return "{delete:always}";
+ case NEVER:
+ return "{delete:never}";
+ }
+ return "";
+ }
+
+ void testAll()
+ {
+ test(RECEIVER);
+ test(SENDER);
+ test(ALWAYS);
+ test(NEVER);
+ }
+
+ virtual ~DeletePolicyFixture() {}
+ virtual void create(const qpid::messaging::Address&) = 0;
+ virtual void destroy(const qpid::messaging::Address&) = 0;
+ virtual bool exists(const qpid::messaging::Address&) = 0;
+
+ void test(Mode mode)
+ {
+ qpid::messaging::Address address("testqueue#; " + getPolicy(mode));
+ create(address);
+
+ Sender s = session.createSender(address);
+ Receiver r = session.createReceiver(address);
+ switch (mode) {
+ case RECEIVER:
+ s.close();
+ BOOST_CHECK(exists(address));
+ r.close();
+ BOOST_CHECK(!exists(address));
+ break;
+ case SENDER:
+ r.close();
+ BOOST_CHECK(exists(address));
+ s.close();
+ BOOST_CHECK(!exists(address));
+ break;
+ case ALWAYS:
+ s.close();
+ BOOST_CHECK(!exists(address));
+ break;
+ case NEVER:
+ r.close();
+ BOOST_CHECK(exists(address));
+ s.close();
+ BOOST_CHECK(exists(address));
+ destroy(address);
+ }
+ }
+};
+
+struct QueueDeletePolicyFixture : DeletePolicyFixture
+{
+ void create(const qpid::messaging::Address& address)
+ {
+ admin.createQueue(address.getName());
+ }
+ void destroy(const qpid::messaging::Address& address)
+ {
+ admin.deleteQueue(address.getName());
+ }
+ bool exists(const qpid::messaging::Address& address)
+ {
+ return admin.checkQueueExists(address.getName());
+ }
+};
+
+struct ExchangeDeletePolicyFixture : DeletePolicyFixture
+{
+ const std::string exchangeType;
+ ExchangeDeletePolicyFixture(const std::string type = "topic") : exchangeType(type) {}
+
+ void create(const qpid::messaging::Address& address)
+ {
+ admin.createExchange(address.getName(), exchangeType);
+ }
+ void destroy(const qpid::messaging::Address& address)
+ {
+ admin.deleteExchange(address.getName());
+ }
+ bool exists(const qpid::messaging::Address& address)
+ {
+ std::string actualType;
+ return admin.checkExchangeExists(address.getName(), actualType) && actualType == exchangeType;
+ }
+};
+
+QPID_AUTO_TEST_CASE(testDeletePolicyQueue)
+{
+ QueueDeletePolicyFixture fix;
+ fix.testAll();
+}
+
+QPID_AUTO_TEST_CASE(testDeletePolicyExchange)
+{
+ ExchangeDeletePolicyFixture fix;
+ fix.testAll();
+}
+
+QPID_AUTO_TEST_CASE(testAssertPolicyQueue)
+{
+ MessagingFixture fix;
+ std::string a1 = "q; {create:always, assert:always, node:{type:queue, durable:false, x-declare:{arguments:{qpid.max-count:100}}}}";
+ Sender s1 = fix.session.createSender(a1);
+ s1.close();
+ Receiver r1 = fix.session.createReceiver(a1);
+ r1.close();
+
+ std::string a2 = "q; {assert:receiver, node:{durable:true, x-declare:{arguments:{qpid.max-count:100}}}}";
+ Sender s2 = fix.session.createSender(a2);
+ s2.close();
+ BOOST_CHECK_THROW(fix.session.createReceiver(a2), qpid::messaging::AssertionFailed);
+
+ std::string a3 = "q; {assert:sender, node:{x-declare:{arguments:{qpid.max-count:99}}}}";
+ BOOST_CHECK_THROW(fix.session.createSender(a3), qpid::messaging::AssertionFailed);
+ Receiver r3 = fix.session.createReceiver(a3);
+ r3.close();
+
+ fix.admin.deleteQueue("q");
+}
+
+QPID_AUTO_TEST_CASE(testAssertExchangeOption)
+{
+ MessagingFixture fix;
+ std::string a1 = "e; {create:always, assert:always, node:{type:topic, x-declare:{type:direct, arguments:{qpid.msg_sequence:True}}}}";
+ Sender s1 = fix.session.createSender(a1);
+ s1.close();
+ Receiver r1 = fix.session.createReceiver(a1);
+ r1.close();
+
+ std::string a2 = "e; {assert:receiver, node:{type:topic, x-declare:{type:fanout, arguments:{qpid.msg_sequence:True}}}}";
+ Sender s2 = fix.session.createSender(a2);
+ s2.close();
+ BOOST_CHECK_THROW(fix.session.createReceiver(a2), qpid::messaging::AssertionFailed);
+
+ std::string a3 = "e; {assert:sender, node:{x-declare:{arguments:{qpid.msg_sequence:False}}}}";
+ BOOST_CHECK_THROW(fix.session.createSender(a3), qpid::messaging::AssertionFailed);
+ Receiver r3 = fix.session.createReceiver(a3);
+ r3.close();
+
+ fix.admin.deleteExchange("e");
+}
+
+QPID_AUTO_TEST_CASE(testGetSender)
+{
+ QueueFixture fix;
+ std::string name = fix.session.createSender(fix.queue).getName();
+ Sender sender = fix.session.getSender(name);
+ BOOST_CHECK_EQUAL(name, sender.getName());
+ Message out(Uuid(true).str());
+ sender.send(out);
+ Message in;
+ BOOST_CHECK(fix.session.createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.session.getSender("UnknownSender"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetReceiver)
+{
+ QueueFixture fix;
+ std::string name = fix.session.createReceiver(fix.queue).getName();
+ Receiver receiver = fix.session.getReceiver(name);
+ BOOST_CHECK_EQUAL(name, receiver.getName());
+ Message out(Uuid(true).str());
+ fix.session.createSender(fix.queue).send(out);
+ Message in;
+ BOOST_CHECK(receiver.fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.session.getReceiver("UnknownReceiver"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetSessionFromConnection)
+{
+ QueueFixture fix;
+ fix.connection.createSession("my-session");
+ Session session = fix.connection.getSession("my-session");
+ Message out(Uuid(true).str());
+ session.createSender(fix.queue).send(out);
+ Message in;
+ BOOST_CHECK(session.createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK_THROW(fix.connection.getSession("UnknownSession"), qpid::messaging::KeyError);
+}
+
+QPID_AUTO_TEST_CASE(testGetConnectionFromSession)
+{
+ QueueFixture fix;
+ Message out(Uuid(true).str());
+ Sender sender = fix.session.createSender(fix.queue);
+ sender.send(out);
+ Message in;
+ sender.getSession().getConnection().createSession("incoming");
+ BOOST_CHECK(fix.connection.getSession("incoming").createReceiver(fix.queue).fetch(in));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+}
+
+QPID_AUTO_TEST_CASE(testTx)
+{
+ QueueFixture fix;
+ Session ssn1 = fix.connection.createTransactionalSession();
+ Session ssn2 = fix.connection.createTransactionalSession();
+ Sender sender1 = ssn1.createSender(fix.queue);
+ Sender sender2 = ssn2.createSender(fix.queue);
+ Receiver receiver1 = ssn1.createReceiver(fix.queue);
+ Receiver receiver2 = ssn2.createReceiver(fix.queue);
+ Message in;
+
+ send(sender1, 5, 1, "A");
+ send(sender2, 5, 1, "B");
+ ssn2.commit();
+ receive(receiver1, 5, 1, "B");//(only those from sender2 should be received)
+ BOOST_CHECK(!receiver1.fetch(in, Duration::IMMEDIATE));//check there are no more messages
+ ssn1.rollback();
+ receive(receiver2, 5, 1, "B");
+ BOOST_CHECK(!receiver2.fetch(in, Duration::IMMEDIATE));//check there are no more messages
+ ssn2.rollback();
+ receive(receiver1, 5, 1, "B");
+ BOOST_CHECK(!receiver1.fetch(in, Duration::IMMEDIATE));//check there are no more messages
+ ssn1.commit();
+ //check neither receiver gets any more messages:
+ BOOST_CHECK(!receiver1.fetch(in, Duration::IMMEDIATE));
+ BOOST_CHECK(!receiver2.fetch(in, Duration::IMMEDIATE));
+}
+
+QPID_AUTO_TEST_CASE(testRelease)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ sender.send(out, true);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message m1 = receiver.fetch(Duration::IMMEDIATE);
+ fix.session.release(m1);
+ Message m2 = receiver.fetch(Duration::SECOND * 1);
+ BOOST_CHECK_EQUAL(m1.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(m1.getContent(), m2.getContent());
+ BOOST_CHECK(m2.getRedelivered());
+ fix.session.acknowledge(true);
+}
+
+QPID_AUTO_TEST_CASE(testOptionVerification)
+{
+ MessagingFixture fix;
+ fix.session.createReceiver("my-queue; {create: always, assert: always, delete: always, node: {type: queue, durable: false, x-declare: {arguments: {a: b}}, x-bindings: [{exchange: amq.fanout}]}, link: {name: abc, durable: false, reliability: exactly-once, x-subscribe: {arguments:{a:b}}, x-bindings:[{exchange: amq.fanout}]}, mode: browse}");
+ BOOST_CHECK_THROW(fix.session.createReceiver("my-queue; {invalid-option:blah}"), qpid::messaging::AddressError);
+}
+
+QPID_AUTO_TEST_CASE(testReceiveSpecialProperties)
+{
+ QueueFixture fix;
+
+ qpid::client::Message out;
+ out.getDeliveryProperties().setRoutingKey(fix.queue);
+ out.getMessageProperties().setAppId("my-app-id");
+ out.getMessageProperties().setMessageId(qpid::framing::Uuid(true));
+ out.getMessageProperties().setContentEncoding("my-content-encoding");
+ fix.admin.send(out);
+
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::SECOND * 5);
+ BOOST_CHECK_EQUAL(in.getProperties()["x-amqp-0-10.routing-key"].asString(), out.getDeliveryProperties().getRoutingKey());
+ BOOST_CHECK_EQUAL(in.getProperties()["x-amqp-0-10.app-id"].asString(), out.getMessageProperties().getAppId());
+ BOOST_CHECK_EQUAL(in.getProperties()["x-amqp-0-10.content-encoding"].asString(), out.getMessageProperties().getContentEncoding());
+ BOOST_CHECK_EQUAL(in.getMessageId(), out.getMessageProperties().getMessageId().str());
+ fix.session.acknowledge(true);
+}
+
+QPID_AUTO_TEST_CASE(testSendSpecialProperties)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test-message");
+ std::string appId = "my-app-id";
+ std::string contentEncoding = "my-content-encoding";
+ out.getProperties()["x-amqp-0-10.app-id"] = appId;
+ out.getProperties()["x-amqp-0-10.content-encoding"] = contentEncoding;
+ out.setMessageId(qpid::framing::Uuid(true).str());
+ sender.send(out, true);
+
+ qpid::client::LocalQueue q;
+ qpid::client::SubscriptionManager subs(fix.admin.session);
+ qpid::client::Subscription s = subs.subscribe(q, fix.queue);
+ qpid::client::Message in = q.get();
+ s.cancel();
+ fix.admin.session.sync();
+
+ BOOST_CHECK_EQUAL(in.getMessageProperties().getAppId(), appId);
+ BOOST_CHECK_EQUAL(in.getMessageProperties().getContentEncoding(), contentEncoding);
+ BOOST_CHECK_EQUAL(in.getMessageProperties().getMessageId().str(), out.getMessageId());
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveSubscriber)
+{
+ QueueFixture fix;
+ std::string address = (boost::format("%1%; { link: { x-subscribe : { exclusive:true } } }") % fix.queue).str();
+ Receiver receiver = fix.session.createReceiver(address);
+ ScopedSuppressLogging sl;
+ try {
+ fix.session.createReceiver(address);
+ fix.session.sync();
+ BOOST_FAIL("Expected exception.");
+ } catch (const MessagingException& /*e*/) {}
+}
+
+
+QPID_AUTO_TEST_CASE(testExclusiveQueueSubscriberAndBrowser)
+{
+ MessagingFixture fix;
+
+ std::string address = "exclusive-queue; { create: receiver, node : { x-declare : { auto-delete: true, exclusive: true } } }";
+ std::string browseAddress = "exclusive-queue; { mode: browse }";
+
+ Receiver receiver = fix.session.createReceiver(address);
+ fix.session.sync();
+
+ Connection c2 = fix.newConnection();
+ c2.open();
+ Session s2 = c2.createSession();
+
+ BOOST_CHECK_NO_THROW(Receiver browser = s2.createReceiver(browseAddress));
+ c2.close();
+}
+
+
+QPID_AUTO_TEST_CASE(testDeleteQueueWithUnackedMessages)
+{
+ MessagingFixture fix;
+ const uint capacity = 5;
+
+ Sender sender = fix.session.createSender("test.ex;{create:always,node:{type:topic}}");
+ Receiver receiver2 = fix.session.createReceiver("alternate.ex;{create:always,node:{type:topic}}");
+ Receiver receiver1 = fix.session.createReceiver("test.q;{create:always, delete:always,node:{type:queue, x-declare:{alternate-exchange:alternate.ex}},link:{x-bindings:[{exchange:test.ex,queue:test.q,key:#}]}}");
+
+ receiver1.setCapacity(capacity);
+ receiver2.setCapacity(capacity*2);
+
+ Message out("test-message");
+ for (uint i = 0; i < capacity*2; ++i) {
+ sender.send(out);
+ }
+
+ receiver1.close();
+
+ // Make sure all pending messages were sent to the alternate
+ // exchange when the queue was deleted.
+ Message in;
+ for (uint i = 0; i < capacity*2; ++i) {
+ in = receiver2.fetch(Duration::SECOND * 5);
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testAuthenticatedUsername)
+{
+ MessagingFixture fix;
+ Connection connection = fix.newConnection();
+ connection.setOption("sasl-mechanism", "PLAIN");
+ connection.setOption("username", "test-user");
+ connection.setOption("password", "ignored");
+ connection.open();
+ BOOST_CHECK_EQUAL(connection.getAuthenticatedUsername(), std::string("test-user"));
+}
+
+QPID_AUTO_TEST_CASE(testExceptionOnClosedConnection)
+{
+ MessagingFixture fix;
+ fix.connection.close();
+ BOOST_CHECK_THROW(fix.connection.createSession(), MessagingException);
+ Connection connection("blah");
+ BOOST_CHECK_THROW(connection.createSession(), MessagingException);
+}
+
+QPID_AUTO_TEST_CASE(testAcknowledge)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ const uint count(20);
+ for (uint i = 0; i < count; ++i) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+
+ Session other = fix.connection.createSession();
+ Receiver receiver = other.createReceiver(fix.queue);
+ std::vector<Message> messages;
+ for (uint i = 0; i < count; ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ messages.push_back(msg);
+ }
+ const uint batch(10); //acknowledge first 10 messages only
+ for (uint i = 0; i < batch; ++i) {
+ other.acknowledge(messages[i]);
+ }
+ messages.clear();
+ other.sync();
+ other.close();
+
+ other = fix.connection.createSession();
+ receiver = other.createReceiver(fix.queue);
+ for (uint i = 0; i < (count-batch); ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1+batch)).str());
+ if (i % 2) other.acknowledge(msg); //acknowledge every other message
+ }
+ other.sync();
+ other.close();
+
+ //check unacknowledged messages are still enqueued
+ other = fix.connection.createSession();
+ receiver = other.createReceiver(fix.queue);
+ for (uint i = 0; i < ((count-batch)/2); ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % ((i*2)+1+batch)).str());
+ }
+ other.acknowledge();//acknowledge all messages
+ other.sync();
+ other.close();
+
+ Message m;
+ //check queue is empty
+ BOOST_CHECK(!fix.session.createReceiver(fix.queue).fetch(m, Duration::IMMEDIATE));
+}
+
+QPID_AUTO_TEST_CASE(testQmfCreateAndDelete)
+{
+ MessagingFixture fix(BrokerOptions(), true/*enable management*/);
+ MethodInvoker control(fix.session);
+ control.createQueue("my-queue");
+ control.createExchange("my-exchange", "topic");
+ control.bind("my-exchange", "my-queue", "subject1");
+
+ Sender sender = fix.session.createSender("my-exchange");
+ Receiver receiver = fix.session.createReceiver("my-queue");
+ Message out;
+ out.setSubject("subject1");
+ out.setContent("one");
+ sender.send(out);
+ Message in;
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND*5));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ control.unbind("my-exchange", "my-queue", "subject1");
+ control.bind("my-exchange", "my-queue", "subject2");
+
+ out.setContent("two");
+ sender.send(out);//should be dropped
+
+ out.setSubject("subject2");
+ out.setContent("three");
+ sender.send(out);//should not be dropped
+
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND*5));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK(!receiver.fetch(in, Duration::IMMEDIATE));
+ sender.close();
+ receiver.close();
+
+ control.deleteExchange("my-exchange");
+ messaging::Session other = fix.connection.createSession();
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(other.createSender("my-exchange"), qpid::messaging::NotFound);
+ }
+ control.deleteQueue("my-queue");
+ other = fix.connection.createSession();
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(other.createReceiver("my-queue"), qpid::messaging::NotFound);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRejectAndCredit)
+{
+ //Ensure credit is restored on completing rejected messages
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+
+ const uint count(10);
+ receiver.setCapacity(count);
+ for (uint i = 0; i < count; i++) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+
+ Message in;
+ for (uint i = 0; i < count; ++i) {
+ if (receiver.fetch(in, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(in.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ fix.session.reject(in);
+ } else {
+ BOOST_FAIL((boost::format("Message_%1% not received as expected") % (i+1)).str());
+ break;
+ }
+ }
+ //send another batch of messages
+ for (uint i = 0; i < count; i++) {
+ sender.send(Message((boost::format("Message_%1%") % (i+count)).str()));
+ }
+
+ for (uint i = 0; i < count; ++i) {
+ if (receiver.fetch(in, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(in.getContent(), (boost::format("Message_%1%") % (i+count)).str());
+ } else {
+ BOOST_FAIL((boost::format("Message_%1% not received as expected") % (i+count)).str());
+ break;
+ }
+ }
+ fix.session.acknowledge();
+ receiver.close();
+ sender.close();
+}
+
+QPID_AUTO_TEST_CASE(testTtlForever)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("I want to live forever!");
+ out.setTtl(Duration::FOREVER);
+ sender.send(out, true);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::IMMEDIATE);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK(in.getTtl() == Duration::FOREVER);
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveTopicSubscriber)
+{
+ TopicFixture fix;
+ std::string address = (boost::format("%1%; { link: { name: 'my-subscription', x-declare: { auto-delete: true, exclusive: true }}}") % fix.topic).str();
+ Sender sender = fix.session.createSender(fix.topic);
+ Receiver receiver1 = fix.session.createReceiver(address);
+ {
+ ScopedSuppressLogging sl;
+ try {
+ fix.session.createReceiver(address);
+ fix.session.sync();
+ BOOST_FAIL("Expected exception.");
+ } catch (const MessagingException& /*e*/) {}
+ }
+}
+
+QPID_AUTO_TEST_CASE(testNonExclusiveSubscriber)
+{
+ TopicFixture fix;
+ std::string address = (boost::format("%1%; {node:{type:topic}, link:{name:'my-subscription', x-declare:{auto-delete:true, exclusive:false}}}") % fix.topic).str();
+ Receiver receiver1 = fix.session.createReceiver(address);
+ Receiver receiver2 = fix.session.createReceiver(address);
+ Sender sender = fix.session.createSender(fix.topic);
+ sender.send(Message("one"), true);
+ Message in = receiver1.fetch(Duration::IMMEDIATE);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("one"));
+ sender.send(Message("two"), true);
+ in = receiver2.fetch(Duration::IMMEDIATE);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("two"));
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testAcknowledgeUpTo)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ const uint count(20);
+ for (uint i = 0; i < count; ++i) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+
+ Session other = fix.connection.createSession();
+ Receiver receiver = other.createReceiver(fix.queue);
+ std::vector<Message> messages;
+ for (uint i = 0; i < count; ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ messages.push_back(msg);
+ }
+ const uint batch = 10;
+ other.acknowledgeUpTo(messages[batch-1]);//acknowledge first 10 messages only
+
+ messages.clear();
+ other.sync();
+ other.close();
+
+ other = fix.connection.createSession();
+ receiver = other.createReceiver(fix.queue);
+ Message msg;
+ for (uint i = 0; i < (count-batch); ++i) {
+ msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1+batch)).str());
+ }
+ other.acknowledgeUpTo(msg);
+ other.sync();
+ other.close();
+
+ Message m;
+ //check queue is empty
+ BOOST_CHECK(!fix.session.createReceiver(fix.queue).fetch(m, Duration::IMMEDIATE));
+}
+
+QPID_AUTO_TEST_CASE(testCreateBindingsOnStandardExchange)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender((boost::format("amq.direct; {create:always, node:{type:topic, x-bindings:[{queue:%1%, key:my-subject}]}}") % fix.queue).str());
+ Message out("test-message");
+ out.setSubject("my-subject");
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::SECOND * 5);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(in.getSubject(), out.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testUnsubscribeOnClose)
+{
+ MessagingFixture fix;
+ Sender sender = fix.session.createSender("my-exchange/my-subject; {create: always, delete:sender, node:{type:topic, x-declare:{alternate-exchange:amq.fanout}}}");
+ Receiver receiver = fix.session.createReceiver("my-exchange/my-subject");
+ Receiver deadletters = fix.session.createReceiver("amq.fanout");
+
+ sender.send(Message("first"));
+ Message in = receiver.fetch(Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("first"));
+ fix.session.acknowledge();
+ receiver.close();
+ sender.send(Message("second"));
+ in = deadletters.fetch(Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("second"));
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testHeadersExchange)
+{
+ MessagingFixture fix;
+ //use both quoted and unquoted values
+ Receiver receiver = fix.session.createReceiver("amq.match; {link:{x-bindings:[{arguments:{x-match:all,qpid.subject:'abc',my-property:abc}}]}}");
+ Sender sender = fix.session.createSender("amq.match");
+ Message out("test-message");
+ out.setSubject("abc");
+ Variant& property = out.getProperties()["my-property"];
+ property = "abc";
+ property.setEncoding("utf8");
+ sender.send(out, true);
+ Message in;
+ if (receiver.fetch(in, Duration::SECOND)) {
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ } else {
+ BOOST_FAIL("Message did not match as expected!");
+ }
+}
+
+QPID_AUTO_TEST_CASE(testLargeRoutingKey)
+{
+ MessagingFixture fix;
+ std::string address = "amq.direct/" + std::string(300, 'x');//routing/binding key can be at most 225 chars in 0-10
+ BOOST_CHECK_THROW(fix.session.createReceiver(address), qpid::messaging::MessagingException);
+}
+
+QPID_AUTO_TEST_CASE(testAlternateExchangeInLinkDeclare)
+{
+ MessagingFixture fix;
+ Sender s = fix.session.createSender("amq.direct/key");
+ Receiver r1 = fix.session.createReceiver("amq.direct/key;{link:{x-declare:{alternate-exchange:'amq.fanout'}}}");
+ Receiver r2 = fix.session.createReceiver("amq.fanout");
+
+ for (uint i = 0; i < 10; ++i) {
+ s.send(Message((boost::format("Message_%1%") % (i+1)).str()), true);
+ }
+ r1.close();//orphans all messages in subscription queue, which should then be routed through alternate exchange
+ for (uint i = 0; i < 10; ++i) {
+ Message received;
+ BOOST_CHECK(r2.fetch(received, Duration::SECOND));
+ BOOST_CHECK_EQUAL(received.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testBrowseOnly)
+{
+ /* Set up a queue browse-only, and try to receive
+ the same messages twice with two different receivers.
+ This works because the browse-only queue does not
+ allow message acquisition. */
+
+ QueueFixture fix;
+ std::string addr = "q; {create:always, node:{type:queue, durable:false, x-declare:{arguments:{qpid.browse-only:1}}}}";
+ Sender sender = fix.session.createSender(addr);
+ Message out("test-message");
+
+ int count = 10;
+ for ( int i = 0; i < count; ++ i ) {
+ sender.send(out);
+ }
+
+ Message m;
+
+ Receiver receiver_1 = fix.session.createReceiver(addr);
+ for ( int i = 0; i < count; ++ i ) {
+ BOOST_CHECK(receiver_1.fetch(m, Duration::SECOND));
+ }
+
+ Receiver receiver_2 = fix.session.createReceiver(addr);
+ for ( int i = 0; i < count; ++ i ) {
+ BOOST_CHECK(receiver_2.fetch(m, Duration::SECOND));
+ }
+
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testLinkBindingCleanup)
+{
+ MessagingFixture fix;
+
+ Sender sender = fix.session.createSender("test.ex;{create:always,node:{type:topic}}");
+
+ Connection connection = fix.newConnection();
+ connection.open();
+
+ Session session(connection.createSession());
+ Receiver receiver1 = session.createReceiver("test.q;{create:always, node:{type:queue, x-bindings:[{exchange:test.ex,queue:test.q,key:#,arguments:{x-scope:session}}]}}");
+ Receiver receiver2 = fix.session.createReceiver("test.q;{create:never, delete:always}");
+ connection.close();
+
+ sender.send(Message("test-message"), true);
+
+ // The session-scoped binding should be removed when receiver1's network connection is lost
+ Message in;
+ BOOST_CHECK(!receiver2.fetch(in, Duration::IMMEDIATE));
+}
+
+namespace {
+struct Fetcher : public qpid::sys::Runnable {
+ Receiver receiver;
+ Message message;
+ bool result;
+ qpid::messaging::Duration timeout;
+ bool timedOut;
+
+ Fetcher(Receiver r) : receiver(r), result(false), timeout(Duration::SECOND*10), timedOut(false) {}
+ void run()
+ {
+ qpid::sys::AbsTime start(qpid::sys::now());
+ try {
+ result = receiver.fetch(message, timeout);
+ } catch (const MessagingException&) {}
+ qpid::sys::Duration timeTaken(start, qpid::sys::now());
+ timedOut = (uint64_t) timeTaken >= timeout.getMilliseconds() * qpid::sys::TIME_MSEC;
+ }
+};
+}
+
+QPID_AUTO_TEST_CASE(testConcurrentFetch)
+{
+ MessagingFixture fix;
+ Sender sender = fix.session.createSender("my-test-queue;{create:always, node : { x-declare : { auto-delete: true}}}");
+ Receiver receiver = fix.session.createReceiver("my-test-queue");
+ Fetcher fetcher(fix.session.createReceiver("amq.fanout"));
+ qpid::sys::Thread runner(fetcher);
+ Message out("test-message");
+ for (int i = 0; i < 10; i++) {//try several times to make sure
+ sender.send(out, true);
+ //since the message is now on the queue, it should take less than the timeout to actually fetch it
+ qpid::sys::AbsTime start = qpid::sys::AbsTime::now();
+ Message in;
+ BOOST_CHECK(receiver.fetch(in, qpid::messaging::Duration::SECOND*2));
+ qpid::sys::Duration time(start, qpid::sys::AbsTime::now());
+ BOOST_CHECK(time < qpid::sys::TIME_SEC*2);
+ if (time >= qpid::sys::TIME_SEC*2) break;//if we failed, no need to keep testing
+ }
+ fix.session.createSender("amq.fanout").send(out);
+ runner.join();
+ BOOST_CHECK(fetcher.result);
+}
+
+QPID_AUTO_TEST_CASE(testSimpleRequestResponse)
+{
+ QueueFixture fix;
+ //create receiver on temp queue for responses (using shorthand for temp queue)
+ Receiver r1 = fix.session.createReceiver("#");
+ //send request
+ Sender s1 = fix.session.createSender(fix.queue);
+ Message original("test-message");
+ original.setSubject("test-subject");
+ original.setReplyTo(r1.getAddress());
+ s1.send(original);
+
+ //receive request and send response
+ Receiver r2 = fix.session.createReceiver(fix.queue);
+ Message m = r2.fetch(Duration::SECOND * 5);
+ Sender s2 = fix.session.createSender(m.getReplyTo());
+ s2.send(m);
+ m = r1.fetch(Duration::SECOND * 5);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(m.getContent(), original.getContent());
+ BOOST_CHECK_EQUAL(m.getSubject(), original.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testSelfDestructQueue)
+{
+ MessagingFixture fix;
+ Session other = fix.connection.createSession();
+ Receiver r1 = other.createReceiver("amq.fanout; {link:{reliability:at-least-once, x-declare:{arguments:{qpid.max_count:10,qpid.policy_type:self-destruct}}}}");
+ Receiver r2 = fix.session.createReceiver("amq.fanout");
+ //send request
+ Sender s = fix.session.createSender("amq.fanout");
+ for (uint i = 0; i < 20; ++i) {
+ s.send(Message((boost::format("MSG_%1%") % (i+1)).str()));
+ }
+ try {
+ ScopedSuppressLogging sl;
+ for (uint i = 0; i < 20; ++i) {
+ r1.fetch(Duration::SECOND);
+ }
+ BOOST_FAIL("Expected exception.");
+ } catch (const qpid::messaging::MessagingException&) {
+ }
+
+ for (uint i = 0; i < 20; ++i) {
+ BOOST_CHECK_EQUAL(r2.fetch(Duration::SECOND).getContent(), (boost::format("MSG_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testReroutingRingQueue)
+{
+ MessagingFixture fix;
+ Receiver r1 = fix.session.createReceiver("my-queue; {create:always, node:{x-declare:{alternate-exchange:amq.fanout, auto-delete:True, arguments:{qpid.max_count:10,qpid.policy_type:ring}}}}");
+ Receiver r2 = fix.session.createReceiver("amq.fanout");
+
+ Sender s = fix.session.createSender("my-queue");
+ for (uint i = 0; i < 20; ++i) {
+ s.send(Message((boost::format("MSG_%1%") % (i+1)).str()));
+ }
+ for (uint i = 10; i < 20; ++i) {
+ BOOST_CHECK_EQUAL(r1.fetch(Duration::SECOND).getContent(), (boost::format("MSG_%1%") % (i+1)).str());
+ }
+ for (uint i = 0; i < 10; ++i) {
+ BOOST_CHECK_EQUAL(r2.fetch(Duration::SECOND).getContent(), (boost::format("MSG_%1%") % (i+1)).str());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testReleaseOnPriorityQueue)
+{
+ MessagingFixture fix;
+ std::string queue("queue; {create:always, node:{x-declare:{auto-delete:True, arguments:{qpid.priorities:10}}}}");
+ std::string text("my message");
+ Sender sender = fix.session.createSender(queue);
+ sender.send(Message(text));
+ Receiver receiver = fix.session.createReceiver(queue);
+ Message msg;
+ for (uint i = 0; i < 10; ++i) {
+ if (receiver.fetch(msg, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(msg.getContent(), text);
+ fix.session.release(msg);
+ } else {
+ BOOST_FAIL("Released message not redelivered as expected.");
+ }
+ }
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testRollbackWithFullPrefetch)
+{
+ QueueFixture fix;
+ std::string first("first");
+ std::string second("second");
+ Sender sender = fix.session.createSender(fix.queue);
+ for (uint i = 0; i < 10; ++i) {
+ sender.send(Message((boost::format("MSG_%1%") % (i+1)).str()));
+ }
+ Session txsession = fix.connection.createTransactionalSession();
+ Receiver receiver = txsession.createReceiver(fix.queue);
+ receiver.setCapacity(9);
+ Message msg;
+ for (uint i = 0; i < 10; ++i) {
+ if (receiver.fetch(msg, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(msg.getContent(), std::string("MSG_1"));
+ txsession.rollback();
+ } else {
+ BOOST_FAIL("Released message not redelivered as expected.");
+ break;
+ }
+ }
+ txsession.acknowledge();
+ txsession.commit();
+}
+
+QPID_AUTO_TEST_CASE(testCloseAndConcurrentFetch)
+{
+ QueueFixture fix;
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Fetcher fetcher(receiver);
+ qpid::sys::Thread runner(fetcher);
+ qpid::sys::usleep(500);
+ receiver.close();
+ runner.join();
+ BOOST_CHECK(!fetcher.timedOut);
+}
+
+QPID_AUTO_TEST_CASE(testCloseAndMultipleConcurrentFetches)
+{
+ QueueFixture fix;
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Receiver receiver2 = fix.session.createReceiver("amq.fanout");
+ Receiver receiver3 = fix.session.createReceiver("amq.fanout");
+ Fetcher fetcher(receiver);
+ Fetcher fetcher2(receiver2);
+ Fetcher fetcher3(receiver3);
+ qpid::sys::Thread runner(fetcher);
+ qpid::sys::Thread runner2(fetcher2);
+ qpid::sys::Thread runner3(fetcher3);
+ qpid::sys::usleep(500);
+ receiver.close();
+ Message message("Test");
+ fix.session.createSender("amq.fanout").send(message);
+ runner2.join();
+ BOOST_CHECK(fetcher2.result);
+ BOOST_CHECK_EQUAL(fetcher2.message.getContent(), message.getContent());
+ runner3.join();
+ BOOST_CHECK(fetcher3.result);
+ BOOST_CHECK_EQUAL(fetcher3.message.getContent(), message.getContent());
+ runner.join();
+ BOOST_CHECK(!fetcher.timedOut);
+}
+
+QPID_AUTO_TEST_CASE(testSessionCheckError)
+{
+ MessagingFixture fix;
+ Session session = fix.connection.createSession();
+ Sender sender = session.createSender("q; {create:always, node:{x-declare:{auto-delete:True, arguments:{qpid.max_count:1}}}}");
+ ScopedSuppressLogging sl;
+ for (uint i = 0; i < 2; ++i) {
+ sender.send(Message((boost::format("A_%1%") % (i+1)).str()));
+ }
+ try {
+ while (true) session.checkError();
+ } catch (const qpid::types::Exception&) {
+ //this is ok
+ } catch (const qpid::Exception&) {
+ BOOST_FAIL("Wrong exception type thrown");
+ }
+}
+
+QPID_AUTO_TEST_CASE(testImmediateNextReceiver)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("test message");
+ sender.send(out);
+ fix.session.createReceiver(fix.queue).setCapacity(1);
+ Receiver next;
+ qpid::sys::AbsTime start = qpid::sys::now();
+ try {
+ while (!fix.session.nextReceiver(next, qpid::messaging::Duration::IMMEDIATE)) {
+ qpid::sys::Duration running(start, qpid::sys::now());
+ if (running > 5*qpid::sys::TIME_SEC) {
+ throw qpid::types::Exception("Timed out spinning on nextReceiver(IMMEDIATE)");
+ }
+ qpid::sys::usleep(1); // for valgrind
+ }
+ Message in;
+ BOOST_CHECK(next.fetch(in, qpid::messaging::Duration::IMMEDIATE));
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ next.close();
+ } catch (const std::exception& e) {
+ BOOST_FAIL(e.what());
+ }
+}
+
+QPID_AUTO_TEST_CASE(testImmediateNextReceiverNoMessage)
+{
+ QueueFixture fix;
+ Receiver r = fix.session.createReceiver(fix.queue);
+ r.setCapacity(1);
+ Receiver next;
+ try {
+ BOOST_CHECK(!fix.session.nextReceiver(next, qpid::messaging::Duration::IMMEDIATE));
+ r.close();
+ } catch (const std::exception& e) {
+ BOOST_FAIL(e.what());
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/MessagingThreadTests.cpp b/qpid/cpp/src/tests/MessagingThreadTests.cpp
new file mode 100644
index 0000000000..48264735b1
--- /dev/null
+++ b/qpid/cpp/src/tests/MessagingThreadTests.cpp
@@ -0,0 +1,144 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "MessagingFixture.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include <boost/lexical_cast.hpp>
+
+namespace qpid {
+namespace tests {
+QPID_AUTO_TEST_SUITE(MessagingThreadTests)
+
+using namespace messaging;
+using namespace boost::assign;
+using namespace std;
+
+struct ReceiveThread : public sys::Runnable {
+ Receiver receiver;
+ vector<string> received;
+ string error;
+
+ ReceiveThread(Receiver s) : receiver(s) {}
+ void run() {
+ try {
+ while(true) {
+ Message m = receiver.fetch(Duration::SECOND*5);
+ if (m.getContent() == "END") break;
+ received.push_back(m.getContent());
+ }
+ } catch (const NoMessageAvailable& e) {
+ // Indicates that fetch timed out OR receiver was closed by other thread.
+ if (!receiver.isClosed()) // timeout
+ error = e.what();
+ } catch (const std::exception& e) {
+ error = e.what();
+ }
+ }
+};
+
+struct NextReceiverThread : public sys::Runnable {
+ Session session;
+ vector<string> received;
+ string error;
+
+ NextReceiverThread(Session s) : session(s) {}
+ void run() {
+ try {
+ while(true) {
+ Message m = session.nextReceiver(Duration::SECOND*5).fetch();
+ if (m.getContent() == "END") break;
+ received.push_back(m.getContent());
+ }
+ } catch (const std::exception& e) {
+ error = e.what();
+ }
+ }
+};
+
+
+QPID_AUTO_TEST_CASE(testConcurrentSendReceive) {
+ MessagingFixture fix;
+ Sender s = fix.session.createSender("concurrent;{create:always}");
+ Receiver r = fix.session.createReceiver("concurrent;{create:always,link:{reliability:unreliable}}");
+ ReceiveThread rt(r);
+ sys::Thread thread(rt);
+ const size_t COUNT=100;
+ for (size_t i = 0; i < COUNT; ++i) {
+ s.send(Message());
+ }
+ s.send(Message("END"));
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+ BOOST_CHECK_EQUAL(COUNT, rt.received.size());
+}
+
+QPID_AUTO_TEST_CASE(testCloseBusyReceiver) {
+ MessagingFixture fix;
+ Receiver r = fix.session.createReceiver("closeReceiver;{create:always}");
+ ReceiveThread rt(r);
+ sys::Thread thread(rt);
+ sys::usleep(1000); // Give the receive thread time to block.
+ r.close();
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+
+ // Fetching on closed receiver should fail.
+ Message m;
+ BOOST_CHECK(!r.fetch(m, Duration(0)));
+ BOOST_CHECK_THROW(r.fetch(Duration(0)), NoMessageAvailable);
+}
+
+QPID_AUTO_TEST_CASE(testCloseSessionBusyReceiver) {
+ MessagingFixture fix;
+ Receiver r = fix.session.createReceiver("closeSession;{create:always}");
+ ReceiveThread rt(r);
+ sys::Thread thread(rt);
+ sys::usleep(1000); // Give the receive thread time to block.
+ fix.session.close();
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+
+ // Fetching on closed receiver should fail.
+ Message m;
+ BOOST_CHECK(!r.fetch(m, Duration(0)));
+ BOOST_CHECK_THROW(r.fetch(Duration(0)), NoMessageAvailable);
+}
+
+QPID_AUTO_TEST_CASE(testConcurrentSendNextReceiver) {
+ MessagingFixture fix;
+ Receiver r = fix.session.createReceiver("concurrent;{create:always,link:{reliability:unreliable}}");
+ const size_t COUNT=100;
+ r.setCapacity(COUNT);
+ NextReceiverThread rt(fix.session);
+ sys::Thread thread(rt);
+ sys::usleep(1000); // Give the receive thread time to block.
+ Sender s = fix.session.createSender("concurrent;{create:always}");
+ for (size_t i = 0; i < COUNT; ++i) {
+ s.send(Message());
+ }
+ s.send(Message("END"));
+ thread.join();
+ BOOST_CHECK_EQUAL(rt.error, string());
+ BOOST_CHECK_EQUAL(COUNT, rt.received.size());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/PollableCondition.cpp b/qpid/cpp/src/tests/PollableCondition.cpp
new file mode 100644
index 0000000000..f9b3c25c93
--- /dev/null
+++ b/qpid/cpp/src/tests/PollableCondition.cpp
@@ -0,0 +1,109 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "test_tools.h"
+#include "unit_test.h"
+#include "qpid/sys/Poller.h"
+#include "qpid/sys/PollableCondition.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(PollableConditionTest)
+
+using namespace qpid::sys;
+
+const Duration SHORT = TIME_SEC/100;
+const Duration LONG = TIME_SEC/10;
+
+class Callback {
+ public:
+ enum Action { NONE, CLEAR };
+
+ Callback() : count(), action(NONE) {}
+
+ void call(PollableCondition& pc) {
+ Mutex::ScopedLock l(lock);
+ ++count;
+ switch(action) {
+ case NONE: break;
+ case CLEAR: pc.clear(); break;
+ }
+ action = NONE;
+ lock.notify();
+ }
+
+ bool isCalling() { Mutex::ScopedLock l(lock); return wait(LONG); }
+
+ bool isNotCalling() { Mutex::ScopedLock l(lock); return !wait(SHORT); }
+
+ bool nextCall(Action a=NONE) {
+ Mutex::ScopedLock l(lock);
+ action = a;
+ return wait(LONG);
+ }
+
+ private:
+ bool wait(Duration timeout) {
+ int n = count;
+ AbsTime deadline(now(), timeout);
+ while (n == count && lock.wait(deadline))
+ ;
+ return n != count;
+ }
+
+ Monitor lock;
+ int count;
+ Action action;
+};
+
+QPID_AUTO_TEST_CASE(testPollableCondition) {
+ boost::shared_ptr<Poller> poller(new Poller());
+ Callback callback;
+ PollableCondition pc(boost::bind(&Callback::call, &callback, _1), poller);
+
+ Thread runner = Thread(*poller);
+
+ BOOST_CHECK(callback.isNotCalling()); // condition is not set.
+
+ pc.set();
+ BOOST_CHECK(callback.isCalling()); // Set.
+ BOOST_CHECK(callback.isCalling()); // Still set.
+
+ callback.nextCall(Callback::CLEAR);
+ BOOST_CHECK(callback.isNotCalling()); // Cleared
+
+ pc.set();
+ BOOST_CHECK(callback.isCalling()); // Set.
+ callback.nextCall(Callback::CLEAR);
+ BOOST_CHECK(callback.isNotCalling()); // Cleared.
+
+ poller->shutdown();
+ runner.join();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} //namespace qpid::tests
diff --git a/qpid/cpp/src/tests/PollerTest.cpp b/qpid/cpp/src/tests/PollerTest.cpp
new file mode 100644
index 0000000000..5a1d02964c
--- /dev/null
+++ b/qpid/cpp/src/tests/PollerTest.cpp
@@ -0,0 +1,262 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "qpid/sys/posix/PrivatePosix.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;
+}
+
+void makesocketpair(int (&sv)[2]) {
+ int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ assert(rc >= 0);
+
+ // Set non-blocking
+ rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+
+ rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK);
+ assert(rc >= 0);
+}
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ try
+ {
+ int sv[2];
+ makesocketpair(sv);
+
+ // 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);
+
+ // Write as much as we can to socket 0
+ int bytesWritten = writeALot(sv[0], testString);
+
+ // Read as much as we can from socket 1
+ bytesRead = readALot(sv[1]);
+ assert(bytesRead == bytesWritten);
+
+ auto_ptr<Poller> poller(new Poller);
+
+ IOHandle f0(sv[0]);
+ IOHandle f1(sv[1]);
+
+ PollerHandle h0(f0);
+ PollerHandle h1(f1);
+
+ poller->registerHandle(h0);
+ poller->monitorHandle(h0, Poller::INOUT);
+
+ // h0 should be writable
+ Poller::Event event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ // Write as much as we can to socket 0
+ bytesWritten = writeALot(sv[0], testString);
+
+ // Wait for 500ms - h0 no longer writable
+ event = poller->wait(500000000);
+ assert(event.handle == 0);
+
+ // Test we can read it all now
+ poller->registerHandle(h1);
+ poller->monitorHandle(h1, Poller::INOUT);
+ event = poller->wait();
+ assert(event.handle == &h1);
+ assert(event.type == Poller::READ_WRITABLE);
+
+ bytesRead = readALot(sv[1]);
+ assert(bytesRead == bytesWritten);
+
+ // Test poller interrupt
+ assert(poller->interrupt(h0) == true);
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::INTERRUPTED);
+
+ // Test multiple interrupts
+ assert(poller->interrupt(h0) == true);
+ assert(poller->interrupt(h1) == true);
+
+ // Make sure we can interrupt them again
+ assert(poller->interrupt(h0) == true);
+ assert(poller->interrupt(h1) == true);
+
+ // Make sure that they both come out
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h0 || event.handle == &h1);
+ if (event.handle == &h0) {
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h1);
+ } else {
+ event = poller->wait();
+ assert(event.type == Poller::INTERRUPTED);
+ assert(event.handle == &h0);
+ }
+
+ poller->unmonitorHandle(h1, Poller::INOUT);
+
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ // We didn't write anything so it should still be writable
+ event = poller->wait();
+ assert(event.handle == &h0);
+ assert(event.type == Poller::WRITABLE);
+
+ poller->unmonitorHandle(h0, Poller::INOUT);
+
+ event = poller->wait(500000000);
+ assert(event.handle == 0);
+
+ poller->unregisterHandle(h1);
+ assert(poller->interrupt(h1) == false);
+
+ // close the other end to force a disconnect
+ ::close(sv[1]);
+
+ // Now make sure that we are readable followed by disconnected
+ // and after that we never return again
+ poller->monitorHandle(h0, Poller::INOUT);
+ event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::READABLE);
+ event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::DISCONNECTED);
+ event = poller->wait(1500000000);
+ assert(event.handle == 0);
+
+ // Now we're disconnected monitoring should have no effect at all
+ poller->unmonitorHandle(h0, Poller::INOUT);
+ event = poller->wait(1500000000);
+ assert(event.handle == 0);
+
+ poller->unregisterHandle(h0);
+ assert(poller->interrupt(h0) == false);
+
+ // Test shutdown
+ poller->shutdown();
+ event = poller->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ event = poller->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ ::close(sv[0]);
+
+ // Test for correct interaction of shutdown and interrupts - need to have new poller
+ // etc. for this
+ makesocketpair(sv);
+
+ auto_ptr<Poller> poller1(new Poller);
+
+ IOHandle f2(sv[0]);
+ IOHandle f3(sv[1]);
+
+ PollerHandle h2(f2);
+ PollerHandle h3(f3);
+
+ poller1->registerHandle(h2);
+ poller1->monitorHandle(h2, Poller::INOUT);
+ event = poller1->wait();
+ assert(event.handle == &h2);
+ assert(event.type == Poller::WRITABLE);
+
+ // Shutdown
+ poller1->shutdown();
+ event = poller1->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ assert(poller1->interrupt(h2) == true);
+ event = poller1->wait();
+ assert(event.handle == &h2);
+ assert(event.type == Poller::INTERRUPTED);
+ poller1->unmonitorHandle(h2, Poller::INOUT);
+
+ event = poller1->wait();
+ assert(event.handle == 0);
+ assert(event.type == Poller::SHUTDOWN);
+
+ poller1->unregisterHandle(h2);
+ return 0;
+ } catch (exception& e) {
+ cout << "Caught exception " << e.what() << "\n";
+ }
+}
+
+
diff --git a/qpid/cpp/src/tests/ProxyTest.cpp b/qpid/cpp/src/tests/ProxyTest.cpp
new file mode 100644
index 0000000000..a926b28395
--- /dev/null
+++ b/qpid/cpp/src/tests/ProxyTest.cpp
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <iostream>
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/ExecutionSyncBody.h"
+#include "qpid/framing/Proxy.h"
+
+#include "unit_test.h"
+
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ProxyTestSuite)
+
+
+QPID_AUTO_TEST_CASE(testScopedSync)
+{
+ struct DummyHandler : FrameHandler
+ {
+ void handle(AMQFrame& f) {
+ AMQMethodBody* m = f.getMethod();
+ BOOST_CHECK(m);
+ BOOST_CHECK(m->isA<ExecutionSyncBody>());
+ BOOST_CHECK(m->isSync());
+ }
+ };
+ DummyHandler f;
+ Proxy p(f);
+ Proxy::ScopedSync s(p);
+ p.send(ExecutionSyncBody(p.getVersion()));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Qmf2.cpp b/qpid/cpp/src/tests/Qmf2.cpp
new file mode 100644
index 0000000000..bc263d5c6d
--- /dev/null
+++ b/qpid/cpp/src/tests/Qmf2.cpp
@@ -0,0 +1,422 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/types/Variant.h"
+#include "qmf/QueryImpl.h"
+#include "qmf/SchemaImpl.h"
+#include "qmf/exceptions.h"
+#include "qpid/messaging/Connection.h"
+#include "qmf/PosixEventNotifierImpl.h"
+#include "qmf/AgentSession.h"
+#include "qmf/AgentSessionImpl.h"
+#include "qmf/ConsoleSession.h"
+#include "qmf/ConsoleSessionImpl.h"
+#include "unit_test.h"
+
+using namespace std;
+using namespace qpid::types;
+using namespace qpid::messaging;
+using namespace qmf;
+
+bool isReadable(int fd)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int nfds, result;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ nfds = fd + 1;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ result = select(nfds, &rfds, NULL, NULL, &tv);
+
+ return result > 0;
+}
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(Qmf2Suite)
+
+QPID_AUTO_TEST_CASE(testQuery)
+{
+ Query query(QUERY_OBJECT, "class_name", "package_name", "[and, [eq, name, [quote, smith]], [lt, age, [quote, 27]]]");
+ Query newQuery(new QueryImpl(QueryImplAccess::get(query).asMap()));
+
+ BOOST_CHECK_EQUAL(newQuery.getTarget(), QUERY_OBJECT);
+ BOOST_CHECK_EQUAL(newQuery.getSchemaId().getName(), "class_name");
+ BOOST_CHECK_EQUAL(newQuery.getSchemaId().getPackageName(), "package_name");
+
+ Variant::List pred(newQuery.getPredicate());
+ BOOST_CHECK_EQUAL(pred.size(), size_t(3));
+
+ Variant::List::iterator iter(pred.begin());
+ BOOST_CHECK_EQUAL(iter->asString(), "and");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->getType(), VAR_LIST);
+ iter++;
+ BOOST_CHECK_EQUAL(iter->getType(), VAR_LIST);
+ iter = iter->asList().begin();
+ BOOST_CHECK_EQUAL(iter->asString(), "lt");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->asString(), "age");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->getType(), VAR_LIST);
+ iter = iter->asList().begin();
+ BOOST_CHECK_EQUAL(iter->asString(), "quote");
+ iter++;
+ BOOST_CHECK_EQUAL(iter->asUint32(), uint32_t(27));
+
+ Query query2(QUERY_OBJECT_ID);
+ Query newQuery2(new QueryImpl(QueryImplAccess::get(query2).asMap()));
+ BOOST_CHECK_EQUAL(newQuery2.getTarget(), QUERY_OBJECT_ID);
+
+ Query query3(QUERY_SCHEMA);
+ Query newQuery3(new QueryImpl(QueryImplAccess::get(query3).asMap()));
+ BOOST_CHECK_EQUAL(newQuery3.getTarget(), QUERY_SCHEMA);
+
+ Query query4(QUERY_SCHEMA_ID);
+ Query newQuery4(new QueryImpl(QueryImplAccess::get(query4).asMap()));
+ BOOST_CHECK_EQUAL(newQuery4.getTarget(), QUERY_SCHEMA_ID);
+
+ DataAddr addr("name", "agent_name", 34);
+ Query query5(addr);
+ Query newQuery5(new QueryImpl(QueryImplAccess::get(query5).asMap()));
+ BOOST_CHECK_EQUAL(newQuery5.getTarget(), QUERY_OBJECT);
+ BOOST_CHECK_EQUAL(newQuery5.getDataAddr().getName(), "name");
+ BOOST_CHECK_EQUAL(newQuery5.getDataAddr().getAgentName(), "agent_name");
+ BOOST_CHECK_EQUAL(newQuery5.getDataAddr().getAgentEpoch(), uint32_t(34));
+}
+
+QPID_AUTO_TEST_CASE(testQueryPredicateErrors)
+{
+ Query query;
+ Variant::Map map;
+
+ BOOST_CHECK_THROW(Query(QUERY_OBJECT, "INVALID"), QmfException);
+ query = Query(QUERY_OBJECT, "[unknown, one, two]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[exists]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, [quote, 1, 2, 3]]]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, [unexpected, 3]]]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, {}]]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[eq, first, second, third]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+
+ query = Query(QUERY_OBJECT, "[and, first, second, third]");
+ BOOST_CHECK_THROW(query.matchesPredicate(map), QmfException);
+}
+
+QPID_AUTO_TEST_CASE(testQueryPredicate)
+{
+ Query query;
+ Variant::Map map;
+
+ map["forty"] = 40;
+ map["fifty"] = 50;
+ map["minus_ten"] = -10;
+ map["pos_float"] = 100.05;
+ map["neg_float"] = -1000.33;
+ map["name"] = "jones";
+ map["bool_t"] = true;
+ map["bool_f"] = false;
+
+ BOOST_CHECK_THROW(Query(QUERY_OBJECT, "INVALID"), QmfException);
+
+ query = Query(QUERY_OBJECT);
+ BOOST_CHECK_EQUAL(query.matchesPredicate(Variant::Map()), true);
+
+ query = Query(QUERY_OBJECT, "[eq, forty, [quote, 40]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, forty, [quote, 41]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[le, forty, fifty]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[and, [eq, forty, [quote, 40]], [eq, name, [quote, jones]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[and, [eq, forty, [quote, 40]], [eq, name, [quote, smith]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[or, [eq, forty, [quote, 40]], [eq, name, [quote, smith]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[or, [eq, forty, [quote, 41]], [eq, name, [quote, smith]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[not, [le, forty, [quote, 40]]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[le, forty, [quote, 40]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[ge, forty, [quote, 40]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[lt, forty, [quote, 45]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[lt, [quote, 45], forty]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[gt, forty, [quote, 45]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[gt, [quote, 45], forty]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_t, [quote, True]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_t, [quote, False]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_f, [quote, True]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[eq, bool_f, [quote, False]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[eq, minus_ten, [quote, -10]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[lt, minus_ten, [quote, -20]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[lt, [quote, -20], minus_ten]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[exists, name]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+
+ query = Query(QUERY_OBJECT, "[exists, nonexfield]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), false);
+
+ query = Query(QUERY_OBJECT, "[eq, pos_float, [quote, 100.05]]");
+ BOOST_CHECK_EQUAL(query.matchesPredicate(map), true);
+}
+
+QPID_AUTO_TEST_CASE(testSchema)
+{
+ Schema in(SCHEMA_TYPE_DATA, "package", "class");
+ in.addProperty(SchemaProperty("prop1", SCHEMA_DATA_BOOL, "{desc:'Property One'}"));
+ in.addProperty(SchemaProperty("prop2", SCHEMA_DATA_INT, "{desc:'Property Two',unit:'Furlong'}"));
+ in.addProperty(SchemaProperty("prop3", SCHEMA_DATA_STRING, "{desc:'Property Three'}"));
+
+ SchemaMethod method1("method1", "{desc:'Method One'}");
+ method1.addArgument(SchemaProperty("arg1", SCHEMA_DATA_BOOL, "{desc:'Argument One',dir:IN}"));
+ method1.addArgument(SchemaProperty("arg2", SCHEMA_DATA_INT, "{desc:'Argument Two',dir:OUT}"));
+ method1.addArgument(SchemaProperty("arg3", SCHEMA_DATA_FLOAT, "{desc:'Argument Three',dir:INOUT}"));
+ in.addMethod(method1);
+
+ SchemaMethod method2("method2", "{desc:'Method Two'}");
+ method2.addArgument(SchemaProperty("arg21", SCHEMA_DATA_BOOL, "{desc:'Argument One',dir:IN}"));
+ method2.addArgument(SchemaProperty("arg22", SCHEMA_DATA_INT, "{desc:'Argument Two',dir:OUT}"));
+ method2.addArgument(SchemaProperty("arg23", SCHEMA_DATA_FLOAT, "{desc:'Argument Three',dir:INOUT}"));
+ in.addMethod(method2);
+
+ BOOST_CHECK(!in.isFinalized());
+ in.finalize();
+ BOOST_CHECK(in.isFinalized());
+
+ Variant::Map map(SchemaImplAccess::get(in).asMap());
+ Schema out(new SchemaImpl(map));
+
+ BOOST_CHECK(out.isFinalized());
+ BOOST_CHECK_EQUAL(out.getSchemaId().getType(), SCHEMA_TYPE_DATA);
+ BOOST_CHECK_EQUAL(out.getSchemaId().getPackageName(), "package");
+ BOOST_CHECK_EQUAL(out.getSchemaId().getName(), "class");
+ BOOST_CHECK_EQUAL(out.getSchemaId().getHash(), in.getSchemaId().getHash());
+
+ BOOST_CHECK_EQUAL(out.getPropertyCount(), uint32_t(3));
+ SchemaProperty prop;
+
+ prop = out.getProperty(0);
+ BOOST_CHECK_EQUAL(prop.getName(), "prop1");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_BOOL);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Property One");
+
+ prop = out.getProperty(1);
+ BOOST_CHECK_EQUAL(prop.getName(), "prop2");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_INT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Property Two");
+ BOOST_CHECK_EQUAL(prop.getUnit(), "Furlong");
+ BOOST_CHECK(!prop.isIndex());
+
+ prop = out.getProperty(2);
+ BOOST_CHECK_EQUAL(prop.getName(), "prop3");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_STRING);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Property Three");
+
+ BOOST_CHECK_THROW(out.getProperty(3), QmfException);
+
+ BOOST_CHECK_EQUAL(out.getMethodCount(), uint32_t(2));
+ SchemaMethod method;
+
+ method = out.getMethod(0);
+ BOOST_CHECK_EQUAL(method.getName(), "method1");
+ BOOST_CHECK_EQUAL(method.getDesc(), "Method One");
+ BOOST_CHECK_EQUAL(method.getArgumentCount(), uint32_t(3));
+
+ prop = method.getArgument(0);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg1");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_BOOL);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument One");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN);
+
+ prop = method.getArgument(1);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg2");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_INT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Two");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_OUT);
+
+ prop = method.getArgument(2);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg3");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_FLOAT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Three");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN_OUT);
+
+ BOOST_CHECK_THROW(method.getArgument(3), QmfException);
+
+ method = out.getMethod(1);
+ BOOST_CHECK_EQUAL(method.getName(), "method2");
+ BOOST_CHECK_EQUAL(method.getDesc(), "Method Two");
+ BOOST_CHECK_EQUAL(method.getArgumentCount(), uint32_t(3));
+
+ prop = method.getArgument(0);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg21");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_BOOL);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument One");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN);
+
+ prop = method.getArgument(1);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg22");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_INT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Two");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_OUT);
+
+ prop = method.getArgument(2);
+ BOOST_CHECK_EQUAL(prop.getName(), "arg23");
+ BOOST_CHECK_EQUAL(prop.getType(), SCHEMA_DATA_FLOAT);
+ BOOST_CHECK_EQUAL(prop.getDesc(), "Argument Three");
+ BOOST_CHECK_EQUAL(prop.getDirection(), DIR_IN_OUT);
+
+ BOOST_CHECK_THROW(method.getArgument(3), QmfException);
+}
+
+QPID_AUTO_TEST_CASE(testAgentSessionEventListener)
+{
+ Connection connection("localhost");
+ AgentSession session(connection, "");
+ posix::EventNotifier notifier(session);
+
+ AgentSessionImpl& sessionImpl = AgentSessionImplAccess::get(session);
+
+ BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
+}
+
+QPID_AUTO_TEST_CASE(testConsoleSessionEventListener)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+
+ ConsoleSessionImpl& sessionImpl = ConsoleSessionImplAccess::get(session);
+
+ BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
+}
+
+QPID_AUTO_TEST_CASE(testGetHandle)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+
+ BOOST_CHECK(notifier.getHandle() > 0);
+}
+
+QPID_AUTO_TEST_CASE(testSetReadableToFalse)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+ PosixEventNotifierImplAccess::get(notifier).setReadable(false);
+
+ bool readable(isReadable(notifier.getHandle()));
+ BOOST_CHECK(!readable);
+}
+
+QPID_AUTO_TEST_CASE(testSetReadable)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+ PosixEventNotifierImplAccess::get(notifier).setReadable(true);
+
+ bool readable(isReadable(notifier.getHandle()));
+ BOOST_CHECK(readable);
+}
+
+QPID_AUTO_TEST_CASE(testSetReadableMultiple)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+ for (int i = 0; i < 15; i++)
+ PosixEventNotifierImplAccess::get(notifier).setReadable(true);
+ PosixEventNotifierImplAccess::get(notifier).setReadable(false);
+
+ bool readable(isReadable(notifier.getHandle()));
+ BOOST_CHECK(!readable);
+}
+
+QPID_AUTO_TEST_CASE(testDeleteNotifier)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ ConsoleSessionImpl& sessionImpl = ConsoleSessionImplAccess::get(session);
+ {
+ posix::EventNotifier notifier(session);
+ BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
+ }
+ BOOST_CHECK(sessionImpl.getEventNotifier() == 0);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueDepth.cpp b/qpid/cpp/src/tests/QueueDepth.cpp
new file mode 100644
index 0000000000..09b221b3a8
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueDepth.cpp
@@ -0,0 +1,105 @@
+/*
+ *
+ * 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/broker/QueueDepth.h"
+
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueDepthTestSuite)
+
+using namespace qpid::broker;
+
+QPID_AUTO_TEST_CASE(testCompare)
+{
+ QueueDepth a(0, 0);
+ QueueDepth b(1, 1);
+ QueueDepth c(2, 2);
+ QueueDepth d(1, 1);
+
+ BOOST_CHECK(a < b);
+ BOOST_CHECK(b < c);
+ BOOST_CHECK(a < c);
+
+ BOOST_CHECK(b > a);
+ BOOST_CHECK(c > b);
+ BOOST_CHECK(c > a);
+
+ BOOST_CHECK(b == d);
+ BOOST_CHECK(d == b);
+ BOOST_CHECK(a != b);
+ BOOST_CHECK(b != a);
+
+ QueueDepth e; e.setCount(1);
+ QueueDepth f; f.setCount(2);
+ BOOST_CHECK(e < f);
+ BOOST_CHECK(f > e);
+
+ QueueDepth g; g.setSize(1);
+ QueueDepth h; h.setSize(2);
+ BOOST_CHECK(g < h);
+ BOOST_CHECK(h > g);
+}
+
+QPID_AUTO_TEST_CASE(testIncrement)
+{
+ QueueDepth a(5, 10);
+ QueueDepth b(3, 6);
+ QueueDepth c(8, 16);
+ a += b;
+ BOOST_CHECK(a == c);
+ BOOST_CHECK_EQUAL(8u, a.getCount());
+ BOOST_CHECK_EQUAL(16u, a.getSize());
+}
+
+QPID_AUTO_TEST_CASE(testDecrement)
+{
+ QueueDepth a(5, 10);
+ QueueDepth b(3, 6);
+ QueueDepth c(2, 4);
+ a -= b;
+ BOOST_CHECK(a == c);
+ BOOST_CHECK_EQUAL(2u, a.getCount());
+ BOOST_CHECK_EQUAL(4u, a.getSize());
+}
+
+QPID_AUTO_TEST_CASE(testAddition)
+{
+ QueueDepth a(5, 10);
+ QueueDepth b(3, 6);
+
+ QueueDepth c = a + b;
+ BOOST_CHECK_EQUAL(8u, c.getCount());
+ BOOST_CHECK_EQUAL(16u, c.getSize());
+}
+
+QPID_AUTO_TEST_CASE(testSubtraction)
+{
+ QueueDepth a(5, 10);
+ QueueDepth b(3, 6);
+
+ QueueDepth c = a - b;
+ BOOST_CHECK_EQUAL(2u, c.getCount());
+ BOOST_CHECK_EQUAL(4u, c.getSize());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueFlowLimitTest.cpp b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp
new file mode 100644
index 0000000000..b35294922d
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp
@@ -0,0 +1,457 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <sstream>
+#include <deque>
+#include "unit_test.h"
+#include "test_tools.h"
+
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/broker/QueueSettings.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/FieldValue.h"
+#include "MessageUtils.h"
+#include "BrokerFixture.h"
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueFlowLimitTestSuite)
+
+namespace {
+
+class TestFlow : public QueueFlowLimit
+{
+public:
+ TestFlow(uint32_t flowStopCount, uint32_t flowResumeCount,
+ uint64_t flowStopSize, uint64_t flowResumeSize) :
+ QueueFlowLimit("", flowStopCount, flowResumeCount, flowStopSize, flowResumeSize)
+ {}
+ virtual ~TestFlow() {}
+
+ static TestFlow *createTestFlow(const qpid::framing::FieldTable& settings)
+ {
+ FieldTable::ValuePtr v;
+
+ v = settings.get(flowStopCountKey);
+ uint32_t flowStopCount = (v) ? (uint32_t)v->get<int64_t>() : 0;
+ v = settings.get(flowResumeCountKey);
+ uint32_t flowResumeCount = (v) ? (uint32_t)v->get<int64_t>() : 0;
+ v = settings.get(flowStopSizeKey);
+ uint64_t flowStopSize = (v) ? (uint64_t)v->get<int64_t>() : 0;
+ v = settings.get(flowResumeSizeKey);
+ uint64_t flowResumeSize = (v) ? (uint64_t)v->get<int64_t>() : 0;
+
+ return new TestFlow(flowStopCount, flowResumeCount, flowStopSize, flowResumeSize);
+ }
+
+ static boost::shared_ptr<qpid::broker::QueueFlowLimit> getQueueFlowLimit(const qpid::framing::FieldTable& arguments)
+ {
+ QueueSettings settings;
+ settings.populate(arguments, settings.storeSettings);
+ return QueueFlowLimit::createLimit("", settings);
+ }
+};
+
+Message createMessage(uint32_t size)
+{
+ static uint32_t seqNum;
+ //Need to compute what data size is required to make a given
+ //overall size (use one byte of content in test message to ensure
+ //content frame is added)
+ Message test = MessageUtils::createMessage(qpid::types::Variant::Map(), std::string("x"));
+ size_t min = test.getMessageSize() - 1;
+ if (min > size) throw qpid::Exception("Can't create message that small!");
+ Message msg = MessageUtils::createMessage(qpid::types::Variant::Map(), std::string (size - min, 'x'));
+ msg.setSequence(++seqNum);//this doesn't affect message size
+ return msg;
+}
+}
+
+QPID_AUTO_TEST_CASE(testFlowCount)
+{
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 7);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 5);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 7, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 5, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+
+ std::deque<Message> msgs;
+ for (size_t i = 0; i < 6; i++) {
+ msgs.push_back(createMessage(100));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ BOOST_CHECK(!flow->isFlowControlActive()); // 6 on queue
+ msgs.push_back(createMessage(100));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive()); // 7 on queue
+ msgs.push_back(createMessage(100));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 8 on queue, ON
+ msgs.push_back(createMessage(100));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 9 on queue, no change to flow control
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 8 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 7 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 6 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 5 on queue, no change
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 4 on queue, OFF
+}
+
+QPID_AUTO_TEST_CASE(testFlowSize)
+{
+ FieldTable args;
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, 700);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 460);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint32_t) 700, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 460, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+
+ std::deque<Message> msgs;
+ for (size_t i = 0; i < 6; i++) {
+ msgs.push_back(createMessage(100));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ BOOST_CHECK(!flow->isFlowControlActive()); // 600 on queue
+ BOOST_CHECK_EQUAL(6u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(600u, flow->getFlowSize());
+
+ Message msg_50 = createMessage(50);
+ flow->enqueued(msg_50);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 650 on queue
+ Message tinyMsg_1 = createMessage(40);
+ flow->enqueued(tinyMsg_1);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 690 on queue
+
+ Message tinyMsg_2 = createMessage(40);
+ flow->enqueued(tinyMsg_2);
+ BOOST_CHECK(flow->isFlowControlActive()); // 730 on queue, ON
+ msgs.push_back(createMessage(100));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 830 on queue
+ BOOST_CHECK_EQUAL(10u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(830u, flow->getFlowSize());
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 730 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 630 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 530 on queue
+
+ flow->dequeued(tinyMsg_1);
+ BOOST_CHECK(flow->isFlowControlActive()); // 490 on queue
+ flow->dequeued(tinyMsg_2);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 450 on queue, OFF
+
+ flow->dequeued(msg_50);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 400 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 300 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 200 on queue
+ BOOST_CHECK_EQUAL(2u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(200u, flow->getFlowSize());
+}
+
+QPID_AUTO_TEST_CASE(testFlowArgs)
+{
+ FieldTable args;
+ const uint64_t stop(0x2FFFFFFFFull);
+ const uint64_t resume(0x1FFFFFFFFull);
+ args.setInt(QueueFlowLimit::flowStopCountKey, 30);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 21);
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, stop);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, resume);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 30, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 21, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL(stop, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL(resume, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowCombo)
+{
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 10);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 5);
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, 2000);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 1000);
+
+ std::deque<Message> msgs_50;
+ std::deque<Message> msgs_100;
+ std::deque<Message> msgs_500;
+ std::deque<Message> msgs_1000;
+
+ Message msg;
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+ BOOST_CHECK(!flow->isFlowControlActive()); // count:0 size:0
+
+ // verify flow control comes ON when only count passes its stop point.
+
+ for (size_t i = 0; i < 10; i++) {
+ msgs_100.push_back(createMessage(100));
+ flow->enqueued(msgs_100.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:10 size:1000
+
+ msgs_50.push_back(createMessage(50));
+ flow->enqueued(msgs_50.back()); // count:11 size: 1050 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 6; i++) {
+ flow->dequeued(msgs_100.front());
+ msgs_100.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+ }
+ // count:5 size: 450
+
+ flow->dequeued(msgs_50.front()); // count: 4 size: 400 ->OFF
+ msgs_50.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 4; i++) {
+ flow->dequeued(msgs_100.front());
+ msgs_100.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:0 size:0
+
+ // verify flow control comes ON when only size passes its stop point.
+
+ msgs_1000.push_back(createMessage(1000));
+ flow->enqueued(msgs_1000.back()); // count:1 size: 1000
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_500.push_back(createMessage(500));
+ flow->enqueued(msgs_500.back()); // count:2 size: 1500
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_500.push_back(createMessage(500));
+ flow->enqueued(msgs_500.back()); // count:3 size: 2000
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_50.push_back(createMessage(50));
+ flow->enqueued(msgs_50.back()); // count:4 size: 2050 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_1000.front()); // count:3 size:1050
+ msgs_1000.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_50.front()); // count:2 size:1000
+ msgs_50.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_500.front()); // count:1 size:500 ->OFF
+ msgs_500.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ // verify flow control remains ON until both thresholds drop below their
+ // resume point.
+
+ for (size_t i = 0; i < 8; i++) {
+ msgs_100.push_back(createMessage(100));
+ flow->enqueued(msgs_100.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:9 size:1300
+
+ msgs_100.push_back(createMessage(100));
+ flow->enqueued(msgs_100.back()); // count:10 size: 1400
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_50.push_back(createMessage(50));
+ flow->enqueued(msgs_50.back()); // count:11 size: 1450 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ msgs_1000.push_back(createMessage(1000));
+ flow->enqueued(msgs_1000.back()); // count:12 size: 2450 (both thresholds crossed)
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ // at this point: 9@100 + 1@500 + 1@1000 + 1@50 == 12@2450
+
+ flow->dequeued(msgs_500.front()); // count:11 size:1950
+ msgs_500.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 9; i++) {
+ flow->dequeued(msgs_100.front());
+ msgs_100.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+ }
+ // count:2 size:1050
+ flow->dequeued(msgs_50.front()); // count:1 size:1000
+ msgs_50.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // still active due to size
+
+ flow->dequeued(msgs_1000.front()); // count:0 size:0 ->OFF
+ msgs_1000.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowDefaultArgs)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 80, // 80% stop threshold
+ 70); // 70% resume threshold
+ FieldTable args;
+ boost::shared_ptr<QueueFlowLimit> flow = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(flow);
+
+ BOOST_CHECK_EQUAL((uint64_t) 2360001, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 2065000, flow->getFlowResumeSize());
+ BOOST_CHECK_EQUAL( 0u, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL( 0u, flow->getFlowResumeCount());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowOverrideArgs)
+{
+ QueueFlowLimit::setDefaults(0, // max queue byte count
+ 80, // 80% stop threshold
+ 70); // 70% resume threshold
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 35000);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 30000);
+// args.setInt(QueueFlowLimit::flowStopSizeKey, 0);
+
+ boost::shared_ptr<QueueFlowLimit> flow = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(flow);
+
+ BOOST_CHECK_EQUAL((uint32_t) 35000, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 30000, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 0, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 0, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 350000);
+ args.setInt(QueueFlowLimit::flowResumeSizeKey, 300000);
+
+ boost::shared_ptr<QueueFlowLimit> flow = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(flow);
+
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 350000, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 300000, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 35000);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 30000);
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 350000);
+ args.setInt(QueueFlowLimit::flowResumeSizeKey, 300000);
+
+ boost::shared_ptr<QueueFlowLimit> flow = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(flow);
+
+ BOOST_CHECK_EQUAL((uint32_t) 35000, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 30000, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 350000, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 300000, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowOverrideDefaults)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 97, // stop threshold
+ 73); // resume threshold
+ FieldTable args;
+ boost::shared_ptr<QueueFlowLimit> flow = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(flow);
+
+ BOOST_CHECK_EQUAL((uint32_t) 2861501, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 2153500, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowDisable)
+{
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 0);
+ boost::shared_ptr<QueueFlowLimit> flow = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(!flow);
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueOptionsTest.cpp b/qpid/cpp/src/tests/QueueOptionsTest.cpp
new file mode 100644
index 0000000000..bdb83d7d22
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueOptionsTest.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 <iostream>
+#include "qpid/framing/Array.h"
+#include "qpid/client/QueueOptions.h"
+
+#include "unit_test.h"
+
+using namespace qpid::client;
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueOptionsTestSuite)
+
+QPID_AUTO_TEST_CASE(testSizePolicy)
+{
+ QueueOptions ft;
+
+ ft.setSizePolicy(REJECT,1,2);
+
+ BOOST_CHECK(QueueOptions::strREJECT == ft.getAsString(QueueOptions::strTypeKey));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey));
+
+ ft.setSizePolicy(FLOW_TO_DISK,0,2);
+ BOOST_CHECK(QueueOptions::strFLOW_TO_DISK == ft.getAsString(QueueOptions::strTypeKey));
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(2 == ft.getAsInt(QueueOptions::strMaxCountKey));
+
+ ft.setSizePolicy(RING,1,0);
+ BOOST_CHECK(QueueOptions::strRING == ft.getAsString(QueueOptions::strTypeKey));
+
+ ft.setSizePolicy(RING_STRICT,1,0);
+ BOOST_CHECK(QueueOptions::strRING_STRICT == ft.getAsString(QueueOptions::strTypeKey));
+
+ ft.clearSizePolicy();
+ BOOST_CHECK(!ft.isSet(QueueOptions::strTypeKey));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strMaxSizeKey));
+ BOOST_CHECK(!ft.isSet(QueueOptions::strMaxCountKey));
+}
+
+QPID_AUTO_TEST_CASE(testFlags)
+{
+ QueueOptions ft;
+
+ ft.setOrdering(LVQ);
+ BOOST_CHECK(1 == ft.getAsInt(QueueOptions::strLastValueQueue));
+ ft.setOrdering(FIFO);
+ 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_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueuePolicyTest.cpp b/qpid/cpp/src/tests/QueuePolicyTest.cpp
new file mode 100644
index 0000000000..f61c283fd4
--- /dev/null
+++ b/qpid/cpp/src/tests/QueuePolicyTest.cpp
@@ -0,0 +1,300 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <sstream>
+#include "unit_test.h"
+#include "test_tools.h"
+
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "BrokerFixture.h"
+
+#include <boost/format.hpp>
+
+using namespace qpid::broker;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite)
+
+QPID_AUTO_TEST_CASE(testRingPolicyCount)
+{
+ QueueOptions args;
+ args.setSizePolicy(RING, 0, 5);
+
+ SessionFixture f;
+ std::string q("my-ring-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ client::Message msg;
+ for (int i = 5; i < 10; i++) {
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+
+ for (int i = 10; i < 20; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 15; i < 20; i++) {
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL((boost::format("%1%_%2%") % "Message" % (i+1)).str(), msg.getData());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+}
+
+QPID_AUTO_TEST_CASE(testRingPolicySize)
+{
+ //The message size now includes all headers as well as the content
+ //aka body, so compute the amount of data needed to hit a given
+ //overall size
+ std::string q("my-ring-queue");
+ size_t minMessageSize = 25/*minimum size of headers*/ + q.size()/*routing key length*/ + 4/*default exchange, added by broker*/;
+
+ std::string hundredBytes = std::string(100 - minMessageSize, 'h');
+ std::string fourHundredBytes = std::string (400 - minMessageSize, 'f');
+ std::string thousandBytes = std::string(1000 - minMessageSize, 't');
+
+ // Ring queue, 500 bytes maxSize
+
+ QueueOptions args;
+ args.setSizePolicy(RING, 500, 0);
+
+ SessionFixture f;
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+
+ // A. Send messages 0 .. 5, each 100 bytes
+
+ client::Message m(hundredBytes, q);
+
+ for (int i = 0; i < 6; i++) {
+ std::stringstream id;
+ id << i;
+ m.getMessageProperties().setCorrelationId(id.str());
+ f.session.messageTransfer(arg::content=m);
+ }
+
+ // should find 1 .. 5 on the queue, 0 is displaced by 5
+ client::Message msg;
+ for (int i = 1; i < 6; i++) {
+ std::stringstream id;
+ id << i;
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getMessageProperties().getCorrelationId(), id.str());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+
+ // B. Now make sure that one 400 byte message displaces four 100 byte messages
+
+ // Send messages 0 .. 5, each 100 bytes
+ for (int i = 0; i < 6; i++) {
+ client::Message m(hundredBytes, q);
+ std::stringstream id;
+ id << i;
+ m.getMessageProperties().setCorrelationId(id.str());
+ f.session.messageTransfer(arg::content=m);
+ }
+
+ // Now send one 400 byte message
+ client::Message m2(fourHundredBytes, q);
+ m2.getMessageProperties().setCorrelationId("6");
+ f.session.messageTransfer(arg::content=m2);
+
+ // expect to see 5, 6 on the queue
+ for (int i = 5; i < 7; i++) {
+ std::stringstream id;
+ id << i;
+ BOOST_CHECK(f.subs.get(msg, q, qpid::sys::TIME_SEC));
+ BOOST_CHECK_EQUAL(msg.getMessageProperties().getCorrelationId(), id.str());
+ }
+ BOOST_CHECK(!f.subs.get(msg, q));
+
+
+ // C. Try sending a 1000-byte message, should fail - exceeds maxSize of queue
+
+ client::Message m3(thousandBytes, q);
+ m3.getMessageProperties().setCorrelationId("6");
+ try {
+ ScopedSuppressLogging sl;
+ f.session.messageTransfer(arg::content=m3);
+ BOOST_FAIL("Ooops - successfully added a 1000 byte message to a 512 byte ring queue ...");
+ }
+ catch (...) {
+ }
+
+}
+
+
+QPID_AUTO_TEST_CASE(testStrictRingPolicy)
+{
+ QueueOptions args;
+ args.setSizePolicy(RING_STRICT, 0, 5);
+ args.setString("qpid.flow_stop_count", "0");
+
+ SessionFixture 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)
+{
+ QueueOptions args;
+ args.setSizePolicy(REJECT, 0, 5);
+
+ SessionFixture 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);
+ // Disable flow control, or else we'll never hit the max limit
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
+
+ SessionFixture f;
+ std::string q("my-queue");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ LocalQueue incoming;
+ SubscriptionSettings settings(FlowControl::unlimited());
+ settings.autoAck = 0; // no auto ack.
+ Subscription sub = f.subs.subscribe(incoming, q, settings);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ for (int i = 0; i < 5; i++) {
+ BOOST_CHECK_EQUAL(incoming.pop().getData(), (boost::format("%1%_%2%") % "Message" % (i+1)).str());
+ }
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ f.session.messageTransfer(arg::content=client::Message("Message_6", q));
+ BOOST_FAIL("expecting ResourceLimitExceededException.");
+ } catch (const ResourceLimitExceededException&) {}
+}
+
+QPID_AUTO_TEST_CASE(testPolicyFailureOnCommit)
+{
+ QueueOptions args;
+ args.setSizePolicy(REJECT, 0, 5);
+
+ SessionFixture f;
+ std::string q("q");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ f.session.txSelect();
+ for (int i = 0; i < 10; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ BOOST_CHECK_THROW(f.session.txCommit(), InternalErrorException);
+}
+
+QPID_AUTO_TEST_CASE(testCapacityConversion)
+{
+ FieldTable args;
+ args.setString("qpid.max_count", "5");
+ args.setString("qpid.flow_stop_count", "0");
+
+ SessionFixture f;
+ std::string q("q");
+ f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
+ for (int i = 0; i < 5; i++) {
+ f.session.messageTransfer(arg::content=client::Message((boost::format("%1%_%2%") % "Message" % (i+1)).str(), q));
+ }
+ 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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueRegistryTest.cpp b/qpid/cpp/src/tests/QueueRegistryTest.cpp
new file mode 100644
index 0000000000..364d66c525
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueRegistryTest.cpp
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "qpid/broker/Queue.h"
+#include "qpid/broker/QueueSettings.h"
+#include "unit_test.h"
+#include <string>
+
+using namespace qpid::broker;
+
+namespace qpid {
+namespace tests {
+
+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, QueueSettings());
+ 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, QueueSettings());
+ BOOST_CHECK_EQUAL(q, qc.first);
+ BOOST_CHECK(!qc.second);
+
+ qc = reg.declare(bar, QueueSettings());
+ q = qc.first;
+ BOOST_CHECK(q);
+ BOOST_CHECK_EQUAL(true, qc.second);
+ BOOST_CHECK_EQUAL(bar, q->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, QueueSettings());
+ reg.declare(bar, QueueSettings());
+ 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, QueueSettings());
+ 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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp
new file mode 100644
index 0000000000..ee9d37e76d
--- /dev/null
+++ b/qpid/cpp/src/tests/QueueTest.cpp
@@ -0,0 +1,629 @@
+ /*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "MessageUtils.h"
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/Exception.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/broker/FanOutExchange.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/Deliverable.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/client/QueueOptions.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/broker/QueueSettings.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Timer.h"
+
+#include <iostream>
+#include <vector>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+using namespace std;
+using boost::intrusive_ptr;
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+class TestConsumer : public virtual Consumer{
+public:
+ typedef boost::shared_ptr<TestConsumer> shared_ptr;
+
+ QueueCursor lastCursor;
+ Message lastMessage;
+ bool received;
+ TestConsumer(std::string name="test", bool acquire = true) : Consumer(name, acquire ? CONSUMER : BROWSER, ""), received(false) {};
+
+ virtual bool deliver(const QueueCursor& cursor, const Message& message){
+ lastCursor = cursor;
+ lastMessage = message;
+ received = true;
+ return true;
+ };
+ void notify() {}
+ void cancel() {}
+ void acknowledged(const DeliveryRecord&) {}
+ OwnershipToken* getSession() { return 0; }
+};
+
+class FailOnDeliver : public Deliverable
+{
+ Message msg;
+public:
+ FailOnDeliver() : msg(MessageUtils::createMessage()) {}
+ void deliverTo(const boost::shared_ptr<Queue>& queue)
+ {
+ throw Exception(QPID_MSG("Invalid delivery to " << queue->getName()));
+ }
+ Message& getMessage() { return msg; }
+};
+
+QPID_AUTO_TEST_SUITE(QueueTestSuite)
+
+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"));
+ 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);
+
+ //ensure the remaining exchanges don't still have the queue bound to them:
+ FailOnDeliver deliverable;
+ exchange1->route(deliverable);
+ exchange3->route(deliverable);
+}
+
+QPID_AUTO_TEST_CASE(testLVQ){
+
+ QueueSettings settings;
+ string key="key";
+ settings.lvqKey = key;
+ QueueFactory factory;
+ Queue::shared_ptr q(factory.create("my-queue", settings));
+
+ const char* values[] = { "a", "b", "c", "a"};
+ for (size_t i = 0; i < sizeof(values)/sizeof(values[0]); ++i) {
+ qpid::types::Variant::Map properties;
+ properties[key] = values[i];
+ q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+1)));
+ }
+ BOOST_CHECK_EQUAL(q->getMessageCount(), 3u);
+
+ TestConsumer::shared_ptr c(new TestConsumer("test", true));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(std::string("2"), c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(std::string("3"), c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(std::string("4"), c->lastMessage.getContent());
+
+
+ const char* values2[] = { "a", "b", "c"};
+ for (size_t i = 0; i < sizeof(values2)/sizeof(values2[0]); ++i) {
+ qpid::types::Variant::Map properties;
+ properties[key] = values[i];
+ q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+5)));
+ }
+ BOOST_CHECK_EQUAL(q->getMessageCount(), 3u);
+
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(std::string("5"), c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(std::string("6"), c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(std::string("7"), c->lastMessage.getContent());
+}
+
+QPID_AUTO_TEST_CASE(testLVQEmptyKey){
+
+ QueueSettings settings;
+ string key="key";
+ settings.lvqKey = key;
+ QueueFactory factory;
+ Queue::shared_ptr q(factory.create("my-queue", settings));
+
+
+ qpid::types::Variant::Map properties;
+ properties["key"] = "a";
+ q->deliver(MessageUtils::createMessage(properties, "one"));
+ properties.clear();
+ q->deliver(MessageUtils::createMessage(properties, "two"));
+ BOOST_CHECK_EQUAL(q->getMessageCount(), 2u);
+}
+
+void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0)
+{
+ for (uint i = 0; i < count; i++) {
+ Message m = MessageUtils::createMessage("exchange", "key", i % 2 ? oddTtl : evenTtl);
+ 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(0);
+ BOOST_CHECK_EQUAL(queue.getMessageCount(), 5u);
+}
+
+QPID_AUTO_TEST_CASE(testQueueCleaner) {
+ boost::shared_ptr<Poller> poller(new Poller);
+ Thread runner(poller.get());
+ Timer timer;
+ QueueRegistry queues;
+ Queue::shared_ptr queue = queues.declare("my-queue", QueueSettings()).first;
+ addMessagesToQueue(10, *queue, 200, 400);
+ BOOST_CHECK_EQUAL(queue->getMessageCount(), 10u);
+
+ QueueCleaner cleaner(queues, poller, &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);
+ poller->shutdown();
+ runner.join();
+}
+namespace {
+int getIntProperty(const Message& message, const std::string& key)
+{
+ qpid::types::Variant v = message.getProperty(key);
+ int i(0);
+ if (!v.isVoid()) i = v;
+ return i;
+}
+// helper for group tests
+void verifyAcquire( Queue::shared_ptr queue,
+ TestConsumer::shared_ptr c,
+ std::deque<QueueCursor>& results,
+ const std::string& expectedGroup,
+ const int expectedId )
+{
+ bool success = queue->dispatch(c);
+ BOOST_CHECK(success);
+ if (success) {
+ results.push_back(c->lastCursor);
+ std::string group = c->lastMessage.getPropertyAsString("GROUP-ID");
+ int id = getIntProperty(c->lastMessage, "MY-ID");
+ BOOST_CHECK_EQUAL( group, expectedGroup );
+ BOOST_CHECK_EQUAL( id, expectedId );
+ }
+}
+
+Message createGroupMessage(int id, const std::string& group)
+{
+ qpid::types::Variant::Map properties;
+ properties["GROUP-ID"] = group;
+ properties["MY-ID"] = id;
+ return MessageUtils::createMessage(properties);
+}
+}
+
+QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) {
+ //
+ // Verify that consumers of grouped messages own the groups once a message is acquired,
+ // and release the groups once all acquired messages have been dequeued or requeued
+ //
+ QueueSettings settings;
+ settings.shareGroups = 1;
+ settings.groupKey = "GROUP-ID";
+ QueueFactory factory;
+ Queue::shared_ptr queue(factory.create("my_queue", settings));
+
+ std::string groups[] = { std::string("a"), std::string("a"), std::string("a"),
+ std::string("b"), std::string("b"), std::string("b"),
+ std::string("c"), std::string("c"), std::string("c") };
+ for (int i = 0; i < 9; ++i) {
+ queue->deliver(createGroupMessage(i, groups[i]));
+ }
+
+ // Queue = a-0, a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ---, ---, ---, ---, ---, ---, ---, ---, ---,
+
+ BOOST_CHECK_EQUAL(uint32_t(9), queue->getMessageCount());
+
+ TestConsumer::shared_ptr c1(new TestConsumer("C1"));
+ TestConsumer::shared_ptr c2(new TestConsumer("C2"));
+
+ queue->consume(c1);
+ queue->consume(c2);
+
+ std::deque<QueueCursor> dequeMeC1;
+ std::deque<QueueCursor> dequeMeC2;
+
+
+ verifyAcquire(queue, c1, dequeMeC1, "a", 0 ); // c1 now owns group "a" (acquire a-0)
+ verifyAcquire(queue, c2, dequeMeC2, "b", 3 ); // c2 should now own group "b" (acquire b-3)
+
+ // now let c1 complete the 'a-0' message - this should free the 'a' group
+ queue->dequeue( 0, dequeMeC1.front() );
+ dequeMeC1.pop_front();
+
+ // Queue = a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ---, ---, ^C2, ^C2, ^C2, ---, ---, ---
+
+ // now c2 should pick up the next 'a-1', since it is oldest free
+ verifyAcquire(queue, c2, dequeMeC2, "a", 1 ); // c2 should now own groups "a" and "b"
+
+ // Queue = a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C2, ^C2, ^C2, ---, ---, ---
+
+ // c1 should only be able to snarf up the first "c" message now...
+ verifyAcquire(queue, c1, dequeMeC1, "c", 6 ); // should skip to the first "c"
+
+ // Queue = a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C2, ^C2, ^C2, ^C1, ^C1, ^C1
+
+ // hmmm... what if c2 now dequeues "b-3"? (now only has a-1 acquired)
+ queue->dequeue( 0, dequeMeC2.front() );
+ dequeMeC2.pop_front();
+
+ // Queue = a-1, a-2, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ---, ---, ^C1, ^C1, ^C1
+
+ // b group is free, c is owned by c1 - c1's next get should grab 'b-4'
+ verifyAcquire(queue, c1, dequeMeC1, "b", 4 );
+
+ // Queue = a-1, a-2, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C1, ^C1, ^C1, ^C1, ^C1
+
+ // c2 can now only grab a-2, and that's all
+ verifyAcquire(queue, c2, dequeMeC2, "a", 2 );
+
+ // now C2 can't get any more, since C1 owns "b" and "c" group...
+ bool gotOne = queue->dispatch(c2);
+ BOOST_CHECK( !gotOne );
+
+ // hmmm... what if c1 now dequeues "c-6"? (now only own's b-4)
+ queue->dequeue( 0, dequeMeC1.front() );
+ dequeMeC1.pop_front();
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C1, ^C1, ---, ---
+
+ // c2 can now grab c-7
+ verifyAcquire(queue, c2, dequeMeC2, "c", 7 );
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C1, ^C1, ^C2, ^C2
+
+ // what happens if C-2 "requeues" a-1 and a-2?
+ queue->release( dequeMeC2.front() );
+ dequeMeC2.pop_front();
+ queue->release( dequeMeC2.front() );
+ dequeMeC2.pop_front(); // now just has c-7 acquired
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ---, ---, ^C1, ^C1, ^C2, ^C2
+
+ // now c1 will grab a-1 and a-2...
+ verifyAcquire(queue, c1, dequeMeC1, "a", 1 );
+ verifyAcquire(queue, c1, dequeMeC1, "a", 2 );
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ^C1, ^C1, ^C1, ^C1, ^C2, ^C2
+
+ // c2 can now acquire c-8 only
+ verifyAcquire(queue, c2, dequeMeC2, "c", 8 );
+
+ // and c1 can get b-5
+ verifyAcquire(queue, c1, dequeMeC1, "b", 5 );
+
+ // should be no more acquire-able for anyone now:
+ gotOne = queue->dispatch(c1);
+ BOOST_CHECK( !gotOne );
+ gotOne = queue->dispatch(c2);
+ BOOST_CHECK( !gotOne );
+
+ // release all of C1's acquired messages, then cancel C1
+ while (!dequeMeC1.empty()) {
+ queue->release(dequeMeC1.front());
+ dequeMeC1.pop_front();
+ }
+ queue->cancel(c1);
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ---, ---, ---, ---, ^C2, ^C2
+
+ // b-4, a-1, a-2, b-5 all should be available, right?
+ verifyAcquire(queue, c2, dequeMeC2, "a", 1 );
+
+ while (!dequeMeC2.empty()) {
+ queue->dequeue(0, dequeMeC2.front());
+ dequeMeC2.pop_front();
+ }
+
+ // Queue = a-2, b-4, b-5
+ // Owners= ---, ---, ---
+
+ TestConsumer::shared_ptr c3(new TestConsumer("C3"));
+ queue->consume(c3);
+ std::deque<QueueCursor> dequeMeC3;
+
+ verifyAcquire(queue, c3, dequeMeC3, "a", 2 );
+ verifyAcquire(queue, c2, dequeMeC2, "b", 4 );
+
+ // Queue = a-2, b-4, b-5
+ // Owners= ^C3, ^C2, ^C2
+
+ gotOne = queue->dispatch(c3);
+ BOOST_CHECK( !gotOne );
+
+ verifyAcquire(queue, c2, dequeMeC2, "b", 5 );
+
+ while (!dequeMeC2.empty()) {
+ queue->dequeue(0, dequeMeC2.front());
+ dequeMeC2.pop_front();
+ }
+
+ // Queue = a-2,
+ // Owners= ^C3,
+ queue->deliver(createGroupMessage(9, "a"));
+
+ // Queue = a-2, a-9
+ // Owners= ^C3, ^C3
+
+ gotOne = queue->dispatch(c2);
+ BOOST_CHECK( !gotOne );
+
+ queue->deliver(createGroupMessage(10, "b"));
+
+ // Queue = a-2, a-9, b-10
+ // Owners= ^C3, ^C3, ----
+
+ verifyAcquire(queue, c2, dequeMeC2, "b", 10 );
+ verifyAcquire(queue, c3, dequeMeC3, "a", 9 );
+
+ gotOne = queue->dispatch(c3);
+ BOOST_CHECK( !gotOne );
+
+ queue->cancel(c2);
+ queue->cancel(c3);
+}
+
+
+QPID_AUTO_TEST_CASE(testGroupsMultiConsumerDefaults) {
+ //
+ // Verify that the same default group name is automatically applied to messages that
+ // do not specify a group name.
+ //
+ QueueSettings settings;
+ settings.shareGroups = 1;
+ settings.groupKey = "GROUP-ID";
+ QueueFactory factory;
+ Queue::shared_ptr queue(factory.create("my_queue", settings));
+
+ for (int i = 0; i < 3; ++i) {
+ qpid::types::Variant::Map properties;
+ // no "GROUP-ID" header
+ properties["MY-ID"] = i;
+ queue->deliver(MessageUtils::createMessage(properties));
+ }
+
+ // Queue = 0, 1, 2
+
+ BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount());
+
+ TestConsumer::shared_ptr c1(new TestConsumer("C1"));
+ TestConsumer::shared_ptr c2(new TestConsumer("C2"));
+
+ queue->consume(c1);
+ queue->consume(c2);
+
+ std::deque<QueueCursor> dequeMeC1;
+ std::deque<QueueCursor> dequeMeC2;
+
+ queue->dispatch(c1); // c1 now owns default group (acquired 0)
+ dequeMeC1.push_back(c1->lastCursor);
+ int id = getIntProperty(c1->lastMessage, "MY-ID");
+ BOOST_CHECK_EQUAL( id, 0 );
+
+ bool gotOne = queue->dispatch(c2); // c2 should get nothing
+ BOOST_CHECK( !gotOne );
+
+ queue->dispatch(c1); // c1 now acquires 1
+ dequeMeC1.push_back(c1->lastCursor);
+ id = getIntProperty(c1->lastMessage, "MY-ID");
+ BOOST_CHECK_EQUAL( id, 1 );
+
+ gotOne = queue->dispatch(c2); // c2 should still get nothing
+ BOOST_CHECK( !gotOne );
+
+ while (!dequeMeC1.empty()) {
+ queue->dequeue(0, dequeMeC1.front());
+ dequeMeC1.pop_front();
+ }
+
+ // now default group should be available...
+ queue->dispatch(c2); // c2 now owns default group (acquired 2)
+ id = c2->lastMessage.getProperty("MY-ID");
+ BOOST_CHECK_EQUAL( id, 2 );
+
+ gotOne = queue->dispatch(c1); // c1 should get nothing
+ BOOST_CHECK( !gotOne );
+
+ queue->cancel(c1);
+ queue->cancel(c2);
+}
+
+QPID_AUTO_TEST_CASE(testSetPositionFifo) {
+ Queue::shared_ptr q(new Queue("my-queue", true));
+ BOOST_CHECK_EQUAL(q->getPosition(), SequenceNumber(0));
+ for (int i = 0; i < 10; ++i)
+ q->deliver(MessageUtils::createMessage(qpid::types::Variant::Map(), boost::lexical_cast<string>(i+1)));
+
+ // Verify the front of the queue
+ TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Don't acquire
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(1u, c->lastMessage.getSequence()); // Numbered from 1
+ BOOST_CHECK_EQUAL("1", c->lastMessage.getContent());
+
+ // Verify the back of the queue
+ BOOST_CHECK_EQUAL(10u, q->getPosition());
+ BOOST_CHECK_EQUAL(10u, q->getMessageCount());
+
+ // Using setPosition to introduce a gap in sequence numbers.
+ q->setPosition(15);
+ BOOST_CHECK_EQUAL(10u, q->getMessageCount());
+ BOOST_CHECK_EQUAL(15u, q->getPosition());
+ q->deliver(MessageUtils::createMessage(qpid::types::Variant::Map(), "16"));
+
+ q->seek(*c, Queue::MessagePredicate(), 9);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(10u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("10", c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(16u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("16", c->lastMessage.getContent());
+
+ // Using setPosition to trunkcate the queue
+ q->setPosition(5);
+ BOOST_CHECK_EQUAL(5u, q->getMessageCount());
+ q->deliver(MessageUtils::createMessage(qpid::types::Variant::Map(), "6a"));
+ c = boost::shared_ptr<TestConsumer>(new TestConsumer("test", false)); // Don't acquire
+ q->seek(*c, Queue::MessagePredicate(), 4);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(5u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("5", c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(6u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("6a", c->lastMessage.getContent());
+ BOOST_CHECK(!q->dispatch(c)); // No more messages.
+}
+
+QPID_AUTO_TEST_CASE(testSetPositionLvq) {
+ QueueSettings settings;
+ string key="key";
+ settings.lvqKey = key;
+ QueueFactory factory;
+ Queue::shared_ptr q(factory.create("my-queue", settings));
+
+ const char* values[] = { "a", "b", "c", "a", "b", "c" };
+ for (size_t i = 0; i < sizeof(values)/sizeof(values[0]); ++i) {
+ qpid::types::Variant::Map properties;
+ properties[key] = values[i];
+ q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+1)));
+ }
+ BOOST_CHECK_EQUAL(3u, q->getMessageCount());
+ // Verify the front of the queue
+ TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Don't acquire
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(4u, c->lastMessage.getSequence()); // Numbered from 1
+ BOOST_CHECK_EQUAL("4", c->lastMessage.getContent());
+ // Verify the back of the queue
+ BOOST_CHECK_EQUAL(6u, q->getPosition());
+
+ q->setPosition(5);
+
+ c = boost::shared_ptr<TestConsumer>(new TestConsumer("test", false)); // Don't acquire
+ q->seek(*c, Queue::MessagePredicate(), 4);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(5u, c->lastMessage.getSequence()); // Numbered from 1
+ BOOST_CHECK(!q->dispatch(c));
+}
+
+QPID_AUTO_TEST_CASE(testSetPositionPriority) {
+ QueueSettings settings;
+ settings.priorities = 10;
+ QueueFactory factory;
+ Queue::shared_ptr q(factory.create("my-queue", settings));
+
+ const int priorities[] = { 1, 2, 3, 2, 1, 3 };
+ for (size_t i = 0; i < sizeof(priorities)/sizeof(priorities[0]); ++i) {
+ qpid::types::Variant::Map properties;
+ properties["priority"] = priorities[i];
+ q->deliver(MessageUtils::createMessage(properties, boost::lexical_cast<string>(i+1)));
+ }
+
+ // Truncation removes messages in fifo order, not priority order.
+ q->setPosition(3);
+ TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Browse in priority order
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(3u, c->lastMessage.getSequence());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(2u, c->lastMessage.getSequence());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(1u, c->lastMessage.getSequence());
+ BOOST_CHECK(!q->dispatch(c));
+
+ qpid::types::Variant::Map properties;
+ properties["priority"] = 4;
+ q->deliver(MessageUtils::createMessage(properties, "4a"));
+
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(4u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("4a", c->lastMessage.getContent());
+
+ // But consumers see priority order
+ c.reset(new TestConsumer("test", true));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(4u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("4a", c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(3u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("3", c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(2u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("2", c->lastMessage.getContent());
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(1u, c->lastMessage.getSequence());
+ BOOST_CHECK_EQUAL("1", c->lastMessage.getContent());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/README.txt b/qpid/cpp/src/tests/README.txt
new file mode 100644
index 0000000000..8eaa5bbd25
--- /dev/null
+++ b/qpid/cpp/src/tests/README.txt
@@ -0,0 +1,37 @@
+= Running Qpid C++ tests =
+
+General philosophy is that "make test" run all tests by default, but
+developers can run tests selectively as explained below.
+
+== Unit Tests ==
+
+Unit tests use the boost test framework, and are compiled to the programd
+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
+
+== System Tests ==
+
+System tests are executables or scripts. You can run executable tests directly
+as well as via "make test" or "ctest". Some tests require environment settings
+which are set by src/tests/test_env.sh on Unix or by src/tests/test_env.ps1 on
+Windows.
+
+./python_tests: runs ../python/run_tests. This is the main set of
+system tests for the broker.
+
+Other C++ client test executables and scripts under client/test are
+system tests for the client.
+
+== Running selected tests ==
+
+The make target "make test" simply runs the command "ctest". Running ctest
+directly gives you additional options, e.g.
+
+ ctest -R <regexp> -VV
+
+This runs tests with names matching the regular expression <regexp> and will
+print the full output of the tests rather than just listing which tests pass or
+fail.
+
diff --git a/qpid/cpp/src/tests/RangeSet.cpp b/qpid/cpp/src/tests/RangeSet.cpp
new file mode 100644
index 0000000000..285f432bf7
--- /dev/null
+++ b/qpid/cpp/src/tests/RangeSet.cpp
@@ -0,0 +1,154 @@
+/*
+ *
+ * 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;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(RangeSetTestSuite)
+
+typedef qpid::Range<int> TR; // Test Range
+typedef RangeSet<int> TRSet;
+
+QPID_AUTO_TEST_CASE(testEmptyRange) {
+ TR r;
+ BOOST_CHECK_EQUAL(r, TR(0,0));
+ BOOST_CHECK(r.empty());
+ BOOST_CHECK(!r.contains(0));
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetAddPoint) {
+ TRSet r;
+ BOOST_CHECK(r.empty());
+ r += 3;
+ BOOST_CHECK_MESSAGE(r.contains(3), r);
+ BOOST_CHECK_MESSAGE(r.contains(TR(3,4)), r);
+ BOOST_CHECK(!r.empty());
+ r += 5;
+ BOOST_CHECK_MESSAGE(r.contains(5), r);
+ BOOST_CHECK_MESSAGE(r.contains(TR(5,6)), r);
+ BOOST_CHECK_MESSAGE(!r.contains(TR(3,6)), r);
+ r += 4;
+ BOOST_CHECK_MESSAGE(r.contains(TR(3,6)), r);
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetAddRange) {
+ TRSet r;
+ r += TR(0,3);
+ BOOST_CHECK(r.contains(TR(0,3)));
+ BOOST_CHECK(r.contiguous());
+ r += TR(4,6);
+ BOOST_CHECK(!r.contiguous());
+ BOOST_CHECK_MESSAGE(r.contains(TR(4,6)), r);
+ r += 3;
+ BOOST_CHECK_MESSAGE(r.contains(TR(0,6)), r);
+ BOOST_CHECK(r.front() == 0);
+ BOOST_CHECK(r.back() == 6);
+
+ // Merging additions
+ r = TRSet(0,3)+TR(5,6);
+ TRSet e(0,6);
+ BOOST_CHECK_EQUAL(r + TR(3,5), e);
+ BOOST_CHECK(e.contiguous());
+ r = TRSet(0,5)+TR(10,15)+TR(20,25)+TR(30,35)+TR(40,45);
+ BOOST_CHECK_EQUAL(r + TR(11,37), TRSet(0,5)+TR(11,37)+TR(40,45));
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetAddSet) {
+ TRSet r;
+ TRSet s = TRSet(0,3)+TR(5,10);
+ r += s;
+ BOOST_CHECK_EQUAL(r,s);
+ r += TRSet(3,5) + TR(7,12) + 15;
+ BOOST_CHECK_EQUAL(r, TRSet(0,12) + 15);
+
+ r.clear();
+ BOOST_CHECK(r.empty());
+ r += TR::makeClosed(6,10);
+ BOOST_CHECK_EQUAL(r, TRSet(6,11));
+ r += TRSet(2,6)+8;
+ BOOST_CHECK_EQUAL(r, TRSet(2,11));
+}
+
+QPID_AUTO_TEST_CASE(testRangeSetIterate) {
+ TRSet r = TRSet(1,3)+TR(4,7)+TR(10,11);
+ 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(TRSet(0,5)-3, TRSet(0,3)+TR(4,5));
+ BOOST_CHECK_EQUAL(TRSet(1,5)-5, TRSet(1,5));
+ BOOST_CHECK_EQUAL(TRSet(1,5)-0, TRSet(1,5));
+
+ TRSet r(TRSet(0,5)+TR(10,15)+TR(20,25));
+
+ // TRs
+ BOOST_CHECK_EQUAL(r-TR(0,5), TRSet(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(10,15), TRSet(0,5)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(20,25), TRSet(0,5)+TR(10,15));
+
+ BOOST_CHECK_EQUAL(r-TR(-5, 30), TRSet());
+
+ BOOST_CHECK_EQUAL(r-TR(-5, 7), TRSet(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(8,19), TRSet(0,5)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(17,30), TRSet(0,5)+TR(10,15));
+
+ BOOST_CHECK_EQUAL(r-TR(-5, 5), TRSet(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(10,19), TRSet(0,5)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(18,25), TRSet(0,5)+TR(10,15));
+ BOOST_CHECK_EQUAL(r-TR(23,25), TRSet(0,5)+TR(10,15)+TR(20,23));
+
+ BOOST_CHECK_EQUAL(r-TR(-3, 3), TRSet(3,5)+TR(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(3, 7), TRSet(0,2)+TR(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(3, 12), TRSet(0,3)+TR(12,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(3, 22), TRSet(12,15)+TR(22,25));
+ BOOST_CHECK_EQUAL(r-TR(12, 22), TRSet(0,5)+TR(10,11)+TR(22,25));
+
+ // Sets
+ BOOST_CHECK_EQUAL(r-(TRSet(-1,6)+TR(11,14)+TR(23,25)),
+ TRSet(10,11)+TR(14,15)+TR(20,23));
+ // Split the ranges
+ BOOST_CHECK_EQUAL(r-(TRSet(2,3)+TR(11,13)+TR(21,23)),
+ TRSet(0,2)+TR(4,5)+
+ TR(10,11)+TR(14,15)+
+ TR(20,21)+TR(23,25));
+ // Truncate the ranges
+ BOOST_CHECK_EQUAL(r-(TRSet(0,3)+TR(13,15)+TR(19,23)),
+ TRSet(3,5)+TR(10,13)+TR(20,23));
+ // Remove multiple ranges with truncation
+ BOOST_CHECK_EQUAL(r-(TRSet(3,23)), TRSet(0,3)+TR(23,25));
+ // Remove multiple ranges in middle
+ TRSet r2 = TRSet(0,5)+TR(10,15)+TR(20,25)+TR(30,35);
+ BOOST_CHECK_EQUAL(r2-TRSet(11,24),
+ TRSet(0,5)+TR(10,11)+TR(24,25)+TR(30,35));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/RefCounted.cpp b/qpid/cpp/src/tests/RefCounted.cpp
new file mode 100644
index 0000000000..3ac3895322
--- /dev/null
+++ b/qpid/cpp/src/tests/RefCounted.cpp
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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"
+
+namespace qpid {
+namespace tests {
+
+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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/RetryList.cpp b/qpid/cpp/src/tests/RetryList.cpp
new file mode 100644
index 0000000000..50cd5edfe8
--- /dev/null
+++ b/qpid/cpp/src/tests/RetryList.cpp
@@ -0,0 +1,111 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/broker/RetryList.h"
+
+using namespace qpid;
+using namespace qpid::broker;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(RetryListTestSuite)
+
+struct RetryListFixture
+{
+ RetryList list;
+ std::vector<Url> urls;
+ std::vector<Address> expected;
+
+ void addUrl(const std::string& s)
+ {
+ urls.push_back(Url(s));
+ }
+
+ void addExpectation(const std::string& host, uint16_t port)
+ {
+ expected.push_back(Address("tcp", host, port));
+ }
+
+ void check()
+ {
+ list.reset(urls);
+ for (int t = 0; t < 2; t++) {
+ Address next;
+ for (std::vector<Address>::const_iterator i = expected.begin(); i != expected.end(); ++i) {
+ BOOST_CHECK(list.next(next));
+ BOOST_CHECK_EQUAL(i->host, next.host);
+ BOOST_CHECK_EQUAL(i->port, next.port);
+ }
+ BOOST_CHECK(!list.next(next));
+ }
+ }
+};
+
+QPID_AUTO_TEST_CASE(testWithSingleAddress)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:host:5673");
+ test.addExpectation("host", 5673);
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testWithSingleUrlOfMultipleAddresses)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:host1,host2:2222,tcp:host3:5673,host4:1");
+
+ test.addExpectation("host1", 5672);
+ test.addExpectation("host2", 2222);
+ test.addExpectation("host3", 5673);
+ test.addExpectation("host4", 1);
+
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testWithMultipleUrlsOfMultipleAddresses)
+{
+ RetryListFixture test;
+ test.addUrl("amqp:my-host");
+ test.addUrl("amqp:host1:6666,host2:2222,tcp:host3:5673,host4:1");
+ test.addUrl("amqp:host5,host6:2222,tcp:host7:5673");
+
+ test.addExpectation("my-host", 5672);
+ test.addExpectation("host1", 6666);
+ test.addExpectation("host2", 2222);
+ test.addExpectation("host3", 5673);
+ test.addExpectation("host4", 1);
+ test.addExpectation("host5", 5672);
+ test.addExpectation("host6", 2222);
+ test.addExpectation("host7", 5673);
+
+ test.check();
+}
+
+QPID_AUTO_TEST_CASE(testEmptyList)
+{
+ RetryListFixture test;
+ test.check();
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Selector.cpp b/qpid/cpp/src/tests/Selector.cpp
new file mode 100644
index 0000000000..73af1a9623
--- /dev/null
+++ b/qpid/cpp/src/tests/Selector.cpp
@@ -0,0 +1,442 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/SelectorToken.h"
+#include "qpid/broker/Selector.h"
+#include "qpid/broker/SelectorValue.h"
+
+#include "unit_test.h"
+
+#include <string>
+#include <map>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+using std::string;
+using std::map;
+using std::vector;
+
+namespace qb = qpid::broker;
+
+using qpid::broker::Token;
+using qpid::broker::TokenType;
+using qpid::broker::Tokeniser;
+using qpid::broker::tokenise;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(SelectorSuite)
+
+typedef bool (*TokeniseF)(string::const_iterator&,string::const_iterator&,Token&);
+
+bool tokeniseEos(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
+{
+ Token t1;
+ std::string::const_iterator t = s;
+ bool r = tokenise(t, e, t1);
+ if (r && (t1.type==qb::T_EOS)) {tok = t1; s = t; return true;}
+ return false;
+}
+
+bool tokeniseParens(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
+{
+ Token t1;
+ std::string::const_iterator t = s;
+ bool r = tokenise(t, e, t1);
+ if (r && (t1.type==qb::T_LPAREN || t1.type==qb::T_RPAREN)) {tok = t1; s = t; return true;}
+ return false;
+}
+
+bool tokeniseOperator(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
+{
+ Token t1;
+ std::string::const_iterator t = s;
+ bool r = tokenise(t, e, t1);
+ if (r && (t1.type>=qb::T_PLUS && t1.type<=qb::T_GREQ)) {tok = t1; s = t; return true;}
+ return false;
+}
+
+bool tokeniseString(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
+{
+ Token t1;
+ std::string::const_iterator t = s;
+ bool r = tokenise(t, e, t1);
+ if (r && (t1.type==qb::T_STRING)) {tok = t1; s = t; return true;}
+ return false;
+}
+
+bool tokeniseIdentifier(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
+{
+ Token t1;
+ std::string::const_iterator t = s;
+ bool r = tokenise(t, e, t1);
+ if (r && (t1.type==qb::T_IDENTIFIER)) {tok = t1; s = t; return true;}
+ return false;
+}
+
+bool tokeniseReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
+{
+ std::string::const_iterator t = s;
+ Token t1;
+ if (tokenise(t, e, t1)) {
+ switch (t1.type) {
+ case qb::T_AND:
+ case qb::T_BETWEEN:
+ case qb::T_ESCAPE:
+ case qb::T_FALSE:
+ case qb::T_IN:
+ case qb::T_IS:
+ case qb::T_LIKE:
+ case qb::T_NOT:
+ case qb::T_NULL:
+ case qb::T_OR:
+ case qb::T_TRUE:
+ tok = t1;
+ s = t;
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
+{
+ Token t1;
+ std::string::const_iterator t = s;
+ bool r = tokenise(t, e, t1);
+ if (r && (t1.type==qb::T_NUMERIC_EXACT || t1.type==qb::T_NUMERIC_APPROX)) {tok = t1; s = t; return true;}
+ return false;
+}
+
+
+void verifyTokeniserSuccess(TokeniseF t, const char* ss, TokenType tt, const char* tv, const char* fs) {
+ Token tok;
+ string s(ss);
+ string::const_iterator sb = s.begin();
+ string::const_iterator se = s.end();
+ BOOST_CHECK(t(sb, se, tok));
+ BOOST_CHECK_EQUAL(tok, Token(tt, tv));
+ BOOST_CHECK_EQUAL(string(sb, se), fs);
+}
+
+void verifyTokeniserFail(TokeniseF t, const char* c) {
+ Token tok;
+ string s(c);
+ string::const_iterator sb = s.begin();
+ string::const_iterator se = s.end();
+ BOOST_CHECK(!t(sb, se, tok));
+ BOOST_CHECK_EQUAL(string(sb, se), c);
+}
+
+QPID_AUTO_TEST_CASE(tokeniseSuccess)
+{
+ verifyTokeniserSuccess(&tokenise, "", qb::T_EOS, "", "");
+ verifyTokeniserSuccess(&tokenise, " ", qb::T_EOS, "", "");
+ verifyTokeniserSuccess(&tokenise, "null_123+blah", qb::T_IDENTIFIER, "null_123", "+blah");
+ verifyTokeniserSuccess(&tokenise, "\"null-123\"+blah", qb::T_IDENTIFIER, "null-123", "+blah");
+ verifyTokeniserSuccess(&tokenise, "\"This is an \"\"odd!\"\" identifier\"+blah", qb::T_IDENTIFIER, "This is an \"odd!\" identifier", "+blah");
+ verifyTokeniserSuccess(&tokenise, "null+blah", qb::T_NULL, "null", "+blah");
+ verifyTokeniserSuccess(&tokenise, "null+blah", qb::T_NULL, "null", "+blah");
+ verifyTokeniserSuccess(&tokenise, "Is nOt null", qb::T_IS, "Is", " nOt null");
+ verifyTokeniserSuccess(&tokenise, "nOt null", qb::T_NOT, "nOt", " null");
+ verifyTokeniserSuccess(&tokenise, "Is nOt null", qb::T_IS, "Is", " nOt null");
+ verifyTokeniserSuccess(&tokenise, "'Hello World'", qb::T_STRING, "Hello World", "");
+ verifyTokeniserSuccess(&tokenise, "'Hello World''s end'a bit more", qb::T_STRING, "Hello World's end", "a bit more");
+ verifyTokeniserSuccess(&tokenise, "=blah", qb::T_EQUAL, "=", "blah");
+ verifyTokeniserSuccess(&tokenise, "<> Identifier", qb::T_NEQ, "<>", " Identifier");
+ verifyTokeniserSuccess(&tokenise, "(a and b) not c", qb::T_LPAREN, "(", "a and b) not c");
+ verifyTokeniserSuccess(&tokenise, ") not c", qb::T_RPAREN, ")", " not c");
+ verifyTokeniserSuccess(&tokenise, "019kill", qb::T_NUMERIC_EXACT, "019", "kill");
+ verifyTokeniserSuccess(&tokenise, "0kill", qb::T_NUMERIC_EXACT, "0", "kill");
+ verifyTokeniserSuccess(&tokenise, "0.kill", qb::T_NUMERIC_APPROX, "0.", "kill");
+ verifyTokeniserSuccess(&tokenise, "3.1415=pi", qb::T_NUMERIC_APPROX, "3.1415", "=pi");
+ verifyTokeniserSuccess(&tokenise, ".25.kill", qb::T_NUMERIC_APPROX, ".25", ".kill");
+ verifyTokeniserSuccess(&tokenise, "2e5.kill", qb::T_NUMERIC_APPROX, "2e5", ".kill");
+ verifyTokeniserSuccess(&tokenise, "3.e50easy to kill", qb::T_NUMERIC_APPROX, "3.e50", "easy to kill");
+ verifyTokeniserSuccess(&tokenise, "34.25e+50easy to kill", qb::T_NUMERIC_APPROX, "34.25e+50", "easy to kill");
+ verifyTokeniserSuccess(&tokenise, "34.e-50easy to kill", qb::T_NUMERIC_APPROX, "34.e-50", "easy to kill");
+}
+
+QPID_AUTO_TEST_CASE(tokeniseFailure)
+{
+ verifyTokeniserFail(&tokeniseEos, "hb23");
+ verifyTokeniserFail(&tokeniseIdentifier, "123");
+ verifyTokeniserFail(&tokeniseIdentifier, "'Embedded 123'");
+ verifyTokeniserFail(&tokeniseReservedWord, "1.2e5");
+ verifyTokeniserFail(&tokeniseReservedWord, "'Stringy thing'");
+ verifyTokeniserFail(&tokeniseReservedWord, "oR_andsomething");
+ verifyTokeniserFail(&tokeniseString, "'Embedded 123");
+ verifyTokeniserFail(&tokeniseString, "'This isn''t fair");
+ verifyTokeniserFail(&tokeniseOperator, "123");
+ verifyTokeniserFail(&tokeniseOperator, "'Stringy thing'");
+ verifyTokeniserFail(&tokeniseOperator, "NoT");
+ verifyTokeniserFail(&tokeniseOperator, "(a and b)");
+ verifyTokeniserFail(&tokeniseOperator, ")");
+ verifyTokeniserFail(&tokeniseParens, "=");
+ verifyTokeniserFail(&tokeniseParens, "what ho!");
+ verifyTokeniserFail(&tokeniseNumeric, "kill");
+ verifyTokeniserFail(&tokeniseNumeric, "e3");
+ verifyTokeniserFail(&tokeniseNumeric, "1.e.5");
+ verifyTokeniserFail(&tokeniseNumeric, ".e5");
+ verifyTokeniserFail(&tokeniseNumeric, "34e");
+ verifyTokeniserFail(&tokeniseNumeric, ".3e+");
+ verifyTokeniserFail(&tokeniseNumeric, ".3e-.");
+}
+
+QPID_AUTO_TEST_CASE(tokenString)
+{
+
+ string exp(" a =b");
+ string::const_iterator s = exp.begin();
+ string::const_iterator e = exp.end();
+ Tokeniser t(s, e);
+
+ BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_IDENTIFIER, "a"));
+ BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_EQUAL, "="));
+ BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_IDENTIFIER, "b"));
+ BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_EOS, ""));
+
+ exp = " not 'hello kitty''s friend' = Is null ";
+ s = exp.begin();
+ e = exp.end();
+ Tokeniser u(s, e);
+
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_NOT, "not"));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_STRING, "hello kitty's friend"));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EQUAL, "="));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_IS, "Is"));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_NULL, "null"));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
+
+ u.returnTokens(3);
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_IS, "Is"));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_NULL, "null"));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
+ BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
+
+ exp = "(a+6)*7.5/1e6";
+ s = exp.begin();
+ e = exp.end();
+ Tokeniser v(s, e);
+
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_LPAREN, "("));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_IDENTIFIER, "a"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_PLUS, "+"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_EXACT, "6"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_RPAREN, ")"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_MULT, "*"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_APPROX, "7.5"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_DIV, "/"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_APPROX, "1e6"));
+}
+
+QPID_AUTO_TEST_CASE(parseStringFail)
+{
+ BOOST_CHECK_THROW(qb::Selector e("hello world"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("hello ^ world"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A is null not"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A is null or not"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A is null or and"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A is null and (B='hello out there'"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("in='hello kitty'"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A like 234"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A not 234 escape"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A not like 'eclecti_' escape 'happy'"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A not like 'eclecti_' escape happy"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A not like 'eclecti_' escape '%'"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A BETWEEN AND 'true'"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A NOT BETWEEN 34 OR 3.9"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A IN ()"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A NOT IN ()"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A IN 'hello', 'there', 1, true, (1-17))"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A IN ('hello', 'there' 1, true, (1-17))"), std::range_error);
+}
+
+QPID_AUTO_TEST_CASE(parseString)
+{
+ BOOST_CHECK_NO_THROW(qb::Selector e("'Daft' is not null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("42 is null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A is not null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A is null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A = C"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A <> C"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A='hello kitty'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A<>'hello kitty'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A=B"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A<>B"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A='hello kitty' OR B='Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("B='hello kitty' AnD A='Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A is null or A='Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("Z is null OR A is not null and A<>'Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("(Z is null OR A is not null) and A<>'Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("NOT C is not null OR C is null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("Not A='' or B=z"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("Not A=17 or B=5.6"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A<>17 and B=5.6e17"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A LIKE 'excep%ional'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("B NOT LIKE 'excep%ional'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A LIKE 'excep%ional' EScape '\'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A BETWEEN 13 AND 'true'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A NOT BETWEEN 100 AND 3.9"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("true"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("-354"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("-(X or Y)"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("-687 or 567"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("(354.6)"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A is null and 'hello out there'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("17/4>4"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("17/4>+4"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("17/4>-4"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A IN ('hello', 'there', 1 , true, (1-17))"));
+}
+
+class TestSelectorEnv : public qpid::broker::SelectorEnv {
+ mutable map<string, qb::Value> values;
+ boost::ptr_vector<string> strings;
+ static const qb::Value EMPTY;
+
+ const qb::Value& value(const string& v) const {
+ const qb::Value& r = values.find(v)!=values.end() ? values[v] : EMPTY;
+ return r;
+ }
+
+public:
+ void set(const string& id, const char* value) {
+ strings.push_back(new string(value));
+ values[id] = strings[strings.size()-1];
+ }
+
+ void set(const string& id, const qb::Value& value) {
+ if (value.type==qb::Value::T_STRING) {
+ strings.push_back(new string(*value.s));
+ values[id] = strings[strings.size()-1];
+ } else {
+ values[id] = value;
+ }
+ }
+};
+
+const qb::Value TestSelectorEnv::EMPTY;
+
+QPID_AUTO_TEST_CASE(simpleEval)
+{
+ TestSelectorEnv env;
+ env.set("A", "Bye, bye cruel world");
+ env.set("B", "hello kitty");
+
+ BOOST_CHECK(qb::Selector("").eval(env));
+ BOOST_CHECK(qb::Selector(" ").eval(env));
+ BOOST_CHECK(qb::Selector("A is not null").eval(env));
+ BOOST_CHECK(!qb::Selector("A is null").eval(env));
+ BOOST_CHECK(!qb::Selector("A = C").eval(env));
+ BOOST_CHECK(!qb::Selector("A <> C").eval(env));
+ BOOST_CHECK(!qb::Selector("C is not null").eval(env));
+ BOOST_CHECK(qb::Selector("C is null").eval(env));
+ BOOST_CHECK(qb::Selector("A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("A<>'Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("A='hello kitty'").eval(env));
+ BOOST_CHECK(qb::Selector("A<>'hello kitty'").eval(env));
+ BOOST_CHECK(!qb::Selector("A=B").eval(env));
+ BOOST_CHECK(qb::Selector("A<>B").eval(env));
+ BOOST_CHECK(!qb::Selector("A='hello kitty' OR B='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("B='hello kitty' OR A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("B='hello kitty' AnD A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("B='hello kitty' AnD B='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("A is null or A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("Z is null OR A is not null and A<>'Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("(Z is null OR A is not null) and A<>'Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("NOT C is not null OR C is null").eval(env));
+ BOOST_CHECK(qb::Selector("Not A='' or B=z").eval(env));
+ BOOST_CHECK(qb::Selector("Not A=17 or B=5.6").eval(env));
+ BOOST_CHECK(!qb::Selector("A<>17 and B=5.6e17").eval(env));
+ BOOST_CHECK(!qb::Selector("C=D").eval(env));
+ BOOST_CHECK(qb::Selector("13 is not null").eval(env));
+ BOOST_CHECK(!qb::Selector("'boo!' is null").eval(env));
+ BOOST_CHECK(qb::Selector("A LIKE '%cru_l%'").eval(env));
+ BOOST_CHECK(qb::Selector("'_%%_hello.th_re%' LIKE 'z_%.%z_%z%' escape 'z'").eval(env));
+ BOOST_CHECK(qb::Selector("A NOT LIKE 'z_%.%z_%z%' escape 'z'").eval(env));
+ BOOST_CHECK(qb::Selector("'{}[]<>,.!\"$%^&*()_-+=?/|\\' LIKE '{}[]<>,.!\"$z%^&*()z_-+=?/|\\' escape 'z'").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(numericEval)
+{
+ TestSelectorEnv env;
+ env.set("A", 42.0);
+ env.set("B", 39);
+
+ BOOST_CHECK(qb::Selector("A>B").eval(env));
+ BOOST_CHECK(qb::Selector("A=42").eval(env));
+ BOOST_CHECK(qb::Selector("B=39.0").eval(env));
+ BOOST_CHECK(qb::Selector("Not A=17 or B=5.6").eval(env));
+ BOOST_CHECK(!qb::Selector("A<>17 and B=5.6e17").eval(env));
+ BOOST_CHECK(qb::Selector("3 BETWEEN -17 and 98.5").eval(env));
+ BOOST_CHECK(qb::Selector("A BETWEEN B and 98.5").eval(env));
+ BOOST_CHECK(!qb::Selector("B NOT BETWEEN 35 AND 100").eval(env));
+ BOOST_CHECK(!qb::Selector("A BETWEEN B and 40").eval(env));
+ BOOST_CHECK(!qb::Selector("A BETWEEN C and 40").eval(env));
+ BOOST_CHECK(!qb::Selector("A BETWEEN 45 and C").eval(env));
+ BOOST_CHECK(qb::Selector("(A BETWEEN 40 and C) IS NULL").eval(env));
+ BOOST_CHECK(qb::Selector("(A BETWEEN C and 45) IS NULL").eval(env));
+ BOOST_CHECK(qb::Selector("17/4=4").eval(env));
+ BOOST_CHECK(!qb::Selector("A/0=0").eval(env));
+ BOOST_CHECK(qb::Selector("A*B+19<A*(B+19)").eval(env));
+ BOOST_CHECK(qb::Selector("-A=0-A").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(comparisonEval)
+{
+ TestSelectorEnv env;
+
+ BOOST_CHECK(!qb::Selector("17 > 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello' > 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello' < 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello' = 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello'>42 and 'hello'<42 and 'hello'=42 and 'hello'<>42").eval(env));
+ BOOST_CHECK(qb::Selector("20 >= 19.0 and 20 > 19").eval(env));
+ BOOST_CHECK(qb::Selector("42 <= 42.0 and 37.0 >= 37").eval(env));
+ BOOST_CHECK(qb::Selector("(A IN ('hello', 'there', 1 , true, (1-17))) IS NULL").eval(env));
+ BOOST_CHECK(qb::Selector("'hello' IN ('hello', 'there', 1 , true, (1-17))").eval(env));
+ BOOST_CHECK(qb::Selector("TRUE IN ('hello', 'there', 1 , true, (1-17))").eval(env));
+ BOOST_CHECK(qb::Selector("-16 IN ('hello', 'there', 1 , true, (1-17))").eval(env));
+ BOOST_CHECK(!qb::Selector("'hell' IN ('hello', 'there', 1 , true, (1-17))").eval(env));
+ BOOST_CHECK(qb::Selector("('hell' IN ('hello', 'there', 1 , true, (1-17), A)) IS NULL").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(NullEval)
+{
+ TestSelectorEnv env;
+
+ BOOST_CHECK(qb::Selector("P > 19.0 or (P is null)").eval(env));
+ BOOST_CHECK(qb::Selector("P is null or P=''").eval(env));
+ BOOST_CHECK(!qb::Selector("P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("not P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("not P=Q and not P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("P=Q or not P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("P > 19.0 or P <= 19.0").eval(env));
+ BOOST_CHECK(qb::Selector("P > 19.0 or 17 <= 19.0").eval(env));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}}
diff --git a/qpid/cpp/src/tests/SequenceNumberTest.cpp b/qpid/cpp/src/tests/SequenceNumberTest.cpp
new file mode 100644
index 0000000000..f3c934e3ca
--- /dev/null
+++ b/qpid/cpp/src/tests/SequenceNumberTest.cpp
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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;
+
+namespace qpid {
+namespace tests {
+
+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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/SequenceSet.cpp b/qpid/cpp/src/tests/SequenceSet.cpp
new file mode 100644
index 0000000000..bc0a8ea509
--- /dev/null
+++ b/qpid/cpp/src/tests/SequenceSet.cpp
@@ -0,0 +1,187 @@
+/*
+ *
+ * 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>
+
+namespace qpid {
+namespace tests {
+
+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_CASE(testOutOfOrderRemove) {
+
+ SequenceSet s(2, 20);
+
+ // test remove from middle:
+ s.remove(7);
+ RangeExpectations().expect(2, 6).expect(8, 20).check(s);
+ s.remove(14);
+ RangeExpectations().expect(2, 6).expect(8, 13).expect(15, 20).check(s);
+
+ // remove from front of subrange:
+ s.remove(8, 8);
+ RangeExpectations().expect(2, 6).expect(9, 13).expect(15, 20).check(s);
+
+ // remove from tail of subrange:
+ s.remove(6);
+ RangeExpectations().expect(2, 5).expect(9, 13).expect(15, 20).check(s);
+
+ // remove across subrange:
+ s.remove(13, 15);
+ RangeExpectations().expect(2, 5).expect(9, 12).expect(16, 20).check(s);
+
+ // remove within subrange:
+ s.remove(6, 8);
+ RangeExpectations().expect(2, 5).expect(9, 12).expect(16, 20).check(s);
+
+ // remove overlap subrange tail:
+ s.remove(11, 15);
+ RangeExpectations().expect(2, 5).expect(9, 10).expect(16, 20).check(s);
+
+ // remove overlap subrange head:
+ s.remove(14, 17);
+ RangeExpectations().expect(2, 5).expect(9, 10).expect(18, 20).check(s);
+
+ // remove overlap sequence tail:
+ s.remove(20, 22);
+ RangeExpectations().expect(2, 5).expect(9, 10).expect(18, 19).check(s);
+
+ // remove overlap sequence head:
+ s.remove(1, 3);
+ RangeExpectations().expect(4, 5).expect(9, 10).expect(18, 19).check(s);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/SessionState.cpp b/qpid/cpp/src/tests/SessionState.cpp
new file mode 100644
index 0000000000..1cf3415484
--- /dev/null
+++ b/qpid/cpp/src/tests/SessionState.cpp
@@ -0,0 +1,303 @@
+/*
+ *
+ * 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>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(SessionStateTestSuite)
+
+using namespace std;
+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, boost::bind(std::plus<T>(), _1, boost::bind(f, _2)));
+}
+
+// Create a frame with a one-char string.
+AMQFrame& frame(char s) {
+ static AMQFrame frame((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((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((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,
+ boost::bind(&send, boost::ref(s),
+ boost::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,
+ boost::bind(transfer1Char, boost::ref(s), _1));
+}
+
+size_t contentFrameSize(size_t n=1) { return AMQFrame(( AMQContentBody())).encodedSize() + n; }
+size_t transferFrameSize() { return AMQFrame((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(new 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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Shlib.cpp b/qpid/cpp/src/tests/Shlib.cpp
new file mode 100644
index 0000000000..7f01323e3c
--- /dev/null
+++ b/qpid/cpp/src/tests/Shlib.cpp
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "test_tools.h"
+#include "config.h"
+#include "qpid/sys/Shlib.h"
+#include "qpid/Exception.h"
+
+#include "unit_test.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(ShlibTestSuite)
+
+using namespace qpid::sys;
+typedef void (*CallMe)(int*);
+
+
+QPID_AUTO_TEST_CASE(testShlib) {
+ Shlib sh("./" QPID_SHLIB_PREFIX "shlibtest" QPID_SHLIB_POSTFIX QPID_SHLIB_SUFFIX);
+ // 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("./" QPID_SHLIB_PREFIX "shlibtest" QPID_SHLIB_POSTFIX QPID_SHLIB_SUFFIX);
+ CallMe callMe=sh.getSymbol<CallMe>("callMe");
+ BOOST_REQUIRE(callMe != 0);
+ callMe(&unloaded);
+ }
+ BOOST_CHECK_EQUAL(42, unloaded);
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Statistics.cpp b/qpid/cpp/src/tests/Statistics.cpp
new file mode 100644
index 0000000000..7cacde8b74
--- /dev/null
+++ b/qpid/cpp/src/tests/Statistics.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 "Statistics.h"
+#include <qpid/messaging/Message.h>
+#include <ostream>
+#include <iomanip>
+
+namespace qpid {
+namespace tests {
+
+using namespace std;
+
+Statistic::~Statistic() {}
+
+Throughput::Throughput() : messages(0), started(false) {}
+
+void Throughput::message(const messaging::Message&) {
+ ++messages;
+ if (!started) {
+ start = sys::now();
+ started = true;
+ }
+}
+
+void Throughput::header(ostream& o) const {
+ o << "tp(m/s)";
+}
+
+void Throughput::report(ostream& o) const {
+ double elapsed(int64_t(sys::Duration(start, sys::now()))/double(sys::TIME_SEC));
+ o << fixed << setprecision(0) << messages/elapsed;
+}
+
+ThroughputAndLatency::ThroughputAndLatency() :
+ total(0),
+ min(numeric_limits<double>::max()),
+ max(numeric_limits<double>::min()),
+ samples(0)
+{}
+
+const std::string TS = "ts";
+
+void ThroughputAndLatency::message(const messaging::Message& m) {
+ Throughput::message(m);
+ types::Variant::Map::const_iterator i = m.getProperties().find(TS);
+ if (i != m.getProperties().end()) {
+ ++samples;
+ int64_t start(i->second.asInt64());
+ int64_t end(sys::Duration::FromEpoch());
+ double latency = double(end - start)/sys::TIME_MSEC;
+ if (latency > 0) {
+ total += latency;
+ if (latency < min) min = latency;
+ if (latency > max) max = latency;
+ }
+ }
+}
+
+void ThroughputAndLatency::header(ostream& o) const {
+ Throughput::header(o);
+ o << '\t' << "l-min" << '\t' << "l-max" << '\t' << "l-avg";
+}
+
+void ThroughputAndLatency::report(ostream& o) const {
+ Throughput::report(o);
+ if (samples) {
+ o << fixed << setprecision(2)
+ << '\t' << min << '\t' << max << '\t' << total/samples;
+ }
+}
+
+ReporterBase::ReporterBase(ostream& o, int batch, bool wantHeader)
+ : batchSize(batch), batchCount(0), headerPrinted(!wantHeader), out(o)
+{}
+
+ReporterBase::~ReporterBase() {}
+
+/** Count message in the statistics */
+void ReporterBase::message(const messaging::Message& m) {
+ if (!overall.get()) overall = create();
+ overall->message(m);
+ if (batchSize) {
+ if (!batch.get()) batch = create();
+ batch->message(m);
+ if (++batchCount == batchSize) {
+ header();
+ batch->report(out);
+ out << endl;
+ batch = create();
+ batchCount = 0;
+ }
+ }
+}
+
+/** Print overall report. */
+void ReporterBase::report() {
+ if (!overall.get()) overall = create();
+ header();
+ overall->report(out);
+ out << endl;
+}
+
+void ReporterBase::header() {
+ if (!headerPrinted) {
+ if (!overall.get()) overall = create();
+ overall->header(out);
+ out << endl;
+ headerPrinted = true;
+ }
+}
+
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Statistics.h b/qpid/cpp/src/tests/Statistics.h
new file mode 100644
index 0000000000..091046a17f
--- /dev/null
+++ b/qpid/cpp/src/tests/Statistics.h
@@ -0,0 +1,111 @@
+#ifndef TESTS_STATISTICS_H
+#define TESTS_STATISTICS_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/Time.h>
+#include <limits>
+#include <iosfwd>
+#include <memory>
+
+namespace qpid {
+
+namespace messaging {
+class Message;
+}
+
+namespace tests {
+
+class Statistic {
+ public:
+ virtual ~Statistic();
+ virtual void message(const messaging::Message&) = 0;
+ virtual void report(std::ostream&) const = 0;
+ virtual void header(std::ostream&) const = 0;
+};
+
+class Throughput : public Statistic {
+ public:
+ Throughput();
+ virtual void message(const messaging::Message&);
+ virtual void report(std::ostream&) const;
+ virtual void header(std::ostream&) const;
+
+ protected:
+ int messages;
+
+ private:
+ bool started;
+ sys::AbsTime start;
+};
+
+class ThroughputAndLatency : public Throughput {
+ public:
+ ThroughputAndLatency();
+ virtual void message(const messaging::Message&);
+ virtual void report(std::ostream&) const;
+ virtual void header(std::ostream&) const;
+
+ private:
+ double total, min, max; // Milliseconds
+ int samples;
+};
+
+/** Report batch and overall statistics */
+class ReporterBase {
+ public:
+ virtual ~ReporterBase();
+
+ /** Count message in the statistics */
+ void message(const messaging::Message& m);
+
+ /** Print overall report. */
+ void report();
+
+ protected:
+ ReporterBase(std::ostream& o, int batchSize, bool wantHeader);
+ virtual std::auto_ptr<Statistic> create() = 0;
+
+ private:
+ void header();
+ void report(const Statistic& s);
+ std::auto_ptr<Statistic> overall;
+ std::auto_ptr<Statistic> batch;
+ int batchSize, batchCount;
+ bool stopped, headerPrinted;
+ std::ostream& out;
+};
+
+template <class Stats> class Reporter : public ReporterBase {
+ public:
+ Reporter(std::ostream& o, int batchSize, bool wantHeader)
+ : ReporterBase(o, batchSize, wantHeader) {}
+
+ virtual std::auto_ptr<Statistic> create() {
+ return std::auto_ptr<Statistic>(new Stats);
+ }
+};
+
+}} // namespace qpid::tests
+
+#endif /*!TESTS_STATISTICS_H*/
diff --git a/qpid/cpp/src/tests/StringUtils.cpp b/qpid/cpp/src/tests/StringUtils.cpp
new file mode 100644
index 0000000000..c50287a4f4
--- /dev/null
+++ b/qpid/cpp/src/tests/StringUtils.cpp
@@ -0,0 +1,81 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(StringUtilsTestSuite)
+
+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/qpid/cpp/src/tests/SystemInfo.cpp b/qpid/cpp/src/tests/SystemInfo.cpp
new file mode 100644
index 0000000000..34f1ac408e
--- /dev/null
+++ b/qpid/cpp/src/tests/SystemInfo.cpp
@@ -0,0 +1,36 @@
+/*
+ *
+ * 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/SystemInfo.h"
+#include <boost/assign.hpp>
+
+using namespace std;
+using namespace qpid::sys;
+using namespace boost::assign;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(SystemInfoTestSuite)
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TestMessageStore.h b/qpid/cpp/src/tests/TestMessageStore.h
new file mode 100644
index 0000000000..0b63bc9c15
--- /dev/null
+++ b/qpid/cpp/src/tests/TestMessageStore.h
@@ -0,0 +1,63 @@
+#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;
+
+namespace qpid {
+namespace tests {
+
+typedef std::pair<std::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(){}
+};
+
+}} // namespace qpid::tests
+
+#endif
diff --git a/qpid/cpp/src/tests/TestOptions.h b/qpid/cpp/src/tests/TestOptions.h
new file mode 100644
index 0000000000..f8da0f59cf
--- /dev/null
+++ b/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/qpid/cpp/src/tests/TimerTest.cpp b/qpid/cpp/src/tests/TimerTest.cpp
new file mode 100644
index 0000000000..d28eeeffc1
--- /dev/null
+++ b/qpid/cpp/src/tests/TimerTest.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/sys/Timer.h"
+#include "qpid/sys/Monitor.h"
+#include "qpid/Options.h"
+#include "unit_test.h"
+#include <math.h>
+#include <iostream>
+#include <memory>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+using namespace qpid::sys;
+using boost::intrusive_ptr;
+using boost::dynamic_pointer_cast;
+
+namespace qpid {
+namespace tests {
+
+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, "Test"), 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);
+#ifdef _MSC_VER
+ uint64_t difference = _abs64(expected - actual);
+#elif defined(_WIN32)
+ uint64_t difference = labs(expected - actual);
+#elif defined(__SUNPRO_CC) || defined (__IBMCPP__)
+ uint64_t difference = llabs(expected - actual);
+#else
+ uint64_t difference = abs(expected - actual);
+#endif
+ std::string msg(boost::lexical_cast<std::string>(boost::format("tolerance = %1%, difference = %2%") % tolerance % difference));
+ BOOST_CHECK_MESSAGE(difference < tolerance, msg);
+ }
+
+ 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);
+}
+
+std::string toString(Duration d) { return boost::lexical_cast<std::string>(d); }
+Duration fromString(const std::string& str) { return boost::lexical_cast<Duration>(str); }
+
+QPID_AUTO_TEST_CASE(testOstreamInOut) {
+ std::string empty;
+ BOOST_CHECK_EQUAL(toString(Duration(int64_t(TIME_SEC))), "1s");
+ BOOST_CHECK_EQUAL(toString(Duration(int64_t(TIME_SEC*123.4))), "123.4s");
+ BOOST_CHECK_EQUAL(toString(Duration(int64_t(TIME_MSEC*123.4))), "123.4ms");
+ BOOST_CHECK_EQUAL(toString(Duration(int64_t(TIME_USEC*123.4))), "123.4us");
+ BOOST_CHECK_EQUAL(toString(Duration(int64_t(TIME_NSEC*123))), "123ns");
+
+ BOOST_CHECK_EQUAL(fromString("123.4"), Duration(int64_t(TIME_SEC*123.4)));
+ BOOST_CHECK_EQUAL(fromString("123.4s"), Duration(int64_t(TIME_SEC*123.4)));
+ BOOST_CHECK_EQUAL(fromString("123ms"), Duration(int64_t(TIME_MSEC*123)));
+ BOOST_CHECK_EQUAL(fromString("123us"), Duration(int64_t(TIME_USEC*123)));
+ BOOST_CHECK_EQUAL(fromString("123ns"), Duration(int64_t(TIME_NSEC*123)));
+
+ Duration d = 0;
+ std::istringstream i;
+ std::string s;
+
+ i.str("123x");
+ i >> d;
+ BOOST_CHECK(i.fail());
+ BOOST_CHECK_EQUAL(d, 0);
+ BOOST_CHECK_EQUAL(i.str(), "123x");
+
+ i.str("xxx");
+ i >> d;
+ BOOST_CHECK(i.fail());
+ BOOST_CHECK_EQUAL(d, 0);
+ BOOST_CHECK_EQUAL(i.str(), "xxx");
+}
+
+QPID_AUTO_TEST_CASE(testOptionParse) {
+ Options opts;
+ Duration interval;
+ opts.addOptions()("interval", optValue(interval, "I"), "blah");
+ const char *args[] = { "fakeexe", "--interval", "123.4" };
+ opts.parse(sizeof(args)/sizeof(args[0]), args);
+ BOOST_CHECK_EQUAL(interval, Duration(int64_t(TIME_SEC*123.4)));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TopicExchangeTest.cpp b/qpid/cpp/src/tests/TopicExchangeTest.cpp
new file mode 100644
index 0000000000..d57951ea3f
--- /dev/null
+++ b/qpid/cpp/src/tests/TopicExchangeTest.cpp
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/TopicKeyNode.h"
+#include "qpid/broker/TopicExchange.h"
+#include "unit_test.h"
+#include "test_tools.h"
+
+using namespace qpid::broker;
+using namespace std;
+
+
+namespace qpid {
+namespace broker {
+
+// Class for exercising the pattern match code in the TopicExchange
+class TopicExchange::TopicExchangeTester {
+
+public:
+ typedef std::vector<std::string> BindingVec;
+ typedef TopicKeyNode<TopicExchange::BindingKey> TestBindingNode;
+
+private:
+ // binding node iterator that collects all routes that are bound
+ class TestFinder : public TestBindingNode::TreeIterator {
+ public:
+ TestFinder(BindingVec& m) : bv(m) {};
+ ~TestFinder() {};
+ bool visit(TestBindingNode& node) {
+ if (!node.bindings.bindingVector.empty())
+ bv.push_back(node.routePattern);
+ return true;
+ }
+
+ BindingVec& bv;
+ };
+
+public:
+ TopicExchangeTester() {};
+ ~TopicExchangeTester() {};
+ bool addBindingKey(const std::string& bKey) {
+ string routingPattern = normalize(bKey);
+ BindingKey *bk = bindingTree.add(routingPattern);
+ if (bk) {
+ // push a dummy binding to mark this node as "non-leaf"
+ bk->bindingVector.push_back(Binding::shared_ptr());
+ return true;
+ }
+ return false;
+ }
+
+ bool removeBindingKey(const std::string& bKey){
+ string routingPattern = normalize(bKey);
+ BindingKey *bk = bindingTree.get(routingPattern);
+ if (bk) {
+ bk->bindingVector.pop_back();
+ if (bk->bindingVector.empty()) {
+ // no more bindings - remove this node
+ bindingTree.remove(routingPattern);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void findMatches(const std::string& rKey, BindingVec& matches) {
+ TestFinder testFinder(matches);
+ bindingTree.iterateMatch( rKey, testFinder );
+ }
+
+ void getAll(BindingVec& bindings) {
+ TestFinder testFinder(bindings);
+ bindingTree.iterateAll( testFinder );
+ }
+
+private:
+ TestBindingNode bindingTree;
+};
+} // namespace broker
+
+
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(TopicExchangeTestSuite)
+
+#define CHECK_NORMALIZED(expect, pattern) BOOST_CHECK_EQUAL(expect, TopicExchange::normalize(pattern));
+
+namespace {
+ // return the count of bindings that match 'pattern'
+ int match(TopicExchange::TopicExchangeTester &tt,
+ const std::string& pattern)
+ {
+ TopicExchange::TopicExchangeTester::BindingVec bv;
+ tt.findMatches(pattern, bv);
+ return int(bv.size());
+ }
+
+ // return true if expected contains exactly all bindings that match
+ // against pattern.
+ bool compare(TopicExchange::TopicExchangeTester& tt,
+ const std::string& pattern,
+ const TopicExchange::TopicExchangeTester::BindingVec& expected)
+ {
+ TopicExchange::TopicExchangeTester::BindingVec bv;
+ tt.findMatches(pattern, bv);
+ if (expected.size() != bv.size()) {
+ // std::cout << "match failed 1 f=[" << bv << "]" << std::endl;
+ // std::cout << "match failed 1 e=[" << expected << "]" << std::endl;
+ return false;
+ }
+ TopicExchange::TopicExchangeTester::BindingVec::const_iterator i;
+ for (i = expected.begin(); i != expected.end(); i++) {
+ TopicExchange::TopicExchangeTester::BindingVec::iterator j;
+ for (j = bv.begin(); j != bv.end(); j++) {
+ // std::cout << "matched [" << *j << "]" << std::endl;
+ if (*i == *j) break;
+ }
+ if (j == bv.end()) {
+ // std::cout << "match failed 2 [" << bv << "]" << std::endl;
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+
+QPID_AUTO_TEST_CASE(testNormalize)
+{
+ CHECK_NORMALIZED("", "");
+ CHECK_NORMALIZED("a.b.c", "a.b.c");
+ CHECK_NORMALIZED("a.*.c", "a.*.c");
+ CHECK_NORMALIZED("#", "#");
+ CHECK_NORMALIZED("#", "#.#.#.#");
+ CHECK_NORMALIZED("*.*.*.#", "#.*.#.*.#.#.*");
+ CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*.#");
+ CHECK_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*");
+ CHECK_NORMALIZED("*.*.*.#", "*.#.#.*.*.#");
+}
+
+QPID_AUTO_TEST_CASE(testPlain)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("ab.cd.e");
+
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "ab.cd.e"));
+ BOOST_CHECK_EQUAL(0, match(tt, "abx.cd.e"));
+ BOOST_CHECK_EQUAL(0, match(tt, "ab.cd"));
+ BOOST_CHECK_EQUAL(0, match(tt, "ab.cd..e."));
+ BOOST_CHECK_EQUAL(0, match(tt, "ab.cd.e."));
+ BOOST_CHECK_EQUAL(0, match(tt, ".ab.cd.e"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, ""));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = ".";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "."));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+
+QPID_AUTO_TEST_CASE(testStar)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("a.*.b");
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.xx.b"));
+ BOOST_CHECK_EQUAL(0, match(tt, "a.b"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "*.x";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "y.x"));
+ BOOST_CHECK_EQUAL(1, match(tt, ".x"));
+ BOOST_CHECK_EQUAL(0, match(tt, "x"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "x.x.*";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.x.y"));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.x."));
+ BOOST_CHECK_EQUAL(0, match(tt, "x.x"));
+ BOOST_CHECK_EQUAL(0, match(tt, "q.x.y"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+QPID_AUTO_TEST_CASE(testHash)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("a.#.b");
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.b"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a..x.y.zz.b"));
+ BOOST_CHECK_EQUAL(0, match(tt, "a.b."));
+ BOOST_CHECK_EQUAL(0, match(tt, "q.x.b"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "a.#";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b.c"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "#.a";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a"));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.y.a"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "a.#.b.#.c";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b.c"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.b.y.c"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.x.b.y.y.c"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+QPID_AUTO_TEST_CASE(testMixed)
+{
+ TopicExchange::TopicExchangeTester tt;
+ string pattern("*.x.#.y");
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.y"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.p.qq.y"));
+ BOOST_CHECK_EQUAL(0, match(tt, "a.a.x.y"));
+ BOOST_CHECK_EQUAL(0, match(tt, "aa.x.b.c"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "a.#.b.*";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.b.x"));
+ BOOST_CHECK_EQUAL(1, match(tt, "a.x.x.x.b.x"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+
+ pattern = "*.*.*.#";
+ BOOST_CHECK(tt.addBindingKey(pattern));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.y.z"));
+ BOOST_CHECK_EQUAL(1, match(tt, "x.y.z.a.b.c"));
+ BOOST_CHECK_EQUAL(0, match(tt, "x.y"));
+ BOOST_CHECK_EQUAL(0, match(tt, "x"));
+ BOOST_CHECK(tt.removeBindingKey(pattern));
+}
+
+
+QPID_AUTO_TEST_CASE(testMultiple)
+{
+ TopicExchange::TopicExchangeTester tt;
+ const std::string bindings[] =
+ { "a", "b",
+ "a.b", "b.c",
+ "a.b.c.d", "b.c.d.e",
+ "a.*", "a.#", "a.*.#",
+ "#.b", "*.b", "*.#.b",
+ "a.*.b", "a.#.b", "a.*.#.b",
+ "*.b.*", "#.b.#",
+ };
+ const size_t nBindings = sizeof(bindings)/sizeof(bindings[0]);
+
+ // setup bindings
+ for (size_t idx = 0; idx < nBindings; idx++) {
+ BOOST_CHECK(tt.addBindingKey(bindings[idx]));
+ }
+
+ {
+ // read all bindings, and verify all are present
+ TopicExchange::TopicExchangeTester::BindingVec b;
+ tt.getAll(b);
+ BOOST_CHECK_EQUAL(b.size(), nBindings);
+ for (size_t idx = 0; idx < nBindings; idx++) {
+ bool found = false;
+ for (TopicExchange::TopicExchangeTester::BindingVec::iterator i = b.begin();
+ i != b.end(); i++) {
+ if (*i == bindings[idx]) {
+ found = true;
+ break;
+ }
+ }
+ BOOST_CHECK(found);
+ }
+ }
+
+ { // test match on pattern "a"
+ const std::string matches[] = { "a", "a.#" };
+ const size_t nMatches = 2;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a", expected));
+ }
+
+ { // test match on pattern "a.z"
+ const std::string matches[] = { "a.*", "a.#", "a.*.#" };
+ const size_t nMatches = 3;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.z", expected));
+ }
+
+ { // test match on pattern "a.b"
+ const std::string matches[] = {
+ "a.b", "a.*", "a.#", "a.*.#",
+ "#.b", "#.b.#", "*.#.b", "*.b",
+ "a.#.b"
+ };
+ const size_t nMatches = 9;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.b", expected));
+ }
+
+ { // test match on pattern "a.c.c.b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "*.#.b", "a.#.b",
+ "a.#", "a.*.#.b", "a.*.#"
+ };
+ const size_t nMatches = 7;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.c.c.b", expected));
+ }
+
+ { // test match on pattern "a.b.c"
+
+ const std::string matches[] = {
+ "#.b.#", "*.b.*", "a.#", "a.*.#"
+ };
+ const size_t nMatches = 4;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.b.c", expected));
+ }
+
+ { // test match on pattern "b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "b"
+ };
+ const size_t nMatches = 3;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "b", expected));
+ }
+
+ { // test match on pattern "x.b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "*.#.b", "*.b"
+ };
+ const size_t nMatches = 4;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "x.b", expected));
+ }
+
+ { // test match on pattern "x.y.z.b"
+
+ const std::string matches[] = {
+ "#.b", "#.b.#", "*.#.b"
+ };
+ const size_t nMatches = 3;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "x.y.z.b", expected));
+ }
+
+ { // test match on pattern "x.y.z.b.a.b.c"
+
+ const std::string matches[] = {
+ "#.b.#", "#.b.#"
+ };
+ const size_t nMatches = 2;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "x.y.z.b.a.b.c", expected));
+ }
+
+ { // test match on pattern "a.b.c.d"
+
+ const std::string matches[] = {
+ "#.b.#", "a.#", "a.*.#", "a.b.c.d",
+ };
+ const size_t nMatches = 4;
+ TopicExchange::TopicExchangeTester::BindingVec expected(matches, matches + nMatches);
+ BOOST_CHECK(compare(tt, "a.b.c.d", expected));
+ }
+
+ // cleanup bindings
+ for (size_t idx = 0; idx < nBindings; idx++) {
+ BOOST_CHECK(tt.removeBindingKey(bindings[idx]));
+ }
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TransactionObserverTest.cpp b/qpid/cpp/src/tests/TransactionObserverTest.cpp
new file mode 100644
index 0000000000..80ef494c21
--- /dev/null
+++ b/qpid/cpp/src/tests/TransactionObserverTest.cpp
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "MessagingFixture.h"
+#include "qpid/broker/BrokerObserver.h"
+#include "qpid/broker/TransactionObserver.h"
+#include "qpid/broker/TxBuffer.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/ha/types.h"
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/lexical_cast.hpp>
+#include <iostream>
+#include <vector>
+
+namespace qpid {
+namespace tests {
+
+using framing::SequenceSet;
+using messaging::Message;
+using boost::shared_ptr;
+
+using namespace boost::assign;
+using namespace boost;
+using namespace broker;
+using namespace std;
+using namespace messaging;
+using namespace types;
+
+QPID_AUTO_TEST_SUITE(TransactionalObserverTest)
+
+Message msg(string content) { return Message(content); }
+
+struct MockTransactionObserver : public TransactionObserver {
+ bool prep;
+ vector<string> events;
+
+ MockTransactionObserver(bool prep_=true) : prep(prep_) {}
+
+ void record(const string& e) { events.push_back(e); }
+
+ void enqueue(const shared_ptr<Queue>& q, const broker::Message& m) {
+ record("enqueue "+q->getName()+" "+m.getContent());
+ }
+ void dequeue(const Queue::shared_ptr& q, SequenceNumber p, SequenceNumber r) {
+ record("dequeue "+q->getName()+" "+
+ lexical_cast<string>(p)+" "+lexical_cast<string>(r));
+ }
+ bool prepare() { record("prepare"); return prep; }
+ void commit() { record("commit"); }
+ void rollback() {record("rollback"); }
+};
+
+struct MockBrokerObserver : public BrokerObserver {
+ bool prep;
+ shared_ptr<MockTransactionObserver> tx;
+
+ MockBrokerObserver(bool prep_=true) : prep(prep_) {}
+
+ void startTx(const intrusive_ptr<TxBuffer>& buffer) {
+ if (!tx) { // Don't overwrite first tx with automatically started second tx.
+ tx.reset(new MockTransactionObserver(prep));
+ buffer->setObserver(tx);
+ }
+ }
+};
+
+Session simpleTxTransaction(MessagingFixture& fix) {
+ fix.session.createSender("q1;{create:always}").send(msg("foo")); // Not in TX
+ // Transaction with 1 enqueue and 1 dequeue.
+ Session txSession = fix.connection.createTransactionalSession();
+ BOOST_CHECK_EQUAL("foo", txSession.createReceiver("q1").fetch().getContent());
+ txSession.acknowledge();
+ txSession.createSender("q2;{create:always}").send(msg("bar"));
+ return txSession;
+}
+
+QPID_AUTO_TEST_CASE(testTxCommit) {
+ MessagingFixture fix;
+ shared_ptr<MockBrokerObserver> brokerObserver(new MockBrokerObserver);
+ fix.broker->getBrokerObservers().add(brokerObserver);
+ Session txSession = simpleTxTransaction(fix);
+ txSession.commit();
+ // Note on ordering: observers see enqueues as they happen, but dequeues just
+ // before prepare.
+ BOOST_CHECK_EQUAL(
+ list_of<string>("enqueue q2 bar")("dequeue q1 1 0")("prepare")("commit"),
+ brokerObserver->tx->events
+ );
+}
+
+QPID_AUTO_TEST_CASE(testTxFail) {
+ MessagingFixture fix;
+ shared_ptr<MockBrokerObserver> brokerObserver(new MockBrokerObserver(false));
+ fix.broker->getBrokerObservers().add(brokerObserver);
+ Session txSession = simpleTxTransaction(fix);
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected error.
+ txSession.commit();
+ BOOST_FAIL("Expected exception");
+ } catch(...) {}
+
+ BOOST_CHECK_EQUAL(
+ list_of<string>("enqueue q2 bar")("dequeue q1 1 0")("prepare")("rollback"),
+ brokerObserver->tx->events
+ );
+}
+
+QPID_AUTO_TEST_CASE(testTxRollback) {
+ MessagingFixture fix;
+ shared_ptr<MockBrokerObserver> brokerObserver(new MockBrokerObserver(false));
+ fix.broker->getBrokerObservers().add(brokerObserver);
+ Session txSession = simpleTxTransaction(fix);
+ txSession.rollback();
+ // Note: The dequeue does not appear here. This is because TxAccepts
+ // (i.e. dequeues) are not enlisted until SemanticState::commit and are
+ // never enlisted if the transaction is rolled back.
+ BOOST_CHECK_EQUAL(
+ list_of<string>("enqueue q2 bar")("rollback"),
+ brokerObserver->tx->events
+ );
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TxBufferTest.cpp b/qpid/cpp/src/tests/TxBufferTest.cpp
new file mode 100644
index 0000000000..3f052d213e
--- /dev/null
+++ b/qpid/cpp/src/tests/TxBufferTest.cpp
@@ -0,0 +1,188 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "test_tools.h"
+#include <iostream>
+#include <vector>
+#include "TxMocks.h"
+
+using namespace qpid::broker;
+using boost::static_pointer_cast;
+
+namespace qpid {
+namespace tests {
+
+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));
+
+ buffer.startCommit(&store);
+ buffer.endCommit(&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));
+
+ try {
+ ScopedSuppressLogging sl; // Suppress messages for expected error.
+ buffer.startCommit(&store);
+ buffer.endCommit(&store);
+ BOOST_FAIL("Expected exception");
+ } catch (...) {}
+ 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()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TxMocks.h b/qpid/cpp/src/tests/TxMocks.h
new file mode 100644
index 0000000000..8b54e7484b
--- /dev/null
+++ b/qpid/cpp/src/tests/TxMocks.h
@@ -0,0 +1,236 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/Msg.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;
+
+namespace qpid {
+namespace tests {
+
+template <class T> void assertEqualVector(std::vector<T>& expected, std::vector<T>& actual){
+ unsigned int i = 0;
+ while(i < expected.size() && i < actual.size()){
+ 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);
+ }
+
+ void callObserver(const boost::shared_ptr<TransactionObserver>&) {}
+
+ 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);
+ }
+
+ ~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(){}
+};
+
+}} // namespace qpid::tests
+
+#endif
diff --git a/qpid/cpp/src/tests/Url.cpp b/qpid/cpp/src/tests/Url.cpp
new file mode 100644
index 0000000000..b30de682bc
--- /dev/null
+++ b/qpid/cpp/src/tests/Url.cpp
@@ -0,0 +1,116 @@
+/*
+ *
+ * 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;
+
+namespace qpid {
+namespace tests {
+
+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("host").str(), "amqp:tcp:host:5672");
+}
+
+QPID_AUTO_TEST_CASE(TestParseInvalid) {
+ //host is required:
+ URL_CHECK_INVALID("amqp:tcp:");
+ URL_CHECK_INVALID("amqp:");
+ URL_CHECK_INVALID("amqp::42");
+ URL_CHECK_INVALID("");
+
+ // Port must be numeric
+ URL_CHECK_INVALID("host:badPort");
+}
+
+QPID_AUTO_TEST_CASE(TestParseXyz) {
+ Url::addProtocol("xyz");
+ URL_CHECK_STR("amqp:xyz:host:123");
+ BOOST_CHECK_EQUAL(Url("xyz:host").str(), "amqp:xyz:host:5672");
+}
+
+QPID_AUTO_TEST_CASE(TestParseTricky) {
+ BOOST_CHECK_EQUAL(Url("amqp").str(), "amqp:tcp:amqp:5672");
+ BOOST_CHECK_EQUAL(Url("amqp:tcp").str(), "amqp:tcp:tcp:5672");
+ // These are ambiguous parses and arguably not the best result
+ BOOST_CHECK_EQUAL(Url("amqp:876").str(), "amqp:tcp:876:5672");
+ BOOST_CHECK_EQUAL(Url("tcp:567").str(), "amqp:tcp:567:5672");
+}
+
+QPID_AUTO_TEST_CASE(TestParseIPv6) {
+ Url u1("[::]");
+ BOOST_CHECK_EQUAL(u1[0].host, "::");
+ BOOST_CHECK_EQUAL(u1[0].port, 5672);
+ Url u2("[::1]");
+ BOOST_CHECK_EQUAL(u2[0].host, "::1");
+ BOOST_CHECK_EQUAL(u2[0].port, 5672);
+ Url u3("[::127.0.0.1]");
+ BOOST_CHECK_EQUAL(u3[0].host, "::127.0.0.1");
+ BOOST_CHECK_EQUAL(u3[0].port, 5672);
+ Url u4("[2002::222:68ff:fe0b:e61a]");
+ BOOST_CHECK_EQUAL(u4[0].host, "2002::222:68ff:fe0b:e61a");
+ BOOST_CHECK_EQUAL(u4[0].port, 5672);
+ Url u5("[2002::222:68ff:fe0b:e61a]:123");
+ BOOST_CHECK_EQUAL(u5[0].host, "2002::222:68ff:fe0b:e61a");
+ BOOST_CHECK_EQUAL(u5[0].port, 123);
+}
+
+QPID_AUTO_TEST_CASE(TestParseMultiAddress) {
+ Url::addProtocol("xyz");
+ URL_CHECK_STR("amqp:tcp:host:0,xyz:foo:123,tcp:foo:0,xyz:bar:1");
+ URL_CHECK_STR("amqp:xyz:foo:222,tcp:foo:0");
+ URL_CHECK_INVALID("amqp:tcp:h:0,");
+ URL_CHECK_INVALID(",amqp:tcp:h");
+}
+
+QPID_AUTO_TEST_CASE(TestParseUserPass) {
+ URL_CHECK_STR("amqp:user/pass@tcp:host:123");
+ URL_CHECK_STR("amqp:user@tcp:host:123");
+ BOOST_CHECK_EQUAL(Url("user/pass@host").str(), "amqp:user/pass@tcp:host:5672");
+ BOOST_CHECK_EQUAL(Url("user@host").str(), "amqp:user@tcp:host:5672");
+
+ Url u("user/pass@host");
+ BOOST_CHECK_EQUAL(u.getUser(), "user");
+ BOOST_CHECK_EQUAL(u.getPass(), "pass");
+ Url v("foo@host");
+ BOOST_CHECK_EQUAL(v.getUser(), "foo");
+ BOOST_CHECK_EQUAL(v.getPass(), "");
+ u = v;
+ BOOST_CHECK_EQUAL(u.getUser(), "foo");
+ BOOST_CHECK_EQUAL(u.getPass(), "");
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Uuid.cpp b/qpid/cpp/src/tests/Uuid.cpp
new file mode 100644
index 0000000000..f9a67d9db0
--- /dev/null
+++ b/qpid/cpp/src/tests/Uuid.cpp
@@ -0,0 +1,150 @@
+/*
+ *
+ * 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 "qpid/types/Uuid.h"
+
+#include "unit_test.h"
+
+#include <set>
+
+#include <boost/array.hpp>
+
+namespace qpid {
+namespace tests {
+
+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 = {{0x1b, 0x4e, 0x28, 0xba, 0x2f, 0xa1, 0x11, 0x02, 0x88, 0x3f, 0xb9, 0xa7, 0x61, 0xbd, 0xe3, 0xfb}};
+const string sampleStr("1b4e28ba-2fa1-1102-883f-b9a761bde3fb");
+const string zeroStr("00000000-0000-0000-0000-000000000000");
+const string badUuid1("1b4e28ba-2fa1-11d2-883f-b9761bde3fb");
+const string badUuid2("1b4e28ba-2fa1-11d23883f-b9761dbde3fb");
+
+QPID_AUTO_TEST_CASE(testUuidIstream) {
+ Uuid uuid;
+ istringstream in(sampleStr);
+ in >> uuid;
+ BOOST_CHECK(!in.fail());
+ BOOST_CHECK(::memcmp(uuid.data(), sample.data(), uuid.size())==0);
+
+ istringstream is(zeroStr);
+ Uuid zero;
+ is >> zero;
+ BOOST_CHECK(!is.fail());
+ BOOST_CHECK_EQUAL(zero, Uuid());
+}
+
+QPID_AUTO_TEST_CASE(testUuidOstream) {
+ Uuid uuid(sample.c_array());
+ ostringstream out;
+ out << uuid;
+ BOOST_CHECK(out.good());
+ BOOST_CHECK_EQUAL(out.str(), sampleStr);
+
+ ostringstream os;
+ os << Uuid();
+ BOOST_CHECK(out.good());
+ BOOST_CHECK_EQUAL(os.str(), zeroStr);
+}
+
+QPID_AUTO_TEST_CASE(testBadUuidIstream) {
+ Uuid a;
+ istringstream is(badUuid1);
+ is >> a;
+ BOOST_CHECK(!is.good());
+ istringstream is2(badUuid2);
+ is2 >> a;
+ BOOST_CHECK(!is2.good());
+}
+
+QPID_AUTO_TEST_CASE(testUuidIOstream) {
+ Uuid a(true), b(true);
+ ostringstream os;
+ os << a << endl << b;
+ Uuid aa, bb;
+ istringstream is(os.str());
+ is >> aa >> ws >> bb;
+ BOOST_CHECK(os.good());
+ BOOST_CHECK_EQUAL(a, aa);
+ BOOST_CHECK_EQUAL(b, bb);
+}
+
+QPID_AUTO_TEST_CASE(testUuidEncodeDecode) {
+ std::vector<char> buff(Uuid::size());
+ Buffer wbuf(&buff[0], Uuid::size());
+ Uuid uuid(sample.c_array());
+ uuid.encode(wbuf);
+
+ Buffer rbuf(&buff[0], Uuid::size());
+ Uuid decoded;
+ decoded.decode(rbuf);
+ BOOST_CHECK_EQUAL(string(sample.begin(), sample.end()),
+ string(decoded.data(), decoded.data()+decoded.size()));
+}
+
+QPID_AUTO_TEST_CASE(testTypesUuid)
+{
+ //tests for the Uuid class in the types namespace (introduced
+ //to avoid pulling in dependencies from framing)
+ types::Uuid a;
+ types::Uuid b(true);
+ types::Uuid c(true);
+ types::Uuid d(b);
+ types::Uuid e;
+ e = c;
+
+ BOOST_CHECK(!a);
+ BOOST_CHECK(b);
+
+ BOOST_CHECK(a != b);
+ BOOST_CHECK(b != c);
+
+ BOOST_CHECK_EQUAL(b, d);
+ BOOST_CHECK_EQUAL(c, e);
+
+ ostringstream out;
+ out << b;
+ istringstream in(out.str());
+ in >> a;
+ BOOST_CHECK(!in.fail());
+ BOOST_CHECK_EQUAL(a, b);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/Variant.cpp b/qpid/cpp/src/tests/Variant.cpp
new file mode 100644
index 0000000000..5ae7fc89eb
--- /dev/null
+++ b/qpid/cpp/src/tests/Variant.cpp
@@ -0,0 +1,834 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/types/Variant.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include <boost/assign.hpp>
+#include <iostream>
+
+using namespace qpid::types;
+using namespace qpid::amqp_0_10;
+using boost::assign::list_of;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(VariantSuite)
+
+QPID_AUTO_TEST_CASE(testConversions)
+{
+ Variant value;
+
+ //string to float/double
+ value = "1.5";
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+
+ //float to string or double
+ value = 1.5f;
+ BOOST_CHECK_EQUAL((float) 1.5, value.asFloat());
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //double to string (conversion to float not valid)
+ value = 1.5;
+ BOOST_CHECK_THROW(value.asFloat(), InvalidConversion);
+ BOOST_CHECK_EQUAL((double) 1.5, value.asDouble());
+ BOOST_CHECK_EQUAL(std::string("1.5"), value.asString());
+
+ //uint8 to larger unsigned ints and string
+ value = (uint8_t) 7;
+ BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("7"), value.asString());
+
+ value = (uint16_t) 8;
+ BOOST_CHECK_EQUAL(std::string("8"), value.asString());
+ value = (uint32_t) 9;
+ BOOST_CHECK_EQUAL(std::string("9"), value.asString());
+
+ //uint32 to larger unsigned ints and string
+ value = (uint32_t) 9999999;
+ BOOST_CHECK_EQUAL((uint32_t) 9999999, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 9999999, value.asUint64());
+ BOOST_CHECK_EQUAL(std::string("9999999"), value.asString());
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+
+ value = "true";
+ BOOST_CHECK(value.asBool());
+ value = "false";
+ BOOST_CHECK(!value.asBool());
+ value = "1";
+ BOOST_CHECK(value.asBool());
+ value = "0";
+ BOOST_CHECK(!value.asBool());
+ value = "other";
+ BOOST_CHECK_THROW(value.asBool(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testConversionsFromString)
+{
+ Variant value;
+ value = "5";
+ BOOST_CHECK_EQUAL(5, value.asInt16());
+ BOOST_CHECK_EQUAL(5u, value.asUint16());
+
+ value = "-5";
+ BOOST_CHECK_EQUAL(-5, value.asInt16());
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+
+ value = "18446744073709551615";
+ BOOST_CHECK_EQUAL(18446744073709551615ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "9223372036854775808";
+ BOOST_CHECK_EQUAL(9223372036854775808ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "-9223372036854775809";
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "2147483648";
+ BOOST_CHECK_EQUAL(2147483648ul, value.asUint32());
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "-2147483649";
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "32768";
+ BOOST_CHECK_EQUAL(32768u, value.asUint16());
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+
+ value = "-32769";
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+
+ value = "-2.5";
+ BOOST_CHECK_EQUAL(-2.5, value.asFloat());
+
+ value = "-0.875432e10";
+ BOOST_CHECK_EQUAL(-0.875432e10, value.asDouble());
+
+ value = "-0";
+ BOOST_CHECK_EQUAL(0, value.asInt16());
+ BOOST_CHECK_EQUAL(0u, value.asUint16());
+
+ value = "-Blah";
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asFloat(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asDouble(), InvalidConversion);
+
+ value = "-000";
+ BOOST_CHECK_EQUAL(0, value.asInt16());
+ BOOST_CHECK_EQUAL(0u, value.asUint16());
+
+ value = "-0010";
+ BOOST_CHECK_EQUAL(-10, value.asInt16());
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testSizeConversionsUint)
+{
+ Variant value;
+
+ //uint8 (in 7 bits) to other uints, ints
+ value = (uint8_t) 7;
+ BOOST_CHECK_EQUAL((uint8_t) 7, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 7, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 7, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 7, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 7, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 7, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 7, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 7, value.asInt64());
+
+ //uint8 (in 8 bits) to other uints, ints
+ value = (uint8_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+
+
+ //uint16 (in 7 bits) to other uints, ints
+ value = (uint16_t) 120;
+ BOOST_CHECK_EQUAL((uint8_t) 120, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 120, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 120, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 120, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 120, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 120, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 120, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 120, value.asInt64());
+
+ //uint16 (more than 7 bits) to other uints, ints
+ value = (uint16_t) 240;
+ BOOST_CHECK_EQUAL((uint8_t) 240, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 240, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 240, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 240, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 240, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 240, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 240, value.asInt64());
+
+ //uint16 (more than 8 bits) to other uints, ints
+ value = (uint16_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //uint16 (more than 15 bits) to other uints, ints
+ value = (uint16_t) 32770;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 32770, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 32770, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 32770, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 32770, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 32770, value.asInt64());
+
+
+
+ //uint32 (in 7 bits) to other uints, ints
+ value = (uint32_t) 120;
+ BOOST_CHECK_EQUAL((uint8_t) 120, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 120, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 120, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 120, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 120, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 120, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 120, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 120, value.asInt64());
+
+ //uint32 (more than 7 bits) to other uints, ints
+ value = (uint32_t) 240;
+ BOOST_CHECK_EQUAL((uint8_t) 240, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 240, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 240, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 240, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 240, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 240, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 240, value.asInt64());
+
+ //uint32 (more than 8 bits) to other uints, ints
+ value = (uint32_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //uint32 (more than 15 bits) to other uints, ints
+ value = (uint32_t) 32770;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 32770, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 32770, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 32770, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 32770, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 32770, value.asInt64());
+
+ //uint32 (more than 16 bits) to other uints, ints
+ value = (uint32_t) 66000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 66000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 66000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 66000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 66000, value.asInt64());
+
+
+
+ //uint64 (in 7 bits) to other uints, ints
+ value = (uint64_t) 120;
+ BOOST_CHECK_EQUAL((uint8_t) 120, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 120, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 120, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 120, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 120, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 120, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 120, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 120, value.asInt64());
+
+ //uint64 (more than 7 bits) to other uints, ints
+ value = (uint64_t) 240;
+ BOOST_CHECK_EQUAL((uint8_t) 240, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 240, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 240, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 240, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 240, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 240, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 240, value.asInt64());
+
+ //uint64 (more than 8 bits) to other uints, ints
+ value = (uint64_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //uint64 (more than 15 bits) to other uints, ints
+ value = (uint64_t) 32770;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 32770, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 32770, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 32770, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 32770, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 32770, value.asInt64());
+
+ //uint64 (more than 16 bits) to other uints, ints
+ value = (uint64_t) 66000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 66000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 66000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 66000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 66000, value.asInt64());
+
+ //uint64 (more than 31 bits) to other uints, ints
+ value = (uint64_t) 3000000000ul;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 3000000000ul, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 3000000000ul, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 3000000000ul, value.asInt64());
+
+ //uint64 (more than 32 bits) to other uints, ints
+ value = (uint64_t) 7000000000ull;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint64_t) 7000000000ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 7000000000ull, value.asInt64());
+
+ //uint64 (more than 63 bits) to other uints, ints
+ value = (uint64_t) 0x8000000000000000ull;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint64_t) 0x8000000000000000ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+}
+
+QPID_AUTO_TEST_CASE(testSizeConversionsInt)
+{
+ Variant value;
+
+ //int8 (positive in 7 bits)
+ value = (int8_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int8 (negative)
+ value = (int8_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+
+
+ //int16 (positive in 7 bits)
+ value = (int16_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int16 (positive in 8 bits)
+ value = (int16_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+ //int16 (positive in more than 8 bits)
+ value = (int16_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //int16 (negative in 7 bits)
+ value = (int16_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+ //int16 (negative in more than 7 bits)
+ value = (int16_t) -1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) -1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -1000, value.asInt64());
+
+
+
+ //int32 (positive in 7 bits)
+ value = (int32_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int32 (positive in 8 bits)
+ value = (int32_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+ //int32 (positive in more than 8 bits)
+ value = (int32_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //int32 (positive in more than 15 bits)
+ value = (int32_t) 40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 40000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 40000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 40000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 40000, value.asInt64());
+
+ //int32 (negative in 7 bits)
+ value = (int32_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+ //int32 (negative in more than 7 bits)
+ value = (int32_t) -1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) -1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -1000, value.asInt64());
+
+ //int32 (negative in more than 15 bits)
+ value = (int32_t) -40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) -40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -40000, value.asInt64());
+
+
+
+ //int64 (positive in 7 bits)
+ value = (int64_t) 100;
+ BOOST_CHECK_EQUAL((uint8_t) 100, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 100, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 100, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 100, value.asUint64());
+ BOOST_CHECK_EQUAL((int8_t) 100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) 100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 100, value.asInt64());
+
+ //int64 (positive in 8 bits)
+ value = (int64_t) 200;
+ BOOST_CHECK_EQUAL((uint8_t) 200, value.asUint8());
+ BOOST_CHECK_EQUAL((uint16_t) 200, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 200, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 200, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 200, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 200, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 200, value.asInt64());
+
+ //int64 (positive in more than 8 bits)
+ value = (int64_t) 1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 1000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 1000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 1000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) 1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) 1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 1000, value.asInt64());
+
+ //int64 (positive in more than 15 bits)
+ value = (int64_t) 40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint16_t) 40000, value.asUint16());
+ BOOST_CHECK_EQUAL((uint32_t) 40000, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 40000, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) 40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) 40000, value.asInt64());
+
+ //int64 (positive in more than 31 bits)
+ value = (int64_t) 3000000000ll;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint32_t) 3000000000ll, value.asUint32());
+ BOOST_CHECK_EQUAL((uint64_t) 3000000000ll, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 3000000000ll, value.asInt64());
+
+ //int64 (positive in more than 32 bits)
+ value = (int64_t) 5000000000ll;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((uint64_t) 5000000000ll, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) 5000000000ll, value.asInt64());
+
+ //int64 (negative in 7 bits)
+ value = (int64_t) -100;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int8_t) -100, value.asInt8());
+ BOOST_CHECK_EQUAL((int16_t) -100, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -100, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -100, value.asInt64());
+
+ //int64 (negative in more than 7 bits)
+ value = (int64_t) -1000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int16_t) -1000, value.asInt16());
+ BOOST_CHECK_EQUAL((int32_t) -1000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -1000, value.asInt64());
+
+ //int64 (negative in more than 15 bits)
+ value = (int64_t) -40000;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int32_t) -40000, value.asInt32());
+ BOOST_CHECK_EQUAL((int64_t) -40000, value.asInt64());
+
+ //int64 (negative in more than 31 bits)
+ value = (int64_t) -3000000000ll;
+ BOOST_CHECK_THROW(value.asUint8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt8(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+ BOOST_CHECK_EQUAL((int64_t) -3000000000ll, value.asInt64());
+}
+
+QPID_AUTO_TEST_CASE(testAssignment)
+{
+ Variant value("abc");
+ Variant other = value;
+ BOOST_CHECK_EQUAL(VAR_STRING, value.getType());
+ BOOST_CHECK_EQUAL(other.getType(), value.getType());
+ BOOST_CHECK_EQUAL(other.asString(), value.asString());
+
+ const uint32_t i(1000);
+ value = i;
+ BOOST_CHECK_EQUAL(VAR_UINT32, value.getType());
+ BOOST_CHECK_EQUAL(VAR_STRING, other.getType());
+}
+
+QPID_AUTO_TEST_CASE(testList)
+{
+ const std::string s("abc");
+ const float f(9.876f);
+ const int16_t x(1000);
+
+ Variant value = Variant::List();
+ value.asList().push_back(Variant(s));
+ value.asList().push_back(Variant(f));
+ value.asList().push_back(Variant(x));
+ BOOST_CHECK_EQUAL(3u, value.asList().size());
+ Variant::List::const_iterator i = value.asList().begin();
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_STRING, i->getType());
+ BOOST_CHECK_EQUAL(s, i->asString());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_FLOAT, i->getType());
+ BOOST_CHECK_EQUAL(f, i->asFloat());
+ i++;
+
+ BOOST_CHECK(i != value.asList().end());
+ BOOST_CHECK_EQUAL(VAR_INT16, i->getType());
+ BOOST_CHECK_EQUAL(x, i->asInt16());
+ i++;
+
+ BOOST_CHECK(i == value.asList().end());
+}
+
+QPID_AUTO_TEST_CASE(testMap)
+{
+ const std::string red("red");
+ const float pi(3.14f);
+ const int16_t x(1000);
+ const Uuid u(true);
+
+ Variant value = Variant::Map();
+ value.asMap()["colour"] = red;
+ value.asMap()["pi"] = pi;
+ value.asMap()["my-key"] = x;
+ value.asMap()["id"] = u;
+ BOOST_CHECK_EQUAL(4u, value.asMap().size());
+
+ BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["colour"].getType());
+ BOOST_CHECK_EQUAL(red, value.asMap()["colour"].asString());
+
+ BOOST_CHECK_EQUAL(VAR_FLOAT, value.asMap()["pi"].getType());
+ BOOST_CHECK_EQUAL(pi, value.asMap()["pi"].asFloat());
+
+ BOOST_CHECK_EQUAL(VAR_INT16, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(x, value.asMap()["my-key"].asInt16());
+
+ BOOST_CHECK_EQUAL(VAR_UUID, value.asMap()["id"].getType());
+ BOOST_CHECK_EQUAL(u, value.asMap()["id"].asUuid());
+
+ value.asMap()["my-key"] = "now it's a string";
+ BOOST_CHECK_EQUAL(VAR_STRING, value.asMap()["my-key"].getType());
+ BOOST_CHECK_EQUAL(std::string("now it's a string"), value.asMap()["my-key"].asString());
+}
+
+QPID_AUTO_TEST_CASE(testIsEqualTo)
+{
+ BOOST_CHECK_EQUAL(Variant("abc"), Variant("abc"));
+ BOOST_CHECK_EQUAL(Variant(1234), Variant(1234));
+
+ Variant a = Variant::Map();
+ a.asMap()["colour"] = "red";
+ a.asMap()["pi"] = 3.14f;
+ a.asMap()["my-key"] = 1234;
+ Variant b = Variant::Map();
+ b.asMap()["colour"] = "red";
+ b.asMap()["pi"] = 3.14f;
+ b.asMap()["my-key"] = 1234;
+ BOOST_CHECK_EQUAL(a, b);
+}
+
+QPID_AUTO_TEST_CASE(testEncoding)
+{
+ Variant a("abc");
+ a.setEncoding("utf8");
+ Variant b = a;
+ Variant map = Variant::Map();
+ map.asMap()["a"] = a;
+ map.asMap()["b"] = b;
+ BOOST_CHECK_EQUAL(a.getEncoding(), std::string("utf8"));
+ BOOST_CHECK_EQUAL(a.getEncoding(), b.getEncoding());
+ BOOST_CHECK_EQUAL(a.getEncoding(), map.asMap()["a"].getEncoding());
+ BOOST_CHECK_EQUAL(b.getEncoding(), map.asMap()["b"].getEncoding());
+ BOOST_CHECK_EQUAL(map.asMap()["a"].getEncoding(), map.asMap()["b"].getEncoding());
+}
+
+QPID_AUTO_TEST_CASE(testBufferEncoding)
+{
+ Variant a("abc");
+ a.setEncoding("utf8");
+ std::string buffer;
+
+ Variant::Map inMap, outMap;
+ inMap["a"] = a;
+
+ MapCodec::encode(inMap, buffer);
+ MapCodec::decode(buffer, outMap);
+ BOOST_CHECK_EQUAL(inMap, outMap);
+
+ inMap["b"] = Variant(std::string(65535, 'X'));
+ inMap["b"].setEncoding("utf16");
+ MapCodec::encode(inMap, buffer);
+ MapCodec::decode(buffer, outMap);
+ BOOST_CHECK_EQUAL(inMap, outMap);
+
+ inMap["fail"] = Variant(std::string(65536, 'X'));
+ inMap["fail"].setEncoding("utf16");
+ BOOST_CHECK_THROW(MapCodec::encode(inMap, buffer), std::exception);
+}
+
+QPID_AUTO_TEST_CASE(parse)
+{
+ Variant a;
+ a.parse("What a fine mess");
+ BOOST_CHECK(a.getType()==types::VAR_STRING);
+ a.parse("true");
+ BOOST_CHECK(a.getType()==types::VAR_BOOL);
+ a.parse("FalsE");
+ BOOST_CHECK(a.getType()==types::VAR_BOOL);
+ a.parse("3.1415926");
+ BOOST_CHECK(a.getType()==types::VAR_DOUBLE);
+ a.parse("-7.2e-15");
+ BOOST_CHECK(a.getType()==types::VAR_DOUBLE);
+ a.parse("9223372036854775807");
+ BOOST_CHECK(a.getType()==types::VAR_INT64);
+ a.parse("9223372036854775808");
+ BOOST_CHECK(a.getType()==types::VAR_UINT64);
+ a.parse("-9223372036854775807");
+ BOOST_CHECK(a.getType()==types::VAR_INT64);
+ a.parse("-9223372036854775808");
+ BOOST_CHECK(a.getType()==types::VAR_DOUBLE);
+ a.parse("18446744073709551615");
+ BOOST_CHECK(a.getType()==types::VAR_UINT64);
+ a.parse("18446744073709551616");
+ BOOST_CHECK(a.getType()==types::VAR_DOUBLE);
+}
+
+QPID_AUTO_TEST_CASE(described)
+{
+ Variant a;
+ BOOST_CHECK(!a.isDescribed());
+ a.getDescriptors().push_back("foo");
+ BOOST_CHECK(a.isDescribed());
+ BOOST_CHECK_EQUAL(a.getDescriptors().size(), 1U);
+ BOOST_CHECK_EQUAL(a.getDescriptors().front(), Variant("foo"));
+ a = 42;
+ BOOST_CHECK(a.isDescribed());
+ BOOST_CHECK_EQUAL(a.getDescriptors().size(), 1U);
+ BOOST_CHECK_EQUAL(a.getDescriptors().front(), Variant("foo"));
+ a.getDescriptors().push_back(33);
+ BOOST_CHECK_EQUAL(a.getDescriptors().size(), 2U);
+ BOOST_CHECK_EQUAL(a.getDescriptors().front(), Variant("foo"));
+ BOOST_CHECK_EQUAL(*(++a.getDescriptors().begin()), Variant(33));
+ a.getDescriptors().clear();
+ BOOST_CHECK(!a.isDescribed());
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/XmlClientSessionTest.cpp b/qpid/cpp/src/tests/XmlClientSessionTest.cpp
new file mode 100644
index 0000000000..bfa6ed096b
--- /dev/null
+++ b/qpid/cpp/src/tests/XmlClientSessionTest.cpp
@@ -0,0 +1,301 @@
+/*
+ *
+ * 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/client/Message.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>
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(XmlClientSessionTest)
+
+struct XmlFixture {
+ XmlFixture() {
+ qpid::sys::Shlib shlib(getLibPath("XML_LIB"));
+ }
+ ~XmlFixture() {}
+};
+
+using namespace qpid::client;
+using namespace qpid::client::arg;
+using namespace qpid::framing;
+using namespace qpid;
+
+using qpid::sys::Monitor;
+using std::string;
+using std::cout;
+using std::endl;
+
+
+class SubscribedLocalQueue : public LocalQueue {
+ private:
+ SubscriptionManager& subscriptions;
+ public:
+ SubscribedLocalQueue(SubscriptionManager& subs) : subscriptions(subs) {}
+ Message get () { return pop(); }
+ Message get (sys::Duration timeout) { return pop(timeout); }
+ 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 SessionFixture
+{
+ 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_FIXTURE_TEST_CASE(testXmlBinding, XmlFixture) {
+ 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(1*qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(m, m2.getData());
+}
+
+/**
+ * Ensure that multiple queues can be bound using the same routing key
+ */
+QPID_FIXTURE_TEST_CASE(testXMLBindMultipleQueues, XmlFixture) {
+ 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_FIXTURE_TEST_CASE(testXMLSendBadXML, XmlFixture) {
+ 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_FIXTURE_TEST_CASE(testXMLBadXQuery, XmlFixture) {
+ 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: double, string, and integer field values can all be bound to queries
+
+QPID_FIXTURE_TEST_CASE(testXmlBindingUntyped, XmlFixture) {
+ 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 $s external;"
+ "declare variable $i external;"
+ "declare variable $d external;"
+ "$s = 'string' and $i = 1 and $d < 1");
+ 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("s", "string");
+ message.getHeaders().setInt("i", 1);
+ message.getHeaders().setDouble("d", 0.5);
+ string m = "<message>Hi, Mom!</message>";
+ message.setData(m);
+
+ f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml");
+
+ Message m2 = localQueue.get(1*qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(m, m2.getData());
+}
+
+
+//### Test: double, string, and integer field values can all be bound to queries
+
+QPID_FIXTURE_TEST_CASE(testXmlBindingTyped, XmlFixture) {
+ 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 $s as xs:string external;"
+ "declare variable $i as xs:integer external;"
+ "declare variable $d external;" // XQilla bug when declaring xs:float, xs:double types? Fine if untyped, acts as float.
+ "$s = 'string' and $i = 1 and $d < 1");
+ 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("s", "string");
+ message.getHeaders().setInt("i", 1);
+ message.getHeaders().setDouble("d", 0.5);
+ string m = "<message>Hi, Mom!</message>";
+ message.setData(m);
+
+ f.session.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="xml");
+
+ Message m2 = localQueue.get(1*qpid::sys::TIME_SEC);
+ BOOST_CHECK_EQUAL(m, m2.getData());
+}
+
+
+//### Test: Each session can provide its own definition for a query name
+
+
+
+//### Test: Bindings persist, surviving broker restart
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py
new file mode 100755
index 0000000000..2838bd3f83
--- /dev/null
+++ b/qpid/cpp/src/tests/acl.py
@@ -0,0 +1,3959 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+import qpid
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import uuid4
+from qpid.testlib import TestBase010
+from qmf.console import Session
+from qpid.datatypes import Message
+import qpid.messaging
+from qpidtoollibs import BrokerAgent
+
+class ACLFile:
+ def __init__(self, policy='data_dir/policy.acl'):
+ self.f = open(policy,'w')
+
+ def write(self,line):
+ self.f.write(line)
+
+ def close(self):
+ self.f.close()
+
+class ACLTests(TestBase010):
+
+ # required for testing QMF methods
+ def get_messaging_connection(self, user, passwd):
+ parms = {'username':user, 'password':passwd, 'sasl_mechanisms':'PLAIN'}
+ brokerurl="%s:%s" %(self.broker.host, self.broker.port)
+ connection = qpid.messaging.Connection(brokerurl, **parms)
+ connection.open()
+ return connection
+
+ # For connection limit tests this function
+ # throws if the connection won't start
+ # returns a connection that the caller can close if he likes.
+ def get_connection(self, user, passwd):
+ socket = connect(self.broker.host, self.broker.port)
+ connection = Connection (sock=socket, username=user, password=passwd,
+ mechanism="PLAIN")
+ connection.start()
+ return connection
+
+ def get_session(self, user, passwd):
+ socket = connect(self.broker.host, self.broker.port)
+ connection = Connection (sock=socket, username=user, password=passwd,
+ mechanism="PLAIN")
+ connection.start()
+ return connection.session(str(uuid4()))
+
+ def port_i(self):
+ return int(self.defines["port-i"])
+
+ def port_u(self):
+ return int(self.defines["port-u"])
+
+ def port_q(self):
+ return int(self.defines["port-q"])
+
+ def get_session_by_port(self, user, passwd, byPort):
+ socket = connect(self.broker.host, byPort)
+ connection = Connection (sock=socket, username=user, password=passwd,
+ mechanism="PLAIN")
+ connection.start()
+ return connection.session(str(uuid4()))
+
+ def reload_acl(self):
+ result = None
+ try:
+ self.broker_access.reloadAclFile()
+ except Exception, e:
+ result = str(e)
+ return result
+
+ def acl_lookup(self, userName, action, aclObj, aclObjName, propMap):
+ result = {}
+ try:
+ result = self.broker_access.acl_lookup(userName, action, aclObj, aclObjName, propMap)
+ except Exception, e:
+ result['text'] = str(e)
+ result['result'] = str(e)
+ return result
+
+ def acl_lookupPublish(self, userName, exchange, key):
+ result = {}
+ try:
+ result = self.broker_access.acl_lookupPublish(userName, exchange, key)
+ except Exception, e:
+ result['text'] = str(e)
+ result['result'] = str(e)
+ return result
+
+ def get_acl_file(self):
+ return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl"))
+
+ def setUp(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ TestBase010.setUp(self)
+ self.startBrokerAccess()
+ self.reload_acl()
+
+ def tearDown(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ self.reload_acl()
+ TestBase010.tearDown(self)
+
+
+ def Lookup(self, userName, action, aclObj, aclObjName, propMap, expectedResult):
+ result = self.acl_lookup(userName, action, aclObj, aclObjName, propMap)
+ if (result['result'] != expectedResult):
+ suffix = ', [ERROR: Expected= ' + expectedResult
+ if (result['result'] is None):
+ suffix = suffix + ', Exception= ' + result['text'] + ']'
+ else:
+ suffix = suffix + ', Actual= ' + result['result'] + ']'
+ self.fail('Lookup: name=' + userName + ', action=' + action + ', aclObj=' + aclObj + ', aclObjName=' + aclObjName + ', propertyMap=' + str(propMap) + suffix)
+
+
+ def LookupPublish(self, userName, exchName, keyName, expectedResult):
+ result = self.acl_lookupPublish(userName, exchName, keyName)
+ if (result['result'] != expectedResult):
+ suffix = ', [ERROR: Expected= ' + expectedResult
+ if (result['result'] is None):
+ suffix = suffix + ', Exception= ' + result['text'] + ']'
+ else:
+ suffix = suffix + ', Actual= ' + result['result'] + ']'
+ self.fail('LookupPublish: name=' + userName + ', exchange=' + exchName + ', key=' + keyName + suffix)
+
+ def AllBut(self, allList, removeList):
+ tmpList = allList[:]
+ for item in removeList:
+ try:
+ tmpList.remove(item)
+ except Exception, e:
+ self.fail("ERROR in AllBut() \nallList = %s \nremoveList = %s \nerror = %s " \
+ % (allList, removeList, e))
+ return tmpList
+
+ #=====================================
+ # ACL general tests
+ #=====================================
+
+ def test_deny_mode(self):
+ """
+ Test the deny all mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl allow bob@QPID create queue\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ try:
+ session.queue_declare(queue="deny_queue")
+ except qpid.session.SessionException, e:
+ if (403 == 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(403,e.args[0].error_code)
+
+ def test_allow_mode(self):
+ """
+ Test the allow all mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID bind exchange\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ try:
+ session.queue_declare(queue="allow_queue")
+ except qpid.session.SessionException, e:
+ if (403 == 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(403,e.args[0].error_code)
+
+
+ def test_allow_mode_with_specfic_allow_override(self):
+ """
+ Specific allow overrides a general deny
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group admins bob@QPID joe@QPID \n')
+ aclf.write('acl allow bob@QPID create queue \n')
+ aclf.write('acl deny admins create queue \n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue='zed')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow create queue request");
+
+
+ #=====================================
+ # ACL file format tests
+ #=====================================
+
+ def test_empty_groups(self):
+ """
+ Test empty groups
+ """
+ aclf = self.get_acl_file()
+ 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.find("Insufficient tokens for acl definition",0,len(result)) == -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 = self.get_acl_file()
+ aclf.write('acl group admins bob@QPID joe@QPID\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.find("Unknown ACL permission",0,len(result)) == -1):
+ self.fail(result)
+
+ def test_illegal_extension_lines(self):
+ """
+ Test illegal extension lines
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('group admins bob@QPID \n')
+ aclf.write(' \ \n')
+ aclf.write('joe@QPID \n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.find("contains an illegal extension",0,len(result)) == -1):
+ self.fail(result)
+
+ if (result.find("Non-continuation line must start with \"group\" or \"acl\"",0,len(result)) == -1):
+ self.fail(result)
+
+ def test_illegal_extension_lines(self):
+ """
+ Test proper extention lines
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group test1 joe@EXAMPLE.com \\ \n') # should be allowed
+ aclf.write(' jack@EXAMPLE.com \\ \n') # should be allowed
+ aclf.write('jill@TEST.COM \\ \n') # should be allowed
+ aclf.write('host/123.example.com@TEST.COM\n') # should be allowed
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ def test_nested_groups(self):
+ """
+ Test nested groups
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('group user-consume martin@QPID ted@QPID\n')
+ aclf.write('group group2 kim@QPID user-consume rob@QPID \n')
+ aclf.write('acl allow anonymous all all \n')
+ aclf.write('acl allow group2 create queue \n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('rob','rob')
+ try:
+ session.queue_declare(queue="rob_queue")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request");
+ self.fail("Error during queue create request");
+
+
+
+ def test_user_realm(self):
+ """
+ Test a user defined without a realm
+ Ex. group admin rajith
+ Note: a user name without a realm is interpreted as a group name
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group admin bob\n') # shouldn't be allowed
+ aclf.write('acl deny admin bind exchange\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.find("not defined yet.",0,len(result)) == -1):
+ self.fail(result)
+
+ def test_allowed_chars_for_username(self):
+ """
+ Test a user defined without a realm
+ Ex. group admin rajith
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group test1 joe@EXAMPLE.com\n') # should be allowed
+ aclf.write('group test2 jack_123-jill@EXAMPLE.com\n') # should be allowed
+ aclf.write('group test4 host/somemachine.example.com@EXAMPLE.COM\n') # should be allowed
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('group test1 joe$H@EXAMPLE.com\n') # shouldn't be allowed
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.find("Username \"joe$H@EXAMPLE.com\" contains illegal characters",0,len(result)) == -1):
+ self.fail(result)
+
+ #=====================================
+ # ACL validation tests
+ #=====================================
+
+ def test_illegal_queue_policy(self):
+ """
+ Test illegal queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ding\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "ding is not a valid value for 'policytype', possible values are one of"
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ def test_illegal_queuemaxsize_upper_limit_spec(self):
+ """
+ Test illegal queue policy
+ """
+ #
+ # Use maxqueuesize
+ #
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'queuemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'queuemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ #
+ # Use queuemaxsizeupperlimit
+ #
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizeupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'queuemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizeupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'queuemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+
+ def test_illegal_queuemaxcount_upper_limit_spec(self):
+ """
+ Test illegal queue policy
+ """
+ #
+ # Use maxqueuecount
+ #
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'queuemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'queuemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ #
+ # use maxqueuecountupperlimit
+ #
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'queuemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'queuemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_queuemaxsize_lower_limit_spec(self):
+ """
+ Test illegal queue policy
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizelowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'queuemaxsizelowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxsizelowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'queuemaxsizelowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+
+ def test_illegal_queuemaxcount_lower_limit_spec(self):
+ """
+ Test illegal queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountlowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'queuemaxcountlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 queuemaxcountlowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'queuemaxcountlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_filemaxsize_upper_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+ #
+ # Use filemaxsizeupperlimit
+ #
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizeupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizeupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxsizeupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+
+ def test_illegal_filemaxcount_upper_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+ #
+ # use maxfilecountupperlimit
+ #
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxcountupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_filemaxsize_lower_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizelowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxsizelowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxsizelowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxsizelowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+
+ def test_illegal_filemaxcount_lower_limit_spec(self):
+ """
+ Test illegal file policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountlowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'filemaxcountlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 filemaxcountlowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'filemaxcountlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_pages_lower_limit_spec(self):
+ """
+ Test illegal paged queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pageslowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'pageslowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pageslowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'pageslowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_pages_upper_limit_spec(self):
+ """
+ Test illegal paged queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pagesupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'pagesupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pagesupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'pagesupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_pagefactor_lower_limit_spec(self):
+ """
+ Test illegal paged queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pagefactorlowerlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'pagefactorlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pagefactorlowerlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'pagefactorlowerlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ def test_illegal_pagefactor_upper_limit_spec(self):
+ """
+ Test illegal paged queue policy
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pagefactorupperlimit=-1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "-1 is not a valid value for 'pagefactorupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=q2 pagefactorupperlimit=9223372036854775808\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ expected = "9223372036854775808 is not a valid value for 'pagefactorupperlimit', " \
+ "values should be between 0 and 9223372036854775807";
+ if (result.find(expected) == -1):
+ self.fail(result)
+
+
+ #=====================================
+ # ACL queue tests
+ #=====================================
+
+ def test_queue_allow_mode(self):
+ """
+ Test cases for queue acl in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID access queue name=q1\n')
+ aclf.write('acl deny bob@QPID create queue name=q1 durable=true\n')
+ aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
+ aclf.write('acl deny bob@QPID access queue name=q3\n')
+ aclf.write('acl deny bob@QPID delete queue name=q4\n')
+ aclf.write('acl deny bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n')
+ aclf.write('acl deny bob@QPID create queue name=q6 paging=true\n')
+ aclf.write('acl deny bob@QPID purge queue name=q7\n')
+ aclf.write('acl deny bob@QPID move queue name=q7\n')
+ aclf.write('acl deny bob@QPID move queue name=q8 queuename=q7\n')
+ aclf.write('acl deny bob@QPID redirect queue name=q7\n')
+ aclf.write('acl deny bob@QPID redirect queue name=q8 queuename=q7\n')
+ aclf.write('acl deny bob@QPID reroute queue name=q7\n')
+ aclf.write('acl deny bob@QPID reroute queue name=q8 exchangename=amq.fanout\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q1", durable=True)
+ self.fail("ACL should deny queue create request with name=q1 durable=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q1", durable=True, passive=True)
+ self.fail("ACL should deny queue passive declare request with name=q1 durable=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2 exclusive=true qpid.policy_type=ring");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring_strict"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2 exclusive=true qpid.policy_type=ring_strict");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", exclusive=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q2, qpid.max_size=500 and qpid.max_count=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.paging"] = True
+ session.queue_declare(queue="q6", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q6, qpid.paging=True");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 100
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q2, qpid.max_size=100 and qpid.max_count=200 ");
+ try:
+ session.queue_declare(queue="q3", exclusive=True)
+ session.queue_declare(queue="q4", durable=True)
+ except qpid.session.SessionException, e:
+ if (403 == 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(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ # some queues needs to be created for testing purge / move / reroute / redirect
+ session.queue_declare(queue="q7")
+ session.queue_declare(queue="q8")
+ session.queue_declare(queue="q9")
+ try:
+ session.queue_purge(queue="q7")
+ self.fail("ACL should deny queue purge request for q7");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q8")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue purge request for q8");
+
+ # as we use QMF methods, it is easier to use BrokerAgent from messaging.connection and not use session object as above
+ broker_agent = BrokerAgent(self.get_messaging_connection('bob','bob'))
+
+ try:
+ broker_agent.queueMoveMessages("q7", "q8", 0)
+ self.fail("ACL should deny queue move request from q7 to q8");
+ except Exception, e:
+ self.assertTrue("'error_code': 7" in e.args[0])
+ broker_agent = BrokerAgent(self.get_messaging_connection('bob','bob'))
+
+ try:
+ broker_agent.queueMoveMessages("q8", "q9", 0)
+ except Exception, e:
+ if ("'error_code': 7" in e.args[0]):
+ self.fail("ACL should allow queue move request from q8 to q9");
+
+ try:
+ broker_agent.queueMoveMessages("q9", "q8", 0)
+ except Exception, e:
+ if ("'error_code': 7" in e.args[0]):
+ self.fail("ACL should allow queue move request from q9 to q8");
+
+ try:
+ broker_agent.Redirect("q7", "q8")
+ self.fail("ACL should deny queue redirect request from q7 to q8");
+ except Exception, e:
+ self.assertTrue("'error_code': 7" in e.args[0])
+ broker_agent = BrokerAgent(self.get_messaging_connection('bob','bob'))
+
+ try:
+ broker_agent.Redirect("q8", "q9")
+ except Exception, e:
+ if ("'error_code': 7" in e.args[0]):
+ self.fail("ACL should allow queue redirect request from q8 to q9");
+
+ try:
+ broker_agent.Redirect("q9", "q8")
+ except Exception, e:
+ if ("'error_code': 7" in e.args[0]):
+ self.fail("ACL should allow queue redirect request from q9 to q8");
+
+ try:
+ broker_agent.getQueue('q7').reroute(0, False, "amq.fanout")
+ self.fail("ACL should deny queue reroute request from q7 to amq.fanout");
+ except Exception, e:
+ self.assertTrue("'error_code': 7" in e.args[0])
+ broker_agent = BrokerAgent(self.get_messaging_connection('bob','bob'))
+
+ try:
+ broker_agent.getQueue('q8').reroute(0, False, "amq.fanout")
+ self.fail("ACL should deny queue reroute request from q8 to amq.fanout");
+ except Exception, e:
+ self.assertTrue("'error_code': 7" in e.args[0])
+ broker_agent = BrokerAgent(self.get_messaging_connection('bob','bob'))
+
+ try:
+ broker_agent.getQueue('q8').reroute(0, False, "amq.direct")
+ except Exception, e:
+ if ("'error_code': 7" in e.args[0]):
+ self.fail("ACL should allow queue reroute request from q8 to amq.direct");
+
+ try:
+ broker_agent.getQueue('q9').reroute(0, False, "amq.fanout")
+ except Exception, e:
+ if ("'error_code': 7" in e.args[0]):
+ self.fail("ACL should allow queue reroute request from q9 to amq.fanout");
+
+ try:
+ session.queue_delete(queue="q4")
+ self.fail("ACL should deny queue delete request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_delete(queue="q3")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue delete request for q3");
+
+
+ def test_queue_deny_mode(self):
+ """
+ Test cases for queue acl in deny mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID access queue name=q1\n')
+ aclf.write('acl allow bob@QPID create queue name=q1 durable=true\n')
+ aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
+ aclf.write('acl allow bob@QPID access queue name=q3\n')
+ aclf.write('acl allow bob@QPID purge queue name=q3\n')
+ aclf.write('acl allow bob@QPID create queue name=q3\n')
+ aclf.write('acl allow bob@QPID create queue name=q4\n')
+ aclf.write('acl allow bob@QPID delete queue name=q4\n')
+ aclf.write('acl allow bob@QPID create queue name=q5 maxqueuesize=1000 maxqueuecount=100\n')
+ aclf.write('acl allow bob@QPID create queue name=q6 queuemaxsizelowerlimit=50 queuemaxsizeupperlimit=100 queuemaxcountlowerlimit=50 queuemaxcountupperlimit=100\n')
+ aclf.write('acl allow bob@QPID create queue name=q7 policytype=self-destruct\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q1", durable=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q1 durable=true");
+
+ try:
+ session.queue_declare(queue="q1", durable=True, passive=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue passive declare request with name=q1 durable=true passive=true");
+
+ try:
+ session.queue_declare(queue="q1", durable=False, passive=False)
+ self.fail("ACL should deny queue create request with name=q1 durable=true passive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q2", exclusive=False)
+ self.fail("ACL should deny queue create request with name=q2 exclusive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 200
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q5 maxqueuesize=500 maxqueuecount=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 100
+ queue_options["qpid.max_size"] = 500
+ session.queue_declare(queue="q5", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q5 maxqueuesize=500 maxqueuecount=200");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 49
+ queue_options["qpid.max_size"] = 100
+ session.queue_declare(queue="q6", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q6 maxqueuesize=100 maxqueuecount=49");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 101
+ queue_options["qpid.max_size"] = 100
+ session.queue_declare(queue="q6", arguments=queue_options)
+ self.fail("ACL should allow queue create request with name=q6 maxqueuesize=100 maxqueuecount=101");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 100
+ queue_options["qpid.max_size"] = 49
+ session.queue_declare(queue="q6", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q6 maxqueuesize=49 maxqueuecount=100");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 100
+ queue_options["qpid.max_size"] =101
+ session.queue_declare(queue="q6", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=q6 maxqueuesize=101 maxqueuecount=100");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.max_count"] = 50
+ queue_options["qpid.max_size"] = 50
+ session.queue_declare(queue="q6", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q6 maxqueuesize=50 maxqueuecount=50");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring"
+ session.queue_declare(queue="q2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q2 with exclusive=true policytype=ring");
+
+ try:
+ session.queue_declare(queue="q7", arguments={"qpid.policy_type": "ring"})
+ self.fail("ACL should not allow queue create request for q7 with policytype=ring");
+ except qpid.session.SessionException, e:
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="q7", arguments={"qpid.policy_type": "self-destruct"})
+ except qpid.session.SessionException, e:
+ self.fail("ACL should allow queue create request for q7 with policytype=self-destruct");
+
+ try:
+ session.queue_declare(queue="q3")
+ session.queue_declare(queue="q4")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for q3 and q4");
+
+ try:
+ session.queue_query(queue="q4")
+ self.fail("ACL should deny queue query request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q4")
+ self.fail("ACL should deny queue purge request for q4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_purge(queue="q3")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue purge request for q3");
+
+ try:
+ session.queue_query(queue="q3")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue query request for q3");
+
+ try:
+ session.queue_delete(queue="q3")
+ self.fail("ACL should deny queue delete request for q3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_delete(queue="q4")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue delete request for q4");
+
+ #=====================================
+ # ACL paged tests
+ #=====================================
+
+ def test_paged_allow_mode(self):
+ """
+ Test cases for paged acl in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID create queue name=qf1 pageslowerlimit=1000\n')
+ aclf.write('acl deny bob@QPID create queue name=qf2 pagesupperlimit=100\n')
+ aclf.write('acl deny bob@QPID create queue name=qf3 pagefactorlowerlimit=10\n')
+ aclf.write('acl deny bob@QPID create queue name=qf4 pagefactorupperlimit=1\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.paging"] = True
+ queue_options["qpid.max_pages_loaded"] = 500
+ session.queue_declare(queue="qf1", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qf1, qpid.paging=True, qpid.max_pages_loaded=500");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.paging"] = True
+ queue_options["qpid.max_pages_loaded"] = 500
+ session.queue_declare(queue="qf2", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qf2, qpid.paging=True, qpid.max_pages_loaded=500");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.paging"] = True
+ queue_options["qpid.page_factor"] = 5
+ session.queue_declare(queue="qf3", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qf3, qpid.paging=True, qpid.page_factor=5");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.paging"] = True
+ queue_options["qpid.page_factor"] = 5
+ session.queue_declare(queue="qf4", arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qf4, qpid.paging=True, qpid.page_factor=5");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+
+ def test_paged_deny_mode(self):
+ """
+ Test cases for paged acl in deny mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create queue name=qf1 pageslowerlimit=100 pagesupperlimit=1000\n')
+ aclf.write('acl allow bob@QPID create queue name=qf2 pagefactorlowerlimit=1 pagefactorupperlimit=10\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.paging"] = True
+ queue_options["qpid.max_pages_loaded"] = 500
+ session.queue_declare(queue="qf1", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qf1, qpid.paging=True, qpid.max_pages_loaded=500");
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.paging"] = True
+ queue_options["qpid.page_factor"] = 5
+ session.queue_declare(queue="qf2", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qf2, qpid.paging=True, qpid.page_factor=5");
+ session = self.get_session('bob','bob')
+
+
+ #=====================================
+ # ACL file tests
+ #=====================================
+
+ def test_file_allow_mode(self):
+ """
+ Test cases for file acl in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID access queue name=qf1\n')
+ aclf.write('acl deny bob@QPID create queue name=qf1 durable=true\n')
+ aclf.write('acl deny bob@QPID create queue name=qf2 exclusive=true policytype=ring\n')
+ aclf.write('acl deny bob@QPID access queue name=qf3\n')
+ aclf.write('acl deny bob@QPID purge queue name=qf3\n')
+ aclf.write('acl deny bob@QPID delete queue name=qf4\n')
+ aclf.write('acl deny bob@QPID create queue name=qf5 filemaxsizeupperlimit=1000 filemaxcountupperlimit=100\n')
+ aclf.write('acl deny bob@QPID create queue name=ABCDE queuemaxsizelowerlimit=900000 queuemaxsizeupperlimit=1024000 queuemaxcountlowerlimit=900 queuemaxcountupperlimit=2000 filemaxsizelowerlimit=0 filemaxsizeupperlimit=32 filemaxcountlowerlimit=0 filemaxcountupperlimit=4 policytype=ring durable=false autodelete=true\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ self.Lookup("bob@QPID", "create", "queue", "ABCDE", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"0",
+ "maxfilecount":"0" }, "deny")
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 200
+ queue_options["qpid.file_size"] = 500
+ session.queue_declare(queue="qf5", exclusive=True, durable=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qf5, qpid.file_size=500 and qpid.file_count=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 200
+ queue_options["qpid.file_size"] = 100
+ session.queue_declare(queue="qf2", exclusive=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qf2, qpid.file_size=100 and qpid.file_count=200 ");
+
+
+ def test_file_deny_mode(self):
+ """
+ Test cases for queue acl in deny mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID access queue name=qfd1\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd1 durable=true\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd2 exclusive=true policytype=ring\n')
+ aclf.write('acl allow bob@QPID access queue name=qfd3\n')
+ aclf.write('acl allow bob@QPID purge queue name=qfd3\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd3\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd4\n')
+ aclf.write('acl allow bob@QPID delete queue name=qfd4\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd5 filemaxsizeupperlimit=1000 filemaxcountupperlimit=100\n')
+ aclf.write('acl allow bob@QPID create queue name=qfd6 filemaxsizelowerlimit=50 filemaxsizeupperlimit=100 filemaxcountlowerlimit=50 filemaxcountupperlimit=100\n')
+ aclf.write('acl allow bob@QPID create queue name=ABCDE queuemaxsizelowerlimit=900000 queuemaxsizeupperlimit=1024000 queuemaxcountlowerlimit=900 queuemaxcountupperlimit=2000 filemaxsizelowerlimit=0 filemaxsizeupperlimit=32 filemaxcountlowerlimit=0 filemaxcountupperlimit=4 policytype=ring durable=false autodelete=true\n')
+ aclf.write('acl allow bob@QPID create queue name=FGHIJ queuemaxsizelowerlimit=900000 queuemaxsizeupperlimit=1024000 queuemaxcountlowerlimit=900 queuemaxcountupperlimit=2000 filemaxsizelowerlimit=2 filemaxsizeupperlimit=32 filemaxcountlowerlimit=0 filemaxcountupperlimit=4 policytype=ring durable=false autodelete=true\n')
+ aclf.write('acl allow bob@QPID create queue name=KLMNO queuemaxsizelowerlimit=900000 queuemaxsizeupperlimit=1024000 queuemaxcountlowerlimit=900 queuemaxcountupperlimit=2000 filemaxsizelowerlimit=0 filemaxsizeupperlimit=0 filemaxcountlowerlimit=0 filemaxcountupperlimit=4 policytype=ring durable=false autodelete=true\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ self.Lookup("bob@QPID", "create", "queue", "ABCDE", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"0",
+ "maxfilecount":"0" }, "allow")
+
+ self.Lookup("bob@QPID", "create", "queue", "FGHIJ", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"1",
+ "maxfilecount":"0" }, "deny")
+
+ self.Lookup("bob@QPID", "create", "queue", "FGHIJ", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"2",
+ "maxfilecount":"0" }, "allow")
+
+ self.Lookup("bob@QPID", "create", "queue", "FGHIJ", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"32",
+ "maxfilecount":"0" }, "allow")
+
+ self.Lookup("bob@QPID", "create", "queue", "FGHIJ", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"33",
+ "maxfilecount":"0" }, "deny")
+
+ self.Lookup("bob@QPID", "create", "queue", "KLMNO", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"0",
+ "maxfilecount":"0" }, "allow")
+
+ self.Lookup("bob@QPID", "create", "queue", "KLMNO", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"17",
+ "maxfilecount":"0" }, "allow")
+
+ self.Lookup("bob@QPID", "create", "queue", "KLMNO", {"durable":"false",
+ "autodelete":"true",
+ "exclusive":"false",
+ "alternate":"",
+ "policytype":"ring",
+ "maxqueuesize":"1024000",
+ "maxqueuecount":"1000",
+ "maxfilesize":"33",
+ "maxfilecount":"0" }, "allow")
+
+ try:
+ session.queue_declare(queue="qfd1", durable=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qfd1 durable=true");
+
+ try:
+ session.queue_declare(queue="qfd1", durable=True, passive=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue passive declare request with name=qfd1 durable=true passive=true");
+
+ try:
+ session.queue_declare(queue="qfd1", durable=False, passive=False)
+ self.fail("ACL should deny queue create request with name=qfd1 durable=true passive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qfd2", exclusive=False)
+ self.fail("ACL should deny queue create request with name=qfd2 exclusive=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 200
+ queue_options["qpid.file_size"] = 500
+ session.queue_declare(queue="qfd5", durable=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd5 filemaxsizeupperlimit=500 filemaxcountupperlimit=200");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 100
+ queue_options["qpid.file_size"] = 500
+ session.queue_declare(queue="qfd5", durable=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qfd5 filemaxsizeupperlimit=500 filemaxcountupperlimit=200");
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 49
+ queue_options["qpid.file_size"] = 100
+ session.queue_declare(queue="qfd6", durable=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd6 filemaxsizeupperlimit=100 filemaxcountupperlimit=49");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 101
+ queue_options["qpid.file_size"] = 100
+ session.queue_declare(queue="qfd6", durable=True, arguments=queue_options)
+ self.fail("ACL should allow queue create request with name=qfd6 filemaxsizeupperlimit=100 filemaxcountupperlimit=101");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 100
+ queue_options["qpid.file_size"] = 49
+ session.queue_declare(queue="qfd6", durable=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd6 filemaxsizeupperlimit=49 filemaxcountupperlimit=100");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 100
+ queue_options["qpid.file_size"] =101
+ session.queue_declare(queue="qfd6", durable=True, arguments=queue_options)
+ self.fail("ACL should deny queue create request with name=qfd6 filemaxsizeupperlimit=101 filemaxcountupperlimit=100");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.file_count"] = 50
+ queue_options["qpid.file_size"] = 50
+ session.queue_declare(queue="qfd6", durable=True, arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qfd6 filemaxsizeupperlimit=50 filemaxcountupperlimit=50");
+
+
+ #=====================================
+ # ACL exchange tests
+ #=====================================
+
+ def test_exchange_acl_allow_mode(self):
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue="baz")
+
+ """
+ Test cases for exchange acl in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID access exchange name=testEx\n')
+ aclf.write('acl deny bob@QPID create exchange name=testEx durable=true\n')
+ aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n')
+ aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
+ aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID unbind exchange name=myEx queuename=q1 routingkey=rk1\n')
+ aclf.write('acl deny bob@QPID delete exchange name=myEx\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.exchange_declare(exchange='myEx', type='direct')
+
+ try:
+ session.exchange_declare(exchange='testEx', durable=True)
+ self.fail("ACL should deny exchange create request with name=testEx durable=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='testEx', durable=True, passive=True)
+ self.fail("ACL should deny passive exchange declare request with name=testEx durable=true passive=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='testEx', type='direct', durable=False)
+ except qpid.session.SessionException, e:
+ print e
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true");
+
+ try:
+ session.exchange_declare(exchange='ex1', type='direct')
+ self.fail("ACL should deny exchange create request with name=ex1 type=direct");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myXml', type='direct')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for myXml with any parameter");
+
+ try:
+ session.exchange_query(name='myEx')
+ self.fail("ACL should deny exchange query request for myEx");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*')
+ self.fail("ACL should deny exchange bound request for myEx with queuename=q1 and routing_key='rk1.*' ");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='amq.topic')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange query request for exchange='amq.topic'");
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk2.*'");
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1')
+ self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q1', binding_key='x')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q1', binding_key='x'");
+
+ try:
+ session.exchange_bind(exchange='myEx', queue='q2', binding_key='rk1')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='myEx', queue='q2', binding_key='rk1'");
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q1', binding_key='rk1')
+ self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q1', binding_key='x')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q1', binding_key='x'");
+
+ try:
+ session.exchange_unbind(exchange='myEx', queue='q2', binding_key='rk1')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='myEx', queue='q2', binding_key='rk1'");
+
+ try:
+ session.exchange_delete(exchange='myEx')
+ self.fail("ACL should deny exchange delete request for myEx");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_delete(exchange='myXml')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange delete request for myXml");
+
+
+ def test_exchange_acl_deny_mode(self):
+ session = self.get_session('bob','bob')
+ session.queue_declare(queue='bar')
+
+ """
+ Test cases for exchange acl in deny mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create exchange name=myEx durable=true\n')
+ aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
+ aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
+ aclf.write('acl allow bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
+ aclf.write('acl allow bob@QPID delete exchange name=myEx\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=True, passive=False)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange create request for myEx with durable=true and passive=false");
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ self.fail("ACL should deny exchange create request with name=myEx durable=false");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='foo.bar')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'");
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='baz', binding_key='foo.bar')
+ self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bind(exchange='amq.topic', queue='bar', binding_key='fooz.bar')
+ self.fail("ACL should deny exchange bind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='foo.bar')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange unbind request for exchange='amq.topic', queue='bar', binding_key='foor.bar'");
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='baz', binding_key='foo.bar')
+ self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='baz', binding_key='foo.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_unbind(exchange='amq.topic', queue='bar', binding_key='fooz.bar')
+ self.fail("ACL should deny exchange unbind request for exchange='amq.topic', queue='bar', binding_key='fooz.bar'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='amq.topic')
+ self.fail("ACL should deny exchange query request for amq.topic");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk2.*')
+ self.fail("ACL should deny exchange bound request for amq.topic with queuename=q1 and routing_key='rk2.*' ");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_query(name='myEx')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange query request for exchange='myEx'");
+
+ try:
+ session.exchange_bound(exchange='myEx', queue='q1', binding_key='rk1.*')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange bound request for myEx with queuename=q1 and binding_key='rk1.*'");
+
+ try:
+ session.exchange_delete(exchange='myEx')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow exchange delete request for myEx");
+
+ def test_create_and_delete_exchange_via_qmf(self):
+ """
+ Test acl is enforced when creating/deleting via QMF
+ methods. Note that in order to be able to send the QMF methods
+ and receive the responses a significant amount of permissions
+ need to be enabled (TODO: can the set below be narrowed down
+ at all?)
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create exchange\n')
+ aclf.write('acl allow admin@QPID delete exchange\n')
+ aclf.write('acl allow all access exchange\n')
+ aclf.write('acl allow all bind exchange\n')
+ aclf.write('acl allow all create queue\n')
+ aclf.write('acl allow all access queue\n')
+ aclf.write('acl allow all delete queue\n')
+ aclf.write('acl allow all consume queue\n')
+ aclf.write('acl allow all access method\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+ bob.create_exchange("my-exchange") #should pass
+ #cleanup by deleting exchange
+ try:
+ bob.delete_exchange("my-exchange") #should fail
+ self.fail("ACL should deny exchange delete request for my-exchange");
+ except Exception, e:
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+ admin = BrokerAdmin(self.config.broker, "admin", "admin")
+ admin.delete_exchange("my-exchange") #should pass
+
+ anonymous = BrokerAdmin(self.config.broker)
+ try:
+ anonymous.create_exchange("another-exchange") #should fail
+ self.fail("ACL should deny exchange create request for another-exchange");
+ except Exception, e:
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+
+ def test_qmf_query(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all access exchange\n')
+ aclf.write('acl allow all bind exchange\n')
+ aclf.write('acl allow all create queue\n')
+ aclf.write('acl allow all access queue\n')
+ aclf.write('acl allow all delete queue\n')
+ aclf.write('acl allow all consume queue\n')
+ aclf.write('acl allow all access method\n')
+ aclf.write('acl allow bob@QPID access query name=org.apache.qpid.broker:queue:q1\n')
+ aclf.write('acl allow bob@QPID access query schemaclass=exchange\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+
+ try:
+ bob.query(object_name="org.apache.qpid.broker:queue:q1")
+ except Exception, e:
+ if ("unauthorized-access:" in e.args[0]):
+ self.fail("ACL should allow queue QMF query for q1");
+
+ try:
+ bob.query(object_name="org.apache.qpid.broker:queue:q2")
+ self.fail("ACL should deny queue QMF query for q2");
+ except Exception, e:
+ self.assertTrue("unauthorized-access:" in e.args[0])
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+
+ try:
+ bob.query(class_name="binding")
+ self.fail("ACL should deny class binding QMF query");
+ except Exception, e:
+ self.assertTrue("unauthorized-access:" in e.args[0])
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+
+ try:
+ bob.query(class_name="exchange")
+ except Exception, e:
+ if ("unauthorized-access:" in e.args[0]):
+ self.fail("ACL should allow class exchange QMF query");
+
+
+ #=====================================
+ # ACL consume tests
+ #=====================================
+
+ def test_consume_allow_mode(self):
+ """
+ Test cases for consume in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny bob@QPID consume queue name=q1\n')
+ aclf.write('acl deny bob@QPID consume queue name=q2\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ try:
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.queue_declare(queue='q3')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow create queue request");
+
+ try:
+ session.message_subscribe(queue='q1', destination='myq1')
+ self.fail("ACL should deny subscription for queue='q1'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_subscribe(queue='q2', destination='myq1')
+ self.fail("ACL should deny subscription for queue='q2'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_subscribe(queue='q3', destination='myq3')
+ session.message_cancel(destination='myq3')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow subscription for q3");
+
+
+ def test_consume_deny_mode(self):
+ """
+ Test cases for consume in allow mode
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID consume queue name=q1\n')
+ aclf.write('acl allow bob@QPID consume queue name=q2\n')
+ aclf.write('acl allow bob@QPID create queue\n')
+ aclf.write('acl allow anonymous all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ try:
+ session.queue_declare(queue='q1')
+ session.queue_declare(queue='q2')
+ session.queue_declare(queue='q3')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow create queue request");
+
+ try:
+ session.message_subscribe(queue='q1', destination='myq1')
+ session.message_subscribe(queue='q2', destination='myq2')
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow subscription for q1 and q2");
+
+ try:
+ session.message_subscribe(queue='q3', destination='myq3')
+ self.fail("ACL should deny subscription for queue='q3'");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+
+ #=====================================
+ # ACL publish tests
+ #=====================================
+
+ def test_publish_acl_allow_mode(self):
+ """
+ Test various publish acl
+ """
+ aclf = self.get_acl_file()
+ 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 deny bob@QPID publish exchange name=amq.default routingkey=restricted\n")
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ props = session.delivery_properties(routing_key="rk1")
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.topic", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.topic");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == 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 (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk2");
+
+ self.LookupPublish("bob@QPID", "", "restricted", "deny")
+ self.LookupPublish("bob@QPID", "", "another", "allow")
+ self.LookupPublish("joe@QPID", "", "restricted", "allow")
+
+
+ def test_publish_acl_deny_mode(self):
+ """
+ Test various publish acl
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
+ aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n')
+ aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n')
+ aclf.write('acl allow bob@QPID create exchange\n')
+ aclf.write("acl allow bob@QPID publish exchange name=amq.default routingkey=unrestricted\n")
+ aclf.write('acl allow anonymous all all \n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ props = session.delivery_properties(routing_key="rk2")
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk2");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.topic", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.topic with any routing key");
+
+ try:
+ session.exchange_declare(exchange='myEx', type='direct', durable=False)
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange myEx with routing key=rk2");
+
+ props = session.delivery_properties(routing_key="rk1")
+
+ try:
+ session.message_transfer(destination="myEx", message=Message(props,"Test"))
+ self.fail("ACL should deny message transfer to name=myEx routingkey=rk1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk1");
+
+ self.LookupPublish("bob@QPID", "", "unrestricted", "allow")
+ self.LookupPublish("bob@QPID", "", "another", "deny")
+ self.LookupPublish("joe@QPID", "", "unrestricted", "deny")
+
+
+ #=====================================
+ # ACL broker configuration tests
+ #=====================================
+
+ def test_broker_timestamp_config(self):
+ """
+ Test ACL control of the broker timestamp configuration
+ """
+ aclf = self.get_acl_file()
+ # enable lots of stuff to allow QMF to work
+ aclf.write('acl allow all create exchange\n')
+ aclf.write('acl allow all access exchange\n')
+ aclf.write('acl allow all bind exchange\n')
+ aclf.write('acl allow all publish exchange\n')
+ aclf.write('acl allow all create queue\n')
+ aclf.write('acl allow all access queue\n')
+ aclf.write('acl allow all delete queue\n')
+ aclf.write('acl allow all consume queue\n')
+ aclf.write('acl allow all access method\n')
+ # this should let bob access the timestamp configuration
+ aclf.write('acl allow bob@QPID access broker\n')
+ aclf.write('acl allow bob@QPID update broker\n')
+ aclf.write('acl allow admin@QPID all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ ts = None
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+ ts = bob.get_timestamp_cfg() #should work
+ bob.set_timestamp_cfg(ts); #should work
+
+ obo = BrokerAdmin(self.config.broker, "obo", "obo")
+ try:
+ ts = obo.get_timestamp_cfg() #should fail
+ failed = False
+ except Exception, e:
+ failed = True
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+ assert(failed)
+
+ try:
+ obo.set_timestamp_cfg(ts) #should fail
+ failed = False
+ except Exception, e:
+ failed = True
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+ assert(failed)
+
+ admin = BrokerAdmin(self.config.broker, "admin", "admin")
+ ts = admin.get_timestamp_cfg() #should pass
+ admin.set_timestamp_cfg(ts) #should pass
+
+
+
+ #=====================================
+ # QMF Functional tests
+ #=====================================
+
+ def test_qmf_functional_tests(self):
+ """
+ Test using QMF method hooks into ACL logic
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group admins moe@COMPANY.COM \\\n')
+ aclf.write(' larry@COMPANY.COM \\\n')
+ aclf.write(' curly@COMPANY.COM \\\n')
+ aclf.write(' shemp@COMPANY.COM\n')
+ aclf.write('group auditors aaudit@COMPANY.COM baudit@COMPANY.COM caudit@COMPANY.COM \\\n')
+ aclf.write(' daudit@COMPANY.COM eaduit@COMPANY.COM eaudit@COMPANY.COM\n')
+ aclf.write('group tatunghosts tatung01@COMPANY.COM \\\n')
+ aclf.write(' tatung02/x86.build.company.com@COMPANY.COM \\\n')
+ aclf.write(' tatung03/x86.build.company.com@COMPANY.COM \\\n')
+ aclf.write(' tatung04/x86.build.company.com@COMPANY.COM \n')
+ aclf.write('group publishusers publish@COMPANY.COM x-pubs@COMPANY.COM\n')
+ aclf.write('acl allow-log admins all all\n')
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ aclf.write('acl allow-log auditors all exchange name=company.topic routingkey=private.audit.*\n')
+ aclf.write('acl allow-log tatunghosts publish exchange name=company.topic routingkey=tatung.*\n')
+ aclf.write('acl allow-log tatunghosts publish exchange name=company.direct routingkey=tatung-service-queue\n')
+ aclf.write('acl allow-log publishusers create queue\n')
+ aclf.write('acl allow-log publishusers publish exchange name=qpid.management routingkey=broker\n')
+ aclf.write('acl allow-log publishusers publish exchange name=qmf.default.topic routingkey=*\n')
+ aclf.write('acl allow-log publishusers publish exchange name=qmf.default.direct routingkey=*\n')
+ aclf.write('acl allow-log all bind exchange name=company.topic routingkey=tatung.*\n')
+ aclf.write('acl allow-log all bind exchange name=company.direct routingkey=tatung-service-queue\n')
+ aclf.write('acl allow-log all consume queue\n')
+ aclf.write('acl allow-log all access exchange\n')
+ aclf.write('acl allow-log all access queue\n')
+ aclf.write('acl allow-log all create queue name=tmp.* durable=false autodelete=true exclusive=true policytype=ring\n')
+ aclf.write('acl allow mrQ create queue queuemaxsizelowerlimit=100 queuemaxsizeupperlimit=200 queuemaxcountlowerlimit=300 queuemaxcountupperlimit=400\n')
+ aclf.write('acl deny-log all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ #
+ # define some group lists
+ #
+ g_admins = ['moe@COMPANY.COM', \
+ 'larry@COMPANY.COM', \
+ 'curly@COMPANY.COM', \
+ 'shemp@COMPANY.COM']
+
+ g_auditors = [ 'aaudit@COMPANY.COM','baudit@COMPANY.COM','caudit@COMPANY.COM', \
+ 'daudit@COMPANY.COM','eaduit@COMPANY.COM','eaudit@COMPANY.COM']
+
+ g_tatunghosts = ['tatung01@COMPANY.COM', \
+ 'tatung02/x86.build.company.com@COMPANY.COM', \
+ 'tatung03/x86.build.company.com@COMPANY.COM', \
+ 'tatung04/x86.build.company.com@COMPANY.COM']
+
+ g_publishusers = ['publish@COMPANY.COM', 'x-pubs@COMPANY.COM']
+
+ g_public = ['jpublic@COMPANY.COM', 'me@yahoo.com']
+
+ g_all = g_admins + g_auditors + g_tatunghosts + g_publishusers + g_public
+
+ action_all = ['consume','publish','create','access','bind','unbind','delete','purge','update']
+
+ #
+ # Run some tests verifying against users who are in and who are out of given groups.
+ #
+
+ for u in g_admins:
+ self.Lookup(u, "create", "queue", "anything", {"durable":"true"}, "allow-log")
+ uInTest = g_auditors + g_admins
+ uOutTest = self.AllBut(g_all, uInTest)
+
+ for u in uInTest:
+ self.LookupPublish(u, "company.topic", "private.audit.This", "allow-log")
+
+ for u in uInTest:
+ for a in ['bind', 'unbind', 'access', 'publish']:
+ self.Lookup(u, a, "exchange", "company.topic", {"routingkey":"private.audit.This"}, "allow-log")
+
+ for u in uOutTest:
+ self.LookupPublish(u, "company.topic", "private.audit.This", "deny-log")
+ self.Lookup(u, "bind", "exchange", "company.topic", {"routingkey":"private.audit.This"}, "deny-log")
+
+ uInTest = g_admins + g_tatunghosts
+ uOutTest = self.AllBut(g_all, uInTest)
+
+ for u in uInTest:
+ self.LookupPublish(u, "company.topic", "tatung.this2", "allow-log")
+ self.LookupPublish(u, "company.direct", "tatung-service-queue", "allow-log")
+
+ for u in uOutTest:
+ self.LookupPublish(u, "company.topic", "tatung.this2", "deny-log")
+ self.LookupPublish(u, "company.direct", "tatung-service-queue", "deny-log")
+
+ for u in uOutTest:
+ for a in ["bind", "access"]:
+ self.Lookup(u, a, "exchange", "company.topic", {"routingkey":"tatung.this2"}, "allow-log")
+ self.Lookup(u, a, "exchange", "company.direct", {"routingkey":"tatung-service-queue"}, "allow-log")
+
+ uInTest = g_admins + g_publishusers
+ uOutTest = self.AllBut(g_all, uInTest)
+
+ for u in uInTest:
+ self.LookupPublish(u, "qpid.management", "broker", "allow-log")
+ self.LookupPublish(u, "qmf.default.topic", "this3", "allow-log")
+ self.LookupPublish(u, "qmf.default.direct", "this4", "allow-log")
+
+ for u in uOutTest:
+ self.LookupPublish(u, "qpid.management", "broker", "deny-log")
+ self.LookupPublish(u, "qmf.default.topic", "this3", "deny-log")
+ self.LookupPublish(u, "qmf.default.direct", "this4", "deny-log")
+
+ for u in uOutTest:
+ for a in ["bind"]:
+ self.Lookup(u, a, "exchange", "qpid.management", {"routingkey":"broker"}, "deny-log")
+ self.Lookup(u, a, "exchange", "qmf.default.topic", {"routingkey":"this3"}, "deny-log")
+ self.Lookup(u, a, "exchange", "qmf.default.direct", {"routingkey":"this4"}, "deny-log")
+ for a in ["access"]:
+ self.Lookup(u, a, "exchange", "qpid.management", {"routingkey":"broker"}, "allow-log")
+ self.Lookup(u, a, "exchange", "qmf.default.topic", {"routingkey":"this3"}, "allow-log")
+ self.Lookup(u, a, "exchange", "qmf.default.direct", {"routingkey":"this4"}, "allow-log")
+
+ # Test against queue size limits
+
+ self.Lookup('mrQ', 'create', 'queue', 'abc', {"maxqueuesize":"150", "maxqueuecount":"350"}, "allow")
+ self.Lookup('mrQ', 'create', 'queue', 'def', {"maxqueuesize":"99", "maxqueuecount":"350"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', 'uvw', {"maxqueuesize":"201", "maxqueuecount":"350"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', 'xyz', {"maxqueuesize":"150", "maxqueuecount":"299"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"150", "maxqueuecount":"401"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"0", "maxqueuecount":"401"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"150", "maxqueuecount":"0" }, "deny")
+
+
+ #=====================================
+ # Routingkey lookup using Topic Exchange tests
+ #=====================================
+
+ def test_topic_exchange_publish_tests(self):
+ """
+ Test using QMF method hooks into ACL logic
+ """
+ aclf = self.get_acl_file()
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ aclf.write('acl allow-log uPlain1@COMPANY publish exchange name=X routingkey=ab.cd.e\n')
+ aclf.write('acl allow-log uPlain2@COMPANY publish exchange name=X routingkey=.\n')
+ aclf.write('acl allow-log uStar1@COMPANY publish exchange name=X routingkey=a.*.b\n')
+ aclf.write('acl allow-log uStar2@COMPANY publish exchange name=X routingkey=*.x\n')
+ aclf.write('acl allow-log uStar3@COMPANY publish exchange name=X routingkey=x.x.*\n')
+ aclf.write('acl allow-log uHash1@COMPANY publish exchange name=X routingkey=a.#.b\n')
+ aclf.write('acl allow-log uHash2@COMPANY publish exchange name=X routingkey=a.#\n')
+ aclf.write('acl allow-log uHash3@COMPANY publish exchange name=X routingkey=#.a\n')
+ aclf.write('acl allow-log uHash4@COMPANY publish exchange name=X routingkey=a.#.b.#.c\n')
+ aclf.write('acl allow-log uMixed1@COMPANY publish exchange name=X routingkey=*.x.#.y\n')
+ aclf.write('acl allow-log uMixed2@COMPANY publish exchange name=X routingkey=a.#.b.*\n')
+ aclf.write('acl allow-log uMixed3@COMPANY publish exchange name=X routingkey=*.*.*.#\n')
+
+ aclf.write('acl allow-log all publish exchange name=X routingkey=MN.OP.Q\n')
+ aclf.write('acl allow-log all publish exchange name=X routingkey=M.*.N\n')
+ aclf.write('acl allow-log all publish exchange name=X routingkey=M.#.N\n')
+ aclf.write('acl allow-log all publish exchange name=X routingkey=*.M.#.N\n')
+
+ aclf.write('acl deny-log all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # aclKey: "ab.cd.e"
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd.e", "allow-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "abx.cd.e", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd..e.", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd.e.", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", ".ab.cd.e", "deny-log")
+ # aclKey: "."
+ self.LookupPublish("uPlain2@COMPANY", "X", ".", "allow-log")
+
+ # aclKey: "a.*.b"
+ self.LookupPublish("uStar1@COMPANY", "X", "a.xx.b", "allow-log")
+ self.LookupPublish("uStar1@COMPANY", "X", "a.b", "deny-log")
+ # aclKey: "*.x"
+ self.LookupPublish("uStar2@COMPANY", "X", "y.x", "allow-log")
+ self.LookupPublish("uStar2@COMPANY", "X", ".x", "allow-log")
+ self.LookupPublish("uStar2@COMPANY", "X", "x", "deny-log")
+ # aclKey: "x.x.*"
+ self.LookupPublish("uStar3@COMPANY", "X", "x.x.y", "allow-log")
+ self.LookupPublish("uStar3@COMPANY", "X", "x.x.", "allow-log")
+ self.LookupPublish("uStar3@COMPANY", "X", "x.x", "deny-log")
+ self.LookupPublish("uStar3@COMPANY", "X", "q.x.y", "deny-log")
+
+ # aclKey: "a.#.b"
+ self.LookupPublish("uHash1@COMPANY", "X", "a.b", "allow-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "a.x.b", "allow-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "a..x.y.zz.b", "allow-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "a.b.", "deny-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "q.x.b", "deny-log")
+
+ # aclKey: "a.#"
+ self.LookupPublish("uHash2@COMPANY", "X", "a", "allow-log")
+ self.LookupPublish("uHash2@COMPANY", "X", "a.b", "allow-log")
+ self.LookupPublish("uHash2@COMPANY", "X", "a.b.c", "allow-log")
+
+ # aclKey: "#.a"
+ self.LookupPublish("uHash3@COMPANY", "X", "a", "allow-log")
+ self.LookupPublish("uHash3@COMPANY", "X", "x.y.a", "allow-log")
+
+ # aclKey: "a.#.b.#.c"
+ self.LookupPublish("uHash4@COMPANY", "X", "a.b.c", "allow-log")
+ self.LookupPublish("uHash4@COMPANY", "X", "a.x.b.y.c", "allow-log")
+ self.LookupPublish("uHash4@COMPANY", "X", "a.x.x.b.y.y.c", "allow-log")
+
+ # aclKey: "*.x.#.y"
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.x.y", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.x.p.qq.y", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.a.x.y", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "aa.x.b.c", "deny-log")
+
+ # aclKey: "a.#.b.*"
+ self.LookupPublish("uMixed2@COMPANY", "X", "a.b.x", "allow-log")
+ self.LookupPublish("uMixed2@COMPANY", "X", "a.x.x.x.b.x", "allow-log")
+
+ # aclKey: "*.*.*.#"
+ self.LookupPublish("uMixed3@COMPANY", "X", "x.y.z", "allow-log")
+ self.LookupPublish("uMixed3@COMPANY", "X", "x.y.z.a.b.c", "allow-log")
+ self.LookupPublish("uMixed3@COMPANY", "X", "x.y", "deny-log")
+ self.LookupPublish("uMixed3@COMPANY", "X", "x", "deny-log")
+
+ # Repeat the keys with wildcard user spec
+ self.LookupPublish("uPlain1@COMPANY", "X", "MN.OP.Q", "allow-log")
+ self.LookupPublish("uStar1@COMPANY" , "X", "M.xx.N", "allow-log")
+ self.LookupPublish("uHash1@COMPANY" , "X", "M.N", "allow-log")
+ self.LookupPublish("uHash1@COMPANY" , "X", "M.x.N", "allow-log")
+ self.LookupPublish("uHash1@COMPANY" , "X", "M..x.y.zz.N", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.M.N", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.M.p.qq.N", "allow-log")
+
+ self.LookupPublish("dev@QPID", "X", "MN.OP.Q", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M.xx.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M.x.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M..x.y.zz.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "a.M.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "a.M.p.qq.N", "allow-log")
+
+ def test_topic_exchange_other_tests(self):
+ """
+ Test using QMF method hooks into ACL logic
+ """
+ action_list = ['access','bind','unbind']
+
+ aclf = self.get_acl_file()
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ for action in action_list:
+ aclf.write('acl allow-log uPlain1@COMPANY ' + action + ' exchange name=X routingkey=ab.cd.e\n')
+ aclf.write('acl allow-log uPlain2@COMPANY ' + action + ' exchange name=X routingkey=.\n')
+ aclf.write('acl allow-log uStar1@COMPANY ' + action + ' exchange name=X routingkey=a.*.b\n')
+ aclf.write('acl allow-log uStar2@COMPANY ' + action + ' exchange name=X routingkey=*.x\n')
+ aclf.write('acl allow-log uStar3@COMPANY ' + action + ' exchange name=X routingkey=x.x.*\n')
+ aclf.write('acl allow-log uHash1@COMPANY ' + action + ' exchange name=X routingkey=a.#.b\n')
+ aclf.write('acl allow-log uHash2@COMPANY ' + action + ' exchange name=X routingkey=a.#\n')
+ aclf.write('acl allow-log uHash3@COMPANY ' + action + ' exchange name=X routingkey=#.a\n')
+ aclf.write('acl allow-log uHash4@COMPANY ' + action + ' exchange name=X routingkey=a.#.b.#.c\n')
+ aclf.write('acl allow-log uMixed1@COMPANY ' + action + ' exchange name=X routingkey=*.x.#.y\n')
+ aclf.write('acl allow-log uMixed2@COMPANY ' + action + ' exchange name=X routingkey=a.#.b.*\n')
+ aclf.write('acl allow-log uMixed3@COMPANY ' + action + ' exchange name=X routingkey=*.*.*.#\n')
+
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=MN.OP.Q\n')
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=M.*.N\n')
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=M.#.N\n')
+ aclf.write('acl allow-log all ' + action + ' exchange name=X routingkey=*.M.#.N\n')
+
+ aclf.write('acl deny-log all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ for action in action_list:
+ # aclKey: "ab.cd.e"
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e"}, "allow-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e"}, "allow-log")
+
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e"}, "allow-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"abx.cd.e"}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd"}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd..e."}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"ab.cd.e."}, "deny-log")
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":".ab.cd.e"}, "deny-log")
+ # aclKey: "."
+ self.Lookup("uPlain2@COMPANY", action, "exchange", "X", {"routingkey":"."}, "allow-log")
+
+ # aclKey: "a.*.b"
+ self.Lookup("uStar1@COMPANY", action, "exchange", "X", {"routingkey":"a.xx.b"}, "allow-log")
+ self.Lookup("uStar1@COMPANY", action, "exchange", "X", {"routingkey":"a.b"}, "deny-log")
+ # aclKey: "*.x"
+ self.Lookup("uStar2@COMPANY", action, "exchange", "X", {"routingkey":"y.x"}, "allow-log")
+ self.Lookup("uStar2@COMPANY", action, "exchange", "X", {"routingkey":".x"}, "allow-log")
+ self.Lookup("uStar2@COMPANY", action, "exchange", "X", {"routingkey":"x"}, "deny-log")
+ # aclKey: "x.x.*"
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"x.x.y"}, "allow-log")
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"x.x."}, "allow-log")
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"x.x"}, "deny-log")
+ self.Lookup("uStar3@COMPANY", action, "exchange", "X", {"routingkey":"q.x.y"}, "deny-log")
+
+ # aclKey: "a.#.b"
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a.b"}, "allow-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a.x.b"}, "allow-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a..x.y.zz.b"}, "allow-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"a.b."}, "deny-log")
+ self.Lookup("uHash1@COMPANY", action, "exchange", "X", {"routingkey":"q.x.b"}, "deny-log")
+
+ # aclKey: "a.#"
+ self.Lookup("uHash2@COMPANY", action, "exchange", "X", {"routingkey":"a"}, "allow-log")
+ self.Lookup("uHash2@COMPANY", action, "exchange", "X", {"routingkey":"a.b"}, "allow-log")
+ self.Lookup("uHash2@COMPANY", action, "exchange", "X", {"routingkey":"a.b.c"}, "allow-log")
+
+ # aclKey: "#.a"
+ self.Lookup("uHash3@COMPANY", action, "exchange", "X", {"routingkey":"a"}, "allow-log")
+ self.Lookup("uHash3@COMPANY", action, "exchange", "X", {"routingkey":"x.y.a"}, "allow-log")
+
+ # aclKey: "a.#.b.#.c"
+ self.Lookup("uHash4@COMPANY", action, "exchange", "X", {"routingkey":"a.b.c"}, "allow-log")
+ self.Lookup("uHash4@COMPANY", action, "exchange", "X", {"routingkey":"a.x.b.y.c"}, "allow-log")
+ self.Lookup("uHash4@COMPANY", action, "exchange", "X", {"routingkey":"a.x.x.b.y.y.c"}, "allow-log")
+
+ # aclKey: "*.x.#.y"
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.x.y"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.x.p.qq.y"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.a.x.y"}, "deny-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"aa.x.b.c"}, "deny-log")
+
+ # aclKey: "a.#.b.*"
+ self.Lookup("uMixed2@COMPANY", action, "exchange", "X", {"routingkey":"a.b.x"}, "allow-log")
+ self.Lookup("uMixed2@COMPANY", action, "exchange", "X", {"routingkey":"a.x.x.x.b.x"}, "allow-log")
+
+ # aclKey: "*.*.*.#"
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x.y.z"}, "allow-log")
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x.y.z.a.b.c"}, "allow-log")
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x.y"}, "deny-log")
+ self.Lookup("uMixed3@COMPANY", action, "exchange", "X", {"routingkey":"x"}, "deny-log")
+
+ # Repeat the keys with wildcard user spec
+ self.Lookup("uPlain1@COMPANY", action, "exchange", "X", {"routingkey":"MN.OP.Q"}, "allow-log")
+ self.Lookup("uStar1@COMPANY" , action, "exchange", "X", {"routingkey":"M.xx.N"}, "allow-log")
+ self.Lookup("uHash1@COMPANY" , action, "exchange", "X", {"routingkey":"M.N"}, "allow-log")
+ self.Lookup("uHash1@COMPANY" , action, "exchange", "X", {"routingkey":"M.x.N"}, "allow-log")
+ self.Lookup("uHash1@COMPANY" , action, "exchange", "X", {"routingkey":"M..x.y.zz.N"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.M.N"}, "allow-log")
+ self.Lookup("uMixed1@COMPANY", action, "exchange", "X", {"routingkey":"a.M.p.qq.N"}, "allow-log")
+
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "MN.OP.Q"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M.xx.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M.x.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "M..x.y.zz.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "a.M.N"}, "allow-log")
+ self.Lookup("dev@QPID", action, "exchange", "X", {"routingkey": "a.M.p.qq.N"}, "allow-log")
+
+ #=====================================
+ # Connection limits
+ #=====================================
+
+ def test_connection_limits_cli_sets_all(self):
+
+ try:
+ sessiona1 = self.get_session_by_port('alice','alice', self.port_u())
+ sessiona2 = self.get_session_by_port('alice','alice', self.port_u())
+ except Exception, e:
+ self.fail("Could not create two connections for user alice: " + str(e))
+
+ # Third session should fail
+ try:
+ sessiona3 = self.get_session_by_port('alice','alice', self.port_u())
+ self.fail("Should not be able to create third connection for user alice")
+ except Exception, e:
+ result = None
+
+
+
+ def test_connection_limits_by_named_user(self):
+ """
+ Test ACL control connection limits
+ """
+ aclf = self.get_acl_file()
+ aclf.write('quota connections 2 aliceCL@QPID bobCL@QPID\n')
+ aclf.write('quota connections 0 evildude@QPID\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # By username should be able to connect twice per user
+ try:
+ conna1 = self.get_connection('aliceCL','aliceCL')
+ conna2 = self.get_connection('aliceCL','aliceCL')
+ except Exception, e:
+ self.fail("Could not create two connections for user aliceCL: " + str(e))
+
+ # Third session should fail
+ try:
+ conna3 = self.get_connection('aliceCL','aliceCL')
+ self.fail("Should not be able to create third connection for user aliceCL")
+ except Exception, e:
+ result = None
+
+ # Disconnecting should allow another session.
+ conna1.close()
+ try:
+ conna3 = self.get_connection('aliceCL','aliceCL')
+ except Exception, e:
+ self.fail("Could not recreate second connection for user aliceCL: " + str(e))
+
+ # By username should be able to connect twice per user
+ try:
+ connb1 = self.get_connection('bobCL','bobCL')
+ connb2 = self.get_connection('bobCL','bobCL')
+ except Exception, e:
+ self.fail("Could not create two connections for user bobCL: " + str(e))
+
+ # Third session should fail
+ try:
+ connb3 = self.get_connection('bobCL','bobCL')
+ self.fail("Should not be able to create third connection for user bobCL")
+ except Exception, e:
+ result = None
+
+
+ # User with quota of 0 is denied
+ try:
+ conne1 = self.get_connection('evildude','evildude')
+ self.fail("Should not be able to create a connection for user evildude")
+ except Exception, e:
+ result = None
+
+
+ # User not named in quotas is denied
+ try:
+ connc1 = self.get_connection('charlie','charlie')
+ self.fail("Should not be able to create a connection for user charlie")
+ except Exception, e:
+ result = None
+
+ # Clean up the connections
+ conna2.close()
+ conna3.close()
+ connb1.close()
+ connb2.close()
+
+
+
+ def test_connection_limits_by_unnamed_all(self):
+ """
+ Test ACL control connection limits
+ """
+ aclf = self.get_acl_file()
+ aclf.write('quota connections 2 aliceUA@QPID bobUA@QPID\n')
+ aclf.write('quota connections 1 all\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # By username should be able to connect twice per user
+ try:
+ connectiona1 = self.get_connection('aliceUA','alice')
+ connectiona2 = self.get_connection('aliceUA','alice')
+ except Exception, e:
+ self.fail("Could not create two connections for user alice: " + str(e))
+
+ # Third connection should fail
+ try:
+ connectiona3 = self.get_connection('aliceUA','alice')
+ self.fail("Should not be able to create third connection for user alice")
+ except Exception, e:
+ result = None
+
+ # By username should be able to connect twice per user
+ try:
+ connectionb1 = self.get_connection('bobUA','bob')
+ connectionb2 = self.get_connection('bobUA','bob')
+ except Exception, e:
+ self.fail("Could not create two connections for user bob: " + str(e))
+
+ # Third connection should fail
+ try:
+ connectionb3 = self.get_connection('bobUA','bob')
+ self.fail("Should not be able to create third connection for user bob")
+ except Exception, e:
+ result = None
+
+ # User not named in quotas gets 'all' quota
+ try:
+ connectionc1 = self.get_connection('charlieUA','charlie')
+ except Exception, e:
+ self.fail("Could not create one connection for user charlie: " + str(e))
+
+ # Next connection should fail
+ try:
+ connectionc2 = self.get_connection('charlieUA','charlie')
+ self.fail("Should not be able to create second connection for user charlie")
+ except Exception, e:
+ result = None
+
+ # Clean up the connections
+ connectiona1.close()
+ connectiona2.close()
+ connectionb1.close()
+ connectionb2.close()
+ connectionc1.close()
+
+
+ def test_connection_limits_by_group(self):
+ """
+ Test ACL control connection limits
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group stooges moeGR@QPID larryGR@QPID curlyGR@QPID\n')
+ aclf.write('quota connections 2 aliceGR@QPID bobGR@QPID\n')
+ aclf.write('quota connections 2 stooges charlieGR@QPID\n')
+ aclf.write('# user and groups may be overwritten. Should use last value\n')
+ aclf.write('quota connections 3 bobGR@QPID stooges\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # Alice gets 2
+ try:
+ connectiona1 = self.get_connection('aliceGR','alice')
+ connectiona2 = self.get_connection('aliceGR','alice')
+ except Exception, e:
+ self.fail("Could not create two connections for user alice: " + str(e))
+
+ # Third connection should fail
+ try:
+ connectiona3 = self.get_connection('aliceGR','alice')
+ self.fail("Should not be able to create third connection for user alice")
+ except Exception, e:
+ result = None
+
+ # Bob gets 3
+ try:
+ connectionb1 = self.get_connection('bobGR','bob')
+ connectionb2 = self.get_connection('bobGR','bob')
+ connectionb3 = self.get_connection('bobGR','bob')
+ except Exception, e:
+ self.fail("Could not create three connections for user bob: " + str(e))
+
+ # Fourth connection should fail
+ try:
+ connectionb4 = self.get_connection('bobGR','bob')
+ self.fail("Should not be able to create fourth connection for user bob")
+ except Exception, e:
+ result = None
+
+ # Moe gets 3
+ try:
+ connectionm1 = self.get_connection('moeGR','moe')
+ connectionm2 = self.get_connection('moeGR','moe')
+ connectionm3 = self.get_connection('moeGR','moe')
+ except Exception, e:
+ self.fail("Could not create three connections for user moe: " + str(e))
+
+ # Fourth connection should fail
+ try:
+ connectionb4 = self.get_connection('moeGR','moe')
+ self.fail("Should not be able to create fourth connection for user ,pe")
+ except Exception, e:
+ result = None
+
+ # User not named in quotas is denied
+ try:
+ connections1 = self.get_connection('shempGR','shemp')
+ self.fail("Should not be able to create a connection for user shemp")
+ except Exception, e:
+ result = None
+
+ # Clean up the connections
+ connectiona1.close()
+ connectiona2.close()
+ connectionb1.close()
+ connectionb2.close()
+ connectionb3.close()
+ connectionm1.close()
+ connectionm2.close()
+ connectionm3.close()
+
+
+ def test_connection_limits_by_ip_address(self):
+ """
+ Test ACL control connection limits by ip address
+ """
+ # By IP address should be able to connect twice per client address
+ try:
+ sessionb1 = self.get_session_by_port('alice','alice', self.port_i())
+ sessionb2 = self.get_session_by_port('bob','bob', self.port_i())
+ except Exception, e:
+ self.fail("Could not create two connections for client address: " + str(e))
+
+ # Third session should fail
+ try:
+ sessionb3 = self.get_session_by_port('charlie','charlie', self.port_i())
+ self.fail("Should not be able to create third connection for client address")
+ except Exception, e:
+ result = None
+
+ sessionb1.close()
+ sessionb2.close()
+
+ #=====================================
+ # User name substitution
+ #=====================================
+
+ def test_user_name_substitution(self):
+ """
+ Test name substitution internals, limits, and edge cases.
+ """
+ aclf = self.get_acl_file()
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ aclf.write('acl allow all create queue name=tmp-${userdomain}\n')
+ aclf.write('acl allow all create queue name=${userdomain}-tmp\n')
+ aclf.write('acl allow all create queue name=tmp-${userdomain}-tmp\n')
+ aclf.write('acl allow all create queue name=tmp-${userdomain}-tmp-${userdomain}\n')
+ aclf.write('acl allow all create queue name=temp0-${userdomain}\n')
+ aclf.write('acl allow all access queue name=temp0-${userdomain}\n')
+ aclf.write('acl allow all purge queue name=temp0-${userdomain}\n')
+ aclf.write('acl allow all consume queue name=temp0-${userdomain}\n')
+ aclf.write('acl allow all delete queue name=temp0-${userdomain}\n')
+ aclf.write('acl allow all create exchange name=temp0-${userdomain}\n')
+ aclf.write('acl allow all access exchange name=temp0-${userdomain}\n')
+ aclf.write('acl allow all bind exchange name=temp0-${userdomain}\n')
+ aclf.write('acl allow all unbind exchange name=temp0-${userdomain}\n')
+ aclf.write('acl allow all delete exchange name=temp0-${userdomain}\n')
+ aclf.write('acl allow all publish exchange name=temp0-${userdomain}\n')
+
+ aclf.write('acl allow all publish exchange name=X routingkey=${userdomain}.cd.e\n')
+ aclf.write('acl allow all publish exchange name=X routingkey=a.*.${userdomain}\n')
+ aclf.write('acl allow all publish exchange name=X routingkey=b.#.${userdomain}\n')
+ aclf.write('acl allow all publish exchange name=X routingkey=*.${userdomain}.#.y\n')
+
+ aclf.write('acl allow all create queue name=user-${user}\n')
+ aclf.write('acl allow all publish exchange name=U routingkey=${user}.cd.e\n')
+ aclf.write('acl allow all publish exchange name=U routingkey=a.*.${user}\n')
+ aclf.write('acl allow all publish exchange name=U routingkey=b.#.${user}\n')
+ aclf.write('acl allow all publish exchange name=U routingkey=*.${user}.#.y\n')
+
+ aclf.write('acl allow all create queue name=domain-${domain}\n')
+ aclf.write('acl allow all publish exchange name=D routingkey=${domain}.cd.e\n')
+ aclf.write('acl allow all publish exchange name=D routingkey=a.*.${domain}\n')
+ aclf.write('acl allow all publish exchange name=D routingkey=b.#.${domain}\n')
+ aclf.write('acl allow all publish exchange name=D routingkey=*.${domain}.#.y\n')
+
+ # Resolving ${user}_${domain} into ${userdomain} works for everything but routing keys
+ aclf.write('acl allow all create queue name=mixed-OK-${user}_${domain}\n')
+ # For routing keys ${user}_${domain} will be parsed into ${userdomain}.
+ # Routing keys not be found when the rule specifies ${user}_${domain}.
+ aclf.write('acl allow all publish exchange name=NOGO routingkey=${user}_${domain}.cd.e\n')
+ # This works since it is does not conflict with ${userdomain}
+ aclf.write('acl allow all publish exchange name=OK routingkey=${user}___${domain}.cd.e\n')
+
+ aclf.write('acl deny-log all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ self.Lookup("alice@QPID", "create", "queue", "tmp-alice_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "bob_QPID-tmp", {}, "allow")
+ self.Lookup("charlie@QPID", "create", "queue", "tmp-charlie_QPID-tmp", {}, "allow")
+ self.Lookup("dave@QPID", "create", "queue", "tmp-dave_QPID-tmp-dave_QPID", {}, "allow")
+ self.Lookup("ed@BIG.COM", "create", "queue", "tmp-ed_BIG_COM", {}, "allow")
+ self.Lookup("c.e.r@BIG.GER.COM", "create", "queue", "tmp-c_e_r_BIG_GER_COM", {}, "allow")
+ self.Lookup("c@", "create", "queue", "tmp-c_", {}, "allow")
+ self.Lookup("someuser", "create", "queue", "tmp-someuser", {}, "allow")
+
+ self.Lookup("alice@QPID", "create", "queue", "tmp-${user}", {}, "deny-log")
+
+ self.Lookup("bob@QPID", "create", "exchange", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "access", "exchange", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "bind", "exchange", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "unbind", "exchange", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "delete", "exchange", "temp0-bob_QPID", {}, "allow")
+ self.LookupPublish("bob@QPID", "temp0-bob_QPID", "x", "allow")
+
+ self.Lookup("bob@QPID", "create", "queue", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "access", "queue", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "purge", "queue", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "consume", "queue", "temp0-bob_QPID", {}, "allow")
+ self.Lookup("bob@QPID", "delete", "queue", "temp0-bob_QPID", {}, "allow")
+
+ self.Lookup("alice@QPID", "access", "queue", "temp0-bob_QPID", {}, "deny-log")
+
+ # aclKey: "${userdomain}.cd.e"
+ self.LookupPublish("uPlain1@COMPANY", "X", "uPlain1_COMPANY.cd.e", "allow")
+ # aclKey: "a.*.${userdomain}"
+ self.LookupPublish("uStar1@COMPANY", "X", "a.xx.uStar1_COMPANY", "allow")
+ self.LookupPublish("uStar1@COMPANY", "X", "a.b", "deny-log")
+ # aclKey: "b.#.${userdomain}"
+ self.LookupPublish("uHash1@COMPANY", "X", "b.uHash1_COMPANY", "allow")
+ self.LookupPublish("uHash1@COMPANY", "X", "b.x.uHash1_COMPANY", "allow")
+ self.LookupPublish("uHash1@COMPANY", "X", "b..x.y.zz.uHash1_COMPANY", "allow")
+ self.LookupPublish("uHash1@COMPANY", "X", "b.uHash1_COMPANY.", "deny-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "q.x.uHash1_COMPANY", "deny-log")
+ # aclKey: "*.${userdomain}.#.y"
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.uMixed1_COMPANY.y", "allow")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.uMixed1_COMPANY.p.qq.y", "allow")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.a.uMixed1_COMPANY.y", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "aa.uMixed1_COMPANY.b.c", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY.COM", "X", "a.uMixed1_COMPANY_COM.y", "allow")
+
+
+ self.Lookup("bob@QPID", "create", "queue", "user-bob", {}, "allow")
+ # aclKey: "${user}.cd.e"
+ self.LookupPublish("uPlain1@COMPANY", "U", "uPlain1.cd.e", "allow")
+ # aclKey: "a.*.${user}"
+ self.LookupPublish("uStar1@COMPANY", "U", "a.xx.uStar1", "allow")
+ self.LookupPublish("uStar1@COMPANY", "U", "a.b", "deny-log")
+ # aclKey: "b.#.${user}"
+ self.LookupPublish("uHash1@COMPANY", "U", "b.uHash1", "allow")
+ self.LookupPublish("uHash1@COMPANY", "U", "b.x.uHash1", "allow")
+ self.LookupPublish("uHash1@COMPANY", "U", "b..x.y.zz.uHash1", "allow")
+ self.LookupPublish("uHash1@COMPANY", "U", "b.uHash1.", "deny-log")
+ self.LookupPublish("uHash1@COMPANY", "U", "q.x.uHash1", "deny-log")
+ # aclKey: "*.${user}.#.y"
+ self.LookupPublish("uMixed1@COMPANY", "U", "a.uMixed1.y", "allow")
+ self.LookupPublish("uMixed1@COMPANY", "U", "a.uMixed1.p.qq.y", "allow")
+ self.LookupPublish("uMixed1@COMPANY", "U", "a.a.uMixed1.y", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY", "U", "aa.uMixed1.b.c", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY.COM", "U", "a.uMixed1.y", "allow")
+
+
+ self.Lookup("bob@QPID", "create", "queue", "domain-QPID", {}, "allow")
+ # aclKey: "${domain}.cd.e"
+ self.LookupPublish("uPlain1@COMPANY", "D", "COMPANY.cd.e", "allow")
+ # aclKey: "a.*.${domain}"
+ self.LookupPublish("uStar1@COMPANY", "D", "a.xx.COMPANY", "allow")
+ self.LookupPublish("uStar1@COMPANY", "D", "a.b", "deny-log")
+ # aclKey: "b.#.${domain}"
+ self.LookupPublish("uHash1@COMPANY", "D", "b.COMPANY", "allow")
+ self.LookupPublish("uHash1@COMPANY", "D", "b.x.COMPANY", "allow")
+ self.LookupPublish("uHash1@COMPANY", "D", "b..x.y.zz.COMPANY", "allow")
+ self.LookupPublish("uHash1@COMPANY", "D", "b.COMPANY.", "deny-log")
+ self.LookupPublish("uHash1@COMPANY", "D", "q.x.COMPANY", "deny-log")
+ # aclKey: "*.${domain}.#.y"
+ self.LookupPublish("uMixed1@COMPANY", "D", "a.COMPANY.y", "allow")
+ self.LookupPublish("uMixed1@COMPANY", "D", "a.COMPANY.p.qq.y", "allow")
+ self.LookupPublish("uMixed1@COMPANY", "D", "a.a.COMPANY.y", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY", "D", "aa.COMPANY.b.c", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY.COM", "D", "a.COMPANY_COM.y", "allow")
+
+ self.Lookup("uPlain1@COMPANY", "create", "queue", "mixed-OK-uPlain1_COMPANY", {}, "allow")
+ self.LookupPublish("uPlain1@COMPANY", "NOGO", "uPlain1_COMPANY.cd.e", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "OK", "uPlain1___COMPANY.cd.e", "allow")
+
+
+ #=====================================
+ # User name substitution details
+ #=====================================
+ # User name substitution allows for three flavors of keyword in the Acl file.
+ # Given a user name of bob.user@QPID.COM the keywords are normalized and resolve as follows:
+ # ${userdomain} - bob_user_QPID_COM
+ # ${user} - bob_user
+ # ${domain} - QPID_COM
+ #
+ # The following substitution tests are very similar but differ in the flavor of keyword used
+ # in the rules. The tests results using the different keywords differ slightly in how permissive
+ # the rules become.
+ # ${userdomain} limits access to one authenticated user
+ # ${user} limits access to a user name regardless of user's domain
+ # ${domain} limits access to a domain regardless of user name
+ #
+
+ def test_user_name_substitution_userdomain(self):
+ """
+ Test a setup where users can create, bind, and publish to a main exchange and queue.
+ Allow access to a single alternate exchange and queue.
+ """
+ aclf = self.get_acl_file()
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ # Create primary queue and exchange:
+ # allow predefined alternate
+ # deny any other alternate
+ # allow no alternate
+ aclf.write('acl allow all create queue name=${userdomain}-work alternate=${userdomain}-work2\n')
+ aclf.write('acl deny all create queue name=${userdomain}-work alternate=*\n')
+ aclf.write('acl allow all create queue name=${userdomain}-work\n')
+ aclf.write('acl allow all create exchange name=${userdomain}-work alternate=${userdomain}-work2\n')
+ aclf.write('acl deny all create exchange name=${userdomain}-work alternate=*\n')
+ aclf.write('acl allow all create exchange name=${userdomain}-work\n')
+ # Create backup queue and exchange
+ # Deny any alternate
+ aclf.write('acl deny all create queue name=${userdomain}-work2 alternate=*\n')
+ aclf.write('acl allow all create queue name=${userdomain}-work2\n')
+ aclf.write('acl deny all create exchange name=${userdomain}-work2 alternate=*\n')
+ aclf.write('acl allow all create exchange name=${userdomain}-work2\n')
+ # Bind/unbind primary exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all bind exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n')
+ aclf.write('acl allow all unbind exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n')
+ # Bind/unbind backup exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all bind exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n')
+ aclf.write('acl allow all unbind exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n')
+ # Access primary exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all access exchange name=${userdomain}-work routingkey=${userdomain} queuename=${userdomain}-work\n')
+ # Access backup exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all access exchange name=${userdomain}-work2 routingkey=${userdomain} queuename=${userdomain}-work2\n')
+ # Publish primary exchange
+ # Use only predefined routingkey
+ aclf.write('acl allow all publish exchange name=${userdomain}-work routingkey=${userdomain}\n')
+ # Publish backup exchange
+ # Use only predefined routingkey
+ aclf.write('acl allow all publish exchange name=${userdomain}-work2 routingkey=${userdomain}\n')
+ # deny mode
+ aclf.write('acl deny all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # create queues
+ self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work", {}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work2", {}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "joe_QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "joe_QPID-work2", {}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work3", {}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work", {"alternate":"bob_QPID-work2"}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work", {"alternate":"joe_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "bob_QPID-work2", {"alternate":"someexchange"}, "deny")
+ # create exchanges
+ self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work", {}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work2",{}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "joe_QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "create", "exchange", "joe_QPID-work2",{}, "deny")
+ self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work3",{}, "deny")
+ self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work", {"alternate":"bob_QPID-work2"}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "bob_QPID-work2",{"alternate":"someexchange"}, "deny")
+ # bind/unbind/access
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", { "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow")
+ self.Lookup("bob@QPID", "bind", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny")
+
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", { "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow")
+ self.Lookup("bob@QPID", "bind", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny")
+
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", { "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow")
+ self.Lookup("bob@QPID", "unbind", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny")
+
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", { "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow")
+ self.Lookup("bob@QPID", "unbind", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny")
+
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", { "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "allow")
+ self.Lookup("bob@QPID", "access", "exchange", "joe_QPID-work", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work"}, "deny")
+
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", { "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "allow")
+ self.Lookup("bob@QPID", "access", "exchange", "joe_QPID-work2", {"routingkey":"bob_QPID", "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"joe_QPID", "queuename":"bob_QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob_QPID-work2", {"routingkey":"bob_QPID", "queuename":"joe_QPID-work2"}, "deny")
+ # publish
+ self.LookupPublish("bob@QPID", "bob_QPID-work", "bob_QPID", "allow")
+ self.LookupPublish("bob@QPID", "bob_QPID-work2", "bob_QPID", "allow")
+ self.LookupPublish("bob@QPID", "joe_QPID-work", "bob_QPID", "deny")
+ self.LookupPublish("bob@QPID", "joe_QPID-work2", "bob_QPID", "deny")
+ self.LookupPublish("bob@QPID", "bob_QPID-work", "joe_QPID", "deny")
+ self.LookupPublish("bob@QPID", "bob_QPID-work2", "joe_QPID", "deny")
+
+
+ def test_user_name_substitution_user(self):
+ """
+ Test a setup where users can create, bind, and publish to a main exchange and queue.
+ Allow access to a single backup exchange and queue.
+ """
+ aclf = self.get_acl_file()
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ # Create primary queue and exchange
+ # allow predefined alternate
+ # deny any other alternate
+ # allow no alternate
+ aclf.write('acl allow all create queue name=${user}-work alternate=${user}-work2\n')
+ aclf.write('acl deny all create queue name=${user}-work alternate=*\n')
+ aclf.write('acl allow all create queue name=${user}-work\n')
+ aclf.write('acl allow all create exchange name=${user}-work alternate=${user}-work2\n')
+ aclf.write('acl deny all create exchange name=${user}-work alternate=*\n')
+ aclf.write('acl allow all create exchange name=${user}-work\n')
+ # Create backup queue and exchange
+ # Deny any alternate
+ aclf.write('acl deny all create queue name=${user}-work2 alternate=*\n')
+ aclf.write('acl allow all create queue name=${user}-work2\n')
+ aclf.write('acl deny all create exchange name=${user}-work2 alternate=*\n')
+ aclf.write('acl allow all create exchange name=${user}-work2\n')
+ # Bind/unbind primary exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all bind exchange name=${user}-work routingkey=${user} queuename=${user}-work\n')
+ aclf.write('acl allow all unbind exchange name=${user}-work routingkey=${user} queuename=${user}-work\n')
+ # Bind/unbind backup exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all bind exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n')
+ aclf.write('acl allow all unbind exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n')
+ # Access primary exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all access exchange name=${user}-work routingkey=${user} queuename=${user}-work\n')
+ # Access backup exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all access exchange name=${user}-work2 routingkey=${user} queuename=${user}-work2\n')
+ # Publish primary exchange
+ # Use only predefined routingkey
+ aclf.write('acl allow all publish exchange name=${user}-work routingkey=${user}\n')
+ # Publish backup exchange
+ # Use only predefined routingkey
+ aclf.write('acl allow all publish exchange name=${user}-work2 routingkey=${user}\n')
+ # deny mode
+ aclf.write('acl deny all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # create queues
+ self.Lookup("bob@QPID", "create", "queue", "bob-work", {}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "bob-work2", {}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "joe-work", {}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "joe-work2", {}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "bob-work3", {}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "bob-work", {"alternate":"bob-work2"}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "bob-work", {"alternate":"joe-work2"}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "bob-work2", {"alternate":"someexchange"},"deny")
+ # create exchanges
+ self.Lookup("bob@QPID", "create", "exchange", "bob-work", {}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "bob-work2",{}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "joe-work", {}, "deny")
+ self.Lookup("bob@QPID", "create", "exchange", "joe-work2",{}, "deny")
+ self.Lookup("bob@QPID", "create", "exchange", "bob-work3",{}, "deny")
+ self.Lookup("bob@QPID", "create", "exchange", "bob-work", {"alternate":"bob-work2"}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "bob-work2",{"alternate":"someexchange"},"deny")
+ # bind/unbind/access
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work", { "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow")
+ self.Lookup("bob@QPID", "bind", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny")
+
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", { "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow")
+ self.Lookup("bob@QPID", "bind", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny")
+
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", { "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow")
+ self.Lookup("bob@QPID", "unbind", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny")
+
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", { "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow")
+ self.Lookup("bob@QPID", "unbind", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny")
+
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work", {}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work", { "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob", "queuename":"bob-work"}, "allow")
+ self.Lookup("bob@QPID", "access", "exchange", "joe-work", {"routingkey":"bob", "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"joe", "queuename":"bob-work"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work", {"routingkey":"bob", "queuename":"joe-work"}, "deny")
+
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work2", { "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "allow")
+ self.Lookup("bob@QPID", "access", "exchange", "joe-work2", {"routingkey":"bob", "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"joe", "queuename":"bob-work2"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "bob-work2", {"routingkey":"bob", "queuename":"joe-work2"}, "deny")
+ # publish
+ self.LookupPublish("bob@QPID", "bob-work", "bob", "allow")
+ self.LookupPublish("bob@QPID", "bob-work2", "bob", "allow")
+ self.LookupPublish("bob@QPID", "joe-work", "bob", "deny")
+ self.LookupPublish("bob@QPID", "joe-work2", "bob", "deny")
+ self.LookupPublish("bob@QPID", "bob-work", "joe", "deny")
+ self.LookupPublish("bob@QPID", "bob-work2", "joe", "deny")
+
+
+ def test_user_name_substitution_domain(self):
+ """
+ Test a setup where users can create, bind, and publish to a main exchange and queue.
+ Allow access to a single backup exchange and queue.
+ """
+ aclf = self.get_acl_file()
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ # Create primary queue and exchange
+ # allow predefined alternate
+ # deny any other alternate
+ # allow no alternate
+ aclf.write('acl allow all create queue name=${domain}-work alternate=${domain}-work2\n')
+ aclf.write('acl deny all create queue name=${domain}-work alternate=*\n')
+ aclf.write('acl allow all create queue name=${domain}-work\n')
+ aclf.write('acl allow all create exchange name=${domain}-work alternate=${domain}-work2\n')
+ aclf.write('acl deny all create exchange name=${domain}-work alternate=*\n')
+ aclf.write('acl allow all create exchange name=${domain}-work\n')
+ # Create backup queue and exchange
+ # Deny any alternate
+ aclf.write('acl deny all create queue name=${domain}-work2 alternate=*\n')
+ aclf.write('acl allow all create queue name=${domain}-work2\n')
+ aclf.write('acl deny all create exchange name=${domain}-work2 alternate=*\n')
+ aclf.write('acl allow all create exchange name=${domain}-work2\n')
+ # Bind/unbind primary exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all bind exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n')
+ aclf.write('acl allow all unbind exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n')
+ # Bind/unbind backup exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all bind exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n')
+ aclf.write('acl allow all unbind exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n')
+ # Access primary exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all access exchange name=${domain}-work routingkey=${domain} queuename=${domain}-work\n')
+ # Access backup exchange
+ # Use only predefined routingkey and queuename
+ aclf.write('acl allow all access exchange name=${domain}-work2 routingkey=${domain} queuename=${domain}-work2\n')
+ # Publish primary exchange
+ # Use only predefined routingkey
+ aclf.write('acl allow all publish exchange name=${domain}-work routingkey=${domain}\n')
+ # Publish backup exchange
+ # Use only predefined routingkey
+ aclf.write('acl allow all publish exchange name=${domain}-work2 routingkey=${domain}\n')
+ # deny mode
+ aclf.write('acl deny all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # create queues
+ self.Lookup("bob@QPID", "create", "queue", "QPID-work", {}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "QPID-work2", {}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "QPID-work3", {}, "deny")
+ self.Lookup("bob@QPID", "create", "queue", "QPID-work", {"alternate":"QPID-work2"}, "allow")
+ self.Lookup("bob@QPID", "create", "queue", "QPID-work", {"alternate":"bob_QPID-work2"},"deny")
+ self.Lookup("bob@QPID", "create", "queue", "QPID-work", {"alternate":"joe_QPID-work2"},"deny")
+ self.Lookup("bob@QPID", "create", "queue", "QPID-work2", {"alternate":"someexchange"}, "deny")
+ # create exchanges
+ self.Lookup("bob@QPID", "create", "exchange", "QPID-work", {}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "QPID-work2",{}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "QPID-work3",{}, "deny")
+ self.Lookup("bob@QPID", "create", "exchange", "QPID-work", {"alternate":"QPID-work2"}, "allow")
+ self.Lookup("bob@QPID", "create", "exchange", "QPID-work2",{"alternate":"someexchange"}, "deny")
+ # bind/unbind/access
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {"routingkey":"QPID"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", { "queuename":"QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow")
+
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {"routingkey":"QPID"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", { "queuename":"QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "bind", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow")
+
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {"routingkey":"QPID"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", { "queuename":"QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow")
+
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {"routingkey":"QPID"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", { "queuename":"QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "unbind", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow")
+
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {"routingkey":"QPID"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work", { "queuename":"QPID-work"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work", {"routingkey":"QPID", "queuename":"QPID-work"}, "allow")
+
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {"routingkey":"QPID"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", { "queuename":"QPID-work2"}, "deny")
+ self.Lookup("bob@QPID", "access", "exchange", "QPID-work2", {"routingkey":"QPID", "queuename":"QPID-work2"}, "allow")
+ # publish
+ self.LookupPublish("bob@QPID", "QPID-work", "QPID", "allow")
+ self.LookupPublish("bob@QPID", "QPID-work2", "QPID", "allow")
+ self.LookupPublish("joe@QPID", "QPID-work", "QPID", "allow")
+ self.LookupPublish("joe@QPID", "QPID-work2", "QPID", "allow")
+
+ #=====================================
+ # Queue per-user quota
+ #=====================================
+
+ def queue_quota(self, user, passwd, count, byPort=None):
+ """ Helper method to:
+ - create a number of queues (should succeed)
+ - create too many queues (should fail)
+ - create another queue after deleting a queue (should succeed)
+ """
+
+ try:
+ if byPort:
+ session = self.get_session_by_port(user, passwd, byPort)
+ else:
+ session = self.get_session(user, passwd)
+ except Exception, e:
+ self.fail("Unexpected error creating session for %s: %s" % (user, str(e)))
+
+ # Should be able to create count queues per user
+ try:
+ for i in range(count):
+ session.queue_declare(queue="%s%d" % (user, i))
+ except Exception, e:
+ self.fail("Could not create %s for %s: %s" % ("%s%d" % (user, i), user, str(e)))
+
+ # next queue should fail
+ try:
+ session.queue_declare(queue="%s%d" % (user, count))
+ self.fail("Should not be able to create another queue for user %s" % user)
+ except Exception, e:
+ if byPort:
+ session = self.get_session_by_port(user, passwd, byPort)
+ else:
+ session = self.get_session(user, passwd)
+
+ if count > 0:
+ # Deleting a queue should allow another queue.
+ session.queue_delete(queue="%s0" % user)
+ try:
+ session.queue_declare(queue="%s%d" % (user, count))
+ except Exception, e:
+ self.fail("Could not recreate additional queue for user %s: %s " % (user, str(e)))
+
+ # Clean up
+ for i in range(1, count+1):
+ session.queue_delete(queue="%s%d" % (user, i))
+ try:
+ session.close()
+ except Exception, e:
+ pass
+
+ def test_queue_per_named_user_quota(self):
+ """
+ Test ACL queue counting limits per named user.
+ """
+ aclf = self.get_acl_file()
+ aclf.write('quota queues 2 ted@QPID carrol@QPID\n')
+ aclf.write('quota queues 1 edward@QPID\n')
+ aclf.write('quota queues 0 mick@QPID\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # named users should be able to create specified number of queues
+ self.queue_quota("ted", 'ted', 2)
+ self.queue_quota("carrol", 'carrol', 2)
+ self.queue_quota("edward", 'edward', 1)
+
+ # User with quota of 0 is denied
+ self.queue_quota("mick", 'mick', 0)
+
+ # User not named in quotas is denied
+ self.queue_quota("dan", 'dan', 0)
+
+ def test_queue_per_user_quota(self):
+ """
+ Test ACL queue counting limits.
+ port_q has a limit of 2
+ """
+ # bob should be able to create two queues
+ self.queue_quota("bob", 'bob', 2, self.port_q())
+
+ # alice should be able to create two queues
+ self.queue_quota("alice", 'alice', 2, self.port_q())
+
+
+ def test_queue_limits_by_unnamed_all(self):
+ """
+ Test ACL control queue limits
+ """
+ aclf = self.get_acl_file()
+ aclf.write('quota queues 2 aliceQUA@QPID bobQUA@QPID\n')
+ aclf.write('quota queues 1 all\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # By username should be able to connect twice per user
+ self.queue_quota('aliceQUA', 'alice', 2)
+ self.queue_quota('bobQUA', 'bob', 2)
+
+ # User not named in quotas gets 'all' quota
+ self.queue_quota('charlieQUA', 'charlie', 1)
+
+
+ def test_queue_limits_by_group(self):
+ """
+ Test ACL control queue limits
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group hobbits frodoGR@QPID samGR@QPID merryGR@QPID\n')
+ aclf.write('quota queues 2 gandalfGR@QPID aragornGR@QPID\n')
+ aclf.write('quota queues 2 hobbits rosieGR@QPID\n')
+ aclf.write('# user and groups may be overwritten. Should use last value\n')
+ aclf.write('quota queues 3 aragornGR@QPID hobbits\n')
+ aclf.write('acl allow all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # gandalf gets 2
+ self.queue_quota('gandalfGR', 'gandalf', 2)
+
+ # aragorn gets 3
+ self.queue_quota('aragornGR', 'aragorn', 3)
+
+ # frodo gets 3
+ self.queue_quota('frodoGR', 'frodo', 3)
+
+ # User not named in quotas is denied
+ self.queue_quota('bilboGR', 'bilbo', 0)
+
+ def test_queue_delete_with_properties(self):
+ """
+ Test cases for queue delete with properties
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID access queue\n')
+ aclf.write('acl allow bob@QPID access exchange\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq1 durable=true\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq2 exclusive=true\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq3 policytype=ring\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq4 durable=false\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq5 exclusive=false\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq6 policytype=reject\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq7 autodelete=true\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq8 autodelete=false\n')
+ aclf.write('acl allow bob@QPID create queue name=qdaq9\n')
+ aclf.write('acl allow bob@QPID create exchange name=qdae9\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq1 durable=true\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq2 exclusive=true\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq3 policytype=ring\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq4 durable=false\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq5 exclusive=false\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq6 policytype=reject\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq7 autodelete=true\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq8 autodelete=false\n')
+ aclf.write('acl deny bob@QPID delete queue name=qdaq9 alternate=qdaq9a\n')
+ aclf.write('acl allow all access queue\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qdaq1", durable=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qdaq1 durable=true");
+
+ try:
+ session.queue_delete(queue="qdaq1")
+ self.fail("ACL should deny queue delete request for qdaq1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qdaq2", exclusive=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qdaq2 exclusive=true");
+
+ try:
+ session.queue_delete(queue="qdaq2")
+ self.fail("ACL should deny queue delete request for qdaq2");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "ring"
+ session.queue_declare(queue="qdaq3", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for qdaq3 with policytype=ring");
+
+ try:
+ session.queue_delete(queue="qdaq3")
+ self.fail("ACL should deny queue delete request for qdaq3");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qdaq4", durable=False)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qdaq4 durable=false");
+
+ try:
+ session.queue_delete(queue="qdaq4")
+ self.fail("ACL should deny queue delete request for qdaq4");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qdaq5", exclusive=False)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qdaq5 exclusive=false");
+
+ try:
+ session.queue_delete(queue="qdaq5")
+ self.fail("ACL should deny queue delete request for qdaq5");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ queue_options = {}
+ queue_options["qpid.policy_type"] = "reject"
+ session.queue_declare(queue="qdaq6", arguments=queue_options)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request for qdaq6 with policytype=reject");
+
+ try:
+ session.queue_delete(queue="qdaq6")
+ self.fail("ACL should deny queue delete request for qdaq6");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qdaq7", auto_delete=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qdaq7 autodelete=true");
+
+ try:
+ session.queue_delete(queue="qdaq7")
+ self.fail("ACL should deny queue delete request for qdaq7");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.queue_declare(queue="qdaq8", auto_delete=False)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qdaq8 autodelete=false");
+
+ try:
+ session.queue_delete(queue="qdaq8")
+ self.fail("ACL should deny queue delete request for qdaq8");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='qdae9', type='direct')
+ except qpid.session.SessionException, e:
+ self.fail("ACL should allow exchange create request with name=qdae9");
+
+ try:
+ session.queue_declare(queue="qdaq9", alternate_exchange="qdae9")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=qdaq9 alternate=qdaq9a");
+
+ try:
+ session.queue_delete(queue="qdaq9")
+ self.fail("ACL should deny queue delete request for qdaq9");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+
+ def test_exchange_delete_with_properties(self):
+ """
+ Test cases for exchange delete with properties
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID access exchange\n')
+ aclf.write('acl allow bob@QPID create exchange\n')
+ aclf.write('acl deny bob@QPID delete exchange name=edae1 durable=true\n')
+ aclf.write('acl deny bob@QPID delete exchange name=edae2 alternate=edae2a\n')
+ aclf.write('acl deny bob@QPID delete exchange type=direct\n')
+ aclf.write('acl allow bob@QPID delete exchange type=headers\n')
+ aclf.write('acl allow anonymous all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='edae1', type='direct', durable=True)
+ except qpid.session.SessionException, e:
+ self.fail("ACL should allow exchange create request with name=edae1");
+
+ try:
+ session.exchange_delete(exchange="edae1")
+ self.fail("ACL should deny exchange delete request for edae1");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='edae2a', type='direct')
+ except qpid.session.SessionException, e:
+ self.fail("ACL should allow exchange create request with name=edae2a");
+
+ try:
+ session.exchange_declare(exchange='edae2', type='direct', alternate_exchange='edae2a')
+ except qpid.session.SessionException, e:
+ self.fail("ACL should allow exchange create request with name=edae2");
+
+ try:
+ session.exchange_delete(exchange="edae2")
+ self.fail("ACL should deny exchange delete request for edae2");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_declare(exchange='edae3d', type='direct')
+ except qpid.session.SessionException, e:
+ self.fail("ACL should allow exchange create request with name=edae3d");
+
+ try:
+ session.exchange_declare(exchange='edae3h', type='headers')
+ except qpid.session.SessionException, e:
+ self.fail("ACL should allow exchange create request with name=eda3h");
+
+ try:
+ session.exchange_delete(exchange="edae3d")
+ self.fail("ACL should deny exchange delete request for edae3d");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
+ session.exchange_delete(exchange="edae3h")
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ self.fail("ACL should allow exchange delete request for edae3h");
+
+ #=====================================
+ # 'create connection' tests
+ #=====================================
+# def test_connect_mode_file_rejects_two_defaults(self):
+# """
+# Should reject a file with two connect mode statements
+# """
+# aclf = self.get_acl_file()
+# aclf.write('acl allow all create connection host=all\n')
+# aclf.write('acl allow all create connection host=all\n')
+# aclf.close()
+#
+# result = self.reload_acl()
+# if (result):
+# pass
+# else:
+# self.fail(result)
+
+ def test_connect_mode_accepts_host_spec_formats(self):
+ """
+ Should accept host specs of various forms
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create connection host=all\n')
+ aclf.write('acl allow bob@QPID create connection host=1.1.1.1\n')
+ aclf.write('acl allow bob@QPID create connection host=1.1.1.1,2.2.2.2\n')
+ aclf.write('acl allow bob@QPID create connection host=localhost\n')
+ aclf.write('acl allow all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ def test_connect_mode_allow_all_mode(self):
+ """
+ Should allow one 'all', 'all'
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all create connection host=all\n')
+ aclf.write('acl allow all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ def test_connect_mode_allow_all_localhost(self):
+ """
+ Should allow 'all' 'localhost'
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all create connection host=localhost\n')
+ aclf.write('acl deny all create connection host=all\n')
+ aclf.write('acl allow all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+
+ def test_connect_mode_global_deny(self):
+ """
+ Should allow 'all' 'localhost'
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all create connection host=localhost\n')
+ aclf.write('acl deny all create connection host=all\n')
+ aclf.write('acl allow all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"127.0.0.1"}, "allow")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"127.0.0.2"}, "deny")
+
+
+ def test_connect_mode_global_range(self):
+ """
+ Should allow 'all' 'localhost'
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all create connection host=10.0.0.0,10.255.255.255\n')
+ aclf.write('acl allow all create connection host=localhost\n')
+ aclf.write('acl deny all create connection host=all\n')
+ aclf.write('acl allow all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"0.0.0.0"}, "deny")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"9.255.255.255"}, "deny")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.0.0.0"}, "allow")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.255.255.255"}, "allow")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"11.0.0.0"}, "deny")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"255.255.255.255"},"deny")
+
+
+ def test_connect_mode_nested_ranges(self):
+ """
+ Tests nested ranges for single user
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl deny-log bob@QPID create connection host=10.0.1.0,10.0.1.255\n')
+ aclf.write('acl allow-log bob@QPID create connection host=10.0.0.0,10.255.255.255\n')
+ aclf.write('acl deny-log bob@QPID create connection host=all\n')
+ aclf.write('acl allow all create connection host=localhost\n')
+ aclf.write('acl allow all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"0.0.0.0"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"9.255.255.255"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.0.0.0"}, "allow-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.0.0.255"}, "allow-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.0.1.0"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.0.1.255"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.0.2.0"}, "allow-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.255.255.255"}, "allow-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"11.0.0.0"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"255.255.255.255"},"deny-log")
+
+
+ def test_connect_mode_user_ranges(self):
+ """
+ Two user ranges should not interfere with each other
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow-log bob@QPID create connection host=10.0.0.0,10.255.255.255\n')
+ aclf.write('acl deny-log bob@QPID create connection host=all\n')
+ aclf.write('acl allow-log cat@QPID create connection host=192.168.0.0,192.168.255.255\n')
+ aclf.write('acl deny-log cat@QPID create connection host=all\n')
+ aclf.write('acl allow all create connection host=localhost\n')
+ aclf.write('acl allow all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('bob','bob')
+
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"0.0.0.0"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"9.255.255.255"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.0.0.0"}, "allow-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"10.255.255.255"}, "allow-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"11.0.0.0"}, "deny-log")
+ self.Lookup("bob@QPID", "create", "connection", "", {"host":"255.255.255.255"},"deny-log")
+ self.Lookup("cat@QPID", "create", "connection", "", {"host":"0.0.0.0"}, "deny-log")
+ self.Lookup("cat@QPID", "create", "connection", "", {"host":"192.167.255.255"},"deny-log")
+ self.Lookup("cat@QPID", "create", "connection", "", {"host":"192.168.0.0"}, "allow-log")
+ self.Lookup("cat@QPID", "create", "connection", "", {"host":"192.168.255.255"},"allow-log")
+ self.Lookup("cat@QPID", "create", "connection", "", {"host":"192.169.0.0"}, "deny-log")
+ self.Lookup("cat@QPID", "create", "connection", "", {"host":"255.255.255.255"},"deny-log")
+
+
+class BrokerAdmin:
+ def __init__(self, broker, username=None, password=None):
+ self.connection = qpid.messaging.Connection(broker)
+ if username:
+ self.connection.username = username
+ self.connection.password = password
+ self.connection.sasl_mechanisms = "PLAIN"
+ self.connection.open()
+ self.session = self.connection.session()
+ self.sender = self.session.sender("qmf.default.direct/broker")
+ self.reply_to = "responses-#; {create:always}"
+ self.receiver = self.session.receiver(self.reply_to)
+
+ def invoke(self, method, arguments):
+ content = {
+ "_object_id": {"_object_name": "org.apache.qpid.broker:broker:amqp-broker"},
+ "_method_name": method,
+ "_arguments": arguments
+ }
+ request = qpid.messaging.Message(reply_to=self.reply_to, content=content)
+ request.properties["x-amqp-0-10.app-id"] = "qmf2"
+ request.properties["qmf.opcode"] = "_method_request"
+ self.sender.send(request)
+ response = self.receiver.fetch()
+ self.session.acknowledge()
+ if response.properties['x-amqp-0-10.app-id'] == 'qmf2':
+ if response.properties['qmf.opcode'] == '_method_response':
+ return response.content['_arguments']
+ elif response.properties['qmf.opcode'] == '_exception':
+ raise Exception(response.content['_values'])
+ else: raise Exception("Invalid response received, unexpected opcode: %s" % response.properties['qmf.opcode'])
+ else: raise Exception("Invalid response received, not a qmfv2 method: %s" % response.properties['x-amqp-0-10.app-id'])
+
+ def query(self, object_name=None, class_name=None):
+ content = { "_what": "OBJECT" }
+ if object_name is not None:
+ content["_object_id"] = {"_object_name": object_name }
+ if class_name is not None:
+ content["_schema_id"] = {"_class_name": class_name }
+ request = qpid.messaging.Message(reply_to=self.reply_to, content=content)
+ request.properties["x-amqp-0-10.app-id"] = "qmf2"
+ request.properties["qmf.opcode"] = "_query_request"
+ self.sender.send(request)
+ response = self.receiver.fetch()
+ self.session.acknowledge()
+ if response.properties['x-amqp-0-10.app-id'] == 'qmf2':
+ if response.properties['qmf.opcode'] == '_query_response':
+ return
+ elif response.properties['qmf.opcode'] == '_exception':
+ raise Exception(response.content['_values'])
+ else: raise Exception("Invalid response received, unexpected opcode: %s" % response.properties['qmf.opcode'])
+ else: raise Exception("Invalid response received, not a qmfv2 method: %s" % response.properties['x-amqp-0-10.app-id'])
+
+ def create_exchange(self, name, exchange_type=None, options={}):
+ properties = options
+ if exchange_type: properties["exchange_type"] = exchange_type
+ self.invoke("create", {"type": "exchange", "name":name, "properties":properties})
+
+ def create_queue(self, name, properties={}):
+ self.invoke("create", {"type": "queue", "name":name, "properties":properties})
+
+ def delete_exchange(self, name):
+ self.invoke("delete", {"type": "exchange", "name":name})
+
+ def delete_queue(self, name):
+ self.invoke("delete", {"type": "queue", "name":name})
+
+ def get_timestamp_cfg(self):
+ return self.invoke("getTimestampConfig", {})
+
+ def set_timestamp_cfg(self, receive):
+ return self.invoke("getTimestampConfig", {"receive":receive})
diff --git a/qpid/cpp/src/tests/ais_test.cpp b/qpid/cpp/src/tests/ais_test.cpp
new file mode 100644
index 0000000000..00c61242e4
--- /dev/null
+++ b/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/qpid/cpp/src/tests/allhosts b/qpid/cpp/src/tests/allhosts
new file mode 100755
index 0000000000..07bc04fff5
--- /dev/null
+++ b/qpid/cpp/src/tests/allhosts
@@ -0,0 +1,79 @@
+#!/usr/bin/env 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.
+#
+
+usage() {
+ echo "Usage: $0 [options] command.
+Run a command on each host in \$HOSTS.
+Options:
+ -l USER passed to ssh - run as USER.
+ -t passed to ssh - create a terminal.
+ -b run in background, wait for commands to complete.
+ -d run in background, don't wait for commands to complete.
+ -s SECONDS sleep between starting commands.
+ -q don't print banner lines for each host.
+ -o SUFFIX log output of each command to <host>.SUFFIX
+ -X passed to ssh - forward X connection.
+"
+ exit 1
+}
+
+while getopts "tl:bs:dqo:X" opt; do
+ case $opt in
+ l) SSHOPTS="-l$OPTARG $SSHOPTS" ;;
+ t) SSHOPTS="-t $SSHOPTS" ;;
+ b) BACKGROUND=1 ;;
+ d) BACKGROUND=1; DISOWN=1 ;;
+ s) SLEEP="sleep $OPTARG" ;;
+ q) NOBANNER=1 ;;
+ o) SUFFIX=$OPTARG ;;
+ X) SSHOPTS="-X $SSHOPTS" ;;
+ *) usage;;
+ esac
+done
+shift `expr $OPTIND - 1`
+test "$*" || usage;
+
+OK_FILE=`mktemp` # Will be deleted if anything goes wrong.
+trap "rm -f $OK_FILE" EXIT
+
+do_ssh() {
+ h=$1; shift
+ if test $SUFFIX ; then ssh $SSHOPTS $h "$@" &> $h.$SUFFIX
+ else ssh $SSHOPTS $h "$@"; fi || rm -rf $OK_FILE;
+}
+
+for h in $HOSTS ; do
+ test "$NOBANNER" || echo "== ssh $SSHOPTS $h $@ =="
+ if [ "$BACKGROUND" = 1 ]; then
+ do_ssh $h "$@" &
+ CHILDREN="$! $CHILDREN"
+ else
+ do_ssh $h "$@"
+ fi
+ $SLEEP
+done
+
+if [ "$DISOWN" = 1 ]; then
+ for c in $CHILDREN; do disown $c; done
+else
+ wait
+fi
+
+test -f $OK_FILE
diff --git a/qpid/cpp/src/tests/assertions.py b/qpid/cpp/src/tests/assertions.py
new file mode 100644
index 0000000000..930afd124d
--- /dev/null
+++ b/qpid/cpp/src/tests/assertions.py
@@ -0,0 +1,194 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from qpid.tests.messaging.implementation import *
+from qpid.tests.messaging import VersionTest
+
+class AssertionTests (VersionTest):
+ """
+ Tests for assertions with qpidd
+ """
+ def test_queues_alternate_exchange1(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{properties:{alternate-exchange:amq.fanout}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{properties:{alternate-exchange:amq.fanout}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{properties:{alternate-exchange:amq.topic}}}" % name)
+ assert False, "Expected assertion to fail on alternate-exchange"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_queues_alternate_exchange2(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{x-declare:{alternate-exchange:amq.fanout}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{alternate-exchange:amq.fanout}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{alternate-exchange:amq.topic}}}" % name)
+ assert False, "Expected assertion to fail on alternate-exchange"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_queue_type(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always}" % name)
+ self.ssn.sender("%s; {assert:always, node:{type:queue}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{type:topic}}" % name)
+ assert False, "Expected assertion to fail on type"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_queue_not_durable(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always}" % name)
+ self.ssn.sender("%s; {assert:always, node:{durable:False}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{durable:True}}" % name)
+ assert False, "Expected assertion to fail on durability"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_queue_is_durable(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{durable:True}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{durable:True}}" % name)
+
+ def test_queue_is_autodelete(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{x-declare:{auto-delete:True}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{auto-delete:True}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{auto-delete:False}}}" % name)
+ assert False, "Expected assertion to fail for auto-delete"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def do_test_queue_options(self, name):
+ self.ssn.sender("%s; {create:always, node:{x-declare:{arguments:{foo:bar,'qpid.last_value_queue_key':abc}}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.last_value_queue_key':abc}}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{foo:bar}}}}" % name)
+ assert False, "Expected assertion to fail on unrecognised option"
+ except AssertionFailed: None
+ except MessagingError: None
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.max_count':10}}}}" % name)
+ assert False, "Expected assertion to fail on unspecified option"
+ except AssertionFailed: None
+ except MessagingError: None
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.last_value_key':xyz}}}}" % name)
+ assert False, "Expected assertion to fail on option with different value"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_queue_options(self):
+ self.do_test_queue_options(str(uuid4()))
+
+ def test_queue_options_from_0_10(self):
+ name = str(uuid4())
+ self.do_test_queue_options(name)
+ ssn_0_10 = self.create_connection("amqp0-10", True).session()
+ ssn_0_10.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.last_value_queue_key':abc}}}}" % name)
+ try:
+ ssn_0_10.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.last_value_key':xyz}}}}" % name)
+ assert False, "Expected assertion to fail on option with different value"
+ except AssertionFailed: None
+ except MessagingError: None
+
+
+ def test_exchanges_alternate_exchange1(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{type:topic, properties:{alternate-exchange:amq.fanout}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{type:topic, properties:{alternate-exchange:amq.fanout}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{properties:{alternate-exchange:amq.topic}}}" % name)
+ assert False, "Expected assertion to fail on alternate-exchange"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_exchanges_alternate_exchange2(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{type:topic, x-declare:{alternate-exchange:amq.fanout}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{type:topic, x-declare:{alternate-exchange:amq.fanout}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{alternate-exchange:amq.topic}}}" % name)
+ assert False, "Expected assertion to fail on alternate-exchange"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_exchange_type(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{type:topic}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{type:topic}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{type:queue}}" % name)
+ assert False, "Expected assertion to fail on type"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_exchange_durability(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{type:topic}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{durable:False}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{durable:True}}" % name)
+ assert False, "Expected assertion to fail on durability"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_exchange_is_autodelete(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{type:topic, x-declare:{auto-delete:True}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{auto-delete:True}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{auto-delete:False}}}" % name)
+ assert False, "Expected assertion to fail for auto-delete"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_exchange_options(self):
+ name = str(uuid4())
+ self.ssn.sender("%s; {create:always, node:{type:topic, x-declare:{arguments:{foo:bar,'qpid.msg_sequence':True}}}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.msg_sequence':True}}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{foo:bar}}}}" % name)
+ assert False, "Expected assertion to fail on unrecognised option"
+ except AssertionFailed: None
+ except MessagingError: None
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments:{'qpid.ive':True}}}}" % name)
+ assert False, "Expected assertion to fail on unspecified option"
+ except AssertionFailed: None
+ except MessagingError: None
+
+ def test_queue_autodelete_timeout(self):
+ name = str(uuid4())
+ # create subscription queue with 0-10 to be sure of name
+ ssn_0_10 = self.create_connection("amqp0-10", True).session()
+ ssn_0_10.receiver("amq.direct; {link:{name:%s,timeout:30}}" % name)
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments: {qpid.auto_delete_timeout: 30}}}}" % name)
+ ssn_0_10_other = self.create_connection("amqp0-10", True).session()
+ ssn_0_10_other.sender("%s; {assert:always, node:{x-declare:{arguments: {qpid.auto_delete_timeout: 30}}}}" % name)
+ try:
+ self.ssn.sender("%s; {assert:always, node:{x-declare:{arguments: {qpid.auto_delete_timeout: 60}}}}" % name)
+ ssn_0_10_other.sender("%s; {assert:always, node:{x-declare:{arguments: {qpid.auto_delete_timeout: 60}}}}" % name)
+ assert False, "Expected assertion to fail for auto_delete_timeout"
+ except AssertionFailed: None
+ except MessagingError: None
diff --git a/qpid/cpp/src/tests/background.ps1 b/qpid/cpp/src/tests/background.ps1
new file mode 100644
index 0000000000..36e9e4e6e9
--- /dev/null
+++ b/qpid/cpp/src/tests/background.ps1
@@ -0,0 +1,55 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Run a PowerShell scriptblock in a background process.
+param(
+ [scriptblock] $script # scriptblock to run
+)
+
+# break out of the script on any errors
+trap { break }
+
+# In order to pass a scriptblock to another powershell instance, it must
+# be encoded to pass through the command line.
+$encodedScript = [convert]::ToBase64String(
+ [Text.Encoding]::Unicode.GetBytes([string] $script))
+
+#$p = new-object System.Diagnostics.Process
+$si = new-object System.Diagnostics.ProcessStartInfo
+$si.WorkingDirectory = $pwd
+$si.FileName = (get-command powershell.exe).Definition
+$si.Arguments = "-encodedCommand $encodedScript"
+
+###### debugging setup
+#$si.CreateNoWindow = $true
+# UseShellExecute false required for RedirectStandard(Error, Output)
+#$si.UseShellExecute = $false
+#$si.RedirectStandardError = $true
+#$si.RedirectStandardOutput = $true
+######
+$si.UseShellExecute = $true
+
+##### Debugging, instead of the plain Start() above.
+#$output = [io.File]::AppendText("start.out")
+#$error = [io.File]::AppendText("start.err")
+$p = [System.Diagnostics.Process]::Start($si)
+#$output.WriteLine($p.StandardOutput.ReadToEnd())
+#$error.WriteLine($p.StandardError.ReadToEnd())
+#$p.WaitForExit()
+#$output.Close()
diff --git a/qpid/cpp/src/tests/brokertest.py b/qpid/cpp/src/tests/brokertest.py
new file mode 100644
index 0000000000..6fae88092b
--- /dev/null
+++ b/qpid/cpp/src/tests/brokertest.py
@@ -0,0 +1,752 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Support library for tests that start multiple brokers, e.g. HA or federation
+
+import os, signal, string, tempfile, subprocess, socket, threading, time, imp, re
+import qpid, traceback, signal
+from qpid import connection, util
+from qpid.compat import format_exc
+from unittest import TestCase
+from copy import copy
+from threading import Thread, Lock, Condition
+from logging import getLogger
+from qpidtoollibs import BrokerAgent
+
+# NOTE: Always import native client qpid.messaging, import swigged client
+# qpid_messaging if possible. qpid_messaing is set to None if not available.
+#
+# qm is set to qpid_messaging if it is available, qpid.messaging if not.
+# Use qm.X to specify names from the default messaging module.
+#
+# Set environment variable QPID_PY_NO_SWIG=1 to prevent qpid_messaging from loading.
+#
+# BrokerTest can be configured to determine which protocol is used by default:
+#
+# -DPROTOCOL="amqpX": Use protocol "amqpX". Defaults to amqp1.0 if available.
+#
+# The configured defaults can be over-ridden on BrokerTest.connect and some
+# other methods by specifying native=True|False and protocol="amqpX"
+#
+
+import qpid.messaging
+qm = qpid.messaging
+qpid_messaging = None
+
+def env_has_log_config():
+ """True if there are qpid log configuratoin settings in the environment."""
+ return "QPID_LOG_ENABLE" in os.environ or "QPID_TRACE" in os.environ
+
+if not os.environ.get("QPID_PY_NO_SWIG"):
+ try:
+ import qpid_messaging
+ from qpid.datatypes import uuid4
+ qm = qpid_messaging
+ # Silence warnings from swigged messaging library unless enabled in environment.
+ if not env_has_log_config():
+ qm.Logger.configure(["--log-enable=error"])
+ except ImportError:
+ print "Cannot load python SWIG bindings, falling back to native qpid.messaging."
+
+log = getLogger("brokertest")
+
+# Values for expected outcome of process at end of test
+EXPECT_EXIT_OK=1 # Expect to exit with 0 status before end of test.
+EXPECT_EXIT_FAIL=2 # Expect to exit with non-0 status before end of test.
+EXPECT_RUNNING=3 # Expect to still be running at end of test
+EXPECT_UNKNOWN=4 # No expectation, don't check exit status.
+
+def find_exe(program):
+ """Find an executable in the system PATH"""
+ def is_exe(fpath):
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+ mydir, name = os.path.split(program)
+ if mydir:
+ if is_exe(program): return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file): return exe_file
+ return None
+
+def is_running(pid):
+ try:
+ os.kill(pid, 0)
+ return True
+ except:
+ return False
+
+class BadProcessStatus(Exception):
+ pass
+
+def error_line(filename, n=1):
+ """Get the last n line(s) of filename for error messages"""
+ result = []
+ try:
+ f = open(filename)
+ try:
+ for l in f:
+ if len(result) == n: result.pop(0)
+ result.append(" "+l)
+ finally:
+ f.close()
+ except: return ""
+ return ":\n" + "".join(result)
+
+def retry(function, timeout=10, delay=.001, max_delay=1):
+ """Call function until it returns a true value or timeout expires.
+ Double the delay for each retry up to max_delay.
+ Returns what function returns if true, None if timeout expires."""
+ deadline = time.time() + timeout
+ ret = None
+ while True:
+ ret = function()
+ if ret: return ret
+ remaining = deadline - time.time()
+ if remaining <= 0: return False
+ delay = min(delay, remaining)
+ time.sleep(delay)
+ delay = min(delay*2, max_delay)
+
+class AtomicCounter:
+ def __init__(self):
+ self.count = 0
+ self.lock = Lock()
+
+ def next(self):
+ self.lock.acquire();
+ ret = self.count
+ self.count += 1
+ self.lock.release();
+ return ret
+
+_popen_id = AtomicCounter() # Popen identifier for use in output file names.
+
+# Constants for file descriptor arguments to Popen
+FILE = "FILE" # Write to file named after process
+from subprocess import PIPE, STDOUT
+
+class Popen(subprocess.Popen):
+ """
+ Can set and verify expectation of process status at end of test.
+ Dumps command line, stdout, stderr to data dir for debugging.
+ """
+
+ def __init__(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE):
+ """Run cmd (should be a list of program and arguments)
+ expect - if set verify expectation at end of test.
+ stdout, stderr - can have the same values as for subprocess.Popen as well as
+ FILE (the default) which means write to a file named after the process.
+ stdin - like subprocess.Popen but defauts to PIPE
+ """
+ self._clean = False
+ self._clean_lock = Lock()
+ assert find_exe(cmd[0]), "executable not found: "+cmd[0]
+ if type(cmd) is type(""): cmd = [cmd] # Make it a list.
+ self.cmd = [ str(x) for x in cmd ]
+ self.expect = expect
+ self.id = _popen_id.next()
+ self.pname = "%s-%d" % (os.path.split(self.cmd[0])[1], self.id)
+ if stdout == FILE: stdout = open(self.outfile("out"), "w")
+ if stderr == FILE: stderr = open(self.outfile("err"), "w")
+ subprocess.Popen.__init__(self, self.cmd, bufsize=0, executable=None,
+ stdin=stdin, stdout=stdout, stderr=stderr)
+ f = open(self.outfile("cmd"), "w")
+ try: f.write("%s\n%d"%(self.cmd_str(), self.pid))
+ finally: f.close()
+ log.debug("Started process %s: %s" % (self.pname, " ".join(self.cmd)))
+
+ def __repr__(self): return "Popen<%s>"%(self.pname)
+
+ def outfile(self, ext): return "%s.%s" % (self.pname, ext)
+
+ def unexpected(self,msg):
+ err = error_line(self.outfile("err")) or error_line(self.outfile("out"))
+ raise BadProcessStatus("%s %s%s" % (self.pname, msg, err))
+
+ def teardown(self): # Clean up at end of test.
+ if self.expect == EXPECT_UNKNOWN:
+ try: self.kill() # Just make sure its dead
+ except: pass
+ elif self.expect == EXPECT_RUNNING:
+ if self.poll() != None:
+ self.unexpected("expected running, exit code %d" % self.returncode)
+ else:
+ try:
+ self.kill()
+ except Exception,e:
+ self.unexpected("exception from kill: %s" % str(e))
+ else:
+ retry(lambda: self.poll() is not None)
+ if self.returncode is None: # Still haven't stopped
+ self.kill()
+ self.unexpected("still running")
+ elif self.expect == EXPECT_EXIT_OK and self.returncode != 0:
+ self.unexpected("exit code %d" % self.returncode)
+ elif self.expect == EXPECT_EXIT_FAIL and self.returncode == 0:
+ self.unexpected("expected error")
+ self.wait()
+
+
+ def communicate(self, input=None):
+ ret = subprocess.Popen.communicate(self, input)
+ self._cleanup()
+ return ret
+
+ def is_running(self): return self.poll() is None
+
+ def assert_running(self):
+ if not self.is_running(): self.unexpected("Exit code %d" % self.returncode)
+
+ def wait(self):
+ ret = subprocess.Popen.wait(self)
+ self._cleanup()
+ return ret
+
+ def assert_exit_ok(self):
+ if self.wait() != 0: self.unexpected("Exit code %d" % self.returncode)
+
+ def terminate(self):
+ try: subprocess.Popen.terminate(self)
+ except AttributeError: # No terminate method
+ try:
+ os.kill( self.pid , signal.SIGTERM)
+ except AttributeError: # no os.kill, using taskkill.. (Windows only)
+ os.popen('TASKKILL /PID ' +str(self.pid) + ' /F')
+ self.wait()
+
+ def kill(self):
+ # Set to EXPECT_UNKNOWN, EXPECT_EXIT_FAIL creates a race condition
+ # if the process exits normally concurrent with the call to kill.
+ self.expect = EXPECT_UNKNOWN
+ try: subprocess.Popen.kill(self)
+ except AttributeError: # No terminate method
+ try:
+ os.kill( self.pid , signal.SIGKILL)
+ except AttributeError: # no os.kill, using taskkill.. (Windows only)
+ os.popen('TASKKILL /PID ' +str(self.pid) + ' /F')
+ self.wait()
+
+ def _cleanup(self):
+ """Clean up after a dead process"""
+ self._clean_lock.acquire()
+ if not self._clean:
+ self._clean = True
+ try: self.stdin.close()
+ except: pass
+ try: self.stdout.close()
+ except: pass
+ try: self.stderr.close()
+ except: pass
+ self._clean_lock.release()
+
+ def cmd_str(self): return " ".join([str(s) for s in self.cmd])
+
+
+def checkenv(name):
+ value = os.getenv(name)
+ if not value: raise Exception("Environment variable %s is not set" % name)
+ return value
+
+def find_in_file(str, filename):
+ if not os.path.exists(filename): return False
+ f = open(filename)
+ try: return str in f.read()
+ finally: f.close()
+
+class Broker(Popen):
+ "A broker process. Takes care of start, stop and logging."
+ _broker_count = 0
+ _log_count = 0
+
+ def __repr__(self): return "<Broker:%s:%d>"%(self.log, self.port())
+
+ def get_log(self):
+ return os.path.abspath(self.log)
+
+ def __init__(self, test, args=[], test_store=False, name=None, expect=EXPECT_RUNNING, port=0, wait=None, show_cmd=False):
+ """Start a broker daemon. name determines the data-dir and log
+ file names."""
+
+ self.test = test
+ self._port=port
+ args = copy(args)
+ if BrokerTest.amqp_lib: args += ["--load-module", BrokerTest.amqp_lib]
+ if BrokerTest.store_lib and not test_store:
+ args += ['--load-module', BrokerTest.store_lib]
+ if BrokerTest.sql_store_lib:
+ args += ['--load-module', BrokerTest.sql_store_lib]
+ args += ['--catalog', BrokerTest.sql_catalog]
+ if BrokerTest.sql_clfs_store_lib:
+ args += ['--load-module', BrokerTest.sql_clfs_store_lib]
+ args += ['--catalog', BrokerTest.sql_catalog]
+ cmd = [BrokerTest.qpidd_exec, "--port", port, "--interface", "127.0.0.1", "--no-module-dir"] + args
+ if not "--auth" in args: cmd.append("--auth=no")
+ if wait != None:
+ cmd += ["--wait", str(wait)]
+ if name: self.name = name
+ else:
+ self.name = "broker%d" % Broker._broker_count
+ Broker._broker_count += 1
+
+ self.log = "%03d:%s.log" % (Broker._log_count, self.name)
+ self.store_log = "%03d:%s.store.log" % (Broker._log_count, self.name)
+ Broker._log_count += 1
+
+ cmd += ["--log-to-file", self.log]
+ cmd += ["--log-to-stderr=no"]
+
+ # Add default --log-enable arguments unless args already has --log arguments.
+ if not env_has_log_config() and not [l for l in args if l.startswith("--log")]:
+ args += ["--log-enable=info+"]
+
+ if test_store: cmd += ["--load-module", BrokerTest.test_store_lib,
+ "--test-store-events", self.store_log]
+
+ self.datadir = os.path.abspath(self.name)
+ cmd += ["--data-dir", self.datadir]
+ if show_cmd: print cmd
+ Popen.__init__(self, cmd, expect, stdout=PIPE)
+ test.teardown_add(self)
+ self._host = "127.0.0.1"
+ self._agent = None
+
+ log.debug("Started broker %s" % self)
+
+ def host(self): return self._host
+
+ def port(self):
+ # Read port from broker process stdout if not already read.
+ if (self._port == 0):
+ try: self._port = int(self.stdout.readline())
+ except ValueError, e:
+ raise Exception("Can't get port for broker %s (%s)%s: %s" %
+ (self.name, self.pname, error_line(self.log,5), e))
+ return self._port
+
+ def unexpected(self,msg):
+ raise BadProcessStatus("%s: %s (%s)" % (msg, self.name, self.pname))
+
+ def connect(self, timeout=5, native=False, **kwargs):
+ """New API connection to the broker.
+ @param native if True force use of the native qpid.messaging client
+ even if swig client is available.
+ """
+ if native: connection_class = qpid.messaging.Connection
+ else:
+ connection_class = qm.Connection
+ if (self.test.protocol and qm == qpid_messaging):
+ kwargs.setdefault("protocol", self.test.protocol)
+ return connection_class.establish(self.host_port(), timeout=timeout, **kwargs)
+
+ @property
+ def agent(self, **kwargs):
+ """Return a BrokerAgent for this broker"""
+ if not self._agent: self._agent = BrokerAgent(self.connect(**kwargs))
+ return self._agent
+
+
+ def declare_queue(self, queue):
+ self.agent.addQueue(queue)
+
+ def _prep_sender(self, queue, durable, xprops):
+ s = queue + "; {create:always, node:{durable:" + str(durable)
+ if xprops != None: s += ", x-declare:{" + xprops + "}"
+ return s + "}}"
+
+ def send_message(self, queue, message, durable=True, xprops=None, session=None):
+ if session == None:
+ s = self.connect().session()
+ else:
+ s = session
+ s.sender(self._prep_sender(queue, durable, xprops)).send(message)
+ if session == None:
+ s.connection.close()
+
+ def send_messages(self, queue, messages, durable=True, xprops=None, session=None):
+ if session == None:
+ s = self.connect().session()
+ else:
+ s = session
+ sender = s.sender(self._prep_sender(queue, durable, xprops))
+ for m in messages: sender.send(m)
+ if session == None:
+ s.connection.close()
+
+ def get_message(self, queue):
+ s = self.connect().session()
+ m = s.receiver(queue+"; {create:always}", capacity=1).fetch(timeout=1)
+ s.acknowledge()
+ s.connection.close()
+ return m
+
+ def get_messages(self, queue, n):
+ s = self.connect().session()
+ receiver = s.receiver(queue+"; {create:always}", capacity=n)
+ m = [receiver.fetch(timeout=1) for i in range(n)]
+ s.acknowledge()
+ s.connection.close()
+ return m
+
+ def host_port(self): return "%s:%s" % (self.host(), self.port())
+
+ def ready(self, timeout=10, **kwargs):
+ """Wait till broker is ready to serve clients"""
+ deadline = time.time()+timeout
+ while True:
+ try:
+ c = self.connect(timeout=timeout, **kwargs)
+ try:
+ c.session()
+ return # All good
+ finally: c.close()
+ except Exception,e: # Retry up to timeout
+ if time.time() > deadline:
+ raise RethrownException(
+ "Broker %s not responding: (%s)%s"%(
+ self.name,e,error_line(self.log, 5)))
+
+ def assert_log_clean(self, ignore=None):
+ log = open(self.get_log())
+ try:
+ error = re.compile("] error|] critical")
+ if ignore: ignore = re.compile(ignore)
+ else: ignore = re.compile("\000") # Won't match anything
+ for line in log.readlines():
+ assert not error.search(line) or ignore.search(line), "Errors in log file %s: %s"%(log, line)
+ finally: log.close()
+
+def receiver_iter(receiver, timeout=0):
+ """Make an iterator out of a receiver. Returns messages till Empty is raised."""
+ try:
+ while True:
+ yield receiver.fetch(timeout=timeout)
+ except qm.Empty:
+ pass
+
+def browse(session, queue, timeout=0, transform=lambda m: m.content):
+ """Return a list with the contents of each message on queue."""
+ r = session.receiver("%s;{mode:browse}"%(queue))
+ r.capacity = 100
+ try:
+ return [transform(m) for m in receiver_iter(r, timeout)]
+ finally:
+ r.close()
+
+def assert_browse(session, queue, expect_contents, timeout=0, transform=lambda m: m.content, msg=None):
+ """Assert that the contents of messages on queue (as retrieved
+ using session and timeout) exactly match the strings in
+ expect_contents"""
+ if msg is None: msg = "browse '%s' failed" % queue
+ actual_contents = browse(session, queue, timeout, transform=transform)
+ if msg: msg = "%s: %r != %r"%(msg, expect_contents, actual_contents)
+ assert expect_contents == actual_contents, msg
+
+def assert_browse_retry(session, queue, expect_contents, timeout=1, delay=.001, transform=lambda m:m.content, msg="browse failed"):
+ """Wait up to timeout for contents of queue to match expect_contents"""
+ test = lambda: browse(session, queue, 0, transform=transform) == expect_contents
+ retry(test, timeout, delay)
+ actual_contents = browse(session, queue, 0, transform=transform)
+ if msg: msg = "%s: %r != %r"%(msg, expect_contents, actual_contents)
+ assert expect_contents == actual_contents, msg
+
+class BrokerTest(TestCase):
+ """
+ Tracks processes started by test and kills at end of test.
+ Provides a well-known working directory for each test.
+ """
+
+ def __init__(self, *args, **kwargs):
+ self.longMessage = True # Enable long messages for assert*(..., msg=xxx)
+ TestCase.__init__(self, *args, **kwargs)
+
+ # Environment settings.
+ qpidd_exec = os.path.abspath(checkenv("QPIDD_EXEC"))
+ ha_lib = os.getenv("HA_LIB")
+ xml_lib = os.getenv("XML_LIB")
+ amqp_lib = os.getenv("AMQP_LIB")
+ qpid_config_exec = os.getenv("QPID_CONFIG_EXEC")
+ qpid_route_exec = os.getenv("QPID_ROUTE_EXEC")
+ receiver_exec = os.getenv("RECEIVER_EXEC")
+ sender_exec = os.getenv("SENDER_EXEC")
+ sql_store_lib = os.getenv("STORE_SQL_LIB")
+ sql_clfs_store_lib = os.getenv("STORE_SQL_CLFS_LIB")
+ sql_catalog = os.getenv("STORE_CATALOG")
+ store_lib = os.getenv("STORE_LIB")
+ test_store_lib = os.getenv("TEST_STORE_LIB")
+ rootdir = os.getcwd()
+
+ try:
+ import proton
+ PN_VERSION = (proton.VERSION_MAJOR, proton.VERSION_MINOR)
+ except ImportError:
+ # proton not on path, can't determine version
+ PN_VERSION = (0, 0)
+ except AttributeError:
+ # prior to 0.8 proton did not expose version info
+ PN_VERSION = (0, 7)
+
+ PN_TX_VERSION = (0, 9)
+
+ amqp_tx_supported = PN_VERSION >= PN_TX_VERSION
+
+ @classmethod
+ def amqp_tx_warning(cls):
+ if not cls.amqp_tx_supported:
+ if cls.PN_VERSION == (0, 0):
+ print "WARNING: Cannot test transactions over AMQP 1.0, proton not on path so version could not be determined"
+ elif cls.PN_VERSION == (0, 7):
+ print "WARNING: Cannot test transactions over AMQP 1.0, proton version is 0.7 or less, %s.%s required" % cls.PN_TX_VERSION
+ else:
+ print "WARNING: Cannot test transactions over AMQP 1.0, proton version %s.%s < %s.%s" % (cls.PN_VERSION + cls.PN_TX_VERSION)
+ return False
+ return True
+
+ def configure(self, config): self.config=config
+
+ def setUp(self):
+ defs = self.config.defines
+ outdir = defs.get("OUTDIR") or "brokertest.tmp"
+ self.dir = os.path.join(self.rootdir, outdir, self.id())
+ os.makedirs(self.dir)
+ os.chdir(self.dir)
+ self.teardown_list = [] # things to tear down at end of test
+ if qpid_messaging and self.amqp_lib: default_protocol="amqp1.0"
+ else: default_protocol="amqp0-10"
+ self.protocol = defs.get("PROTOCOL") or default_protocol
+ self.tx_protocol = self.protocol
+ if not self.amqp_tx_supported: self.tx_protocol = "amqp0-10"
+
+ def tearDown(self):
+ err = []
+ self.teardown_list.reverse() # Tear down in reverse order
+ for p in self.teardown_list:
+ log.debug("Tearing down %s", p)
+ try:
+ # Call the first of the methods that is available on p.
+ for m in ["teardown", "close"]:
+ a = getattr(p, m, None)
+ if a: a(); break
+ else: raise Exception("Don't know how to tear down %s", p)
+ except Exception, e:
+ if m != "close": # Ignore connection close errors.
+ err.append("%s: %s"%(e.__class__.__name__, str(e)))
+ self.teardown_list = [] # reset in case more processes start
+ os.chdir(self.rootdir)
+ if err: raise Exception("Unexpected process status:\n "+"\n ".join(err))
+
+ def teardown_add(self, thing):
+ """Call thing.teardown() or thing.close() at end of test"""
+ self.teardown_list.append(thing)
+
+ def popen(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE):
+ """Start a process that will be killed at end of test, in the test dir."""
+ os.chdir(self.dir)
+ p = Popen(cmd, expect, stdin=stdin, stdout=stdout, stderr=stderr)
+ self.teardown_add(p)
+ return p
+
+ def broker(self, args=[], name=None, expect=EXPECT_RUNNING, wait=True, port=0, show_cmd=False, **kw):
+ """Create and return a broker ready for use"""
+ b = Broker(self, args=args, name=name, expect=expect, port=port, show_cmd=show_cmd, **kw)
+ if (wait):
+ try: b.ready()
+ except Exception, e:
+ raise RethrownException("Failed to start broker %s(%s): %s" % (b.name, b.log, e))
+ return b
+
+ def check_output(self, args, stdin=None):
+ p = self.popen(args, stdout=PIPE, stderr=STDOUT)
+ out = p.communicate(stdin)
+ if p.returncode != 0:
+ raise Exception("%s exit code %s, output:\n%s" % (args, p.returncode, out[0]))
+ return out[0]
+
+ def browse(self, *args, **kwargs): browse(*args, **kwargs)
+ def assert_browse(self, *args, **kwargs): assert_browse(*args, **kwargs)
+ def assert_browse_retry(self, *args, **kwargs): assert_browse_retry(*args, **kwargs)
+
+ def protocol_option(self, connection_options=""):
+ if "protocol" in connection_options: return connection_options
+ else: return ",".join(filter(None, [connection_options,"protocol:'%s'"%self.protocol]))
+
+
+def join(thread, timeout=30):
+ thread.join(timeout)
+ if thread.isAlive(): raise Exception("Timed out joining thread %s"%thread)
+
+class RethrownException(Exception):
+ """Captures the stack trace of the current exception to be thrown later"""
+ def __init__(self, msg=""):
+ Exception.__init__(self, msg+"\n"+format_exc())
+
+class StoppableThread(Thread):
+ """
+ Base class for threads that do something in a loop and periodically check
+ to see if they have been stopped.
+ """
+ def __init__(self):
+ self.stopped = False
+ self.error = None
+ Thread.__init__(self)
+
+ def stop(self):
+ self.stopped = True
+ join(self)
+ if self.error: raise self.error
+
+# Options for a client that wants to reconnect automatically.
+RECONNECT_OPTIONS="reconnect:true,reconnect-timeout:10,reconnect-urls-replace:true"
+
+class NumberedSender(Thread):
+ """
+ Thread to run a sender client and send numbered messages until stopped.
+ """
+
+ def __init__(self, broker, max_depth=None, queue="test-queue",
+ connection_options=RECONNECT_OPTIONS,
+ failover_updates=False, url=None, args=[]):
+ """
+ max_depth: enable flow control, ensure sent - received <= max_depth.
+ Requires self.notify_received(n) to be called each time messages are received.
+ """
+ Thread.__init__(self)
+ cmd = ["qpid-send",
+ "--broker", url or broker.host_port(),
+ "--address", "%s;{create:always}"%queue,
+ "--connection-options", "{%s}"%(broker.test.protocol_option(connection_options)),
+ "--content-stdin"
+ ] + args
+ if failover_updates: cmd += ["--failover-updates"]
+ self.sender = broker.test.popen(
+ cmd, expect=EXPECT_RUNNING, stdin=PIPE)
+ self.condition = Condition()
+ self.max = max_depth
+ self.received = 0
+ self.stopped = False
+ self.error = None
+ self.queue = queue
+
+ def write_message(self, n):
+ self.sender.stdin.write(str(n)+"\n")
+ self.sender.stdin.flush()
+
+ def run(self):
+ try:
+ self.sent = 0
+ while not self.stopped:
+ self.sender.assert_running()
+ if self.max:
+ self.condition.acquire()
+ while not self.stopped and self.sent - self.received > self.max:
+ self.condition.wait()
+ self.condition.release()
+ self.write_message(self.sent)
+ self.sent += 1
+ except Exception, e:
+ self.error = RethrownException(
+ "%s: (%s)%s"%(self.sender.pname,e,
+ error_line(self.sender.outfile("err"))))
+
+
+ def notify_received(self, count):
+ """Called by receiver to enable flow control. count = messages received so far."""
+ self.condition.acquire()
+ self.received = count
+ self.condition.notify()
+ self.condition.release()
+
+ def stop(self):
+ self.condition.acquire()
+ try:
+ self.stopped = True
+ self.condition.notify()
+ finally: self.condition.release()
+ join(self)
+ self.write_message(-1) # end-of-messages marker.
+ if self.error: raise self.error
+
+class NumberedReceiver(Thread):
+ """
+ Thread to run a receiver client and verify it receives
+ sequentially numbered messages.
+ """
+ def __init__(self, broker, sender=None, queue="test-queue",
+ connection_options=RECONNECT_OPTIONS,
+ failover_updates=False, url=None, args=[]):
+ """
+ sender: enable flow control. Call sender.received(n) for each message received.
+ """
+ Thread.__init__(self)
+ self.test = broker.test
+ cmd = ["qpid-receive",
+ "--broker", url or broker.host_port(),
+ "--address", "%s;{create:always}"%queue,
+ "--connection-options", "{%s}"%(broker.test.protocol_option(connection_options)),
+ "--forever"
+ ]
+ if failover_updates: cmd += [ "--failover-updates" ]
+ cmd += args
+ self.receiver = self.test.popen(
+ cmd, expect=EXPECT_RUNNING, stdout=PIPE)
+ self.lock = Lock()
+ self.error = None
+ self.sender = sender
+ self.received = 0
+ self.queue = queue
+
+ def read_message(self):
+ n = int(self.receiver.stdout.readline())
+ return n
+
+ def run(self):
+ try:
+ m = self.read_message()
+ while m != -1:
+ self.receiver.assert_running()
+ assert m <= self.received, "%s missing message %s>%s"%(self.queue, m, self.received)
+ if (m == self.received): # Ignore duplicates
+ self.received += 1
+ if self.sender:
+ self.sender.notify_received(self.received)
+ m = self.read_message()
+ except Exception, e:
+ self.error = RethrownException(
+ "%s: (%s)%s"%(self.receiver.pname,e,
+ error_line(self.receiver.outfile("err"))))
+
+ def check(self):
+ """Raise an exception if there has been an error"""
+ if self.error: raise self.error
+
+ def stop(self):
+ """Returns when termination message is received"""
+ join(self)
+ self.check()
+
+def import_script(path):
+ """
+ Import executable script at path as a module.
+ Requires some trickery as scripts are not in standard module format
+ """
+ f = open(path)
+ try:
+ name=os.path.split(path)[1].replace("-","_")
+ return imp.load_module(name, f, path, ("", "r", imp.PY_SOURCE))
+ finally: f.close()
diff --git a/qpid/cpp/src/tests/cli_tests.py b/qpid/cpp/src/tests/cli_tests.py
new file mode 100755
index 0000000000..eee9bc648c
--- /dev/null
+++ b/qpid/cpp/src/tests/cli_tests.py
@@ -0,0 +1,477 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+import os
+import imp
+from qpid.testlib import TestBase010
+from qpid.datatypes import Message
+from qpid.queue import Empty
+from time import sleep
+
+def import_script(path):
+ """
+ Import executable script at path as a module.
+ Requires some trickery as scripts are not in standard module format
+ """
+ f = open(path)
+ try:
+ name=os.path.split(path)[1].replace("-","_")
+ return imp.load_module(name, f, path, ("", "r", imp.PY_SOURCE))
+ finally: f.close()
+
+def checkenv(name):
+ value = os.getenv(name)
+ if not value: raise Exception("Environment variable %s is not set" % name)
+ return value
+
+class CliTests(TestBase010):
+
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
+
+ def remote_port(self):
+ return int(self.defines["remote-port"])
+
+ def cli_dir(self):
+ return self.defines["cli-dir"]
+
+ def makeQueue(self, qname, arguments, api=False):
+ if api:
+ ret = self.qpid_config_api(" add queue " + qname + " " + arguments)
+ else:
+ ret = os.system(self.qpid_config_command(" add queue " + qname + " " + arguments))
+
+ self.assertEqual(ret, 0)
+ queue = self.broker_access.getQueue(qname)
+ if queue:
+ return queue
+ assert False
+
+ def test_queue_params(self):
+ self.startBrokerAccess()
+ queue1 = self.makeQueue("test_queue_params1", "--limit-policy none")
+ queue2 = self.makeQueue("test_queue_params2", "--limit-policy reject")
+ queue3 = self.makeQueue("test_queue_params3", "--limit-policy ring")
+
+ LIMIT = "qpid.policy_type"
+ assert LIMIT not in queue1.arguments
+ self.assertEqual(queue2.arguments[LIMIT], "reject")
+ self.assertEqual(queue3.arguments[LIMIT], "ring")
+
+ queue4 = self.makeQueue("test_queue_params4", "--lvq-key lkey")
+
+ LVQKEY = "qpid.last_value_queue_key"
+
+ assert LVQKEY not in queue3.arguments
+ assert LVQKEY in queue4.arguments
+ assert queue4.arguments[LVQKEY] == "lkey"
+
+ def test_queue_params_api(self):
+ self.startBrokerAccess()
+ queue1 = self.makeQueue("test_queue_params_api1", "--limit-policy none", True)
+ queue2 = self.makeQueue("test_queue_params_api2", "--limit-policy reject", True)
+ queue3 = self.makeQueue("test_queue_params_api3", "--limit-policy ring", True)
+
+ LIMIT = "qpid.policy_type"
+ assert LIMIT not in queue1.arguments
+ self.assertEqual(queue2.arguments[LIMIT], "reject")
+ self.assertEqual(queue3.arguments[LIMIT], "ring")
+
+ queue4 = self.makeQueue("test_queue_params_api4", "--lvq-key lkey")
+
+ LVQKEY = "qpid.last_value_queue_key"
+
+ assert LVQKEY not in queue3.arguments
+ assert LVQKEY in queue4.arguments
+ assert queue4.arguments[LVQKEY] == "lkey"
+
+
+ def test_qpid_config(self):
+ self.startBrokerAccess();
+ qname = "test_qpid_config"
+
+ ret = os.system(self.qpid_config_command(" add queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = os.system(self.qpid_config_command(" del queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+ def test_qpid_config_del_nonempty_queue(self):
+ self.startBrokerAccess();
+ qname = "test_qpid_config_del"
+
+ ret = os.system(self.qpid_config_command(" add queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, True)
+
+ self.startBrokerAccess()
+
+ sess = self.broker_conn.session()
+ tx = sess.sender(qname)
+ tx.send("MESSAGE")
+
+ ret = os.system(self.qpid_config_command(" del queue " + qname))
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, True)
+
+ ret = os.system(self.qpid_config_command(" del queue " + qname + " --force"))
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+
+ def test_qpid_config_api(self):
+ self.startBrokerAccess();
+ qname = "test_qpid_config_api"
+
+ ret = self.qpid_config_api(" add queue " + qname)
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = self.qpid_config_api(" del queue " + qname)
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+
+ def test_qpid_config_sasl_plain_expect_succeed(self):
+ self.startBrokerAccess();
+ qname = "test_qpid_config_sasl_plain_expect_succeed"
+ cmd = " --sasl-mechanism PLAIN -b guest/guest@localhost:"+str(self.broker.port) + " add queue " + qname
+ ret = self.qpid_config_api(cmd)
+ self.assertEqual(ret, 0)
+
+ def test_qpid_config_sasl_plain_expect_fail(self):
+ """Fails because no user name and password is supplied"""
+ self.startBrokerAccess();
+ qname = "test_qpid_config_sasl_plain_expect_fail"
+ cmd = " --sasl-mechanism PLAIN -b localhost:"+str(self.broker.port) + " add queue " + qname
+ ret = self.qpid_config_api(cmd)
+ assert ret != 0
+
+ # helpers for some of the test methods
+ def helper_find_exchange(self, xchgname, typ, expected=True):
+ xchgs = self.broker_access.getAllExchanges()
+ found = False
+ for xchg in xchgs:
+ if xchg.name == xchgname:
+ if typ:
+ self.assertEqual(xchg.type, typ)
+ found = True
+ self.assertEqual(found, expected)
+
+ def helper_create_exchange(self, xchgname, typ="direct", opts=""):
+ foo = self.qpid_config_command(opts + " add exchange " + typ + " " + xchgname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_exchange(xchgname, typ, True)
+
+ def helper_destroy_exchange(self, xchgname):
+ foo = self.qpid_config_command(" del exchange " + xchgname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_exchange(xchgname, False, expected=False)
+
+ def helper_find_queue(self, qname, expected=True):
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, False)
+ found = True
+ self.assertEqual(found, expected)
+
+ def helper_create_queue(self, qname):
+ foo = self.qpid_config_command(" add queue " + qname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_queue(qname, True)
+
+ def helper_destroy_queue(self, qname):
+ foo = self.qpid_config_command(" del queue " + qname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+ self.helper_find_queue(qname, False)
+
+ # test the bind-queue-to-header-exchange functionality
+ def test_qpid_config_headers(self):
+ self.startBrokerAccess();
+ qname = "test_qpid_config"
+ xchgname = "test_xchg"
+
+ # first create a header xchg
+ self.helper_create_exchange(xchgname, typ="headers")
+
+ # create the queue
+ self.helper_create_queue(qname)
+
+ # now bind the queue to the xchg
+ foo = self.qpid_config_command(" bind " + xchgname + " " + qname +
+ " key all foo=bar baz=quux")
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+
+ # he likes it, mikey. Ok, now tear it all down. first the binding
+ ret = os.system(self.qpid_config_command(" unbind " + xchgname + " " + qname +
+ " key"))
+ self.assertEqual(ret, 0)
+
+ # then the queue
+ self.helper_destroy_queue(qname)
+
+ # then the exchange
+ self.helper_destroy_exchange(xchgname)
+
+
+ def test_qpid_config_xml(self):
+ self.startBrokerAccess();
+ qname = "test_qpid_config"
+ xchgname = "test_xchg"
+
+ # first create a header xchg
+ self.helper_create_exchange(xchgname, typ="xml")
+
+ # create the queue
+ self.helper_create_queue(qname)
+
+ # now bind the queue to the xchg
+ foo = self.qpid_config_command("-f test.xquery bind " + xchgname + " " + qname)
+ # print foo
+ ret = os.system(foo)
+ self.assertEqual(ret, 0)
+
+ # he likes it, mikey. Ok, now tear it all down. first the binding
+ ret = os.system(self.qpid_config_command(" unbind " + xchgname + " " + qname +
+ " key"))
+ self.assertEqual(ret, 0)
+
+ # then the queue
+ self.helper_destroy_queue(qname)
+
+ # then the exchange
+ self.helper_destroy_exchange(xchgname)
+
+ def test_qpid_config_durable(self):
+ self.startBrokerAccess();
+ qname = "test_qpid_config"
+
+ ret = os.system(self.qpid_config_command(" add queue --durable " + qname))
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ self.assertEqual(queue.durable, True)
+ found = True
+ self.assertEqual(found, True)
+
+ ret = os.system(self.qpid_config_command(" del queue " + qname))
+ self.assertEqual(ret, 0)
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qname:
+ found = True
+ self.assertEqual(found, False)
+
+ def test_qpid_config_altex(self):
+ self.startBrokerAccess();
+ exName = "testalt"
+ qName = "testqalt"
+ altName = "amq.direct"
+
+ ret = os.system(self.qpid_config_command(" add exchange topic %s --alternate-exchange=%s" % (exName, altName)))
+ self.assertEqual(ret, 0)
+
+ exchanges = self.broker_access.getAllExchanges()
+ found = False
+ for exchange in exchanges:
+ if exchange.name == altName:
+ self.assertEqual(exchange.altExchange, None)
+
+ if exchange.name == exName:
+ found = True
+ if not exchange.altExchange:
+ self.fail("Alternate exchange not set")
+ self.assertEqual(exchange.altExchange, altName)
+ self.assertEqual(found, True)
+
+ ret = os.system(self.qpid_config_command(" add queue %s --alternate-exchange=%s" % (qName, altName)))
+ self.assertEqual(ret, 0)
+
+ ret = os.system(self.qpid_config_command(" queues"))
+ self.assertEqual(ret, 0)
+
+ queues = self.broker_access.getAllQueues()
+ found = False
+ for queue in queues:
+ if queue.name == qName:
+ found = True
+ if not queue.altExchange:
+ self.fail("Alternate exchange not set")
+ self.assertEqual(queue.altExchange, altName)
+ self.assertEqual(found, True)
+
+ def test_qpid_config_list_queues_arguments(self):
+ """
+ Test to verify that when the type of a policy limit is
+ actually a string (though still a valid value), it does not
+ upset qpid-config
+ """
+ self.startBrokerAccess();
+
+ names = ["queue_capacity%s" % (i) for i in range(1, 6)]
+ for name in names:
+ self.session.queue_declare(queue=name, exclusive=True,
+ arguments={'qpid.max_count' : str(i), 'qpid.max_size': '100'})
+
+ output = os.popen(self.qpid_config_command(" queues")).readlines()
+ queues = [line.split()[0] for line in output[2:len(output)]] #ignore first two lines (header)
+
+ for name in names:
+ assert name in queues, "%s not in %s" % (name, queues)
+
+ def test_qpid_route(self):
+ self.startBrokerAccess();
+
+ command = self.cli_dir() + "/qpid-route dynamic add guest/guest@localhost:%d %s:%d amq.topic" %\
+ (self.broker.port, self.remote_host(), self.remote_port())
+ ret = os.system(command)
+ self.assertEqual(ret, 0)
+
+ links = self.broker_access.getAllLinks()
+ found = False
+ for link in links:
+ if link.port == self.remote_port():
+ found = True
+ self.assertEqual(found, True)
+
+ def test_qpid_route_api(self):
+ self.startBrokerAccess();
+
+ ret = self.qpid_route_api("dynamic add "
+ + "guest/guest@localhost:"+str(self.broker.port) + " "
+ + str(self.remote_host())+":"+str(self.remote_port()) + " "
+ +"amq.direct")
+
+ self.assertEqual(ret, 0)
+
+ links = self.broker_access.getAllLinks()
+ found = False
+ for link in links:
+ if link.port == self.remote_port():
+ found = True
+ self.assertEqual(found, True)
+
+
+ def test_qpid_route_api(self):
+ self.startBrokerAccess();
+
+ ret = self.qpid_route_api("dynamic add "
+ + " --client-sasl-mechanism PLAIN "
+ + "guest/guest@localhost:"+str(self.broker.port) + " "
+ + str(self.remote_host())+":"+str(self.remote_port()) + " "
+ +"amq.direct")
+
+ self.assertEqual(ret, 0)
+
+ links = self.broker_access.getAllLinks()
+ found = False
+ for link in links:
+ if link.port == self.remote_port():
+ found = True
+ self.assertEqual(found, True)
+
+ def test_qpid_route_api_expect_fail(self):
+ self.startBrokerAccess();
+
+ ret = self.qpid_route_api("dynamic add "
+ + " --client-sasl-mechanism PLAIN "
+ + "localhost:"+str(self.broker.port) + " "
+ + str(self.remote_host())+":"+str(self.remote_port()) + " "
+ +"amq.direct")
+ assert ret != 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
+
+ def qpid_config_command(self, arg = ""):
+ return self.cli_dir() + "/qpid-config -b localhost:%d" % self.broker.port + " " + arg
+
+ def qpid_config_api(self, arg = ""):
+ script = import_script(checkenv("QPID_CONFIG_EXEC"))
+ broker = ["-b", "localhost:"+str(self.broker.port)]
+ return script.main(broker + arg.split())
+
+ def qpid_route_api(self, arg = ""):
+ script = import_script(checkenv("QPID_ROUTE_EXEC"))
+ return script.main(arg.split())
diff --git a/qpid/cpp/src/tests/config.null b/qpid/cpp/src/tests/config.null
new file mode 100644
index 0000000000..e2f355768b
--- /dev/null
+++ b/qpid/cpp/src/tests/config.null
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Deliberately empty configuration file for tests.
+
diff --git a/qpid/cpp/src/tests/consume.cpp b/qpid/cpp/src/tests/consume.cpp
new file mode 100644
index 0000000000..69110d151f
--- /dev/null
+++ b/qpid/cpp/src/tests/consume.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 <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;
+
+namespace qpid {
+namespace tests {
+
+typedef vector<string> StringSet;
+
+struct Args : public qpid::TestOptions {
+ uint count;
+ uint ack;
+ string queue;
+ bool declare;
+ bool summary;
+ bool print;
+ bool durable;
+
+ Args() : count(1000), ack(0), queue("publish-consume"),
+ declare(false), summary(false), print(false)
+ {
+ addOptions()
+ ("count", optValue(count, "N"), "number of messages to publish")
+ ("ack-frequency", optValue(ack, "N"), "ack every N messages (0 means use no-ack mode)")
+ ("queue", optValue(queue, "<queue name>"), "queue to consume from")
+ ("declare", optValue(declare), "declare the queue")
+ ("durable", optValue(durable), "declare the queue durable, use with declare")
+ ("print-data", optValue(print), "Print the recieved data at info level")
+ ("s,summary", optValue(summary), "Print undecorated rate.");
+ }
+};
+
+Args opts;
+
+struct Client
+{
+ Connection connection;
+ Session session;
+
+ Client()
+ {
+ opts.open(connection);
+ session = connection.newSession();
+ }
+
+ void consume()
+ {
+ if (opts.declare)
+ session.queueDeclare(arg::queue=opts.queue, arg::durable=opts.durable);
+ 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.print) QPID_LOG(info, "Data: " << msg.getData());
+ }
+ 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;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+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/qpid/cpp/src/tests/datagen.cpp b/qpid/cpp/src/tests/datagen.cpp
new file mode 100644
index 0000000000..acbc07d63c
--- /dev/null
+++ b/qpid/cpp/src/tests/datagen.cpp
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <exception>
+#include <iostream>
+#include <stdlib.h>
+#include <time.h>
+#include "qpid/Options.h"
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options
+{
+ uint count;
+ uint minSize;
+ uint maxSize;
+ uint minChar;
+ uint maxChar;
+ bool help;
+
+ Args() : qpid::Options("Random data generator"),
+ count(1), minSize(8), maxSize(4096),
+ minChar(32), maxChar(126),//safely printable ascii chars
+ help(false)
+ {
+ addOptions()
+ ("count", qpid::optValue(count, "N"), "number of data strings to generate")
+ ("min-size", qpid::optValue(minSize, "N"), "minimum size of data string")
+ ("max-size", qpid::optValue(maxSize, "N"), "maximum size of data string")
+ ("min-char", qpid::optValue(minChar, "N"), "minimum char value used in data string")
+ ("max-char", qpid::optValue(maxChar, "N"), "maximum char value used in data string")
+ ("help", qpid::optValue(help), "print this usage statement");
+ }
+
+ bool parse(int argc, char** argv) {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (maxSize < minSize) throw qpid::Options::Exception("max-size must be greater than min-size");
+ if (maxChar < minChar) throw qpid::Options::Exception("max-char must be greater than min-char");
+
+ if (help) {
+ std::cerr << *this << std::endl << std::endl;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ }
+ return false;
+ }
+
+};
+
+uint random(uint min, uint max)
+{
+ return (rand() % (max-min+1)) + min;
+}
+
+std::string generateData(uint size, uint min, uint max)
+{
+ std::string data;
+ for (uint i = 0; i < size; i++) {
+ data += (char) random(min, max);
+ }
+ return data;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ if (opts.parse(argc, argv)) {
+ srand(time(0));
+ for (uint i = 0; i < opts.count; i++) {
+ std::cout << generateData(random(opts.minSize, opts.maxSize), opts.minChar, opts.maxChar) << std::endl;
+ }
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/declare_queues.cpp b/qpid/cpp/src/tests/declare_queues.cpp
new file mode 100644
index 0000000000..bf85b9c04b
--- /dev/null
+++ b/qpid/cpp/src/tests/declare_queues.cpp
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Exception.h>
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+
+using namespace qpid::client;
+
+using namespace std;
+
+int
+main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+ if ( argc != 6 )
+ {
+ cerr << "Usage: declare_queues host port durability queue_name_prefix n_queues\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int durability = atoi(argv[3]);
+ char const * queue_name_prefix = argv[4];
+ int n_queues = atoi(argv[5]);
+
+ FailoverManager connection(settings);
+
+ int max_fail = 13;
+ for ( int i = 0; i < n_queues; ++ i ) {
+ stringstream queue_name;
+ queue_name << queue_name_prefix << '_' << i;
+
+ bool queue_created = false;
+ int failure_count;
+
+ // Any non-transport failure is Bad.
+ try
+ {
+ while ( ! queue_created ) {
+ Session session = connection.connect().newSession();
+ // TransportFailures aren't too bad -- they might happen because
+ // we are doing a cluster failover test. But if we get too many,
+ // we will still bug out.
+ failure_count = 0;
+ try {
+ if ( durability )
+ session.queueDeclare(arg::queue=queue_name.str(), arg::durable=true);
+ else
+ session.queueDeclare(arg::queue=queue_name.str());
+ queue_created = true;
+ cout << "declare_queues: Created queue " << queue_name.str() << endl;
+ }
+ catch ( const qpid::TransportFailure& ) {
+ if ( ++ failure_count >= max_fail ) {
+ cerr << "declare_queues failed: too many transport errors.\n";
+ cerr << " host: " << settings.host
+ << " port: " << settings.port << endl;
+ return 1;
+ }
+ qpid::sys::sleep ( 1 );
+ }
+ }
+ }
+ catch ( const exception & error) {
+ cerr << "declare_queues failed:" << error.what() << endl;
+ cerr << " host: " << settings.host
+ << " port: " << settings.port << endl;
+ return 1;
+ }
+ }
+}
+
+
+
+
+
diff --git a/qpid/cpp/src/tests/dlclose_noop.c b/qpid/cpp/src/tests/dlclose_noop.c
new file mode 100644
index 0000000000..b78cf486d8
--- /dev/null
+++ b/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) { return 0; }
+
diff --git a/qpid/cpp/src/tests/dynamic_log_hires_timestamp b/qpid/cpp/src/tests/dynamic_log_hires_timestamp
new file mode 100755
index 0000000000..75034f9902
--- /dev/null
+++ b/qpid/cpp/src/tests/dynamic_log_hires_timestamp
@@ -0,0 +1,75 @@
+#!/usr/bin/env 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 a simple test to verify dynamic log highres timestamp changes
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping python tests, no python dir."; exit 0; }
+
+LOG_FILE=hires_test.log
+trap cleanup EXIT
+
+cleanup() {
+ test -n "$PORT" && $QPIDD_EXEC --no-module-dir --quit --port $PORT
+}
+
+error() {
+ echo $*;
+ exit 1;
+}
+
+rm -rf $LOG_FILE
+PORT=$($QPIDD_EXEC --auth=no --no-module-dir --daemon --port=0 --interface 127.0.0.1 --log-to-file $LOG_FILE) || error "Could not start broker"
+
+echo Broker for log highres timestamp test started on $PORT, pid is $($QPIDD_EXEC --no-module-dir --check --port $PORT)
+
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='debug+:Broker' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=1 body=LOWRES > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT setLogHiresTimestamp logHires='true' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=2 body=HI_RES > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT setLogHiresTimestamp logHires='false' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=3 body=LOWRES > /dev/null
+
+# Expect 3 log entries with 'echo' in them
+if [[ $(grep echo $LOG_FILE | wc -l) -ne 3 ]]; then
+ cat $LOG_FILE
+ error "Log content error - expected 3 echo log entries"
+fi
+
+# Lines 1 and 3 are length X
+# Line 2 is length X+10 because of timestamp addition
+LEN1=$(grep echo $LOG_FILE | grep \(1 | wc -m)
+LEN2=$(grep echo $LOG_FILE | grep \(2 | wc -m)
+LEN3=$(grep echo $LOG_FILE | grep \(3 | wc -m)
+EXPECTED_LEN2=$(( $LEN1 + 10 ))
+
+if [ $LEN1 -ne $LEN3 ]; then
+ cat $LOG_FILE
+ error "Log content error - expected echo 3 to be same line length as echo 1"
+fi
+
+if [ $LEN2 -ne $EXPECTED_LEN2 ]; then
+ cat $LOG_FILE
+ error "Log content error - expected echo 2 to be 10 characters longer than echo 1"
+fi
+
+rm -rf $LOG_FILE
+echo OK
+
diff --git a/qpid/cpp/src/tests/dynamic_log_level_test b/qpid/cpp/src/tests/dynamic_log_level_test
new file mode 100755
index 0000000000..f8fd7a8dd8
--- /dev/null
+++ b/qpid/cpp/src/tests/dynamic_log_level_test
@@ -0,0 +1,90 @@
+#!/usr/bin/env 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 a simple test to verify dynamic log level changes
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping python tests, no python dir."; exit 0; }
+
+LOG_FILE=log_test.log
+trap cleanup EXIT
+
+cleanup() {
+ test -n "$PORT" && $QPIDD_EXEC --no-module-dir --quit --port $PORT
+}
+
+error() {
+ echo $*;
+ exit 1;
+}
+
+checklog() {
+ if [[ $(grep echo $LOG_FILE | wc -l) -ne $1 ]]; then
+ cat $LOG_FILE
+ error "Log contents not as expected - " $2
+ fi
+}
+
+rm -rf $LOG_FILE
+PORT=$($QPIDD_EXEC --auth=no --no-module-dir --daemon --port=0 --interface 127.0.0.1 --log-to-file $LOG_FILE) || error "Could not start broker"
+
+echo Broker for log level test started on $PORT, pid is $($QPIDD_EXEC --no-module-dir --check --port $PORT)
+
+# Set level to notice+ and send an echo request
+# The 'echo' in the log is hidden since it is at debug level.
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='notice+' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=1 body=HIDDEN > /dev/null
+checklog 0 "Step 1 Expected no echo log entries"
+
+# Next, enable all Broker logs at debug and higher levels and send another echo
+# This 'echo' should be in the log.
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='debug+:Broker' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=2 body=VISIBLE > /dev/null
+checklog 1 "Step 2 Expected one echo log entry"
+
+# Now turn on Broker debug messages but specifically disable ManagementMethod logs
+# The 'echo' should be hidden.
+$srcdir/qpid-ctrl -b localhost:$PORT setLogLevel level='debug+:Broker !debug+:broker::Broker::ManagementMethod' > /dev/null
+$srcdir/qpid-ctrl -b localhost:$PORT echo sequence=3 body=HIDDEN > /dev/null
+checklog 1 "Step 3 Expected one echo log entry"
+
+# Verify that the management get returns what was just set
+$srcdir/qpid-ctrl -b localhost:$PORT getLogLevel > dynamic_log_level.tmp
+if [[ $(grep 'level=debug+:Broker,!debug+:broker::Broker::ManagementMethod' dynamic_log_level.tmp | wc -l) -ne 1 ]]; then
+ error "Step 4 getLogLevel returned unexpected value: " `cat dynamic_log_level.tmp`
+fi
+rm -rf dynamic_log_level.tmp
+
+cleanup
+
+# Start another broker with --log-disable settings and make sure the management string receives them
+rm -rf $LOG_FILE
+PORT=$($QPIDD_EXEC --auth=no --no-module-dir --daemon --port=0 --interface 127.0.0.1 --log-to-file $LOG_FILE --log-enable debug:foo --log-disable debug:bar) || error "Could not start broker"
+echo Broker for log level test started on $PORT, pid is $($QPIDD_EXEC --no-module-dir --check --port $PORT)
+
+$srcdir/qpid-ctrl -b localhost:$PORT getLogLevel > dynamic_log_level.tmp
+if [[ $(grep 'level=debug:foo,!debug:bar' dynamic_log_level.tmp | wc -l) -ne 1 ]]; then
+ error "Step 5 getLogLevel returned unexpected value: " `cat dynamic_log_level.tmp`
+fi
+rm -rf dynamic_log_level.tmp
+
+rm -rf $LOG_FILE
+echo OK
+
diff --git a/qpid/cpp/src/tests/echotest.cpp b/qpid/cpp/src/tests/echotest.cpp
new file mode 100644
index 0000000000..7c30989098
--- /dev/null
+++ b/qpid/cpp/src/tests/echotest.cpp
@@ -0,0 +1,157 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/Connection.h>
+#include <qpid/client/SubscriptionManager.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Options.h>
+
+#include <iostream>
+
+using namespace qpid::client;
+using namespace qpid::framing;
+using namespace qpid::sys;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options,
+ public qpid::client::ConnectionSettings
+{
+ bool help;
+ uint count;
+ uint size;
+ bool summary;
+
+ Args() : qpid::Options("Simple latency test optins"), help(false), count(20), size(0), summary()
+ {
+ using namespace qpid;
+ addOptions()
+ ("help", optValue(help), "Print this usage statement")
+ ("count", optValue(count, "N"), "Number of messages to send")
+ ("size", optValue(count, "N"), "Size of messages")
+ ("broker,b", optValue(host, "HOST"), "Broker host to connect to")
+ ("port,p", optValue(port, "PORT"), "Broker port to connect to")
+ ("username", optValue(username, "USER"), "user name for broker log in.")
+ ("password", optValue(password, "PASSWORD"), "password for broker log in.")
+ ("mechanism", optValue(mechanism, "MECH"), "SASL mechanism to use when authenticating.")
+ ("tcp-nodelay", optValue(tcpNoDelay), "Turn on tcp-nodelay")
+ ("s,summary", optValue(summary), "Print only average latency.");
+ }
+};
+
+uint64_t current_time()
+{
+ return Duration::FromEpoch();
+}
+
+class Listener : public MessageListener
+{
+ private:
+ Session session;
+ SubscriptionManager subscriptions;
+ uint counter;
+ const uint limit;
+ std::string queue;
+ Message request;
+ double total, min, max;
+ bool summary;
+
+ public:
+ Listener(Session& session, uint limit, bool summary);
+ void start(uint size);
+ void received(Message& message);
+};
+
+Listener::Listener(Session& s, uint l, bool summary_) :
+ session(s), subscriptions(s), counter(0), limit(l),
+ queue(session.getId().getName()), total(),
+ min(std::numeric_limits<double>::max()), max(), summary(summary_)
+{}
+
+void Listener::start(uint size)
+{
+ session.queueDeclare(arg::queue=queue, arg::exclusive=true, arg::autoDelete=true);
+ request.getDeliveryProperties().setRoutingKey(queue);
+ subscriptions.subscribe(*this, queue, SubscriptionSettings(FlowControl::unlimited(), ACCEPT_MODE_NONE));
+
+ request.getDeliveryProperties().setTimestamp(current_time());
+ if (size) request.setData(std::string(size, 'X'));
+ async(session).messageTransfer(arg::content=request);
+ subscriptions.run();
+}
+
+void Listener::received(Message& response)
+{
+ //extract timestamp and compute latency:
+ uint64_t sentAt = response.getDeliveryProperties().getTimestamp();
+ uint64_t receivedAt = current_time();
+
+ double latency = ((double) (receivedAt - sentAt)) / TIME_MSEC;
+ if (!summary) cout << "Latency: " << latency << "ms" << endl;
+ min = std::min(latency, min);
+ max = std::max(latency, max);
+ total += latency;
+
+ if (++counter < limit) {
+ request.getDeliveryProperties().setTimestamp(current_time());
+ async(session).messageTransfer(arg::content=request);
+ } else {
+ subscriptions.cancel(queue);
+ if (summary) cout << min << "\t" << max << "\t" << total/limit << endl;
+ else cout << "min: " << min << " max: " << max << " average: " << total/limit << endl;
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ opts.parse(argc, argv);
+
+ if (opts.help) {
+ std::cout << opts << std::endl;
+ return 0;
+ }
+
+ Connection connection;
+ try {
+ connection.open(opts);
+ Session session = connection.newSession();
+ Listener listener(session, opts.count, opts.summary);
+ listener.start(opts.size);
+
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/src/tests/exception_test.cpp b/qpid/cpp/src/tests/exception_test.cpp
new file mode 100644
index 0000000000..3e844b4e58
--- /dev/null
+++ b/qpid/cpp/src/tests/exception_test.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 "test_tools.h"
+#include "BrokerFixture.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/client/MessageListener.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/framing/reply_exceptions.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(exception_test)
+
+// FIXME aconway 2008-06-12: need to update our exception handling to
+// 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) {
+ 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) {
+ SessionFixture fix;
+ fix.session.queueDeclare(arg::queue="q");
+ fix.subs.subscribe(fix.lq, "q");
+ Catcher<TransportFailure> pop(bind(&LocalQueue::pop, &fix.lq, sys::TIME_SEC));
+ fix.shutdownBroker();
+ BOOST_CHECK(pop.join());
+}
+
+QPID_AUTO_TEST_CASE(DisconnectedListen) {
+ SessionFixture fix;
+ struct NullListener : public MessageListener {
+ void received(Message&) { BOOST_FAIL("Unexpected message"); }
+ } l;
+ fix.session.queueDeclare(arg::queue="q");
+ fix.subs.subscribe(l, "q");
+
+ Catcher<TransportFailure> runner(bind(&SubscriptionManager::run, boost::ref(fix.subs)));
+ fix.shutdownBroker();
+ runner.join();
+ BOOST_CHECK_THROW(fix.session.queueDeclare(arg::queue="x"), TransportFailure);
+}
+
+QPID_AUTO_TEST_CASE(NoSuchQueueTest) {
+ SessionFixture fix;
+ ScopedSuppressLogging sl; // Suppress messages for expected errors.
+ BOOST_CHECK_THROW(fix.subs.subscribe(fix.lq, "no such queue"), NotFoundException);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/failing-amqp0-10-python-tests b/qpid/cpp/src/tests/failing-amqp0-10-python-tests
new file mode 100644
index 0000000000..afa263217f
--- /dev/null
+++ b/qpid/cpp/src/tests/failing-amqp0-10-python-tests
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#The following four tests fail the because pure python client excludes
+#the node type for queues from the reply-to address, weheras the swigged
+#client does not (as that prevents it resolving the node on every send)
+qpid.tests.messaging.message.MessageEchoTests.testReplyTo
+qpid.tests.messaging.message.MessageEchoTests.testReplyToQueue
+qpid.tests.messaging.message.MessageEchoTests.testReplyToQueueSubject
+qpid.tests.messaging.message.MessageEchoTests.testProperties
+
+# The following test fails because the swig client throws an error
+# when creating a sender with a node name that is ambiguous and no
+# type specified. By contrast the pure python client defaults to the
+# queue in this case.
+qpid_tests.broker_0_10.new_api.GeneralTests.test_node_disambiguation_2
diff --git a/qpid/cpp/src/tests/failing-amqp1.0-python-tests b/qpid/cpp/src/tests/failing-amqp1.0-python-tests
new file mode 100644
index 0000000000..e76d05d7be
--- /dev/null
+++ b/qpid/cpp/src/tests/failing-amqp1.0-python-tests
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+qpid_tests.broker_0_10.new_api.GeneralTests.test_qpid_3481_acquired_to_alt_exchange_2_consumers
+qpid_tests.broker_0_10.new_api.GeneralTests.test_qpid_3481_acquired_to_alt_exchange
+qpid_tests.broker_0_10.new_api.GeneralTests.test_nolocal_rerouted
+qpid_tests.broker_0_10.new_api.GeneralTests.test_ambiguous_delete_1
+qpid_tests.broker_0_10.new_api.GeneralTests.test_ambiguous_delete_2
+qpid_tests.broker_0_10.new_api.GeneralTests.test_node_disambiguation_2
diff --git a/qpid/cpp/src/tests/fanout_perftest b/qpid/cpp/src/tests/fanout_perftest
new file mode 100755
index 0000000000..168994d372
--- /dev/null
+++ b/qpid/cpp/src/tests/fanout_perftest
@@ -0,0 +1,22 @@
+#!/usr/bin/env 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.
+#
+
+exec `dirname $0`/run_perftest 10000 --mode fanout --npubs 16 --nsubs 16 --size 64
diff --git a/qpid/cpp/src/tests/federated_topic_test b/qpid/cpp/src/tests/federated_topic_test
new file mode 100755
index 0000000000..2d31f9af5a
--- /dev/null
+++ b/qpid/cpp/src/tests/federated_topic_test
@@ -0,0 +1,128 @@
+#!/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
+
+source ./test_env.sh
+
+trap stop_brokers EXIT
+
+start_broker() {
+ $QPIDD_EXEC --daemon --port 0 --interface 127.0.0.1 --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
+ $QPIDD_EXEC --no-module-dir -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"
+ ./qpid-topic-listener -p $MY_PORT > $LOG 2>&1 && rm -f $LOG
+}
+
+publish() {
+ ./qpid-topic-publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS -p $PORT_A
+}
+
+setup_routes() {
+ BROKER_A="daffodil:$PORT_A"
+ BROKER_B="daffodil:$PORT_B"
+ BROKER_C="daffodil:$PORT_C"
+ if (($VERBOSE)); then
+ echo "Establishing routes for topic..."
+ fi
+ $QPID_ROUTE_EXEC route add $BROKER_B $BROKER_A amq.topic topic_control B B
+ $QPID_ROUTE_EXEC route add $BROKER_C $BROKER_B amq.topic topic_control C C
+ if (($VERBOSE)); then
+ echo "linked A->B->C"
+ fi
+ $QPID_ROUTE_EXEC route add $BROKER_B $BROKER_C amq.topic topic_control B B
+ $QPID_ROUTE_EXEC 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
+
+ $QPID_ROUTE_EXEC route add $BROKER_B $BROKER_C amq.direct response B B
+ $QPID_ROUTE_EXEC 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"
+ $QPID_ROUTE_EXEC 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/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py
new file mode 100755
index 0000000000..f5b62019e5
--- /dev/null
+++ b/qpid/cpp/src/tests/federation.py
@@ -0,0 +1,2793 @@
+#!/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
+from qpid.datatypes import Message
+from qpid.queue import Empty
+from qpid.util import URL
+import qpid.messaging
+from time import sleep, time
+
+
+class _FedBroker(object):
+ """
+ A proxy object for a remote broker. Contains connection and management
+ state.
+ """
+ def __init__(self, host, port,
+ conn=None, session=None, qmf_broker=None):
+ self.host = host
+ self.port = port
+ self.url = "%s:%d" % (host, port)
+ self.client_conn = None
+ self.client_session = None
+ self.qmf_broker = None
+ self.qmf_object = None
+ if conn is not None:
+ self.client_conn = conn
+ if session is not None:
+ self.client_session = session
+ if qmf_broker is not None:
+ self.qmf_broker = qmf_broker
+
+
+class FederationTests(TestBase010):
+
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
+
+ def remote_port(self):
+ return int(self.defines["remote-port"])
+
+ def verify_cleanup(self):
+ attempts = 0
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+ while total > 0:
+ attempts += 1
+ if attempts >= 10:
+ self.fail("Bridges and links didn't clean up")
+ return
+ sleep(1)
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+
+ def _setup_brokers(self):
+ ports = [self.remote_port()]
+ extra = self.defines.get("extra-brokers")
+ if extra:
+ for p in extra.split():
+ ports.append(int(p))
+
+ # broker[0] has already been set up.
+ self._brokers = [_FedBroker(self.broker.host,
+ self.broker.port,
+ self.conn,
+ self.session,
+ self.qmf_broker)]
+ self._brokers[0].qmf_object = self.qmf.getObjects(_class="broker")[0]
+
+ # setup remaining brokers
+ for _p in ports:
+ _b = _FedBroker(self.remote_host(), _p)
+ _b.client_conn = self.connect(host=self.remote_host(), port=_p)
+ _b.client_session = _b.client_conn.session("Fed_client_session_" + str(_p))
+ _b.qmf_broker = self.qmf.addBroker(_b.url)
+ for _bo in self.qmf.getObjects(_class="broker"):
+ if _bo.getBroker().getUrl() == _b.qmf_broker.getUrl():
+ _b.qmf_object = _bo
+ break
+ self._brokers.append(_b)
+
+ # add a new-style messaging connection to each broker
+ for _b in self._brokers:
+ _b.connection = qpid.messaging.Connection(_b.url)
+ _b.connection.open()
+
+ def _teardown_brokers(self):
+ """ Un-does _setup_brokers()
+ """
+ # broker[0] is configured at test setup, so it must remain configured
+ for _b in self._brokers[1:]:
+ self.qmf.delBroker(_b.qmf_broker)
+ if not _b.client_session.error():
+ _b.client_session.close(timeout=10)
+ _b.client_conn.close(timeout=10)
+ _b.connection.close()
+
+ def test_bridge_create_and_close(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0, result)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.direct", "my-key", "",
+ "", False, False, False, 0, 0)
+ self.assertEqual(result.status, 0, result)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ result = bridge.close()
+ self.assertEqual(result.status, 0, result)
+
+ result = link.close()
+ self.assertEqual(result.status, 0, result)
+
+ self.verify_cleanup()
+
+ def test_pull_from_exchange(self):
+ """ This test uses an alternative method to manage links and bridges
+ via the broker object.
+ """
+ session = self.session
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+
+ # create link
+ link_args = {"host":self.remote_host(), "port":self.remote_port(), "durable":False,
+ "authMechanism":"PLAIN", "username":"guest", "password":"guest",
+ "transport":"tcp"}
+ result = broker.create("link", "test-link-1", link_args, False)
+ self.assertEqual(result.status, 0, result)
+ link = qmf.getObjects(_class="link")[0]
+
+ # create bridge
+ bridge_args = {"link":"test-link-1", "src":"amq.direct", "dest":"amq.fanout",
+ "key":"my-key"}
+ result = broker.create("bridge", "test-bridge-1", bridge_args, False);
+ self.assertEqual(result.status, 0, result)
+ 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=self.remote_host(), port=self.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 = broker.delete("bridge", "test-bridge-1", {})
+ self.assertEqual(result.status, 0, result)
+
+ result = broker.delete("link", "test-link-1", {})
+ self.assertEqual(result.status, 0, result)
+
+ self.verify_cleanup()
+
+ def test_push_to_exchange(self):
+ session = self.session
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0, result)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, True, False, 0, 0)
+ self.assertEqual(result.status, 0, result)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+
+ #setup queue to receive messages from remote broker
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_push_to_exchange")
+ r_session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ r_session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(session=r_session, queue="fed1", destination="f1")
+ queue = r_session.incoming("f1")
+ sleep(6)
+
+ #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)
+ result = link.close()
+ self.assertEqual(result.status, 0, result)
+
+ self.verify_cleanup()
+
+ def test_pull_from_queue(self):
+ session = self.session
+
+ #setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.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(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0, result)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False, 1, 0)
+ self.assertEqual(result.status, 0, result)
+
+ 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)
+ result = link.close()
+ self.assertEqual(result.status, 0, result)
+
+ self.verify_cleanup()
+
+ def test_pull_from_queue_recovery(self):
+ session = self.session
+
+ #setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_pull_from_queue_recovery")
+ # disable auto-delete otherwise the detach of the fed session may
+ # delete the queue right after this test re-creates it.
+ r_session.queue_declare(queue="my-bridge-queue", auto_delete=False)
+ 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(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0, result)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False, 1, 0)
+ self.assertEqual(result.status, 0, result)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ #recreate the remote bridge queue to invalidate the bridge session
+ r_session.queue_delete (queue="my-bridge-queue", if_empty=False, if_unused=False)
+ r_session.queue_declare(queue="my-bridge-queue", auto_delete=False)
+
+ #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)
+ self.verify_cleanup()
+ r_session.queue_delete (queue="my-bridge-queue", if_empty=False, if_unused=False)
+
+ def test_tracing_automatic(self):
+ remoteUrl = "%s:%d" % (self.remote_host(), self.remote_port())
+ self.startQmf()
+ l_broker = self.qmf_broker
+ r_broker = self.qmf.addBroker(remoteUrl)
+
+ l_brokerObj = self.qmf.getObjects(_class="broker", _broker=l_broker)[0]
+ r_brokerObj = self.qmf.getObjects(_class="broker", _broker=r_broker)[0]
+
+ l_res = l_brokerObj.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ r_res = r_brokerObj.connect(self.broker.host, self.broker.port, False, "PLAIN", "guest", "guest", "tcp")
+
+ self.assertEqual(l_res.status, 0)
+ self.assertEqual(r_res.status, 0)
+
+ l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0]
+ r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0]
+
+ l_res = l_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False, 0, 0)
+ r_res = r_link.bridge(False, "amq.direct", "amq.direct", "key", "", "", False, False, False, 0, 0)
+
+ self.assertEqual(l_res.status, 0)
+ self.assertEqual(r_res.status, 0)
+
+ count = 0
+ while l_link.state != "Operational" or r_link.state != "Operational":
+ count += 1
+ if count > 10:
+ self.fail("Fed links didn't become operational after 10 seconds")
+ sleep(1)
+ l_link = self.qmf.getObjects(_class="link", _broker=l_broker)[0]
+ r_link = self.qmf.getObjects(_class="link", _broker=r_broker)[0]
+ sleep(3)
+
+ #setup queue to receive messages from local broker
+ session = self.session
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.direct", binding_key="key")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ #setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_trace")
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="key")
+ r_session.message_transfer(destination="amq.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ try:
+ msg = queue.get(timeout=5)
+ mp = msg.get("message_properties").application_headers
+ self.assertEqual(mp.__class__, dict)
+ self.assertEqual(mp['x-qpid.trace'], 'REMOTE') # check that the federation-tag override works
+ 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(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "my-bridge-id",
+ "exclude-me,also-exclude-me", False, False, False, 0, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+
+ #setup queue to receive messages from local broker
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+ sleep(6)
+
+ #send messages to remote broker and confirm it is routed to local broker
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_tracing")
+
+ trace = [None, "exclude-me", "a,exclude-me,b", "also-exclude-me,c", "dont-exclude-me"]
+ 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", ttl=1000*60*5)
+ 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"))
+ assert(msg.get("delivery_properties").ttl > 0)
+ assert(msg.get("delivery_properties").ttl < 1000*60*50)
+
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_fanout(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_fanout")
+
+ session.exchange_declare(exchange="fed.fanout", type="fanout")
+ r_session.exchange_declare(exchange="fed.fanout", type="fanout")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.fanout", "fed.fanout", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties()
+ r_session.message_transfer(destination="fed.fanout", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_direct(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_direct")
+
+ session.exchange_declare(exchange="fed.direct", type="direct")
+ r_session.exchange_declare(exchange="fed.direct", type="direct")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.direct", "fed.direct", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.direct", binding_key="fd-key")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="fd-key")
+ r_session.message_transfer(destination="fed.direct", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic")
+
+ session.exchange_declare(exchange="fed.topic", type="topic")
+ r_session.exchange_declare(exchange="fed.topic", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic", "fed.topic", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="ft-key.#")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one.two")
+ r_session.message_transfer(destination="fed.topic", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic_reorigin")
+
+ session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+
+ session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.topic_reorigin_2", binding_key="ft-key.one.#")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic_reorigin", "fed.topic_reorigin", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.topic_reorigin_2", "fed.topic_reorigin_2", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic_reorigin", binding_key="ft-key.#")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one.two")
+ r_session.message_transfer(destination="fed.topic_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = bridge2.close()
+ self.assertEqual(result.status, 0)
+
+ # extra check: verify we don't leak bridge objects - keep the link
+ # around and verify the bridge count has gone to zero
+
+ attempts = 0
+ bridgeCount = len(qmf.getObjects(_class="bridge"))
+ while bridgeCount > 0:
+ attempts += 1
+ if attempts >= 5:
+ self.fail("Bridges didn't clean up")
+ return
+ sleep(1)
+ bridgeCount = len(qmf.getObjects(_class="bridge"))
+
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_direct_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_direct_reorigin")
+
+ session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+
+ session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.direct_reorigin_2", binding_key="ft-key.two")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.direct_reorigin", "fed.direct_reorigin", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.direct_reorigin_2", "fed.direct_reorigin_2", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.direct_reorigin", binding_key="ft-key.one")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one")
+ r_session.message_transfer(destination="fed.direct_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ # Extra test: don't explicitly close() bridge2. When the link is closed,
+ # it should clean up bridge2 automagically. verify_cleanup() will detect
+ # if bridge2 isn't cleaned up and will fail the test.
+ #
+ #result = bridge2.close()
+ #self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_any(self):
+ self.do_test_dynamic_headers('any')
+
+ def test_dynamic_headers_all(self):
+ self.do_test_dynamic_headers('all')
+
+
+ def do_test_dynamic_headers(self, match_mode):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_%s" % match_mode)
+
+ session.exchange_declare(exchange="fed.headers", type="headers")
+ r_session.exchange_declare(exchange="fed.headers", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers", "fed.headers", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.headers", binding_key="key1", arguments={'x-match':match_mode, 'class':'first'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.message_properties(application_headers={'class':'first'})
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.headers", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ content = msg.body
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_reorigin")
+
+ session.exchange_declare(exchange="fed.headers_reorigin", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_reorigin", type="headers")
+
+ session.exchange_declare(exchange="fed.headers_reorigin_2", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_reorigin_2", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.headers_reorigin_2", binding_key="key2", arguments={'x-match':'any', 'class':'second'})
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers_reorigin", "fed.headers_reorigin", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.headers_reorigin_2", "fed.headers_reorigin_2", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.headers_reorigin", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.message_properties(application_headers={'class':'first'})
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.headers_reorigin", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ # Extra test: don't explicitly close() bridge2. When the link is closed,
+ # it should clean up bridge2 automagically. verify_cleanup() will detect
+ # if bridge2 isn't cleaned up and will fail the test.
+ #
+ #result = bridge2.close()
+ #self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_unbind(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_unbind")
+
+ session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers_unbind", "fed.headers_unbind", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ queue = qmf.getObjects(_class="queue", name="fed1")[0]
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ session.exchange_bind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ queue.update()
+ self.assertEqual(queue.bindingCount, 2,
+ "bindings not accounted for (expected 2, got %d)" % queue.bindingCount)
+
+ session.exchange_unbind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1")
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_headers_xml(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_xml")
+
+ session.exchange_declare(exchange="fed.xml", type="xml")
+ r_session.exchange_declare(exchange="fed.xml", type="xml")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.xml", "fed.xml", "", "", "", False, False, True, 0, 0)
+
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.xml", binding_key="key1", arguments={'xquery':'true()'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.delivery_properties(routing_key="key1")
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.xml", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ content = msg.body
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_reorigin_xml(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_reorigin_xml")
+
+ session.exchange_declare(exchange="fed.xml_reorigin", type="xml")
+ r_session.exchange_declare(exchange="fed.xml_reorigin", type="xml")
+
+ session.exchange_declare(exchange="fed.xml_reorigin_2", type="xml")
+ r_session.exchange_declare(exchange="fed.xml_reorigin_2", type="xml")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.xml_reorigin_2", binding_key="key2", arguments={'xquery':'true()'})
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.xml_reorigin", "fed.xml_reorigin", "", "", "", False, False, True, 0, 0)
+
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.xml_reorigin_2", "fed.xml_reorigin_2", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ foo=qmf.getObjects(_class="link")
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.xml_reorigin", binding_key="key1", arguments={'xquery':'true()'})
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ props = r_session.delivery_properties(routing_key="key1")
+ for i in range(1, 11):
+ r_session.message_transfer(destination="fed.xml_reorigin", message=Message(props, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+
+ # Extra test: don't explicitly close() bridge2. When the link is closed,
+ # it should clean up bridge2 automagically. verify_cleanup() will detect
+ # if bridge2 isn't cleaned up and will fail the test.
+ #
+ #result = bridge2.close()
+ #self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def test_dynamic_headers_unbind_xml(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_xml_unbind")
+
+ session.exchange_declare(exchange="fed.xml_unbind", type="xml")
+ r_session.exchange_declare(exchange="fed.xml_unbind", type="xml")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.xml_unbind", "fed.xml_unbind", "", "", "", False, False, True, 0, 0)
+
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ queue = qmf.getObjects(_class="queue", name="fed1")[0]
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ session.exchange_bind(queue="fed1", exchange="fed.xml_unbind", binding_key="key1", arguments={'xquery':'true()'})
+ queue.update()
+ self.assertEqual(queue.bindingCount, 2,
+ "bindings not accounted for (expected 2, got %d)" % queue.bindingCount)
+
+ session.exchange_unbind(queue="fed1", exchange="fed.xml_unbind", binding_key="key1")
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_topic_nodup(self):
+ """Verify that a message whose routing key matches more than one
+ binding does not get duplicated to the same queue.
+ """
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic_nodup")
+
+ session.exchange_declare(exchange="fed.topic", type="topic")
+ r_session.exchange_declare(exchange="fed.topic", type="topic")
+
+ self.startQmf()
+ qmf = self.qmf
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic", "fed.topic", "", "", "", False, False, True, 0, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="red.*")
+ session.exchange_bind(queue="fed1", exchange="fed.topic", binding_key="*.herring")
+
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="red.herring")
+ r_session.message_transfer(destination="fed.topic", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_direct_route_prop(self):
+ """ Set up a tree of uni-directional routes across the direct exchange.
+ Bind the same key to the same queues on the leaf nodes. Verify a
+ message sent with the routing key transverses the tree an arrives at
+ each leaf. Remove one leaf's queue, and verify that messages still
+ reach the other leaf.
+
+ Route Topology:
+
+ +---> B2 queue:"test-queue", binding key:"spudboy"
+ B0 --> B1 --+
+ +---> B3 queue:"test-queue", binding key:"spudboy"
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create direct exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.direct", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.direct").type,
+ "direct", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.direct":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B2
+ result = self._brokers[2].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B3
+ result = self._brokers[3].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX.direct" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX.direct", # src
+ "fedX.direct", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0, # sync
+ 0) # credit
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active. Hopefully, this is long enough!
+ sleep(6)
+
+ # create a queue on B2, bound to "spudboy"
+ self._brokers[2].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[2].client_session.exchange_bind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+
+ # create a queue on B3, bound to "spudboy"
+ self._brokers[3].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[3].client_session.exchange_bind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[2].client_session, queue="fedX1", destination="f1")
+ queue_2 = self._brokers[2].client_session.incoming("f1")
+
+ # subscribe to messages arriving on B3's queue
+ self.subscribe(self._brokers[3].client_session, queue="fedX1", destination="f1")
+ queue_3 = self._brokers[3].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker (twice at
+ # broker B1). Work backwards from binding brokers.
+
+ binding_counts = [1, 2, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ # send 10 msgs from B0
+ for i in range(1, 11):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spudboy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.direct", message=Message(dp, "Message_drp %d" % i))
+
+ # wait for 10 messages to be forwarded from B0->B1,
+ # 10 messages from B1->B2,
+ # and 10 messages from B1->B3
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 10 or exchanges[0].msgRoutes != 10 or
+ exchanges[1].msgReceives != 10 or exchanges[1].msgRoutes != 20 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 10 or exchanges[3].msgRoutes != 10):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B2 and B3
+ for i in range(1, 11):
+ msg = queue_2.get(timeout=5)
+ self.assertEqual("Message_drp %d" % i, msg.body)
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_drp %d" % i, msg.body)
+
+ try:
+ extra = queue_2.get(timeout=1)
+ self.fail("Got unexpected message in queue_2: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+
+ # tear down the queue on B2
+ self._brokers[2].client_session.exchange_unbind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+ self._brokers[2].client_session.message_cancel(destination="f1")
+ self._brokers[2].client_session.queue_delete(queue="fedX1")
+
+ # @todo - restore code when QPID-2499 fixed!!
+ sleep(6)
+ # wait for the binding count on B1 to drop from 2 to 1
+ retries = 0
+ exchanges[1].update()
+ while exchanges[1].bindingCount != 1:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "unbinding failed to propagate to broker B1: %d"
+ % exchanges[1].bindingCount)
+ sleep(1)
+ exchanges[1].update()
+
+ # send 10 msgs from B0
+ for i in range(11, 21):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spudboy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.direct", message=Message(dp, "Message_drp %d" % i))
+
+ # verify messages are forwarded to B3 only
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 20 or exchanges[0].msgRoutes != 20 or
+ exchanges[1].msgReceives != 20 or exchanges[1].msgRoutes != 30 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 20 or exchanges[3].msgRoutes != 20):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route more msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B3 only
+ for i in range(11, 21):
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_drp %d" % i, msg.body)
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # cleanup
+
+ self._brokers[3].client_session.exchange_unbind(queue="fedX1", exchange="fedX.direct", binding_key="spudboy")
+ self._brokers[3].client_session.message_cancel(destination="f1")
+ self._brokers[3].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.direct")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+ def test_dynamic_topic_route_prop(self):
+ """ Set up a tree of uni-directional routes across a topic exchange.
+ Bind the same key to the same queues on the leaf nodes. Verify a
+ message sent with the routing key transverses the tree an arrives at
+ each leaf. Remove one leaf's queue, and verify that messages still
+ reach the other leaf.
+
+ Route Topology:
+
+ +---> B2 queue:"test-queue", binding key:"spud.*"
+ B0 --> B1 --+
+ +---> B3 queue:"test-queue", binding key:"spud.*"
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.topic", type="topic")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.topic").type,
+ "topic", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.topic":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B2
+ result = self._brokers[2].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B3
+ result = self._brokers[3].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX.topic" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX.topic", # src
+ "fedX.topic", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0, # sync
+ 0) # credit
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active.
+ sleep(6)
+
+ # create a queue on B2, bound to "spudboy"
+ self._brokers[2].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[2].client_session.exchange_bind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+
+ # create a queue on B3, bound to "spudboy"
+ self._brokers[3].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[3].client_session.exchange_bind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[2].client_session, queue="fedX1", destination="f1")
+ queue_2 = self._brokers[2].client_session.incoming("f1")
+
+ # subscribe to messages arriving on B3's queue
+ self.subscribe(self._brokers[3].client_session, queue="fedX1", destination="f1")
+ queue_3 = self._brokers[3].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker (twice at
+ # broker B1). Work backwards from binding brokers.
+
+ binding_counts = [1, 2, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ # send 10 msgs from B0
+ for i in range(1, 11):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spud.boy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.topic", message=Message(dp, "Message_trp %d" % i))
+
+ # wait for 10 messages to be forwarded from B0->B1,
+ # 10 messages from B1->B2,
+ # and 10 messages from B1->B3
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 10 or exchanges[0].msgRoutes != 10 or
+ exchanges[1].msgReceives != 10 or exchanges[1].msgRoutes != 20 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 10 or exchanges[3].msgRoutes != 10):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B2 and B3
+ for i in range(1, 11):
+ msg = queue_2.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+
+ try:
+ extra = queue_2.get(timeout=1)
+ self.fail("Got unexpected message in queue_2: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # tear down the queue on B2
+ self._brokers[2].client_session.exchange_unbind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+ self._brokers[2].client_session.message_cancel(destination="f1")
+ self._brokers[2].client_session.queue_delete(queue="fedX1")
+
+ # wait for the binding count on B1 to drop from 2 to 1
+ retries = 0
+ exchanges[1].update()
+ while exchanges[1].bindingCount != 1:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "unbinding failed to propagate to broker B1: %d"
+ % exchanges[1].bindingCount)
+ sleep(1)
+ exchanges[1].update()
+
+ # send 10 msgs from B0
+ for i in range(11, 21):
+ dp = self._brokers[0].client_session.delivery_properties(routing_key="spud.boy")
+ self._brokers[0].client_session.message_transfer(destination="fedX.topic", message=Message(dp, "Message_trp %d" % i))
+
+ # verify messages are forwarded to B3 only
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 20 or exchanges[0].msgRoutes != 20 or
+ exchanges[1].msgReceives != 20 or exchanges[1].msgRoutes != 30 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 20 or exchanges[3].msgRoutes != 20):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route more msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B3 only
+ for i in range(11, 21):
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # cleanup
+
+ self._brokers[3].client_session.exchange_unbind(queue="fedX1", exchange="fedX.topic", binding_key="spud.*")
+ self._brokers[3].client_session.message_cancel(destination="f1")
+ self._brokers[3].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.topic")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_fanout_route_prop(self):
+ """ Set up a tree of uni-directional routes across a fanout exchange.
+ Bind the same key to the same queues on the leaf nodes. Verify a
+ message sent with the routing key transverses the tree an arrives at
+ each leaf. Remove one leaf's queue, and verify that messages still
+ reach the other leaf.
+
+ Route Topology:
+
+ +---> B2 queue:"test-queue", binding key:"spud.*"
+ B0 --> B1 --+
+ +---> B3 queue:"test-queue", binding key:"spud.*"
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create fanout exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.fanout", type="fanout")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.fanout").type,
+ "fanout", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.fanout":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B2
+ result = self._brokers[2].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B3
+ result = self._brokers[3].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX.fanout" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX.fanout", # src
+ "fedX.fanout", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0, # sync
+ 0) # credit
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active.
+ sleep(6)
+
+ # create a queue on B2, bound to the exchange
+ self._brokers[2].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[2].client_session.exchange_bind(queue="fedX1", exchange="fedX.fanout")
+
+ # create a queue on B3, bound to the exchange
+ self._brokers[3].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ self._brokers[3].client_session.exchange_bind(queue="fedX1", exchange="fedX.fanout")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[2].client_session, queue="fedX1", destination="f1")
+ queue_2 = self._brokers[2].client_session.incoming("f1")
+
+ # subscribe to messages arriving on B3's queue
+ self.subscribe(self._brokers[3].client_session, queue="fedX1", destination="f1")
+ queue_3 = self._brokers[3].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker (twice at
+ # broker B1). Work backwards from binding brokers.
+
+ binding_counts = [1, 2, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ # send 10 msgs from B0
+ for i in range(1, 11):
+ dp = self._brokers[0].client_session.delivery_properties()
+ self._brokers[0].client_session.message_transfer(destination="fedX.fanout", message=Message(dp, "Message_frp %d" % i))
+
+ # wait for 10 messages to be forwarded from B0->B1,
+ # 10 messages from B1->B2,
+ # and 10 messages from B1->B3
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 10 or exchanges[0].msgRoutes != 10 or
+ exchanges[1].msgReceives != 10 or exchanges[1].msgRoutes != 20 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 10 or exchanges[3].msgRoutes != 10):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B2 and B3
+ for i in range(1, 11):
+ msg = queue_2.get(timeout=5)
+ self.assertEqual("Message_frp %d" % i, msg.body)
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_frp %d" % i, msg.body)
+
+ try:
+ extra = queue_2.get(timeout=1)
+ self.fail("Got unexpected message in queue_2: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # tear down the queue on B2
+ self._brokers[2].client_session.exchange_unbind(queue="fedX1", exchange="fedX.fanout")
+ self._brokers[2].client_session.message_cancel(destination="f1")
+ self._brokers[2].client_session.queue_delete(queue="fedX1")
+
+ # wait for the binding count on B1 to drop from 2 to 1
+ retries = 0
+ exchanges[1].update()
+ while exchanges[1].bindingCount != 1:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "unbinding failed to propagate to broker B1: %d"
+ % exchanges[1].bindingCount)
+ sleep(1)
+ exchanges[1].update()
+
+ # send 10 msgs from B0
+ for i in range(11, 21):
+ dp = self._brokers[0].client_session.delivery_properties()
+ self._brokers[0].client_session.message_transfer(destination="fedX.fanout", message=Message(dp, "Message_frp %d" % i))
+
+ # verify messages are forwarded to B3 only
+ retries = 0
+ for ex in exchanges:
+ ex.update()
+ while (exchanges[0].msgReceives != 20 or exchanges[0].msgRoutes != 20 or
+ exchanges[1].msgReceives != 20 or exchanges[1].msgRoutes != 30 or
+ exchanges[2].msgReceives != 10 or exchanges[2].msgRoutes != 10 or
+ exchanges[3].msgReceives != 20 or exchanges[3].msgRoutes != 20):
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "federation failed to route more msgs %d:%d %d:%d %d:%d %d:%d"
+ % (exchanges[0].msgReceives,
+ exchanges[0].msgRoutes,
+ exchanges[1].msgReceives,
+ exchanges[1].msgRoutes,
+ exchanges[2].msgReceives,
+ exchanges[2].msgRoutes,
+ exchanges[3].msgReceives,
+ exchanges[3].msgRoutes))
+ sleep(1)
+ for ex in exchanges:
+ ex.update()
+
+ # get exactly 10 msgs on B3 only
+ for i in range(11, 21):
+ msg = queue_3.get(timeout=5)
+ self.assertEqual("Message_frp %d" % i, msg.body)
+
+ try:
+ extra = queue_3.get(timeout=1)
+ self.fail("Got unexpected message in queue_3: " + extra.body)
+ except Empty: None
+
+ # cleanup
+
+ self._brokers[3].client_session.exchange_unbind(queue="fedX1", exchange="fedX.fanout")
+ self._brokers[3].client_session.message_cancel(destination="f1")
+ self._brokers[3].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.fanout")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def getAppHeader(self, msg, name):
+ headers = self.getProperty(msg, "application_headers")
+ if headers:
+ return headers[name]
+ return None
+
+ def test_dynamic_topic_bounce(self):
+ """ Bounce the connection between federated Topic Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "topic"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename,
+ binding_key="spud.*")
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud.*")
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud.boy")
+
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_direct_bounce(self):
+ """ Bounce the connection between federated Direct Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "direct"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename, binding_key="spud")
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud")
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud")
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_fanout_bounce(self):
+ """ Bounce the connection between federated Fanout Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "fanout"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename)
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename)
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud")
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_headers_bounce(self):
+ """ Bounce the connection between federated Headers Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "headers"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename,
+ binding_key="spud", arguments={'x-match':'any', 'class':'first'})
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud")
+ def delivery_properties(self, ssn):
+ return ssn.message_properties(application_headers={'class':'first'})
+ ## @todo KAG - re-enable once federation bugs with headers exchanges
+ ## are fixed.
+ #self.generic_dynamic_bounce_test(Params())
+ return
+
+
+ def generic_dynamic_bounce_test(self, params):
+ """ Verify that a federated broker can maintain a binding to a local
+ queue using the same key as a remote binding. Destroy and reconnect
+ the federation link, and verify routes are restored correctly.
+ See QPID-3170.
+ Topology:
+
+ Queue1 <---"Key"---B0<==[Federated Exchange]==>B1---"Key"--->Queue2
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_declare(exchange="fedX", type=params.exchange_type())
+ self.assertEqual(_b.client_session.exchange_query(name="fedX").type,
+ params.exchange_type(), "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ timeout = time() + 10
+ while my_exchange is None and time() <= timeout:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ self.fail("QMF failed to find new exchange!")
+ exchanges.append(my_exchange)
+
+ #
+ # on each broker, create a local queue bound to the exchange with the
+ # same key value.
+ #
+
+ self._brokers[0].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ params.bind_queue(self._brokers[0].client_session, "fedX1", "fedX")
+ self.subscribe(self._brokers[0].client_session, queue="fedX1", destination="f1")
+ queue_0 = self._brokers[0].client_session.incoming("f1")
+
+ self._brokers[1].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ params.bind_queue(self._brokers[1].client_session, "fedX1", "fedX")
+ self.subscribe(self._brokers[1].client_session, queue="fedX1", destination="f1")
+ queue_1 = self._brokers[1].client_session.incoming("f1")
+
+ # now federate the two brokers
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B0
+ result = self._brokers[0].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX", # src
+ "fedX", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0, # sync
+ 0) # credit
+ self.assertEqual(result.status, 0)
+
+ # wait for all the inter-broker links to become operational
+ operational = False
+ timeout = time() + 10
+ while not operational and time() <= timeout:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ self.failUnless(operational, "inter-broker links failed to become operational.")
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active.
+
+ # wait until the binding key has propagated to each broker - each
+ # broker should see 2 bindings (1 local, 1 remote)
+
+ binding_counts = [2, 2]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount < binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ # send 10 msgs to B0
+ for i in range(1, 11):
+ # dp = self._brokers[0].client_session.delivery_properties(routing_key=params.routing_key())
+ dp = params.delivery_properties(self._brokers[0].client_session)
+ self._brokers[0].client_session.message_transfer(destination="fedX", message=Message(dp, "Message_trp %d" % i))
+
+ # get exactly 10 msgs on B0's local queue and B1's queue
+ for i in range(1, 11):
+ try:
+ msg = queue_0.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_1.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ except Empty:
+ self.fail("Only got %d msgs - expected 10" % i)
+ try:
+ extra = queue_0.get(timeout=1)
+ self.fail("Got unexpected message in queue_0: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_1.get(timeout=1)
+ self.fail("Got unexpected message in queue_1: " + extra.body)
+ except Empty: None
+
+ #
+ # Tear down the bridges between the two exchanges, then wait
+ # for the bindings to be cleaned up
+ #
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ binding_counts = [1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount != binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ #
+ # restore the bridges between the two exchanges, and wait for the
+ # bindings to propagate.
+ #
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX", # src
+ "fedX", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0, # sync
+ 0) # credit
+ self.assertEqual(result.status, 0)
+
+ binding_counts = [2, 2]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount != binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ #
+ # verify traffic flows correctly
+ #
+
+ for i in range(1, 11):
+ #dp = self._brokers[1].client_session.delivery_properties(routing_key=params.routing_key())
+ dp = params.delivery_properties(self._brokers[1].client_session)
+ self._brokers[1].client_session.message_transfer(destination="fedX", message=Message(dp, "Message_trp %d" % i))
+
+ # get exactly 10 msgs on B0's queue and B1's queue
+ for i in range(1, 11):
+ try:
+ msg = queue_0.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_1.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ except Empty:
+ self.fail("Only got %d msgs - expected 10" % i)
+ try:
+ extra = queue_0.get(timeout=1)
+ self.fail("Got unexpected message in queue_0: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_1.get(timeout=1)
+ self.fail("Got unexpected message in queue_1: " + extra.body)
+ except Empty: None
+
+
+ #
+ # cleanup
+ #
+ params.unbind_queue(self._brokers[0].client_session, "fedX1", "fedX")
+ self._brokers[0].client_session.message_cancel(destination="f1")
+ self._brokers[0].client_session.queue_delete(queue="fedX1")
+
+ params.unbind_queue(self._brokers[1].client_session, "fedX1", "fedX")
+ self._brokers[1].client_session.message_cancel(destination="f1")
+ self._brokers[1].client_session.queue_delete(queue="fedX1")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_delete(exchange="fedX")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def test_multilink_direct(self):
+ """ Verify that two distinct links can be created between federated
+ brokers.
+ """
+ self.startQmf()
+ qmf = self.qmf
+ self._setup_brokers()
+ src_broker = self._brokers[0]
+ dst_broker = self._brokers[1]
+
+ # create a direct exchange on each broker
+ for _b in [src_broker, dst_broker]:
+ _b.client_session.exchange_declare(exchange="fedX.direct", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.direct").type,
+ "direct", "exchange_declare failed!")
+
+ # create destination queues
+ for _q in [("HiQ", "high"), ("MedQ", "medium"), ("LoQ", "low")]:
+ dst_broker.client_session.queue_declare(queue=_q[0], auto_delete=True)
+ dst_broker.client_session.exchange_bind(queue=_q[0], exchange="fedX.direct", binding_key=_q[1])
+
+ # create two connections, one for high priority traffic
+ for _q in ["HiPri", "Traffic"]:
+ result = dst_broker.qmf_object.create("link", _q,
+ {"host":src_broker.host,
+ "port":src_broker.port},
+ False)
+ self.assertEqual(result.status, 0);
+
+ links = qmf.getObjects(_broker=dst_broker.qmf_broker, _class="link")
+ for _l in links:
+ if _l.name == "HiPri":
+ hi_link = _l
+ elif _l.name == "Traffic":
+ data_link = _l
+ else:
+ self.fail("Unexpected Link found: " + _l.name)
+
+ # now create a route for messages sent with key "high" to use the
+ # hi_link
+ result = dst_broker.qmf_object.create("bridge", "HiPriBridge",
+ {"link":hi_link.name,
+ "src":"fedX.direct",
+ "dest":"fedX.direct",
+ "key":"high"}, False)
+ self.assertEqual(result.status, 0);
+
+
+ # create routes for the "medium" and "low" links to use the normal
+ # data_link
+ for _b in [("MediumBridge", "medium"), ("LowBridge", "low")]:
+ result = dst_broker.qmf_object.create("bridge", _b[0],
+ {"link":data_link.name,
+ "src":"fedX.direct",
+ "dest":"fedX.direct",
+ "key":_b[1]}, False)
+ self.assertEqual(result.status, 0);
+
+ # now wait for the links to become operational
+ for _l in [hi_link, data_link]:
+ expire_time = time() + 30
+ while _l.state != "Operational" and time() < expire_time:
+ _l.update()
+ self.assertEqual(_l.state, "Operational", "Link failed to become operational")
+
+ # verify each link uses a different connection
+ self.assertNotEqual(hi_link.connectionRef, data_link.connectionRef,
+ "Different links using the same connection")
+
+ hi_conn = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=hi_link.connectionRef)[0]
+ data_conn = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=data_link.connectionRef)[0]
+
+
+ # send hi data, verify only goes over hi link
+
+ r_ssn = dst_broker.connection.session()
+ hi_receiver = r_ssn.receiver("HiQ");
+ med_receiver = r_ssn.receiver("MedQ");
+ low_receiver = r_ssn.receiver("LoQ");
+
+ for _c in [hi_conn, data_conn]:
+ _c.update()
+ self.assertEqual(_c.msgsToClient, 0, "Unexpected messages received")
+
+ s_ssn = src_broker.connection.session()
+ hi_sender = s_ssn.sender("fedX.direct/high")
+ med_sender = s_ssn.sender("fedX.direct/medium")
+ low_sender = s_ssn.sender("fedX.direct/low")
+
+ try:
+ hi_sender.send(qpid.messaging.Message(content="hi priority"))
+ msg = hi_receiver.fetch(timeout=10)
+ r_ssn.acknowledge()
+ self.assertEqual(msg.content, "hi priority");
+ except:
+ self.fail("Hi Pri message failure")
+
+ hi_conn.update()
+ data_conn.update()
+ self.assertEqual(hi_conn.msgsToClient, 1, "Expected 1 hi pri message")
+ self.assertEqual(data_conn.msgsToClient, 0, "Expected 0 data messages")
+
+ # send low and medium, verify it does not go over hi link
+
+ try:
+ med_sender.send(qpid.messaging.Message(content="medium priority"))
+ msg = med_receiver.fetch(timeout=10)
+ r_ssn.acknowledge()
+ self.assertEqual(msg.content, "medium priority");
+ except:
+ self.fail("Medium Pri message failure")
+
+ hi_conn.update()
+ data_conn.update()
+ self.assertEqual(hi_conn.msgsToClient, 1, "Expected 1 hi pri message")
+ self.assertEqual(data_conn.msgsToClient, 1, "Expected 1 data message")
+
+ try:
+ low_sender.send(qpid.messaging.Message(content="low priority"))
+ msg = low_receiver.fetch(timeout=10)
+ r_ssn.acknowledge()
+ self.assertEqual(msg.content, "low priority");
+ except:
+ self.fail("Low Pri message failure")
+
+ hi_conn.update()
+ data_conn.update()
+ self.assertEqual(hi_conn.msgsToClient, 1, "Expected 1 hi pri message")
+ self.assertEqual(data_conn.msgsToClient, 2, "Expected 2 data message")
+
+ # cleanup
+
+ for _b in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _q in [("HiQ", "high"), ("MedQ", "medium"), ("LoQ", "low")]:
+ dst_broker.client_session.exchange_unbind(queue=_q[0], exchange="fedX.direct", binding_key=_q[1])
+ dst_broker.client_session.queue_delete(queue=_q[0])
+
+ for _b in [src_broker, dst_broker]:
+ _b.client_session.exchange_delete(exchange="fedX.direct")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def test_multilink_shared_queue(self):
+ """ Verify that two distinct links can be created between federated
+ brokers.
+ """
+ self.startQmf()
+ qmf = self.qmf
+ self._setup_brokers()
+ src_broker = self._brokers[0]
+ dst_broker = self._brokers[1]
+
+ # create a topic exchange on the destination broker
+ dst_broker.client_session.exchange_declare(exchange="fedX.topic", type="topic")
+ self.assertEqual(dst_broker.client_session.exchange_query(name="fedX.topic").type,
+ "topic", "exchange_declare failed!")
+
+ # create a destination queue
+ dst_broker.client_session.queue_declare(queue="destQ", auto_delete=True)
+ dst_broker.client_session.exchange_bind(queue="destQ", exchange="fedX.topic", binding_key="srcQ")
+
+ # create a single source queue
+ src_broker.client_session.queue_declare(queue="srcQ", auto_delete=True)
+
+ # create two connections
+ for _q in ["Link1", "Link2"]:
+ result = dst_broker.qmf_object.create("link", _q,
+ {"host":src_broker.host,
+ "port":src_broker.port},
+ False)
+ self.assertEqual(result.status, 0);
+
+ links = qmf.getObjects(_broker=dst_broker.qmf_broker, _class="link")
+ self.assertEqual(len(links), 2)
+
+ # now create two "parallel" queue routes from the source queue to the
+ # destination exchange.
+ result = dst_broker.qmf_object.create("bridge", "Bridge1",
+ {"link":"Link1",
+ "src":"srcQ",
+ "dest":"fedX.topic",
+ "srcIsQueue": True},
+ False)
+ self.assertEqual(result.status, 0);
+ result = dst_broker.qmf_object.create("bridge", "Bridge2",
+ {"link":"Link2",
+ "src":"srcQ",
+ "dest":"fedX.topic",
+ "srcIsQueue": True},
+ False)
+ self.assertEqual(result.status, 0);
+
+
+ # now wait for the links to become operational
+ for _l in links:
+ expire_time = time() + 30
+ while _l.state != "Operational" and time() < expire_time:
+ _l.update()
+ self.assertEqual(_l.state, "Operational", "Link failed to become operational")
+
+ # verify each link uses a different connection
+ self.assertNotEqual(links[0].connectionRef, links[1].connectionRef,
+ "Different links using the same connection")
+
+ conn1 = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=links[0].connectionRef)[0]
+ conn2 = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=links[1].connectionRef)[0]
+
+ # verify messages sent to the queue are pulled by each connection
+
+ r_ssn = dst_broker.connection.session()
+ receiver = r_ssn.receiver("destQ");
+
+ for _c in [conn1, conn2]:
+ _c.update()
+ self.assertEqual(_c.msgsToClient, 0, "Unexpected messages received")
+
+ s_ssn = src_broker.connection.session()
+ sender = s_ssn.sender("srcQ")
+
+ try:
+ for x in range(5):
+ sender.send(qpid.messaging.Message(content="hello"))
+ for x in range(5):
+ msg = receiver.fetch(timeout=10)
+ self.assertEqual(msg.content, "hello");
+ r_ssn.acknowledge()
+ except:
+ self.fail("Message failure")
+
+ # expect messages to be split over each connection.
+ conn1.update()
+ conn2.update()
+ self.assertNotEqual(conn1.msgsToClient, 0, "No messages sent")
+ self.assertNotEqual(conn2.msgsToClient, 0, "No messages sent")
+ self.assertEqual(conn2.msgsToClient + conn1.msgsToClient, 5,
+ "Expected 5 messages total")
+
+ for _b in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ dst_broker.client_session.exchange_unbind(queue="destQ", exchange="fedX.topic", binding_key="srcQ")
+ dst_broker.client_session.exchange_delete(exchange="fedX.topic")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_direct_shared_queue(self):
+ """
+ Route Topology:
+
+ +<--- B1
+ B0 <---+<--- B2
+ +<--- B3
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create direct exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.direct", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.direct").type,
+ "direct", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.direct":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # Create 2 links per each source broker (1,2,3) to the downstream
+ # broker 0:
+ for _b in range(1,4):
+ for _l in ["dynamic", "queue"]:
+ result = self._brokers[0].qmf_object.create( "link",
+ "Link-%d-%s" % (_b, _l),
+ {"host":self._brokers[_b].host,
+ "port":self._brokers[_b].port}, False)
+ self.assertEqual(result.status, 0)
+
+ # create queue on source brokers for use by the dynamic route
+ self._brokers[_b].client_session.queue_declare(queue="fedSrcQ", exclusive=False, auto_delete=True)
+
+ for _l in range(1,4):
+ # for each dynamic link, create a dynamic bridge for the "fedX.direct"
+ # exchanges, using the fedSrcQ on each upstream source broker
+ result = self._brokers[0].qmf_object.create("bridge",
+ "Bridge-%d-dynamic" % _l,
+ {"link":"Link-%d-dynamic" % _l,
+ "src":"fedX.direct",
+ "dest":"fedX.direct",
+ "dynamic":True,
+ "queue":"fedSrcQ"}, False)
+ self.assertEqual(result.status, 0)
+
+ # create a queue route that shares the queue used by the dynamic route
+ result = self._brokers[0].qmf_object.create("bridge",
+ "Bridge-%d-queue" % _l,
+ {"link":"Link-%d-queue" % _l,
+ "src":"fedSrcQ",
+ "dest":"fedX.direct",
+ "srcIsQueue":True}, False)
+ self.assertEqual(result.status, 0)
+
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active. Hopefully, this is long enough!
+ sleep(6)
+
+ # create a queue on B0, bound to "spudboy"
+ self._brokers[0].client_session.queue_declare(queue="DestQ", exclusive=True, auto_delete=True)
+ self._brokers[0].client_session.exchange_bind(queue="DestQ", exchange="fedX.direct", binding_key="spudboy")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[0].client_session, queue="DestQ", destination="f1")
+ queue = self._brokers[0].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker
+
+ binding_counts = [1, 1, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ for _b in range(1,4):
+ # send 3 msgs from each source broker
+ for i in range(3):
+ dp = self._brokers[_b].client_session.delivery_properties(routing_key="spudboy")
+ self._brokers[_b].client_session.message_transfer(destination="fedX.direct", message=Message(dp, "Message_drp %d" % i))
+
+ # get exactly 9 (3 per broker) on B0
+ for i in range(9):
+ msg = queue.get(timeout=5)
+
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ # verify that messages went across every link
+ for _l in qmf.getObjects(_broker=self._brokers[0].qmf_broker,
+ _class="link"):
+ for _c in qmf.getObjects(_broker=self._brokers[0].qmf_broker,
+ _objectId=_l.connectionRef):
+ self.assertNotEqual(_c.msgsToClient, 0, "Messages did not pass over link as expected.")
+
+ # cleanup
+
+ self._brokers[0].client_session.exchange_unbind(queue="DestQ", exchange="fedX.direct", binding_key="spudboy")
+ self._brokers[0].client_session.message_cancel(destination="f1")
+ self._brokers[0].client_session.queue_delete(queue="DestQ")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.direct")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+ def test_dynamic_bounce_unbinds_named_queue(self):
+ """ Verify that a propagated binding is removed when the connection is
+ bounced
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_declare(exchange="fedX", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX").type,
+ "direct", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ timeout = time() + 10
+ while my_exchange is None and time() <= timeout:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ self.fail("QMF failed to find new exchange!")
+ exchanges.append(my_exchange)
+
+ # on the destination broker, create a binding for propagation
+ self._brokers[0].client_session.queue_declare(queue="fedDstQ")
+ self._brokers[0].client_session.exchange_bind(queue="fedDstQ", exchange="fedX", binding_key="spud")
+
+ # on the source broker, create a bridge queue
+ self._brokers[1].client_session.queue_declare(queue="fedSrcQ")
+
+ # connect B1 --> B0
+ result = self._brokers[0].qmf_object.create( "link",
+ "Link-dynamic",
+ {"host":self._brokers[1].host,
+ "port":self._brokers[1].port}, False)
+ self.assertEqual(result.status, 0)
+
+ # bridge the "fedX" exchange:
+ result = self._brokers[0].qmf_object.create("bridge",
+ "Bridge-dynamic",
+ {"link":"Link-dynamic",
+ "src":"fedX",
+ "dest":"fedX",
+ "dynamic":True,
+ "queue":"fedSrcQ"}, False)
+ self.assertEqual(result.status, 0)
+
+ # wait for the inter-broker links to become operational
+ operational = False
+ timeout = time() + 10
+ while not operational and time() <= timeout:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ self.failUnless(operational, "inter-broker links failed to become operational.")
+
+ # wait until the binding key has propagated to the src broker
+ exchanges[1].update()
+ timeout = time() + 10
+ while exchanges[1].bindingCount < 1 and time() <= timeout:
+ exchanges[1].update()
+ self.failUnless(exchanges[1].bindingCount == 1)
+
+ #
+ # Tear down the bridges between the two exchanges, then wait
+ # for the bindings to be cleaned up
+ #
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+ exchanges[1].update()
+ timeout = time() + 10
+ while exchanges[1].bindingCount != 0 and time() <= timeout:
+ exchanges[1].update()
+ self.failUnless(exchanges[1].bindingCount == 0)
+
+ self._brokers[1].client_session.queue_delete(queue="fedSrcQ")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_delete(exchange="fedX")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+ def test_credit(self):
+ """ Test a federation link configured to use explict acks and a credit
+ limit
+ """
+ session = self.session
+
+ # setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_credit")
+ r_session.queue_declare(queue="my-bridge-queue", auto_delete=True)
+
+ #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(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0, result)
+
+ link = qmf.getObjects(_class="link")[0]
+
+ # now wait for Link to go operational
+ retries = 0
+ operational = False
+ while not operational:
+ link.update()
+ if link.state == "Operational":
+ operational = True;
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # create the subscription
+ result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key",
+ "", "", True, False, False,
+ 3, # explicit ack, with sync every 3 msgs
+ 7) # msg credit
+ self.assertEqual(result.status, 0, result)
+ bridge = qmf.getObjects(_class="bridge")[0]
+
+ # generate enough traffic to trigger flow control and syncs
+ for i in range(1000):
+ dp = r_session.delivery_properties(routing_key="my-bridge-queue")
+ r_session.message_transfer(message=Message(dp, "Message %d" % i))
+
+ for i in range(1000):
+ 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)
+ result = link.close()
+ self.assertEqual(result.status, 0, result)
+
+ r_session.close()
+ r_conn.close()
+
+ self.verify_cleanup()
+
diff --git a/qpid/cpp/src/tests/federation_sys.py b/qpid/cpp/src/tests/federation_sys.py
new file mode 100755
index 0000000000..be9613bb9f
--- /dev/null
+++ b/qpid/cpp/src/tests/federation_sys.py
@@ -0,0 +1,977 @@
+#!/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.
+#
+
+from inspect import stack
+from qpid import messaging
+from qpid.messaging import Message
+from qpid.messaging.exceptions import Empty
+from qpid.testlib import TestBase010
+from random import randint
+from sys import stdout
+from time import sleep
+
+
+class Enum(object):
+ def __init__(self, **entries):
+ self.__dict__.update(entries)
+ def __repr__(self):
+ args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
+ return 'Enum(%s)' % ', '.join(args)
+
+
+class QmfTestBase010(TestBase010):
+
+ _brokers = []
+ _links = []
+ _bridges = []
+ _alt_exch_ops = Enum(none=0, create=1, delete=2)
+
+ class _Broker(object):
+ """
+ This broker proxy object holds the Qmf proxy to a broker of known address as well as the QMF broker
+ object, connection and sessions to the broker.
+ """
+ def __init__(self, url):
+ self.url = url # format: "host:port"
+ url_parts = url.split(':')
+ self.host = url_parts[0]
+ self.port = int(url_parts[1])
+ self.qmf_broker = None
+ self.connection = messaging.Connection.establish(self.url)
+ self.sessions = []
+ def __str__(self):
+ return "_Broker %s:%s (%d open sessions)" % (self.host, self.port, len(self.sessions))
+ def destroy(self, qmf = None):
+ if qmf is not None:
+ qmf.delBroker(self.qmf_broker.getBroker())
+ for session in self.sessions:
+ try: # Session may have been closed by broker error
+ session.close()
+ except Exception, e: print "WARNING: %s: Unable to close session %s (%s): %s %s" % (self, session, hex(id(session)), type(e), e)
+ try: # Connection may have been closed by broker error
+ self.connection.close()
+ except Exception, e: print "WARNING: %s: Unable to close connection %s (%s): %s %s" % (self, self.connection, hex(id(self.connection)), type(e), e)
+ def session(self, name, transactional_flag = False):
+ session = self.connection.session(name, transactional_flag)
+ self.sessions.append(session)
+ return session
+
+ def setUp(self):
+ """
+ Called one before each test starts
+ """
+ TestBase010.setUp(self)
+ self.startQmf();
+
+ def tearDown(self):
+ """
+ Called once after each test competes. Close all Qmf objects (bridges, links and brokers)
+ """
+ while len(self._bridges):
+ self._bridges.pop().close()
+ while len(self._links):
+ self._links.pop().close()
+ while len(self._brokers):
+ b = self._brokers.pop()
+ if len(self._brokers) <= 1:
+ b.destroy(None)
+ else:
+ b.destroy(self.qmf)
+ TestBase010.tearDown(self)
+ self.qmf.close()
+
+ #--- General test utility functions
+
+ def _get_name(self):
+ """
+ Return the name of method which called this method stripped of "test_" prefix. Used for naming
+ queues and exchanges on a per-test basis.
+ """
+ return stack()[1][3][5:]
+
+ def _get_broker_port(self, key):
+ """
+ Get the port of a broker defined in the environment using -D<key>=portno
+ """
+ return int(self.defines[key])
+
+ def _get_send_address(self, exch_name, queue_name):
+ """
+ Get an address to which to send messages based on the exchange name and queue name, but taking into account
+ that the exchange name may be "" (the default exchange), in whcih case the format changes slightly.
+ """
+ if len(exch_name) == 0: # Default exchange
+ return queue_name
+ return "%s/%s" % (exch_name, queue_name)
+
+ def _get_broker(self, broker_port_key):
+ """
+ Read the port numbers for pre-started brokers from the environment using keys, then find or create and return
+ the Qmf broker proxy for the appropriate broker
+ """
+ port = self._get_broker_port(broker_port_key)
+ return self._find_create_broker("localhost:%s" % port)
+ ################
+ def _get_msg_subject(self, topic_key):
+ """
+ Return an appropriate subject for sending a message to a known topic. Return None if there is no topic.
+ """
+ if len(topic_key) == 0: return None
+ if "*" in topic_key: return topic_key.replace("*", "test")
+ if "#" in topic_key: return topic_key.replace("#", "multipart.test")
+ return topic_key
+
+ def _send_msgs(self, session_name, broker, addr, msg_count, msg_content = "Message_%03d", topic_key = "",
+ msg_durable_flag = False, enq_txn_size = 0):
+ """
+ Send messages to a broker using address addr
+ """
+ send_session = broker.session(session_name, transactional_flag = enq_txn_size > 0)
+ sender = send_session.sender(addr)
+ txn_cnt = 0
+ for i in range(0, msg_count):
+ sender.send(Message(msg_content % (i + 1), subject = self._get_msg_subject(topic_key), durable = msg_durable_flag))
+ if enq_txn_size > 0:
+ txn_cnt += 1
+ if txn_cnt >= enq_txn_size:
+ send_session.commit()
+ txn_cnt = 0
+ if enq_txn_size > 0 and txn_cnt > 0:
+ send_session.commit()
+ sender.close()
+ send_session.close()
+
+ def _receive_msgs(self, session_name, broker, addr, msg_count, msg_content = "Message_%03d", deq_txn_size = 0,
+ timeout = 0):
+ """
+ Receive messages from a broker
+ """
+ receive_session = broker.session(session_name, transactional_flag = deq_txn_size > 0)
+ receiver = receive_session.receiver(addr)
+ txn_cnt = 0
+ for i in range(0, msg_count):
+ try:
+ msg = receiver.fetch(timeout = timeout)
+ if deq_txn_size > 0:
+ txn_cnt += 1
+ if txn_cnt >= deq_txn_size:
+ receive_session.commit()
+ txn_cnt = 0
+ receive_session.acknowledge()
+ except Empty:
+ if deq_txn_size > 0: receive_session.rollback()
+ receiver.close()
+ receive_session.close()
+ if i == 0:
+ self.fail("Broker %s queue \"%s\" is empty" % (broker.qmf_broker.getBroker().getUrl(), addr))
+ else:
+ self.fail("Unable to receive message %d from broker %s queue \"%s\"" % (i, broker.qmf_broker.getBroker().getUrl(), addr))
+ if msg.content != msg_content % (i + 1):
+ receiver.close()
+ receive_session.close()
+ self.fail("Unexpected message \"%s\", was expecting \"%s\"." % (msg.content, msg_content % (i + 1)))
+ try:
+ msg = receiver.fetch(timeout = 0)
+ if deq_txn_size > 0: receive_session.rollback()
+ receiver.close()
+ receive_session.close()
+ self.fail("Extra message \"%s\" found on broker %s address \"%s\"" % (msg.content, broker.qmf_broker.getBroker().getUrl(), addr))
+ except Empty:
+ pass
+ if deq_txn_size > 0 and txn_cnt > 0:
+ receive_session.commit()
+ receiver.close()
+ receive_session.close()
+
+ #--- QMF-specific utility functions
+
+ def _get_qmf_property(self, props, key):
+ """
+ Get the value of a named property key kj from a property list [(k0, v0), (k1, v1), ... (kn, vn)].
+ """
+ for k,v in props:
+ if k.name == key:
+ return v
+ return None
+
+ def _check_qmf_return(self, method_result):
+ """
+ Check the result of a Qmf-defined method call
+ """
+ self.assertTrue(method_result.status == 0, method_result.text)
+
+ def _check_optional_qmf_property(self, qmf_broker, type, qmf_object, key, expected_val, obj_ref_flag):
+ """
+ Optional Qmf properties don't show up in the properties list when they are not specified. Checks for
+ these property types involve searching the properties list and making sure it is present or not as
+ expected.
+ """
+ val = self._get_qmf_property(qmf_object.getProperties(), key)
+ if val is None:
+ if len(expected_val) > 0:
+ self.fail("%s %s exists, but has does not have %s property. Expected value: \"%s\"" %
+ (type, qmf_object.name, key, expected_val))
+ else:
+ if len(expected_val) > 0:
+ if obj_ref_flag:
+ obj = self.qmf.getObjects(_objectId = val, _broker = qmf_broker.getBroker())
+ self.assertEqual(len(obj), 1, "More than one object with the same objectId: %s" % obj)
+ val = obj[0].name
+ self.assertEqual(val, expected_val, "%s %s exists, but has incorrect %s property. Found \"%s\", expected \"%s\"" %
+ (type, qmf_object.name, key, val, expected_val))
+ else:
+ self.fail("%s %s exists, but has an unexpected %s property \"%s\" set." % (type, qmf_object.name, key, val))
+
+ #--- Find/create Qmf broker objects
+
+ def _find_qmf_broker(self, url):
+ """
+ Find the Qmf broker object for the given broker URL. The broker must have been previously added to Qmf through
+ addBroker()
+ """
+ for b in self.qmf.getObjects(_class="broker"):
+ if b.getBroker().getUrl() == url:
+ return b
+ return None
+
+ def _find_create_broker(self, url):
+ """
+ Find a running broker through Qmf. If it does not exist, add it (assuming the broker is already running).
+ """
+ broker = self._Broker(url)
+ self._brokers.append(broker)
+ if self.qmf is not None:
+ qmf_broker = self._find_qmf_broker(broker.url)
+ if qmf_broker is None:
+ self.qmf.addBroker(broker.url)
+ broker.qmf_broker = self._find_qmf_broker(broker.url)
+ else:
+ broker.qmf_broker = qmf_broker
+ return broker
+
+ #--- Find/create/delete exchanges
+
+ def _find_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete):
+ """
+ Find Qmf exchange object
+ """
+ for e in self.qmf.getObjects(_class="exchange", _broker = qmf_broker.getBroker()):
+ if e.name == name:
+ if len(name) == 0 or (len(name) >= 4 and name[:4] == "amq."): return e # skip checks for special exchanges
+ self.assertEqual(e.type, type,
+ "Exchange \"%s\" exists, but is of unexpected type %s; expected type %s." %
+ (name, e.type, type))
+ self._check_optional_qmf_property(qmf_broker, "Exchange", e, "altExchange", alternate, True)
+ self.assertEqual(e.durable, durable,
+ "Exchange \"%s\" exists, but has incorrect durability. Found durable=%s, expected durable=%s" %
+ (name, e.durable, durable))
+ self.assertEqual(e.autoDelete, auto_delete,
+ "Exchange \"%s\" exists, but has incorrect auto-delete property. Found %s, expected %s" %
+ (name, e.autoDelete, auto_delete))
+ return e
+ return None
+
+ def _find_create_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete, args):
+ """
+ Find Qmf exchange object if exchange exists, create exchange and return its Qmf object if not
+ """
+ e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
+ if e is not None: return e
+ # Does not exist, so create it
+ props = dict({"exchange-type": type, "type": type, "durable": durable, "auto-delete": auto_delete, "alternate-exchange": alternate}, **args)
+ self._check_qmf_return(qmf_broker.create(type="exchange", name=name, properties=props, strict=True))
+ e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
+ self.assertNotEqual(e, None, "Creation of exchange %s on broker %s failed" % (name, qmf_broker.getBroker().getUrl()))
+ return e
+
+ def _find_delete_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete):
+ """
+ Find and delete Qmf exchange object if it exists
+ """
+ e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
+ if e is not None and not auto_delete:
+ self._check_qmf_return(qmf_broker.delete(type="exchange", name=name, options={}))
+
+ #--- Find/create/delete queues
+
+ def _find_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete):
+ """
+ Find a Qmf queue object
+ """
+ for q in self.qmf.getObjects(_class="queue", _broker = qmf_broker.getBroker()):
+ if q.name == name:
+ self._check_optional_qmf_property(qmf_broker, "Queue", q, "altExchange", alternate_exchange, True)
+ self.assertEqual(q.durable, durable,
+ "Queue \"%s\" exists, but has incorrect durable property. Found %s, expected %s" %
+ (name, q.durable, durable))
+ self.assertEqual(q.exclusive, exclusive,
+ "Queue \"%s\" exists, but has incorrect exclusive property. Found %s, expected %s" %
+ (name, q.exclusive, exclusive))
+ self.assertEqual(q.autoDelete, auto_delete,
+ "Queue \"%s\" exists, but has incorrect auto-delete property. Found %s, expected %s" %
+ (name, q.autoDelete, auto_delete))
+ return q
+ return None
+
+ def _find_create_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete, args):
+ """
+ Find Qmf queue object if queue exists, create queue and return its Qmf object if not
+ """
+ q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
+ if q is not None: return q
+ # Queue does not exist, so create it
+ props = dict({"durable": durable, "auto-delete": auto_delete, "exclusive": exclusive, "alternate-exchange": alternate_exchange}, **args)
+ self._check_qmf_return(qmf_broker.create(type="queue", name=name, properties=props, strict=True))
+ q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
+ self.assertNotEqual(q, None, "Creation of queue %s on broker %s failed" % (name, qmf_broker.getBroker().getUrl()))
+ return q
+
+ def _find_delete_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete, args):
+ """
+ Find and delete Qmf queue object if it exists
+ """
+ q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
+ if q is not None and not auto_delete:
+ self._check_qmf_return(qmf_broker.delete(type="queue", name=name, options={}))
+
+ #--- Find/create/delete bindings (between an exchange and a queue)
+
+ def _find_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
+ """
+ Find a Qmf binding object
+ """
+ for b in self.qmf.getObjects(_class="binding", _broker = qmf_broker.getBroker()):
+ if b.exchangeRef == qmf_exchange.getObjectId() and b.queueRef == qmf_queue.getObjectId():
+ if qmf_exchange.type != "fanout": # Fanout ignores the binding key, and always returns "" as the key
+ self.assertEqual(b.bindingKey, binding_key,
+ "Binding between exchange %s and queue %s exists, but has mismatching binding key: Found %s, expected %s." %
+ (qmf_exchange.name, qmf_queue.name, b.bindingKey, binding_key))
+ self.assertEqual(b.arguments, binding_args,
+ "Binding between exchange %s and queue %s exists, but has mismatching arguments: Found %s, expected %s" %
+ (qmf_exchange.name, qmf_queue.name, b.arguments, binding_args))
+ return b
+ return None
+
+ def _find_create_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
+ """
+ Find Qmf binding object if it exists, create binding and return its Qmf object if not
+ """
+ b = self._find_qmf_binding(qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args)
+ if b is not None: return b
+ # Does not exist, so create it
+ self._check_qmf_return(qmf_broker.create(type="binding", name="%s/%s/%s" % (qmf_exchange.name, qmf_queue.name, binding_key), properties=binding_args, strict=True))
+ b = self._find_qmf_binding(qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args)
+ self.assertNotEqual(b, None, "Creation of binding between exchange %s and queue %s with key %s failed" %
+ (qmf_exchange.name, qmf_queue.name, binding_key))
+ return b
+
+ def _find_delete_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
+ """
+ Find and delete Qmf binding object if it exists
+ """
+ b = self._find_qmf_binding(qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args)
+ if b is not None:
+ if len(qmf_exchange.name) > 0: # not default exchange
+ self._check_qmf_return(qmf_broker.delete(type="binding", name="%s/%s/%s" % (qmf_exchange.name, qmf_queue.name, binding_key), options={}))
+
+ #--- Find/create a link
+
+ def _find_qmf_link(self, qmf_from_broker_proxy, host, port):
+ """
+ Find a Qmf link object
+ """
+ for l in self.qmf.getObjects(_class="link", _broker=qmf_from_broker_proxy):
+ if l.host == host and l.port == port:
+ return l
+ return None
+
+ def _find_create_qmf_link(self, qmf_from_broker, qmf_to_broker_proxy, link_durable_flag, auth_mechanism, user_id,
+ password, transport, pause_interval, link_ready_timeout):
+ """
+ Find a Qmf link object if it exists, create it and return its Qmf link object if not
+ """
+ to_broker_host = qmf_to_broker_proxy.host
+ to_broker_port = qmf_to_broker_proxy.port
+ l = self._find_qmf_link(qmf_from_broker.getBroker(), to_broker_host, to_broker_port)
+ if l is not None: return l
+ # Does not exist, so create it
+ self._check_qmf_return(qmf_from_broker.connect(to_broker_host, to_broker_port, link_durable_flag, auth_mechanism, user_id, password, transport))
+ l = self._find_qmf_link(qmf_from_broker.getBroker(), to_broker_host, to_broker_port)
+ self.assertNotEqual(l, None, "Creation of link from broker %s to broker %s failed" %
+ (qmf_from_broker.getBroker().getUrl(), qmf_to_broker_proxy.getUrl()))
+ self._wait_for_link(l, pause_interval, link_ready_timeout)
+ return l
+
+ def _wait_for_link(self, link, pause_interval, link_ready_timeout):
+ """
+ Wait for link to become active (state=Operational)
+ """
+ tot_time = 0
+ link.update()
+ while link.state != "Operational" and tot_time < link_ready_timeout:
+ sleep(pause_interval)
+ tot_time += pause_interval
+ link.update()
+ self.assertEqual(link.state, "Operational", "Timeout: Link not operational, state=%s" % link.state)
+
+ #--- Find/create a bridge
+
+ def _find_qmf_bridge(self, qmf_broker_proxy, qmf_link, source, destination, key):
+ """
+ Find a Qmf link object
+ """
+ for b in self.qmf.getObjects(_class="bridge", _broker=qmf_broker_proxy):
+ if b.linkRef == qmf_link.getObjectId() and b.src == source and b.dest == destination and b.key == key:
+ return b
+ return None
+
+ def _find_create_qmf_bridge(self, qmf_broker_proxy, qmf_link, queue_name, exch_name, topic_key,
+ queue_route_type_flag, bridge_durable_flag):
+ """
+ Find a Qmf bridge object if it exists, create it and return its Qmf object if not
+ """
+ if queue_route_type_flag:
+ src = queue_name
+ dest = exch_name
+ key = ""
+ else:
+ src = exch_name
+ dest = exch_name
+ if len(topic_key) > 0:
+ key = topic_key
+ else:
+ key = queue_name
+ b = self._find_qmf_bridge(qmf_broker_proxy, qmf_link, src, dest, key)
+ if b is not None:
+ return b
+ # Does not exist, so create it
+ self._check_qmf_return(qmf_link.bridge(bridge_durable_flag, src, dest, key, "", "", queue_route_type_flag, False, False, 1, 0))
+ b = self._find_qmf_bridge(qmf_broker_proxy, qmf_link, src, dest, key)
+ self.assertNotEqual(b, None, "Bridge creation failed: src=%s dest=%s key=%s" % (src, dest, key))
+ return b
+
+ def _wait_for_bridge(self, bridge, src_broker, dest_broker, exch_name, queue_name, topic_key, pause_interval,
+ bridge_ready_timeout):
+ """
+ Wait for bridge to become active by sending messages over the bridge at 1 sec intervals until they are
+ observed at the destination.
+ """
+ tot_time = 0
+ active = False
+ send_session = src_broker.session("tx")
+ sender = send_session.sender(self._get_send_address(exch_name, queue_name))
+ src_receive_session = src_broker.session("src_rx")
+ src_receiver = src_receive_session.receiver(queue_name)
+ dest_receive_session = dest_broker.session("dest_rx")
+ dest_receiver = dest_receive_session.receiver(queue_name)
+ while not active and tot_time < bridge_ready_timeout:
+ sender.send(Message("xyz123", subject = self._get_msg_subject(topic_key)))
+ try:
+ src_receiver.fetch(timeout = 0)
+ src_receive_session.acknowledge()
+ # Keep receiving msgs, as several may have accumulated
+ while True:
+ dest_receiver.fetch(timeout = 0)
+ dest_receive_session.acknowledge()
+ sleep(1)
+ active = True
+ except Empty:
+ sleep(pause_interval)
+ tot_time += pause_interval
+ dest_receiver.close()
+ dest_receive_session.close()
+ src_receiver.close()
+ src_receive_session.close()
+ sender.close()
+ send_session.close()
+ self.assertTrue(active, "Bridge failed to become active after %ds: %s" % (bridge_ready_timeout, bridge))
+
+ #--- Find/create/delete utility functions
+
+ def _create_and_bind(self, qmf_broker, exchange_args, queue_args, binding_args):
+ """
+ Create a binding between a named exchange and queue on a broker
+ """
+ e = self._find_create_qmf_exchange(qmf_broker, **exchange_args)
+ q = self._find_create_qmf_queue(qmf_broker, **queue_args)
+ return self._find_create_qmf_binding(qmf_broker, e, q, **binding_args)
+
+ def _check_alt_exchange(self, qmf_broker, alt_exch_name, alt_exch_type, alt_exch_op):
+ """
+ Check for existence of alternate exchange. Return the Qmf exchange proxy object for the alternate exchange
+ """
+ if len(alt_exch_name) == 0: return None
+ if alt_exch_op == _alt_exch_ops.create:
+ return self._find_create_qmf_exchange(qmf_broker=qmf_broker, name=alt_exch_name, type=alt_exch_type,
+ alternate="", durable=False, auto_delete=False, args={})
+ if alt_exch_op == _alt_exch_ops.delete:
+ return self._find_delete_qmf_exchange(qmf_broker=qmf_broker, name=alt_exch_name, type=alt_exch_type,
+ alternate="", durable=False, auto_delete=False)
+ return self._find_qmf_exchange(qmf_broker=qmf_broker, name=alt_exchange_name, type=alt_exchange_type,
+ alternate="", durable=False, auto_delete=False)
+
+ def _delete_queue_binding(self, qmf_broker, exchange_args, queue_args, binding_args):
+ """
+ Delete a queue and the binding between it and the exchange
+ """
+ e = self._find_qmf_exchange(qmf_broker, exchange_args["name"], exchange_args["type"], exchange_args["alternate"], exchange_args["durable"], exchange_args["auto_delete"])
+ q = self._find_qmf_queue(qmf_broker, queue_args["name"], queue_args["alternate_exchange"], queue_args["durable"], queue_args["exclusive"], queue_args["auto_delete"])
+ self._find_delete_qmf_binding(qmf_broker, e, q, **binding_args)
+ self._find_delete_qmf_queue(qmf_broker, **queue_args)
+
+ def _create_route(self, queue_route_type_flag, src_broker, dest_broker, exch_name, queue_name, topic_key,
+ link_durable_flag, bridge_durable_flag, auth_mechanism, user_id, password, transport,
+ pause_interval = 1, link_ready_timeout = 20, bridge_ready_timeout = 20):
+ """
+ Create a route from a source broker to a destination broker
+ """
+ l = self._find_create_qmf_link(dest_broker.qmf_broker, src_broker.qmf_broker.getBroker(), link_durable_flag,
+ auth_mechanism, user_id, password, transport, pause_interval, link_ready_timeout)
+ self._links.append(l)
+ b = self._find_create_qmf_bridge(dest_broker.qmf_broker.getBroker(), l, queue_name, exch_name, topic_key,
+ queue_route_type_flag, bridge_durable_flag)
+ self._bridges.append(b)
+ self._wait_for_bridge(b, src_broker, dest_broker, exch_name, queue_name, topic_key, pause_interval, bridge_ready_timeout)
+
+ # Parameterized test - entry point for tests
+
+ def _do_test(self,
+ test_name, # Name of test
+ exch_name = "amq.direct", # Remote exchange name
+ exch_type = "direct", # Remote exchange type
+ exch_alt_exch = "", # Remote exchange alternate exchange
+ exch_alt_exch_type = "direct", # Remote exchange alternate exchange type
+ exch_durable_flag = False, # Remote exchange durability
+ exch_auto_delete_flag = False, # Remote exchange auto-delete property
+ exch_x_args = {}, # Remote exchange args
+ queue_alt_exch = "", # Remote queue alternate exchange
+ queue_alt_exch_type = "direct", # Remote queue alternate exchange type
+ queue_durable_flag = False, # Remote queue durability
+ queue_exclusive_flag = False, # Remote queue exclusive property
+ queue_auto_delete_flag = False, # Remote queue auto-delete property
+ queue_x_args = {}, # Remote queue args
+ binding_durable_flag = False, # Remote binding durability
+ binding_x_args = {}, # Remote binding args
+ topic_key = "", # Binding key For remote topic exchanges only
+ msg_count = 10, # Number of messages to send
+ msg_durable_flag = False, # Message durability
+ link_durable_flag = False, # Route link durability
+ bridge_durable_flag = False, # Route bridge durability
+ queue_route_type_flag = False, # Route type: false = bridge route, true = queue route
+ enq_txn_size = 0, # Enqueue transaction size, 0 = no transactions
+ deq_txn_size = 0, # Dequeue transaction size, 0 = no transactions
+ alt_exch_op = _alt_exch_ops.create,# Op on alt exch [create (ensure present), delete (ensure not present), none (neither create nor delete)]
+ auth_mechanism = "", # Authorization mechanism for linked broker
+ user_id = "", # User ID for authorization on linked broker
+ password = "", # Password for authorization on linked broker
+ transport = "tcp" # Transport for route to linked broker
+ ):
+ """
+ Parameterized federation test. Sets up a federated link between a source broker and a destination broker and
+ checks that messages correctly pass over the link to the destination. Where appropriate (non-queue-routes), also
+ checks for the presence of messages on the source broker.
+
+ In these tests, the concept is to create a LOCAL broker, then create a link to a REMOTE broker using federation.
+ In other words, the messages sent to the LOCAL broker will be replicated on the REMOTE broker, and tests are
+ performed on the REMOTE broker to check that the required messages are present. In the case of regular routes,
+ the LOCAL broker will also retain the messages, and a similar test is performed on this broker.
+
+ TODO: There are several items to improve here:
+ 1. _do_test() is rather general. Rather create a version for each exchange type and test the exchange/queue
+ interaction in more detail based on the exchange type
+ 2. Add a headers and an xml exchange type
+ 3. Restructure the tests to start and stop brokers directly rather than relying on previously
+ started brokers. Then persistence can be checked by stopping and restarting the brokers. In particular,
+ test the persistence of links and bridges, both of which take a persistence flag.
+ 4. Test the behavior of the alternate exchanges when messages are sourced through a link. Also check behavior
+ when the alternate exchange is not present or is deleted after the reference is made.
+ 5. Test special queue types (eg LVQ)
+ """
+ local_broker = self._get_broker("local-port")
+ remote_broker = self._get_broker("remote-port")
+
+ # Check alternate exchanges exist (and create them if not) on both local and remote brokers
+ self._check_alt_exchange(local_broker.qmf_broker, exch_alt_exch, exch_alt_exch_type, alt_exch_op)
+ self._check_alt_exchange(local_broker.qmf_broker, queue_alt_exch, queue_alt_exch_type, alt_exch_op)
+ self._check_alt_exchange(remote_broker.qmf_broker, exch_alt_exch, exch_alt_exch_type, alt_exch_op)
+ self._check_alt_exchange(remote_broker.qmf_broker, queue_alt_exch, queue_alt_exch_type, alt_exch_op)
+
+ queue_name = "queue_%s" % test_name
+ exchange_args = {"name": exch_name, "type": exch_type, "alternate": exch_alt_exch,
+ "durable": exch_durable_flag, "auto_delete": exch_auto_delete_flag, "args": exch_x_args}
+ queue_args = {"name": queue_name, "alternate_exchange": queue_alt_exch, "durable": queue_durable_flag,
+ "exclusive": queue_exclusive_flag, "auto_delete": queue_auto_delete_flag, "args": queue_x_args}
+ binding_args = {"binding_args": binding_x_args}
+ if exch_type == "topic":
+ self.assertTrue(len(topic_key) > 0, "Topic exchange selected, but no topic key was set.")
+ binding_args["binding_key"] = topic_key
+ elif exch_type == "direct":
+ binding_args["binding_key"] = queue_name
+ else:
+ binding_args["binding_key"] = ""
+ self._create_and_bind(qmf_broker=local_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+ self._create_and_bind(qmf_broker=remote_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+ self._create_route(queue_route_type_flag, local_broker, remote_broker, exch_name, queue_name, topic_key,
+ link_durable_flag, bridge_durable_flag, auth_mechanism, user_id, password, transport)
+
+ self._send_msgs("send_session", local_broker, addr = self._get_send_address(exch_name, queue_name),
+ msg_count = msg_count, topic_key = topic_key, msg_durable_flag = msg_durable_flag, enq_txn_size = enq_txn_size)
+ if not queue_route_type_flag:
+ self._receive_msgs("local_receive_session", local_broker, addr = queue_name, msg_count = msg_count, deq_txn_size = deq_txn_size)
+ self._receive_msgs("remote_receive_session", remote_broker, addr = queue_name, msg_count = msg_count, deq_txn_size = deq_txn_size, timeout = 5)
+
+ # Clean up
+ self._delete_queue_binding(qmf_broker=local_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+ self._delete_queue_binding(qmf_broker=remote_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+
+class A_ShortTests(QmfTestBase010):
+
+ def test_route_defaultExch(self):
+ self._do_test(self._get_name())
+
+ def test_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True)
+
+
+class A_LongTests(QmfTestBase010):
+
+ def test_route_amqDirectExch(self):
+ self._do_test(self._get_name(), exch_name="amq.direct")
+
+ def test_queueRoute_amqDirectExch(self):
+ self._do_test(self._get_name(), exch_name="amq.direct", queue_route_type_flag=True)
+
+
+ def test_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange")
+
+ def test_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True)
+
+
+ def test_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout")
+
+ def test_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True)
+
+
+ def test_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#")
+
+ def test_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True)
+
+
+class B_ShortTransactionTests(QmfTestBase010):
+
+ def test_txEnq01_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+class B_LongTransactionTests(QmfTestBase010):
+
+ def test_txEnq10_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+
+
+
+ def test_txEnq01_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+class E_ShortPersistenceTests(QmfTestBase010):
+
+ def test_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+class E_LongPersistenceTests(QmfTestBase010):
+
+
+ def test_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+ def test_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+ def test_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+class F_ShortPersistenceTransactionTests(QmfTestBase010):
+
+ def test_txEnq01_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+class F_LongPersistenceTransactionTests(QmfTestBase010):
+
+ def test_txEnq10_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+
+
+
+ def test_txEnq01_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
diff --git a/qpid/cpp/src/tests/find_prog.ps1 b/qpid/cpp/src/tests/find_prog.ps1
new file mode 100644
index 0000000000..5c482debbf
--- /dev/null
+++ b/qpid/cpp/src/tests/find_prog.ps1
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Locate the subdirectory where the specified program resides; the program
+# must have a directory and a file name, even if the directory is .
+param(
+ [string] $prog # program to look for somewhere below cwd
+)
+
+$dir = Split-Path $prog
+$exe = Split-Path $prog -leaf
+$sub = ""
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($try in $subs) {
+ $prog = "$dir\$try\$exe"
+ if (Test-Path $prog) {
+ $sub = $try
+ break
+ }
+}
diff --git a/qpid/cpp/src/tests/ha_test.py b/qpid/cpp/src/tests/ha_test.py
new file mode 100755
index 0000000000..82ca808cb1
--- /dev/null
+++ b/qpid/cpp/src/tests/ha_test.py
@@ -0,0 +1,403 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest, random
+import traceback
+from brokertest import *
+from threading import Thread, Lock, Condition
+from logging import getLogger, WARN, ERROR, DEBUG, INFO
+from qpidtoollibs import BrokerAgent
+from qpid.harness import Skipped
+
+log = getLogger(__name__)
+
+class LogLevel:
+ """
+ Temporarily change the log settings on the root logger.
+ Used to suppress expected WARN messages from the python client.
+ """
+ def __init__(self, level):
+ self.save_level = getLogger().getEffectiveLevel()
+ getLogger().setLevel(level)
+
+ def restore(self):
+ getLogger().setLevel(self.save_level)
+
+class QmfAgent(object):
+ """Access to a QMF broker agent."""
+ def __init__(self, address, **kwargs):
+ self._connection = qm.Connection.establish(
+ address, client_properties={"qpid.ha-admin":1}, **kwargs)
+ self._agent = BrokerAgent(self._connection)
+
+ def queues(self):
+ return [q.values['name'] for q in self._agent.getAllQueues()]
+
+ def repsub_queue(self, sub):
+ """If QMF subscription sub is a replicating subscription return
+ the name of the replicated queue, else return None"""
+ session = self.getSession(sub.sessionRef)
+ if not session: return None
+ m = re.search("qpid.ha-q:(.*)\.", session.name)
+ return m and m.group(1)
+
+ def repsub_queues(self):
+ """Return queue names for all replicating subscriptions"""
+ return filter(None, [self.repsub_queue(s) for s in self.getAllSubscriptions()])
+
+ def tx_queues(self):
+ """Return names of all tx-queues"""
+ return [q for q in self.queues() if q.startswith("qpid.ha-tx")]
+
+ def __getattr__(self, name):
+ a = getattr(self._agent, name)
+ return a
+
+class Credentials(object):
+ """SASL credentials: username, password, and mechanism"""
+ def __init__(self, username, password, mechanism):
+ (self.username, self.password, self.mechanism) = (username, password, mechanism)
+
+ def __str__(self): return "Credentials%s"%(self.tuple(),)
+
+ def tuple(self): return (self.username, self.password, self.mechanism)
+
+ def add_user(self, url): return "%s/%s@%s"%(self.username, self.password, url)
+
+class HaPort:
+ """Many HA tests need to allocate a broker port dynamically and then kill
+ and restart a broker on that same port multiple times. qpidd --port=0 only
+ ensures the port for the initial broker process, subsequent brokers re-using
+ the same port may fail with "address already in use".
+
+ HaPort binds and listens to the port and returns a file descriptor to pass
+ to qpidd --socket-fd. It holds on to the port untill the end of the test so
+ the broker can restart multiple times.
+ """
+
+ def __init__(self, test, port=0):
+ """Bind and listen to port. port=0 allocates a port dynamically.
+ self.port is the allocated port, self.fileno is the file descriptor for
+ qpid --socket-fd."""
+
+ self.test = test
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.socket.bind(("", port))
+ self.socket.listen(5)
+ self.port = self.socket.getsockname()[1]
+ self.fileno = self.socket.fileno()
+ self.stopped = False
+ test.teardown_add(self) # Stop during test.tearDown
+
+ def teardown(self): # Called in tearDown
+ if not self.stopped:
+ self.stopped = True
+ self.socket.shutdown(socket.SHUT_RDWR)
+ self.socket.close()
+
+ def __str__(self): return "HaPort<port:%s, fileno:%s>"%(self.port, self.fileno)
+
+
+class HaBroker(Broker):
+ """Start a broker with HA enabled
+ @param client_cred: (user, password, mechanism) for admin clients started by the HaBroker.
+ """
+
+ heartbeat=5
+
+ def __init__(self, test, ha_port=None, args=[], brokers_url=None, ha_cluster=True,
+ ha_replicate="all", client_credentials=None, **kwargs):
+ assert BrokerTest.ha_lib, "Cannot locate HA plug-in"
+ ha_port = ha_port or HaPort(test)
+ args = copy(args)
+ args += ["--load-module", BrokerTest.ha_lib,
+ # Non-standard settings for faster tests.
+ "--link-maintenance-interval=0.1",
+ "--ha-cluster=%s"%ha_cluster]
+ # Add default --log-enable arguments unless args already has --log arguments.
+ if not env_has_log_config() and not [l for l in args if l.startswith("--log")]:
+ args += ["--log-enable=info+", "--log-enable=debug+:ha::"]
+ if not [h for h in args if h.startswith("--link-heartbeat-interval")]:
+ args += ["--link-heartbeat-interval=%s"%(HaBroker.heartbeat)]
+
+ if ha_replicate is not None:
+ args += [ "--ha-replicate=%s"%ha_replicate ]
+ if brokers_url: args += [ "--ha-brokers-url", brokers_url ]
+ # Set up default ACL
+ acl=os.path.join(os.getcwd(), "unrestricted.acl")
+ if not os.path.exists(acl):
+ aclf=file(acl,"w")
+ aclf.write("""
+acl allow all all
+ """)
+ aclf.close()
+ if not "--acl-file" in args:
+ args += [ "--acl-file", acl, ]
+ args += ["--socket-fd=%s"%ha_port.fileno, "--listen-disable=tcp"]
+ self._agent = None
+ self.client_credentials = client_credentials
+ self.ha_port = ha_port
+ Broker.__init__(self, test, args, port=ha_port.port, **kwargs)
+
+ # Do some static setup to locate the qpid-config and qpid-ha tools.
+ @property
+ def qpid_ha_script(self):
+ if not hasattr(self, "_qpid_ha_script"):
+ qpid_ha_exec = os.getenv("QPID_HA_EXEC")
+ if not qpid_ha_exec or not os.path.isfile(qpid_ha_exec):
+ raise Skipped("qpid-ha not available")
+ self._qpid_ha_script = import_script(qpid_ha_exec)
+ return self._qpid_ha_script
+
+ def __repr__(self): return "<HaBroker:%s:%d>"%(self.log, self.port())
+
+ def qpid_ha(self, args):
+ if not self.qpid_ha_script:
+ raise Skipped("qpid-ha not available")
+ try:
+ cred = self.client_credentials
+ url = self.host_port()
+ if cred:
+ url =cred.add_user(url)
+ args = args + ["--sasl-mechanism", cred.mechanism]
+ self.qpid_ha_script.main_except(["", "-b", url]+args)
+ except Exception, e:
+ raise Exception("Error in qpid_ha -b %s %s: %s"%(url, args,e))
+
+ def promote(self): self.ready(); self.qpid_ha(["promote", "--cluster-manager"])
+ def replicate(self, from_broker, queue): self.qpid_ha(["replicate", from_broker, queue])
+ @property
+ def agent(self):
+ if not self._agent:
+ cred = self.client_credentials
+ if cred:
+ self._agent = QmfAgent(cred.add_user(self.host_port()), sasl_mechanisms=cred.mechanism)
+ else:
+ self._agent = QmfAgent(self.host_port())
+ return self._agent
+
+ def qmf(self):
+ hb = self.agent.getHaBroker()
+ hb.update()
+ return hb
+
+ def ha_status(self): return self.qmf().status
+
+ def wait_status(self, status, timeout=10):
+
+ def try_get_status():
+ self._status = "<unknown>"
+ try:
+ self._status = self.ha_status()
+ except qm.ConnectionError, e:
+ # Record the error but don't raise, the broker may not be up yet.
+ self._status = "%s: %s" % (type(e).__name__, e)
+ return self._status == status;
+ assert retry(try_get_status, timeout=timeout), "%s expected=%r, actual=%r"%(
+ self, status, self._status)
+
+ def wait_queue(self, queue, timeout=10, msg="wait_queue"):
+ """ Wait for queue to be visible via QMF"""
+ agent = self.agent
+ assert retry(lambda: agent.getQueue(queue) is not None, timeout=timeout), \
+ "%s queue %s not present" % (msg, queue)
+
+ def wait_no_queue(self, queue, timeout=10, msg="wait_no_queue"):
+ """ Wait for queue to be invisible via QMF"""
+ agent = self.agent
+ assert retry(lambda: agent.getQueue(queue) is None, timeout=timeout), "%s: queue %s still present"%(msg,queue)
+
+ def qpid_config(self, args):
+ qpid_config_exec = os.getenv("QPID_CONFIG_EXEC")
+ if not qpid_config_exec or not os.path.isfile(qpid_config_exec):
+ raise Skipped("qpid-config not available")
+ assert subprocess.call(
+ [qpid_config_exec, "--broker", self.host_port()]+args, stdout=1, stderr=subprocess.STDOUT
+ ) == 0, "qpid-config failed"
+
+ def config_replicate(self, from_broker, queue):
+ self.qpid_config(["add", "queue", "--start-replica", from_broker, queue])
+
+ def config_declare(self, queue, replication):
+ self.qpid_config(["add", "queue", queue, "--replicate", replication])
+
+ def connect_admin(self, **kwargs):
+ cred = self.client_credentials
+ if cred:
+ return Broker.connect(
+ self, client_properties={"qpid.ha-admin":1},
+ username=cred.username, password=cred.password, sasl_mechanisms=cred.mechanism,
+ **kwargs)
+ else:
+ return Broker.connect(self, client_properties={"qpid.ha-admin":1}, **kwargs)
+
+ def wait_address(self, address):
+ """Wait for address to become valid on the broker."""
+ c = self.connect_admin()
+ try: wait_address(c, address)
+ finally: c.close()
+
+ wait_backup = wait_address
+
+ def browse(self, queue, timeout=0, transform=lambda m: m.content):
+ c = self.connect_admin()
+ try:
+ return browse(c.session(), queue, timeout, transform)
+ finally: c.close()
+
+ def assert_browse_backup(self, queue, expected, **kwargs):
+ """Combines wait_backup and assert_browse_retry."""
+ c = self.connect_admin()
+ try:
+ wait_address(c, queue)
+ assert_browse_retry(c.session(), queue, expected, **kwargs)
+ finally: c.close()
+
+ assert_browse = assert_browse_backup
+
+ def assert_connect_fail(self):
+ try:
+ self.connect()
+ self.test.fail("Expected qm.ConnectionError")
+ except qm.ConnectionError: pass
+
+ def try_connect(self):
+ try: return self.connect()
+ except qm.ConnectionError: return None
+
+ def ready(self, *args, **kwargs):
+ if not 'client_properties' in kwargs: kwargs['client_properties'] = {}
+ kwargs['client_properties']['qpid.ha-admin'] = True
+ return Broker.ready(self, *args, **kwargs)
+
+ def kill(self, final=True):
+ if final: self.ha_port.teardown()
+ self._agent = None
+ return Broker.kill(self)
+
+
+class HaCluster(object):
+ _cluster_count = 0
+
+ def __init__(self, test, n, promote=True, wait=True, args=[], s_args=[], **kwargs):
+ """Start a cluster of n brokers.
+
+ @test: The test being run
+ @n: start n brokers
+ @promote: promote self[0] to primary
+ @wait: wait for primary active and backups ready. Ignored if promote=False
+ @args: args for all brokers in the cluster.
+ @s_args: args for specific brokers: s_args[i] for broker i.
+ """
+ self.test = test
+ self.args = copy(args)
+ self.s_args = copy(s_args)
+ self.kwargs = kwargs
+ self._ports = [HaPort(test) for i in xrange(n)]
+ self._set_url()
+ self._brokers = []
+ self.id = HaCluster._cluster_count
+ self.broker_id = 0
+ HaCluster._cluster_count += 1
+ for i in xrange(n): self.start()
+ if promote:
+ self[0].promote()
+ if wait:
+ self[0].wait_status("active")
+ for b in self[1:]: b.wait_status("ready")
+
+ def next_name(self):
+ name="cluster%s-%s"%(self.id, self.broker_id)
+ self.broker_id += 1
+ return name
+
+ def _ha_broker(self, i, name):
+ args = self.args
+ if i < len(self.s_args): args += self.s_args[i]
+ ha_port = self._ports[i]
+ b = HaBroker(ha_port.test, ha_port, brokers_url=self.url, name=name,
+ args=args, **self.kwargs)
+ b.ready(timeout=10)
+ return b
+
+ def start(self):
+ """Start a new broker in the cluster"""
+ i = len(self)
+ assert i <= len(self._ports)
+ if i == len(self._ports): # Adding new broker after cluster init
+ self._ports.append(HaPort(self.test))
+ self._set_url()
+ b = self._ha_broker(i, self.next_name())
+ self._brokers.append(b)
+ return b
+
+ def _set_url(self):
+ self.url = ",".join("127.0.0.1:%s"%(p.port) for p in self._ports)
+
+ def connect(self, i, **kwargs):
+ """Connect with reconnect_urls"""
+ c = self[i].connect(reconnect=True, reconnect_urls=self.url.split(","), **kwargs)
+ self.test.teardown_add(c) # Clean up
+ return c
+
+ def kill(self, i, promote_next=True, final=True):
+ """Kill broker i, promote broker i+1"""
+ self[i].kill(final=final)
+ if promote_next: self[(i+1) % len(self)].promote()
+
+ def restart(self, i):
+ """Start a broker with the same port, name and data directory. It will get
+ a separate log file: foo.n.log"""
+ if self._ports[i].stopped: raise Exception("Restart after final kill: %s"%(self))
+ b = self._brokers[i]
+ self._brokers[i] = self._ha_broker(i, b.name)
+ self._brokers[i].ready()
+
+ def bounce(self, i, promote_next=True):
+ """Stop and restart a broker in a cluster."""
+ if (len(self) == 1):
+ self.kill(i, promote_next=False, final=False)
+ self.restart(i)
+ self[i].ready()
+ if promote_next: self[i].promote()
+ else:
+ self.kill(i, promote_next, final=False)
+ self.restart(i)
+
+ # Behave like a list of brokers.
+ def __len__(self): return len(self._brokers)
+ def __getitem__(self,index): return self._brokers[index]
+ def __iter__(self): return self._brokers.__iter__()
+
+
+def wait_address(connection, address):
+ """Wait for an address to become valid."""
+ assert retry(lambda: valid_address(connection, address)), "Timed out waiting for address %s"%(address)
+
+def valid_address(connection, address):
+ """Test if an address is valid"""
+ try:
+ s = connection.session().receiver(address)
+ s.session.close()
+ return True
+ except qm.NotFound:
+ return False
+
+
diff --git a/qpid/cpp/src/tests/ha_test_max_queues.cpp b/qpid/cpp/src/tests/ha_test_max_queues.cpp
new file mode 100644
index 0000000000..fcce4c3151
--- /dev/null
+++ b/qpid/cpp/src/tests/ha_test_max_queues.cpp
@@ -0,0 +1,67 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/Connection.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/Url.h>
+#include <qpid/framing/reply_exceptions.h>
+#include <sstream>
+
+using namespace qpid::client;
+using namespace std;
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ cerr << "Expecing URL of broker as argument" << endl;
+ exit(1);
+ }
+ try {
+ // We need to create a large number of queues quickly, so we
+ // use the old API for it's asynchronous commands.
+ // The qpid::messaging API does not allow async queue creation.
+ //
+ Connection c;
+ c.open(qpid::Url(argv[1]));
+ AsyncSession s = async(c.newSession());
+ // Generate too many queues, make sure we get an exception.
+ for (uint64_t i = 0; i < 100000; ++i) {
+ ostringstream os;
+ os << "q" << i;
+ string q = os.str();
+ s.queueDeclare(q, arg::sync=false);
+ if (i && i % 1000 == 0) {
+ s.sync(); // Check for exceptions.
+ cout << "Declared " << q << endl;
+ }
+ }
+ cout << "Expected resource-limit-exceeded exception" << endl;
+ return 1;
+ }
+ catch (const qpid::framing::ResourceLimitExceededException& e) {
+ cout << "Resource limit exceeded: " << e.what() << endl;
+ return 0;
+ }
+ catch (const std::exception& e) {
+ cout << "Error: " << e.what() << endl;
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/ha_tests.py b/qpid/cpp/src/tests/ha_tests.py
new file mode 100755
index 0000000000..2ee2e291e2
--- /dev/null
+++ b/qpid/cpp/src/tests/ha_tests.py
@@ -0,0 +1,1634 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest
+import traceback
+from qpid.datatypes import uuid4, UUID
+from brokertest import *
+from ha_test import *
+from threading import Thread, Lock, Condition
+from logging import getLogger, WARN, ERROR, DEBUG, INFO
+from qpidtoollibs import BrokerAgent, EventHelper
+
+log = getLogger(__name__)
+
+class HaBrokerTest(BrokerTest):
+ """Base class for HA broker tests"""
+
+class ReplicationTests(HaBrokerTest):
+ """Correctness tests for HA replication."""
+
+ def test_replication(self):
+ """Test basic replication of configuration and messages before and
+ after backup has connected"""
+
+ def setup(prefix, primary):
+ """Create config, send messages on the primary p"""
+ a = primary.agent
+
+ def queue(name, replicate):
+ a.addQueue(name, options={'qpid.replicate':replicate})
+ return name
+
+ def exchange(name, replicate, bindq, key):
+ a.addExchange("fanout", name, options={'qpid.replicate':replicate})
+ a.bind(name, bindq, key)
+ return name
+
+ # Test replication of messages
+ p = primary.connect().session()
+ s = p.sender(queue(prefix+"q1", "all"))
+ for m in ["a", "b", "1"]: s.send(qm.Message(m))
+ # Test replication of dequeue
+ self.assertEqual(p.receiver(prefix+"q1").fetch(timeout=0).content, "a")
+ p.acknowledge()
+
+ p.sender(queue(prefix+"q2", "configuration")).send(qm.Message("2"))
+ p.sender(queue(prefix+"q3", "none")).send(qm.Message("3"))
+ p.sender(exchange(prefix+"e1", "all", prefix+"q1", "key1")).send(qm.Message("4"))
+ p.sender(exchange(prefix+"e2", "configuration", prefix+"q2", "key2")).send(qm.Message("5"))
+ # Test unbind
+ p.sender(queue(prefix+"q4", "all")).send(qm.Message("6"))
+ s3 = p.sender(exchange(prefix+"e4", "all", prefix+"q4", "key4"))
+ s3.send(qm.Message("7"))
+ a.unbind(prefix+"e4", prefix+"q4", "key4")
+ p.sender(prefix+"e4").send(qm.Message("drop1")) # Should be dropped
+
+ # Test replication of deletes
+ queue(prefix+"dq", "all")
+ exchange(prefix+"de", "all", prefix+"dq", "")
+ a.delQueue(prefix+"dq")
+ a.delExchange(prefix+"de")
+
+ # Need a marker so we can wait till sync is done.
+ queue(prefix+"x", "configuration")
+
+ def verify(b, prefix, p):
+ """Verify setup was replicated to backup b"""
+ # Wait for configuration to replicate.
+ wait_address(b.connection, prefix+"x");
+ self.assert_browse_retry(b, prefix+"q1", ["b", "1", "4"])
+
+ self.assertEqual(p.receiver(prefix+"q1").fetch(timeout=0).content, "b")
+ p.acknowledge()
+ self.assert_browse_retry(b, prefix+"q1", ["1", "4"])
+
+ self.assert_browse_retry(b, prefix+"q2", []) # configuration only
+ assert not valid_address(b.connection, prefix+"q3")
+
+ # Verify exchange with replicate=all
+ b.sender(prefix+"e1/key1").send(qm.Message(prefix+"e1"))
+ self.assert_browse_retry(b, prefix+"q1", ["1", "4", prefix+"e1"])
+
+ # Verify exchange with replicate=configuration
+ b.sender(prefix+"e2/key2").send(qm.Message(prefix+"e2"))
+ self.assert_browse_retry(b, prefix+"q2", [prefix+"e2"])
+
+ b.sender(prefix+"e4/key4").send(qm.Message("drop2")) # Verify unbind.
+ self.assert_browse_retry(b, prefix+"q4", ["6","7"])
+
+ # Verify deletes
+ assert not valid_address(b.connection, prefix+"dq")
+ assert not valid_address(b.connection, prefix+"de")
+
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ cluster = HaCluster(self, 2)
+ primary = cluster[0]
+ backup = cluster[1]
+
+ # Send messages before re-starting the backup, test catch-up replication.
+ cluster.kill(1, promote_next=False, final=False)
+ setup("1", primary)
+ cluster.restart(1)
+
+ # Send messages after re-starting the backup, to test steady-state replication.
+ setup("2", primary)
+
+ p = primary.connect().session()
+
+ # Verify the data on the backup
+ b = backup.connect_admin().session()
+ verify(b, "1", p)
+ verify(b, "2", p)
+ # Test a series of messages, enqueue all then dequeue all.
+ primary.agent.addQueue("foo")
+ s = p.sender("foo")
+ wait_address(b.connection, "foo")
+ msgs = [str(i) for i in range(10)]
+ for m in msgs: s.send(qm.Message(m))
+ self.assert_browse_retry(p, "foo", msgs)
+ self.assert_browse_retry(b, "foo", msgs)
+ r = p.receiver("foo")
+ for m in msgs: self.assertEqual(m, r.fetch(timeout=0).content)
+ p.acknowledge()
+ self.assert_browse_retry(p, "foo", [])
+ self.assert_browse_retry(b, "foo", [])
+
+ # Another series, this time verify each dequeue individually.
+ for m in msgs: s.send(qm.Message(m))
+ self.assert_browse_retry(p, "foo", msgs)
+ self.assert_browse_retry(b, "foo", msgs)
+ for i in range(len(msgs)):
+ self.assertEqual(msgs[i], r.fetch(timeout=0).content)
+ p.acknowledge()
+ self.assert_browse_retry(p, "foo", msgs[i+1:])
+ self.assert_browse_retry(b, "foo", msgs[i+1:])
+ finally: l.restore()
+
+ def test_sync(self):
+ primary = HaBroker(self, name="primary")
+ primary.promote()
+ p = primary.connect().session()
+ s = p.sender("q;{create:always}")
+ for m in [str(i) for i in range(0,10)]: s.send(m)
+ s.sync()
+ backup1 = HaBroker(self, name="backup1", brokers_url=primary.host_port())
+ for m in [str(i) for i in range(10,20)]: s.send(m)
+ s.sync()
+ backup2 = HaBroker(self, name="backup2", brokers_url=primary.host_port())
+ for m in [str(i) for i in range(20,30)]: s.send(m)
+ s.sync()
+
+ msgs = [str(i) for i in range(30)]
+ b1 = backup1.connect_admin().session()
+ backup1.assert_browse_backup("q", msgs)
+ backup2.assert_browse_backup("q", msgs)
+
+ def test_send_receive(self):
+ """Verify sequence numbers of messages sent by qpid-send"""
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ brokers = HaCluster(self, 3)
+ sender = self.popen(
+ ["qpid-send",
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
+ "--messages=1000",
+ "--content-string=x",
+ "--connection-options={%s}"%self.protocol_option()
+ ])
+ receiver = self.popen(
+ ["qpid-receive",
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
+ "--messages=990",
+ "--timeout=10",
+ "--connection-options={%s}"%self.protocol_option()
+ ])
+ self.assertEqual(sender.wait(), 0)
+ self.assertEqual(receiver.wait(), 0)
+ expect = [long(i) for i in range(991, 1001)]
+ sn = lambda m: m.properties["sn"]
+ brokers[1].assert_browse_backup("q", expect, transform=sn)
+ brokers[2].assert_browse_backup("q", expect, transform=sn)
+ finally: l.restore()
+
+ def test_failover_python(self):
+ """Verify that backups rejects connections and that fail-over works in python client"""
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ primary = HaBroker(self, name="primary")
+ primary.promote()
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ # Check that backup rejects normal connections
+ try:
+ backup.connect().session()
+ self.fail("Expected connection to backup to fail")
+ except qm.ConnectionError: pass
+ # Check that admin connections are allowed to backup.
+ backup.connect_admin().close()
+
+ # Test discovery: should connect to primary after reject by backup
+ c = backup.connect(reconnect_urls=[primary.host_port(), backup.host_port()],
+ reconnect=True)
+ s = c.session()
+ sender = s.sender("q;{create:always}")
+ sender.send("foo", sync=True)
+ s.sync()
+ primary.kill()
+ assert retry(lambda: not is_running(primary.pid))
+ backup.promote()
+ sender.send("bar")
+ self.assert_browse_retry(s, "q", ["foo", "bar"])
+ c.close()
+ finally: l.restore()
+
+
+ def test_heartbeat_python(self):
+ """Verify that a python client with a heartbeat specified disconnects
+ from a stalled broker and does not hang indefinitely."""
+
+ broker = Broker(self)
+ broker_addr = broker.host_port()
+
+ # Case 1: Connect before stalling the broker, use the connection after stalling.
+ c = qm.Connection(broker_addr, heartbeat=1)
+ c.open()
+ os.kill(broker.pid, signal.SIGSTOP) # Stall the broker
+
+ def make_sender(): c.session().sender("foo")
+ self.assertRaises(qm.ConnectionError, make_sender)
+
+ # Case 2: Connect to a stalled broker
+ c = qm.Connection(broker_addr, heartbeat=1)
+ self.assertRaises(qm.ConnectionError, c.open)
+
+ # Case 3: Re-connect to a stalled broker.
+ broker2 = Broker(self)
+ c = qm.Connection(broker2.host_port(), heartbeat=1, reconnect_limit=1,
+ reconnect=True, reconnect_urls=[broker_addr],
+ reconnect_log=False) # Hide expected warnings
+ c.open()
+ broker2.kill() # Cause re-connection to broker
+ self.assertRaises(qm.ConnectionError, make_sender)
+
+ def test_failover_cpp(self):
+ """Verify that failover works in the C++ client."""
+ cluster = HaCluster(self, 2)
+ cluster[0].connect().session().sender("q;{create:always}")
+ cluster[1].wait_backup("q")
+ # FIXME aconway 2014-02-21: using 0-10, there is a failover problem with 1.0
+ sender = NumberedSender(cluster[0], url=cluster.url, queue="q",
+ connection_options="reconnect:true,protocol:'amqp0-10'")
+ receiver = NumberedReceiver(cluster[0], url=cluster.url, queue="q",
+ connection_options="reconnect:true,protocol:'amqp0-10'")
+ receiver.start()
+ sender.start()
+ assert retry(lambda: receiver.received > 10) # Wait for some messages to get thru
+ cluster.kill(0)
+ n = receiver.received
+ assert retry(lambda: receiver.received > n + 10) # Verify we are still going
+ sender.stop()
+ receiver.stop()
+
+ def test_backup_failover(self):
+ """Verify that a backup broker fails over and recovers queue state"""
+ brokers = HaCluster(self, 3)
+ brokers[0].connect().session().sender("q;{create:always}").send("a")
+ brokers.kill(0)
+ brokers[1].connect().session().sender("q").send("b")
+ brokers[2].assert_browse_backup("q", ["a","b"])
+ s = brokers[1].connect().session()
+ self.assertEqual("a", s.receiver("q").fetch().content)
+ s.acknowledge()
+ brokers[2].assert_browse_backup("q", ["b"])
+
+ def test_empty_backup_failover(self):
+ """Verify that a new primary becomes active with no queues.
+ Regression test for QPID-5430"""
+ brokers = HaCluster(self, 3)
+ brokers.kill(0)
+ brokers[1].wait_status("active")
+
+ def test_qpid_config_replication(self):
+ """Set up replication via qpid-config"""
+ brokers = HaCluster(self,2)
+ brokers[0].config_declare("q","all")
+ brokers[0].connect().session().sender("q").send("foo")
+ brokers[1].assert_browse_backup("q", ["foo"])
+
+ def test_standalone_queue_replica(self):
+ """Test replication of individual queues outside of cluster mode"""
+ primary = HaBroker(self, name="primary", ha_cluster=False,
+ args=["--ha-queue-replication=yes"]);
+ pc = primary.connect()
+ ps = pc.session().sender("q;{create:always}")
+ pr = pc.session().receiver("q;{create:always}")
+ backup = HaBroker(self, name="backup", ha_cluster=False,
+ args=["--ha-queue-replication=yes"])
+ bs = backup.connect().session()
+ br = bs.receiver("q;{create:always}")
+
+ def srange(*args): return [str(i) for i in xrange(*args)]
+
+ for m in srange(3): ps.send(m)
+ # Set up replication with qpid-ha
+ backup.replicate(primary.host_port(), "q")
+ backup.assert_browse_backup("q", srange(3))
+ for m in srange(3,6): ps.send(str(m))
+ backup.assert_browse_backup("q", srange(6))
+ self.assertEqual("0", pr.fetch().content)
+ pr.session.acknowledge()
+ backup.assert_browse_backup("q", srange(1,6))
+
+ # Set up replication with qpid-config
+ ps2 = pc.session().sender("q2;{create:always}")
+ backup.config_replicate(primary.host_port(), "q2");
+ ps2.send("x")
+ backup.assert_browse_backup("q2", ["x"])
+
+
+ def test_standalone_queue_replica_failover(self):
+ """Test individual queue replication from a cluster to a standalone
+ backup broker, verify it fails over."""
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ cluster = HaCluster(self, 2)
+ primary = cluster[0]
+ pc = cluster.connect(0)
+ ps = pc.session().sender("q;{create:always}")
+ pr = pc.session().receiver("q;{create:always}")
+ backup = HaBroker(self, name="backup", ha_cluster=False,
+ args=["--ha-queue-replication=yes"])
+ br = backup.connect().session().receiver("q;{create:always}")
+ backup.replicate(cluster.url, "q")
+ ps.send("a")
+ ps.sync()
+ backup.assert_browse_backup("q", ["a"])
+ cluster.bounce(0)
+ backup.assert_browse_backup("q", ["a"])
+ ps.send("b")
+ backup.assert_browse_backup("q", ["a", "b"])
+ cluster[0].wait_status("ready")
+ cluster.bounce(1)
+ # FIXME aconway 2014-02-20: pr does not fail over with 1.0/swig
+ if qm == qpid_messaging:
+ print "WARNING: Skipping SWIG client failover bug"
+ return
+ self.assertEqual("a", pr.fetch().content)
+ pr.session.acknowledge()
+ backup.assert_browse_backup("q", ["b"])
+ pc.close()
+ br.close()
+ finally: l.restore()
+
+ def test_lvq(self):
+ """Verify that we replicate to an LVQ correctly"""
+ cluster = HaCluster(self, 2)
+ s = cluster[0].connect().session().sender("lvq; {create:always, node:{x-declare:{arguments:{'qpid.last_value_queue_key':lvq-key}}}}")
+
+ def send(key,value,expect):
+ s.send(qm.Message(content=value,properties={"lvq-key":key}))
+ cluster[1].assert_browse_backup("lvq", expect)
+
+ send("a", "a-1", ["a-1"])
+ send("b", "b-1", ["a-1", "b-1"])
+ send("a", "a-2", ["b-1", "a-2"])
+ send("a", "a-3", ["b-1", "a-3"])
+ send("c", "c-1", ["b-1", "a-3", "c-1"])
+ send("c", "c-2", ["b-1", "a-3", "c-2"])
+ send("b", "b-2", ["a-3", "c-2", "b-2"])
+ send("c", "c-3", ["a-3", "b-2", "c-3"])
+ send("d", "d-1", ["a-3", "b-2", "c-3", "d-1"])
+
+ def test_ring(self):
+ """Test replication with the ring queue policy"""
+ """Verify that we replicate to an LVQ correctly"""
+ cluster = HaCluster(self, 2)
+ s = cluster[0].connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5}}}}")
+ for i in range(10): s.send(qm.Message(str(i)))
+ cluster[1].assert_browse_backup("q", [str(i) for i in range(5,10)])
+
+ def test_reject(self):
+ """Test replication with the reject queue policy"""
+ cluster = HaCluster(self, 2)
+ primary, backup = cluster
+ s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':reject, 'qpid.max_count':5}}}}")
+ try:
+ for i in range(10): s.send(qm.Message(str(i)), sync=False)
+ except qm.LinkError: pass
+ backup.assert_browse_backup("q", [str(i) for i in range(0,5)])
+ try: s.session.connection.close()
+ except: pass # Expect exception from broken session
+
+ def test_priority(self):
+ """Verify priority queues replicate correctly"""
+ cluster = HaCluster(self, 2)
+ session = cluster[0].connect().session()
+ s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':10}}}}")
+ priorities = [8,9,5,1,2,2,3,4,9,7,8,9,9,2]
+ for p in priorities: s.send(qm.Message(priority=p))
+ # Can't use browse_backup as browser sees messages in delivery order not priority.
+ cluster[1].wait_backup("priority-queue")
+ r = cluster[1].connect_admin().session().receiver("priority-queue")
+ received = [r.fetch().priority for i in priorities]
+ self.assertEqual(sorted(priorities, reverse=True), received)
+
+ def test_priority_fairshare(self):
+ """Verify priority queues replicate correctly"""
+ cluster = HaCluster(self, 2)
+ primary, backup = cluster
+ session = primary.connect().session()
+ levels = 8
+ priorities = [4,5,3,7,8,8,2,8,2,8,8,16,6,6,6,6,6,6,8,3,5,8,3,5,5,3,3,8,8,3,7,3,7,7,7,8,8,8,2,3]
+ limits={7:0,6:4,5:3,4:2,3:2,2:2,1:2}
+ limit_policy = ",".join(["'qpid.fairshare':5"] + ["'qpid.fairshare-%s':%s"%(i[0],i[1]) for i in limits.iteritems()])
+ s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':%s, %s}}}}"%(levels,limit_policy))
+ messages = [qm.Message(content=str(uuid4()), priority = p) for p in priorities]
+ for m in messages: s.send(m)
+ backup.wait_backup(s.target)
+ r = backup.connect_admin().session().receiver("priority-queue")
+ received = [r.fetch().content for i in priorities]
+ sort = sorted(messages, key=lambda m: priority_level(m.priority, levels), reverse=True)
+ fair = [m.content for m in fairshare(sort, lambda l: limits.get(l,0), levels)]
+ self.assertEqual(received, fair)
+
+ def test_priority_ring(self):
+ cluster = HaCluster(self, 2)
+ primary, backup = cluster
+ s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5, 'qpid.priorities':10}}}}")
+ priorities = [8,9,5,1,2,2,3,4,9,7,8,9,9,2]
+ for p in priorities: s.send(qm.Message(priority=p))
+ expect = sorted(priorities,reverse=True)[0:5]
+ primary.assert_browse("q", expect, transform=lambda m: m.priority)
+ backup.assert_browse_backup("q", expect, transform=lambda m: m.priority)
+
+ def test_backup_acquired(self):
+ """Verify that acquired messages are backed up, for all queue types."""
+ class Test:
+ def __init__(self, queue, arguments, expect):
+ self.queue = queue
+ self.address = "%s;{create:always,node:{x-declare:{arguments:{%s}}}}"%(
+ self.queue, ",".join(arguments))
+ self.expect = [str(i) for i in expect]
+
+ def send(self, connection):
+ """Send messages, then acquire one but don't acknowledge"""
+ s = connection.session()
+ for m in range(10): s.sender(self.address).send(str(m))
+ s.receiver(self.address).fetch()
+
+ def verify(self, brokertest, backup):
+ backup.assert_browse_backup(self.queue, self.expect, msg=self.queue)
+
+ tests = [
+ Test("plain",[],range(10)),
+ Test("ring", ["'qpid.policy_type':ring", "'qpid.max_count':5"], range(5,10)),
+ Test("priority",["'qpid.priorities':10"], range(10)),
+ Test("fairshare", ["'qpid.priorities':10,'qpid.fairshare':5"], range(10)),
+ Test("lvq", ["'qpid.last_value_queue_key':lvq-key"], [9])
+ ]
+
+ cluster = HaCluster(self, 3)
+ cluster.kill(2, final=False) # restart after messages are sent to test catch-up
+
+ c = cluster[0].connect()
+ for t in tests: t.send(c) # Send messages, leave one unacknowledged.
+
+ cluster.restart(2)
+ cluster[2].wait_status("ready")
+
+ # Verify acquired message was replicated
+ for t in tests: t.verify(self, cluster[1])
+ for t in tests: t.verify(self, cluster[2])
+
+ def test_replicate_default(self):
+ """Make sure we don't replicate if ha-replicate is unspecified or none"""
+ cluster1 = HaCluster(self, 2, ha_replicate=None)
+ cluster1[1].wait_status("ready")
+ c1 = cluster1[0].connect().session().sender("q;{create:always}")
+ cluster2 = HaCluster(self, 2, ha_replicate="none")
+ cluster2[1].wait_status("ready")
+ cluster2[0].connect().session().sender("q;{create:always}")
+ time.sleep(.1) # Give replication a chance.
+ # Expect queues not to be found
+ self.assertRaises(qm.NotFound, cluster1[1].connect_admin().session().receiver, "q")
+ self.assertRaises(qm.NotFound, cluster2[1].connect_admin().session().receiver, "q")
+
+ def test_replicate_binding(self):
+ """Verify that binding replication can be disabled"""
+ cluster = HaCluster(self, 2)
+ primary, backup = cluster[0], cluster[1]
+ ps = primary.connect().session()
+ a = primary.agent
+ a.addExchange("fanout", "ex")
+ a.addQueue("q")
+ a.bind("ex", "q", options={'qpid.replicate':'none'})
+ backup.wait_backup("q")
+
+ primary.kill()
+ assert retry(lambda: not is_running(primary.pid)) # Wait for primary to die
+ backup.promote()
+ bs = backup.connect_admin().session()
+ bs.sender("ex").send(qm.Message("msg"))
+ self.assert_browse_retry(bs, "q", [])
+
+ def test_invalid_replication(self):
+ """Verify that we reject an attempt to declare a queue with invalid replication value."""
+ cluster = HaCluster(self, 1, ha_replicate="all")
+ self.assertRaises(Exception, cluster[0].connect().session().sender,
+ "q;{create:always, node:{x-declare:{arguments:{'qpid.replicate':XXinvalidXX}}}}")
+
+ def test_exclusive_queue(self):
+ """Ensure that we can back-up exclusive queues, i.e. the replicating
+ subscriptions are exempt from the exclusivity"""
+ cluster = HaCluster(self, 2)
+ def test(addr):
+ c = cluster[0].connect()
+ q = addr.split(";")[0]
+ r = c.session().receiver(addr)
+ self.assertRaises(qm.LinkError, c.session().receiver, addr)
+ s = c.session().sender(q).send(q)
+ cluster[1].assert_browse_backup(q, [q])
+ test("excl_queue;{create:always, node:{x-declare:{exclusive:True}}}")
+ if qm == qpid.messaging: # FIXME aconway 2014-02-20: swig client no exclusive subscribe
+ test("excl_sub;{create:always, link:{x-subscribe:{exclusive:True}}}");
+
+ def test_auto_delete_exclusive(self):
+ """Verify that we ignore auto-delete, exclusive, non-auto-delete-timeout queues"""
+ cluster = HaCluster(self, 2)
+ s0 = cluster[0].connect().session()
+ s0.receiver("exad;{create:always,node:{x-declare:{exclusive:True,auto-delete:True}}}")
+ s0.receiver("ex;{create:always,node:{x-declare:{exclusive:True}}}")
+ ad = s0.receiver("ad;{create:always,node:{x-declare:{auto-delete:True}}}")
+ s0.receiver("time;{create:always,node:{x-declare:{exclusive:True,auto-delete:True,arguments:{'qpid.auto_delete_timeout':1}}}}")
+ s0.receiver("q;{create:always}")
+
+ s1 = cluster[1].connect_admin().session()
+ cluster[1].wait_backup("q")
+ assert not valid_address(s1.connection, "exad")
+ assert valid_address(s1.connection, "ex")
+ assert valid_address(s1.connection, "ad")
+ assert valid_address(s1.connection, "time")
+
+ # Verify that auto-delete queues are not kept alive by
+ # replicating subscriptions
+ ad.close()
+ s0.sync()
+ assert not valid_address(s0.connection, "ad")
+
+ def test_broker_info(self):
+ """Check that broker information is correctly published via management"""
+ cluster = HaCluster(self, 3)
+
+ def ha_broker(broker):
+ ha_broker = broker.agent.getHaBroker();
+ ha_broker.update()
+ return ha_broker
+
+ for broker in cluster: # Make sure HA system-id matches broker's
+ self.assertEqual(ha_broker(broker).systemId, UUID(broker.agent.getBroker().systemRef))
+
+ # Check that all brokers have the same membership as the cluster
+ def check_ids(broker):
+ cluster_ids = set([ ha_broker(b).systemId for b in cluster])
+ broker_ids = set([m["system-id"] for m in ha_broker(broker).members])
+ assert retry(lambda: cluster_ids == broker_ids, 1), "%s != %s on %s"%(cluster_ids, broker_ids, broker)
+
+ for broker in cluster: check_ids(broker)
+
+ # Add a new broker, check it is updated everywhere
+ b = cluster.start()
+ for broker in cluster: check_ids(broker)
+
+ def test_auth(self):
+ """Verify that authentication does not interfere with replication."""
+ # TODO aconway 2012-07-09: generate test sasl config portably for cmake
+ sasl_config=os.path.join(self.rootdir, "sasl_config")
+ if not os.path.exists(sasl_config):
+ print "WARNING: Skipping test, SASL test configuration %s not found."%sasl_config
+ return
+ acl=os.path.join(os.getcwd(), "policy.acl")
+ aclf=file(acl,"w")
+ # Minimum set of privileges required for the HA user.
+ aclf.write("""
+# HA user
+acl allow zag@QPID access queue
+acl allow zag@QPID create queue
+acl allow zag@QPID consume queue
+acl allow zag@QPID delete queue
+acl allow zag@QPID access exchange
+acl allow zag@QPID create exchange
+acl allow zag@QPID bind exchange
+acl allow zag@QPID publish exchange
+acl allow zag@QPID delete exchange
+acl allow zag@QPID access method
+acl allow zag@QPID create link
+acl allow zag@QPID access query
+# Normal user
+acl allow zig@QPID all all
+acl deny all all
+ """)
+ aclf.close()
+ cluster = HaCluster(
+ self, 2,
+ args=["--auth", "yes", "--sasl-config", sasl_config,
+ "--acl-file", acl,
+ "--ha-username=zag", "--ha-password=zag", "--ha-mechanism=PLAIN"
+ ],
+ client_credentials=Credentials("zag", "zag", "PLAIN"))
+ c = cluster[0].connect(username="zig", password="zig")
+ s0 = c.session();
+ a = cluster[0].agent
+ a.addQueue("q")
+ a.addExchange("fanout", "ex")
+ a.bind("ex", "q", "")
+ s0.sender("ex").send("foo");
+
+ # Transactions should be done over the tx_protocol
+ c = cluster[0].connect(protocol=self.tx_protocol, username="zig", password="zig")
+ s1 = c.session(transactional=True)
+ s1.sender("ex").send("foo-tx");
+ cluster[1].assert_browse_backup("q", ["foo"])
+ s1.commit()
+ cluster[1].assert_browse_backup("q", ["foo", "foo-tx"])
+
+ def test_alternate_exchange(self):
+ """Verify that alternate-exchange on exchanges and queues is propagated
+ to new members of a cluster. """
+ cluster = HaCluster(self, 2)
+ s = cluster[0].connect().session()
+ # altex exchange: acts as alternate exchange
+ a = cluster[0].agent
+ a.addExchange("fanout", "altex")
+ # altq queue bound to altex, collect re-routed messages.
+ a.addQueue("altq")
+ a.bind("altex", "altq", "")
+ # ex exchange with alternate-exchange altex and no queues bound
+ a.addExchange("direct", "ex", {"alternate-exchange":"altex"})
+ # create queue q with alternate-exchange altex
+ a.addQueue("q", {"alternate-exchange":"altex"})
+ # create a bunch of exchanges to ensure we don't clean up prematurely if the
+ # response comes in multiple fragments.
+ for i in xrange(200): s.sender("ex.%s;{create:always,node:{type:topic}}"%i)
+
+ def verify(broker):
+ c = broker.connect()
+ s = c.session()
+ # Verify unmatched message goes to ex's alternate.
+ s.sender("ex").send("foo")
+ altq = s.receiver("altq")
+ self.assertEqual("foo", altq.fetch(timeout=0).content)
+ s.acknowledge()
+ # Verify rejected message goes to q's alternate.
+ s.sender("q").send("bar")
+ msg = s.receiver("q").fetch(timeout=0)
+ self.assertEqual("bar", msg.content)
+ s.acknowledge(msg, qm.Disposition(qm.REJECTED)) # Reject the message
+ self.assertEqual("bar", altq.fetch(timeout=0).content)
+ s.acknowledge()
+ s.sync() # Make sure backups are caught-up.
+ c.close()
+
+ # Sanity check: alternate exchanges on original broker
+ verify(cluster[0])
+ a = cluster[0].agent
+ # Altex is in use as an alternate exchange, we should get an exception
+ self.assertRaises(Exception, a.delExchange, "altex")
+ # Check backup that was connected during setup.
+ def wait(broker):
+ broker.wait_status("ready")
+ for a in ["q", "ex", "altq", "altex"]:
+ broker.wait_backup(a)
+ wait(cluster[1])
+ cluster.bounce(0)
+ verify(cluster[1])
+
+ # Check a newly started backup.
+ cluster.start()
+ wait(cluster[2])
+ cluster.bounce(1)
+ verify(cluster[2])
+
+ # Check that alt-exchange in-use count is replicated
+ a = cluster[2].agent
+ self.assertRaises(Exception, a.delExchange, "altex")
+ a.delQueue("q")
+ self.assertRaises(Exception, a.delExchange, "altex")
+ a.delExchange("ex")
+ a.delExchange("altex")
+
+ def test_priority_reroute(self):
+ """Regression test for QPID-4262, rerouting messages from a priority queue
+ to itself causes a crash"""
+ cluster = HaCluster(self, 2)
+ primary = cluster[0]
+ session = primary.connect().session()
+ a = primary.agent
+ a.addQueue("pq", {'qpid.priorities':10})
+ a.bind("amq.fanout", "pq")
+ s = session.sender("pq")
+ for m in xrange(100): s.send(qm.Message(str(m), priority=m%10))
+ pq = QmfAgent(primary.host_port()).getQueue("pq")
+ pq.reroute(request=0, useAltExchange=False, exchange="amq.fanout")
+ # Verify that consuming is in priority order
+ expect = [str(10*i+p) for p in xrange(9,-1,-1) for i in xrange(0,10) ]
+ actual = [m.content for m in primary.get_messages("pq", 100)]
+ self.assertEqual(expect, actual)
+
+ def test_delete_missing_response(self):
+ """Check that a backup correctly deletes leftover queues and exchanges that are
+ missing from the initial reponse set."""
+ # This test is a bit contrived, we set up the situation on backup brokers
+ # and then promote one.
+ cluster = HaCluster(self, 2, promote=False)
+
+ # cluster[0] Will be the primary
+ s = cluster[0].connect_admin().session()
+ s.sender("q1;{create:always}")
+ s.sender("e1;{create:always, node:{type:topic}}")
+
+ # cluster[1] will be the backup, has extra queues/exchanges
+ xdecl = "x-declare:{arguments:{'qpid.replicate':'all'}}"
+ node = "node:{%s}"%(xdecl)
+ s = cluster[1].connect_admin().session()
+ s.sender("q1;{create:always, %s}"%(node))
+ s.sender("q2;{create:always, %s}"%(node))
+ s.sender("e1;{create:always, node:{type:topic, %s}}"%(xdecl))
+ s.sender("e2;{create:always, node:{type:topic, %s}}"%(xdecl))
+ for a in ["q1", "q2", "e1", "e2"]: cluster[1].wait_backup(a)
+
+ cluster[0].promote()
+ # Verify the backup deletes the surplus queue and exchange
+ cluster[1].wait_status("ready")
+ s = cluster[1].connect_admin().session()
+ self.assertRaises(qm.NotFound, s.receiver, ("q2"));
+ self.assertRaises(qm.NotFound, s.receiver, ("e2"));
+
+
+ def test_delete_qpid_4285(self):
+ """Regression test for QPID-4285: on deleting a queue it gets stuck in a
+ partially deleted state and causes replication errors."""
+ cluster = HaCluster(self,2)
+ s = cluster[0].connect().session()
+ s.receiver("q;{create:always}")
+ cluster[1].wait_backup("q")
+ cluster.kill(0) # Make the backup take over.
+ s = cluster[1].connect().session()
+ cluster[1].agent.delQueue("q") # Delete q on new primary
+ self.assertRaises(qm.NotFound, s.receiver, "q")
+ assert not cluster[1].agent.getQueue("q") # Should not be in QMF
+
+ def test_auto_delete_failover(self):
+ """Test auto-delete queues. Verify that:
+ - queues auto-deleted on the primary are deleted on the backup.
+ - auto-delete queues with/without timeout are deleted after a failover correctly
+ - auto-delete queues never used (subscribe to) to are not deleted
+ - messages are correctly routed to the alternate exchange.
+ """
+ cluster = HaCluster(self, 3)
+ s = cluster[0].connect().session()
+ a = cluster[0].agent
+
+ def setup(q, timeout=None):
+ # Create alternate exchange, auto-delete queue and queue bound to alt. ex.
+ a.addExchange("fanout", q+"-altex")
+ args = {"auto-delete":True, "alternate-exchange":q+"-altex"}
+ if timeout is not None: args['qpid.auto_delete_timeout'] = timeout
+ a.addQueue(q, args)
+ a.addQueue(q+"-altq")
+ a.bind("%s-altex"%q, "%s-altq"%q)
+
+ for args in [["q1"],["q2",0],["q3",1],["q4"],["q5"]]: setup(*args)
+ receivers = []
+ for i in xrange(1,5): # Don't use q5
+ q = "q%s"%i
+ receivers.append(s.receiver(q)) # Subscribe
+ qs = s.sender(q); qs.send(q); qs.close() # Send q name as message
+
+ receivers[3].close() # Trigger auto-delete for q4
+ for b in cluster[1:3]: b.wait_no_queue("q4") # Verify deleted on backups
+
+ cluster[0].kill(final=False) # Kill primary
+ cluster[2].promote()
+ cluster.restart(0)
+ cluster[2].wait_queue("q3") # Not yet auto-deleted, 1 sec timeout.
+ for b in cluster:
+ for q in ["q%s"%i for i in xrange(1,5)]:
+ b.wait_no_queue(q,timeout=2, msg=str(b)) # auto-deleted
+ b.assert_browse_backup("%s-altq"%q, [q]) # Routed to alternate
+ cluster[2].wait_queue("q5") # Not auto-deleted, never subscribed
+ cluster[2].connect().session().receiver("q5").close()
+ cluster[2].wait_no_queue("q5")
+
+ def test_auto_delete_close(self):
+ """Verify auto-delete queues are deleted on backup if auto-deleted
+ on primary"""
+ cluster=HaCluster(self, 2)
+
+ # Create altex to use as alternate exchange, with altq bound to it
+ a = cluster[0].agent
+ a.addExchange("fanout", "altex")
+ a.addQueue("altq", {"auto-delete":True})
+ a.bind("altex", "altq")
+
+ p = cluster[0].connect().session()
+ r = p.receiver("adq1;{create:always,node:{x-declare:{auto-delete:True,alternate-exchange:'altex'}}}")
+ s = p.sender("adq1")
+ for m in ["aa","bb","cc"]: s.send(m)
+ s.close()
+ cluster[1].wait_queue("adq1")
+ r.close() # trigger auto-delete of adq1
+ cluster[1].wait_no_queue("adq1")
+ cluster[1].assert_browse_backup("altq", ["aa","bb","cc"])
+
+ def test_expired(self):
+ """Regression test for QPID-4379: HA does not properly handle expired messages"""
+ # Race between messages expiring and HA replicating consumer.
+ cluster = HaCluster(self, 2)
+ s = cluster[0].connect().session().sender("q;{create:always}", capacity=2)
+ def send_ttl_messages():
+ for i in xrange(100): s.send(qm.Message(str(i), ttl=0.001))
+ send_ttl_messages()
+ cluster.start()
+ send_ttl_messages()
+
+ def test_missed_recreate(self):
+ """If a queue or exchange is destroyed and one with the same name re-created
+ while a backup is disconnected, the backup should also delete/recreate
+ the object when it re-connects"""
+ cluster = HaCluster(self, 3)
+ sn = cluster[0].connect().session()
+ # Create a queue with messages
+ s = sn.sender("qq;{create:always}")
+ msgs = [str(i) for i in xrange(3)]
+ for m in msgs: s.send(m)
+ cluster[1].assert_browse_backup("qq", msgs)
+ cluster[2].assert_browse_backup("qq", msgs)
+ # Set up an exchange with a binding.
+ a = cluster[0].agent
+ a.addExchange("fanout", "xx")
+ a.addQueue("xxq")
+ a.bind("xx", "xxq", "xxq")
+ cluster[1].wait_address("xx")
+ self.assertEqual(cluster[1].agent.getExchange("xx").values["bindingCount"], 1)
+ cluster[2].wait_address("xx")
+ self.assertEqual(cluster[2].agent.getExchange("xx").values["bindingCount"], 1)
+
+ # Simulate the race by re-creating the objects before promoting the new primary
+ cluster.kill(0, promote_next=False)
+ xdecl = "x-declare:{arguments:{'qpid.replicate':'all'}}"
+ node = "node:{%s}"%(xdecl)
+ sn = cluster[1].connect_admin().session()
+ a = cluster[1].agent
+ a.delQueue("qq", if_empty=False)
+ s = sn.sender("qq;{create:always, %s}"%(node))
+ s.send("foo")
+ a.delExchange("xx")
+ sn.sender("xx;{create:always,node:{type:topic,%s}}"%(xdecl))
+ cluster[1].promote()
+ cluster[1].wait_status("active")
+ # Verify we are not still using the old objects on cluster[2]
+ cluster[2].assert_browse_backup("qq", ["foo"])
+ cluster[2].wait_address("xx")
+ self.assertEqual(cluster[2].agent.getExchange("xx").values["bindingCount"], 0)
+
+ def test_resource_limit_bug(self):
+ """QPID-5666 Regression test: Incorrect resource limit exception for queue creation."""
+ cluster = HaCluster(self, 3)
+ qs = ["q%s"%i for i in xrange(10)]
+ a = cluster[0].agent
+ a.addQueue("q")
+ cluster[1].wait_backup("q")
+ cluster.kill(0)
+ cluster[1].promote()
+ cluster[1].wait_status("active")
+ a = cluster[1].agent
+ a.delQueue("q")
+ a.addQueue("q")
+
+def fairshare(msgs, limit, levels):
+ """
+ Generator to return prioritised messages in expected order for a given fairshare limit
+ """
+ count = 0
+ last_priority = None
+ postponed = []
+ while msgs or postponed:
+ if not msgs:
+ msgs = postponed
+ count = 0
+ last_priority = None
+ postponed = [ ]
+ msg = msgs.pop(0)
+ if last_priority and priority_level(msg.priority, levels) == last_priority:
+ count += 1
+ else:
+ last_priority = priority_level(msg.priority, levels)
+ count = 1
+ l = limit(last_priority)
+ if (l and count > l):
+ postponed.append(msg)
+ else:
+ yield msg
+ return
+
+def priority_level(value, levels):
+ """
+ Method to determine which of a distinct number of priority levels
+ a given value falls into.
+ """
+ offset = 5-math.ceil(levels/2.0)
+ return min(max(value - offset, 0), levels-1)
+
+class LongTests(HaBrokerTest):
+ """Tests that can run for a long time if -DDURATION=<minutes> is set"""
+
+ def duration(self):
+ d = self.config.defines.get("DURATION")
+ if d: return float(d)*60
+ else: return 3 # Default is to be quick
+
+ def test_failover_send_receive(self):
+ """Test failover with continuous send-receive"""
+ brokers = HaCluster(self, 3)
+
+ # Start sender and receiver threads
+ n = 10
+ senders = [
+ NumberedSender(
+ brokers[0], url=brokers.url,max_depth=50,
+ queue="test%s"%(i), args=["--capacity=10"]) for i in xrange(n)]
+
+ receivers = [
+ NumberedReceiver(
+ brokers[0], url=brokers.url, sender=senders[i],
+ queue="test%s"%(i), args=["--capacity=10"]) for i in xrange(n)]
+
+ for r in receivers: r.start()
+ for s in senders: s.start()
+
+ def wait_passed(r, n):
+ """Wait for receiver r to pass n"""
+ def check():
+ r.check() # Verify no exceptions
+ return r.received > n + 100
+ assert retry(check), "Stalled %s waiting for %s, sent %s"%(
+ r.queue, n, [s for s in senders if s.queue==r.queue][0].sent)
+
+ for r in receivers: wait_passed(r, 0)
+
+ # Kill and restart brokers in a cycle:
+ endtime = time.time() + self.duration()
+ i = 0
+ primary = 0
+ try:
+ try:
+ while time.time() < endtime or i < 3: # At least 3 iterations
+ # Precondition: All 3 brokers running,
+ # primary = index of promoted primary
+ # one or two backups are running,
+ for s in senders: s.sender.assert_running()
+ for r in receivers: r.receiver.assert_running()
+ checkpoint = [ r.received+10 for r in receivers ]
+ victim = random.choice([0,1,2,primary]) # Give the primary a better chance.
+ if victim == primary:
+ # Don't kill primary till it is active and the next
+ # backup is ready, otherwise we can lose messages.
+ brokers[victim].wait_status("active")
+ next = (victim+1)%3
+ brokers[next].wait_status("ready")
+ brokers.bounce(victim) # Next one is promoted
+ primary = next
+ else:
+ brokers.bounce(victim, promote_next=False)
+
+ # Make sure we are not stalled
+ map(wait_passed, receivers, checkpoint)
+ # Run another checkpoint to ensure things work in this configuration
+ checkpoint = [ r.received+10 for r in receivers ]
+ map(wait_passed, receivers, checkpoint)
+ i += 1
+ except:
+ traceback.print_exc()
+ raise
+ finally:
+ for s in senders: s.stop()
+ for r in receivers: r.stop()
+ dead = filter(lambda b: not b.is_running(), brokers)
+ if dead: raise Exception("Brokers not running: %s"%dead)
+
+ def test_tx_send_receive(self):
+ brokers = HaCluster(self, 3)
+ sender = self.popen(
+ ["qpid-send",
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
+ "--messages=1000",
+ "--tx=10",
+ "--connection-options={protocol:%s}" % self.tx_protocol
+ ])
+ receiver = self.popen(
+ ["qpid-receive",
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
+ "--messages=990",
+ "--timeout=10",
+ "--tx=10",
+ "--connection-options={protocol:%s}" % self.tx_protocol
+ ])
+ self.assertEqual(sender.wait(), 0)
+ self.assertEqual(receiver.wait(), 0)
+ expect = [long(i) for i in range(991, 1001)]
+ sn = lambda m: m.properties["sn"]
+ brokers[0].assert_browse("q", expect, transform=sn)
+ brokers[1].assert_browse_backup("q", expect, transform=sn)
+ brokers[2].assert_browse_backup("q", expect, transform=sn)
+
+
+ def test_qmf_order(self):
+ """QPID 4402: HA QMF events can be out of order.
+ This test mimics the test described in the JIRA. Two threads repeatedly
+ declare the same auto-delete queue and close their connection.
+ """
+ broker = Broker(self)
+ class Receiver(Thread):
+ def __init__(self, qname):
+ Thread.__init__(self)
+ self.qname = qname
+ self.stopped = False
+
+ def run(self):
+ while not self.stopped:
+ self.connection = broker.connect()
+ try:
+ self.connection.session().receiver(
+ self.qname+";{create:always,node:{x-declare:{auto-delete:True}}}")
+ except qm.NotFound: pass # Can occur occasionally, not an error.
+ try: self.connection.close()
+ except: pass
+
+ class QmfObject(object):
+ """Track existance of an object and validate QMF events"""
+ def __init__(self, type_name, name_field, name):
+ self.type_name, self.name_field, self.name = type_name, name_field, name
+ self.exists = False
+
+ def qmf_event(self, event):
+ content = event.content[0]
+ event_type = content['_schema_id']['_class_name']
+ values = content['_values']
+ if event_type == self.type_name+"Declare" and values[self.name_field] == self.name:
+ disp = values['disp']
+ log.debug("Event %s: disp=%s exists=%s"%(
+ event_type, values['disp'], self.exists))
+ if self.exists: assert values['disp'] == 'existing'
+ else: assert values['disp'] == 'created'
+ self.exists = True
+ elif event_type == self.type_name+"Delete" and values[self.name_field] == self.name:
+ log.debug("Event %s: exists=%s"%(event_type, self.exists))
+ assert self.exists
+ self.exists = False
+
+ # Verify order of QMF events.
+ helper = EventHelper()
+ r = broker.connect().session().receiver(helper.eventAddress())
+ threads = [Receiver("qq"), Receiver("qq")]
+ for t in threads: t.start()
+ queue = QmfObject("queue", "qName", "qq")
+ finish = time.time() + self.duration()
+ try:
+ while time.time() < finish:
+ queue.qmf_event(r.fetch())
+ finally:
+ for t in threads: t.stopped = True; t.join()
+
+ def test_max_queues(self):
+ """Verify that we behave properly if we try to exceed the max number
+ of replicated queues - currently limited by the max number of channels
+ in the replication link"""
+ # This test is very slow (3 mins), skip it unless duration() > 1 minute.
+ if self.duration() < 60: return
+ # This test is written in C++ for speed, it takes a long time
+ # to create 64k queues in python. See ha_test_max_queues.cpp.
+ cluster = HaCluster(self, 2)
+ test = self.popen(["ha_test_max_queues", cluster[0].host_port()])
+ self.assertEqual(test.wait(), 0)
+
+class RecoveryTests(HaBrokerTest):
+ """Tests for recovery after a failure."""
+
+ def test_queue_hold(self):
+ """Verify that the broker holds queues without sufficient backup,
+ i.e. does not complete messages sent to those queues."""
+
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ # We don't want backups to time out for this test, set long timeout.
+ cluster = HaCluster(self, 4, args=["--ha-backup-timeout=120"]);
+ # Wait for the primary to be ready
+ cluster[0].wait_status("active")
+ for b in cluster[1:4]: b.wait_status("ready")
+ # Create a queue before the failure.
+ # FIXME aconway 2014-02-20: SWIG client doesn't respect sync=False
+ s1 = cluster.connect(0, native=True).session().sender("q1;{create:always}")
+ for b in cluster: b.wait_backup("q1")
+ for i in xrange(10): s1.send(str(i), timeout=0.1)
+
+ # Kill primary and 2 backups
+ cluster[3].wait_status("ready")
+ for i in [0,1,2]: cluster.kill(i, promote_next=False, final=False)
+ cluster[3].promote() # New primary, backups will be 1 and 2
+ cluster[3].wait_status("recovering")
+
+ def assertSyncTimeout(s):
+ self.assertRaises(qpid.messaging.Timeout, s.sync, timeout=.01)
+
+ # Create a queue after the failure
+ # FIXME aconway 2014-02-20: SWIG client doesn't respect sync=False
+ s2 = cluster.connect(3, native=True).session().sender("q2;{create:always}")
+
+ # Verify that messages sent are not completed
+ for i in xrange(10,20):
+ s1.send(str(i), sync=False, timeout=0.1);
+ s2.send(str(i), sync=False, timeout=0.1)
+
+ assertSyncTimeout(s1)
+ self.assertEqual(s1.unsettled(), 10)
+ assertSyncTimeout(s2)
+ self.assertEqual(s2.unsettled(), 10)
+
+ # Verify we can receive even if sending is on hold:
+ cluster[3].assert_browse("q1", [str(i) for i in range(10)])
+
+ # Restart backups, verify queues are released only when both backups are up
+ cluster.restart(1)
+ assertSyncTimeout(s1)
+ self.assertEqual(s1.unsettled(), 10)
+ assertSyncTimeout(s2)
+ self.assertEqual(s2.unsettled(), 10)
+ cluster.restart(2)
+ cluster.restart(0)
+
+ # Verify everything is up to date and active
+ def settled(sender): sender.sync(timeout=1); return sender.unsettled() == 0;
+ assert retry(lambda: settled(s1)), "Unsetttled=%s"%(s1.unsettled())
+ assert retry(lambda: settled(s2)), "Unsetttled=%s"%(s2.unsettled())
+ cluster[1].assert_browse_backup("q1", [str(i) for i in range(10)+range(10,20)])
+ cluster[1].assert_browse_backup("q2", [str(i) for i in range(10,20)])
+ cluster[3].wait_status("active"),
+ s1.session.connection.close()
+ s2.session.connection.close()
+ finally: l.restore()
+
+ def test_expected_backup_timeout(self):
+ """Verify that we time-out expected backups and release held queues
+ after a configured interval. Verify backup is demoted to catch-up,
+ but can still rejoin.
+ """
+ cluster = HaCluster(self, 3, args=["--ha-backup-timeout=0.5"]);
+ for i in [0,1]: cluster.kill(i, False)
+ cluster[2].promote() # New primary, expected backup will be 1
+ # Should not go active till the expected backup connects or times out.
+ cluster[2].wait_status("recovering")
+ # Messages should be held till expected backup times out
+ ss = cluster[2].connect().session()
+ s = ss.sender("q;{create:always}")
+ s.send("foo", sync=False)
+ self.assertEqual(s.unsettled(), 1) # Verify message not settled immediately.
+ s.sync(timeout=1) # And settled after timeout.
+ cluster[2].wait_status("active")
+
+ def test_join_ready_cluster(self):
+ """If we join a cluster where the primary is dead, the new primary is
+ not yet promoted and there are ready backups then we should refuse
+ promotion so that one of the ready backups can be chosen."""
+ cluster = HaCluster(self, 2)
+ cluster[0].wait_status("active")
+ cluster[1].wait_status("ready")
+ cluster.bounce(0, promote_next=False)
+ self.assertRaises(Exception, cluster[0].promote)
+ os.kill(cluster[1].pid, signal.SIGSTOP) # Test for timeout if unresponsive.
+ cluster.bounce(0, promote_next=False)
+ cluster[0].promote()
+
+ def test_stalled_backup(self):
+ """Make sure that a stalled backup broker does not stall the primary"""
+ cluster = HaCluster(self, 3, args=["--link-heartbeat-interval=1"])
+ os.kill(cluster[1].pid, signal.SIGSTOP)
+ s = cluster[0].connect().session()
+ s.sender("q;{create:always}").send("x")
+ self.assertEqual("x", s.receiver("q").fetch(0).content)
+
+class StoreTests(HaBrokerTest):
+ """Test for HA with persistence."""
+
+ def check_skip(self):
+ if not BrokerTest.store_lib:
+ print "WARNING: skipping HA+store tests, no store lib found."
+ return not BrokerTest.store_lib
+
+ def test_store_recovery(self):
+ """Verify basic store and recover functionality"""
+ if self.check_skip(): return
+ cluster = HaCluster(self, 1)
+ sn = cluster[0].connect().session()
+ # Create queue qq, exchange exx and binding between them
+ s = sn.sender("qq;{create:always,node:{durable:true}}")
+ sk = sn.sender("exx/k;{create:always,node:{type:topic, durable:true, x-declare:{type:'direct'}}}")
+ cluster[0].agent.bind("exx", "qq", "k")
+ for m in ["foo", "bar", "baz"]: s.send(qm.Message(m, durable=True))
+ r = cluster[0].connect().session().receiver("qq")
+ self.assertEqual(r.fetch().content, "foo")
+ r.session.acknowledge()
+ # Sending this message is a hack to flush the dequeue operation on qq.
+ s.send(qm.Message("flush", durable=True))
+
+ def verify(broker, x_count):
+ sn = broker.connect().session()
+ assert_browse(sn, "qq", [ "bar", "baz", "flush" ]+ (x_count)*["x"])
+ sn.sender("exx/k").send(qm.Message("x", durable=True))
+ assert_browse(sn, "qq", [ "bar", "baz", "flush" ]+ (x_count+1)*["x"])
+
+ verify(cluster[0], 0) # Sanity check
+ cluster.bounce(0)
+ cluster[0].wait_status("active")
+ verify(cluster[0], 1) # Loaded from store
+ cluster.start()
+ cluster[1].wait_status("ready")
+ cluster.kill(0)
+ cluster[1].wait_status("active")
+ verify(cluster[1], 2)
+ cluster.bounce(1, promote_next=False)
+ cluster[1].promote()
+ cluster[1].wait_status("active")
+ verify(cluster[1], 3)
+
+ def test_catchup_store(self):
+ """Verify that a backup erases queue data from store recovery before
+ doing catch-up from the primary."""
+ if self.check_skip(): return
+ cluster = HaCluster(self, 2)
+ sn = cluster[0].connect(heartbeat=HaBroker.heartbeat).session()
+ s1 = sn.sender("q1;{create:always,node:{durable:true}}")
+ for m in ["foo","bar"]: s1.send(qm.Message(m, durable=True))
+ s2 = sn.sender("q2;{create:always,node:{durable:true}}")
+ sk2 = sn.sender("ex/k2;{create:always,node:{type:topic, durable:true, x-declare:{type:'direct'}}}")
+ cluster[0].agent.bind("ex", "q2", "k2")
+ sk2.send(qm.Message("hello", durable=True))
+ # Wait for backup to catch up.
+ cluster[1].assert_browse_backup("q1", ["foo","bar"])
+ cluster[1].assert_browse_backup("q2", ["hello"])
+ # Make changes that the backup doesn't see
+ cluster.kill(1, promote_next=False, final=False)
+ r1 = cluster[0].connect(heartbeat=HaBroker.heartbeat).session().receiver("q1")
+ for m in ["foo", "bar"]: self.assertEqual(r1.fetch().content, m)
+ r1.session.acknowledge()
+ for m in ["x","y","z"]: s1.send(qm.Message(m, durable=True))
+ cluster[0].agent.unbind("ex", "q2", "k2")
+ cluster[0].agent.bind("ex", "q1", "k1")
+ # Restart both brokers from store to get inconsistent sequence numbering.
+ cluster.bounce(0, promote_next=False)
+ cluster[0].promote()
+ cluster[0].wait_status("active")
+ cluster.restart(1)
+ cluster[1].wait_status("ready")
+
+ # Verify state
+ cluster[0].assert_browse("q1", ["x","y","z"])
+ cluster[1].assert_browse_backup("q1", ["x","y","z"])
+
+ sn = cluster[0].connect(heartbeat=HaBroker.heartbeat).session()
+ sn.sender("ex/k1").send("boo")
+ cluster[0].assert_browse_backup("q1", ["x","y","z", "boo"])
+ cluster[1].assert_browse_backup("q1", ["x","y","z", "boo"])
+ sn.sender("ex/k2").send("hoo") # q2 was unbound so this should be dropped.
+ sn.sender("q2").send("end") # mark the end of the queue for assert_browse
+ cluster[0].assert_browse("q2", ["hello", "end"])
+ cluster[1].assert_browse_backup("q2", ["hello", "end"])
+
+def open_read(name):
+ try:
+ f = open(name)
+ return f.read()
+ finally: f.close()
+
+class TransactionTests(HaBrokerTest):
+
+ def tx_simple_setup(self, cluster, broker=0):
+ """Start a transaction, remove messages from queue a, add messages to queue b"""
+ c = cluster.connect(broker, protocol=self.tx_protocol)
+ # Send messages to a, no transaction.
+ sa = c.session().sender("a;{create:always,node:{durable:true}}")
+ tx_msgs = ["x","y","z"]
+ for m in tx_msgs: sa.send(qm.Message(content=m, durable=True))
+ sa.close()
+
+ # Receive messages from a, in transaction.
+ tx = c.session(transactional=True)
+ txr = tx.receiver("a")
+ tx_msgs2 = [txr.fetch(1).content for i in xrange(3)]
+ self.assertEqual(tx_msgs, tx_msgs2)
+
+ # Send messages to b, transactional, mixed with non-transactional.
+ sb = c.session().sender("b;{create:always,node:{durable:true}}")
+ txs = tx.sender("b")
+ msgs = [str(i) for i in xrange(3)]
+ for tx_m,m in zip(tx_msgs2, msgs):
+ txs.send(tx_m);
+ sb.send(m)
+ sb.close()
+ return tx
+
+ def tx_subscriptions(self, broker):
+ """Return list of queue names for tx subscriptions"""
+ return [q for q in broker.agent.repsub_queues()
+ if q.startswith("qpid.ha-tx")]
+
+ def test_tx_simple_commit(self):
+ cluster = HaCluster(self, 2, test_store=True, wait=True)
+ tx = self.tx_simple_setup(cluster)
+ tx.sync()
+ tx_queues = cluster[0].agent.tx_queues()
+
+ # NOTE: backup does not process transactional dequeues until prepare
+ cluster[1].assert_browse_backup("a", ["x","y","z"])
+ cluster[1].assert_browse_backup("b", ['0', '1', '2'])
+
+ tx.acknowledge()
+ tx.commit()
+ tx.sync()
+ tx.close()
+
+ for b in cluster:
+ self.assert_simple_commit_outcome(b, tx_queues)
+
+ # Verify non-tx dequeue is replicated correctly
+ c = cluster.connect(0, protocol=self.tx_protocol)
+ r = c.session().receiver("b")
+ ri = receiver_iter(r, timeout=1)
+ self.assertEqual(['0', '1', '2', 'x', 'y', 'z'], [m.content for m in ri])
+ r.session.acknowledge()
+ for b in cluster: b.assert_browse_backup("b", [], msg=b)
+ c.close()
+ tx.connection.close()
+
+
+ def check_enq_deq(self, cluster, queue, expect):
+ for b in cluster:
+ q = b.agent.getQueue(queue)
+ self.assertEqual(
+ (b.name,)+expect,
+ (b.name, q.msgTotalEnqueues, q.msgTotalDequeues, q.msgTxnEnqueues, q.msgTxnDequeues))
+
+ def test_tx_enq_notx_deq(self):
+ """Verify that a non-tx dequeue of a tx enqueue is replicated correctly"""
+ cluster = HaCluster(self, 2, test_store=True)
+ c = cluster.connect(0, protocol=self.tx_protocol)
+
+ tx = c.session(transactional=True)
+ c.session().sender("qq;{create:always}").send("m1")
+ tx.sender("qq;{create:always}").send("tx")
+ tx.commit()
+ tx.close()
+ c.session().sender("qq;{create:always}").send("m2")
+ self.check_enq_deq(cluster, 'qq', (3, 0, 1, 0))
+
+ notx = c.session()
+ self.assertEqual(['m1', 'tx', 'm2'], [m.content for m in receiver_iter(notx.receiver('qq'))])
+ notx.acknowledge()
+ self.check_enq_deq(cluster, 'qq', (3, 3, 1, 0))
+ for b in cluster: b.assert_browse_backup('qq', [], msg=b)
+ for b in cluster: self.assert_tx_clean(b)
+
+ def test_tx_enq_notx_deq_qpid_send(self):
+ """Verify that a non-tx dequeue of a tx enqueue is replicated correctly"""
+ cluster = HaCluster(self, 2, test_store=True)
+
+ self.popen(
+ ['qpid-send', '-a', 'qq;{create:always}', '-b', cluster[0].host_port(), '--tx=1',
+ '--content-string=foo']
+ ).assert_exit_ok()
+ for b in cluster: b.assert_browse_backup('qq', ['foo'], msg=b)
+ self.check_enq_deq(cluster, 'qq', (1, 0, 1, 0))
+
+ self.popen(['qpid-receive', '-a', 'qq', '-b', cluster[0].host_port()]).assert_exit_ok()
+ self.check_enq_deq(cluster, 'qq', (1, 1, 1, 0))
+ for b in cluster: b.assert_browse_backup('qq', [], msg=b)
+ for b in cluster: self.assert_tx_clean(b)
+
+ def assert_tx_clean(self, b):
+ """Verify that there are no transaction artifacts
+ (exchanges, queues, subscriptions) on b."""
+ class FunctionCache: # Call a function and cache the result.
+ def __init__(self, f): self.f, self.value = f, None
+ def __call__(self): self.value = self.f(); return self.value
+
+ txq= FunctionCache(b.agent.tx_queues)
+ assert retry(lambda: not txq()), "%s: unexpected %s"%(b, txq.value)
+ txsub = FunctionCache(lambda: self.tx_subscriptions(b))
+ assert retry(lambda: not txsub()), "%s: unexpected %s"%(b, txsub.value)
+ # TODO aconway 2013-10-15: TX exchanges don't show up in management.
+
+ def assert_simple_commit_outcome(self, b, tx_queues):
+ b.assert_browse_backup("a", [], msg=b)
+ b.assert_browse_backup("b", ['0', '1', '2', 'x', 'y', 'z'], msg=b)
+ # Check for expected actions on the store
+ expect = """<enqueue a x>
+<enqueue a y>
+<enqueue a z>
+<begin tx 1>
+<dequeue a x tx=1>
+<dequeue a y tx=1>
+<dequeue a z tx=1>
+<commit tx=1>
+"""
+ self.assertEqual(expect, open_read(b.store_log), msg=b)
+ self.assert_tx_clean(b)
+
+ def test_tx_simple_rollback(self):
+ cluster = HaCluster(self, 2, test_store=True)
+ tx = self.tx_simple_setup(cluster)
+ tx.sync()
+ tx_queues = cluster[0].agent.tx_queues()
+ tx.acknowledge()
+ tx.rollback()
+ tx.close() # For clean test.
+ for b in cluster: self.assert_simple_rollback_outcome(b, tx_queues)
+ tx.connection.close()
+
+ def assert_simple_rollback_outcome(self, b, tx_queues):
+ b.assert_browse_backup("a", ["x","y","z"], msg=b)
+ b.assert_browse_backup("b", ['0', '1', '2'], msg=b)
+ # Check for expected actions on the store
+ expect = """<enqueue a x>
+<enqueue a y>
+<enqueue a z>
+"""
+ self.assertEqual(open_read(b.store_log), expect, msg=b)
+ self.assert_tx_clean(b)
+
+ def test_tx_simple_failure(self):
+ """Verify we throw TransactionAborted if there is a store error during a transaction"""
+ cluster = HaCluster(self, 3, test_store=True)
+ tx = self.tx_simple_setup(cluster)
+ tx.sync()
+ tx_queues = cluster[0].agent.tx_queues()
+ tx.acknowledge()
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ cluster.bounce(0) # Should cause roll-back
+ tx.connection.session() # Wait for reconnect
+ for b in cluster: self.assert_simple_rollback_outcome(b, tx_queues)
+ self.assertRaises(qm.TransactionAborted, tx.sync)
+ self.assertRaises(qm.TransactionAborted, tx.commit)
+ try: tx.connection.close()
+ except qm.TransactionAborted: pass # Occasionally get exception on close.
+ for b in cluster: self.assert_simple_rollback_outcome(b, tx_queues)
+ finally: l.restore()
+
+ def test_tx_simple_failover(self):
+ """Verify we throw TransactionAborted if there is a fail-over during a transaction"""
+ cluster = HaCluster(self, 3, test_store=True)
+ tx = self.tx_simple_setup(cluster)
+ tx.sync()
+ tx_queues = cluster[0].agent.tx_queues()
+ tx.acknowledge()
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ cluster.bounce(0) # Should cause roll-back
+ tx.connection.session() # Wait for reconnect
+ for b in cluster: self.assert_simple_rollback_outcome(b, tx_queues)
+ self.assertRaises(qm.TransactionAborted, tx.sync)
+ self.assertRaises(qm.TransactionAborted, tx.commit)
+ try: tx.connection.close()
+ except qm.TransactionAborted: pass # Occasionally get exception on close.
+ for b in cluster: self.assert_simple_rollback_outcome(b, tx_queues)
+ finally: l.restore()
+
+ def test_tx_unknown_failover(self):
+ """Verify we throw TransactionUnknown if there is a failure during commit"""
+ cluster = HaCluster(self, 3, test_store=True)
+ tx = self.tx_simple_setup(cluster)
+ tx.sync()
+ tx_queues = cluster[0].agent.tx_queues()
+ tx.acknowledge()
+ l = LogLevel(ERROR) # Hide expected WARNING log messages from failover.
+ try:
+ os.kill(cluster[2].pid, signal.SIGSTOP) # Delay prepare response
+ class CommitThread(Thread):
+ def run(self):
+ try: tx.commit()
+ except Exception, e:
+ self.error = e
+ t = CommitThread()
+ t.start() # Commit in progress
+ t.join(timeout=0.01)
+ self.assertTrue(t.isAlive())
+ cluster.bounce(0)
+ os.kill(cluster[2].pid, signal.SIGCONT)
+ t.join()
+ try: raise t.error
+ except qm.TransactionUnknown: pass
+ for b in cluster: self.assert_tx_clean(b)
+ try: tx.connection.close()
+ except qm.TransactionUnknown: pass # Occasionally get exception on close.
+ finally: l.restore()
+
+ def test_tx_no_backups(self):
+ """Test the special case of a TX where there are no backups"""
+
+ # Test commit
+ cluster = HaCluster(self, 1, test_store=True)
+ tx = self.tx_simple_setup(cluster)
+ tx.acknowledge()
+ tx.commit()
+ tx.sync()
+ tx_queues = cluster[0].agent.tx_queues()
+ tx.close()
+ self.assert_simple_commit_outcome(cluster[0], tx_queues)
+
+ # Test rollback
+ cluster = HaCluster(self, 1, test_store=True)
+ tx = self.tx_simple_setup(cluster)
+ tx.sync()
+ tx_queues = cluster[0].agent.tx_queues()
+ tx.acknowledge()
+ tx.rollback()
+ tx.sync()
+ tx.close()
+ self.assert_simple_rollback_outcome(cluster[0], tx_queues)
+
+ def test_tx_backup_fail(self):
+ cluster = HaCluster(self, 2, test_store=True, s_args=[[],["--test-store-name=bang"]])
+ c = cluster[0].connect(protocol=self.tx_protocol)
+ tx = c.session(transactional=True)
+ s = tx.sender("q;{create:always,node:{durable:true}}")
+ for m in ["foo","TEST_STORE_DO bang: throw","bar"]: s.send(qm.Message(m, durable=True))
+ def commit_sync(): tx.commit(); tx.sync()
+ self.assertRaises(qm.TransactionAborted, commit_sync)
+ for b in cluster: b.assert_browse_backup("q", [])
+ self.assertEqual(open_read(cluster[0].store_log), "<begin tx 1>\n<enqueue q foo tx=1>\n<enqueue q TEST_STORE_DO bang: throw tx=1>\n<enqueue q bar tx=1>\n<abort tx=1>\n")
+ self.assertEqual(open_read(cluster[1].store_log), "<begin tx 1>\n<enqueue q foo tx=1>\n<enqueue q TEST_STORE_DO bang: throw tx=1>\n<abort tx=1>\n")
+
+ def test_tx_join_leave(self):
+ """Test cluster members joining/leaving cluster.
+ Also check that tx-queues are cleaned up at end of transaction."""
+
+ cluster = HaCluster(self, 3)
+
+ # Leaving
+ tx = cluster[0].connect(protocol=self.tx_protocol).session(transactional=True)
+ s = tx.sender("q;{create:always}")
+ s.send("a", sync=True)
+ self.assertEqual([1,1,1], [len(b.agent.tx_queues()) for b in cluster])
+ cluster[1].kill(final=False)
+ s.send("b")
+ tx.commit()
+ tx.connection.close()
+ for b in [cluster[0],cluster[2]]:
+ self.assert_tx_clean(b)
+ b.assert_browse_backup("q", ["a","b"], msg=b)
+ # Joining
+ tx = cluster[0].connect(protocol=self.tx_protocol).session(transactional=True)
+ s = tx.sender("q;{create:always}")
+ s.send("foo")
+ cluster.restart(1) # Not a part of the current transaction.
+ tx.commit()
+ tx.connection.close()
+ for b in cluster: self.assert_tx_clean(b)
+ # The new member is not in the tx but receives the results normal replication.
+ for b in cluster: b.assert_browse_backup("q", ["a", "b", "foo"], msg=b)
+
+ def test_tx_block_threads(self):
+ """Verify that TXs blocked in commit don't deadlock."""
+ cluster = HaCluster(self, 2, args=["--worker-threads=2"], test_store=True)
+ n = 10 # Number of concurrent transactions
+ sessions = [cluster.connect(0, protocol=self.tx_protocol).session(transactional=True) for i in xrange(n)]
+ # Have the store delay the response for 10s
+ for s in sessions:
+ sn = s.sender("qq;{create:always,node:{durable:true}}")
+ sn.send(qm.Message("foo", durable=True))
+ self.assertEqual(n, len(cluster[1].agent.tx_queues()))
+ threads = [ Thread(target=s.commit) for s in sessions]
+ for t in threads: t.start()
+ cluster[0].ready(timeout=1) # Check for deadlock
+ for b in cluster: b.assert_browse_backup('qq', ['foo']*n)
+ for t in threads: t.join()
+ for s in sessions: s.connection.close()
+
+ def test_other_tx_tests(self):
+ try:
+ import qpid_tests.broker_0_10
+ except ImportError:
+ raise Skipped("Tests not found")
+ cluster = HaCluster(self, 3)
+ if "QPID_PORT" in os.environ: del os.environ["QPID_PORT"]
+ self.popen(["qpid-txtest2", "--broker", cluster[0].host_port()]).assert_exit_ok()
+ print
+ self.popen(["qpid-python-test",
+ "-m", "qpid_tests.broker_0_10",
+ "-m", "qpid_tests.broker_1_0",
+ "-b", "localhost:%s"%(cluster[0].port()),
+ "*.tx.*"], stdout=None, stderr=None).assert_exit_ok()
+
+if __name__ == "__main__":
+ qpid_ha_exec = os.getenv("QPID_HA_EXEC")
+ if qpid_ha_exec and os.path.isfile(qpid_ha_exec):
+ BrokerTest.amqp_tx_warning()
+ outdir = "ha_tests.tmp"
+ shutil.rmtree(outdir, True)
+ os.execvp("qpid-python-test",
+ ["qpid-python-test", "-m", "ha_tests", "-DOUTDIR=%s"%outdir]
+ + sys.argv[1:])
+ else:
+ print "Skipping ha_tests, qpid-ha not available"
+
+
diff --git a/qpid/cpp/src/tests/header_test.cpp b/qpid/cpp/src/tests/header_test.cpp
new file mode 100644
index 0000000000..c36b4f3bc3
--- /dev/null
+++ b/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.71828f;
+ Message msg("", q);
+ msg.getMessageProperties().getApplicationHeaders().setDouble("pi", pi);
+ msg.getMessageProperties().getApplicationHeaders().setFloat("e", e);
+ session.messageTransfer(arg::content=msg);
+
+ session.close();
+ connection.close();
+
+ return 0;
+ } catch(const exception& e) {
+ cout << e.what() << endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/header_test.py b/qpid/cpp/src/tests/header_test.py
new file mode 100755
index 0000000000..d5a2c16c01
--- /dev/null
+++ b/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/qpid/cpp/src/tests/headers_federation.py b/qpid/cpp/src/tests/headers_federation.py
new file mode 100644
index 0000000000..60cff1da54
--- /dev/null
+++ b/qpid/cpp/src/tests/headers_federation.py
@@ -0,0 +1,99 @@
+#!/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
+from qpid.datatypes import Message
+from qpid.queue import Empty
+from time import sleep
+
+class HeadersFederationTests(TestBase010):
+
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
+
+ def remote_port(self):
+ return int(self.defines["remote-port"])
+
+ def verify_cleanup(self):
+ attempts = 0
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+ while total > 0:
+ attempts += 1
+ if attempts >= 10:
+ self.fail("Bridges and links didn't clean up")
+ return
+ sleep(1)
+ total = len(self.qmf.getObjects(_class="bridge")) + len(self.qmf.getObjects(_class="link"))
+
+ def test_dynamic_headers_unbind(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_headers_unbind")
+
+ session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+ r_session.exchange_declare(exchange="fed.headers_unbind", type="headers")
+
+ self.startQmf()
+ qmf = self.qmf
+
+ broker = qmf.getObjects(_class="broker")[0]
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.headers_unbind", "fed.headers_unbind", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ queue = qmf.getObjects(_class="queue", name="fed1")[0]
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ session.exchange_bind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ queue.update()
+ self.assertEqual(queue.bindingCount, 2,
+ "bindings not accounted for (expected 2, got %d)" % queue.bindingCount)
+
+ session.exchange_unbind(queue="fed1", exchange="fed.headers_unbind", binding_key="key1")
+ queue.update()
+ self.assertEqual(queue.bindingCount, 1,
+ "bindings not accounted for (expected 1, got %d)" % queue.bindingCount)
+
+ result = bridge.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ self.verify_cleanup()
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ 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
diff --git a/qpid/cpp/src/tests/idle_timeout_tests.py b/qpid/cpp/src/tests/idle_timeout_tests.py
new file mode 100755
index 0000000000..22a107a110
--- /dev/null
+++ b/qpid/cpp/src/tests/idle_timeout_tests.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os
+import shutil
+import signal
+import sys
+
+from brokertest import *
+
+class AmqpIdleTimeoutTest(BrokerTest):
+ """
+ Test AMQP 1.0 idle-timeout support
+ """
+ def setUp(self):
+ BrokerTest.setUp(self)
+ if not BrokerTest.amqp_lib:
+ raise Skipped("AMQP 1.0 library not found")
+ if qm != qpid_messaging:
+ raise Skipped("AMQP 1.0 client not found")
+ self._broker = self.broker()
+
+ def test_client_timeout(self):
+ """Ensure that the client disconnects should the broker stop
+ responding.
+ """
+ conn = self._broker.connect(native=False, timeout=None,
+ protocol="amqp1.0", heartbeat=1)
+ self.assertTrue(conn.isOpen())
+ # should disconnect within 2 seconds of broker stop
+ deadline = time.time() + 8
+ os.kill(self._broker.pid, signal.SIGSTOP)
+ while time.time() < deadline:
+ if not conn.isOpen():
+ break;
+ self.assertTrue(not conn.isOpen())
+ os.kill(self._broker.pid, signal.SIGCONT)
+
+
+ def test_broker_timeout(self):
+ """By default, the broker will adopt the same timeout as the client
+ (mimics the 0-10 timeout behavior). Verify the broker disconnects
+ unresponsive clients.
+ """
+
+ count = len(self._broker.agent.getAllConnections())
+
+ # Create a new connection to the broker:
+ receiver_cmd = ["qpid-receive",
+ "--broker", self._broker.host_port(),
+ "--address=amq.fanout",
+ "--connection-options={protocol:amqp1.0, heartbeat:1}",
+ "--forever"]
+ receiver = self.popen(receiver_cmd, stdout=PIPE, stderr=PIPE,
+ expect=EXPECT_UNKNOWN)
+ start = time.time()
+ deadline = time.time() + 10
+ while time.time() < deadline:
+ if count < len(self._broker.agent.getAllConnections()):
+ break;
+ self.assertTrue(count < len(self._broker.agent.getAllConnections()))
+
+ # now 'hang' the client, the broker should disconnect
+ start = time.time()
+ os.kill(receiver.pid, signal.SIGSTOP)
+ deadline = time.time() + 10
+ while time.time() < deadline:
+ if count == len(self._broker.agent.getAllConnections()):
+ break;
+ self.assertEqual(count, len(self._broker.agent.getAllConnections()))
+ os.kill(receiver.pid, signal.SIGCONT)
+ receiver.teardown()
+
+
+if __name__ == "__main__":
+ shutil.rmtree("brokertest.tmp", True)
+ os.execvp("qpid-python-test",
+ ["qpid-python-test", "-m", "idle_timeout_tests"] + sys.argv[1:])
diff --git a/qpid/cpp/src/tests/install_env.sh.in b/qpid/cpp/src/tests/install_env.sh.in
new file mode 100644
index 0000000000..d29a23930d
--- /dev/null
+++ b/qpid/cpp/src/tests/install_env.sh.in
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+absdir() { echo `cd $1 && pwd`; }
+
+prefix=`absdir @prefix@`
+export QPID_INSTALL_PREFIX=$prefix
+export PATH=$prefix/bin:$prefix/sbin:$prefix/libexec/qpid/tests:$PATH
+export LD_LIBRARY_PATH=$prefix/lib:$LD_LIBRARY_PATH
+export PYTHONPATH=$prefix/lib/python2.6/site-packages:$PYTHONPATH
diff --git a/qpid/cpp/src/tests/interlink_tests.py b/qpid/cpp/src/tests/interlink_tests.py
new file mode 100755
index 0000000000..3eec2422f1
--- /dev/null
+++ b/qpid/cpp/src/tests/interlink_tests.py
@@ -0,0 +1,336 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest, random
+import traceback
+from qpid.messaging import Message, SessionError, NotFound, ConnectionError, ReceiverError, Connection, Timeout, Disposition, REJECTED, Empty
+from brokertest import *
+from ha_test import HaPort
+from threading import Thread, Lock, Condition
+from logging import getLogger, WARN, ERROR, DEBUG, INFO
+from qpidtoollibs import BrokerObject
+
+class Domain(BrokerObject):
+ def __init__(self, broker, values):
+ BrokerObject.__init__(self, broker, values)
+
+class Config:
+ def __init__(self, broker, address="q;{create:always}", version="amqp1.0"):
+ self.url = broker.host_port()
+ self.address = address
+ self.version = version
+
+ def __str__(self):
+ return "url: %s, address: %s, version: %s" % (self.url, self.address, self.version)
+
+class AmqpBrokerTest(BrokerTest):
+ """
+ Tests using AMQP 1.0 support
+ """
+ def setUp(self):
+ BrokerTest.setUp(self)
+ self.port_holder = HaPort(self)
+ self.broker = self.amqp_broker(port_holder=self.port_holder)
+ self.default_config = Config(self.broker)
+ self.agent = self.broker.agent
+
+ def sender(self, config, reply_to=None):
+ cmd = ["qpid-send",
+ "--broker", config.url,
+ "--address", config.address,
+ "--connection-options", "{protocol:%s}" % config.version,
+ "--content-stdin", "--send-eos=1"
+ ]
+ if reply_to:
+ cmd.append( "--reply-to=%s" % reply_to)
+ return self.popen(cmd, stdin=PIPE)
+
+ def receiver(self, config):
+ cmd = ["qpid-receive",
+ "--broker", config.url,
+ "--address", config.address,
+ "--connection-options", "{protocol:%r}" % config.version,
+ "--timeout=10"
+ ]
+ return self.popen(cmd, stdout=PIPE)
+
+ def ready_receiver(self, config):
+ # NOTE: some tests core dump when run with SWIG binding over proton
+ # version<=0.6. This is fixed on proton 0.7.
+ def use_native():
+ pv=os.environ.get("QPID_PROTON_VERSION")
+ return pv and [int(n) for n in pv.split(".")] <= [0,6]
+ s = self.broker.connect(native=use_native()).session()
+ r = s.receiver("readyq; {create:always}")
+ cmd = ["qpid-receive",
+ "--broker", config.url,
+ "--address", config.address,
+ "--connection-options", "{protocol:%r}" % config.version,
+ "--timeout=10", "--ready-address=readyq;{create:always}"
+ ]
+ result = self.popen(cmd, stdout=PIPE)
+ r.fetch(timeout=1) # wait until receiver is actually ready
+ s.acknowledge()
+ r.close()
+ s.close()
+ return result
+
+ def send_and_receive(self, send_config=None, recv_config=None, count=1000, reply_to=None, wait_for_receiver=False, debug=False):
+ if debug:
+ print "sender config is %s" % (send_config or self.default_config)
+ print "receiver config is %s" % (recv_config or self.default_config)
+ sender = self.sender(send_config or self.default_config, reply_to)
+ sender._set_cloexec_flag(sender.stdin) #required for older python, see http://bugs.python.org/issue4112
+ if wait_for_receiver:
+ receiver = self.ready_receiver(recv_config or self.default_config)
+ else:
+ receiver = self.receiver(recv_config or self.default_config)
+
+ messages = ["message-%s" % (i+1) for i in range(count)]
+ for m in messages:
+ sender.stdin.write(m + "\n")
+ sender.stdin.flush()
+ sender.stdin.close()
+ if debug:
+ c = send_config or self.default_config
+ print "sent %s messages to %s sn %s" % (len(messages), c.address, c.url)
+
+ if debug:
+ c = recv_config or self.default_config
+ print "reading messages from %s sn %s" % (c.address, c.url)
+ for m in messages:
+ l = receiver.stdout.readline().rstrip()
+ if debug:
+ print l
+ assert m == l, (m, l)
+
+ sender.wait()
+ receiver.wait()
+
+ def test_simple(self):
+ self.send_and_receive()
+
+ def test_translate1(self):
+ self.send_and_receive(recv_config=Config(self.broker, version="amqp0-10"))
+
+ def test_translate2(self):
+ self.send_and_receive(send_config=Config(self.broker, version="amqp0-10"))
+
+ def test_translate_with_large_routingkey(self):
+ self.send_and_receive(send_config=Config(self.broker, address="amq.topic/a.%s" % ("x" * 256), version="amqp1.0"), recv_config=Config(self.broker, address="amq.topic/a.*", version="amqp0-10"), wait_for_receiver=True)
+
+ def send_and_receive_empty(self, send_config=None, recv_config=None):
+ sconfig = send_config or self.default_config
+ rconfig = recv_config or self.default_config
+ send_cmd = ["qpid-send",
+ "--broker", sconfig.url,
+ "--address=%s" % sconfig.address,
+ "--connection-options={protocol:%s}" % sconfig.version,
+ "--content-size=0",
+ "--messages=1",
+ "-P", "my-header=abc"
+ ]
+ sender = self.popen(send_cmd)
+ sender.wait()
+ receive_cmd = ["qpid-receive",
+ "--broker", rconfig.url,
+ "--address=%s" % rconfig.address,
+ "--connection-options={protocol:%s}" % rconfig.version,
+ "--messages=1",
+ "--print-content=false", "--print-headers=true"
+ ]
+ receiver = self.popen(receive_cmd, stdout=PIPE)
+ l = receiver.stdout.read()
+ assert "my-header:abc" in l
+ receiver.wait()
+
+ def test_translate_empty_1(self):
+ self.send_and_receive_empty(recv_config=Config(self.broker, version="amqp0-10"))
+
+ def test_translate_empty_2(self):
+ self.send_and_receive_empty(send_config=Config(self.broker, version="amqp0-10"))
+
+ def request_response(self, reply_to, send_config=None, request_config=None, response_config=None, count=1000, wait_for_receiver=False):
+ rconfig = request_config or self.default_config
+ echo_cmd = ["qpid-receive",
+ "--broker", rconfig.url,
+ "--address=%s" % rconfig.address,
+ "--connection-options={protocol:%s}" % rconfig.version,
+ "--timeout=10", "--print-content=false", "--print-headers=false"
+ ]
+ requests = self.popen(echo_cmd)
+ self.send_and_receive(send_config, response_config, count, reply_to=reply_to, wait_for_receiver=wait_for_receiver)
+ requests.wait()
+
+ def request_response_local(self, request_address, response_address, wait_for_receiver=False, request_version="amqp1.0", echo_version="amqp1.0"):
+ self.request_response(response_address, send_config=Config(self.broker, address=request_address, version=request_version), request_config=Config(self.broker, address=request_address, version=echo_version), response_config=Config(self.broker, address=response_address, version=request_version), wait_for_receiver=wait_for_receiver)
+
+ def test_request_reponse_queue(self):
+ self.agent.create("queue", "q1")
+ self.agent.create("queue", "q2")
+ self.request_response_local("q1", "q2")
+
+ def test_request_reponse_queue_translated1(self):
+ self.agent.create("queue", "q1")
+ self.agent.create("queue", "q2")
+ self.request_response_local("q1", "q2", request_version="amqp0-10", echo_version="amqp1.0")
+
+ def test_request_reponse_queue_translated2(self):
+ self.agent.create("queue", "q1")
+ self.agent.create("queue", "q2")
+ self.request_response_local("q1", "q2", request_version="amqp1.0", echo_version="amqp0-10")
+
+ def test_request_reponse_exchange(self):
+ self.agent.create("queue", "q1")
+ self.request_response_local("q1", "amq.fanout", wait_for_receiver=True)
+
+ def test_request_reponse_exchange_translated1(self):
+ self.agent.create("queue", "q1")
+ self.request_response_local("q1", "amq.fanout", wait_for_receiver=True, request_version="amqp0-10", echo_version="amqp1.0")
+
+ def test_request_reponse_exchange_translated2(self):
+ self.agent.create("queue", "q1")
+ self.request_response_local("q1", "amq.fanout", wait_for_receiver=True, request_version="amqp1.0", echo_version="amqp0-10")
+
+ def test_request_reponse_exchange_with_subject(self):
+ self.agent.create("queue", "q1")
+ self.request_response_local("q1", "amq.topic/abc; {node:{type:topic}}", wait_for_receiver=True)
+
+ def test_request_reponse_exchange_with_subject_translated1(self):
+ self.agent.create("queue", "q1")
+ self.request_response_local("q1", "amq.topic/abc; {node:{type:topic}}", wait_for_receiver=True, request_version="amqp0-10", echo_version="amqp1.0")
+
+ def test_request_reponse_exchange_with_subject_translated2(self):
+ self.agent.create("queue", "q1")
+ self.request_response_local("q1", "amq.topic/abc; {node:{type:topic}}", wait_for_receiver=True, request_version="amqp1.0", echo_version="amqp0-10")
+
+ def test_domain(self):
+ brokerB = self.amqp_broker()
+ self.agent.create("domain", "BrokerB", {"url":brokerB.host_port()})
+ domains = self.agent._getAllBrokerObjects(Domain)
+ assert len(domains) == 1
+ assert domains[0].name == "BrokerB"
+
+ def incoming_link(self, mechanism):
+ brokerB = self.amqp_broker()
+ agentB = brokerB.agent
+ self.agent.create("queue", "q")
+ agentB.create("queue", "q")
+ self.agent.create("domain", "BrokerB", {"url":brokerB.host_port(), "sasl_mechanisms":mechanism})
+ self.agent.create("incoming", "Link1", {"domain":"BrokerB","source":"q","target":"q"})
+ #send to brokerB, receive from brokerA
+ self.send_and_receive(send_config=Config(brokerB))
+
+ def test_incoming_link_anonymous(self):
+ self.incoming_link("ANONYMOUS")
+
+ def test_incoming_link_nosasl(self):
+ self.incoming_link("NONE")
+
+ def test_outgoing_link(self):
+ brokerB = self.amqp_broker()
+ agentB = brokerB.agent
+ self.agent.create("queue", "q")
+ agentB.create("queue", "q")
+ self.agent.create("domain", "BrokerB", {"url":brokerB.host_port(), "sasl_mechanisms":"NONE"})
+ self.agent.create("outgoing", "Link1", {"domain":"BrokerB","source":"q","target":"q"})
+ #send to brokerA, receive from brokerB
+ self.send_and_receive(recv_config=Config(brokerB))
+
+ def test_relay(self):
+ brokerB = self.amqp_broker()
+ agentB = brokerB.agent
+ agentB.create("queue", "q")
+ self.agent.create("domain", "BrokerB", {"url":brokerB.host_port(), "sasl_mechanisms":"NONE"})
+ #send to q on broker B through brokerA
+ self.send_and_receive(send_config=Config(self.broker, address="q@BrokerB"), recv_config=Config(brokerB))
+
+ def test_reconnect(self):
+ receiver_cmd = ["qpid-receive",
+ "--broker", self.broker.host_port(),
+ "--address=amq.fanout",
+ "--connection-options={protocol:amqp1.0, reconnect:True,container_id:receiver}",
+ "--timeout=10", "--print-content=true", "--print-headers=false"
+ ]
+ receiver = self.popen(receiver_cmd, stdout=PIPE)
+
+ sender_cmd = ["qpid-send",
+ "--broker", self.broker.host_port(),
+ "--address=amq.fanout",
+ "--connection-options={protocol:amqp1.0,reconnect:True,container_id:sender}",
+ "--content-stdin", "--send-eos=1"
+ ]
+ sender = self.popen(sender_cmd, stdin=PIPE)
+ sender._set_cloexec_flag(sender.stdin) #required for older python, see http://bugs.python.org/issue4112
+
+
+ batch1 = ["message-%s" % (i+1) for i in range(10000)]
+ for m in batch1:
+ sender.stdin.write(m + "\n")
+ sender.stdin.flush()
+
+ self.broker.kill()
+ self.broker = self.amqp_broker(port_holder=self.port_holder)
+
+ batch2 = ["message-%s" % (i+1) for i in range(10000, 20000)]
+ for m in batch2:
+ sender.stdin.write(m + "\n")
+ sender.stdin.flush()
+
+ sender.stdin.close()
+
+ last = None
+ m = receiver.stdout.readline().rstrip()
+ while len(m):
+ last = m
+ m = receiver.stdout.readline().rstrip()
+ assert last == "message-20000", (last)
+
+ """ Create and return a broker with AMQP 1.0 support """
+ def amqp_broker(self):
+ assert BrokerTest.amqp_lib, "Cannot locate AMQP 1.0 plug-in"
+ self.port_holder = HaPort(self) #reserve port
+ args = ["--load-module", BrokerTest.amqp_lib,
+ "--socket-fd=%s" % self.port_holder.fileno,
+ "--listen-disable=tcp",
+ "--log-enable=trace+:Protocol",
+ "--log-enable=info+"]
+ return BrokerTest.broker(self, args, port=self.port_holder.port)
+
+ def amqp_broker(self, port_holder=None):
+ assert BrokerTest.amqp_lib, "Cannot locate AMQP 1.0 plug-in"
+ if port_holder:
+ args = ["--load-module", BrokerTest.amqp_lib,
+ "--socket-fd=%s" % port_holder.fileno,
+ "--listen-disable=tcp",
+ "--log-enable=trace+:Protocol",
+ "--log-enable=info+"]
+ return BrokerTest.broker(self, args, port=port_holder.port)
+ else:
+ args = ["--load-module", BrokerTest.amqp_lib,
+ "--log-enable=trace+:Protocol",
+ "--log-enable=info+"]
+ return BrokerTest.broker(self, args)
+
+
+if __name__ == "__main__":
+ shutil.rmtree("brokertest.tmp", True)
+ os.execvp("qpid-python-test",
+ ["qpid-python-test", "-m", "interlink_tests"] + sys.argv[1:])
diff --git a/qpid/cpp/src/tests/interop_tests.py b/qpid/cpp/src/tests/interop_tests.py
new file mode 100755
index 0000000000..f76b9f634b
--- /dev/null
+++ b/qpid/cpp/src/tests/interop_tests.py
@@ -0,0 +1,220 @@
+#!/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.
+#
+
+"""
+A set of tests that can be run against a foreign AMQP 1.0 broker.
+
+RUNNING WITH A FOREIGN BROKER:
+
+1. Start the broker
+2. Create persistent queues named: interop-a interop-b interop-q tx-1 tx-2
+3. Export the environment variable QPID_INTEROP_URL with the URL to connect to your broker
+ in the form [user[:password]@]host[:port]
+4. From the build directory run this test:
+ ctest -VV -R interop_tests
+
+If QPID_INTEROP_URL is not set, a qpidd broker will be started for the test.
+"""
+
+import os, sys, shutil, subprocess
+import qpid_messaging as qm
+from brokertest import *
+
+URL='QPID_INTEROP_URL'
+
+class InteropTest(BrokerTest):
+
+ def setUp(self):
+ super(InteropTest, self).setUp()
+ self.url = os.environ[URL]
+ self.connect_opts = ['--broker', self.url, '--connection-options', '{protocol:amqp1.0}']
+
+ def connect(self, **kwargs):
+ """Python connection to interop URL"""
+ c = qm.Connection.establish(self.url, protocol='amqp1.0', **kwargs)
+ self.teardown_add(c)
+ return c
+
+ def drain(self, queue, connection=None):
+ """
+ Drain a queue to make sure it is empty. Throw away the messages.
+ """
+ c = connection or self.connect()
+ r = c.session().receiver(queue)
+ try:
+ while True:
+ r.fetch(timeout=0)
+ r.session.acknowledge()
+ except qm.Empty:
+ pass
+ r.close()
+
+ def clear_queue(self, queue, connection=None, properties=None, durable=False):
+ """
+ Make empty queue, prefix with self.id(). Create if needed, drain if needed
+ @return queue name.
+ """
+ queue = "interop-%s" % queue
+ c = connection or self.connect()
+ props = {'create':'always'}
+ if durable: props['node'] = {'durable':True}
+ if properties: props.update(properties)
+ self.drain("%s;%s" % (queue, props), c)
+ return queue
+
+
+class SimpleTest(InteropTest):
+ """Simple test to check the broker is responding."""
+
+ def test_send_receive_python(self):
+ c = self.connect()
+ q = self.clear_queue('q', c)
+ s = c.session()
+ s.sender(q).send('foo')
+ self.assertEqual('foo', s.receiver(q).fetch().content)
+
+ def test_send_receive_cpp(self):
+ q = self.clear_queue('q')
+ args = ['-b', self.url, '-a', q]
+ self.check_output(['qpid-send', '--content-string=cpp_foo'] + args)
+ self.assertEqual('cpp_foo', self.check_output(['qpid-receive'] + args).strip())
+
+
+class PythonTxTest(InteropTest):
+
+ def tx_simple_setup(self):
+ """Start a transaction, remove messages from queue a, add messages to queue b"""
+ c = self.connect()
+ qa, qb = self.clear_queue('a', c, durable=True), self.clear_queue('b', c, durable=True)
+
+ # Send messages to a, no transaction.
+ sa = c.session().sender(qa+";{create:always,node:{durable:true}}")
+ tx_msgs = ['x', 'y', 'z']
+ for m in tx_msgs: sa.send(qm.Message(content=m, durable=True))
+
+ # Receive messages from a, in transaction.
+ tx = c.session(transactional=True)
+ txr = tx.receiver(qa)
+ self.assertEqual(tx_msgs, [txr.fetch(1).content for i in xrange(3)])
+ tx.acknowledge()
+
+ # Send messages to b, transactional, mixed with non-transactional.
+ sb = c.session().sender(qb+";{create:always,node:{durable:true}}")
+ txs = tx.sender(qb)
+ msgs = [str(i) for i in xrange(3)]
+ for tx_m, m in zip(tx_msgs, msgs):
+ txs.send(tx_m);
+ sb.send(m)
+ tx.sync()
+ return tx, qa, qb
+
+ def test_tx_simple_commit(self):
+ tx, qa, qb = self.tx_simple_setup()
+ s = self.connect().session()
+ assert_browse(s, qa, [])
+ assert_browse(s, qb, ['0', '1', '2'])
+ tx.commit()
+ assert_browse(s, qa, [])
+ assert_browse(s, qb, ['0', '1', '2', 'x', 'y', 'z'])
+
+ def test_tx_simple_rollback(self):
+ tx, qa, qb = self.tx_simple_setup()
+ s = self.connect().session()
+ assert_browse(s, qa, [])
+ assert_browse(s, qb, ['0', '1', '2'])
+ tx.rollback()
+ assert_browse(s, qa, ['x', 'y', 'z'])
+ assert_browse(s, qb, ['0', '1', '2'])
+
+ def test_tx_sequence(self):
+ tx = self.connect().session(transactional=True)
+ notx = self.connect().session()
+ q = self.clear_queue('q', tx.connection, durable=True)
+ s = tx.sender(q)
+ r = tx.receiver(q)
+ s.send('a')
+ tx.commit()
+ assert_browse(notx, q, ['a'])
+ s.send('b')
+ tx.commit()
+ assert_browse(notx, q, ['a', 'b'])
+ self.assertEqual('a', r.fetch().content)
+ tx.acknowledge();
+ tx.commit()
+ assert_browse(notx, q, ['b'])
+ s.send('z')
+ tx.rollback()
+ assert_browse(notx, q, ['b'])
+ self.assertEqual('b', r.fetch().content)
+ tx.acknowledge();
+ tx.rollback()
+ assert_browse(notx, q, ['b'])
+
+
+class CppTxTest(InteropTest):
+
+ def test_txtest2(self):
+ self.popen(["qpid-txtest2"] + self.connect_opts).assert_exit_ok()
+
+ def test_send_receive(self):
+ q = self.clear_queue('q', durable=True)
+ sender = self.popen(["qpid-send",
+ "--address", q,
+ "--messages=100",
+ "--tx=10",
+ "--durable=yes"] + self.connect_opts)
+ receiver = self.popen(["qpid-receive",
+ "--address", q,
+ "--messages=90",
+ "--timeout=10",
+ "--tx=10"] + self.connect_opts)
+ sender.assert_exit_ok()
+ receiver.assert_exit_ok()
+ expect = [long(i) for i in range(91, 101)]
+ sn = lambda m: m.properties["sn"]
+ assert_browse(self.connect().session(), q, expect, transform=sn)
+
+
+if __name__ == "__main__":
+ if not BrokerTest.amqp_tx_supported:
+ BrokerTest.amqp_tx_warning()
+ print "Skipping interop_tests"
+ sys.exit(0)
+ outdir = "interop_tests.tmp"
+ shutil.rmtree(outdir, True)
+ cmd = ["qpid-python-test", "-m", "interop_tests", "-DOUTDIR=%s"%outdir] + sys.argv[1:]
+ if "QPID_PORT" in os.environ: del os.environ["QPID_PORT"]
+ if os.environ.get(URL):
+ os.execvp(cmd[0], cmd)
+ else:
+ dir = os.getcwd()
+ class StartBroker(BrokerTest):
+ def start_qpidd(self): pass
+ test = StartBroker('start_qpidd')
+ class Config:
+ def __init__(self):
+ self.defines = { 'OUTDIR': outdir }
+ test.configure(Config())
+ test.setUp()
+ os.environ[URL] = test.broker().host_port()
+ os.chdir(dir)
+ p = subprocess.Popen(cmd)
+ status = p.wait()
+ test.tearDown()
+ sys.exit(status)
diff --git a/qpid/cpp/src/tests/ipv6_test b/qpid/cpp/src/tests/ipv6_test
new file mode 100755
index 0000000000..4ac5f95fba
--- /dev/null
+++ b/qpid/cpp/src/tests/ipv6_test
@@ -0,0 +1,120 @@
+#!/usr/bin/env 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.
+#
+
+# Check whether we have any globally configured IPv6 addresses
+# - if not then we can't run the tests because ipv6 lookups won't
+# work within the qpid code. This is a deliberate feature to avoid
+# getting addresses that can't be routed by the machine.
+
+if ip -f inet6 -o addr | cut -f 9 -s -d' ' | grep global > /dev/null ; then
+ echo "IPv6 addresses configured continuing"
+else
+ echo "No global IPv6 addresses configured - skipping test"
+ exit 0
+fi
+
+
+# Run a simple test over IPv6
+source $QPID_TEST_COMMON
+
+CONFIG=$(dirname $0)/config.null
+TEST_HOSTNAME=::1
+COUNT=10
+
+trap cleanup EXIT
+
+error() { echo $*; exit 1; }
+
+# Don't need --no-module-dir or --no-data-dir as they are set as env vars in test_env.sh
+COMMON_OPTS="--interface [::1] --daemon --auth no --config $CONFIG"
+
+# Record all broker ports started
+unset PORTS
+declare -a PORTS
+
+# Start new brokers:
+# $1 must be integer
+# $2 = extra opts
+# Append used ports to PORTS variable
+start_brokers() {
+ local -a ports
+ for (( i=0; $i<$1; i++)) do
+ ports[$i]=$($QPIDD_EXEC --port 0 $COMMON_OPTS $2)
+ done
+ PORTS=( ${PORTS[@]} ${ports[@]} )
+}
+
+stop_brokers() {
+ for port in "${PORTS[@]}";
+ do
+ $QPIDD_EXEC -qp $port
+ done
+ PORTS=()
+}
+
+cleanup() {
+ stop_brokers
+}
+
+start_brokers 1
+PORT=${PORTS[0]}
+echo "Started IPv6 smoke perftest on broker port $PORT"
+
+## Test connection via connection settings
+./qpid-perftest --count ${COUNT} --port ${PORT} -b $TEST_HOSTNAME --summary
+
+## Test connection with a URL
+URL="amqp:[$TEST_HOSTNAME]:$PORT"
+
+./qpid-send -b $URL --content-string=hello -a "foo;{create:always}"
+MSG=`./qpid-receive -b $URL -a "foo;{create:always}" --messages 1`
+test "$MSG" = "hello" || { echo "receive failed '$MSG' != 'hello'"; exit 1; }
+
+stop_brokers
+
+# Federation smoke test follows
+
+# Start 2 brokers
+
+# In a distribution, the python tools will be absent.
+ensure_python_tests
+
+start_brokers 2
+echo "Started Federated brokers on ports ${PORTS[*]}"
+# Make broker urls
+BROKER0="[::1]:${PORTS[0]}"
+BROKER1="[::1]:${PORTS[1]}"
+TEST_QUEUE=ipv6-fed-test
+
+$QPID_CONFIG_EXEC -b $BROKER0 add queue $TEST_QUEUE
+$QPID_CONFIG_EXEC -b $BROKER1 add queue $TEST_QUEUE
+$QPID_ROUTE_EXEC dynamic add $BROKER1 $BROKER0 amq.direct
+$QPID_CONFIG_EXEC -b $BROKER1 bind amq.direct $TEST_QUEUE $TEST_QUEUE
+$QPID_ROUTE_EXEC route map $BROKER1
+
+./datagen --count 100 | tee rdata-in |
+ ./qpid-send -b amqp:$BROKER0 -a amq.direct/$TEST_QUEUE --content-stdin
+./qpid-receive -b amqp:$BROKER1 -a $TEST_QUEUE --print-content yes -m 0 > rdata-out
+
+cmp rdata-in rdata-out || { echo "Federated data over IPv6 does not compare"; exit 1; }
+
+stop_brokers
+rm rdata-in rdata-out
diff --git a/qpid/cpp/src/tests/legacystore/.valgrind.supp b/qpid/cpp/src/tests/legacystore/.valgrind.supp
new file mode 100644
index 0000000000..5c1c5377bf
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/.valgrind.supp
@@ -0,0 +1,35 @@
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSs12_S_constructIPKcEEPcT_S3_RKSaIcESt20forward_iterator_tag
+ fun:_ZNSsC1EPKcRKSaIcE
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSs4_Rep8_M_cloneERKSaIcEm
+ fun:_ZNSs7reserveEm
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSs9_M_mutateEmmm
+ fun:_ZNSs15_M_replace_safeEmmPKcm
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:_Znwm
+ fun:_ZNSs4_Rep9_S_createEmmRKSaIcE
+ fun:_ZNSsC1IPcEET_S1_RKSaIcE
+}
+
diff --git a/qpid/cpp/src/tests/legacystore/.valgrindrc b/qpid/cpp/src/tests/legacystore/.valgrindrc
new file mode 100644
index 0000000000..4aba7661de
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/.valgrindrc
@@ -0,0 +1,7 @@
+--gen-suppressions=all
+--leak-check=full
+--demangle=yes
+--suppressions=.valgrind.supp
+--num-callers=25
+--trace-children=yes
+
diff --git a/qpid/cpp/src/tests/legacystore/CMakeLists.txt b/qpid/cpp/src/tests/legacystore/CMakeLists.txt
new file mode 100644
index 0000000000..5527f23255
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/CMakeLists.txt
@@ -0,0 +1,133 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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(BUILD_LEGACYSTORE AND BUILD_TESTING)
+
+message(STATUS "Building legacystore tests")
+
+# If we're linking Boost for DLLs, turn that on for the tests too.
+if (QPID_LINK_BOOST_DYNAMIC)
+ add_definitions(-DBOOST_TEST_DYN_LINK)
+endif (QPID_LINK_BOOST_DYNAMIC)
+
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+
+set(test_wrap ${shell} ${CMAKE_SOURCE_DIR}/src/tests/run_test${test_script_suffix} -buildDir=${CMAKE_BINARY_DIR})
+
+if (BUILD_TESTING_UNITTESTS)
+
+# Like this to work with cmake 2.4 on Unix
+set (qpid_test_boost_libs
+ ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_SYSTEM_LIBRARY})
+
+# Journal tests
+MACRO (define_journal_test mainSourceFile)
+if ("${ARGV1}" STREQUAL "LONG")
+ set (testname "journal_long_${mainSourceFile}")
+else ()
+ set (testname "journal_${mainSourceFile}")
+endif ()
+add_executable (${testname}
+ jrnl/${mainSourceFile}
+ unit_test
+ ${platform_test_additions})
+target_link_libraries (${testname}
+ ${qpid_test_boost_libs}
+ ${clock_gettime_LIB} legacystore_shared)
+if ("${ARGV1}" STREQUAL "LONG")
+ set_target_properties(${testname} PROPERTIES COMPILE_DEFINITIONS LONG_TEST)
+endif ()
+remember_location(${testname})
+add_test (${testname} ${test_wrap} -boostTest -- ${${testname}_LOCATION})
+unset (testname)
+ENDMACRO (define_journal_test)
+
+define_journal_test (_ut_time_ns)
+define_journal_test (_ut_jexception)
+define_journal_test (_ut_jerrno)
+define_journal_test (_ut_rec_hdr)
+define_journal_test (_ut_jinf)
+define_journal_test (_ut_jdir)
+define_journal_test (_ut_enq_map)
+define_journal_test (_ut_txn_map)
+define_journal_test (_ut_lpmgr)
+define_journal_test (_st_basic)
+define_journal_test (_st_basic_txn)
+define_journal_test (_st_read)
+define_journal_test (_st_read_txn)
+define_journal_test (_st_auto_expand)
+define_journal_test (_ut_lpmgr LONG)
+define_journal_test (_st_basic LONG)
+define_journal_test (_st_read LONG)
+
+add_executable (jtt__ut
+ jrnl/jtt/_ut_data_src.cpp
+ jrnl/jtt/_ut_jrnl_init_params.cpp
+ jrnl/jtt/_ut_read_arg.cpp
+ jrnl/jtt/_ut_jrnl_instance.cpp
+ jrnl/jtt/_ut_test_case.cpp
+ jrnl/jtt/_ut_test_case_result.cpp
+ jrnl/jtt/_ut_test_case_result_agregation.cpp
+ jrnl/jtt/_ut_test_case_set.cpp
+ jrnl/jtt/args.cpp
+ jrnl/jtt/data_src.cpp
+ jrnl/jtt/jrnl_init_params.cpp
+ jrnl/jtt/jrnl_instance.cpp
+ jrnl/jtt/read_arg.cpp
+ jrnl/jtt/test_case.cpp
+ jrnl/jtt/test_case_set.cpp
+ jrnl/jtt/test_case_result.cpp
+ jrnl/jtt/test_case_result_agregation.cpp
+ unit_test.cpp)
+
+target_link_libraries (jtt__ut
+ ${qpid_test_boost_libs}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${clock_gettime_LIB} legacystore_shared)
+
+add_test(journal_jtt_ut ${test_wrap} -boostTest -workingDir=${CMAKE_CURRENT_SOURCE_DIR}/jrnl/jtt -- ${CMAKE_CURRENT_BINARY_DIR}/jtt__ut)
+
+endif (BUILD_TESTING_UNITTESTS)
+
+#
+# Other test programs
+#
+
+add_executable(jtt
+ jrnl/jtt/args.cpp
+ jrnl/jtt/data_src.cpp
+ jrnl/jtt/jrnl_init_params.cpp
+ jrnl/jtt/jrnl_instance.cpp
+ jrnl/jtt/main.cpp
+ jrnl/jtt/read_arg.cpp
+ jrnl/jtt/test_case.cpp
+ jrnl/jtt/test_case_result.cpp
+ jrnl/jtt/test_case_result_agregation.cpp
+ jrnl/jtt/test_case_set.cpp
+ jrnl/jtt/test_mgr.cpp)
+
+target_link_libraries (jtt
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${clock_gettime_LIB} legacystore_shared)
+
+add_test(journal_jtt ${CMAKE_CURRENT_BINARY_DIR}/jtt -c ${CMAKE_CURRENT_SOURCE_DIR}/jrnl/jtt/jtt.csv)
+
+add_test (legacystore_python_tests ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/run_python_tests${test_script_suffix})
+
+endif (BUILD_LEGACYSTORE AND BUILD_TESTING)
diff --git a/qpid/cpp/src/tests/legacystore/MessageUtils.h b/qpid/cpp/src/tests/legacystore/MessageUtils.h
new file mode 100644
index 0000000000..cd23244293
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/MessageUtils.h
@@ -0,0 +1,105 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/broker/Message.h>
+#include <qpid/broker/Queue.h>
+#include <qpid/broker/amqp_0_10/MessageTransfer.h>
+#include <qpid/framing/AMQFrame.h>
+#include <qpid/framing/all_method_bodies.h>
+#include <qpid/framing/Uuid.h>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+struct MessageUtils
+{
+ static Message createMessage(const std::string& exchange, const std::string& routingKey,
+ const Uuid& messageId=Uuid(), const bool durable = false,
+ const uint64_t contentSize = 0, const std::string& correlationId = std::string())
+ {
+ boost::intrusive_ptr<qpid::broker::amqp_0_10::MessageTransfer> msg(new qpid::broker::amqp_0_10::MessageTransfer());
+
+ AMQFrame method(( MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
+ AMQFrame header((AMQHeaderBody()));
+
+ msg->getFrames().append(method);
+ msg->getFrames().append(header);
+ MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true);
+ props->setContentLength(contentSize);
+ props->setMessageId(messageId);
+ props->setCorrelationId(correlationId);
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ if (durable)
+ msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(PERSISTENT);
+ return Message(msg, msg);
+ }
+
+ static void addContent(Message msg, const std::string& data)
+ {
+ AMQFrame content((AMQContentBody(data)));
+ qpid::broker::amqp_0_10::MessageTransfer::get(msg).getFrames().append(content);
+ }
+
+ struct MessageRetriever : public Consumer
+ {
+ MessageRetriever(Queue& q) : Consumer("test", CONSUMER), queue(q) {};
+
+ bool deliver(const QueueCursor& c, const Message& m)
+ {
+ message = m;
+ cursor = c;
+ return true;
+ };
+ void notify() {}
+ void cancel() {}
+ void acknowledged(const DeliveryRecord&) {}
+ OwnershipToken* getSession() { return 0; }
+
+ const Queue& queue;
+ Message message;
+ QueueCursor cursor;
+ };
+
+ static Message get(Queue& queue, QueueCursor* cursor = 0)
+ {
+ boost::shared_ptr<MessageRetriever> consumer(new MessageRetriever(queue));
+ if (!queue.dispatch(consumer))throw qpid::Exception("No message found!");
+ if (cursor) *cursor = consumer->cursor;
+ return consumer->message;
+ }
+
+ static Uuid getMessageId(const Message& message)
+ {
+ return qpid::broker::amqp_0_10::MessageTransfer::get(message).getProperties<MessageProperties>()->getMessageId();
+ }
+
+ static std::string getCorrelationId(const Message& message)
+ {
+ return qpid::broker::amqp_0_10::MessageTransfer::get(message).getProperties<MessageProperties>()->getCorrelationId();
+ }
+
+ static void deliver(Message& msg, FrameHandler& h, uint16_t framesize)
+ {
+ qpid::broker::amqp_0_10::MessageTransfer::get(msg).sendHeader(h, framesize, false, 0, qpid::types::Variant::Map());
+ qpid::broker::amqp_0_10::MessageTransfer::get(msg).sendContent(h, framesize);
+ }
+
+};
diff --git a/qpid/cpp/src/tests/legacystore/TestFramework.cpp b/qpid/cpp/src/tests/legacystore/TestFramework.cpp
new file mode 100644
index 0000000000..2f7faf7682
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/TestFramework.cpp
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// Defines broker to be used by tests
+
+#include "unit_test.h"
+#include "TestFramework.h"
+#include "qpid/broker/Broker.h"
+
+#include <iostream>
+
+//BOOST_GLOBAL_FIXTURE( testBroker )
diff --git a/qpid/cpp/src/tests/legacystore/TestFramework.h b/qpid/cpp/src/tests/legacystore/TestFramework.h
new file mode 100644
index 0000000000..f3066db602
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/TestFramework.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 broker to be used by tests
+
+#include "unit_test.h"
+
+#include <qpid/broker/Broker.h>
+
+namespace {
+ // test broker
+ qpid::broker::Broker::Options opts;
+ qpid::broker::Broker br(opts);
+/*
+ struct testBroker {
+ testBroker() {}
+ ~testBroker() {}
+ };*/
+}
diff --git a/qpid/cpp/src/tests/legacystore/clean.sh b/qpid/cpp/src/tests/legacystore/clean.sh
new file mode 100644
index 0000000000..838f246232
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/clean.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env 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.
+#
+
+# This script cleans up any previous database and journal files, and should
+# be run prior to the store system tests, as these are prone to crashing or
+# hanging under some circumstances if the database is old or inconsistent.
+
+if [ -d ${TMP_DATA_DIR} ]; then
+ rm -rf ${TMP_DATA_DIR}
+fi
+if [ -d ${TMP_PYTHON_TEST_DIR} ]; then
+ rm -rf ${TMP_PYTHON_TEST_DIR}
+fi
+rm -f ${abs_srcdir}/*.vglog*
diff --git a/qpid/cpp/src/tests/legacystore/federation/Makefile.am b/qpid/cpp/src/tests/legacystore/federation/Makefile.am
new file mode 100644
index 0000000000..c48e861a65
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/federation/Makefile.am
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+abs_srcdir=@abs_srcdir@
+
+TMP_DATA_DIR=$(abs_srcdir)/../tmp_data_dir
+
+TESTS = \
+ run_federation_sys_tests
+
+LONG_TESTS = \
+ run_long_federation_sys_tests
+
+EXTRA_DIST = \
+ federation_tests_env.sh \
+ run_federation_sys_tests \
+ run_long_federation_sys_tests
+
+TESTS_ENVIRONMENT = \
+ QPID_DIR=$(QPID_DIR) \
+ QPID_BLD=$(QPID_BLD) \
+ TMP_DATA_DIR=$(TMP_DATA_DIR) \
+ abs_srcdir=$(abs_srcdir)
+
+check-long: all
+ $(MAKE) check TESTS="$(LONG_TESTS)" SUBDIRS=.
+
+# END
+
diff --git a/qpid/cpp/src/tests/legacystore/federation/federation_tests_env.sh b/qpid/cpp/src/tests/legacystore/federation/federation_tests_env.sh
new file mode 100755
index 0000000000..bf75056444
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/federation/federation_tests_env.sh
@@ -0,0 +1,313 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+# --- Function definitions ---
+
+func_check_required_env ()
+#-------------------------
+# Check that EITHER:
+# QPID_DIR is set (for running against svn QPID)
+# OR
+# QPID_PREFIX is set (for running against installed QPID
+# Will exit with error code 1 if neither of these is defined.
+# Params: None
+# Returns: 0 if env vars ok, 1 otherwise
+{
+ if test -z "${QPID_DIR}" -a -z "${QPID_PREFIX}"; then
+ # Try to find qpidd in the normal installed location
+ if test -x /usr/sbin/qpidd; then
+ QPID_PREFIX=/usr
+ else
+ echo "ERROR: Could not find installed Qpid"
+ echo "Either of the following must be set in the environment for this script to run:"
+ echo " QPID_DIR for running against a Qpid svn build"
+ echo " QPID_PREFIX for running against an installed Qpid"
+ return 1
+ fi
+ fi
+ return 0
+}
+
+
+func_check_clustering ()
+#-----------------------
+# Check openAIS/corosync is running and user has correct privileges
+# Params: None
+# Returns: 0 if openAIS/corosync is running, 1 otherwise
+# Sets env var COROSYNC to 1 if corosync is running, not set otherwise
+{
+ # Check either aisexec or corosync is running as root
+ cluster_prog=`ps -u root | grep 'aisexec\|corosync'`
+ test -n "$cluster_prog" || NODAEMON="Neither aisexec nor corosync is running as root"
+ if test -z "$NODAEMON"; then
+ # Test for corosync running
+ echo $cluster_prog | grep "aisexec" > /dev/null || COROSYNC=1
+ if test -n "$COROSYNC"; then
+ # Corosync auth test
+ user=`whoami`
+ ls /etc/corosync/uidgid.d | grep $user > /dev/null || NOAUTH="You are not authorized to use corosync."
+ else
+ # OpenAis auth test
+ id -nG | grep '\<ais\>' >/dev/null || NOAUTH="You are not a member of the ais group."
+ fi
+ fi
+
+ if test -n "$NODAEMON" -o -n "$NOAUTH"; then
+ cat <<EOF
+
+ ========== WARNING: NOT RUNNING CLUSTER TESTS ============
+
+ Cluster tests will not be run because:
+
+ $NODAEMON
+ $NOAUTH
+
+ ==========================================================
+
+EOF
+ return 1
+ fi
+ CLUSTERING_ENABLED=1
+ return 0
+}
+
+
+func_check_qpid_python ()
+#------------------------
+# Check that Qpid python environment is ok
+# Params: None
+# Returns: 0 if Python environment is ok; 1 otherwise
+{
+ if ! python -c "import qpid" ; then
+ cat <<EOF
+
+ =========== WARNING: PYTHON TESTS DISABLED ==============
+
+ Unable to load python qpid module - skipping python tests.
+
+ PYTHONPATH=${PYTHONPATH}
+
+ ===========================================================
+
+EOF
+ return 1
+ fi
+ return 0
+}
+
+func_set_python_env()
+#--------------------
+# Set up the python path
+# Params: None
+# Returns: Nothing
+{
+ if test "${QPID_DIR}" -a -d "${QPID_DIR}" ; then
+ QPID_PYTHON=${QPID_DIR}/python
+ QPID_TOOLS=${QPID_DIR}/tools/src/py
+ QMF_LIB=${QPID_DIR}/extras/qmf/src/py
+ export PYTHONPATH=${QPID_PYTHON}:${QMF_LIB}:${QPID_TOOLS}:$PYTHONPATH
+ fi
+}
+
+func_set_env ()
+#--------------
+# Set up the environment based on value of ${QPID_DIR}: if ${QPID_DIR} exists, assume a svn checkout,
+# otherwise set up for an installed or prefix test.
+# Params: None
+# Returns: Nothing
+{
+ if test "${QPID_DIR}" -a -d "${QPID_DIR}" ; then
+ # QPID_DIR is defined for source tree builds by the --with-qpid-checkout configure option.
+ # QPID_BLD is defined as the build directory, either $QPID_DIR/cpp or separately specified with
+ # the --with-qpid-build option for VPATH builds.
+
+ # Check QPID_BLD is also set
+ if test -z ${QPID_BLD}; then
+ QPID_BLD="${QPID_DIR}/cpp"
+ fi
+ source $QPID_BLD/src/tests/test_env.sh
+# CPP_CLUSTER_EXEC="${QPID_BLD}/src/tests/cluster_test"
+# PYTHON_CLUSTER_EXEC="${QPID_DIR}/cpp/src/tests/$PYTHON_TESTNAME"
+ FEDERATION_SYS_TESTS_FAIL="${QPID_DIR}/cpp/src/tests/federation_sys_tests.fail"
+ if test -z ${STORE_LIB}; then
+ STORE_LIB="../../lib/.libs/msgstore.so"
+ fi
+# export STORE_ENABLE=1
+ else
+ # Set up the environment based on value of ${QPID_PREFIX} for testing against an installed qpid
+ # Alternatively, make sure ${QPID_BIN_DIR}, ${QPID_SBIN_DIR}, ${QPID_LIB_DIR} and ${QPID_LIBEXEC_DIR} are set for
+ # the installed location.
+ if test "${QPID_PREFIX}" -a -d "${QPID_PREFIX}" ; then
+ QPID_BIN_DIR=${QPID_PREFIX}/bin
+ QPID_SBIN_DIR=${QPID_PREFIX}/sbin
+ QPID_LIB_DIR=${QPID_PREFIX}/lib
+ QPID_LIBEXEC_DIR=${QPID_PREFIX}/libexec
+ export PATH="$QPID_BIN_DIR:$QPID_SBIN_DIR:$QPID_LIBEXEC_DIR/qpid/tests:$PATH"
+ fi
+
+ # These four env vars must be set prior to calling this script
+ func_checkpaths QPID_BIN_DIR QPID_SBIN_DIR QPID_LIB_DIR QPID_LIBEXEC_DIR
+
+ # Paths and dirs
+ export PYTHON_DIR="${QPID_BIN_DIR}"
+ export PYTHONPATH="${QPID_LIB_DIR}/python:${QPID_LIBEXEC_DIR}/qpid/tests:${QPID_LIB_DIR}/python2.4:${QPID_LIB_DIR}/python2.4/site-packages:${PYTHONPATH}"
+ # Libraries
+ export CLUSTER_LIB="${QPID_LIB_DIR}/qpid/daemon/cluster.so"
+ export ACL_LIB="${QPID_LIB_DIR}/qpid/daemon/acl.so"
+ export TEST_STORE_LIB="${QPID_LIB_DIR}/qpid/tests/test_store.so"
+
+ # Executables
+# CPP_CLUSTER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/cluster_test"
+# PYTHON_CLUSTER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/$PYTHON_TESTNAME"
+ export QPIDD_EXEC="${QPID_SBIN_DIR}/qpidd"
+ export QPID_CONFIG_EXEC="${QPID_BIN_DIR}/qpid-config"
+ export QPID_ROUTE_EXEC="${QPID_BIN_DIR}/qpid-route"
+ export QPID_CLUSTER_EXEC="${QPID_BIN_DIR}/qpid-cluster"
+# export RECEIVER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/receiver"
+# export SENDER_EXEC="${QPID_LIBEXEC_DIR}/qpid/tests/sender"
+ export QPID_PYTHON_TEST="${QPID_BIN_DIR}/qpid-python-test"
+
+ # Data
+ FEDERATION_SYS_TESTS_FAIL="${QPID_LIBEXEC_DIR}/qpid/tests/federation_sys_tests.fail"
+ fi
+}
+
+
+func_mk_data_dir ()
+#------------------
+# Create a data dir at ${TMP_DATA_DIR} if not present, clear it otherwise.
+# Set TMP_DATA_DIR if it is not set.
+# Params: None
+# Returns: Nothing
+{
+ if test -z "${TMP_DATA_DIR}"; then
+ TMP_DATA_DIR=/tmp/federation_sys_tests
+ echo "TMP_DATA_DIR not set; using ${TMP_DATA_DIR}"
+ fi
+
+ # Delete old cluster test dirs if they exist
+ if test -d "${TMP_DATA_DIR}" ; then
+ rm -rf "${TMP_DATA_DIR}/cluster"
+ fi
+ mkdir -p "${TMP_DATA_DIR}/cluster"
+ export TMP_DATA_DIR
+}
+
+
+func_checkvar ()
+#---------------
+# Check that an environment var is set (ie non-zero length)
+# Params: $1 - env var to be checked
+# Returns: 0 = env var is set (ie non-zero length)
+# 1 = env var is not set
+{
+ local loc_VAR=$1
+ if test -z ${!loc_VAR}; then
+ echo "WARNING: environment variable ${loc_VAR} not set."
+ return 1
+ fi
+ return 0
+}
+
+
+func_checkpaths ()
+#-----------------
+# Check a list of paths (each can contain ':'-separated sub-list) is set and valid (ie each path exists as a dir)
+# Params: $@ - List of path env vars to be checked
+# Returns: Nothing
+{
+ local loc_PATHS=$@
+ for path in ${loc_PATHS}; do
+ func_checkvar ${path}
+ if test $? == 0; then
+ local temp_IFS=${IFS}
+ IFS=":"
+ local pl=${!path}
+ for p in ${pl[@]}; do
+ if test ! -d ${p}; then
+ echo "WARNING: Directory ${p} in var ${path} not found."
+ fi
+ done
+ IFS=${temp_IFS}
+ fi
+ done
+}
+
+
+func_checklibs ()
+#----------------
+# Check that a list of libs is set and valid (ie each lib exists as an executable file)
+# Params: $@ - List of lib values to be checked
+# Returns: Nothing
+{
+ local loc_LIBS=$@
+ for lib in ${loc_LIBS[@]}; do
+ func_checkvar ${lib}
+ if test $? == 0; then
+ if test ! -x ${!lib}; then
+ echo "WARNING: Library ${lib}=${!lib} not found."
+ fi
+ fi
+ done
+}
+
+
+func_checkexecs ()
+#-----------------
+# Check that a list of executable is set and valid (ie each exec exists as an executable file)
+# Params: $@ - List of exec values to be checked
+# Returns: Nothing
+{
+ local loc_EXECS=$@
+ for exec in ${loc_EXECS[@]}; do
+ func_checkvar ${exec}
+ if test $? == 0; then
+ if test ! -x ${!exec}; then
+ echo "WARNING: Executable ${exec}=${!exec} not found or is not executable."
+ fi
+ fi
+ done
+}
+
+
+#--- Start of script ---
+
+func_set_python_env
+func_check_required_env || exit 1 # Cannot run, exit with error
+func_check_qpid_python || exit 1 # Cannot run, exit with error
+func_check_clustering # Warning
+
+PYTHON_TESTNAME=federation_sys.py
+func_set_env
+func_mk_data_dir
+
+# Check expected environment vars are set
+func_checkpaths PYTHON_DIR PYTHONPATH TMP_DATA_DIR
+func_checklibs CLUSTER_LIB STORE_LIB
+func_checkexecs QPIDD_EXEC QPID_CONFIG_EXEC QPID_ROUTE_EXEC QPID_PYTHON_TEST
+
+FAILING_PYTHON_TESTS="${abs_srcdir}/../failing_python_tests.txt"
+if test -z $1; then
+ FEDERATION_SYS_TEST="${QPID_PYTHON_TEST} -m cluster_tests -I ${FEDERATION_SYS_TESTS_FAIL}"
+else
+ FEDERATION_SYS_TEST="${QPID_PYTHON_TEST} -m cluster_tests -I ${FEDERATION_SYS_TESTS_FAIL} cluster_tests.LongTests.*"
+ LONG_TEST=1
+fi
+
diff --git a/qpid/cpp/src/tests/legacystore/federation/run_federation_sys_tests b/qpid/cpp/src/tests/legacystore/federation/run_federation_sys_tests
new file mode 100755
index 0000000000..776f009c05
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/federation/run_federation_sys_tests
@@ -0,0 +1,96 @@
+#!/usr/bin/env 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 federation tests.
+source ${abs_srcdir}/federation_tests_env.sh
+
+MODULENAME=federation_sys
+
+# Test for long test
+if [[ "$1" == "LONG_TEST" ]]; then
+ USE_LONG_TEST=1
+ shift # get rid of this param so it is not treated as a test name
+fi
+
+trap stop_brokers INT TERM QUIT
+
+MODULES="--load-module ${STORE_LIB} --jfile-size 12 --num-jfiles 4"
+CLUSTER_MODULE="--load-module ${CLUSTER_LIB} "
+if [ -z ${USE_LONG_TEST} ]; then
+ SKIPTESTS="-i federation_sys.A_Long* -i federation_sys.B_Long* -i federation_sys.E_Long* -i federation_sys.F_Long*"
+fi
+if [ -z ${CLUSTERING_ENABLED} ]; then
+ SKIPTESTS="${SKIPTESTS} -i federation_sys.C_* -i federation_sys.D_* -i federation_sys.G_* -i federation_sys.H_*"
+elif [ -z ${USE_LONG_TEST} ]; then
+ SKIPTESTS="${SKIPTESTS} -i federation_sys.C_Long* -i federation_sys.D_Long* -i federation_sys.G_Long* -i federation_sys.H_Long*"
+fi
+
+start_brokers() {
+ clean_or_create_dir() {
+ if [ -n "$1" -a -d $1 ]; then
+ rm -rf $1/*
+ else
+ mkdir -p $1
+ fi
+ }
+ start_broker() {
+ clean_or_create_dir $1
+ ${QPIDD_EXEC} --daemon --port 0 --auth no --data-dir $1 $2 > qpidd.port
+ PORT=`cat qpidd.port`
+ eval "$3=${PORT}"
+ }
+ start_broker ${TMP_DATA_DIR}/local "${MODULES} --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.local" LOCAL_PORT
+ start_broker ${TMP_DATA_DIR}/remote "${MODULES} --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.remote" REMOTE_PORT
+ if [ -n "$CLUSTERING_ENABLED" ]; then
+ start_broker ${TMP_DATA_DIR}/cluster/c1.1 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-1 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster1.1" CLUSTER_C1_1
+ start_broker ${TMP_DATA_DIR}/cluster/c1.2 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-1 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster1.2" CLUSTER_C1_2
+ start_broker ${TMP_DATA_DIR}/cluster/c2.1 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-2 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster2.1" CLUSTER_C2_1
+ start_broker ${TMP_DATA_DIR}/cluster/c2.2 "${MODULES} ${CLUSTER_MODULE} --cluster-name test-cluster-2 --log-enable info+ --log-to-file ${TMP_DATA_DIR}/qpidd.log.cluster2.2" CLUSTER_C2_2
+ fi
+ rm qpidd.port
+}
+
+stop_brokers() {
+ ${QPIDD_EXEC} -q --port ${LOCAL_PORT}
+ ${QPIDD_EXEC} -q --port ${REMOTE_PORT}
+ if [ -n "${CLUSTERING_ENABLED}" ]; then
+ ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C1_1}
+ ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C2_1}
+ fi
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ if [ -z ${CLUSTERING_ENABLED} ]; then
+ echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT} (NOTE: clustering is DISABLED)"
+ else
+ echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT}, local cluster nodes ${CLUSTER_C1_1} ${CLUSTER_C1_2}, remote cluster nodes ${CLUSTER_C2_1} ${CLUSTER_C2_2}"
+ fi
+ if [ -z ${USE_LONG_TEST} ]; then
+ echo "NOTE: To run a full set of federation system tests, use \"make check-long\"."
+ fi
+ ${QPID_PYTHON_TEST} -m ${MODULENAME} ${SKIPTESTS} -b localhost:$REMOTE_PORT -Dlocal-port=$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dlocal-cluster-ports="$CLUSTER_C1_1 $CLUSTER_C1_2" -Dremote-cluster-ports="$CLUSTER_C2_1 $CLUSTER_C2_2" $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL federation tests"; exit 1;
+ fi
+fi
diff --git a/qpid/cpp/src/tests/legacystore/federation/run_long_federation_sys_tests b/qpid/cpp/src/tests/legacystore/federation/run_long_federation_sys_tests
new file mode 100755
index 0000000000..012c8d8f18
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/federation/run_long_federation_sys_tests
@@ -0,0 +1,24 @@
+#!/usr/bin/env 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 federation system tests (long version).
+
+./run_federation_sys_tests LONG_TEST $@
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp
new file mode 100644
index 0000000000..fb5c1f1742
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp
@@ -0,0 +1,140 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cmath>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jcntl.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(journal_auto_expand)
+
+const string test_filename("_st_auto_expand");
+
+#include "_st_helper_fns.h"
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(no_ae_threshold)
+{
+ string test_name = get_test_name(test_filename, "no_ae_threshold");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+ unsigned m;
+
+ // Fill journal to just below threshold
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES,
+ DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS);
+ for (m=0; m<t; m++)
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ // This enqueue should exceed the threshold
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t);
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(no_ae_threshold_dequeue_some)
+{
+ string test_name = get_test_name(test_filename, "no_ae_threshold_dequeue_some");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+ unsigned m;
+
+ // Fill journal to just below threshold
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES,
+ DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS);
+ for (m=0; m<t; m++)
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ // This enqueue should exceed the threshold
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t);
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t);
+
+ // Dequeue 25 msgs
+ #define NUM_MSGS_DEQ 25
+ for (m=0; m<NUM_MSGS_DEQ; m++)
+ deq_msg(jc, m, m+t);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(t-NUM_MSGS_DEQ));
+
+ // Check we can still enqueue and dequeue
+ for (m=t+NUM_MSGS_DEQ; m<t+NUM_MSGS_DEQ+NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ for (m=t+NUM_MSGS_DEQ; m<t+NUM_MSGS_DEQ+NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS_DEQ+NUM_MSGS);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(no_ae_threshold_dequeue_all)
+{
+ string test_name = get_test_name(test_filename, "no_ae_threshold_dequeue_all");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+ unsigned m;
+
+ // Fill journal to just below threshold
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES,
+ DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS);
+ for (m=0; m<t; m++)
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ // This enqueue should exceed the threshold
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t);
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t);
+
+ // Dequeue all msgs
+ for (m=0; m<t; m++)
+ deq_msg(jc, m, m+t);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+
+ // Check we can still enqueue and dequeue
+ for (m=2*t; m<2*t + NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ for (m=2*t; m<2*t + NUM_MSGS; m++)
+ deq_msg(jc, m, m+2*t+NUM_MSGS);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp
new file mode 100644
index 0000000000..4aa6d2e29f
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp
@@ -0,0 +1,558 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cmath>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jcntl.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(journal_basic)
+
+const string test_filename("_st_basic");
+
+#include "_st_helper_fns.h"
+
+// === Test suite ===
+
+#ifndef LONG_TEST
+/*
+ * ==============================================
+ * NORMAL TESTS
+ * This section contains normal "make check" tests
+ * for building/packaging. These are built when
+ * LONG_TEST is _not_ defined.
+ * ==============================================
+ */
+
+QPID_AUTO_TEST_CASE(instantiation)
+{
+ string test_name = get_test_name(test_filename, "instantiation");
+ try
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ BOOST_CHECK_EQUAL(jc.is_ready(), false);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(initialization)
+{
+ string test_name = get_test_name(test_filename, "initialization");
+ try
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ BOOST_CHECK_EQUAL(jc.is_ready(), false);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ BOOST_CHECK_EQUAL(jc.is_ready(), true);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_dequeue_block");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m));
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS);
+
+ // Again...
+ for (int m=2*NUM_MSGS; m<3*NUM_MSGS; m++)
+ BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m));
+ for (int m=2*NUM_MSGS; m<3*NUM_MSGS; m++)
+ deq_msg(jc, m, m+3*NUM_MSGS);
+
+ // Disjoint rids
+ for (int m=10*NUM_MSGS; m<11*NUM_MSGS; m++)
+ BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m));
+ for (int m=10*NUM_MSGS; m<11*NUM_MSGS; m++)
+ deq_msg(jc, m, m+11*NUM_MSGS);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_dequeue_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_dequeue_interleaved");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<2*NUM_MSGS; m+=2)
+ {
+ BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m));
+ deq_msg(jc, m, m+1);
+ }
+
+ // Again...
+ for (int m=2*NUM_MSGS; m<4*NUM_MSGS; m+=2)
+ {
+ BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m));
+ deq_msg(jc, m, m+1);
+ }
+
+ // Disjoint rids
+ for (int m=10*NUM_MSGS; m<12*NUM_MSGS; m+=2)
+ {
+ BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m));
+ deq_msg(jc, m, m+1);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_dequeue_interleaved_file_rollover)
+{
+ string test_name = get_test_name(test_filename, "enqueue_dequeue_interleaved_file_rollover");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ unsigned n = num_msgs_to_full(NUM_TEST_JFILES, TEST_JFSIZE_SBLKS * JRNL_SBLK_SIZE,
+ 16*MSG_REC_SIZE_DBLKS, true);
+ for (unsigned m=0; m<3*2*n; m+=2) // overwrite files 3 times
+ {
+ enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false);
+ deq_msg(jc, m, m+1);
+ }
+ jc.stop(true);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(empty_recover)
+{
+ string test_name = get_test_name(test_filename, "empty_recover");
+ try
+ {
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ BOOST_CHECK_EQUAL(jc.is_ready(), false);
+ BOOST_CHECK_EQUAL(jc.is_read_only(), false);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ BOOST_CHECK_EQUAL(jc.is_ready(), true);
+ BOOST_CHECK_EQUAL(jc.is_read_only(), false);
+ }
+ {
+ u_int64_t hrid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ BOOST_CHECK_EQUAL(jc.is_ready(), false);
+ BOOST_CHECK_EQUAL(jc.is_read_only(), false);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(jc.is_ready(), true);
+ BOOST_CHECK_EQUAL(jc.is_read_only(), true);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(0));
+ }
+ {
+ u_int64_t hrid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ BOOST_CHECK_EQUAL(jc.is_ready(), false);
+ BOOST_CHECK_EQUAL(jc.is_read_only(), false);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(jc.is_ready(), true);
+ BOOST_CHECK_EQUAL(jc.is_read_only(), true);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(0));
+ jc.recover_complete();
+ BOOST_CHECK_EQUAL(jc.is_ready(), true);
+ BOOST_CHECK_EQUAL(jc.is_read_only(), false);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_recover_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_recover_dequeue_block");
+ try
+ {
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ }
+ {
+ u_int64_t hrid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1));
+ jc.recover_complete();
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_recover_dequeue_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_recover_dequeue_interleaved");
+ try
+ {
+ string msg;
+ u_int64_t hrid;
+
+ for (int m=0; m<2*NUM_MSGS; m+=2)
+ {
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ if (m == 0)
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); // First time only
+ else
+ {
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(m - 1));
+ jc.recover_complete();
+ }
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ }
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(m));
+ jc.recover_complete();
+ deq_msg(jc, m, m+1);
+ }
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(header_flags)
+{
+ string test_name = get_test_name(test_filename, "header_flags");
+ try
+ {
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ // Transient msgs - should not recover
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), true);
+ // Persistent msgs
+ for (int m=NUM_MSGS; m<NUM_MSGS*2; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ // Transient extern msgs - should not recover
+ for (int m=NUM_MSGS*2; m<NUM_MSGS*3; m++)
+ enq_extern_msg(jc, m, MSG_SIZE, true);
+ // Persistnet extern msgs
+ for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++)
+ enq_extern_msg(jc, m, MSG_SIZE, false);
+ }
+ {
+ string msg;
+ u_int64_t hrid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ // Recover non-transient msgs
+ for (int m=NUM_MSGS; m<NUM_MSGS*2; m++)
+ {
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered.");
+ BOOST_CHECK_MESSAGE(externalFlag == false, "External flag incorrect.");
+ BOOST_CHECK_MESSAGE(create_msg(msg, m, MSG_SIZE).compare(rmsg) == 0,
+ "Non-transient message corrupt during recover.");
+ }
+ // Recover non-transient extern msgs
+ for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++)
+ {
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered.");
+ BOOST_CHECK_MESSAGE(externalFlag == true, "External flag incorrect.");
+ BOOST_CHECK_MESSAGE(rmsg.size() == 0, "External message returned non-zero size.");
+ }
+ jc.recover_complete();
+ // Read recovered non-transient msgs
+ for (int m=NUM_MSGS; m<NUM_MSGS*2; m++)
+ {
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered.");
+ BOOST_CHECK_MESSAGE(externalFlag == false, "External flag incorrect.");
+ BOOST_CHECK_MESSAGE(create_msg(msg, m, MSG_SIZE).compare(rmsg) == 0,
+ "Non-transient message corrupt during recover.");
+ }
+ // Read recovered non-transient extern msgs
+ for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++)
+ {
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered.");
+ BOOST_CHECK_MESSAGE(externalFlag == true, "External flag incorrect.");
+ BOOST_CHECK_MESSAGE(rmsg.size() == 0, "External message returned non-zero size.");
+ }
+ // Dequeue recovered messages
+ for (int m=NUM_MSGS; m<NUM_MSGS*2; m++)
+ deq_msg(jc, m, m+3*NUM_MSGS);
+ for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++)
+ deq_msg(jc, m, m+2*NUM_MSGS);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(double_dequeue)
+{
+ string test_name = get_test_name(test_filename, "double_dequeue");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ enq_msg(jc, 0, create_msg(msg, 0, MSG_SIZE), false);
+ deq_msg(jc, 0, 1);
+ try{ deq_msg(jc, 0, 2); BOOST_ERROR("Did not throw exception on second dequeue."); }
+ catch (const jexception& e){ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_WMGR_DEQRIDNOTENQ); }
+ enq_msg(jc, 2, create_msg(msg, 1, MSG_SIZE), false);
+ deq_msg(jc, 2, 3);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+#else
+/*
+ * ==============================================
+ * LONG TESTS
+ * This section contains long tests and soak tests,
+ * and are run using target check-long (ie "make
+ * check-long"). These are built when LONG_TEST is
+ * defined.
+ * ==============================================
+ */
+
+QPID_AUTO_TEST_CASE(journal_overflow)
+{
+ string test_name = get_test_name(test_filename, "journal_overflow");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+ unsigned m;
+
+ // Fill journal to just below threshold
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE,
+ MSG_REC_SIZE_DBLKS);
+ u_int32_t d = num_dequeues_rem(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE);
+ for (m=0; m<t; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ // This enqueue should exceed the threshold
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH);
+
+ // Dequeue as many msgs as possible except first
+ for (m=1; m<=d; m++)
+ deq_msg(jc, m, m+t);
+ deq_msg(jc, d+1, d+2, RHM_IORES_FULL);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(file_cycle_block)
+{
+ string test_name = get_test_name(test_filename, "file_cycle_block");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+
+ // 5 cycles of enqueue/dequeue blocks of half threshold exception size
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE,
+ LARGE_MSG_REC_SIZE_DBLKS)/2;
+ for (unsigned i=0; i<5; i++)
+ {
+ for (unsigned m=2*i*t; m<(2*i+1)*t; m++)
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ for (unsigned m=2*i*t; m<(2*i+1)*t; m++)
+ deq_msg(jc, m, m+t);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(file_cycle_interleaved)
+{
+ string test_name = get_test_name(test_filename, "file_cycle_interleaved");
+ try
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+
+ // 5 cycles of enqueue/dequeue blocks of half threshold exception size
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE,
+ LARGE_MSG_REC_SIZE_DBLKS)/2;
+ for (unsigned m=0; m<5*2*t; m+=2)
+ {
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ deq_msg(jc, m, m+1);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(recover_file_cycle_block)
+{
+ string test_name = get_test_name(test_filename, "recover_file_cycle_block");
+ try
+ {
+ string msg;
+ u_int64_t hrid;
+
+ // 5 cycles of enqueue/dequeue blocks of half threshold exception size
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE,
+ LARGE_MSG_REC_SIZE_DBLKS)/2;
+ for (unsigned i=0; i<5; i++)
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ if (i)
+ {
+ jc.recover(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(2*i*t - 1));
+ jc.recover_complete();
+ }
+ else
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+
+ for (unsigned m=2*i*t; m<(2*i+1)*t; m++)
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ for (unsigned m=2*i*t; m<(2*i+1)*t; m++)
+ deq_msg(jc, m, m+t);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(recover_file_cycle_interleaved)
+{
+ string test_name = get_test_name(test_filename, "recover_file_cycle_interleaved");
+ try
+ {
+ string msg;
+ u_int64_t hrid;
+
+ // 5 cycles of enqueue/dequeue blocks of half threshold exception size
+ u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE,
+ LARGE_MSG_REC_SIZE_DBLKS)/2;
+ for (unsigned i=0; i<5; i++)
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ if (i)
+ {
+ jc.recover(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(2*i*t - 1));
+ jc.recover_complete();
+ }
+ else
+ jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS);
+
+ for (unsigned m=2*i*t; m<2*(i+1)*t; m+=2)
+ {
+ enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false);
+ deq_msg(jc, m, m+1);
+ }
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+#endif
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp
new file mode 100644
index 0000000000..aa2d31c2ae
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp
@@ -0,0 +1,239 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cmath>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jcntl.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(journal_basic_txn)
+
+const string test_filename("_st_basic_txn");
+
+#include "_st_helper_fns.h"
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(enqueue_commit_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_commit_dequeue_block");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ create_xid(xid, 0, XID_SIZE);
+ for (int m=0; m<NUM_MSGS; m++)
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m));
+ txn_commit(jc, NUM_MSGS, xid);
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS+1);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_abort_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_abort_dequeue_block");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ create_xid(xid, 0, XID_SIZE);
+ for (int m=0; m<NUM_MSGS; m++)
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m));
+ txn_abort(jc, NUM_MSGS, xid);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ try
+ {
+ deq_msg(jc, m, m+NUM_MSGS+1);
+ BOOST_ERROR("Expected dequeue to fail with exception JERR_WMGR_DEQRIDNOTENQ.");
+ }
+ catch (const jexception& e) { if (e.err_code() != jerrno::JERR_WMGR_DEQRIDNOTENQ) throw; }
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_commit_dequeue_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_commit_dequeue_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ create_xid(xid, m, XID_SIZE);
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m));
+ txn_commit(jc, 3*m+1, xid);
+ deq_msg(jc, 3*m, 3*m+2);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_abort_dequeue_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_abort_dequeue_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ create_xid(xid, m, XID_SIZE);
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m));
+ txn_abort(jc, 3*m+1, xid);
+ try
+ {
+ deq_msg(jc, 2*m, 2*m+2);
+ BOOST_ERROR("Expected dequeue to fail with exception JERR_WMGR_DEQRIDNOTENQ.");
+ }
+ catch (const jexception& e) { if (e.err_code() != jerrno::JERR_WMGR_DEQRIDNOTENQ) throw; }
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_dequeue_commit_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_dequeue_commit_block");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ create_xid(xid, 0, XID_SIZE);
+ for (int m=0; m<NUM_MSGS; m++)
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m));
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_txn_msg(jc, m, m+NUM_MSGS, xid);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ txn_commit(jc, 2*NUM_MSGS, xid);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_dequeue_abort_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_dequeue_abort_block");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ create_xid(xid, 0, XID_SIZE);
+ for (int m=0; m<NUM_MSGS; m++)
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m));
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_txn_msg(jc, m, m+NUM_MSGS, xid);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ txn_abort(jc, 2*NUM_MSGS, xid);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_dequeue_commit_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_dequeue_commit_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ create_xid(xid, m, XID_SIZE);
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m));
+ deq_txn_msg(jc, 3*m, 3*m+1, xid);
+ txn_commit(jc, 3*m+2, xid);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_dequeue_abort_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_dequeue_abort_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ create_xid(xid, m, XID_SIZE);
+ BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m));
+ deq_txn_msg(jc, 3*m, 3*m+1, xid);
+ txn_abort(jc, 3*m+2, xid);
+ BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0));
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h b/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h
new file mode 100644
index 0000000000..923065dd11
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h
@@ -0,0 +1,882 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// NOTE: This file is included in _st_*.cpp files inside the QPID_AUTO_TEST_SUITE()
+// definition.
+
+#define MAX_AIO_SLEEPS 500
+#define AIO_SLEEP_TIME 1000
+#define NUM_TEST_JFILES 4
+#define NUM_DEFAULT_JFILES 8
+#define JRNL_DEFAULT_FSIZE 24 // Multiples of JRNL_RMGR_PAGE_SIZE
+#define TEST_JFSIZE_SBLKS 128
+#define DEFAULT_JFSIZE_SBLKS (JRNL_DEFAULT_FSIZE * JRNL_RMGR_PAGE_SIZE)
+#define NUM_MSGS 5
+#define MSG_REC_SIZE_DBLKS 2
+#define MSG_SIZE (MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail)
+#define LARGE_MSG_REC_SIZE_DBLKS (JRNL_SBLK_SIZE * JRNL_RMGR_PAGE_SIZE)
+#define LARGE_MSG_SIZE (LARGE_MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail)
+#define XID_SIZE 64
+
+#define XLARGE_MSG_RATIO (1.0 * LARGE_MSG_REC_SIZE / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE / JRNL_RMGR_PAGE_SIZE)
+#define XLARGE_MSG_THRESHOLD (int)(JRNL_DEFAULT_FSIZE * NUM_DEFAULT_JFILES * JRNL_ENQ_THRESHOLD / 100 / LARGE_MSG_RATIO)
+
+#define NUM_JFILES 4
+#define JFSIZE_SBLKS 128
+
+const char* tdp = getenv("TMP_DATA_DIR");
+const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/" + test_filename : "/var/tmp/jrnl_test");
+
+class test_dtok : public data_tok
+{
+private:
+ bool flag;
+public:
+ test_dtok() : data_tok(), flag(false) {}
+ virtual ~test_dtok() {}
+ bool done() { if (flag || _wstate == NONE) return true; else { flag = true; return false; } }
+};
+
+class test_jrnl_cb : public aio_callback {
+ virtual void wr_aio_cb(std::vector<data_tok*>& dtokl)
+ {
+ for (std::vector<data_tok*>::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++)
+ {
+ test_dtok* dtp = static_cast<test_dtok*>(*i);
+ if (dtp->done())
+ delete dtp;
+ }
+ }
+ virtual void rd_aio_cb(std::vector<u_int16_t>& /*pil*/) {}
+};
+
+class test_jrnl : public jcntl
+{
+test_jrnl_cb* cb;
+
+public:
+ test_jrnl(const std::string& jid, const std::string& jdir, const std::string& base_filename, test_jrnl_cb& cb0) :
+ jcntl(jid, jdir, base_filename),
+ cb(&cb0) {}
+ virtual ~test_jrnl() {}
+ void initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles,
+ const u_int32_t jfsize_sblks)
+ {
+ jcntl::initialize(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE,
+ cb);
+ _jdir.create_dir();
+ }
+ void recover(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks,
+ vector<string>* txn_list, u_int64_t& highest_rid)
+ { jcntl::recover(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE, cb,
+ txn_list, highest_rid); }
+};
+
+/*
+* This class is for testing recover functionality by maintaining an internal lfid-pfid map, then creating physical
+* journal file stubs (just the fhdr section of the journal) and jinf file. This allows the recover functionality (which
+* analyzes these components to determine recover order).
+*
+* First set up a map or "blueprint" of what the journal should look like for recovery, then have the class create the
+* physical files. The jinf object under test then reads and analyzes the created journal, and it's analysis is checked
+* against what is expected.
+*
+* General usage pattern:
+* 1. Create instance of lfid_pfid_map.
+* 2. Call lfid_pfid_map::journal_create() to simulate initial journal creation.
+* 3. (optional) Call lfid_pfid_map::journal_insert() one or more times to simulate the addition of journal files.
+* 4. Call lfid_pfid_map::write_journal() to create dummy journal files (files containing only file headers)
+* 5. Create and initialize the jinf object under test
+* 6. Call jinf::analyze() to determine the pfid order - and thus also first and last lids
+* 7. Call lfid_pfid_map::check_analysis() to check the conclusions of the analysis
+* 8. Call lfid_pfid_map::destroy_journal() to delete the journal files and reset the lfid_pfid_map object.
+* 9. (optional) Back to step 2 for more tests
+*
+* See the individual methods below for more details.
+*/
+class lfid_pfid_map
+{
+ public:
+ typedef pair<u_int16_t, file_hdr> lppair; // Used for loading the map
+ typedef multimap<u_int16_t, file_hdr> lpmap; // Stores the journal "plan" before it is created on-disk
+ typedef lpmap::const_iterator lpmap_citr; // General purpose iterator
+ typedef pair<lpmap_citr, lpmap_citr> lpmap_range; // Range of values returned by multimap's equal_range() fn
+
+ private:
+ string _jid; // Journal id
+ string _base_filename; // Base filename
+ lpmap _map; // Stores the journal "blueprint" before it is created on-disk
+ u_int16_t _num_used_files; // number of files which contain jorunals
+ u_int16_t _oldest_lfid; // lfid where owi flips; always 0 if !_full
+ u_int16_t _last_pfid; // last pfid (ie last file added)
+
+ public:
+ lfid_pfid_map(const string& jid, const string& base_filename) :
+ _jid(jid), _base_filename(base_filename), _num_used_files(0), _oldest_lfid(0), _last_pfid(0)
+ {}
+ virtual ~lfid_pfid_map() {}
+
+ // Mainly used for debugging
+ void print()
+ {
+ int cnt = 0;
+ for (lpmap_citr i=_map.begin(); i!=_map.end(); i++, cnt++)
+ {
+ const file_hdr fh = i->second;
+ cout << " " << cnt << ": owi=" << (fh.get_owi()?"t":"f") << hex << " frid=0x" << fh._rid;
+ cout << " pfid=0x" << fh._pfid << " lfid=0x" << fh._lfid << " fro=0x" << fh._fro << dec << endl;
+ }
+ }
+
+ std::size_t size()
+ {
+ return _map.size();
+ }
+
+ /*
+ * Method journal_create(): Used to simulate the initial creation of a journal before file insertions
+ * take place.
+ *
+ * num_jfiles: The initial journal file count.
+ * num_used_jfiles: If this number is less than num_jfiles, it indicates a clean journal that has not yet
+ * completed its first rotation, and some files are empty (ie all null). The first
+ * num_used_jfiles will contain file headers, the remainder will be blank.
+ * oldest_lfid: The lfid (==pfid, see note 1 below) at which the owi flag flips. During normal operation,
+ * each time the journal rotates back to file 0, a flag (called the overwrite indicator or owi)
+ * is flipped. This flag is saved in the file header. During recovery, if scanning from logical
+ * file 0 upwards, the file at which this flag reverses from its value in file 0 is the file
+ * that was to have been overwritten next, and is thus the "oldest" file. Recovery analysis must
+ * start with this file. oldest_lfid sets the file at which this flag will flip value for the
+ * simulated recovery analysis. Note that this will be ignored if num_used_jfiles < num_jfiles,
+ * as it is not possible for an overwrite to have occurred if not all the files have been used.
+ * first_owi: Sets the value of the owi flag in file 0. If set to false, then the flip will be found with
+ * a true flag (and visa versa).
+ *
+ * NOTES:
+ * 1. By definition, the lfids and pfids coincide for a journal containing no inserted files. Thus pfid == lfid
+ * for all journals created after using initial_journal_create() alone.
+ * 2. By definition, if a journal is not full (num_used_jfiles < num_jfiles), then all owi flags for those files
+ * that are used must be the same. It is not possible for an overwrite situation to arise if a journal is not
+ * full.
+ * 3. This function acts on map _map only, and does not create any test files. Call write_journal() to do that.
+ * 4. This function must be called on a clean test object or on one where the previous test data has been
+ * cleared by calling journal_destroy(). Running this function more than once on existing data will
+ * result in invalid journals which cannot be recovered.
+ */
+ void journal_create(const u_int16_t num_jfiles, // Total number of files
+ const u_int16_t num_used_jfiles, // Number of used files, rest empty at end
+ const u_int16_t oldest_lfid = 0, // Fid where owi reverses
+ const u_int16_t bad_lfid = 0, // Fid where owi reverses again (must be > oldest_lifd),
+ // used for testing bad owi detection
+ const bool first_owi = false) // Value of first owi flag (ie pfid=0)
+ {
+ const bool full = num_used_jfiles == num_jfiles;
+ bool owi = first_owi;
+ _oldest_lfid = full ? oldest_lfid : 0;
+ for (u_int16_t lfid = 0; lfid < num_jfiles; lfid++)
+ {
+ const u_int16_t pfid = lfid;
+ file_hdr fh;
+ if (pfid < num_used_jfiles)
+ {
+ _num_used_files = num_used_jfiles;
+ /*
+ * Invert the owi flag from its current value (initially given by first_owi param) only if:
+ * 1. The journal is full (ie all files are used)
+ * AND
+ * 2. oldest_lfid param is non-zero (this is default, but lfid 0 being inverted is logically
+ * inconsistent with first_owi parameter being present)
+ * AND
+ * 3. Either:
+ * * current lfid == oldest_lfid (ie we are preparing the oldest lfid)
+ * OR
+ * * current lfid == bad_lfid AND bad_lfid > oldest (ie we are past the oldest and preparing the
+ * bad lfid)
+ */
+ if (full && oldest_lfid > 0 &&
+ (lfid == oldest_lfid || (bad_lfid > oldest_lfid && lfid == bad_lfid)))
+ owi = !owi;
+ const u_int64_t frid = u_int64_t(random());
+ init_fhdr(fh, frid, pfid, lfid, owi);
+ }
+ _map.insert(lppair(lfid, fh));
+ }
+ }
+
+ /*
+ * Method journal_insert(): Used to simulate the insertion of journal files into an existing journal.
+ *
+ * after_lfid: The logical file id (lfid) after which the new file is to be inserted.
+ * num_files: The number of files to be inserted.
+ * adjust_lids: Flag indicating that the lids of files _following_ the inserted files are to be adjusted upwards
+ * by the number of inserted files. Not doing so simulates a recovery immediately after insertion
+ * but before the following files are overwritten with their new lids. If this is set false, then:
+ * a) after_lfid MUST be the most recent file (_oldest_lfid-1 ie last lfid before owi changes).
+ * b) This call must be the last insert call.
+ *
+ * NOTES:
+ * 1. It is not possible to insert before lfid/pfid 0; thus these are always coincidental. This operation is
+ * logically equivalent to inserting after the last lfid, which is possible.
+ * 2. It is not possible to insert into a journal that is not full. Doing so will result in an unrecoverable
+ * journal (one that is logically inconsistent that can never occur in reality).
+ * 3. If a journal is stopped/interrupted immediately after a file insertion, there could be duplicate lids in
+ * play at recovery, as the following file lids in their headers are only overwritten when the file is
+ * eventually written to during normal operation. The owi flags, however, are used to determine which of the
+ * ambiguous lids are the inserted files.
+ * 4. This function acts on map _map only, and does not create any test files. Call write_journal() to do that.
+ */
+ void journal_insert(const u_int16_t after_lfid, // Insert files after this lfid
+ const u_int16_t num_files = 1, // Number of files to insert
+ const bool adjust_lids = true) // Adjust lids following inserted files
+ {
+ if (num_files == 0) return;
+ _num_used_files += num_files;
+ const u_int16_t num_jfiles_before_append = _map.size();
+ lpmap_citr i = _map.find(after_lfid);
+ if (i == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map.");
+ const file_hdr fh_before = (*i).second;
+
+ // Move overlapping lids (if req'd)
+ if (adjust_lids && after_lfid < num_jfiles_before_append - 1)
+ {
+ for (u_int16_t lfid = num_jfiles_before_append - 1; lfid > after_lfid; lfid--)
+ {
+ lpmap_citr itr = _map.find(lfid);
+ if (itr == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map.");
+ file_hdr fh = itr->second;
+ _map.erase(lfid);
+ fh._lfid += num_files;
+ if (lfid == _oldest_lfid)
+ _oldest_lfid += num_files;
+ _map.insert(lppair(fh._lfid, fh));
+ }
+ }
+
+ // Add new file headers
+ u_int16_t pfid = num_jfiles_before_append;
+ u_int16_t lfid = after_lfid + 1;
+ while (pfid < num_jfiles_before_append + num_files)
+ {
+ const u_int64_t frid = u_int64_t(random());
+ const size_t fro = 0x200;
+ const file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, frid, pfid, lfid, fro, fh_before.get_owi(),
+ true);
+ _map.insert(lppair(lfid, fh));
+ _last_pfid = pfid;
+ pfid++;
+ lfid++;
+ }
+ }
+
+ /*
+ * Get the list of pfids in the map in order of lfid. The pfids are appended to the supplied vector. Only
+ * as many headers as are in the map are appended.
+ * NOTE: will clear any contents from supplied vector before appending pfid list.
+ */
+ void get_pfid_list(vector<u_int16_t>& pfid_list)
+ {
+ pfid_list.clear();
+ for (lpmap_citr i = _map.begin(); i != _map.end(); i++)
+ pfid_list.push_back(i->second._pfid);
+ }
+
+ /*
+ * Get the list of lfids in the map. The lfids are appended to the supplied vector in the order they appear
+ * in the map (which is not necessarily the natural or sorted order).
+ * NOTE: will clear any contents from supplied vector before appending lfid list.
+ */
+ void get_lfid_list(vector<u_int16_t>& lfid_list)
+ {
+ lfid_list.clear();
+ lfid_list.assign(_map.size(), 0);
+ for (lpmap_citr i = _map.begin(); i != _map.end(); i++)
+ lfid_list[i->second._pfid] = i->first;
+ }
+
+ /*
+ * Method check_analysis(): Used to check the result of the test jinf object analysis by comparing the pfid order
+ * array it produces against the internal map.
+ *
+ * ji: A ref to the jinf object under test.
+ */
+ void check_analysis(jinf& ji) // jinf object under test after analyze() has been called
+ {
+ BOOST_CHECK_EQUAL(ji.get_first_pfid(), get_first_pfid());
+ BOOST_CHECK_EQUAL(ji.get_last_pfid(), get_last_pfid());
+
+ jinf::pfid_list& pfidl = ji.get_pfid_list();
+ const u_int16_t num_jfiles = _map.size();
+ const bool all_used = _num_used_files == num_jfiles;
+ BOOST_CHECK_EQUAL(pfidl.size(), _num_used_files);
+
+ const u_int16_t lfid_start = all_used ? _oldest_lfid : 0;
+ // Because a simulated failure would leave lfid dups in map and last_fid would not exist in map in this
+ // case, we must find lfid_stop via pfid instead. Search for pfid == num_files.
+ lpmap_citr itr = _map.begin();
+ while (itr != _map.end() && itr->second._pfid != _num_used_files - 1) itr++;
+ if (itr == _map.end())
+ BOOST_FAIL("check(): Unable to find pfid=" << (_num_used_files - 1) << " in map.");
+ const u_int16_t lfid_stop = itr->second._lfid;
+
+ std::size_t fidl_index = 0;
+ for (u_int16_t lfid_cnt = lfid_start; lfid_cnt < lfid_stop; lfid_cnt++, fidl_index++)
+ {
+ const u_int16_t lfid = lfid_cnt % num_jfiles;
+ lpmap_citr itr = _map.find(lfid);
+ if (itr == _map.end())
+ BOOST_FAIL("check(): Unable to find lfid=" << lfid << " in map.");
+ BOOST_CHECK_EQUAL(itr->second._pfid, pfidl[fidl_index]);
+ }
+ }
+
+ /*
+ * Method get_pfid(): Look up a pfid from a known lfid.
+ */
+ u_int16_t get_pfid(const u_int16_t lfid, const bool initial_owi = false)
+ {
+ switch (_map.count(lfid))
+ {
+ case 1:
+ return _map.find(lfid)->second._pfid;
+ case 2:
+ for (lpmap_citr itr = _map.lower_bound(lfid); itr != _map.upper_bound(lfid); itr++)
+ {
+ if (itr->second.get_owi() != initial_owi)
+ return itr->second._pfid;
+ }
+ default:;
+ }
+ BOOST_FAIL("get_pfid(): lfid=" << lfid << " not found in map.");
+ return 0xffff;
+ }
+
+ /*
+ * Method get_first_pfid(): Look up the first (oldest, or next-to-be-overwritten) pfid in the analysis sequence.
+ */
+ u_int16_t get_first_pfid()
+ {
+ return get_pfid(_oldest_lfid);
+ }
+
+ /*
+ * Method get_last_pfid(): Look up the last (newest, or most recently written) pfid in the analysis sequence.
+ */
+ u_int16_t get_last_pfid()
+ {
+ u_int16_t flfid = 0;
+ if (_num_used_files == _map.size()) // journal full?
+ {
+ if (_oldest_lfid)
+ {
+ // if failed insert, cycle past duplicate lids
+ while (_map.count(_oldest_lfid) == 2)
+ _oldest_lfid++;
+ while (_map.find(_oldest_lfid) != _map.end() && _map.find(_oldest_lfid)->second.get_owi() == false)
+ _oldest_lfid++;
+ flfid = _oldest_lfid - 1;
+ }
+ else
+ flfid = _map.size() - 1;
+ }
+ else
+ flfid = _num_used_files - 1;
+ return get_pfid(flfid, true);
+ }
+
+ /*
+ * Method write_journal(): Used to create the dummy journal files from the built-up map created by calling
+ * initial_journal_create() and optionally journal_append() one or more times. Since the jinf object reads the
+ * jinf file and the file headers only, the create object creates a dummy journal file containing only a file
+ * header (512 bytes each) and a single jinf file which contains the journal metadata required for recovery
+ * analysis.
+ */
+ void write_journal(const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t fsize_sblks = JFSIZE_SBLKS)
+ {
+ create_jinf(ae, ae_max_jfiles);
+ u_int16_t pfid = 0;
+ for (lpmap_citr itr = _map.begin(); itr != _map.end(); itr++, pfid++)
+ {
+ if (itr->second._pfid == 0 && itr->second._magic == 0) // empty header, use pfid counter instead
+ create_journal_file(pfid, itr->second, _base_filename, fsize_sblks);
+ else
+ create_journal_file(itr->second._pfid, itr->second, _base_filename, fsize_sblks);
+ }
+ }
+
+ /*
+ * Method destroy_journal(): Destroy the files created by create_journal() and reset the lfid_pfid_map test
+ * object. A new test may be started using the same lfid_pfid_map test object once this call has been made.
+ */
+ void destroy_journal()
+ {
+ for (u_int16_t pfid = 0; pfid < _map.size(); pfid++)
+ {
+ string fn = create_journal_filename(pfid, _base_filename);
+ BOOST_WARN_MESSAGE(::unlink(fn.c_str()) == 0, "destroy_journal(): Failed to remove file " << fn);
+ }
+ clean_journal_info_file(_base_filename);
+ _map.clear();
+ _num_used_files = 0;
+ _oldest_lfid = 0;
+ _last_pfid = 0;
+ }
+
+ /*
+ * Method create_new_jinf(): This static call creates a default jinf file only. This is used to test the read
+ * constructor of a jinf test object which reads a jinf file at instantiation.
+ */
+ static void create_new_jinf(const string jid, const string base_filename, const bool ae)
+ {
+ if (jdir::exists(test_dir))
+ jdir::delete_dir(test_dir);
+ create_jinf(NUM_JFILES, ae, (ae ? 5 * NUM_JFILES : 0), jid, base_filename);
+ }
+
+ /*
+ * Method clean_journal_info_file(): This static method deletes only a jinf file without harming any other
+ * journal file or its directory. This is used to clear those tests which rely only on the existence of a
+ * jinf file.
+ */
+ static void clean_journal_info_file(const string base_filename)
+ {
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ BOOST_WARN_MESSAGE(::unlink(fn.str().c_str()) == 0, "clean_journal_info_file(): Failed to remove file " <<
+ fn.str());
+ }
+
+ static string create_journal_filename(const u_int16_t pfid, const string base_filename)
+ {
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << ".";
+ fn << setfill('0') << hex << setw(4) << pfid << "." << JRNL_DATA_EXTENSION;
+ return fn.str();
+ }
+
+ private:
+ static void init_fhdr(file_hdr& fh,
+ const u_int64_t frid,
+ const u_int16_t pfid,
+ const u_int16_t lfid,
+ const bool owi,
+ const bool no_enq = false)
+ {
+ fh._magic = RHM_JDAT_FILE_MAGIC;
+ fh._version = RHM_JDAT_VERSION;
+#if defined(JRNL_BIG_ENDIAN)
+ fh._eflag = RHM_BENDIAN_FLAG;
+#else
+ fh._eflag = RHM_LENDIAN_FLAG;
+#endif
+ fh._uflag = owi ? rec_hdr::HDR_OVERWRITE_INDICATOR_MASK : 0;
+ fh._rid = frid;
+ fh._pfid = pfid;
+ fh._lfid = lfid;
+ fh._fro = no_enq ? 0 : 0x200;
+ timespec ts;
+ ::clock_gettime(CLOCK_REALTIME, &ts);
+ fh._ts_sec = ts.tv_sec;
+ fh._ts_nsec = ts.tv_nsec;
+ }
+
+ void create_jinf(const bool ae, const u_int16_t ae_max_jfiles)
+ {
+ if (jdir::exists(test_dir))
+ jdir::delete_dir(test_dir);
+ create_jinf(_map.size(), ae, ae_max_jfiles, _jid, _base_filename);
+ }
+
+ static void create_jinf(u_int16_t num_files, const bool ae, const u_int16_t ae_max_jfiles, const string jid,
+ const string base_filename)
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ timespec ts;
+ ::clock_gettime(CLOCK_REALTIME, &ts);
+ jinf ji(jid, test_dir, base_filename, num_files, ae, ae_max_jfiles, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE,
+ JRNL_WMGR_DEF_PAGES, ts);
+ ji.write();
+ }
+
+ static void create_journal_file(const u_int16_t pfid,
+ const file_hdr& fh,
+ const string base_filename,
+ const u_int32_t fsize_sblks = JFSIZE_SBLKS,
+ const char fill_char = 0)
+ {
+ const std::string filename = create_journal_filename(pfid, base_filename);
+ ofstream of(filename.c_str(), ofstream::out | ofstream::trunc);
+ if (!of.good())
+ BOOST_FAIL("Unable to open test journal file \"" << filename << "\" for writing.");
+
+ write_file_header(filename, of, fh, fill_char);
+ write_file_body(of, fsize_sblks, fill_char);
+
+ of.close();
+ if (of.fail() || of.bad())
+ BOOST_FAIL("Error closing test journal file \"" << filename << "\".");
+ }
+
+ static void write_file_header(const std::string& filename,
+ ofstream& of,
+ const file_hdr& fh,
+ const char fill_char)
+ {
+ // write file header
+ u_int32_t cnt = sizeof(file_hdr);
+ of.write((const char*)&fh, cnt);
+ if (of.fail() || of.bad())
+ BOOST_FAIL("Error writing file header to test journal file \"" << filename << "\".");
+
+ // fill remaining sblk with fill char
+ while (cnt++ < JRNL_DBLK_SIZE * JRNL_SBLK_SIZE)
+ {
+ of.put(fill_char);
+ if (of.fail() || of.bad())
+ BOOST_FAIL("Error writing filler to test journal file \"" << filename << "\".");
+ }
+ }
+
+ static void write_file_body(ofstream& of, const u_int32_t fsize_sblks, const char fill_char)
+ {
+ if (fsize_sblks > 1)
+ {
+ std::vector<char> sblk_buffer(JRNL_DBLK_SIZE * JRNL_SBLK_SIZE, fill_char);
+ u_int32_t fwritten_sblks = 0; // hdr
+ while (fwritten_sblks++ < fsize_sblks)
+ of.write(&sblk_buffer[0], JRNL_DBLK_SIZE * JRNL_SBLK_SIZE);
+ }
+ }
+};
+
+const string
+get_test_name(const string& file, const string& test_name)
+{
+ cout << test_filename << "." << test_name << ": " << flush;
+ return file + "." + test_name;
+}
+
+bool
+check_iores(const string& ctxt, const iores ret, const iores exp_ret, test_dtok* dtp)
+{
+ if (ret != exp_ret)
+ {
+ delete dtp;
+ BOOST_FAIL(ctxt << ": Expected " << iores_str(exp_ret) << "; got " << iores_str(ret));
+ }
+ return false;
+}
+
+bool
+handle_jcntl_response(const iores res, jcntl& jc, unsigned& aio_sleep_cnt, const std::string& ctxt, const iores exp_ret,
+ test_dtok* dtp)
+{
+ if (res == RHM_IORES_PAGE_AIOWAIT)
+ {
+ if (++aio_sleep_cnt <= MAX_AIO_SLEEPS)
+ {
+ jc.get_wr_events(0); // *** GEV2
+ usleep(AIO_SLEEP_TIME);
+ }
+ else
+ return check_iores(ctxt, res, exp_ret, dtp);
+ }
+ else
+ return check_iores(ctxt, res, exp_ret, dtp);
+ return true;
+}
+
+u_int64_t
+enq_msg(jcntl& jc,
+ const u_int64_t rid,
+ const string& msg,
+ const bool transient,
+ const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ ostringstream ctxt;
+ ctxt << "enq_msg(" << rid << ")";
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_external_rid(true);
+ try
+ {
+ iores res = jc.enqueue_data_record(msg.c_str(), msg.size(), msg.size(), dtp, transient);
+ check_iores(ctxt.str(), res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+u_int64_t
+enq_extern_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const bool transient,
+ const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ ostringstream ctxt;
+ ctxt << "enq_extern_msg(" << rid << ")";
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_external_rid(true);
+ try
+ {
+ iores res = jc.enqueue_extern_data_record(msg_size, dtp, transient);
+ check_iores(ctxt.str(), res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+u_int64_t
+enq_txn_msg(jcntl& jc, const u_int64_t rid, const string& msg, const string& xid, const bool transient,
+ const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ ostringstream ctxt;
+ ctxt << "enq_txn_msg(" << rid << ")";
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_external_rid(true);
+ try
+ {
+ iores res = jc.enqueue_txn_data_record(msg.c_str(), msg.size(), msg.size(), dtp, xid,
+ transient);
+ check_iores(ctxt.str(), res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+u_int64_t
+enq_extern_txn_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const string& xid, const bool transient,
+ const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ ostringstream ctxt;
+ ctxt << "enq_extern_txn_msg(" << rid << ")";
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_external_rid(true);
+ try
+ {
+ iores res = jc.enqueue_extern_txn_data_record(msg_size, dtp, xid, transient);
+ check_iores(ctxt.str(), res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+u_int64_t
+deq_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ ostringstream ctxt;
+ ctxt << "deq_msg(" << drid << ")";
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_dequeue_rid(drid);
+ dtp->set_external_rid(true);
+ dtp->set_wstate(data_tok::ENQ);
+ try
+ {
+ iores res = jc.dequeue_data_record(dtp);
+ check_iores(ctxt.str(), res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+u_int64_t
+deq_txn_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const string& xid,
+ const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ ostringstream ctxt;
+ ctxt << "deq_txn_msg(" << drid << ")";
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_dequeue_rid(drid);
+ dtp->set_external_rid(true);
+ dtp->set_wstate(data_tok::ENQ);
+ try
+ {
+ iores res = jc.dequeue_txn_data_record(dtp, xid);
+ check_iores(ctxt.str(), res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+u_int64_t
+txn_abort(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_external_rid(true);
+ try
+ {
+ iores res = jc.txn_abort(dtp, xid);
+ check_iores("txn_abort", res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+u_int64_t
+txn_commit(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_rid(rid);
+ dtp->set_external_rid(true);
+ try
+ {
+ iores res = jc.txn_commit(dtp, xid);
+ check_iores("txn_commit", res, exp_ret, dtp);
+ u_int64_t dtok_rid = dtp->rid();
+ if (dtp->done()) delete dtp;
+ return dtok_rid;
+ }
+ catch (exception& e) { delete dtp; throw; }
+}
+
+void
+read_msg(jcntl& jc, string& msg, string& xid, bool& transient, bool& external, const iores exp_ret = RHM_IORES_SUCCESS)
+{
+ void* mp = 0;
+ std::size_t msize = 0;
+ void* xp = 0;
+ std::size_t xsize = 0;
+ test_dtok* dtp = new test_dtok;
+ BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0).");
+ dtp->set_wstate(data_tok::ENQ);
+
+ unsigned aio_sleep_cnt = 0;
+ try
+ {
+ iores res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp);
+ while (handle_jcntl_response(res, jc, aio_sleep_cnt, "read_msg", exp_ret, dtp))
+ res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp);
+ }
+ catch (exception& e) { delete dtp; throw; }
+
+ if (mp)
+ msg.assign((char*)mp, msize);
+ if (xp)
+ {
+ xid.assign((char*)xp, xsize);
+ std::free(xp);
+ xp = 0;
+ }
+ else if (mp)
+ {
+ std::free(mp);
+ mp = 0;
+ }
+ delete dtp;
+}
+
+/*
+ * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal with or without
+ * corresponding dequeues (controlled by include_deq) without a threshold - ie until the journal is full. Assumes
+ * that dequeue records fit into one dblk.
+ */
+u_int32_t
+num_msgs_to_full(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks,
+ bool include_deq)
+{
+ u_int32_t rec_size_dblks = msg_rec_size_dblks;
+ if (include_deq)
+ rec_size_dblks++;
+ return u_int32_t(::floor(1.0 * num_files * file_size_dblks / rec_size_dblks));
+}
+
+/*
+ * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal before the enqueue
+ * threshold of JRNL_ENQ_THRESHOLD (%).
+ */
+u_int32_t
+num_msgs_to_threshold(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks)
+{
+ return u_int32_t(::floor(1.0 * num_files * file_size_dblks * JRNL_ENQ_THRESHOLD / msg_rec_size_dblks / 100));
+}
+
+/*
+ * Returns the amount of space reserved in dblks (== num dequeues assuming dequeue size of 1 dblk) for the enqueue
+ * threshold of JRNL_ENQ_THRESHOLD (%).
+ */
+u_int32_t
+num_dequeues_rem(const u_int16_t num_files, const u_int32_t file_size_dblks)
+{
+ /*
+ * Fraction of journal remaining after threshold is used --------------+
+ * Total no. dblks in journal ------+ |
+ * | |
+ * +------------+------------+ +-----------------+---------------------+
+ */
+ return u_int32_t(::ceil(num_files * file_size_dblks * (1.0 - (1.0 * JRNL_ENQ_THRESHOLD / 100))));
+}
+
+string&
+create_msg(string& s, const int msg_num, const int len)
+{
+ ostringstream oss;
+ oss << "MSG_" << setfill('0') << setw(6) << msg_num << "_";
+ for (int i=12; i<=len; i++)
+ oss << (char)('0' + i%10);
+ s.assign(oss.str());
+ return s;
+}
+
+string&
+create_xid(string& s, const int msg_num, const int len)
+{
+ ostringstream oss;
+ oss << "XID_" << setfill('0') << setw(6) << msg_num << "_";
+ for (int i=11; i<len; i++)
+ oss << (char)('a' + i%26);
+ s.assign(oss.str());
+ return s;
+}
+
+long
+get_seed()
+{
+ timespec ts;
+ if (::clock_gettime(CLOCK_REALTIME, &ts))
+ BOOST_FAIL("Unable to read clock to generate seed.");
+ long tenths = ts.tv_nsec / 100000000;
+ return long(10 * ts.tv_sec + tenths); // time in tenths of a second
+}
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp
new file mode 100644
index 0000000000..ff2c39e14c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp
@@ -0,0 +1,460 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cmath>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jcntl.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(journal_read)
+
+const string test_filename("_st_read");
+
+#include "_st_helper_fns.h"
+
+// === Test suite ===
+
+#ifndef LONG_TEST
+/*
+ * ==============================================
+ * NORMAL TESTS
+ * This section contains normal "make check" tests
+ * for building/packaging. These are built when
+ * LONG_TEST is _not_ defined.
+ * ==============================================
+ */
+
+QPID_AUTO_TEST_CASE(empty_read)
+{
+ string test_name = get_test_name(test_filename, "empty_read");
+ try
+ {
+ string msg;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_read_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_read_dequeue_block");
+ try
+ {
+ string msg;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ jc.flush();
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS);
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_read_dequeue_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_read_dequeue_interleaved");
+ try
+ {
+ string msg;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<500*NUM_MSGS; m+=2)
+ {
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ jc.flush();
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ deq_msg(jc, m, m+1);
+ jc.flush();
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_recovered_read_dequeue)
+{
+ string test_name = get_test_name(test_filename, "enqueue_recovered_read_dequeue");
+ try
+ {
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ }
+ {
+ string msg;
+ u_int64_t hrid;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1));
+ jc.recover_complete();
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS);
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(multi_page_enqueue_recovered_read_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "multi_page_enqueue_recovered_read_dequeue_block");
+ try
+ {
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS*125; m++)
+ enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false);
+ }
+ {
+ string msg;
+ u_int64_t hrid;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.recover(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS*125 - 1));
+ jc.recover_complete();
+ for (int m=0; m<NUM_MSGS*125; m++)
+ {
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, 16*MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ for (int m=0; m<NUM_MSGS*125; m++)
+ deq_msg(jc, m, m+NUM_MSGS*125);
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_recover_read_recovered_read_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_recover_read_recovered_read_dequeue_block");
+ try
+ {
+ {
+ string msg;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ }
+ {
+ string msg;
+ u_int64_t hrid;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1));
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ {
+ string msg;
+ u_int64_t hrid;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid);
+ BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1));
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ jc.recover_complete();
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS);
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(delayed_read)
+{
+ string test_name = get_test_name(test_filename, "delayed_read");
+ try
+ {
+ string msg;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ unsigned m;
+ for (m=0; m<2*NUM_MSGS; m+=2)
+ {
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ deq_msg(jc, m, m+1);
+ }
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ jc.flush();
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(msg, rmsg);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(cache_cycled_delayed_read)
+{
+ string test_name = get_test_name(test_filename, "cache_cycled_delayed_read");
+ try
+ {
+ string msg;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ unsigned m;
+ unsigned read_buffer_size_dblks = JRNL_RMGR_PAGES * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE;
+ unsigned n = num_msgs_to_full(1, read_buffer_size_dblks, 16*MSG_REC_SIZE_DBLKS, true);
+ for (m=0; m<2*2*n + 20; m+=2) // fill read buffer twice + 10 msgs
+ {
+ enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false);
+ deq_msg(jc, m, m+1);
+ }
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ jc.flush();
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(msg, rmsg);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+#else
+/*
+ * ==============================================
+ * LONG TESTS
+ * This section contains long tests and soak tests,
+ * and are run using target check-long (ie "make
+ * check-long"). These are built when LONG_TEST is
+ * defined.
+ * ==============================================
+ */
+
+QPID_AUTO_TEST_CASE(multi_page_enqueue_read_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "multi_page_enqueue_read_dequeue_block");
+ try
+ {
+ string msg;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS);
+ for (int i=0; i<10; i++)
+ {
+ for (int m=0; m<NUM_MSGS*125; m++)
+ enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false);
+ jc.flush();
+ for (int m=0; m<NUM_MSGS*125; m++)
+ {
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, 16*MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(xid.size(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ for (int m=0; m<NUM_MSGS*125; m++)
+ deq_msg(jc, m, m+NUM_MSGS*125);
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(increasing_interval_delayed_read)
+{
+ string test_name = get_test_name(test_filename, "increasing_interval_delayed_read");
+ try
+ {
+ string msg;
+ string rmsg;
+ string xid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ unsigned read_buffer_size_dblks = JRNL_RMGR_PAGES * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE;
+ unsigned n = num_msgs_to_full(1, read_buffer_size_dblks, MSG_REC_SIZE_DBLKS, true);
+ unsigned m = 0;
+
+ // Validate read pipeline
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ jc.flush();
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ deq_msg(jc, m, m+1);
+ m += 2;
+
+ // repeat the following multiple times...
+ for (int i=0; i<10; i++)
+ {
+ // Invalidate read pipeline with large write
+ unsigned t = m + (i*n) + 25;
+ for (; m<t; m+=2)
+ {
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ deq_msg(jc, m, m+1);
+ }
+
+ // Revalidate read pipeline
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ jc.flush();
+ read_msg(jc, rmsg, xid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(msg, rmsg);
+ deq_msg(jc, m, m+1);
+ m += 2;
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+#endif
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp
new file mode 100644
index 0000000000..621777d8d3
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp
@@ -0,0 +1,353 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cmath>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jcntl.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(journal_read_txn)
+
+const string test_filename("_st_read_txn");
+
+#include "_st_helper_fns.h"
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(tx_enqueue_commit_block)
+{
+ string test_name = get_test_name(test_filename, "tx_enqueue_commit_block");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ create_xid(xid, 0, XID_SIZE);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_commit(jc, NUM_MSGS, xid);
+ jc.flush();
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(rxid, xid);
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(tx_enqueue_commit_interleaved)
+{
+ string test_name = get_test_name(test_filename, "tx_enqueue_commit_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ create_xid(xid, 2*m, XID_SIZE);
+ enq_txn_msg(jc, 2*m, create_msg(msg, 2*m, MSG_SIZE), xid, false);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_commit(jc, 2*m+1, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, 2*m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(rxid, xid);
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(tx_enqueue_abort_block)
+{
+ string test_name = get_test_name(test_filename, "tx_enqueue_abort_block");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ create_xid(xid, 1, XID_SIZE);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_abort(jc, NUM_MSGS, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(tx_enqueue_abort_interleaved)
+{
+ string test_name = get_test_name(test_filename, "tx_enqueue_abort_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ create_xid(xid, 2*m, XID_SIZE);
+ enq_txn_msg(jc, 2*m, create_msg(msg, 2*m, MSG_SIZE), xid, false);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_abort(jc, 2*m+1, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(tx_enqueue_commit_dequeue_block)
+{
+ string test_name = get_test_name(test_filename, "tx_enqueue_commit_dequeue_block");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ create_xid(xid, 2, XID_SIZE);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false);
+ txn_commit(jc, NUM_MSGS, xid);
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_msg(jc, m, m+NUM_MSGS+1);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(tx_enqueue_commit_dequeue_interleaved)
+{
+ string test_name = get_test_name(test_filename, "tx_enqueue_commit_dequeue_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ create_xid(xid, 3*m, XID_SIZE);
+ enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false);
+ txn_commit(jc, 3*m+1, xid);
+ deq_msg(jc, 3*m, 3*m+2);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_commit_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_commit_block");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ create_xid(xid, 3, XID_SIZE);
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_txn_msg(jc, m, m+NUM_MSGS, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_commit(jc, 2*NUM_MSGS, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_commit_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_commit_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ enq_msg(jc, 3*m, create_msg(msg, 3*m, MSG_SIZE), false);
+ create_xid(xid, 3*m, XID_SIZE);
+ deq_txn_msg(jc, 3*m, 3*m+1, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_commit(jc, 3*m+2, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_abort_block)
+{
+ string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_abort_block");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ create_xid(xid, 4, XID_SIZE);
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false);
+ for (int m=0; m<NUM_MSGS; m++)
+ deq_txn_msg(jc, m, m+NUM_MSGS, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_abort(jc, 2*NUM_MSGS, xid);
+ jc.flush();
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag);
+ BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg);
+ BOOST_CHECK_EQUAL(rxid.length(), std::size_t(0));
+ BOOST_CHECK_EQUAL(transientFlag, false);
+ BOOST_CHECK_EQUAL(externalFlag, false);
+ }
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_abort_interleaved)
+{
+ string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_abort_interleaved");
+ try
+ {
+ string msg;
+ string xid;
+ string rmsg;
+ string rxid;
+ bool transientFlag;
+ bool externalFlag;
+
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS);
+ for (int m=0; m<NUM_MSGS; m++)
+ {
+ enq_msg(jc, 3*m, create_msg(msg, 3*m, MSG_SIZE), false);
+ create_xid(xid, 3*m, XID_SIZE);
+ deq_txn_msg(jc, 3*m, 3*m+1, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING);
+ txn_abort(jc, 3*m+2, xid);
+ jc.flush();
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag);
+ read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp
new file mode 100644
index 0000000000..f05dcb824c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp
@@ -0,0 +1,320 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/legacystore/jrnl/enq_map.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(enq_map_suite)
+
+const string test_filename("_ut_enq_map");
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ enq_map e1;
+ BOOST_CHECK(e1.empty());
+ BOOST_CHECK_EQUAL(e1.size(), u_int32_t(0));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(insert_get)
+{
+ cout << test_filename << ".insert_get: " << flush;
+ u_int16_t pfid;
+ u_int64_t rid;
+ u_int16_t pfid_start = 0x2000U;
+ u_int64_t rid_begin = 0xffffffff00000000ULL;
+ u_int64_t rid_end = 0xffffffff00000200ULL;
+
+ // insert with no dups
+ u_int64_t rid_incr_1 = 4ULL;
+ enq_map e2;
+ e2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1);
+ for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++)
+ BOOST_CHECK_EQUAL(e2.insert_pfid(rid, pfid), enq_map::EMAP_OK);
+ BOOST_CHECK(!e2.empty());
+ BOOST_CHECK_EQUAL(e2.size(), u_int32_t(128));
+
+ // get
+ u_int64_t rid_incr_2 = 6ULL;
+ for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2)
+ {
+ BOOST_CHECK_EQUAL(e2.is_enqueued(rid), (rid%rid_incr_1 ? false : true));
+ u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1);
+ int16_t ret_fid = e2.get_pfid(rid);
+ if (ret_fid < enq_map::EMAP_OK) // fail
+ {
+ BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND);
+ BOOST_CHECK(rid%rid_incr_1);
+ }
+ else
+ {
+ BOOST_CHECK_EQUAL(ret_fid, exp_pfid);
+ BOOST_CHECK(rid%rid_incr_1 == 0);
+ }
+ if ((rid + rid_incr_2)%(8 * rid_incr_2) == 0)
+ pfid++;
+ }
+
+ // insert with dups
+ for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++)
+ {
+ int16_t res = e2.insert_pfid(rid, pfid);
+ if (res < enq_map::EMAP_OK) // fail
+ {
+ BOOST_CHECK_EQUAL(res, enq_map::EMAP_DUP_RID);
+ BOOST_CHECK(rid%rid_incr_1 == 0);
+ }
+ else
+ BOOST_CHECK(rid%rid_incr_1);
+ }
+ BOOST_CHECK_EQUAL(e2.size(), u_int32_t(171));
+ e2.clear();
+ BOOST_CHECK(e2.empty());
+ BOOST_CHECK_EQUAL(e2.size(), u_int32_t(0));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(get_remove)
+{
+ cout << test_filename << ".get_remove: " << flush;
+ u_int16_t pfid;
+ u_int64_t rid;
+ u_int16_t pfid_start = 0x3000U;
+ u_int64_t rid_begin = 0xeeeeeeee00000000ULL;
+ u_int64_t rid_end = 0xeeeeeeee00000200ULL;
+
+ u_int64_t rid_incr_1 = 4ULL;
+ u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1;
+ enq_map e3;
+ e3.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1);
+ for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++)
+ BOOST_CHECK_EQUAL(e3.insert_pfid(rid, pfid), enq_map::EMAP_OK);
+ BOOST_CHECK_EQUAL(e3.size(), num_incr_1);
+
+ u_int64_t rid_incr_2 = 6ULL;
+ for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++)
+ {
+ u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1);
+ int16_t ret_fid = e3.get_remove_pfid(rid);
+ if (ret_fid < enq_map::EMAP_OK) // fail
+ {
+ BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND);
+ BOOST_CHECK(rid%rid_incr_1);
+ }
+ else
+ {
+ BOOST_CHECK_EQUAL(ret_fid, exp_pfid);
+ BOOST_CHECK(rid%rid_incr_1 == 0);
+ }
+ }
+ BOOST_CHECK_EQUAL(e3.size(), u_int32_t(85));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(lock)
+{
+ cout << test_filename << ".lock: " << flush;
+ u_int16_t pfid;
+ u_int64_t rid;
+ u_int16_t pfid_start = 0x4000U;
+ u_int64_t rid_begin = 0xdddddddd00000000ULL;
+ u_int64_t rid_end = 0xdddddddd00000200ULL;
+
+ // insert, every second entry is locked
+ u_int64_t rid_incr_1 = 4ULL;
+ u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1;
+ bool locked = false;
+ enq_map e4;
+ e4.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1);
+ for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++)
+ {
+ BOOST_CHECK_EQUAL(e4.insert_pfid(rid, pfid, locked), enq_map::EMAP_OK);
+ locked = !locked;
+ }
+ BOOST_CHECK_EQUAL(e4.size(), num_incr_1);
+
+ // unlock and lock non-existent rids
+ int16_t res = e4.lock(1ULL);
+ if (res < enq_map::EMAP_OK)
+ BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND);
+ else
+ BOOST_ERROR("Failed to detect locking non-existent rid.");
+ res = e4.unlock(2ULL);
+ if (res < enq_map::EMAP_OK)
+ BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND);
+ else
+ BOOST_ERROR("Failed to detect unlocking non-existent rid.");
+
+ // get / unlock
+ for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1)
+ {
+ int16_t fid = e4.get_pfid(rid);
+ if (fid < enq_map::EMAP_OK) // fail
+ {
+ BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED);
+ BOOST_CHECK(rid%(2*rid_incr_1));
+ // unlock, read, then relock
+ BOOST_CHECK_EQUAL(e4.unlock(rid), enq_map::EMAP_OK);
+ BOOST_CHECK(e4.get_pfid(rid) >= enq_map::EMAP_OK);
+ BOOST_CHECK_EQUAL(e4.lock(rid), enq_map::EMAP_OK);
+ fid = e4.get_pfid(rid);
+ if (fid < enq_map::EMAP_OK) // fail
+ BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED);
+ else
+ BOOST_ERROR("Failed to prevent getting locked record");
+ }
+ }
+
+ // remove all; if locked, use with txn_flag true; should ignore all locked records
+ for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1)
+ BOOST_CHECK(e4.get_remove_pfid(rid, true) >= enq_map::EMAP_OK);
+ BOOST_CHECK(e4.empty());
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(lists)
+{
+ cout << test_filename << ".lists: " << flush;
+ u_int16_t pfid;
+ u_int64_t rid;
+ u_int16_t pfid_start = 0x5000UL;
+ u_int64_t rid_begin = 0xdddddddd00000000ULL;
+ u_int64_t rid_end = 0xdddddddd00000200ULL;
+
+ // insert, every second entry is locked
+ u_int64_t rid_incr_1 = 4ULL;
+ u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1;
+ vector<u_int64_t> rid_list;
+ vector<u_int16_t> pfid_list;
+ enq_map e5;
+ e5.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1);
+ for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++)
+ {
+ BOOST_CHECK_EQUAL(e5.insert_pfid(rid, pfid), enq_map::EMAP_OK);
+ rid_list.push_back(rid);
+ pfid_list.push_back(pfid);
+ }
+ BOOST_CHECK_EQUAL(e5.size(), num_incr_1);
+ BOOST_CHECK_EQUAL(rid_list.size(), num_incr_1);
+ BOOST_CHECK_EQUAL(pfid_list.size(), num_incr_1);
+
+ vector<u_int64_t> ret_rid_list;
+ e5.rid_list(ret_rid_list);
+ BOOST_CHECK_EQUAL(ret_rid_list.size(), num_incr_1);
+ for (unsigned i=0; i<ret_rid_list.size(); i++)
+ BOOST_CHECK_EQUAL(rid_list[i], ret_rid_list[i]);
+
+ vector<u_int16_t> ret_pfid_list;
+ e5.pfid_list(ret_pfid_list);
+ BOOST_CHECK_EQUAL(ret_pfid_list.size(), num_incr_1);
+ for (unsigned i=0; i<ret_pfid_list.size(); i++)
+ BOOST_CHECK_EQUAL(pfid_list[i], ret_pfid_list[i]);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enq_count)
+{
+ cout << test_filename << ".enq_count: " << flush;
+
+ enq_map e6;
+
+ // Check the allocation and cleanup as the file size is set both up and down
+ e6.set_num_jfiles(24);
+ e6.set_num_jfiles(0);
+ e6.set_num_jfiles(100);
+ e6.set_num_jfiles(4);
+
+ // Add 100 enqueues to file 1, check that the counts match
+ for (u_int16_t pfid=0; pfid<4; pfid++)
+ BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0));
+ for (u_int64_t rid=0; rid<100; rid++)
+ BOOST_CHECK_EQUAL(e6.insert_pfid(rid, 1), enq_map::EMAP_OK);
+ for (u_int16_t pfid=0; pfid<4; pfid++)
+ {
+ if (pfid == 1)
+ BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(100));
+ else
+ BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0));
+ }
+
+ // Now remove 10 from file 1, check that the counts match
+ for (u_int64_t rid=0; rid<100; rid+=10)
+ //e6.Xget_remove_pfid(rid);
+ BOOST_CHECK(e6.get_remove_pfid(rid) >= enq_map::EMAP_OK);
+ for (u_int16_t pfid=0; pfid<4; pfid++)
+ {
+ if (pfid == 1)
+ BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90));
+ else
+ BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0));
+ }
+
+ // Now resize the file up and make sure the count in file 1 still exists
+ e6.set_num_jfiles(8);
+ for (u_int16_t pfid=0; pfid<8; pfid++)
+ {
+ if (pfid == 1)
+ BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90));
+ else
+ BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0));
+ }
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(stress)
+{
+ cout << test_filename << ".stress: " << flush;
+ u_int64_t rid;
+ u_int64_t rid_cnt;
+ u_int64_t rid_begin = 0xffffffff00000000ULL;
+ u_int64_t num_rid = 10;
+
+ enq_map e7;
+ e7.set_num_jfiles(rid_begin + num_rid);
+
+ // insert even rids with no dups
+ for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++)
+ BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK);
+ BOOST_CHECK_EQUAL(e7.size(), num_rid);
+
+ // insert odd rids with no dups
+ for (rid = rid_begin + 1, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++)
+ BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK);
+ BOOST_CHECK_EQUAL(e7.size(), num_rid * 2);
+
+ // remove even rids
+ for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++)
+ BOOST_CHECK(e7.get_remove_pfid(rid) >= enq_map::EMAP_OK);
+ BOOST_CHECK_EQUAL(e7.size(), num_rid);
+
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp
new file mode 100644
index 0000000000..b55d5ff8ef
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp
@@ -0,0 +1,416 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cerrno>
+#include <cstring>
+#include <dirent.h>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include "qpid/legacystore/jrnl/file_hdr.h"
+#include "qpid/legacystore/jrnl/jcfg.h"
+#include "qpid/legacystore/jrnl/jdir.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+#include <sys/stat.h>
+
+#define NUM_JFILES 4
+#define JFSIZE_SBLKS 128
+
+#define ERRORSTR(e) std::strerror(e) << " (" << e << ")"
+#define NUM_CLEAR_OPS 20
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jdir_suite)
+
+const string test_filename("_ut_jdir");
+const char* tdp = getenv("TMP_DATA_DIR");
+const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/_ut_jdir" : "/var/tmp/_ut_jdir");
+
+// === Helper functions ===
+
+void create_file(const char* filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+{
+ ofstream of(filename, ofstream::out | ofstream::trunc);
+ if (!of.good())
+ BOOST_FAIL("Unable to open file " << filename << " for writing.");
+ of.write(filename, std::strlen(filename));
+ of.close();
+ ::chmod(filename, fmode);
+}
+
+void create_file(const string filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+{
+ create_file(filename.c_str(), fmode);
+}
+
+void create_jdat_file(const char* dirname, const char* base_filename, u_int32_t fid,
+ u_int64_t first_rid)
+{
+ stringstream fn;
+ fn << dirname << "/" << base_filename << ".";
+ fn << setfill('0') << hex << setw(4) << fid << ".jdat";
+ file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, 0, first_rid, fid, 0x200, true);
+ ofstream of(fn.str().c_str(), ofstream::out | ofstream::trunc);
+ if (!of.good())
+ BOOST_FAIL("Unable to open journal data file " << fn.str() << " for writing.");
+ of.write((const char*)&fh, sizeof(file_hdr));
+ of.close();
+}
+
+void create_jinf_file(const char* dirname, const char* base_filename)
+{
+ timespec ts;
+ ::clock_gettime(CLOCK_REALTIME, &ts);
+ jinf ji("test journal id", dirname, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS,
+ JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts);
+ ji.write();
+}
+
+void create_jrnl_fileset(const char* dirname, const char* base_filename)
+{
+ create_jinf_file(dirname, base_filename);
+ for (u_int32_t fid = 0; fid < NUM_JFILES; fid++)
+ {
+ u_int64_t rid = 0x12340000 + (fid * 0x25);
+ create_jdat_file(dirname, base_filename, fid, rid);
+ }
+}
+
+unsigned count_dir_contents(const char* dirname, bool incl_files, bool incl_dirs = true)
+{
+ struct dirent* entry;
+ struct stat s;
+ unsigned file_cnt = 0;
+ unsigned dir_cnt = 0;
+ unsigned other_cnt = 0;
+ DIR* dir = ::opendir(dirname);
+ if (!dir)
+ BOOST_FAIL("Unable to open directory " << dirname);
+ while ((entry = ::readdir(dir)) != NULL)
+ {
+ // Ignore . and ..
+ if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0)
+ {
+ stringstream fn;
+ fn << dirname << "/" << entry->d_name;
+ if (::stat(fn.str().c_str(), &s))
+ BOOST_FAIL("Unable to stat dir entry " << entry->d_name << "; err=" <<
+ ERRORSTR(errno));
+ if (S_ISREG(s.st_mode))
+ file_cnt++;
+ else if (S_ISDIR(s.st_mode))
+ dir_cnt++;
+ else
+ other_cnt++;
+ }
+ }
+ ::closedir(dir);
+ if (incl_files)
+ {
+ if (incl_dirs)
+ return file_cnt + dir_cnt;
+ return file_cnt;
+ }
+ else if (incl_dirs)
+ return dir_cnt;
+ return other_cnt;
+}
+
+void check_dir_contents(const char* dirname, const char* base_filename, unsigned num_subdirs,
+ bool jrnl_present)
+{
+ if (jdir::is_dir(dirname))
+ {
+ // Subdir count
+ BOOST_CHECK_EQUAL(count_dir_contents(dirname, false, true), num_subdirs);
+
+ // Journal file count
+ unsigned num_jrnl_files = jrnl_present ? NUM_JFILES + 1 : 0;
+ BOOST_CHECK_EQUAL(count_dir_contents(dirname, true, false), num_jrnl_files);
+
+ // Check journal files are present
+ if (jrnl_present)
+ try { jdir::verify_dir(dirname, base_filename); }
+ catch(const jexception& e) { BOOST_ERROR(e); }
+ for (unsigned subdir_num = 1; subdir_num <= num_subdirs; subdir_num++)
+ {
+ stringstream subdir_name;
+ subdir_name << dirname << "/_" << base_filename << ".bak.";
+ subdir_name << hex << setfill('0') << setw(4) << subdir_num;
+ try { jdir::verify_dir(subdir_name.str().c_str(), base_filename); }
+ catch(const jexception& e) { BOOST_ERROR(e); }
+ }
+ }
+ else
+ BOOST_ERROR(dirname << " is not a directory");
+}
+
+void check_dir_not_existing(const char* dirname)
+{
+ if (jdir::exists(dirname) && jdir::is_dir(dirname))
+ jdir::delete_dir(dirname);
+ if (jdir::exists(dirname))
+ BOOST_FAIL("Unable to remove directory " << dirname);
+}
+
+void check_dir_not_existing(const string dirname)
+{
+ check_dir_not_existing(dirname.c_str());
+}
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ string dir(test_dir + "/A/B/C/D/E/F");
+ string bfn("test_base");
+ jdir dir1(dir, bfn);
+ BOOST_CHECK(dir1.dirname().compare(dir) == 0);
+ BOOST_CHECK(dir1.base_filename().compare(bfn) == 0);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(create_delete_dir)
+{
+ cout << test_filename << ".create_delete_dir: " << flush;
+ // Use instance
+ string dir_A(test_dir + "/A");
+ string dir_Ats(test_dir + "/A/"); // trailing '/'
+ check_dir_not_existing(test_dir + "/A");
+ jdir dir1(dir_A, "test_base");
+ dir1.create_dir();
+ // check all combos of jdir::exists and jdir::is_dir()
+ BOOST_CHECK(jdir::exists(dir_A));
+ BOOST_CHECK(jdir::exists(dir_Ats));
+ BOOST_CHECK(jdir::exists(dir_A.c_str()));
+ BOOST_CHECK(jdir::exists(dir_Ats.c_str()));
+ BOOST_CHECK(jdir::is_dir(dir_A));
+ BOOST_CHECK(jdir::is_dir(dir_Ats));
+ BOOST_CHECK(jdir::is_dir(dir_Ats.c_str()));
+ BOOST_CHECK(jdir::is_dir(dir_Ats.c_str()));
+ // do it a second time when dir exists
+ dir1.create_dir();
+ BOOST_CHECK(jdir::is_dir(dir_A));
+ dir1.delete_dir();
+ BOOST_CHECK(!jdir::exists(dir_A));
+
+ // Use static fn
+ check_dir_not_existing(test_dir + "/B");
+ jdir::create_dir(test_dir + "/B");
+ BOOST_CHECK(jdir::is_dir(test_dir + "/B"));
+ jdir::create_dir(test_dir + "/B");
+ BOOST_CHECK(jdir::is_dir(test_dir + "/B"));
+ jdir::delete_dir(test_dir + "/B");
+ BOOST_CHECK(!jdir::exists(test_dir + "/B"));
+
+ // Non-empty dirs
+ check_dir_not_existing(test_dir + "/C");
+ jdir::create_dir(test_dir + "/C");
+ BOOST_CHECK(jdir::is_dir(test_dir + "/C"));
+ create_file(test_dir + "/C/test_file_1.txt"); // mode 644 (default)
+ create_file(test_dir + "/C/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777
+ create_file(test_dir + "/C/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444 (read-only)
+ create_file(test_dir + "/C/test_file_4.txt", 0); // mode 000 (no permissions)
+ BOOST_CHECK(jdir::is_dir(test_dir + "/C"));
+ jdir::create_dir(test_dir + "/C");
+ BOOST_CHECK(jdir::is_dir(test_dir + "/C"));
+ jdir::delete_dir(test_dir + "/C");
+ BOOST_CHECK(!jdir::exists(test_dir + "/C"));
+
+ // Check non-existent dirs fail
+ check_dir_not_existing(test_dir + "/D");
+ try
+ {
+ jdir::is_dir(test_dir + "/D");
+ BOOST_ERROR("jdir::is_dir() failed to throw jexeption for non-existent directory.");
+ }
+ catch(const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_STAT);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(create_delete_dir_recursive)
+{
+ cout << test_filename << ".create_delete_dir_recursive: " << flush;
+ // Use instances
+ check_dir_not_existing(test_dir + "/E");
+ jdir dir1(test_dir + "/E/F/G/H", "test_base");
+ dir1.create_dir();
+ BOOST_CHECK(jdir::is_dir(test_dir + "/E/F/G/H"));
+ dir1.delete_dir();
+ BOOST_CHECK(!jdir::exists(test_dir + "/E/F/G/H")); // only H deleted, E/F/G remain
+ BOOST_CHECK(jdir::exists(test_dir + "/E/F/G"));
+ jdir::delete_dir(test_dir + "/E"); // delete remaining dirs
+ BOOST_CHECK(!jdir::exists(test_dir + "/E"));
+
+ check_dir_not_existing(test_dir + "/F");
+ jdir dir2(test_dir + "/F/G/H/I/", "test_base"); // trailing '/'
+ dir2.create_dir();
+ BOOST_CHECK(jdir::is_dir(test_dir + "/F/G/H/I/"));
+ dir2.delete_dir();
+ BOOST_CHECK(!jdir::exists(test_dir + "/F/G/H/I/"));
+ BOOST_CHECK(jdir::exists(test_dir + "/F/G/H/"));
+ jdir::delete_dir(test_dir + "/F");
+ BOOST_CHECK(!jdir::exists(test_dir + "/F"));
+
+ check_dir_not_existing(test_dir + "/G");
+ jdir dir3(test_dir + "/G/H//I//J", "test_base"); // extra '/' in path
+ dir3.create_dir();
+ BOOST_CHECK(jdir::is_dir(test_dir + "/G/H//I//J"));
+ dir3.delete_dir();
+ BOOST_CHECK(!jdir::exists(test_dir + "/G/H//I//J"));
+ BOOST_CHECK(jdir::exists(test_dir + "/G/H//I"));
+ jdir::delete_dir(test_dir + "/F");
+ BOOST_CHECK(!jdir::exists(test_dir + "/F"));
+
+ // Use static fn
+ check_dir_not_existing(test_dir + "/H");
+ jdir::create_dir(test_dir + "/H/I/J/K");
+ BOOST_CHECK(jdir::is_dir(test_dir + "/H/I/J/K"));
+ jdir::delete_dir(test_dir + "/H/I/J/K");
+ BOOST_CHECK(!jdir::exists(test_dir + "/H/I/J/K")); // only J deleted, H/I/J remain
+ BOOST_CHECK(jdir::exists(test_dir + "/H/I/J"));
+ jdir::delete_dir(test_dir + "/H");
+ BOOST_CHECK(!jdir::exists(test_dir + "/H"));
+
+ check_dir_not_existing(test_dir + "/I");
+ jdir::create_dir(test_dir + "/I/J/K/L/"); // trailing '/'
+ BOOST_CHECK(jdir::is_dir(test_dir + "/I/J/K/L/"));
+ jdir::delete_dir(test_dir + "/I/J/K/L/");
+ BOOST_CHECK(!jdir::exists(test_dir + "/I/J/K/L/"));
+ BOOST_CHECK(jdir::exists(test_dir + "/I/J/K/"));
+ jdir::delete_dir(test_dir + "/I");
+ BOOST_CHECK(!jdir::exists(test_dir + "/I"));
+
+ check_dir_not_existing(test_dir + "//J");
+ jdir::create_dir(test_dir + "//J//K//L//M"); // extra '/' in path
+ BOOST_CHECK(jdir::is_dir(test_dir + "//J//K//L//M"));
+ jdir::delete_dir(test_dir + "//J//K//L//M");
+ BOOST_CHECK(!jdir::exists(test_dir + "//J//K//L//M"));
+ BOOST_CHECK(jdir::exists(test_dir + "//J//K//L"));
+ jdir::delete_dir(test_dir + "//J");
+ BOOST_CHECK(!jdir::exists(test_dir + "//J"));
+
+ // Non-empty dirs
+ check_dir_not_existing(test_dir + "/K");
+ jdir::create_dir(test_dir + "/K/L/M1/N1");
+ jdir::create_dir(test_dir + "/K/L/M1/N2");
+ jdir::create_dir(test_dir + "/K/L/M1/N3");
+ jdir::create_dir(test_dir + "/K/L/M1/N4");
+ create_file(test_dir + "/K/L/M1/N4/test_file_1.txt"); // mode 644 (default)
+ create_file(test_dir + "/K/L/M1/N4/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777
+ create_file(test_dir + "/K/L/M1/N4/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444
+ create_file(test_dir + "/K/L/M1/N4/test_file_4.txt", 0); // mode 000 (no permissions)
+ jdir::create_dir(test_dir + "/K/L/M2");
+ jdir::create_dir(test_dir + "/K/L/M3/N5");
+ jdir::create_dir(test_dir + "/K/L/M3/N6");
+ BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N1"));
+ BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N2"));
+ BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N3"));
+ BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N4"));
+ BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M2"));
+ BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N5"));
+ BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N6"));
+ jdir::delete_dir(test_dir + "/K");
+ BOOST_CHECK(!jdir::exists(test_dir + "/K"));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(clear_verify_dir)
+{
+ cout << test_filename << ".clear_verify_dir: " << flush;
+ // Use instances
+ const char* jrnl_dir = "/var/tmp/test_dir_1";
+ const char* bfn = "test_base";
+ check_dir_not_existing(jrnl_dir);
+ jdir test_dir_1(jrnl_dir, bfn);
+ test_dir_1.create_dir();
+ BOOST_CHECK(jdir::is_dir(jrnl_dir));
+ // add journal files, check they exist, then clear them
+ unsigned cnt = 0;
+ while (cnt < NUM_CLEAR_OPS)
+ {
+ create_jrnl_fileset(jrnl_dir, bfn);
+ check_dir_contents(jrnl_dir, bfn, cnt, true);
+ test_dir_1.clear_dir();
+ check_dir_contents(jrnl_dir, bfn, ++cnt, false);
+ }
+ // clean up
+ test_dir_1.delete_dir();
+ BOOST_CHECK(!jdir::exists(jrnl_dir));
+
+ // Non-existent dir with auto-create true
+ jrnl_dir = "/var/tmp/test_dir_2";
+ check_dir_not_existing(jrnl_dir);
+ jdir test_dir_2(jrnl_dir, bfn);
+ // clear dir
+ test_dir_2.clear_dir(); // create flag is true by default
+ check_dir_contents(jrnl_dir, bfn, 0, false);
+ // clear empty dir, should not create subdir
+ test_dir_2.clear_dir(); // create flag is true by default
+ check_dir_contents(jrnl_dir, bfn, 0, false);
+ // clean up
+ test_dir_2.delete_dir();
+ BOOST_CHECK(!jdir::exists(jrnl_dir));
+
+ // non-existent dir with auto-create false
+ jrnl_dir = "/var/tmp/test_dir_3";
+ check_dir_not_existing(jrnl_dir);
+ jdir test_dir_3(jrnl_dir, bfn);
+ try
+ {
+ test_dir_3.clear_dir(false);
+ BOOST_ERROR("jdir::clear_dir(flase) failed to throw jexeption for non-existent directory.");
+ }
+ catch(const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_OPENDIR);
+ }
+
+ // Use static fn
+ jrnl_dir = "/var/tmp/test_dir_4";
+ check_dir_not_existing(jrnl_dir);
+ jdir::clear_dir(jrnl_dir, bfn); // should create dir if it does not exist
+ // add journal files, check they exist, then clear them
+ cnt = 0;
+ while (cnt < NUM_CLEAR_OPS)
+ {
+ create_jrnl_fileset(jrnl_dir, bfn);
+ check_dir_contents(jrnl_dir, bfn, cnt, true);
+ jdir::clear_dir(jrnl_dir, bfn);
+ check_dir_contents(jrnl_dir, bfn, ++cnt, false);
+ }
+ // clean up
+ jdir::delete_dir(jrnl_dir);
+ BOOST_CHECK(!jdir::exists(jrnl_dir));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp
new file mode 100644
index 0000000000..1ae1138355
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp
@@ -0,0 +1,47 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "../unit_test.h"
+
+#include <cstring>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jerrno.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jerrno_suite)
+using namespace mrg::journal;
+
+const string test_filename("_ut_jerrno");
+
+QPID_AUTO_TEST_CASE(jerrno_val)
+{
+ cout << test_filename << ".jerrno_val: " << flush;
+ const char* m = "JERR__MALLOC";
+ string malloc_msg = string(jerrno::err_msg(jerrno::JERR__MALLOC));
+ BOOST_CHECK(malloc_msg.substr(0, std::strlen(m)).compare(m) == 0);
+ BOOST_CHECK(std::strcmp(jerrno::err_msg(0), "<Unknown error code>") == 0);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp
new file mode 100644
index 0000000000..8b9e876aa6
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp
@@ -0,0 +1,346 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cstring>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "qpid/legacystore/jrnl/jexception.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jexception_suite)
+
+const string test_filename("_ut_jexception");
+
+// === Helper functions ===
+
+void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len,
+ std::size_t tc_len, std::size_t tf_len)
+{
+ try { throw e; }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(std::strlen(e.what()), what_len);
+ BOOST_CHECK_EQUAL(e.additional_info().size(), ai_len);
+ BOOST_CHECK_EQUAL(e.throwing_class().size(), tc_len);
+ BOOST_CHECK_EQUAL(e.throwing_fn().size(), tf_len);
+ }
+}
+
+void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len)
+{
+ throw_exception(e, what_len, ai_len, 0, 0);
+}
+
+void throw_exception(const jexception& e, std::size_t what_len, std::size_t tc_len,
+ std::size_t tf_len)
+{
+ throw_exception(e, what_len, 0, tc_len, tf_len);
+}
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(constructor_1)
+{
+ cout << test_filename << ".constructor_1: " << flush;
+ try
+ {
+ jexception e1;
+ BOOST_CHECK_EQUAL(e1.err_code(), (u_int32_t)0);
+ BOOST_CHECK(e1.additional_info().size() == 0);
+ BOOST_CHECK(e1.throwing_class().size() == 0);
+ BOOST_CHECK(e1.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e1.what()) > 0);
+ throw e1;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0);
+ BOOST_CHECK(e.additional_info().size() == 0);
+ BOOST_CHECK(e.throwing_class().size() == 0);
+ BOOST_CHECK(e.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_2)
+{
+ cout << test_filename << ".constructor_2: " << flush;
+ const u_int32_t err_code = 2;
+ try
+ {
+ jexception e2(err_code);
+ BOOST_CHECK_EQUAL(e2.err_code(), err_code);
+ BOOST_CHECK(e2.additional_info().size() == 0);
+ BOOST_CHECK(e2.throwing_class().size() == 0);
+ BOOST_CHECK(e2.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e2.what()) > 0);
+ throw e2;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), err_code);
+ BOOST_CHECK(e.additional_info().size() == 0);
+ BOOST_CHECK(e.throwing_class().size() == 0);
+ BOOST_CHECK(e.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_3a)
+{
+ cout << test_filename << ".constructor_3a: " << flush;
+ const char* err_msg = "exception3";
+ try
+ {
+ jexception e3(err_msg);
+ BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0);
+ BOOST_CHECK(e3.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e3.throwing_class().size() == 0);
+ BOOST_CHECK(e3.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e3.what()) > 0);
+ throw e3;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0);
+ BOOST_CHECK(e.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e.throwing_class().size() == 0);
+ BOOST_CHECK(e.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_3b)
+{
+ cout << test_filename << ".constructor_3b: " << flush;
+ const string err_msg("exception3");
+ try
+ {
+ jexception e3(err_msg);
+ BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0);
+ BOOST_CHECK(e3.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e3.throwing_class().size() == 0);
+ BOOST_CHECK(e3.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e3.what()) > 0);
+ throw e3;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0);
+ BOOST_CHECK(e.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e.throwing_class().size() == 0);
+ BOOST_CHECK(e.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_4a)
+{
+ cout << test_filename << ".constructor_4a: " << flush;
+ const u_int32_t err_code = 4;
+ const char* err_msg = "exception4";
+ try
+ {
+ jexception e4(err_code, err_msg);
+ BOOST_CHECK_EQUAL(e4.err_code(), err_code);
+ BOOST_CHECK(e4.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e4.throwing_class().size() == 0);
+ BOOST_CHECK(e4.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e4.what()) > 0);
+ throw e4;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), err_code);
+ BOOST_CHECK(e.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e.throwing_class().size() == 0);
+ BOOST_CHECK(e.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_4b)
+{
+ cout << test_filename << ".constructor_4b: " << flush;
+ const u_int32_t err_code = 4;
+ const string err_msg("exception4");
+ try
+ {
+ jexception e4(err_code, err_msg);
+ BOOST_CHECK_EQUAL(e4.err_code(), err_code);
+ BOOST_CHECK(e4.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e4.throwing_class().size() == 0);
+ BOOST_CHECK(e4.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e4.what()) > 0);
+ throw e4;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), err_code);
+ BOOST_CHECK(e.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e.throwing_class().size() == 0);
+ BOOST_CHECK(e.throwing_fn().size() == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_5a)
+{
+ cout << test_filename << ".constructor_5a: " << flush;
+ const u_int32_t err_code = 5;
+ const char* err_class = "class5";
+ const char* err_fn = "fn5";
+ try
+ {
+ jexception e5(err_code, err_class, err_fn);
+ BOOST_CHECK_EQUAL(e5.err_code(), err_code);
+ BOOST_CHECK(e5.additional_info().size() == 0);
+ BOOST_CHECK(e5.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e5.what()) > 0);
+ throw e5;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), err_code);
+ BOOST_CHECK(e.additional_info().size() == 0);
+ BOOST_CHECK(e.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_5b)
+{
+ cout << test_filename << ".constructor_5b: " << flush;
+ const u_int32_t err_code = 5;
+ const string err_class("class5");
+ const string err_fn("fn5");
+ try
+ {
+ jexception e5(err_code, err_class, err_fn);
+ BOOST_CHECK_EQUAL(e5.err_code(), err_code);
+ BOOST_CHECK(e5.additional_info().size() == 0);
+ BOOST_CHECK(e5.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e5.what()) > 0);
+ throw e5;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), err_code);
+ BOOST_CHECK(e.additional_info().size() == 0);
+ BOOST_CHECK(e.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_6a)
+{
+ cout << test_filename << ".constructor_6a: " << flush;
+ const u_int32_t err_code = 6;
+ const char* err_msg = "exception6";
+ const char* err_class = "class6";
+ const char* err_fn = "fn6";
+ try
+ {
+ jexception e6(err_code, err_msg, err_class, err_fn);
+ BOOST_CHECK_EQUAL(e6.err_code(), err_code);
+ BOOST_CHECK(e6.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e6.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e6.what()) > 0);
+ throw e6;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), err_code);
+ BOOST_CHECK(e.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_6b)
+{
+ cout << test_filename << ".constructor_6b: " << flush;
+ const u_int32_t err_code = 6;
+ const string err_msg("exception6");
+ const string err_class("class6");
+ const string err_fn("fn6");
+ try
+ {
+ jexception e6(err_code, err_msg, err_class, err_fn);
+ BOOST_CHECK_EQUAL(e6.err_code(), err_code);
+ BOOST_CHECK(e6.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e6.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e6.what()) > 0);
+ throw e6;
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), err_code);
+ BOOST_CHECK(e.additional_info().compare(err_msg) == 0);
+ BOOST_CHECK(e.throwing_class().compare(err_class) == 0);
+ BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0);
+ BOOST_CHECK(std::strlen(e.what()) > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(msg_scope)
+{
+ cout << test_filename << ".msg_scope: " << flush;
+ try
+ {
+ // These will go out of scope as soon as jexception is thrown...
+ const string msg("Error message");
+ const string cls("class");
+ const string fn("function");
+ throw jexception(100, msg, cls, fn);
+ }
+ catch (const jexception& e)
+ {
+ stringstream ss;
+ ss << e;
+ BOOST_CHECK(ss.str().size() > 0);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp
new file mode 100644
index 0000000000..f239139306
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp
@@ -0,0 +1,402 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cmath>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jcntl.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jinf_suite)
+
+const string test_filename("_ut_jinf");
+
+#include "_st_helper_fns.h"
+
+timespec ts;
+
+QPID_AUTO_TEST_CASE(write_constructor)
+{
+ string test_name = get_test_name(test_filename, "write_constructor");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ ::clock_gettime(CLOCK_REALTIME, &ts);
+ jinf ji(jid, test_dir, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts);
+ BOOST_CHECK_EQUAL(ji.jver(), RHM_JDAT_VERSION);
+ BOOST_CHECK(ji.jid().compare(jid) == 0);
+ BOOST_CHECK(ji.jdir().compare(test_dir) == 0);
+ BOOST_CHECK(ji.base_filename().compare(base_filename) == 0);
+ const timespec this_ts = ji.ts();
+ BOOST_CHECK_EQUAL(this_ts.tv_sec, ts.tv_sec);
+ BOOST_CHECK_EQUAL(this_ts.tv_nsec, ts.tv_nsec);
+ BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES));
+ BOOST_CHECK_EQUAL(ji.is_ae(), false);
+ BOOST_CHECK_EQUAL(ji.ae_max_jfiles(), u_int16_t(0));
+ BOOST_CHECK_EQUAL(ji.jfsize_sblks(), u_int32_t(JFSIZE_SBLKS));
+ BOOST_CHECK_EQUAL(ji.sblk_size_dblks(), u_int16_t(JRNL_SBLK_SIZE));
+ BOOST_CHECK_EQUAL(ji.dblk_size(), u_int32_t(JRNL_DBLK_SIZE));
+ BOOST_CHECK_EQUAL(ji.wcache_pgsize_sblks(), u_int32_t(JRNL_WMGR_DEF_PAGE_SIZE));
+ BOOST_CHECK_EQUAL(ji.wcache_num_pages(), u_int16_t(JRNL_WMGR_DEF_PAGES));
+ BOOST_CHECK_EQUAL(ji.rcache_pgsize_sblks(), u_int32_t(JRNL_RMGR_PAGE_SIZE));
+ BOOST_CHECK_EQUAL(ji.rcache_num_pages(), u_int16_t(JRNL_RMGR_PAGES));
+ ji.write();
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(read_constructor)
+{
+ string test_name = get_test_name(test_filename, "read_constructor");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map::create_new_jinf(jid, base_filename, false);
+
+ stringstream fn;
+ fn << test_dir << "/" <<base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ BOOST_CHECK_EQUAL(ji.jver(), RHM_JDAT_VERSION);
+ BOOST_CHECK(ji.jid().compare(jid) == 0);
+ BOOST_CHECK(ji.jdir().compare(test_dir) == 0);
+ BOOST_CHECK(ji.base_filename().compare(base_filename) == 0);
+// const timespec this_ts = ji.ts();
+// BOOST_CHECK_EQUAL(this_ts.tv_sec, ts.tv_sec);
+// BOOST_CHECK_EQUAL(this_ts.tv_nsec, ts.tv_nsec);
+ BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES));
+ BOOST_CHECK_EQUAL(ji.is_ae(), false);
+ BOOST_CHECK_EQUAL(ji.ae_max_jfiles(), u_int16_t(0));
+ BOOST_CHECK_EQUAL(ji.jfsize_sblks(), u_int32_t(JFSIZE_SBLKS));
+ BOOST_CHECK_EQUAL(ji.sblk_size_dblks(), u_int16_t(JRNL_SBLK_SIZE));
+ BOOST_CHECK_EQUAL(ji.dblk_size(), u_int32_t(JRNL_DBLK_SIZE));
+ BOOST_CHECK_EQUAL(ji.wcache_pgsize_sblks(), u_int32_t(JRNL_WMGR_DEF_PAGE_SIZE));
+ BOOST_CHECK_EQUAL(ji.wcache_num_pages(), u_int16_t(JRNL_WMGR_DEF_PAGES));
+ BOOST_CHECK_EQUAL(ji.rcache_pgsize_sblks(), u_int32_t(JRNL_RMGR_PAGE_SIZE));
+ BOOST_CHECK_EQUAL(ji.rcache_num_pages(), u_int16_t(JRNL_RMGR_PAGES));
+
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(set_functions)
+{
+ string test_name = get_test_name(test_filename, "set_functions");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map::create_new_jinf(jid, base_filename, false);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+
+ ji.set_jdir("abc123");
+ BOOST_CHECK(ji.jdir().compare("abc123") == 0);
+ ji.set_jdir(test_dir);
+ BOOST_CHECK(ji.jdir().compare(test_dir) == 0);
+ ji.incr_num_jfiles();
+ BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES+1));
+ ji.incr_num_jfiles();
+ BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES+2));
+
+ lfid_pfid_map::clean_journal_info_file(test_dir);
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(validate)
+{
+ string test_name = get_test_name(test_filename, "validate");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map::create_new_jinf(jid, base_filename, false);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), true);
+ // TODO: Check validation picks up conflict, but need to be friend to jinf to do it
+
+ lfid_pfid_map::clean_journal_info_file(test_dir);
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_empty_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_empty_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+
+ lfid_pfid_map m(jid, base_filename);
+ m.journal_create(NUM_JFILES, 0, 0);
+ m.write_journal(false, 0);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ try { ji.analyze(); }
+ catch (const jexception& e)
+ {
+ if (e.err_code() != jerrno::JERR_JINF_JDATEMPTY)
+ BOOST_ERROR("Failed to throw expected exception jerrno::JERR_JINF_JDATEMPTY");
+ }
+
+ m.destroy_journal();
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_part_full_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_part_full_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ for (u_int16_t num_files = 1; num_files < NUM_JFILES; num_files++)
+ {
+ m.journal_create(NUM_JFILES, num_files, 0);
+ m.write_journal(false, 0);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ ji.analyze();
+ m.check_analysis(ji);
+
+ m.destroy_journal();
+ }
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_full_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_full_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ for (u_int16_t file_num = 0; file_num < NUM_JFILES; file_num++)
+ {
+ m.journal_create(NUM_JFILES, NUM_JFILES, file_num);
+ m.write_journal(false, 0);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ ji.analyze();
+ m.check_analysis(ji);
+
+ m.destroy_journal();
+ }
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_single_appended_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_single_appended_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ for (u_int16_t oldest_lid = 0; oldest_lid < NUM_JFILES; oldest_lid++)
+ for (u_int16_t after_lid = 0; after_lid < NUM_JFILES; after_lid++)
+ for (u_int16_t num_files = 1; num_files <= 5; num_files++)
+ {
+ m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid);
+ m.journal_insert(after_lid, num_files);
+ m.write_journal(true, 16);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ ji.analyze();
+ m.check_analysis(ji);
+
+ m.destroy_journal();
+ }
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_multi_appended_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_multi_appended_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ ::srand48(1);
+
+ for (u_int16_t num_appends = 1; num_appends <= 2*NUM_JFILES; num_appends++)
+ {
+ const u_int16_t oldest_lid = u_int16_t(NUM_JFILES * ::drand48());
+ m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid);
+ for (u_int16_t a = 0; a < num_appends; a++)
+ {
+ const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48()));
+ const u_int16_t after_lid = u_int16_t(m.size() * ::drand48());
+ m.journal_insert(after_lid, num_files);
+ }
+ m.write_journal(true, 24);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ ji.analyze();
+ m.check_analysis(ji);
+
+ m.destroy_journal();
+ }
+
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_multi_appended_then_failed_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_multi_appended_then_failed_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ ::srand48(1);
+
+ // As this test relies on repeatable but random sequences, use many iterations for coverage
+ for (int c = 1; c <= 100; c++)
+ {
+ for (u_int16_t num_appends = 1; num_appends <= 2*NUM_JFILES; num_appends++)
+ {
+ u_int16_t oldest_lid = u_int16_t(NUM_JFILES * ::drand48());
+ m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid);
+ for (u_int16_t a = 0; a < num_appends-1; a++)
+ {
+ const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48()));
+ const u_int16_t after_lid = u_int16_t(m.size() * ::drand48());
+ m.journal_insert(after_lid, num_files);
+ if (after_lid < oldest_lid)
+ oldest_lid += num_files;
+ }
+ const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48()));
+ const u_int16_t after_lid = oldest_lid == 0 ? m.size() - 1 : oldest_lid - 1;
+ m.journal_insert(after_lid, num_files, false);
+ m.write_journal(true, 32);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ ji.analyze();
+ m.check_analysis(ji);
+
+ m.destroy_journal();
+ }
+ }
+
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_inconsistent_jdat_file_size_in_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_inconsistent_jdat_file_size_in_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ ::srand48(1);
+
+ for (u_int16_t pfid = 1; pfid < NUM_JFILES; pfid++)
+ {
+ m.journal_create(NUM_JFILES, NUM_JFILES, 0);
+ m.write_journal(false, 0);
+
+ const std::string filename = m.create_journal_filename(pfid, base_filename);
+ std::ofstream of(filename.c_str(), ofstream::out | ofstream::app);
+ if (!of.good())
+ BOOST_FAIL("Unable to open test journal file \"" << filename << "\" for writing.");
+ std::size_t expand_size = std::size_t(10 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE * ::drand48());
+ std::vector<char> sblk_buffer(expand_size, 0);
+ of.write(&sblk_buffer[0], expand_size);
+ of.close();
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ try
+ {
+ ji.analyze();
+ BOOST_FAIL("Failed to detect irregular journal file size in file \"" << filename << "\"");
+ }
+ catch (const jexception& e) {} // ignore - expected
+
+ m.destroy_journal();
+ }
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_owi_in_non_ae_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_owi_in_non_ae_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ for (u_int16_t oldest_file = 1; oldest_file < NUM_DEFAULT_JFILES-1; oldest_file++)
+ {
+ for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_DEFAULT_JFILES; bad_owi_file++)
+ {
+ m.journal_create(NUM_DEFAULT_JFILES, NUM_DEFAULT_JFILES, oldest_file, bad_owi_file);
+ m.write_journal(false, 0);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ try
+ {
+ ji.analyze();
+ BOOST_FAIL("Failed to detect irregular OWI flag in non-ae journal file \"" << fn.str() << "\"");
+ }
+ catch (const jexception& e) {} // ignore - expected
+
+ m.destroy_journal();
+ }
+ }
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_CASE(analyze_owi_in_ae_min_size_journal)
+{
+ string test_name = get_test_name(test_filename, "analyze_owi_in_ae_min_size_journal");
+ const string jid = test_name + "_jid";
+ const string base_filename = test_name + "_bfn";
+ lfid_pfid_map m(jid, base_filename);
+ for (u_int16_t oldest_file = 1; oldest_file < NUM_JFILES-1; oldest_file++)
+ {
+ for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_JFILES; bad_owi_file++)
+ {
+ m.journal_create(NUM_JFILES, NUM_JFILES, oldest_file, bad_owi_file);
+ m.write_journal(true, 16);
+
+ stringstream fn;
+ fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION;
+ jinf ji(fn.str(), false);
+ try
+ {
+ ji.analyze();
+ BOOST_FAIL("Failed to detect irregular OWI flag in min-sized ae journal file \"" << fn.str() << "\"");
+ }
+ catch (const jexception& e) {} // ignore - expected
+
+ m.destroy_journal();
+ }
+ }
+ cout << "done" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp
new file mode 100644
index 0000000000..2dc20ffa7c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp
@@ -0,0 +1,886 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cmath>
+#include <iostream>
+#include "qpid/legacystore/jrnl/jcntl.h"
+#include "qpid/legacystore/jrnl/lpmgr.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(arr_cnt_suite)
+
+const string test_filename("_ut_lpmgr");
+
+#include "_st_helper_fns.h"
+
+// === Helper functions and definitions ===
+
+typedef vector<u_int16_t> flist;
+typedef flist::const_iterator flist_citr;
+
+class lpmgr_test_helper
+{
+ lpmgr_test_helper() {}
+ virtual ~lpmgr_test_helper() {}
+
+public:
+ static void check_pfids_lfids(const lpmgr& lm, const u_int16_t pfids[], const u_int16_t lfids[],
+ const size_t pfid_lfid_size)
+ {
+ vector<u_int16_t> res;
+ lm.get_pfid_list(res);
+ vectors_equal(lm, pfids, pfid_lfid_size, res, true);
+ lm.get_lfid_list(res);
+ vectors_equal(lm, lfids, pfid_lfid_size, res, false);
+ }
+
+ static void check_pfids_lfids(const lpmgr& lm, const flist& pfids, const flist lfids)
+ {
+ vector<u_int16_t> res;
+ lm.get_pfid_list(res);
+ vectors_equal(lm, pfids, res, true);
+ lm.get_lfid_list(res);
+ vectors_equal(lm, lfids, res, false);
+ }
+
+ static void check_linear_pfids_lfids(const lpmgr& lm, const size_t pfid_lfid_size)
+ {
+ vector<u_int16_t> res;
+ lm.get_pfid_list(res);
+ linear_vectors_equal(lm, pfid_lfid_size, res, true);
+ lm.get_lfid_list(res);
+ linear_vectors_equal(lm, pfid_lfid_size, res, false);
+ }
+
+ static void rcvdat_init(rcvdat& rd, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles,
+ const u_int16_t pfids[])
+ {
+ rd.reset(num_jfiles, ae, ae_max_jfiles);
+ load_vector(pfids, num_jfiles, rd._fid_list);
+ rd._jempty = false;
+ rd._lfid = pfids[num_jfiles - 1];
+ rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE;
+ }
+
+ static void rcvdat_init(rcvdat& rd, const flist& pfidl, const bool ae, const u_int16_t ae_max_jfiles)
+ {
+ const u_int16_t num_jfiles = pfidl.size();
+ rd.reset(num_jfiles, ae, ae_max_jfiles);
+ load_vector(pfidl, rd._fid_list);
+ rd._jempty = false;
+ rd._lfid = pfidl[num_jfiles - 1];
+ rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE;
+ }
+
+ static void initialize(lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae,
+ const u_int16_t ae_max_jfiles)
+ {
+ lm.initialize(num_jfiles, ae, ae_max_jfiles, &jc, &jc.new_fcntl);
+ BOOST_CHECK_EQUAL(lm.is_init(), true);
+ BOOST_CHECK_EQUAL(lm.is_ae(), ae);
+ BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles);
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles);
+ if (num_jfiles)
+ check_linear_pfids_lfids(lm, num_jfiles);
+ else
+ BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0);
+ }
+
+ // version which sets up the lfid_pfid_map for later manipulation by insert tests
+ static void initialize(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae,
+ const u_int16_t ae_max_jfiles)
+ {
+ lfm.journal_create(num_jfiles, num_jfiles);
+ initialize(lm, jc, num_jfiles, ae, ae_max_jfiles);
+ }
+
+ static void prepare_recover(lfid_pfid_map& lfm, const u_int16_t size)
+ {
+ if (size < 4) BOOST_FAIL("prepare_recover(): size parameter (" << size << ") too small.");
+ lfm.journal_create(4, 4); // initial journal of size 4
+ u_int16_t s = 4; // cumulative size
+ while (s < size)
+ {
+ const u_int16_t ins_posn = u_int16_t(s * ::drand48()); // this insert posn
+ if (3.0 * ::drand48() > 1.0 || size - s < 2) // 2:1 chance of single insert when >= 2 still to insert
+ {
+ lfm.journal_insert(ins_posn); // single insert
+ s++;
+ }
+ else
+ {
+ // multiple insert, either 2 - 5
+ const u_int16_t max_ins_size = size - s >5 ? 5 : size - s;
+ const u_int16_t ins_size = 2 + u_int16_t((max_ins_size - 2) * ::drand48()); // this insert size
+ lfm.journal_insert(ins_posn, ins_size);
+ s += ins_size;
+ }
+ }
+ }
+
+ static void recover(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const bool ae, const u_int16_t ae_max_jfiles)
+ {
+ flist pfidl;
+ flist lfidl;
+ rcvdat rd;
+ const u_int16_t num_jfiles = lfm.size();
+
+ lfm.get_pfid_list(pfidl);
+ lfm.get_lfid_list(lfidl);
+ lm.finalize(); // clear all file handles before erasing old journal files
+ lfm.write_journal(ae, ae_max_jfiles, JFSIZE_SBLKS);
+
+ lpmgr_test_helper::rcvdat_init(rd, pfidl, ae, ae_max_jfiles);
+ lm.recover(rd, &jc, &jc.new_fcntl);
+ BOOST_CHECK_EQUAL(lm.is_init(), true);
+ BOOST_CHECK_EQUAL(lm.is_ae(), ae);
+ BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles);
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles);
+ if (num_jfiles)
+ check_pfids_lfids(lm, pfidl, lfidl);
+ else
+ BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0);
+ }
+
+ static void finalize(lpmgr& lm)
+ {
+ lm.finalize();
+ BOOST_CHECK_EQUAL(lm.is_init(), false);
+ BOOST_CHECK_EQUAL(lm.is_ae(), false);
+ BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0));
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0));
+ BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0);
+ vector<u_int16_t> res;
+ lm.get_pfid_list(res);
+ BOOST_CHECK_EQUAL(res.size(), u_int16_t(0));
+ lm.get_lfid_list(res);
+ BOOST_CHECK_EQUAL(res.size(), u_int16_t(0));
+ }
+
+ static void insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid, const u_int16_t incr = 1)
+ {
+ flist pfidl;
+ flist lfidl;
+ const u_int16_t num_jfiles = lm.num_jfiles();
+ lfm.journal_insert(after_lfid, incr);
+ lfm.get_pfid_list(pfidl);
+ lfm.get_lfid_list(lfidl);
+ lm.insert(after_lfid, &jc, &jc.new_fcntl, incr);
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr);
+ lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl);
+ }
+
+ static void check_ae_max_jfiles(lpmgr& lm, const u_int16_t num_jfiles, const u_int16_t ae_max_jfiles)
+ {
+ bool legal = ae_max_jfiles > num_jfiles || ae_max_jfiles == 0;
+
+ lm.set_ae(false);
+ BOOST_CHECK(!lm.is_ae());
+ if (legal)
+ {
+ lm.set_ae_max_jfiles(ae_max_jfiles);
+ BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles);
+ lm.set_ae(true);
+ BOOST_CHECK(lm.is_ae());
+ BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), ae_max_jfiles
+ ? ae_max_jfiles - num_jfiles
+ : JRNL_MAX_NUM_FILES - num_jfiles);
+ }
+ else
+ {
+ lm.set_ae_max_jfiles(ae_max_jfiles);
+ BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles);
+ try
+ {
+ lm.set_ae(true); // should raise exception
+ BOOST_ERROR("Auto-expand enabled with out-of-range ae_max_jfiles");
+ }
+ catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_BADAEFNUMLIM); }
+ BOOST_CHECK(!lm.is_ae());
+ BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), 0);
+ }
+ BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles);
+ }
+
+ static void check_multiple_initialization_recover(lfid_pfid_map& lfm, test_jrnl& jc,
+ const u_int16_t num_jfiles_arr[][2], const bool init_flag_0, const bool finalize_flag,
+ const bool init_flag_1)
+ {
+ unsigned i_njf = 0;
+ while (num_jfiles_arr[i_njf][0] && num_jfiles_arr[i_njf][1]) // cycle through each entry in num_jfiles_arr
+ {
+ for (unsigned i1_njf = 0; i1_njf <= 1; i1_njf++) // cycle through the two numbers in each entry of num_jfiles_arr
+ {
+ const u_int16_t num_jfiles_0 = num_jfiles_arr[i_njf][i1_njf == 0]; // first number in pair
+ const u_int16_t num_jfiles_1 = num_jfiles_arr[i_njf][i1_njf != 0]; // second number in pair
+
+ for (unsigned i_ae = 0; i_ae < 4; i_ae++) // cycle through combinations of enabling AE
+ {
+ const bool ae_0 = i_ae & 0x1; // first bit: enable AE on first init
+ const bool ae_1 = i_ae & 0x2; // second bit: enable AE on second init
+ for (unsigned i_aemjf = 0; i_aemjf < 4; i_aemjf++) // cycle through combinations of enabling/disabling ae limit
+ {
+ const u_int16_t ae_max_jfiles_0 = i_aemjf & 0x1 ? 3 * num_jfiles_0 : 0; // max ae files, 0 = disable max
+ const u_int16_t ae_max_jfiles_1 = i_aemjf & 0x2 ? 4 * num_jfiles_1 : 0; // max ae files, 0 = disable max
+
+ lpmgr lm; // DUT
+
+ if (init_flag_0)
+ initialize(lm, jc, num_jfiles_0, ae_0, ae_max_jfiles_0);
+ else
+ {
+ prepare_recover(lfm, num_jfiles_0);
+ recover(lfm, lm, jc, ae_1, ae_max_jfiles_0);
+ lfm.destroy_journal();
+ }
+
+ if (finalize_flag) finalize(lm);
+
+ if (init_flag_1)
+ initialize(lm, jc, num_jfiles_1, ae_1, ae_max_jfiles_1);
+ else
+ {
+ prepare_recover(lfm, num_jfiles_1);
+ recover(lfm, lm, jc, ae_1, ae_max_jfiles_1);
+ lfm.destroy_journal();
+ }
+ }
+ }
+ }
+ i_njf++;
+ }
+ }
+
+ static void check_insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid,
+ const u_int16_t incr = 1)
+ {
+ const u_int16_t num_jfiles = lm.num_jfiles();
+ const u_int16_t ae_max_jfiles = lm.ae_max_jfiles();
+ const u_int16_t effective_ae_max_jfiles = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES;
+ BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles);
+ bool legal = lm.is_ae() && num_jfiles + incr <= effective_ae_max_jfiles;
+ if (legal)
+ {
+ insert(lfm, lm, jc, after_lfid, incr);
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr);
+ BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles - incr);
+ }
+ else
+ {
+ try
+ {
+ insert(lfm, lm, jc, after_lfid, incr);
+ if (lm.is_ae())
+ BOOST_ERROR("lpmgr::insert() succeeded and exceeded limit");
+ else
+ BOOST_ERROR("lpmgr::insert() succeeded with auto-expand disabled");
+ }
+ catch (const jexception& e)
+ {
+ if (lm.is_ae())
+ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT);
+ else
+ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEDISABLED);
+ }
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles);
+ BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles);
+ }
+ }
+
+ static void check_limit(lfid_pfid_map& lfm, test_jrnl& jc, const bool ae, const u_int16_t num_jfiles,
+ const u_int16_t ae_max_jfiles)
+ {
+ lpmgr lm;
+
+ for (unsigned i = 0; i < 2; i++)
+ {
+ if (i)
+ initialize(lfm, lm, jc, num_jfiles, ae, ae_max_jfiles);
+ else
+ {
+ prepare_recover(lfm, num_jfiles);
+ recover(lfm, lm, jc, ae, ae_max_jfiles);
+ }
+
+ // use up all available files
+ unsigned j = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES;
+ while (ae && j > num_jfiles)
+ {
+ const u_int16_t posn = static_cast<u_int16_t>((lm.num_jfiles() - 1) * ::drand48());
+ const u_int16_t incr = 1 + static_cast<u_int16_t>((lm.ae_jfiles_rem() > 4
+ ? 3 : lm.ae_jfiles_rem() - 1) * ::drand48());
+ check_insert(lfm, lm, jc, posn, incr);
+ j -= incr;
+ }
+ // these should be over the limit or illegal
+ check_insert(lfm, lm, jc, 0);
+ check_insert(lfm, lm, jc, 2, 2);
+ lfm.destroy_journal();
+ }
+ }
+
+private:
+ static void load_vector(const u_int16_t a[], const size_t n, flist& v)
+ {
+ for (size_t i = 0; i < n; i++)
+ v.push_back(a[i]);
+ }
+
+ static void load_vector(const flist& a, flist& b)
+ {
+ for (flist_citr i = a.begin(); i < a.end(); i++)
+ b.push_back(*i);
+ }
+
+ static void vectors_equal(const lpmgr& lm, const u_int16_t a[], const size_t n, const flist& b,
+ const bool pfid_check)
+ {
+ BOOST_CHECK_EQUAL(n, b.size());
+ for (size_t i = 0; i < n; i++)
+ {
+ BOOST_CHECK_EQUAL(a[i], b[i]);
+ fcntl* fp = lm.get_fcntlp(i);
+ BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()");
+ if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i);
+ }
+ }
+
+ static void vectors_equal(const lpmgr& lm, const flist& a, const flist& b, const bool pfid_check)
+ {
+ BOOST_CHECK_EQUAL(a.size(), b.size());
+ for (size_t i = 0; i < a.size(); i++)
+ {
+ BOOST_CHECK_EQUAL(a[i], b[i]);
+ fcntl* fp = lm.get_fcntlp(i);
+ BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()");
+ if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i);
+ }
+ }
+
+ static void linear_vectors_equal(const lpmgr& lm, const size_t n, const flist& f, const bool pfid_check)
+ {
+ BOOST_CHECK_EQUAL(n, f.size());
+ for (size_t i = 0; i < n; i++)
+ {
+ BOOST_CHECK_EQUAL(i, f[i]);
+ fcntl* fp = lm.get_fcntlp(i);
+ BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()");
+ if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), i);
+ }
+ }
+};
+
+// === Tests ===
+
+#ifndef LONG_TEST
+/*
+ * ==============================================
+ * NORMAL TESTS
+ * This section contains normal "make check" tests
+ * for building/packaging. These are built when
+ * LONG_TEST is _not_ defined.
+ * ==============================================
+ */
+
+/*
+ * Check that after construction, the fcntl array _fcntl_arr is empty and the is_init() function returns false.
+ */
+QPID_AUTO_TEST_CASE(default_constructor)
+{
+ string test_name = get_test_name(test_filename, "default_constructor");
+ try
+ {
+ lpmgr lm;
+ BOOST_CHECK_EQUAL(lm.is_init(), false);
+ BOOST_CHECK_EQUAL(lm.is_ae(), false);
+ BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0));
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0));
+ BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that initialize() correctly creates an ordered fcntl array _fcntl_arr.
+ */
+QPID_AUTO_TEST_CASE(initialize)
+{
+ string test_name = get_test_name(test_filename, "initialize");
+ const u_int16_t num_jfiles = 8;
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ {
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0);
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0);
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that recover() correctly sets up the specified pfid list order.
+ */
+QPID_AUTO_TEST_CASE(recover)
+{
+ string test_name = get_test_name(test_filename, "recover");
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+ try
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+
+ {
+ lpmgr lm;
+ lpmgr_test_helper::prepare_recover(lfm, 8);
+ lpmgr_test_helper::recover(lfm, lm, jc, false, 0);
+ lfm.destroy_journal();
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::prepare_recover(lfm, 8);
+ lpmgr_test_helper::recover(lfm, lm, jc, true, 0);
+ lfm.destroy_journal();
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::prepare_recover(lfm, 8);
+ lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size());
+ lfm.destroy_journal();
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that finalize() after an initialize() empties _fcntl_arr and that afterwards is_init() returns false.
+ */
+QPID_AUTO_TEST_CASE(initialize_finalize)
+{
+ string test_name = get_test_name(test_filename, "initialize_finalize");
+ const u_int16_t num_jfiles = 8;
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ {
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0);
+ lpmgr_test_helper::finalize(lm);
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0);
+ lpmgr_test_helper::finalize(lm);
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles);
+ lpmgr_test_helper::finalize(lm);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that finalize() after a recover() empties _fcntl_arr and that afterwards is_init() returns false.
+ */
+QPID_AUTO_TEST_CASE(recover_finalize)
+{
+ string test_name = get_test_name(test_filename, "recover_finalize");
+ const u_int16_t num_jfiles = 8;
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+ try
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+
+ {
+ lpmgr lm;
+ lpmgr_test_helper::prepare_recover(lfm, num_jfiles);
+ lpmgr_test_helper::recover(lfm, lm, jc, false, 0);
+ lpmgr_test_helper::finalize(lm);
+ lfm.destroy_journal();
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::prepare_recover(lfm, num_jfiles);
+ lpmgr_test_helper::recover(lfm, lm, jc, true, 0);
+ lpmgr_test_helper::finalize(lm);
+ lfm.destroy_journal();
+ }
+ {
+ lpmgr lm;
+ lpmgr_test_helper::prepare_recover(lfm, num_jfiles);
+ lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size());
+ lpmgr_test_helper::finalize(lm);
+ lfm.destroy_journal();
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that 0 and/or null and other extreme/boundary parameters behave as expected.
+ */
+QPID_AUTO_TEST_CASE(zero_null_params)
+{
+ string test_name = get_test_name(test_filename, "zero_null_params");
+ const u_int16_t num_jfiles = 8;
+ try
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, true, 0);
+
+ // Check that inserting 0 files works ok
+ lpmgr_test_helper::insert(lfm, lm, jc, 0, 0);
+ lpmgr_test_helper::insert(lfm, lm, jc, 2, 0);
+ lpmgr_test_helper::insert(lfm, lm, jc, num_jfiles - 1, 0);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that initialize()/recover() works correctly after a previous initialize()/recover() with/without an intervening
+ * finalize().
+ */
+QPID_AUTO_TEST_CASE(multiple_initialization_recover)
+{
+ string test_name = get_test_name(test_filename, "multiple_initialization_recover");
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+
+ // Set combinations of value pairs to be used for number of journal files in first and second init
+ u_int16_t num_jfiles_arr[][2] = {{8, 12}, {4, 7}, {0, 0}}; // end with zeros
+ try
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ for (unsigned p = 0; p < 8; p++)
+ {
+ const bool i_0 = p & 0x01; // first bit
+ const bool i_1 = p & 0x02; // second bit
+ const bool f = p & 0x04; // third bit
+ lpmgr_test_helper::check_multiple_initialization_recover(lfm, jc, num_jfiles_arr, i_0, f, i_1);
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that insert() works correctly after initialize() and shifts the pfid sequence beyond the insert point correctly:
+ *
+ * The following sequence is tested:
+ * initialize 4 pfids=[0,1,2,3] lfids=[0,1,2,3]
+ * insert 1 after lfid 0 pfids=[0,4,1,2,3] lfids=[0,2,3,4,1]
+ * insert 2 after lfid 2 pfids=[0,4,1,5,6,2,3] lfids=[0,2,5,6,1,3,4]
+ * insert 1 after lfid 6 pfids=[0,4,1,5,6,2,3,7] lfids=[0,2,5,6,1,3,4,7]
+ * issert 1 after lfid 3 pfids=[0,4,1,5,8,6,2,3,7] lfids=[0,2,6,7,1,3,5,8,4]
+ */
+QPID_AUTO_TEST_CASE(initialize_insert)
+{
+ string test_name = get_test_name(test_filename, "initialize_insert");
+ const u_int16_t initial_num_jfiles = 8;
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ lpmgr lm;
+ lpmgr_test_helper::initialize(lfm, lm, jc, initial_num_jfiles, true, 0);
+
+ lpmgr_test_helper::insert(lfm, lm, jc, 0);
+ lpmgr_test_helper::insert(lfm, lm, jc, 2, 2);
+ lpmgr_test_helper::insert(lfm, lm, jc, 6);
+ lpmgr_test_helper::insert(lfm, lm, jc, 3);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that insert() works correctly after recover() and shifts the pfid sequence beyond the insert point correctly:
+ *
+ * The following sequence is tested:
+ * recover 4 pfids=[0,2,3,1] lfids=[0,3,1,2]
+ * insert 1 after lfid 0 pfids=[0,4,2,3,1] lfids=[0,4,2,3,1]
+ * insert 2 after lfid 2 pfids=[0,4,2,5,6,3,1] lfids=[0,6,2,5,1,3,4]
+ * insert 1 after lfid 6 pfids=[0,4,2,5,6,3,1,7] lfids=[0,6,2,5,1,3,4,7]
+ * issert 1 after lfid 3 pfids=[0,4,2,5,8,6,3,1,7] lfids=[0,7,2,6,1,3,5,8,4]
+ */
+QPID_AUTO_TEST_CASE(recover_insert)
+{
+ string test_name = get_test_name(test_filename, "recover_insert");
+ const u_int16_t initial_num_jfiles = 4;
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ lpmgr lm;
+ lpmgr_test_helper::prepare_recover(lfm, initial_num_jfiles);
+ lpmgr_test_helper::recover(lfm, lm, jc, true, 0);
+
+ lpmgr_test_helper::insert(lfm, lm, jc, 0);
+ lpmgr_test_helper::insert(lfm, lm, jc, 2, 2);
+ lpmgr_test_helper::insert(lfm, lm, jc, 6);
+ lpmgr_test_helper::insert(lfm, lm, jc, 3);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that illegal ae parameter combinations are caught and result in an exception being thrown.
+ */
+QPID_AUTO_TEST_CASE(ae_parameters)
+{
+ string test_name = get_test_name(test_filename, "ae_parameters");
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ const u_int16_t num_jfiles = 8;
+ lpmgr lm;
+
+ for (unsigned i = 0; i < 2; i++)
+ {
+ if (i)
+ lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, false, 0);
+ else
+ {
+ lpmgr_test_helper::prepare_recover(lfm, num_jfiles);
+ lpmgr_test_helper::recover(lfm, lm, jc, false, 0);
+ }
+
+ lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles - 2);
+ lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 0);
+ lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 2 * num_jfiles);
+ lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles);
+ lfm.destroy_journal();
+ }
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that initialized or recovered journals with auto-expand disabled will not allow either inserts or appends.
+ */
+QPID_AUTO_TEST_CASE(ae_disabled)
+{
+ string test_name = get_test_name(test_filename, "ae_disabled");
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ lpmgr_test_helper::check_limit(lfm, jc, false, 8, 0);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that initialized or recovered journals with auto-expand enabled and a file limit set will enforce the correct
+ * limits on inserts and appends.
+ */
+QPID_AUTO_TEST_CASE(ae_enabled_limit)
+{
+ string test_name = get_test_name(test_filename, "ae_enabled_limit");
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ lpmgr_test_helper::check_limit(lfm, jc, true, 8, 32);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+/*
+ * Check that initialized or recovered journals with auto-expand enabled and no file limit set (0) will allow inserts and
+ * appends up to the file limit JRNL_MAX_NUM_FILES.
+ */
+QPID_AUTO_TEST_CASE(ae_enabled_unlimited)
+{
+ string test_name = get_test_name(test_filename, "ae_enabled_unlimited");
+ ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover()
+ try
+ {
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lfid_pfid_map lfm(test_name, test_name);
+ lpmgr_test_helper::check_limit(lfm, jc, true, 8, 0);
+ }
+ catch(const exception& e) { BOOST_FAIL(e.what()); }
+ cout << "done" << endl;
+}
+
+#else
+/*
+ * ==============================================
+ * LONG TESTS
+ * This section contains long tests and soak tests,
+ * and are run using target check-long (ie "make
+ * check-long"). These are built when LONG_TEST is
+ * defined.
+ * ==============================================
+ */
+
+/*
+ * Tests randomized combinations of initialization/recovery, initial size, number, size and location of inserts.
+ *
+ * To reproduce a specific test, comment out the get_seed() statement and uncomment the literal below, adjusting the seed
+ * value to that required.
+ */
+QPID_AUTO_TEST_CASE(randomized_tests)
+{
+ string test_name = get_test_name(test_filename, "randomized_tests");
+ const long seed = get_seed();
+ // const long seed = 0x2d9b69d32;
+ cout << "seed=0x" << hex << seed << dec << " " << flush;
+ ::srand48(seed);
+
+ lfid_pfid_map lfm(test_name, test_name);
+ flist pfidl;
+ flist lfidl;
+ rcvdat rd;
+ u_int16_t curr_ae_max_jfiles = 0;
+ jdir::create_dir(test_dir); // Check test dir exists; create it if not
+
+ for (int test_num = 0; test_num < 250; test_num++)
+ {
+ test_jrnl_cb cb;
+ test_jrnl jc(test_name, test_dir, test_name, cb);
+ lpmgr lm;
+ // 50% chance of recovery except first run and if there is still ae space left
+ const bool recover_flag = test_num > 0 &&
+ curr_ae_max_jfiles > lfm.size() &&
+ 2.0 * ::drand48() < 1.0;
+ if (recover_flag)
+ {
+ // Recover from previous iteration
+ lfm.get_pfid_list(pfidl);
+ lfm.get_lfid_list(lfidl);
+ lfm.write_journal(true, curr_ae_max_jfiles, JFSIZE_SBLKS);
+ lpmgr_test_helper::rcvdat_init(rd, pfidl, true, curr_ae_max_jfiles);
+ lm.recover(rd, &jc, &jc.new_fcntl);
+ lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl);
+ }
+ else
+ {
+ // Initialize from scratch
+ const u_int16_t num_jfiles = 4 + u_int16_t(21.0 * ::drand48()); // size: 4 - 25 files
+ curr_ae_max_jfiles = u_int16_t(4 * num_jfiles * ::drand48()); // size: 0 - 100 files
+ if (curr_ae_max_jfiles > JRNL_MAX_NUM_FILES) curr_ae_max_jfiles = JRNL_MAX_NUM_FILES;
+ else if (curr_ae_max_jfiles <= num_jfiles) curr_ae_max_jfiles = 0;
+ lfm.destroy_journal();
+ lfm.journal_create(num_jfiles, num_jfiles);
+ lfm.get_pfid_list(pfidl);
+ lfm.get_lfid_list(lfidl);
+ lm.initialize(num_jfiles, true, curr_ae_max_jfiles, &jc, &jc.new_fcntl);
+ lpmgr_test_helper::check_linear_pfids_lfids(lm, num_jfiles);
+ }
+
+ // Loop to insert pfids
+ const int num_inserts = 1 + int(lfm.size() * ::drand48());
+ for (int i = 0; i < num_inserts; i++)
+ {
+ const u_int16_t size = lm.num_jfiles();
+ const u_int16_t after_lfid = u_int16_t(1.0 * size * ::drand48());
+ const u_int16_t num_jfiles = 1 + u_int16_t(4.0 * ::drand48());
+ const bool legal = lm.ae_max_jfiles()
+ ? size + num_jfiles <= lm.ae_max_jfiles()
+ : size + num_jfiles <= JRNL_MAX_NUM_FILES;
+ if (legal)
+ {
+ lfm.journal_insert(after_lfid, num_jfiles);
+ lfm.get_pfid_list(pfidl);
+ lfm.get_lfid_list(lfidl);
+
+ lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles);
+ lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl);
+ }
+ else
+ {
+ try
+ {
+ lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles);
+ BOOST_FAIL("lpmgr::insert() succeeded and exceeded limit");
+ }
+ catch (const jexception& e)
+ {
+ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT);
+ break; // no more inserts...
+ }
+ }
+ }
+ lm.finalize();
+ BOOST_CHECK_EQUAL(lm.is_init(), false);
+ BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0));
+ BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0);
+ }
+ cout << "done" << endl;
+}
+
+#endif
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp
new file mode 100644
index 0000000000..099e576bbd
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp
@@ -0,0 +1,438 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <ctime>
+#include <iostream>
+#include "qpid/legacystore/jrnl/deq_hdr.h"
+#include "qpid/legacystore/jrnl/enq_hdr.h"
+#include "qpid/legacystore/jrnl/file_hdr.h"
+#include "qpid/legacystore/jrnl/jcfg.h"
+#include "qpid/legacystore/jrnl/rec_tail.h"
+#include "qpid/legacystore/jrnl/txn_hdr.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(rec_hdr_suite)
+
+const string test_filename("_ut_rec_hdr");
+
+QPID_AUTO_TEST_CASE(hdr_class)
+{
+ cout << test_filename << ".hdr_class: " << flush;
+ rec_hdr h1;
+ BOOST_CHECK_EQUAL(h1._magic, 0UL);
+ BOOST_CHECK_EQUAL(h1._version, 0);
+ BOOST_CHECK_EQUAL(h1._eflag, 0);
+ BOOST_CHECK_EQUAL(h1._uflag, 0);
+ BOOST_CHECK_EQUAL(h1._rid, 0ULL);
+ BOOST_CHECK(!h1.get_owi());
+
+ const u_int32_t magic = 0x89abcdefUL;
+ const u_int16_t uflag = 0x5537;
+ const u_int8_t version = 0xef;
+ const u_int64_t rid = 0x123456789abcdef0ULL;
+ const bool owi = true;
+
+ rec_hdr h2(magic, version, rid, owi);
+ BOOST_CHECK_EQUAL(h2._magic, magic);
+ BOOST_CHECK_EQUAL(h2._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(h2._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(h2._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(h2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK);
+ BOOST_CHECK_EQUAL(h2._rid, rid);
+ BOOST_CHECK_EQUAL(h2.get_owi(), owi);
+ h2._uflag = uflag;
+ BOOST_CHECK(h2.get_owi());
+ h2.set_owi(true);
+ BOOST_CHECK(h2.get_owi());
+ BOOST_CHECK_EQUAL(h2._uflag, uflag);
+ h2.set_owi(false);
+ BOOST_CHECK(!h2.get_owi());
+ BOOST_CHECK_EQUAL(h2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK));
+ h2.set_owi(true);
+ BOOST_CHECK(h2.get_owi());
+ BOOST_CHECK_EQUAL(h2._uflag, uflag);
+
+ h1.hdr_copy(h2);
+ BOOST_CHECK_EQUAL(h1._magic, magic);
+ BOOST_CHECK_EQUAL(h1._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(h1._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(h1._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(h1._uflag, uflag);
+ BOOST_CHECK_EQUAL(h1._rid, rid);
+ BOOST_CHECK(h1.get_owi());
+ BOOST_CHECK_EQUAL(h1._uflag, uflag);
+
+ h1.reset();
+ BOOST_CHECK_EQUAL(h1._magic, 0UL);
+ BOOST_CHECK_EQUAL(h1._version, 0);
+ BOOST_CHECK_EQUAL(h1._eflag, 0);
+ BOOST_CHECK_EQUAL(h1._uflag, 0);
+ BOOST_CHECK_EQUAL(h1._rid, 0ULL);
+ BOOST_CHECK(!h1.get_owi());
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(rec_tail_class)
+{
+ cout << test_filename << ".rec_tail_class: " << flush;
+ const u_int32_t magic = 0xfedcba98;
+ const u_int64_t rid = 0xfedcba9876543210ULL;
+ const u_int32_t xmagic = ~magic;
+
+ {
+ rec_tail rt1;
+ BOOST_CHECK_EQUAL(rt1._xmagic, 0xffffffffUL);
+ BOOST_CHECK_EQUAL(rt1._rid, 0ULL);
+ }
+
+ {
+ rec_tail rt2(magic, rid);
+ BOOST_CHECK_EQUAL(rt2._xmagic, magic);
+ BOOST_CHECK_EQUAL(rt2._rid, rid);
+ }
+
+ {
+ rec_hdr h(magic, RHM_JDAT_VERSION, rid, true);
+ rec_tail rt3(h);
+ BOOST_CHECK_EQUAL(rt3._xmagic, xmagic);
+ BOOST_CHECK_EQUAL(rt3._rid, rid);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(file_hdr_class)
+{
+ cout << test_filename << ".file_hdr_class: " << flush;
+ const u_int32_t magic = 0xfedcba98UL;
+ const u_int8_t version = 0xa5;
+ const u_int16_t uflag = 0x5537;
+ const u_int64_t rid = 0xfedcba9876543210ULL;
+ const u_int16_t pfid = 0xfedcU;
+ const u_int16_t lfid = 0xf0e1U;
+#ifdef JRNL_32_BIT
+ const std::size_t fro = 0xfedcba98UL;
+#else
+ const std::size_t fro = 0xfedcba9876543210ULL;
+#endif
+ timespec ts;
+ const bool owi = true;
+
+ {
+ file_hdr fh1;
+ BOOST_CHECK_EQUAL(fh1._magic, 0UL);
+ BOOST_CHECK_EQUAL(fh1._version, 0);
+ BOOST_CHECK_EQUAL(fh1._eflag, 0);
+ BOOST_CHECK_EQUAL(fh1._uflag, 0);
+ BOOST_CHECK_EQUAL(fh1._rid, 0ULL);
+ BOOST_CHECK_EQUAL(fh1._pfid, 0UL);
+ BOOST_CHECK_EQUAL(fh1._lfid, 0U);
+ BOOST_CHECK_EQUAL(fh1._fro, std::size_t(0));
+ BOOST_CHECK_EQUAL(fh1._ts_sec, std::time_t(0));
+ BOOST_CHECK_EQUAL(fh1._ts_nsec, u_int32_t(0));
+ BOOST_CHECK(!fh1.get_owi());
+ }
+
+ {
+ file_hdr fh2(magic, version, rid, pfid, lfid, fro, owi, false);
+ BOOST_CHECK_EQUAL(fh2._magic, magic);
+ BOOST_CHECK_EQUAL(fh2._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(fh2._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(fh2._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(fh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK);
+ BOOST_CHECK_EQUAL(fh2._rid, rid);
+ BOOST_CHECK_EQUAL(fh2._pfid, pfid );
+ BOOST_CHECK_EQUAL(fh2._lfid, lfid);
+ BOOST_CHECK_EQUAL(fh2._fro, fro);
+ BOOST_CHECK_EQUAL(fh2._ts_sec, std::time_t(0));
+ BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(0));
+ ::clock_gettime(CLOCK_REALTIME, &ts);
+ fh2.set_time(ts);
+ BOOST_CHECK_EQUAL(fh2._ts_sec, ts.tv_sec);
+ BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(ts.tv_nsec));
+ BOOST_CHECK(fh2.get_owi());
+
+ fh2._uflag = uflag;
+ BOOST_CHECK(fh2.get_owi());
+
+ fh2.set_owi(false);
+ BOOST_CHECK(!fh2.get_owi());
+ BOOST_CHECK_EQUAL(fh2._uflag,
+ (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK));
+
+ fh2.set_owi(true);
+ BOOST_CHECK(fh2.get_owi());
+ BOOST_CHECK_EQUAL(fh2._uflag, uflag);
+ }
+
+ {
+ file_hdr fh3(magic, version, rid, pfid, lfid, fro, owi, true);
+ BOOST_CHECK_EQUAL(fh3._magic, magic);
+ BOOST_CHECK_EQUAL(fh3._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(fh3._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(fh3._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(fh3._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK);
+ BOOST_CHECK_EQUAL(fh3._rid, rid);
+ BOOST_CHECK_EQUAL(fh3._pfid, pfid);
+ BOOST_CHECK_EQUAL(fh3._lfid, lfid);
+ BOOST_CHECK_EQUAL(fh3._fro, fro);
+ BOOST_CHECK(fh3._ts_sec - ts.tv_sec <= 1); // No more than 1 sec difference
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(enq_hdr_class)
+{
+ cout << test_filename << ".enq_hdr_class: " << flush;
+ const u_int32_t magic = 0xfedcba98UL;
+ const u_int8_t version = 0xa5;
+ const u_int64_t rid = 0xfedcba9876543210ULL;
+ const u_int16_t uflag = 0x5537;
+#ifdef JRNL_32_BIT
+ const std::size_t xidsize = 0xfedcba98UL;
+ const std::size_t dsize = 0x76543210UL;
+#else
+ const std::size_t xidsize = 0xfedcba9876543210ULL;
+ const std::size_t dsize = 0x76543210fedcba98ULL;
+#endif
+ const bool owi = true;
+
+ {
+ enq_hdr eh1;
+ BOOST_CHECK_EQUAL(eh1._magic, 0UL);
+ BOOST_CHECK_EQUAL(eh1._version, 0);
+ BOOST_CHECK_EQUAL(eh1._eflag, 0);
+ BOOST_CHECK_EQUAL(eh1._uflag, 0);
+ BOOST_CHECK_EQUAL(eh1._rid, 0ULL);
+ BOOST_CHECK_EQUAL(eh1._xidsize, std::size_t(0));
+ BOOST_CHECK_EQUAL(eh1._dsize, std::size_t(0));
+ BOOST_CHECK(!eh1.get_owi());
+ }
+
+ {
+ enq_hdr eh2(magic, version, rid, xidsize, dsize, owi, false);
+ BOOST_CHECK_EQUAL(eh2._magic, magic);
+ BOOST_CHECK_EQUAL(eh2._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(eh2._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(eh2._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(eh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK);
+ BOOST_CHECK_EQUAL(eh2._rid, rid);
+ BOOST_CHECK_EQUAL(eh2._xidsize, xidsize);
+ BOOST_CHECK_EQUAL(eh2._dsize, dsize);
+ BOOST_CHECK(eh2.get_owi());
+ BOOST_CHECK(!eh2.is_transient());
+ BOOST_CHECK(!eh2.is_external());
+
+ eh2._uflag = uflag;
+ BOOST_CHECK(eh2.get_owi());
+ BOOST_CHECK(eh2.is_transient());
+ BOOST_CHECK(eh2.is_external());
+
+ eh2.set_owi(false);
+ BOOST_CHECK(!eh2.get_owi());
+ BOOST_CHECK(eh2.is_transient());
+ BOOST_CHECK(eh2.is_external());
+ BOOST_CHECK_EQUAL(eh2._uflag,
+ (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK));
+
+ eh2.set_owi(true);
+ BOOST_CHECK(eh2.get_owi());
+ BOOST_CHECK(eh2.is_transient());
+ BOOST_CHECK(eh2.is_external());
+ BOOST_CHECK_EQUAL(eh2._uflag, uflag);
+
+ eh2.set_transient(false);
+ BOOST_CHECK(eh2.get_owi());
+ BOOST_CHECK(!eh2.is_transient());
+ BOOST_CHECK(eh2.is_external());
+ BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK);
+
+ eh2.set_transient(true);
+ BOOST_CHECK(eh2.get_owi());
+ BOOST_CHECK(eh2.is_transient());
+ BOOST_CHECK(eh2.is_external());
+ BOOST_CHECK_EQUAL(eh2._uflag, uflag);
+
+ eh2.set_external(false);
+ BOOST_CHECK(eh2.get_owi());
+ BOOST_CHECK(eh2.is_transient());
+ BOOST_CHECK(!eh2.is_external());
+ BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_EXTERNAL_MASK);
+
+ eh2.set_external(true);
+ BOOST_CHECK(eh2.get_owi());
+ BOOST_CHECK(eh2.is_transient());
+ BOOST_CHECK(eh2.is_external());
+ BOOST_CHECK_EQUAL(eh2._uflag, uflag);
+ }
+
+ {
+ enq_hdr eh3(magic, version, rid, xidsize, dsize, owi, true);
+ BOOST_CHECK_EQUAL(eh3._magic, magic);
+ BOOST_CHECK_EQUAL(eh3._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(eh3._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(eh3._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(eh3._uflag, (const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK |
+ (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK);
+ BOOST_CHECK_EQUAL(eh3._rid, rid);
+ BOOST_CHECK_EQUAL(eh3._xidsize, xidsize);
+ BOOST_CHECK_EQUAL(eh3._dsize, dsize);
+ BOOST_CHECK(eh3.get_owi());
+ BOOST_CHECK(eh3.is_transient());
+ BOOST_CHECK(!eh3.is_external());
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(deq_hdr_class)
+{
+ cout << test_filename << ".deq_hdr_class: " << flush;
+ const u_int32_t magic = 0xfedcba98UL;
+ const u_int8_t version = 0xa5;
+ const u_int16_t uflag = 0x5537;
+ const u_int64_t rid = 0xfedcba9876543210ULL;
+ const u_int64_t drid = 0x76543210fedcba98ULL;
+#ifdef JRNL_32_BIT
+ const std::size_t xidsize = 0xfedcba98UL;
+#else
+ const std::size_t xidsize = 0xfedcba9876543210ULL;
+#endif
+ const bool owi = true;
+
+ {
+ deq_hdr dh1;
+ BOOST_CHECK_EQUAL(dh1._magic, 0UL);
+ BOOST_CHECK_EQUAL(dh1._version, 0);
+ BOOST_CHECK_EQUAL(dh1._eflag, 0);
+ BOOST_CHECK_EQUAL(dh1._uflag, 0);
+ BOOST_CHECK_EQUAL(dh1._rid, 0ULL);
+ BOOST_CHECK_EQUAL(dh1._deq_rid, 0ULL);
+ BOOST_CHECK_EQUAL(dh1._xidsize, std::size_t(0));
+ BOOST_CHECK(!dh1.get_owi());
+ }
+
+ {
+ deq_hdr dh2(magic, version, rid, drid, xidsize, owi);
+ BOOST_CHECK_EQUAL(dh2._magic, magic);
+ BOOST_CHECK_EQUAL(dh2._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(dh2._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(dh2._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(dh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK);
+ BOOST_CHECK_EQUAL(dh2._rid, rid);
+ BOOST_CHECK_EQUAL(dh2._deq_rid, drid);
+ BOOST_CHECK_EQUAL(dh2._xidsize, xidsize);
+ BOOST_CHECK(dh2.get_owi());
+
+ dh2._uflag = uflag;
+ BOOST_CHECK(dh2.get_owi());
+
+ dh2.set_owi(false);
+ BOOST_CHECK(!dh2.get_owi());
+ BOOST_CHECK_EQUAL(dh2._uflag,
+ (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK));
+
+ dh2.set_owi(true);
+ BOOST_CHECK(dh2.get_owi());
+ BOOST_CHECK_EQUAL(dh2._uflag, uflag);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(txn_hdr_class)
+{
+ cout << test_filename << ".txn_hdr_class: " << flush;
+ const u_int32_t magic = 0xfedcba98UL;
+ const u_int8_t version = 0xa5;
+ const u_int16_t uflag = 0x5537;
+ const u_int64_t rid = 0xfedcba9876543210ULL;
+#ifdef JRNL_32_BIT
+ const std::size_t xidsize = 0xfedcba98UL;
+#else
+ const std::size_t xidsize = 0xfedcba9876543210ULL;
+#endif
+ const bool owi = true;
+
+ {
+ txn_hdr th1;
+ BOOST_CHECK_EQUAL(th1._magic, 0UL);
+ BOOST_CHECK_EQUAL(th1._version, 0);
+ BOOST_CHECK_EQUAL(th1._eflag, 0);
+ BOOST_CHECK_EQUAL(th1._uflag, 0);
+ BOOST_CHECK_EQUAL(th1._rid, 0ULL);
+ BOOST_CHECK_EQUAL(th1._xidsize, std::size_t(0));
+ BOOST_CHECK(!th1.get_owi());
+ }
+
+ {
+ txn_hdr th2(magic, version, rid, xidsize, owi);
+ BOOST_CHECK_EQUAL(th2._magic, magic);
+ BOOST_CHECK_EQUAL(th2._version, version);
+#ifdef JRNL_LITTLE_ENDIAN
+ BOOST_CHECK_EQUAL(th2._eflag, RHM_LENDIAN_FLAG);
+#else
+ BOOST_CHECK_EQUAL(th2._eflag, RHM_BENDIAN_FLAG);
+#endif
+ BOOST_CHECK_EQUAL(th2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK);
+ BOOST_CHECK_EQUAL(th2._rid, rid);
+ BOOST_CHECK_EQUAL(th2._xidsize, xidsize);
+ BOOST_CHECK(th2.get_owi());
+
+ th2._uflag = uflag;
+ BOOST_CHECK(th2.get_owi());
+
+ th2.set_owi(false);
+ BOOST_CHECK(!th2.get_owi());
+ BOOST_CHECK_EQUAL(th2._uflag,
+ (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK));
+
+ th2.set_owi(true);
+ BOOST_CHECK(th2.get_owi());
+ BOOST_CHECK_EQUAL(th2._uflag, uflag);
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp
new file mode 100644
index 0000000000..f1b53bb97b
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.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 "../unit_test.h"
+
+#include <ctime>
+#include <iostream>
+#include "qpid/legacystore/jrnl/time_ns.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(time_ns_suite)
+
+const string test_filename("_ut_time_ns");
+
+QPID_AUTO_TEST_CASE(constructors)
+{
+ cout << test_filename << ".constructors: " << flush;
+ const std::time_t sec = 123;
+ const long nsec = 123456789;
+
+ time_ns t1;
+ BOOST_CHECK_EQUAL(t1.tv_sec, 0);
+ BOOST_CHECK_EQUAL(t1.tv_nsec, 0);
+ BOOST_CHECK_EQUAL(t1.is_zero(), true);
+ time_ns t2(sec, nsec);
+ BOOST_CHECK_EQUAL(t2.tv_sec, sec);
+ BOOST_CHECK_EQUAL(t2.tv_nsec, nsec);
+ BOOST_CHECK_EQUAL(t2.is_zero(), false);
+ time_ns t3(t1);
+ BOOST_CHECK_EQUAL(t3.tv_sec, 0);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, 0);
+ BOOST_CHECK_EQUAL(t3.is_zero(), true);
+ time_ns t4(t2);
+ BOOST_CHECK_EQUAL(t4.tv_sec, sec);
+ BOOST_CHECK_EQUAL(t4.tv_nsec, nsec);
+ BOOST_CHECK_EQUAL(t4.is_zero(), false);
+ t4.set_zero();
+ BOOST_CHECK_EQUAL(t4.tv_sec, 0);
+ BOOST_CHECK_EQUAL(t4.tv_nsec, 0);
+ BOOST_CHECK_EQUAL(t4.is_zero(), true);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(operators)
+{
+ cout << test_filename << ".operators: " << flush;
+ const std::time_t sec1 = 123;
+ const long nsec1 = 123456789;
+ const std::time_t sec2 = 1;
+ const long nsec2 = 999999999;
+ const std::time_t sec_sum = sec1 + sec2 + 1;
+ const long nsec_sum = nsec1 + nsec2 - 1000000000;
+ const std::time_t sec_1_minus_2 = sec1 - sec2 - 1;
+ const long nsec_1_minus_2 = nsec1 - nsec2 + 1000000000;
+ const std::time_t sec_2_minus_1 = sec2 - sec1;
+ const long nsec_2_minus_1 = nsec2 - nsec1;
+ time_ns z;
+ time_ns t1(sec1, nsec1);
+ time_ns t2(sec2, nsec2);
+
+ time_ns t3 = z;
+ BOOST_CHECK_EQUAL(t3.tv_sec, 0);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, 0);
+ BOOST_CHECK_EQUAL(t3 == z, true);
+ BOOST_CHECK_EQUAL(t3 != z, false);
+ BOOST_CHECK_EQUAL(t3 > z, false);
+ BOOST_CHECK_EQUAL(t3 >= z, true);
+ BOOST_CHECK_EQUAL(t3 < z, false);
+ BOOST_CHECK_EQUAL(t3 <= z, true);
+
+ t3 = t1;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec1);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1);
+ BOOST_CHECK_EQUAL(t3 == t1, true);
+ BOOST_CHECK_EQUAL(t3 != t1, false);
+ BOOST_CHECK_EQUAL(t3 > t1, false);
+ BOOST_CHECK_EQUAL(t3 >= t1, true);
+ BOOST_CHECK_EQUAL(t3 < t1, false);
+ BOOST_CHECK_EQUAL(t3 <= t1, true);
+
+ t3 += z;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec1);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1);
+
+ t3 = t2;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec2);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2);
+ BOOST_CHECK_EQUAL(t3 == t2, true);
+ BOOST_CHECK_EQUAL(t3 != t2, false);
+ BOOST_CHECK_EQUAL(t3 > t2, false);
+ BOOST_CHECK_EQUAL(t3 >= t2, true);
+ BOOST_CHECK_EQUAL(t3 < t2, false);
+ BOOST_CHECK_EQUAL(t3 <= t2, true);
+
+ t3 += z;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec2);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2);
+
+ t3 = t1;
+ t3 += t2;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum);
+
+ t3 = t1;
+ t3 -= t2;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2);
+
+ t3 = t2;
+ t3 -= t1;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1);
+
+ t3 = t1 + t2;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum);
+
+ t3 = t1 - t2;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2);
+
+ t3 = t2 - t1;
+ BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1);
+ BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(str)
+{
+ cout << test_filename << ".str: " << flush;
+ time_ns t1(123, 123456789);
+ BOOST_CHECK_EQUAL(t1.str(), "123.123457");
+ BOOST_CHECK_EQUAL(t1.str(9), "123.123456789");
+ BOOST_CHECK_EQUAL(t1.str(0), "123");
+ time_ns t2(1, 1);
+ BOOST_CHECK_EQUAL(t2.str(9), "1.000000001");
+ time_ns t3(-12, 345);
+ BOOST_CHECK_EQUAL(t3.str(9), "-11.999999655");
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp
new file mode 100644
index 0000000000..595ce0f6c6
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp
@@ -0,0 +1,106 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <iomanip>
+#include <iostream>
+#include "qpid/legacystore/jrnl/txn_map.h"
+#include <sstream>
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(txn_map_suite)
+
+const string test_filename("_ut_txn_map");
+
+// === Helper functions ===
+
+const string make_xid(u_int64_t rid)
+{
+ stringstream ss;
+ ss << "XID-" << setfill('0') << setw(16) << hex << rid;
+ ss << "-0123456789abcdef";
+ return ss.str();
+}
+
+void check_td_equal(txn_data& td1, txn_data& td2)
+{
+ BOOST_CHECK_EQUAL(td1._rid, td2._rid);
+ BOOST_CHECK_EQUAL(td1._drid, td2._drid);
+ BOOST_CHECK_EQUAL(td1._pfid, td2._pfid);
+ BOOST_CHECK_EQUAL(td1._enq_flag, td2._enq_flag);
+ BOOST_CHECK_EQUAL(td1._aio_compl, td2._aio_compl);
+}
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ const u_int64_t rid = 0x123456789abcdef0ULL;
+ const u_int64_t drid = 0xfedcba9876543210ULL;
+ const u_int16_t pfid = 0xfedcU;
+ const bool enq_flag = true;
+ txn_data td(rid, drid, pfid, enq_flag);
+ BOOST_CHECK_EQUAL(td._rid, rid);
+ BOOST_CHECK_EQUAL(td._drid, drid);
+ BOOST_CHECK_EQUAL(td._pfid, pfid);
+ BOOST_CHECK_EQUAL(td._enq_flag, enq_flag);
+ BOOST_CHECK_EQUAL(td._aio_compl, false);
+
+ txn_map t1;
+ BOOST_CHECK(t1.empty());
+ BOOST_CHECK_EQUAL(t1.size(), u_int32_t(0));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(insert_get)
+{
+ cout << test_filename << ".insert_get: " << flush;
+ u_int16_t fid;
+ u_int64_t rid;
+ u_int16_t pfid_start = 0x2000U;
+ u_int64_t rid_begin = 0xffffffff00000000ULL;
+ u_int64_t rid_end = 0xffffffff00000200ULL;
+
+ // insert with no dups
+ u_int64_t rid_incr_1 = 4ULL;
+ txn_map t2;
+ t2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1);
+ for (rid = rid_begin, fid = pfid_start; rid < rid_end; rid += rid_incr_1, fid++)
+ t2.insert_txn_data(make_xid(rid), txn_data(rid, ~rid, fid, false));
+ BOOST_CHECK(!t2.empty());
+ BOOST_CHECK_EQUAL(t2.size(), u_int32_t(128));
+
+ // get
+ u_int64_t rid_incr_2 = 6ULL;
+ for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2)
+ {
+ string xid = make_xid(rid);
+ BOOST_CHECK_EQUAL(t2.in_map(xid), (rid%rid_incr_1 ? false : true));
+ }
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata b/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata
new file mode 100755
index 0000000000..2ac87d91b9
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata
@@ -0,0 +1,32 @@
+#!/usr/bin/env 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.
+#
+
+
+JRNL_BLK_SIZE=512 # Block size in bytes
+JRNL_PAGE_SIZE=256 # Journal page size in blocks
+JRNL_FILE_SIZE=12 # Journal file size in pages
+let END_OFFSET=${JRNL_BLK_SIZE}*${JRNL_PAGE_SIZE}*${JRNL_FILE_SIZE}
+for f in jdata/test.*.jdat; do
+ echo $f
+ hexdump -C -n 1024 $f
+ hexdump -C -s ${END_OFFSET} $f
+ echo "============"
+done
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl b/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl
new file mode 100755
index 0000000000..e21f991788
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl
@@ -0,0 +1,59 @@
+#!/usr/bin/env 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.
+#
+
+JDATA_DIR=jdata
+TAR_DIR=rd_test_jrnls
+
+function get_filename
+{
+ local prefix=$1
+ local file_num=$2
+ local suffix=$3
+
+ if (( file_num < 10 )); then
+ local num="000${file_num}"
+ elif (( file_num < 100 )); then
+ local num="00${file_num}"
+ elif (( file_num < 1000 )); then
+ local num="0${file_num}"
+ else
+ local num="${file_num}"
+ fi
+ FILENAME=${prefix}${num}${suffix}
+ return 0
+}
+
+if (( $# != 1 )); then
+ echo "Incorrect args, expected 1 arg (usage: \"prep <testnum>\")"
+ exit
+fi
+
+get_filename "t" $1 ".tar.gz"
+if [[ -d ${JDATA_DIR} ]]; then
+ rm -rf ${JDATA_DIR}/*
+else
+ mkdir -p ${JDATA_DIR}
+fi
+if [[ -f "${TAR_DIR}/${FILENAME}" ]]; then
+ tar -C ${JDATA_DIR} -xzf "${TAR_DIR}/${FILENAME}"
+else
+ echo "Error: file \"${TAR_DIR}/${FILENAME}\" not found."
+fi
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jhexdump b/qpid/cpp/src/tests/legacystore/jrnl/jhexdump
new file mode 100755
index 0000000000..b013914441
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jhexdump
@@ -0,0 +1,41 @@
+#!/usr/bin/env 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.
+#
+
+if [ -z "$1" ]; then
+ echo "No directory specified."
+ exit
+fi
+
+JDIR=$1
+echo "Target directory: ${JDIR}"
+
+rm -f j*.txt
+
+if [ -d "${JDIR}" ]; then
+ n=0
+ for f in "${JDIR}"/*.jdat; do
+ echo "$f -> j$n.txt"
+ hexdump -C "$f" > j$n.txt
+ (( n += 1 ))
+ done
+else
+ echo "This directory does not exist."
+fi
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp
new file mode 100644
index 0000000000..e4656ef83f
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp
@@ -0,0 +1,207 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cstddef>
+#include "data_src.h"
+#include <iomanip>
+#include <iostream>
+
+using namespace boost::unit_test;
+using namespace mrg::jtt;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_data_src)
+
+const string test_filename("_ut_data_src");
+
+long
+get_seed()
+{
+ timespec ts;
+ if (::clock_gettime(CLOCK_REALTIME, &ts))
+ BOOST_FAIL("Unable to read clock to generate seed.");
+ long tenths = ts.tv_nsec / 100000000;
+ return long(10 * ts.tv_sec + tenths); // time in tenths of a second
+}
+
+#ifndef LONG_TEST
+/*
+ * ==============================================
+ * NORMAL TESTS
+ * This section contains normal "make check" tests
+ * for building/packaging. These are built when
+ * LONG_TEST is _not_ defined.
+ * ==============================================
+ */
+
+QPID_AUTO_TEST_CASE(data)
+{
+ cout << test_filename << ".data: " << flush;
+ BOOST_CHECK(data_src::max_dsize > 0);
+ for (std::size_t i=0; i<1024; i++)
+ {
+ const char* dp = data_src::get_data(i);
+ BOOST_CHECK_EQUAL(*dp, static_cast<char>('0' + ((i + 1) % 10)));
+ }
+ for (std::size_t i=data_src::max_dsize-1024; i<data_src::max_dsize; i++)
+ {
+ const char* dp = data_src::get_data(i);
+ BOOST_CHECK_EQUAL(*dp, static_cast<char>('0' + ((i + 1) % 10)));
+ }
+ const char* dp1 = data_src::get_data(data_src::max_dsize);
+ BOOST_CHECK_EQUAL(dp1,(char*) 0);
+ const char* dp2 = data_src::get_data(data_src::max_dsize + 0x1000);
+ BOOST_CHECK_EQUAL(dp2, (char*)0);
+ cout << "ok" << endl;
+}
+
+// There is a long version of this test in _ut_long_data_src.cpp
+QPID_AUTO_TEST_CASE(xid_data_xid)
+{
+ const std::size_t num = 64;
+ cout << test_filename << ".xid_data_xid: " << flush;
+ BOOST_CHECK_EQUAL(data_src::get_xid(1), "0");
+ BOOST_CHECK_EQUAL(data_src::get_xid(2), "01");
+ BOOST_CHECK_EQUAL(data_src::get_xid(3), "002");
+ BOOST_CHECK_EQUAL(data_src::get_xid(4), "0003");
+ BOOST_CHECK_EQUAL(data_src::get_xid(5), "00004");
+ BOOST_CHECK_EQUAL(data_src::get_xid(6), "000005");
+ BOOST_CHECK_EQUAL(data_src::get_xid(7), "0000006");
+ BOOST_CHECK_EQUAL(data_src::get_xid(8), "00000007");
+ BOOST_CHECK_EQUAL(data_src::get_xid(9), "xid:00008");
+ BOOST_CHECK_EQUAL(data_src::get_xid(10), "xid:000009");
+ BOOST_CHECK_EQUAL(data_src::get_xid(11), "xid:0000010");
+ BOOST_CHECK_EQUAL(data_src::get_xid(12), "xid:00000011");
+ BOOST_CHECK_EQUAL(data_src::get_xid(13), "xid:00000012:");
+ BOOST_CHECK_EQUAL(data_src::get_xid(14), "xid:00000013:n");
+ BOOST_CHECK_EQUAL(data_src::get_xid(15), "xid:00000014:no");
+ std::size_t i = 15;
+ for (; i<num; i++)
+ {
+ string xid(data_src::get_xid(i));
+
+ ostringstream oss;
+ oss << setfill('0') << "xid:" << setw(8) << i << ":";
+
+ BOOST_CHECK_EQUAL(xid.size(), i);
+ BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str());
+ BOOST_CHECK_EQUAL(xid[13], 'n');
+ BOOST_CHECK_EQUAL(xid[i-1], (char)('a' + ((i-1)%26)));
+ }
+ for (std::size_t j=data_src::max_xsize-num; j<data_src::max_xsize; j++,i++)
+ {
+ string xid(data_src::get_xid(j));
+
+ ostringstream oss;
+ oss << setfill('0') << "xid:" << setw(8) << i << ":";
+
+ BOOST_CHECK_EQUAL(xid.size(), j);
+ BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str());
+ BOOST_CHECK_EQUAL(xid[13], 'n');
+ BOOST_CHECK_EQUAL(xid[j-1], (char)('a' + ((j-1)%26)));
+ }
+ cout << "ok" << endl;
+}
+
+#else
+/*
+ * ==============================================
+ * LONG TESTS
+ * This section contains long tests and soak tests,
+ * and are run using target check-long (ie "make
+ * check-long"). These are built when LONG_TEST is
+ * defined.
+ * ==============================================
+ */
+
+/*
+ * To reproduce a specific test, comment out the get_seed() statement and uncomment the literal below, adjusting the seed
+ * value to that required.
+ */
+QPID_AUTO_TEST_CASE(xid_data_xid)
+{
+ const long seed = get_seed();
+ // const long seed = 0x2d9b69d32;
+ ::srand48(seed);
+
+ const std::size_t num = 1024;
+ cout << test_filename << ".xid_data_xid seed=0x" << std::hex << seed << std::dec << ": " << flush;
+ BOOST_CHECK_EQUAL(data_src::get_xid(1), "0");
+ BOOST_CHECK_EQUAL(data_src::get_xid(2), "01");
+ BOOST_CHECK_EQUAL(data_src::get_xid(3), "002");
+ BOOST_CHECK_EQUAL(data_src::get_xid(4), "0003");
+ BOOST_CHECK_EQUAL(data_src::get_xid(5), "00004");
+ BOOST_CHECK_EQUAL(data_src::get_xid(6), "000005");
+ BOOST_CHECK_EQUAL(data_src::get_xid(7), "0000006");
+ BOOST_CHECK_EQUAL(data_src::get_xid(8), "00000007");
+ BOOST_CHECK_EQUAL(data_src::get_xid(9), "xid:00008");
+ BOOST_CHECK_EQUAL(data_src::get_xid(10), "xid:000009");
+ BOOST_CHECK_EQUAL(data_src::get_xid(11), "xid:0000010");
+ BOOST_CHECK_EQUAL(data_src::get_xid(12), "xid:00000011");
+ BOOST_CHECK_EQUAL(data_src::get_xid(13), "xid:00000012:");
+ BOOST_CHECK_EQUAL(data_src::get_xid(14), "xid:00000013:n");
+ BOOST_CHECK_EQUAL(data_src::get_xid(15), "xid:00000014:no");
+ std::size_t i = 15;
+ for (; i<num; i++)
+ {
+ string xid(data_src::get_xid(i));
+
+ ostringstream oss;
+ oss << setfill('0') << "xid:" << setw(8) << i << ":";
+
+ BOOST_CHECK_EQUAL(xid.size(), i);
+ BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str());
+ BOOST_CHECK_EQUAL(xid[13], 'n');
+ BOOST_CHECK_EQUAL(xid[i-1], (char)('a' + ((i-1)%26)));
+ }
+ for (std::size_t j=data_src::max_xsize-num; j<data_src::max_xsize; j++,i++)
+ {
+ string xid(data_src::get_xid(j));
+
+ ostringstream oss;
+ oss << setfill('0') << "xid:" << setw(8) << i << ":";
+
+ BOOST_CHECK_EQUAL(xid.size(), j);
+ BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str());
+ BOOST_CHECK_EQUAL(xid[13], 'n');
+ BOOST_CHECK_EQUAL(xid[j-1], (char)('a' + ((j-1)%26)));
+ }
+ std::srand(seed);
+ for (int cnt=0; cnt<1000; cnt++,i++)
+ {
+ std::size_t k = 1 + ::lrand48() % (data_src::max_xsize - 1);
+ string xid(data_src::get_xid(k));
+
+ ostringstream oss;
+ oss << setfill('0') << "xid:" << setw(8) << i << ":";
+
+ BOOST_CHECK_EQUAL(xid.size(), k);
+ BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str());
+ BOOST_CHECK_EQUAL(xid[13], 'n');
+ BOOST_CHECK_EQUAL(xid[k-1], (char)('a' + ((k-1)%26)));
+ }
+ cout << "ok" << endl;
+}
+
+#endif
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp
new file mode 100644
index 0000000000..9fefe25105
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.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 "../../unit_test.h"
+#include "jrnl_init_params.h"
+#include <iostream>
+
+using namespace boost::unit_test;
+using namespace mrg::jtt;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_jrnl_init_params)
+
+const string test_filename("_ut_jrnl_init_params");
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ const string jid = "jid";
+ const string jdir = "jdir";
+ const string bfn = "base filename";
+ const u_int16_t num_jfiles = 123;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 456;
+ const u_int32_t jfsize_sblks = 789;
+ jrnl_init_params jip(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks);
+ BOOST_CHECK_EQUAL(jip.jid(), jid);
+ BOOST_CHECK_EQUAL(jip.jdir(), jdir);
+ BOOST_CHECK_EQUAL(jip.base_filename(), bfn);
+ BOOST_CHECK_EQUAL(jip.num_jfiles(), num_jfiles);
+ BOOST_CHECK_EQUAL(jip.is_ae(), ae);
+ BOOST_CHECK_EQUAL(jip.ae_max_jfiles(), ae_max_jfiles);
+ BOOST_CHECK_EQUAL(jip.jfsize_sblks(), jfsize_sblks);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(copy_constructor_1)
+{
+ cout << test_filename << ".copy_constructor_1: " << flush;
+ const string jid = "jid";
+ const string jdir = "jdir";
+ const string bfn = "base filename";
+ const u_int16_t num_jfiles = 123;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 456;
+ const u_int32_t jfsize_sblks = 789;
+ jrnl_init_params jip1(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks);
+ jrnl_init_params jip2(jip1);
+ BOOST_CHECK_EQUAL(jip2.jid(), jid);
+ BOOST_CHECK_EQUAL(jip2.jdir(), jdir);
+ BOOST_CHECK_EQUAL(jip2.base_filename(), bfn);
+ BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles);
+ BOOST_CHECK_EQUAL(jip2.is_ae(), ae);
+ BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles);
+ BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(copy_constructor_2)
+{
+ cout << test_filename << ".copy_constructor_2: " << flush;
+ const string jid = "jid";
+ const string jdir = "jdir";
+ const string bfn = "base filename";
+ const u_int16_t num_jfiles = 123;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 456;
+ const u_int32_t jfsize_sblks = 789;
+ jrnl_init_params::shared_ptr p(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks));
+ jrnl_init_params jip2(p.get());
+ BOOST_CHECK_EQUAL(jip2.jid(), jid);
+ BOOST_CHECK_EQUAL(jip2.jdir(), jdir);
+ BOOST_CHECK_EQUAL(jip2.base_filename(), bfn);
+ BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles);
+ BOOST_CHECK_EQUAL(jip2.is_ae(), ae);
+ BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles);
+ BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp
new file mode 100644
index 0000000000..12f1c542d6
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.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 "../../unit_test.h"
+
+#include <iostream>
+#include "jrnl_init_params.h"
+#include "jrnl_instance.h"
+#include "qpid/legacystore/jrnl/jdir.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace mrg::jtt;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_jrnl_instance)
+
+const string test_filename("_ut_jrnl_instance");
+const char* tdp = getenv("TMP_DATA_DIR");
+const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/var/tmp/JttTest");
+
+QPID_AUTO_TEST_CASE(constructor_1)
+{
+ cout << test_filename << ".constructor_1: " << flush;
+ const string jid = "jid1";
+ const string jdir = test_dir + "/test1";
+ const string bfn = "test";
+ const u_int16_t num_jfiles = 20;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 45;
+ const u_int32_t jfsize_sblks = 128;
+
+ args a("a1");
+ using mrg::jtt::test_case;
+ test_case::shared_ptr p(new test_case(1, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL,
+ "t1"));
+ jrnl_instance ji(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks);
+ ji.init_tc(p, &a);
+ ji.run_tc();
+ ji.tc_wait_compl();
+ try { jdir::verify_dir(jdir, bfn); }
+ catch (const jexception& e) { BOOST_ERROR(e.what()); }
+ jdir::delete_dir(jdir);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_2)
+{
+ cout << test_filename << ".constructor_2: " << flush;
+ const string jid = "jid2";
+ const string jdir = test_dir + "/test2";
+ const string bfn = "test";
+ const u_int16_t num_jfiles = 20;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 45;
+ const u_int32_t jfsize_sblks = 128;
+
+ args a("a2");
+ using mrg::jtt::test_case;
+ test_case::shared_ptr p(new test_case(2, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL,
+ "t2"));
+ jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks));
+ jrnl_instance ji(jpp);
+ ji.init_tc(p, &a);
+ ji.run_tc();
+ ji.tc_wait_compl();
+ try { jdir::verify_dir(jdir, bfn); }
+ catch (const jexception& e) { BOOST_ERROR(e.what()); }
+ jdir::delete_dir(jdir);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_3)
+{
+ cout << test_filename << ".constructor_3: " << flush;
+ const string jid = "jid3";
+ const string jdir = test_dir + "/test3";
+ const string bfn = "test";
+ const u_int16_t num_jfiles = 20;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 45;
+ const u_int32_t jfsize_sblks = 128;
+
+ args a("a3");
+ using mrg::jtt::test_case;
+ test_case::shared_ptr p(new test_case(3, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL,
+ "t3"));
+ jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks));
+ jrnl_instance ji(jpp);
+ ji.init_tc(p, &a);
+ ji.run_tc();
+ ji.tc_wait_compl();
+ try { jdir::verify_dir(jdir, bfn); }
+ catch (const jexception& e) { BOOST_ERROR(e.what()); }
+ jdir::delete_dir(jdir);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(recover)
+{
+ cout << test_filename << ".recover: " << flush;
+ const string jid = "jid5";
+ const string jdir = test_dir + "/test5";
+ const string bfn = "test";
+ const u_int16_t num_jfiles = 20;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 0;
+ const u_int32_t jfsize_sblks = 128;
+
+ args a("a4");
+ using mrg::jtt::test_case;
+ test_case::shared_ptr p(new test_case(5, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL,
+ "t5"));
+ jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks));
+ jrnl_instance ji(jpp);
+ ji.init_tc(p, &a);
+ ji.run_tc();
+ ji.tc_wait_compl();
+ try { jdir::verify_dir(jdir, bfn); }
+ catch (const jexception& e) { BOOST_ERROR(e.what()); }
+ a.recover_mode = true;
+ ji.init_tc(p, &a);
+ ji.run_tc();
+ ji.tc_wait_compl();
+ try { jdir::verify_dir(jdir, bfn); }
+ catch (const jexception& e) { BOOST_ERROR(e.what()); }
+ jdir::delete_dir(jdir);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(recover_no_files)
+{
+ cout << test_filename << ".recover_no_files: " << flush;
+ const string jid = "jid6";
+ const string jdir = test_dir + "/test6";
+ const string bfn = "test";
+ const u_int16_t num_jfiles = 20;
+ const bool ae = false;
+ const u_int16_t ae_max_jfiles = 0;
+ const u_int32_t jfsize_sblks = 128;
+
+ args a("a5");
+ a.recover_mode = true;
+ using mrg::jtt::test_case;
+ test_case::shared_ptr p(new test_case(6, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL,
+ "t6"));
+ jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks));
+ jrnl_instance ji(jpp);
+ ji.init_tc(p, &a);
+ ji.run_tc();
+ ji.tc_wait_compl();
+ try { jdir::verify_dir(jdir, bfn); }
+ catch (const jexception& e) { BOOST_ERROR(e.what()); }
+ jdir::delete_dir(jdir);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp
new file mode 100644
index 0000000000..0d2025270d
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp
@@ -0,0 +1,146 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <boost/test/unit_test_log.hpp>
+#include "read_arg.h"
+#include <iostream>
+
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+using namespace mrg::jtt;
+using namespace boost::unit_test;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_read_arg)
+
+const string test_filename("_ut_read_arg");
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ read_arg ra1;
+ BOOST_CHECK_EQUAL(ra1.val(), read_arg::NONE);
+ BOOST_CHECK_EQUAL(ra1.str(), "NONE");
+ read_arg ra2(read_arg::NONE);
+ BOOST_CHECK_EQUAL(ra2.val(), read_arg::NONE);
+ BOOST_CHECK_EQUAL(ra2.str(), "NONE");
+ read_arg ra3(read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra3.val(), read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra3.str(), "ALL");
+ read_arg ra4(read_arg::RANDOM);
+ BOOST_CHECK_EQUAL(ra4.val(), read_arg::RANDOM);
+ BOOST_CHECK_EQUAL(ra4.str(), "RANDOM");
+ read_arg ra5(read_arg::LAZYLOAD);
+ BOOST_CHECK_EQUAL(ra5.val(), read_arg::LAZYLOAD);
+ BOOST_CHECK_EQUAL(ra5.str(), "LAZYLOAD");
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(set_val)
+{
+ cout << test_filename << ".set_val: " << flush;
+ read_arg ra;
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE);
+ BOOST_CHECK_EQUAL(ra.str(), "NONE");
+ ra.set_val(read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra.str(), "ALL");
+ ra.set_val(read_arg::RANDOM);
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM);
+ BOOST_CHECK_EQUAL(ra.str(), "RANDOM");
+ ra.set_val(read_arg::LAZYLOAD);
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD);
+ BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD");
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(parse)
+{
+ cout << test_filename << ".parse: " << flush;
+ read_arg ra;
+ ra.parse("LAZYLOAD");
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD);
+ BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD");
+ ra.parse("ALL");
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra.str(), "ALL");
+ BOOST_CHECK_THROW(ra.parse(""), po::invalid_option_value)
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra.str(), "ALL");
+ BOOST_CHECK_THROW(ra.parse("abc123"), po::invalid_option_value)
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra.str(), "ALL");
+ ra.parse("NONE");
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE);
+ BOOST_CHECK_EQUAL(ra.str(), "NONE");
+ ra.parse("RANDOM");
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM);
+ BOOST_CHECK_EQUAL(ra.str(), "RANDOM");
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(istream_)
+{
+ cout << test_filename << ".istream_: " << flush;
+ read_arg ra;
+ istringstream ss1("LAZYLOAD", ios::in);
+ ss1 >> ra;
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD);
+ BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD");
+ istringstream ss2("ALL", ios::in);
+ ss2 >> ra;
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL);
+ BOOST_CHECK_EQUAL(ra.str(), "ALL");
+ istringstream ss3("NONE", ios::in);
+ ss3 >> ra;
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE);
+ BOOST_CHECK_EQUAL(ra.str(), "NONE");
+ istringstream ss4("RANDOM", ios::in);
+ ss4 >> ra;
+ BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM);
+ BOOST_CHECK_EQUAL(ra.str(), "RANDOM");
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(ostream_)
+{
+ cout << test_filename << ".ostream_: " << flush;
+ ostringstream s1;
+ read_arg ra(read_arg::LAZYLOAD);
+ s1 << ra;
+ BOOST_CHECK_EQUAL(s1.str(), "LAZYLOAD");
+ ra.set_val(read_arg::ALL);
+ ostringstream s2;
+ s2 << ra;
+ BOOST_CHECK_EQUAL(s2.str(), "ALL");
+ ra.set_val(read_arg::NONE);
+ ostringstream s3;
+ s3 << ra;
+ BOOST_CHECK_EQUAL(s3.str(), "NONE");
+ ra.set_val(read_arg::RANDOM);
+ ostringstream s4;
+ s4 << ra;
+ BOOST_CHECK_EQUAL(s4.str(), "RANDOM");
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp
new file mode 100644
index 0000000000..3a7d0f951c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp
@@ -0,0 +1,113 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cstddef>
+#include <iomanip>
+#include <iostream>
+#include "test_case.h"
+#include "test_case_result.h"
+
+using namespace boost::unit_test;
+using namespace mrg::jtt;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_test_case)
+
+const string test_filename("_ut_test_case");
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ const unsigned test_case_num = 0x12345;
+ const u_int32_t num_msgs = 0x100;
+ const std::size_t min_data_size = 0x1000;
+ const std::size_t max_data_size = 0;
+ const bool auto_deq = true;
+ const std::size_t min_xid_size = 0x200;
+ const std::size_t max_xid_size = 0x200;
+ using mrg::jtt::test_case;
+ const test_case::transient_t transient = test_case::JTT_PERSISTNET;
+ const test_case::external_t external = test_case::JDL_INTERNAL;
+ const string comment = "This is a test";
+
+ test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq,
+ min_xid_size, max_xid_size, transient, external, comment);
+ BOOST_CHECK_EQUAL(tc.test_case_num(), test_case_num);
+ BOOST_CHECK_EQUAL(tc.num_msgs(), num_msgs);
+ BOOST_CHECK_EQUAL(tc.min_data_size(), min_data_size);
+ BOOST_CHECK_EQUAL(tc.max_data_size(), max_data_size);
+ BOOST_CHECK_EQUAL(tc.auto_deq(), auto_deq);
+ BOOST_CHECK_EQUAL(tc.min_xid_size(), min_xid_size);
+ BOOST_CHECK_EQUAL(tc.max_xid_size(), max_xid_size);
+ BOOST_CHECK_EQUAL(tc.transient(), transient);
+ BOOST_CHECK_EQUAL(tc.external(), external);
+ BOOST_CHECK_EQUAL(tc.comment(), comment);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(results)
+{
+ cout << test_filename << ".results: " << flush;
+ const unsigned test_case_num = 0x12345;
+ const u_int32_t num_msgs = 0x100;
+ const std::size_t min_data_size = 0x1000;
+ const std::size_t max_data_size = 0;
+ const bool auto_deq = true;
+ const std::size_t min_xid_size = 0x200;
+ const std::size_t max_xid_size = 0x200;
+ using mrg::jtt::test_case;
+ const test_case::transient_t transient = test_case::JTT_PERSISTNET;
+ const test_case::external_t external = test_case::JDL_INTERNAL;
+ const string comment = "This is a test";
+ const unsigned num_results = 20;
+
+ test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq,
+ min_xid_size, max_xid_size, transient, external, comment);
+ for (unsigned i=0; i<num_results; i++)
+ {
+ ostringstream oss;
+ oss << "JID_" << setfill('0') << setw(2) << i;
+ test_case_result::shared_ptr p(new test_case_result(oss.str()));
+ tc.add_result(p);
+ }
+ BOOST_CHECK_EQUAL(tc.num_results(), num_results);
+ test_case_result_agregation ave = tc.average();
+ unsigned i=0;
+ for (test_case_result_agregation::tcrp_list_citr j=ave.rlist_begin(); j!=ave.rlist_end();
+ i++,j++)
+ {
+ ostringstream oss;
+ oss << "JID_" << setfill('0') << setw(2) << i;
+ BOOST_CHECK_EQUAL((*j)->jid(), oss.str());
+ }
+ for (unsigned i=0; i<num_results; i++)
+ {
+ ostringstream oss;
+ oss << "JID_" << setfill('0') << setw(2) << i;
+ BOOST_CHECK_EQUAL(ave[i]->jid(), oss.str());
+ }
+ tc.clear();
+ BOOST_CHECK_EQUAL(tc.num_results(), unsigned(0));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp
new file mode 100644
index 0000000000..dd83dbee69
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp
@@ -0,0 +1,206 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/legacystore/jrnl/jexception.h"
+#include "test_case_result.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace mrg::jtt;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_test_case_result)
+
+const string test_filename("_ut_test_case_result");
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ const string jid("journal id 1");
+ test_case_result tcr(jid);
+ BOOST_CHECK_EQUAL(tcr.jid(), jid);
+ BOOST_CHECK_EQUAL(tcr.exception(), false);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 0U);
+ const time_ns& ts1 = tcr.start_time();
+ BOOST_CHECK(ts1.is_zero());
+ const time_ns& ts2 = tcr.stop_time();
+ BOOST_CHECK(ts2.is_zero());
+ const time_ns& ts3 = tcr.test_time();
+ BOOST_CHECK(ts3.is_zero());
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(start_stop)
+{
+ cout << test_filename << ".start_stop: " << flush;
+ const string jid("journal id 2");
+ test_case_result tcr(jid);
+ BOOST_CHECK_EQUAL(tcr.exception(), false);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 0U);
+ const time_ns& ts1 = tcr.start_time();
+ BOOST_CHECK(ts1.is_zero());
+ const time_ns& ts2 = tcr.stop_time();
+ BOOST_CHECK(ts2.is_zero());
+ const time_ns& ts3 = tcr.test_time();
+ BOOST_CHECK(ts3.is_zero());
+
+ tcr.set_start_time();
+ BOOST_CHECK_EQUAL(tcr.exception(), false);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 0U);
+ const time_ns& ts4 = tcr.start_time();
+ BOOST_CHECK(!ts4.is_zero());
+ const time_ns& ts5 = tcr.stop_time();
+ BOOST_CHECK(ts5.is_zero());
+ const time_ns& ts6 = tcr.test_time();
+ BOOST_CHECK(ts6.is_zero());
+
+ ::usleep(1100000); // 1.1 sec in microseconds
+ tcr.set_stop_time();
+ BOOST_CHECK_EQUAL(tcr.exception(), false);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 0U);
+ const time_ns& ts7 = tcr.stop_time();
+ BOOST_CHECK(!ts7.is_zero());
+ const time_ns& ts8 = tcr.test_time();
+ BOOST_CHECK(ts8.tv_sec == 1);
+ BOOST_CHECK(ts8.tv_nsec > 100000000); // 0.1 sec in nanoseconds
+ BOOST_CHECK(ts8.tv_nsec < 200000000); // 0.2 sec in nanoseconds
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(start_exception_stop_1)
+{
+ cout << test_filename << ".start_exception_stop_1: " << flush;
+ const string jid("journal id 3");
+ test_case_result tcr(jid);
+ const u_int32_t err_code = 0x321;
+ const string err_msg = "exception message";
+ const jexception e(err_code, err_msg);
+ tcr.set_start_time();
+ ::usleep(1100000); // 1.1 sec in microseconds
+ tcr.add_exception(e);
+ BOOST_CHECK_EQUAL(tcr.exception(), true);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 1U);
+ BOOST_CHECK_EQUAL(tcr[0], e.what());
+ const time_ns& ts1 = tcr.stop_time();
+ BOOST_CHECK(!ts1.is_zero());
+ const time_ns& ts2 = tcr.test_time();
+ BOOST_CHECK(ts2.tv_sec == 1);
+ BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds
+ BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(start_exception_stop_2)
+{
+ cout << test_filename << ".start_exception_stop_2: " << flush;
+ const string jid("journal id 4");
+ test_case_result tcr(jid);
+ const string err_msg = "exception message";
+ tcr.set_start_time();
+ ::usleep(1100000); // 1.1 sec in microseconds
+ tcr.add_exception(err_msg);
+ BOOST_CHECK_EQUAL(tcr.exception(), true);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 1U);
+ BOOST_CHECK_EQUAL(tcr[0], err_msg);
+ const time_ns& ts1 = tcr.stop_time();
+ BOOST_CHECK(!ts1.is_zero());
+ const time_ns& ts2 = tcr.test_time();
+ BOOST_CHECK(ts2.tv_sec == 1);
+ BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds
+ BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(start_exception_stop_3)
+{
+ cout << test_filename << ".start_exception_stop_3: " << flush;
+ const string jid("journal id 5");
+ test_case_result tcr(jid);
+ const char* err_msg = "exception message";
+ tcr.set_start_time();
+ ::usleep(1100000); // 1.1 sec in microseconds
+ tcr.add_exception(err_msg);
+ BOOST_CHECK_EQUAL(tcr.exception(), true);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 1U);
+ BOOST_CHECK_EQUAL(tcr[0], err_msg);
+ const time_ns& ts1 = tcr.stop_time();
+ BOOST_CHECK(!ts1.is_zero());
+ const time_ns& ts2 = tcr.test_time();
+ BOOST_CHECK(ts2.tv_sec == 1);
+ BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds
+ BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(start_exception)
+{
+ cout << test_filename << ".start_exception: " << flush;
+ const string jid("journal id 6");
+ test_case_result tcr(jid);
+ u_int32_t err_code = 0x654;
+ const string err_msg = "exception message";
+ const jexception e(err_code, err_msg);
+ tcr.set_start_time();
+ ::usleep(1100000); // 1.1 sec in microseconds
+ tcr.add_exception(e, false);
+ BOOST_CHECK_EQUAL(tcr.exception(), true);
+ BOOST_CHECK_EQUAL(tcr.exception_count(), 1U);
+ BOOST_CHECK_EQUAL(tcr[0], e.what());
+ const time_ns& ts1 = tcr.stop_time();
+ BOOST_CHECK(ts1.is_zero());
+ const time_ns& ts2 = tcr.test_time();
+ BOOST_CHECK(ts2.is_zero());
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(counters)
+{
+ cout << test_filename << ".counters: " << flush;
+ const u_int32_t num_enq = 125;
+ const u_int32_t num_deq = 64;
+ const u_int32_t num_read = 22;
+ const string jid("journal id 7");
+ test_case_result tcr(jid);
+ BOOST_CHECK_EQUAL(tcr.num_enq(), u_int32_t(0));
+ BOOST_CHECK_EQUAL(tcr.num_deq(), u_int32_t(0));
+ BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0));
+ for (unsigned i=0; i<num_enq; i++)
+ tcr.incr_num_enq();
+ BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq);
+ BOOST_CHECK_EQUAL(tcr.num_deq(), u_int32_t(0));
+ BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0));
+ for (unsigned j=0; j<num_deq; j++)
+ tcr.incr_num_deq();
+ BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq);
+ BOOST_CHECK_EQUAL(tcr.num_deq(), num_deq);
+ BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0));
+ for (unsigned k=0; k<num_read; k++)
+ tcr.incr_num_read();
+ BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq);
+ BOOST_CHECK_EQUAL(tcr.num_deq(), num_deq);
+ BOOST_CHECK_EQUAL(tcr.num_read(), num_read);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp
new file mode 100644
index 0000000000..aa01bf833d
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.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 "../../unit_test.h"
+#include <ctime>
+#include <iostream>
+#include "test_case_result_agregation.h"
+
+using namespace boost::unit_test;
+using namespace mrg::journal;
+using namespace mrg::jtt;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_test_case_result_agregation)
+
+const string test_filename("_ut_test_case_result_agregation");
+
+// === Helper functions ===
+
+void check_agregate(const test_case_result_agregation& tcra, const u_int32_t num_enq,
+ const u_int32_t num_deq, const u_int32_t num_reads, const u_int32_t num_results,
+ const u_int32_t num_exceptions, const std::time_t secs, const long nsec)
+{
+ BOOST_CHECK_EQUAL(tcra.num_enq(), num_enq);
+ BOOST_CHECK_EQUAL(tcra.num_deq(), num_deq);
+ BOOST_CHECK_EQUAL(tcra.num_read(), num_reads);
+ BOOST_CHECK_EQUAL(tcra.num_results(), num_results);
+ BOOST_CHECK_EQUAL(tcra.exception_count(), num_exceptions);
+ BOOST_CHECK_EQUAL(tcra.exception(), num_exceptions > 0);
+ const time_ns& ts1 = tcra.test_time();
+ BOOST_CHECK_EQUAL(ts1.tv_sec, secs);
+ BOOST_CHECK_EQUAL(ts1.tv_nsec, nsec);
+}
+
+test_case_result::shared_ptr make_result(const string& jid, const u_int32_t num_enq,
+ const u_int32_t num_deq, const u_int32_t num_reads, const std::time_t secs, const long nsec)
+{
+ test_case_result::shared_ptr tcrp(new test_case_result(jid));
+ for (unsigned i=0; i<num_enq; i++)
+ tcrp->incr_num_enq();
+ for (unsigned i=0; i<num_deq; i++)
+ tcrp->incr_num_deq();
+ for (unsigned i=0; i<num_reads; i++)
+ tcrp->incr_num_read();
+ time_ns ts(secs, nsec);
+ tcrp->set_test_time(ts);
+ return tcrp;
+}
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(constructor_1)
+{
+ cout << test_filename << ".constructor_1: " << flush;
+ test_case_result_agregation tcra;
+ BOOST_CHECK_EQUAL(tcra.tc_average_mode(), true);
+ BOOST_CHECK_EQUAL(tcra.jid(), "Average");
+ BOOST_CHECK_EQUAL(tcra.exception(), false);
+ BOOST_CHECK_EQUAL(tcra.exception_count(), 0U);
+ const time_ns& ts1 = tcra.start_time();
+ BOOST_CHECK(ts1.is_zero());
+ const time_ns& ts2 = tcra.stop_time();
+ BOOST_CHECK(ts2.is_zero());
+ const time_ns& ts3 = tcra.test_time();
+ BOOST_CHECK(ts3.is_zero());
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(constructor_2)
+{
+ cout << test_filename << ".constructor_2: " << flush;
+ string jid("journal id");
+ test_case_result_agregation tcra(jid);
+ BOOST_CHECK_EQUAL(tcra.tc_average_mode(), false);
+ BOOST_CHECK_EQUAL(tcra.jid(), jid);
+ BOOST_CHECK_EQUAL(tcra.exception(), false);
+ BOOST_CHECK_EQUAL(tcra.exception_count(), 0U);
+ const time_ns& ts1 = tcra.start_time();
+ BOOST_CHECK(ts1.is_zero());
+ const time_ns& ts2 = tcra.stop_time();
+ BOOST_CHECK(ts2.is_zero());
+ const time_ns& ts3 = tcra.test_time();
+ BOOST_CHECK(ts3.is_zero());
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(add_test_case)
+{
+ cout << test_filename << ".add_test_case: " << flush;
+ string jid("jid1");
+ test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L);
+ test_case_result::shared_ptr tcrp2 = make_result("jid1", 25, 0, 35, 10, 20202020L);
+ test_case_result::shared_ptr tcrp3 = make_result("jid1", 0, 15, 5, 2, 555555555L);
+ test_case_result::shared_ptr tcrp4 = make_result("jid2", 100, 100, 100, 100, 323232324L);
+ test_case_result::shared_ptr tcrp5 = make_result("jid1", 5, 0, 0, 0, 100L);
+ tcrp5->add_exception(string("error 1"), false);
+ test_case_result::shared_ptr tcrp6 = make_result("jid3", 0, 5, 0, 0, 100L);
+ jexception e(0x123, "exception 2");
+ tcrp6->add_exception(e, false);
+ test_case_result::shared_ptr tcrp7 = make_result("jid1", 0, 0, 0, 0, 0L);
+ test_case_result::shared_ptr tcrp8 = make_result("jid1", 200, 100, 300, 12, 323232224L);
+
+ test_case_result_agregation tcra(jid);
+ check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L);
+ tcra.add_test_result(tcrp1);
+ check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L);
+ tcra.add_test_result(tcrp2);
+ check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L);
+ tcra.add_test_result(tcrp3);
+ check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L);
+ tcra.add_test_result(tcrp4);
+ check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L);
+ tcra.add_test_result(tcrp5);
+ check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L);
+ tcra.add_test_result(tcrp6);
+ check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L);
+ tcra.add_test_result(tcrp7);
+ check_agregate(tcra, 40, 25, 40, 5, 1, 13, 676767776L);
+ tcra.add_test_result(tcrp8);
+ check_agregate(tcra, 240, 125, 340, 6, 1, 26, 0L);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(add_test_case_average)
+{
+ cout << test_filename << ".add_test_case_average: " << flush;
+ test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L);
+ test_case_result::shared_ptr tcrp2 = make_result("jid2", 25, 0, 35, 10, 20202020L);
+ test_case_result::shared_ptr tcrp3 = make_result("jid3", 0, 15, 5, 2, 555555555L);
+ test_case_result::shared_ptr tcrp4 = make_result("jid4", 100, 100, 100, 100, 323232324L);
+ test_case_result::shared_ptr tcrp5 = make_result("jid5", 5, 0, 0, 0, 100L);
+ tcrp5->add_exception(string("error 1"), false);
+ test_case_result::shared_ptr tcrp6 = make_result("jid6", 0, 5, 0, 0, 100L);
+ jexception e(0x123, "exception 2");
+ tcrp6->add_exception(e, false);
+ test_case_result::shared_ptr tcrp7 = make_result("jid7", 0, 0, 0, 0, 0L);
+ test_case_result::shared_ptr tcrp8 = make_result("jid8", 200, 100, 300, 12, 222222022L);
+
+ test_case_result_agregation tcra;
+ check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L);
+ tcra.add_test_result(tcrp1);
+ check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L);
+ tcra.add_test_result(tcrp2);
+ check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L);
+ tcra.add_test_result(tcrp3);
+ check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L);
+ tcra.add_test_result(tcrp4);
+ check_agregate(tcra, 135, 125, 140, 4, 0, 114, 0L);
+ tcra.add_test_result(tcrp5);
+ check_agregate(tcra, 140, 125, 140, 5, 1, 114, 100L);
+ tcra.add_test_result(tcrp6);
+ check_agregate(tcra, 140, 130, 140, 6, 2, 114, 200L);
+ tcra.add_test_result(tcrp7);
+ check_agregate(tcra, 140, 130, 140, 7, 2, 114, 200L);
+ tcra.add_test_result(tcrp8);
+ check_agregate(tcra, 340, 230, 440, 8, 2, 126, 222222222L);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp
new file mode 100644
index 0000000000..adbdf6884b
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp
@@ -0,0 +1,147 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <cstddef>
+#include <iostream>
+#include <sys/stat.h>
+#include "test_case.h"
+#include "test_case_set.h"
+
+using namespace boost::unit_test;
+using namespace mrg::jtt;
+using namespace std;
+
+QPID_AUTO_TEST_SUITE(jtt_test_case_set)
+
+const string csv_file("_ut_test_case_set.csv");
+const string test_filename("_ut_test_case_set");
+
+// === Helper functions ===
+
+bool check_csv_file(const char* filename)
+{
+ struct stat s;
+ if (::stat(filename, &s))
+ return false;
+ if (S_ISREG(s.st_mode))
+ return true;
+ return false;
+}
+
+// === Test suite ===
+
+QPID_AUTO_TEST_CASE(constructor)
+{
+ cout << test_filename << ".constructor: " << flush;
+ test_case_set tcs;
+ BOOST_CHECK(tcs.empty());
+ BOOST_CHECK_EQUAL(tcs.size(), unsigned(0));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(append_1)
+{
+ cout << test_filename << ".append_1: " << flush;
+ const unsigned test_case_num = 0x12345;
+ const u_int32_t num_msgs = 0x100;
+ const std::size_t min_data_size = 0x1000;
+ const std::size_t max_data_size = 0;
+ const bool auto_deq = true;
+ const std::size_t min_xid_size = 0x200;
+ const std::size_t max_xid_size = 0x200;
+ using mrg::jtt::test_case;
+ const test_case::transient_t transient = test_case::JTT_PERSISTNET;
+ const test_case::external_t external = test_case::JDL_INTERNAL;
+ const string comment = "This is a test";
+
+ test_case_set tcs;
+ tcs.append(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size,
+ max_xid_size, transient, external, comment);
+ BOOST_CHECK(!tcs.empty());
+ BOOST_CHECK_EQUAL(tcs.size(), unsigned(1));
+ test_case::shared_ptr tcp = tcs[0];
+ BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num);
+ BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs);
+ BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size);
+ BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size);
+ BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size);
+ BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size);
+ BOOST_CHECK_EQUAL(tcp->transient(), transient);
+ BOOST_CHECK_EQUAL(tcp->external(), external);
+ BOOST_CHECK_EQUAL(tcp->comment(), comment);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(append_2)
+{
+ cout << test_filename << ".append_2: " << flush;
+ const unsigned test_case_num = 0x12345;
+ const u_int32_t num_msgs = 0x100;
+ const std::size_t min_data_size = 0x1000;
+ const std::size_t max_data_size = 0;
+ const bool auto_deq = true;
+ const std::size_t min_xid_size = 0x200;
+ const std::size_t max_xid_size = 0x200;
+ using mrg::jtt::test_case;
+ const test_case::transient_t transient = test_case::JTT_PERSISTNET;
+ const test_case::external_t external = test_case::JDL_INTERNAL;
+ const string comment = "This is a test";
+
+ test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size, max_data_size,
+ auto_deq, min_xid_size, max_xid_size, transient, external, comment));
+ test_case_set tcs;
+ tcs.append(tcp);
+ BOOST_CHECK(!tcs.empty());
+ BOOST_CHECK_EQUAL(tcs.size(), unsigned(1));
+ tcp = tcs[0];
+ BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num);
+ BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs);
+ BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size);
+ BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size);
+ BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size);
+ BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size);
+ BOOST_CHECK_EQUAL(tcp->transient(), transient);
+ BOOST_CHECK_EQUAL(tcp->external(), external);
+ BOOST_CHECK_EQUAL(tcp->comment(), comment);
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_CASE(append_from_csv)
+{
+ cout << test_filename << ".append_from_csv: " << flush;
+ test_case_set tcs;
+ BOOST_REQUIRE_MESSAGE(check_csv_file(csv_file.c_str()), "Test CSV file \"" << csv_file <<
+ "\" is missing.");
+ tcs.append_from_csv(csv_file, false);
+ BOOST_CHECK(!tcs.empty());
+ BOOST_CHECK_EQUAL(tcs.size(), unsigned(44));
+ BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(0));
+ tcs.clear();
+ BOOST_CHECK(tcs.empty());
+ tcs.append_from_csv(csv_file, true);
+ BOOST_CHECK(!tcs.empty());
+ BOOST_CHECK_EQUAL(tcs.size(), unsigned(18));
+ BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(26));
+ cout << "ok" << endl;
+}
+
+QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv
new file mode 100644
index 0000000000..f886186275
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv
@@ -0,0 +1,74 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",,
+"Col. 0","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"
+"Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment"
+,,,,,,,,,,,,,,,,,,,,
+"Initialize only",,,,,,,,,,,,,,,,,,,,
+0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only"
+,,,,,,,,,,,,,,,,,,,,
+"Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,,
+1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message"
+2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message"
+3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]"
+4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]"
+5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]"
+6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]"
+7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]"
+8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]"
+9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]"
+10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]"
+11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]"
+12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]"
+13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]"
+14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]"
+15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]"
+16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]"
+17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]"
+18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]"
+19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]"
+20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]"
+21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]"
+22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]"
+23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]"
+24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]"
+25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]"
+26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]"
+27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]"
+28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]"
+29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]"
+30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]"
+31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]"
+32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]"
+,,,,,,,,,,,,,,,,,,,,
+"High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,,
+33,"M",1,5000000,0,5000000,0,0,100,1,100,FALSE,RANDOM,RANDOM,244,2,0,0,0,0,"100 bytes xid max + 100 bytes data max [txn]"
+34,"M",3,3000000,0,3000000,0,0,300,1,300,FALSE,RANDOM,RANDOM,644,6,0,0,0,0,"300 bytes xid max + 300 bytes data max [txn]"
+35,"M",10,1600000,0,1600000,0,0,1000,1,1000,FALSE,RANDOM,RANDOM,2044,16,0,0,0,0,"1000 bytes xid max + 1000 bytes data max [txn]"
+36,"M",30,600000,0,600000,0,0,3000,1,3000,FALSE,RANDOM,RANDOM,6044,48,0,0,0,0,"3000 bytes xid max + 3000 bytes data max [txn]"
+37,"M",100,200000,0,200000,0,0,10000,1,10000,FALSE,RANDOM,RANDOM,20044,157,0,0,0,0,"10000 bytes xid max + 10000 bytes data max [txn]"
+38,"M",300,60000,0,60000,0,0,30000,1,30000,FALSE,RANDOM,RANDOM,60044,470,0,0,0,0,"30000 bytes xid max + 30000 bytes data max [txn]"
+39,"M",1000,20000,0,20000,0,0,100000,1,100000,FALSE,RANDOM,RANDOM,200044,1563,0,0,0,0,"100000 bytes xid max + 100000 bytes data max [txn]"
+,,,,,,,,,,,,,,,,,,,,
+"STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,,
+40,"M",1,10000000,0,10000000,0,212,212,0,0,FALSE,FALSE,FALSE,256,2,0,0,0,0,"212 bytes data (2 dblks enq)"
+41,"M",1,10000000,0,10000000,0,148,148,64,64,FALSE,FALSE,FALSE,256,2,0,0,0,0,"148 bytes data + 64 bytes xid (2 dblks enq)"
+42,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)"
+43,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)"
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp
new file mode 100644
index 0000000000..0f041c380e
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp
@@ -0,0 +1,226 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "args.h"
+
+#include <cstddef>
+#include <iostream>
+
+namespace po = boost::program_options;
+
+namespace mrg
+{
+namespace jtt
+{
+
+args::args(std::string opt_title):
+ _options_descr(opt_title),
+ format_chk(false),
+ keep_jrnls(false),
+ lld_rd_num(10),
+ lld_skip_num(100),
+ num_jrnls(1),
+ pause_secs(0),
+ randomize(false),
+ read_mode(),
+ read_prob(50),
+ recover_mode(false),
+ repeat_flag(false),
+ reuse_instance(false),
+ seed(0)
+{
+ _options_descr.add_options()
+ ("csv-file,c",
+ po::value<std::string>(&test_case_csv_file_name)->default_value("jtt.csv"),
+ "CSV file containing test cases.")
+
+ ("format-chk",
+ po::value<bool>(&format_chk)->zero_tokens(),
+ "Check the format of each journal file.")
+
+ ("help,h", "This help message.")
+
+ ("jrnl-dir",
+ po::value<std::string>(&journal_dir)->default_value("/var/tmp/jtt"),
+ "Directory in which journal files will be placed.")
+
+ ("keep-jrnls",
+ po::value<bool>(&keep_jrnls)->zero_tokens(),
+ "Keep all test journals.")
+
+ ("lld-rd-num",
+ po::value<unsigned>(&lld_rd_num)->default_value(10),
+ "Number of consecutive messages to read after only dequeueing lld-skip-num "
+ "messages during lazy-loading. Ignored if read-mode is not set to LAZYLOAD.")
+
+ ("lld-skip-num",
+ po::value<unsigned>(&lld_skip_num)->default_value(100),
+ "Number of consecutive messages to dequeue only (without reading) prior to "
+ "reading lld-rd-num messages. Ignored if read-mode is not set to LAZYLOAD.")
+
+ ("num-jrnls",
+ po::value<unsigned>(&num_jrnls)->default_value(1),
+ "Number of simultaneous journal instances to test.")
+
+ ("pause",
+ po::value<unsigned>(&pause_secs)->default_value(0),
+ "Pause in seconds between test cases (allows disk to catch up).")
+
+ ("randomize",
+ po::value<bool>(&randomize)->zero_tokens(),
+ "Randomize the order of the tests.")
+
+ ("read-mode",
+ po::value<read_arg>(&read_mode)->default_value(read_arg::NONE),
+ read_arg::descr().c_str())
+
+ ("read-prob",
+ po::value<unsigned>(&read_prob)->default_value(50),
+ "Read probability (percent) for each message when read-mode is set to RANDOM.")
+
+ ("recover-mode",
+ po::value<bool>(&recover_mode)->zero_tokens(),
+ "Recover journal from the previous test for each test case.")
+
+ ("repeat",
+ po::value<bool>(&repeat_flag)->zero_tokens(),
+ "Repeat all test cases indefinitely.")
+
+ ("reuse-instance",
+ po::value<bool>(&reuse_instance)->zero_tokens(),
+ "Reuse journal instance for all test cases.")
+
+ ("seed",
+ po::value<unsigned>(&seed)->default_value(0),
+ "Seed for use in random number generator.")
+
+ ("analyzer",
+ po::value<std::string>(&jfile_analyzer)->default_value("./file_chk.py"),
+ "Journal file analyzer program to use when the --format-chk option is used, ignored otherwise.")
+
+ ;
+}
+
+bool
+args::parse(int argc, char** argv) // return true if error, false if ok
+{
+ try
+ {
+ po::store(po::parse_command_line(argc, argv, _options_descr), _vmap);
+ po::notify(_vmap);
+ }
+ catch (const std::exception& e)
+ {
+ std::cout << "ERROR: " << e.what() << std::endl;
+ return usage();
+ }
+ if (_vmap.count("help"))
+ return usage();
+ if (num_jrnls == 0)
+ {
+ std::cout << "ERROR: num-jrnls must be 1 or more." << std::endl;
+ return usage();
+ }
+ if (read_prob > 100) // read_prob is unsigned, so no need to check < 0
+ {
+ std::cout << "ERROR: read-prob must be between 0 and 100 inclusive." << std::endl;
+ return usage();
+ }
+ if (repeat_flag && keep_jrnls)
+ {
+ std::string resp;
+ std::cout << "WARNING: repeat and keep-jrnls: Monitor disk usage as test journals will"
+ " accumulate." << std::endl;
+ std::cout << "Continue? <y/n> ";
+ std::cin >> resp;
+ if (resp.size() == 1)
+ {
+ if (resp[0] != 'y' && resp[0] != 'Y')
+ return true;
+ }
+ else if (resp.size() == 3) // any combo of lower- and upper-case
+ {
+ if (resp[0] != 'y' && resp[0] != 'Y')
+ return true;
+ if (resp[1] != 'e' && resp[1] != 'E')
+ return true;
+ if (resp[2] != 's' && resp[2] != 'S')
+ return true;
+ }
+ else
+ return true;
+ }
+ return false;
+}
+
+bool
+args::usage() const
+{
+ std::cout << _options_descr << std::endl;
+ return true;
+}
+
+void
+args::print_args() const
+{
+ std::cout << "Number of journals: " << num_jrnls << std::endl;
+ std::cout << "Read mode: " << read_mode << std::endl;
+ if (read_mode.val() == read_arg::RANDOM)
+ std::cout << "Read probability: " << read_prob << " %" << std::endl;
+ if (read_mode.val() == read_arg::LAZYLOAD)
+ {
+ std::cout << "Lazy-load skips: " << lld_skip_num << std::endl;
+ std::cout << "Lazy-load reads: " << lld_rd_num << std::endl;
+ }
+ if (pause_secs)
+ std::cout << "Pause between test cases: " << pause_secs << " sec." << std::endl;
+ if (seed)
+ std::cout << "Randomize seed: " << seed << std::endl;
+ print_flags();
+}
+
+void
+args::print_flags() const
+{
+ if (format_chk || keep_jrnls || randomize || recover_mode || repeat_flag ||
+ reuse_instance)
+ {
+ std::cout << "Flag options:";
+ // TODO: Get flag args and their strings directly from _options_descr.
+ if (format_chk)
+ std::cout << " format-chk";
+ if (keep_jrnls)
+ std::cout << " keep-jrnls";
+ if (randomize)
+ std::cout << " randomize";
+ if (recover_mode)
+ std::cout << " recover-mode";
+ if (repeat_flag)
+ std::cout << " repeat-flag";
+ if (reuse_instance)
+ std::cout << " reuse-instance";
+ std::cout << std::endl;
+ }
+ std::cout << std::endl;
+}
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h
new file mode 100644
index 0000000000..b6f7fb4a79
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_args_hpp
+#define mrg_jtt_args_hpp
+
+#include <boost/program_options.hpp>
+#include "read_arg.h"
+
+namespace mrg
+{
+namespace jtt
+{
+
+ struct args
+ {
+ boost::program_options::options_description _options_descr;
+ boost::program_options::variables_map _vmap;
+
+ // Add args here
+ std::string jfile_analyzer;
+ std::string test_case_csv_file_name;
+ std::string journal_dir;
+ bool format_chk;
+ bool keep_jrnls;
+ unsigned lld_rd_num;
+ unsigned lld_skip_num;
+ unsigned num_jrnls;
+ unsigned pause_secs;
+ bool randomize;
+ read_arg read_mode;
+ unsigned read_prob;
+ bool recover_mode;
+ bool repeat_flag;
+ bool reuse_instance;
+ unsigned seed;
+
+ args(std::string opt_title);
+ bool parse(int argc, char** argv); // return true if error, false if ok
+ bool usage() const; // return true
+ void print_args() const;
+ void print_flags() const;
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_args_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp
new file mode 100644
index 0000000000..3530e0b223
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.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 "data_src.h"
+
+#include <cstddef>
+#include <iomanip>
+#include <sstream>
+
+namespace mrg
+{
+namespace jtt
+{
+
+char data_src::_data_src[data_src::max_dsize];
+char data_src::_xid_src[data_src::max_xsize];
+bool data_src::_initialized = data_src::__init();
+u_int64_t data_src::_xid_cnt = 0ULL;
+mrg::journal::smutex data_src::_sm;
+
+data_src::data_src()
+{}
+
+bool
+data_src::__init()
+{
+ for (unsigned i=0; i<max_dsize; i++)
+ _data_src[i] = '0' + ((i + 1) % 10); // 123456789012345...
+ for (unsigned j=0; j<max_xsize; j++)
+ _xid_src[j] = 'a' + (j % 26); // abc...xyzabc...
+ return true;
+}
+
+const char*
+data_src::get_data(const std::size_t offs)
+{
+ if (offs >= max_dsize) return 0;
+ return _data_src + offs;
+}
+
+std::string
+data_src::get_xid(const std::size_t xid_size)
+{
+ if (xid_size == 0)
+ return "";
+ std::ostringstream oss;
+ oss << std::setfill('0');
+ if (xid_size < 9)
+ oss << std::setw(xid_size) << get_xid_cnt();
+ else if (xid_size < 13)
+ oss << "xid:" << std::setw(xid_size - 4) << get_xid_cnt();
+ else
+ {
+ oss << "xid:" << std::setw(8) << get_xid_cnt() << ":";
+ oss.write(get_xid_content(13), xid_size - 13);
+ }
+ return oss.str();
+}
+
+const char*
+data_src::get_xid_content(const std::size_t offs)
+{
+ if (offs >= max_xsize) return 0;
+ return _xid_src + offs;
+}
+
+} // namespace jtt
+} // namespace mrg
+
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h
new file mode 100644
index 0000000000..66dc613787
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_data_src_hpp
+#define mrg_jtt_data_src_hpp
+
+#include <cstddef>
+#include "qpid/legacystore/jrnl/slock.h"
+#include "qpid/legacystore/jrnl/smutex.h"
+#include <pthread.h>
+#include <string>
+#include <sys/types.h>
+
+#define DATA_SIZE 1024 * 1024
+#define XID_SIZE 1024 * 1024
+
+namespace mrg
+{
+namespace jtt
+{
+ class data_src
+ {
+ public:
+ static const std::size_t max_dsize = DATA_SIZE;
+ static const std::size_t max_xsize = XID_SIZE;
+
+ private:
+ static char _data_src[];
+ static char _xid_src[];
+ static u_int64_t _xid_cnt;
+ static bool _initialized;
+ static mrg::journal::smutex _sm;
+
+ public:
+ static const char* get_data(const std::size_t offs);
+ static std::string get_xid(const std::size_t xid_size);
+
+ private:
+ data_src();
+ static u_int64_t get_xid_cnt() { mrg::journal::slock s(_sm); return _xid_cnt++; }
+ static const char* get_xid_content(const std::size_t offs);
+ static bool __init();
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_data_src_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py
new file mode 100755
index 0000000000..36ef511f5c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py
@@ -0,0 +1,838 @@
+#!/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 getopt
+import string
+import xml.parsers.expat
+from struct import unpack, calcsize
+from time import gmtime, strftime
+
+dblk_size = 128
+sblk_size = 4 * dblk_size
+jfsize = None
+hdr_ver = 1
+
+TEST_NUM_COL = 0
+NUM_MSGS_COL = 5
+MIN_MSG_SIZE_COL = 7
+MAX_MSG_SIZE_COL = 8
+MIN_XID_SIZE_COL = 9
+MAX_XID_SIZE_COL = 10
+AUTO_DEQ_COL = 11
+TRANSIENT_COL = 12
+EXTERN_COL = 13
+COMMENT_COL = 20
+
+owi_mask = 0x01
+transient_mask = 0x10
+extern_mask = 0x20
+
+printchars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ '
+
+
+
+#== global functions ===========================================================
+
+def load(f, klass):
+ args = load_args(f, klass)
+ subclass = klass.discriminate(args)
+ result = subclass(*args)
+ if subclass != klass:
+ result.init(f, *load_args(f, subclass))
+ result.skip(f)
+ return result;
+
+def load_args(f, klass):
+ size = calcsize(klass.format)
+ foffs = f.tell(),
+ bin = f.read(size)
+ if len(bin) != size:
+ raise Exception("end of file")
+ return foffs + unpack(klass.format, bin)
+
+def size_blks(size, blk_size):
+ return (size + blk_size - 1)/blk_size
+
+def rem_in_blk(f, blk_size):
+ foffs = f.tell()
+ return (size_blks(f.tell(), blk_size) * blk_size) - foffs;
+
+def file_full(f):
+ return f.tell() >= jfsize
+
+def isprintable(s):
+ return s.strip(printchars) == ''
+
+def print_xid(xidsize, xid):
+ if xid == None:
+ if xidsize > 0:
+ raise Exception('Inconsistent XID size: xidsize=%d, xid=None' % xidsize)
+ return ''
+ if isprintable(xid):
+ xidstr = split_str(xid)
+ else:
+ xidstr = hex_split_str(xid)
+ if xidsize != len(xid):
+ raise Exception('Inconsistent XID size: xidsize=%d, xid(%d)=\"%s\"' % (xidsize, len(xid), xidstr))
+ return 'xid(%d)=\"%s\" ' % (xidsize, xidstr)
+
+def print_data(dsize, data):
+ if data == None:
+ return ''
+ if isprintable(data):
+ datastr = split_str(data)
+ else:
+ datastr = hex_split_str(data)
+ if dsize != len(data):
+ raise Exception('Inconsistent data size: dsize=%d, data(%d)=\"%s\"' % (dsize, len(data), datastr))
+ return 'data(%d)=\"%s\" ' % (dsize, datastr)
+
+def hex_split_str(s, split_size = 50):
+ if len(s) <= split_size:
+ return hex_str(s, 0, len(s))
+ if len(s) > split_size + 25:
+ return hex_str(s, 0, 10) + ' ... ' + hex_str(s, 55, 65) + ' ... ' + hex_str(s, len(s)-10, len(s))
+ return hex_str(s, 0, 10) + ' ... ' + hex_str(s, len(s)-10, len(s))
+
+def hex_str(s, b, e):
+ o = ''
+ for i in range(b, e):
+ if isprintable(s[i]):
+ o += s[i]
+ else:
+ o += '\\%02x' % ord(s[i])
+ return o
+
+def split_str(s, split_size = 50):
+ if len(s) < split_size:
+ return s
+ return s[:25] + ' ... ' + s[-25:]
+
+def inv_str(s):
+ si = ''
+ for i in range(0,len(s)):
+ si += chr(~ord(s[i]) & 0xff)
+ return si
+
+def load_file_data(f, size, data):
+ if size == 0:
+ return (data, True)
+ if data == None:
+ loaded = 0
+ else:
+ loaded = len(data)
+ foverflow = f.tell() + size - loaded > jfsize
+ if foverflow:
+ rsize = jfsize - f.tell()
+ else:
+ rsize = size - loaded
+ bin = f.read(rsize)
+ if data == None:
+ data = unpack('%ds' % (rsize), bin)[0]
+ else:
+ data = data + unpack('%ds' % (rsize), bin)[0]
+ return (data, not foverflow)
+
+def exit(code, qflag):
+ if code != 0 or not qflag:
+ print out.getvalue()
+ out.close()
+ sys.exit(code)
+
+#== class Sizeable =============================================================
+
+class Sizeable:
+
+ def size(self):
+ classes = [self.__class__]
+
+ size = 0
+ while classes:
+ cls = classes.pop()
+ if hasattr(cls, "format"):
+ size += calcsize(cls.format)
+ classes.extend(cls.__bases__)
+
+ return size
+
+
+#== class Hdr ==================================================================
+
+class Hdr(Sizeable):
+
+ format = '=4sBBHQ'
+
+ def discriminate(args):
+ return CLASSES.get(args[1][-1], Hdr)
+ discriminate = staticmethod(discriminate)
+
+ def __init__(self, foffs, magic, ver, end, flags, rid):
+ self.foffs = foffs
+ self.magic = magic
+ self.ver = ver
+ self.end = end
+ self.flags = flags
+ self.rid = rid
+ if self.magic[-1] not in ['0x00', 'a', 'c', 'd', 'e', 'f', 'x']:
+ error = 3
+
+ def __str__(self):
+ if self.empty():
+ return '0x%08x: <empty>' % (self.foffs)
+ if self.magic[-1] == 'x':
+ return '0x%08x: [\"%s\"]' % (self.foffs, self.magic)
+ if self.magic[-1] in ['a', 'c', 'd', 'e', 'f', 'x']:
+ return '0x%08x: [\"%s\" v=%d e=%d f=0x%04x rid=0x%x]' % (self.foffs, self.magic, self.ver, self.end, self.flags, self.rid)
+ return '0x%08x: <error, unknown magic \"%s\" (possible overwrite boundary?)>' % (self.foffs, self.magic)
+
+ def empty(self):
+ return self.magic == '\x00'*4
+
+ def owi(self):
+ return self.flags & owi_mask != 0
+
+ def skip(self, f):
+ f.read(rem_in_blk(f, dblk_size))
+
+ def check(self):
+ if self.empty() or self.magic[:3] != 'RHM' or self.magic[3] not in ['a', 'c', 'd', 'e', 'f', 'x']:
+ return True
+ if self.ver != hdr_ver and self.magic[-1] != 'x':
+ raise Exception('%s: Invalid header version: found %d, expected %d.' % (self, self.ver, hdr_ver))
+ return False
+
+
+#== class FileHdr ==============================================================
+
+class FileHdr(Hdr):
+
+ format = '=2H4x3Q'
+
+ def init(self, f, foffs, fid, lid, fro, time_sec, time_ns):
+ self.fid = fid
+ self.lid = lid
+ self.fro = fro
+ self.time_sec = time_sec
+ self.time_ns = time_ns
+
+ def __str__(self):
+ return '%s fid=%d lid=%d fro=0x%08x t=%s' % (Hdr.__str__(self), self.fid, self.lid, self.fro, self.timestamp_str())
+
+ def skip(self, f):
+ f.read(rem_in_blk(f, sblk_size))
+
+ def timestamp(self):
+ return (self.time_sec, self.time_ns)
+
+ def timestamp_str(self):
+ ts = gmtime(self.time_sec)
+ fstr = '%%a %%b %%d %%H:%%M:%%S.%09d %%Y' % (self.time_ns)
+ return strftime(fstr, ts)
+
+
+#== class DeqHdr ===============================================================
+
+class DeqHdr(Hdr):
+
+ format = '=QQ'
+
+ def init(self, f, foffs, deq_rid, xidsize):
+ self.deq_rid = deq_rid
+ self.xidsize = xidsize
+ self.xid = None
+ self.deq_tail = None
+ self.xid_complete = False
+ self.tail_complete = False
+ self.tail_bin = None
+ self.tail_offs = 0
+ self.load(f)
+
+ def load(self, f):
+ if self.xidsize == 0:
+ self.xid_complete = True
+ self.tail_complete = True
+ else:
+ if not self.xid_complete:
+ ret = load_file_data(f, self.xidsize, self.xid)
+ self.xid = ret[0]
+ self.xid_complete = ret[1]
+ if self.xid_complete and not self.tail_complete:
+ ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin)
+ self.tail_bin = ret[0]
+ if ret[1]:
+ self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin))
+ if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid:
+ print " > %s" % self
+ raise Exception('Invalid dequeue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs))
+ self.enq_tail.skip(f)
+ self.tail_complete = ret[1]
+ return self.complete()
+
+ def complete(self):
+ return self.xid_complete and self.tail_complete
+
+ def __str__(self):
+ return '%s %sdrid=0x%x' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), self.deq_rid)
+
+
+#== class TxnHdr ===============================================================
+
+class TxnHdr(Hdr):
+
+ format = '=Q'
+
+ def init(self, f, foffs, xidsize):
+ self.xidsize = xidsize
+ self.xid = None
+ self.tx_tail = None
+ self.xid_complete = False
+ self.tail_complete = False
+ self.tail_bin = None
+ self.tail_offs = 0
+ self.load(f)
+
+ def load(self, f):
+ if not self.xid_complete:
+ ret = load_file_data(f, self.xidsize, self.xid)
+ self.xid = ret[0]
+ self.xid_complete = ret[1]
+ if self.xid_complete and not self.tail_complete:
+ ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin)
+ self.tail_bin = ret[0]
+ if ret[1]:
+ self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin))
+ if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid:
+ print " > %s" % self
+ raise Exception('Invalid transaction record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs))
+ self.enq_tail.skip(f)
+ self.tail_complete = ret[1]
+ return self.complete()
+
+ def complete(self):
+ return self.xid_complete and self.tail_complete
+
+ def __str__(self):
+ return '%s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid))
+
+
+#== class RecTail ==============================================================
+
+class RecTail(Sizeable):
+
+ format = '=4sQ'
+
+ def __init__(self, foffs, magic_inv, rid):
+ self.foffs = foffs
+ self.magic_inv = magic_inv
+ self.rid = rid
+
+ def __str__(self):
+ magic = inv_str(self.magic_inv)
+ return '[\"%s\" rid=0x%x]' % (magic, self.rid)
+
+ def skip(self, f):
+ f.read(rem_in_blk(f, dblk_size))
+
+
+#== class EnqRec ===============================================================
+
+class EnqRec(Hdr):
+
+ format = '=QQ'
+
+ def init(self, f, foffs, xidsize, dsize):
+ self.xidsize = xidsize
+ self.dsize = dsize
+ self.transient = self.flags & transient_mask > 0
+ self.extern = self.flags & extern_mask > 0
+ self.xid = None
+ self.data = None
+ self.enq_tail = None
+ self.xid_complete = False
+ self.data_complete = False
+ self.tail_complete = False
+ self.tail_bin = None
+ self.tail_offs = 0
+ self.load(f)
+
+ def load(self, f):
+ if not self.xid_complete:
+ ret = load_file_data(f, self.xidsize, self.xid)
+ self.xid = ret[0]
+ self.xid_complete = ret[1]
+ if self.xid_complete and not self.data_complete:
+ if self.extern:
+ self.data_complete = True
+ else:
+ ret = load_file_data(f, self.dsize, self.data)
+ self.data = ret[0]
+ self.data_complete = ret[1]
+ if self.data_complete and not self.tail_complete:
+ ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin)
+ self.tail_bin = ret[0]
+ if ret[1]:
+ self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin))
+ if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid:
+ print " > %s" % self
+ raise Exception('Invalid enqueue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs))
+ self.enq_tail.skip(f)
+ self.tail_complete = ret[1]
+ return self.complete()
+
+ def complete(self):
+ return self.xid_complete and self.data_complete and self.tail_complete
+
+ def print_flags(self):
+ s = ''
+ if self.transient:
+ s = '*TRANSIENT'
+ if self.extern:
+ if len(s) > 0:
+ s += ',EXTERNAL'
+ else:
+ s = '*EXTERNAL'
+ if len(s) > 0:
+ s += '*'
+ return s
+
+ def __str__(self):
+ return '%s %s%s %s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), print_data(self.dsize, self.data), self.enq_tail, self.print_flags())
+
+
+#== class Main =================================================================
+
+class Main:
+ def __init__(self, argv):
+ self.bfn = None
+ self.csvfn = None
+ self.jdir = None
+ self.aflag = False
+ self.hflag = False
+ self.qflag = False
+ self.tnum = None
+ self.num_jfiles = None
+ self.num_msgs = None
+ self.msg_len = None
+ self.auto_deq = None
+ self.xid_len = None
+ self.transient = None
+ self.extern = None
+
+ self.file_start = 0
+ self.file_num = 0
+ self.fro = 0x200
+ self.emap = {}
+ self.tmap = {}
+ self.rec_cnt = 0
+ self.msg_cnt = 0
+ self.txn_msg_cnt = 0
+ self.fhdr = None
+ self.f = None
+ self.first_rec = False
+ self.last_file = False
+ self.last_rid = -1
+ self.fhdr_owi_at_msg_start = None
+
+ self.proc_args(argv)
+ self.proc_csv()
+ self.read_jinf()
+
+ def run(self):
+ try:
+ start_info = self.analyze_files()
+ stop = self.advance_file(*start_info)
+ except Exception:
+ print 'WARNING: All journal files are empty.'
+ if self.num_msgs > 0:
+ raise Exception('All journal files are empty, but %d msgs expectd.' % self.num_msgs)
+ else:
+ stop = True
+ while not stop:
+ warn = ''
+ if file_full(self.f):
+ stop = self.advance_file()
+ if stop:
+ break
+ hdr = load(self.f, Hdr)
+ if hdr.empty():
+ stop = True;
+ break
+ if hdr.check():
+ stop = True;
+ else:
+ self.rec_cnt += 1
+ self.fhdr_owi_at_msg_start = self.fhdr.owi()
+ if self.first_rec:
+ if self.fhdr.fro != hdr.foffs:
+ raise Exception('File header first record offset mismatch: fro=0x%08x; rec_offs=0x%08x' % (self.fhdr.fro, hdr.foffs))
+ else:
+ if not self.qflag: print ' * fro ok: 0x%08x' % self.fhdr.fro
+ self.first_rec = False
+ if isinstance(hdr, EnqRec) and not stop:
+ while not hdr.complete():
+ stop = self.advance_file()
+ if stop:
+ break
+ hdr.load(self.f)
+ if self.extern != None:
+ if hdr.extern:
+ if hdr.data != None:
+ raise Exception('Message data found on external record')
+ else:
+ if self.msg_len > 0 and len(hdr.data) != self.msg_len:
+ raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len))
+ else:
+ if self.msg_len > 0 and len(hdr.data) != self.msg_len:
+ raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len))
+ if self.xid_len > 0 and len(hdr.xid) != self.xid_len:
+ print ' ERROR: XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len)
+ sys.exit(1)
+ #raise Exception('XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len))
+ if self.transient != None:
+ if self.transient:
+ if not hdr.transient:
+ raise Exception('Expected transient record, found persistent')
+ else:
+ if hdr.transient:
+ raise Exception('Expected persistent record, found transient')
+ stop = not self.check_owi(hdr)
+ if stop:
+ warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)'
+ else:
+ self.msg_cnt += 1
+ if self.aflag or self.auto_deq:
+ if hdr.xid == None:
+ self.emap[hdr.rid] = (self.fhdr.fid, hdr, False)
+ else:
+ self.txn_msg_cnt += 1
+ if hdr.xid in self.tmap:
+ self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append tuple to existing list
+ else:
+ self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list
+ elif isinstance(hdr, DeqHdr) and not stop:
+ while not hdr.complete():
+ stop = self.advance_file()
+ if stop:
+ break
+ hdr.load(self.f)
+ stop = not self.check_owi(hdr)
+ if stop:
+ warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)'
+ else:
+ if self.auto_deq != None:
+ if not self.auto_deq:
+ warn = ' WARNING: Dequeue record rid=%d found in non-dequeue test - ignoring.' % hdr.rid
+ if self.aflag or self.auto_deq:
+ if hdr.xid == None:
+ if hdr.deq_rid in self.emap:
+ if self.emap[hdr.deq_rid][2]:
+ warn = ' (WARNING: dequeue rid 0x%x dequeues locked enqueue record 0x%x)' % (hdr.rid, hdr.deq_rid)
+ del self.emap[hdr.deq_rid]
+ else:
+ warn = ' (WARNING: rid being dequeued 0x%x not found in enqueued records)' % hdr.deq_rid
+ else:
+ if hdr.deq_rid in self.emap:
+ t = self.emap[hdr.deq_rid]
+ self.emap[hdr.deq_rid] = (t[0], t[1], True) # Lock enq record
+ if hdr.xid in self.tmap:
+ self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append to existing list
+ else:
+ self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list
+ elif isinstance(hdr, TxnHdr) and not stop:
+ while not hdr.complete():
+ stop = self.advance_file()
+ if stop:
+ break
+ hdr.load(self.f)
+ stop = not self.check_owi(hdr)
+ if stop:
+ warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)'
+ else:
+ if hdr.xid in self.tmap:
+ mismatched_rids = []
+ if hdr.magic[-1] == 'c': # commit
+ for rec in self.tmap[hdr.xid]:
+ if isinstance(rec[1], EnqRec):
+ self.emap[rec[1].rid] = (rec[0], rec[1], False) # Transfer enq to emap
+ elif isinstance(rec[1], DeqHdr):
+ if rec[1].deq_rid in self.emap:
+ del self.emap[rec[1].deq_rid] # Delete from emap
+ else:
+ mismatched_rids.append('0x%x' % rec[1].deq_rid)
+ else:
+ raise Exception('Unknown header found in txn map: %s' % rec[1])
+ elif hdr.magic[-1] == 'a': # abort
+ for rec in self.tmap[hdr.xid]:
+ if isinstance(rec[1], DeqHdr):
+ if rec[1].deq_rid in self.emap:
+ t = self.emap[rec[1].deq_rid]
+ self.emap[rec[1].deq_rid] = (t[0], t[1], False) # Unlock enq record
+ del self.tmap[hdr.xid]
+ if len(mismatched_rids) > 0:
+ warn = ' (WARNING: transactional dequeues not found in enqueue map; rids=%s)' % mismatched_rids
+ else:
+ warn = ' (WARNING: %s not found in transaction map)' % print_xid(len(hdr.xid), hdr.xid)
+ if not self.qflag: print ' > %s%s' % (hdr, warn)
+ if not stop:
+ stop = (self.last_file and hdr.check()) or hdr.empty() or self.fhdr.empty()
+
+ def analyze_files(self):
+ fname = ''
+ fnum = -1
+ rid = -1
+ fro = -1
+ tss = ''
+ if not self.qflag: print 'Analyzing journal files:'
+ owi_found = False
+ for i in range(0, self.num_jfiles):
+ jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % i
+ f = open(jfn)
+ fhdr = load(f, Hdr)
+ if fhdr.empty():
+ if not self.qflag:
+ print ' %s: file empty' % jfn
+ break
+ if i == 0:
+ init_owi = fhdr.owi()
+ fname = jfn
+ fnum = i
+ rid = fhdr.rid
+ fro = fhdr.fro
+ tss = fhdr.timestamp_str()
+ elif fhdr.owi() != init_owi and not owi_found:
+ fname = jfn
+ fnum = i
+ rid = fhdr.rid
+ fro = fhdr.fro
+ tss = fhdr.timestamp_str()
+ owi_found = True
+ if not self.qflag:
+ print ' %s: owi=%s rid=0x%x, fro=0x%08x ts=%s' % (jfn, fhdr.owi(), fhdr.rid, fhdr.fro, fhdr.timestamp_str())
+ if fnum < 0 or rid < 0 or fro < 0:
+ raise Exception('All journal files empty')
+ if not self.qflag: print ' Oldest complete file: %s: rid=%d, fro=0x%08x ts=%s' % (fname, rid, fro, tss)
+ return (fnum, rid, fro)
+
+ def advance_file(self, *start_info):
+ seek_flag = False
+ if len(start_info) == 3:
+ self.file_start = self.file_num = start_info[0]
+ self.fro = start_info[2]
+ seek_flag = True
+ if self.f != None and file_full(self.f):
+ self.file_num = self.incr_fnum()
+ if self.file_num == self.file_start:
+ return True
+ if self.file_start == 0:
+ self.last_file = self.file_num == self.num_jfiles - 1
+ else:
+ self.last_file = self.file_num == self.file_start - 1
+ if self.file_num < 0 or self.file_num >= self.num_jfiles:
+ raise Exception('Bad file number %d' % self.file_num)
+ jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % self.file_num
+ self.f = open(jfn)
+ self.fhdr = load(self.f, Hdr)
+ if seek_flag and self.f.tell() != self.fro:
+ self.f.seek(self.fro)
+ self.first_rec = True
+ if not self.qflag: print jfn, ": ", self.fhdr
+ return False
+
+ def incr_fnum(self):
+ self.file_num += 1
+ if self.file_num >= self.num_jfiles:
+ self.file_num = 0;
+ return self.file_num
+
+ def check_owi(self, hdr):
+ return self.fhdr_owi_at_msg_start == hdr.owi()
+
+ def check_rid(self, hdr):
+ if self.last_rid != -1 and hdr.rid <= self.last_rid:
+ return False
+ self.last_rid = hdr.rid
+ return True
+
+ def read_jinf(self):
+ filename = self.jdir + '/' + self.bfn + '.jinf'
+ try:
+ f = open(filename, 'r')
+ except IOError:
+ print 'ERROR: Unable to open jinf file %s' % filename
+ sys.exit(1)
+ p = xml.parsers.expat.ParserCreate()
+ p.StartElementHandler = self.handleStartElement
+ p.CharacterDataHandler = self.handleCharData
+ p.EndElementHandler = self.handleEndElement
+ p.ParseFile(f)
+ if self.num_jfiles == None:
+ print 'ERROR: number_jrnl_files not found in jinf file "%s"!' % filename
+ if jfsize == None:
+ print 'ERROR: jrnl_file_size_sblks not found in jinf file "%s"!' % filename
+ if self.num_jfiles == None or jfsize == None:
+ sys.exit(1)
+
+ def handleStartElement(self, name, attrs):
+ global jfsize
+ if name == 'number_jrnl_files':
+ self.num_jfiles = int(attrs['value'])
+ if name == 'jrnl_file_size_sblks':
+ jfsize = (int(attrs['value']) + 1) * sblk_size
+
+ def handleCharData(self, data): pass
+
+ def handleEndElement(self, name): pass
+
+ def proc_csv(self):
+ if self.csvfn != None and self.tnum != None:
+ tparams = self.get_test(self.csvfn, self.tnum)
+ if tparams == None:
+ print 'ERROR: Test %d not found in CSV file "%s"' % (self.tnum, self.csvfn)
+ sys.exit(1)
+ self.num_msgs = tparams['num_msgs']
+ if tparams['min_size'] == tparams['max_size']:
+ self.msg_len = tparams['max_size']
+ else:
+ self.msg_len = 0
+ self.auto_deq = tparams['auto_deq']
+ if tparams['xid_min_size'] == tparams['xid_max_size']:
+ self.xid_len = tparams['xid_max_size']
+ else:
+ self.xid_len = 0
+ self.transient = tparams['transient']
+ self.extern = tparams['extern']
+
+ def get_test(self, filename, tnum):
+ try:
+ f=open(filename, 'r')
+ except IOError:
+ print 'ERROR: Unable to open CSV file "%s"' % filename
+ sys.exit(1)
+ for l in f:
+ sl = l.strip().split(',')
+ if len(sl[0]) > 0 and sl[0][0] != '"':
+ try:
+ if (int(sl[TEST_NUM_COL]) == tnum):
+ return { 'num_msgs':int(sl[NUM_MSGS_COL]),
+ 'min_size':int(sl[MIN_MSG_SIZE_COL]),
+ 'max_size':int(sl[MAX_MSG_SIZE_COL]),
+ 'auto_deq':not (sl[AUTO_DEQ_COL] == 'FALSE' or sl[AUTO_DEQ_COL] == '0'),
+ 'xid_min_size':int(sl[MIN_XID_SIZE_COL]),
+ 'xid_max_size':int(sl[MAX_XID_SIZE_COL]),
+ 'transient':not (sl[TRANSIENT_COL] == 'FALSE' or sl[TRANSIENT_COL] == '0'),
+ 'extern':not (sl[EXTERN_COL] == 'FALSE' or sl[EXTERN_COL] == '0'),
+ 'comment':sl[COMMENT_COL] }
+ except Exception:
+ pass
+ return None
+
+ def proc_args(self, argv):
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "ab:c:d:hqt:", ["analyse", "base-filename=", "csv-filename=", "dir=", "help", "quiet", "test-num="])
+ except getopt.GetoptError:
+ self.usage()
+ sys.exit(2)
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ self.usage()
+ sys.exit()
+ if o in ("-a", "--analyze"):
+ self.aflag = True
+ if o in ("-b", "--base-filename"):
+ self.bfn = a
+ if o in ("-c", "--csv-filename"):
+ self.csvfn = a
+ if o in ("-d", "--dir"):
+ self.jdir = a
+ if o in ("-q", "--quiet"):
+ self.qflag = True
+ if o in ("-t", "--test-num"):
+ if not a.isdigit():
+ print 'ERROR: Illegal test-num argument. Must be a non-negative number'
+ sys.exit(2)
+ self.tnum = int(a)
+ if self.bfn == None or self.jdir == None:
+ print 'ERROR: Missing requred args.'
+ self.usage()
+ sys.exit(2)
+ if self.tnum != None and self.csvfn == None:
+ print 'ERROR: Test number specified, but not CSV file'
+ self.usage()
+ sys.exit(2)
+
+ def usage(self):
+ print 'Usage: %s opts' % sys.argv[0]
+ print ' where opts are in either short or long format (*=req\'d):'
+ print ' -a --analyze Analyze enqueue/dequeue records'
+ print ' -b --base-filename [string] * Base filename for journal files'
+ print ' -c --csv-filename [string] CSV filename containing test parameters'
+ print ' -d --dir [string] * Journal directory containing journal files'
+ print ' -h --help Print help'
+ print ' -q --quiet Quiet (reduced output)'
+ print ' -t --test-num [int] Test number from CSV file - only valid if CSV file named'
+
+ def report(self):
+ if not self.qflag:
+ print
+ print ' === REPORT ===='
+ if self.num_msgs > 0 and self.msg_cnt != self.num_msgs:
+ print 'WARNING: Found %d messages; %d expected.' % (self.msg_cnt, self.num_msgs)
+ if len(self.emap) > 0:
+ print
+ print 'Remaining enqueued records (sorted by rid): '
+ keys = sorted(self.emap.keys())
+ for k in keys:
+ if self.emap[k][2] == True: # locked
+ locked = ' (locked)'
+ else:
+ locked = ''
+ print " fid=%d %s%s" % (self.emap[k][0], self.emap[k][1], locked)
+ print 'WARNING: Enqueue-Dequeue mismatch, %d enqueued records remain.' % len(self.emap)
+ if len(self.tmap) > 0:
+ txn_rec_cnt = 0
+ print
+ print 'Remaining transactions: '
+ for t in self.tmap:
+ print_xid(len(t), t)
+ for r in self.tmap[t]:
+ print " fid=%d %s" % (r[0], r[1])
+ print " Total: %d records for xid %s" % (len(self.tmap[t]), t)
+ txn_rec_cnt += len(self.tmap[t])
+ print 'WARNING: Incomplete transactions, %d xids remain containing %d records.' % (len(self.tmap), txn_rec_cnt)
+ print '%d enqueues, %d journal records processed.' % (self.msg_cnt, self.rec_cnt)
+
+
+#===============================================================================
+
+CLASSES = {
+ "a": TxnHdr,
+ "c": TxnHdr,
+ "d": DeqHdr,
+ "e": EnqRec,
+ "f": FileHdr
+}
+
+m = Main(sys.argv)
+m.run()
+m.report()
+
+sys.exit(None)
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp
new file mode 100644
index 0000000000..1bc04110af
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.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 "jrnl_init_params.h"
+
+namespace mrg
+{
+namespace jtt
+{
+
+jrnl_init_params::jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename,
+ const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks):
+ _jid(jid),
+ _jdir(jdir),
+ _base_filename(base_filename),
+ _num_jfiles(num_jfiles),
+ _ae(ae),
+ _ae_max_jfiles(ae_max_jfiles),
+ _jfsize_sblks(jfsize_sblks),
+ _wcache_num_pages(wcache_num_pages),
+ _wcache_pgsize_sblks(wcache_pgsize_sblks)
+{}
+
+jrnl_init_params::jrnl_init_params(const jrnl_init_params& jp):
+ _jid(jp._jid),
+ _jdir(jp._jdir),
+ _base_filename(jp._base_filename),
+ _num_jfiles(jp._num_jfiles),
+ _ae(jp._ae),
+ _ae_max_jfiles(jp._ae_max_jfiles),
+ _jfsize_sblks(jp._jfsize_sblks),
+ _wcache_num_pages(jp._wcache_num_pages),
+ _wcache_pgsize_sblks(jp._wcache_pgsize_sblks)
+{}
+
+jrnl_init_params::jrnl_init_params(const jrnl_init_params* const jp_ptr):
+ _jid(jp_ptr->_jid),
+ _jdir(jp_ptr->_jdir),
+ _base_filename(jp_ptr->_base_filename),
+ _num_jfiles(jp_ptr->_num_jfiles),
+ _ae(jp_ptr->_ae),
+ _ae_max_jfiles(jp_ptr->_ae_max_jfiles),
+ _jfsize_sblks(jp_ptr->_jfsize_sblks),
+ _wcache_num_pages(jp_ptr->_wcache_num_pages),
+ _wcache_pgsize_sblks(jp_ptr->_wcache_pgsize_sblks)
+{}
+
+// static initializers
+
+const u_int16_t jrnl_init_params::def_num_jfiles = 8;
+const bool jrnl_init_params::def_ae = false;
+const u_int16_t jrnl_init_params::def_ae_max_jfiles = 0;
+const u_int32_t jrnl_init_params::def_jfsize_sblks = 0xc00;
+const u_int16_t jrnl_init_params::def_wcache_num_pages = 32;
+const u_int32_t jrnl_init_params::def_wcache_pgsize_sblks = 64;
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h
new file mode 100644
index 0000000000..ece87f8e03
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_jrnl_init_params_hpp
+#define mrg_jtt_jrnl_init_params_hpp
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+#include <sys/types.h>
+
+namespace mrg
+{
+namespace jtt
+{
+
+ class jrnl_init_params
+ {
+ public:
+ static const u_int16_t def_num_jfiles;
+ static const bool def_ae;
+ static const u_int16_t def_ae_max_jfiles;
+ static const u_int32_t def_jfsize_sblks;
+ static const u_int16_t def_wcache_num_pages;
+ static const u_int32_t def_wcache_pgsize_sblks;
+
+ typedef boost::shared_ptr<jrnl_init_params> shared_ptr;
+
+ private:
+ std::string _jid;
+ std::string _jdir;
+ std::string _base_filename;
+ u_int16_t _num_jfiles;
+ bool _ae;
+ u_int16_t _ae_max_jfiles;
+ u_int32_t _jfsize_sblks;
+ u_int16_t _wcache_num_pages;
+ u_int32_t _wcache_pgsize_sblks;
+
+ public:
+ jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename,
+ const u_int16_t num_jfiles = def_num_jfiles, const bool ae = def_ae,
+ const u_int16_t ae_max_jfiles = def_ae_max_jfiles, const u_int32_t jfsize_sblks = def_jfsize_sblks,
+ const u_int16_t wcache_num_pages = def_wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks = def_wcache_pgsize_sblks);
+ jrnl_init_params(const jrnl_init_params& jp);
+ jrnl_init_params(const jrnl_init_params* const jp_ptr);
+
+ inline const std::string& jid() const { return _jid; }
+ inline const std::string& jdir() const { return _jdir; }
+ inline const std::string& base_filename() const { return _base_filename; }
+ inline u_int16_t num_jfiles() const { return _num_jfiles; }
+ inline bool is_ae() const { return _ae; }
+ inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; }
+ inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; }
+ inline u_int16_t wcache_num_pages() const { return _wcache_num_pages; }
+ inline u_int32_t wcache_pgsize_sblks() const { return _wcache_pgsize_sblks; }
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_jrnl_init_params_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp
new file mode 100644
index 0000000000..339dc1b52c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp
@@ -0,0 +1,439 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "jrnl_instance.h"
+
+#include <cstdlib>
+#include "data_src.h"
+#include "qpid/legacystore/jrnl/data_tok.h"
+#include "qpid/legacystore/jrnl/jerrno.h"
+#include "test_case_result.h"
+
+#define MAX_WR_WAIT 10 // in ms
+#define MAX_RD_WAIT 100 // in ms
+#define MAX_ENQCAPTHRESH_CNT 1000 // 10s if MAX_WR_WAIT is 10 ms
+
+namespace mrg
+{
+namespace jtt
+{
+
+jrnl_instance::jrnl_instance(const std::string& jid, const std::string& jdir, const std::string& base_filename,
+ const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks,
+ const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks):
+ mrg::journal::jcntl(jid, jdir, base_filename),
+ _jpp(new jrnl_init_params(jid, jdir, base_filename, num_jfiles, ae, ae_max_jfiles, jfsize_sblks,
+ wcache_num_pages, wcache_pgsize_sblks)),
+ _args_ptr(0),
+ _dtok_master_enq_list(),
+ _dtok_master_txn_list(),
+ _dtok_rd_list(),
+ _dtok_deq_list(),
+ _rd_aio_cv(_rd_aio_mutex),
+ _wr_full_cv(_wr_full_mutex),
+ _rd_list_cv(_rd_list_mutex),
+ _deq_list_cv(_deq_list_mutex),
+ _tcp(),
+ _tcrp()
+{}
+
+jrnl_instance::jrnl_instance(const jrnl_init_params::shared_ptr& p):
+ mrg::journal::jcntl(p->jid(), p->jdir(), p->base_filename()),
+ _jpp(p),
+ _args_ptr(0),
+ _dtok_master_enq_list(),
+ _dtok_master_txn_list(),
+ _dtok_rd_list(),
+ _dtok_deq_list(),
+ _rd_aio_cv(_rd_aio_mutex),
+ _wr_full_cv(_wr_full_mutex),
+ _rd_list_cv(_rd_list_mutex),
+ _deq_list_cv(_deq_list_mutex),
+ _tcp(),
+ _tcrp()
+{}
+
+jrnl_instance::~jrnl_instance() {}
+
+
+void
+jrnl_instance::init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw ()
+{
+ test_case_result::shared_ptr p(new test_case_result(_jpp->jid()));
+ _tcrp = p;
+ _args_ptr = args_ptr;
+ try
+ {
+ _tcp = tcp;
+ _dtok_master_enq_list.clear();
+ _dtok_master_txn_list.clear();
+ _dtok_rd_list.clear();
+ _dtok_deq_list.clear();
+
+ if (_args_ptr->recover_mode)
+ {
+ try
+ {
+ u_int64_t highest_rid;
+ recover(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(),
+ _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this,
+ 0, highest_rid);
+ recover_complete();
+ }
+ catch (const mrg::journal::jexception& e)
+ {
+ if (e.err_code() == mrg::journal::jerrno::JERR_JDIR_STAT)
+ initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(),
+ _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this);
+ else
+ throw;
+ }
+ }
+ else
+ initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(),
+ _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this);
+ }
+ catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); }
+ catch (const std::exception& e) { _tcrp->add_exception(e.what()); }
+ catch (...) { _tcrp->add_exception("Unknown exception"); }
+}
+
+void
+jrnl_instance::run_tc() throw ()
+{
+ _tcrp->set_start_time();
+ ::pthread_create(&_enq_thread, 0, run_enq, this);
+ ::pthread_create(&_read_thread, 0, run_read, this);
+ ::pthread_create(&_deq_thread, 0, run_deq, this);
+}
+
+void
+jrnl_instance::tc_wait_compl() throw ()
+{
+ try
+ {
+ ::pthread_join(_deq_thread, 0);
+ ::pthread_join(_read_thread, 0);
+ ::pthread_join(_enq_thread, 0);
+ stop(true);
+ }
+ catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); }
+ catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); }
+ catch (...) { _tcrp->add_exception("Unknown exception"); panic(); }
+ _lpmgr.finalize();
+ _tcrp->set_stop_time();
+ _tcp->add_result(_tcrp);
+}
+
+void
+jrnl_instance::run_enq() throw ()
+{
+ try
+ {
+ unsigned sleep_cnt = 0U;
+ while(_tcrp->num_enq() < _tcp->num_msgs() && !_tcrp->exception())
+ {
+ dtok_ptr p(new mrg::journal::data_tok);
+ _dtok_master_enq_list.push_back(p);
+ const char* msgp = data_src::get_data(_tcrp->num_enq() % 10);
+ const std::size_t msg_size = _tcp->this_data_size();
+ const std::size_t xid_size = _tcp->this_xid_size();
+ const std::string xid(data_src::get_xid(xid_size));
+ const bool external = _tcp->this_external();
+ const bool transient = _tcp->this_transience();
+ mrg::journal::iores res;
+ if (xid_size)
+ {
+ if (external)
+ res = enqueue_extern_txn_data_record(msg_size, p.get(), xid, transient);
+ else
+ res = enqueue_txn_data_record(msgp, msg_size, msg_size, p.get(), xid,
+ transient);
+ }
+ else
+ {
+ if (external)
+ res = enqueue_extern_data_record(msg_size, p.get(), transient);
+ else
+ res = enqueue_data_record(msgp, msg_size, msg_size, p.get(), transient);
+ }
+ switch (res)
+ {
+ case mrg::journal::RHM_IORES_SUCCESS:
+ sleep_cnt = 0U;
+ _tcrp->incr_num_enq();
+ if (p->has_xid() && !_tcp->auto_deq())
+ commit(p.get());
+ break;
+ case mrg::journal::RHM_IORES_ENQCAPTHRESH:
+ if (++sleep_cnt > MAX_ENQCAPTHRESH_CNT)
+ {
+ _tcrp->add_exception("Timeout waiting for RHM_IORES_ENQCAPTHRESH to clear.");
+ panic();
+ }
+ else if (get_wr_events(0) == 0) // *** GEV2
+ {
+ mrg::journal::slock sl(_wr_full_mutex);
+ _wr_full_cv.waitintvl(MAX_WR_WAIT * 1000000); // MAX_WR_WAIT in ms
+ }
+ break;
+ default:
+ std::ostringstream oss;
+ oss << "ERROR: enqueue operation in journal \"" << _jid << "\" returned ";
+ oss << mrg::journal::iores_str(res) << ".";
+ _tcrp->add_exception(oss.str());
+ }
+ }
+ flush(true);
+ }
+ catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); }
+ catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); }
+ catch (...) { _tcrp->add_exception("Unknown exception"); panic(); }
+}
+
+void
+jrnl_instance::run_read() throw ()
+{
+ try
+ {
+ read_arg::read_mode_t rd_mode = _args_ptr->read_mode.val();
+ if (rd_mode != read_arg::NONE)
+ {
+ while (_tcrp->num_rproc() < _tcp->num_msgs() && !_tcrp->exception())
+ {
+ journal::data_tok* dtokp = 0;
+ {
+ mrg::journal::slock sl(_rd_list_mutex);
+ if (_dtok_rd_list.empty())
+ _rd_list_cv.wait();
+ if (!_dtok_rd_list.empty())
+ {
+ dtokp = _dtok_rd_list.front();
+ _dtok_rd_list.pop_front();
+ }
+ }
+ if (dtokp)
+ {
+ _tcrp->incr_num_rproc();
+
+ bool do_read = true;
+ if (rd_mode == read_arg::RANDOM)
+ do_read = 1.0 * std::rand() / RAND_MAX < _args_ptr->read_prob / 100.0;
+ else if (rd_mode == read_arg::LAZYLOAD)
+ do_read = _tcrp->num_rproc() >= _args_ptr->lld_skip_num &&
+ _tcrp->num_read() < _args_ptr->lld_rd_num;
+ bool read_compl = false;
+ while (do_read && !read_compl && !_tcrp->exception())
+ {
+ void* dptr = 0;
+ std::size_t dsize = 0;
+ void* xptr = 0;
+ std::size_t xsize = 0;
+ bool tr = false;
+ bool ext = false;
+ mrg::journal::iores res = read_data_record(&dptr, dsize, &xptr, xsize, tr,
+ ext, dtokp);
+ switch (res)
+ {
+ case mrg::journal::RHM_IORES_SUCCESS:
+ {
+ mrg::journal::slock sl(_deq_list_mutex);
+ _dtok_deq_list.push_back(dtokp);
+ _deq_list_cv.broadcast();
+ }
+ read_compl = true;
+ _tcrp->incr_num_read();
+
+ // clean up
+ if (xsize)
+ std::free(xptr);
+ else if (dsize)
+ std::free(dptr);
+ dptr = 0;
+ xptr = 0;
+ break;
+ case mrg::journal::RHM_IORES_PAGE_AIOWAIT:
+ if (get_rd_events(0) == 0)
+ {
+ mrg::journal::slock sl(_rd_aio_mutex);
+ _rd_aio_cv.waitintvl(MAX_RD_WAIT * 1000000); // MAX_RD_WAIT in ms
+ }
+ break;
+ default:
+ std::ostringstream oss;
+ oss << "ERROR: read operation in journal \"" << _jid;
+ oss << "\" returned " << mrg::journal::iores_str(res) << ".";
+ _tcrp->add_exception(oss.str());
+ {
+ mrg::journal::slock sl(_deq_list_mutex);
+ _deq_list_cv.broadcast(); // wake up deq thread
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); }
+ catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); }
+ catch (...) { _tcrp->add_exception("Unknown exception"); panic(); }
+}
+
+void
+jrnl_instance::run_deq() throw ()
+{
+ try
+ {
+ if (_tcp->auto_deq())
+ {
+ while(_tcrp->num_deq() < _tcp->num_msgs() && !_tcrp->exception())
+ {
+ journal::data_tok* dtokp = 0;
+ {
+ mrg::journal::slock sl(_deq_list_mutex);
+ if (_dtok_deq_list.empty())
+ _deq_list_cv.wait();
+ if (!_dtok_deq_list.empty())
+ {
+ dtokp = _dtok_deq_list.front();
+ _dtok_deq_list.pop_front();
+ }
+ }
+ if (dtokp)
+ {
+ mrg::journal::iores res;
+ if (dtokp->has_xid())
+ res = dequeue_txn_data_record(dtokp, dtokp->xid());
+ else
+ res = dequeue_data_record(dtokp);
+ if (res == mrg::journal::RHM_IORES_SUCCESS)
+ {
+ _tcrp->incr_num_deq();
+ commit(dtokp);
+ }
+ else
+ {
+ std::ostringstream oss;
+ oss << "ERROR: dequeue operation in journal \"" << _jid;
+ oss << "\" returned " << mrg::journal::iores_str(res) << ".";
+ _tcrp->add_exception(oss.str());
+ }
+ }
+ }
+ flush(true);
+ }
+ }
+ catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); }
+ catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); }
+ catch (...) { _tcrp->add_exception("Unknown exception"); panic(); }
+}
+
+void
+jrnl_instance::abort(const mrg::journal::data_tok* dtokp)
+{
+ txn(dtokp, false);
+}
+
+void
+jrnl_instance::commit(const mrg::journal::data_tok* dtokp)
+{
+ txn(dtokp, true);
+}
+
+void
+jrnl_instance::txn(const mrg::journal::data_tok* dtokp, const bool commit)
+{
+ if (dtokp->has_xid())
+ {
+ mrg::journal::data_tok* p = prep_txn_dtok(dtokp);
+ mrg::journal::iores res = commit ? txn_commit(p, p->xid()) : txn_abort(p, p->xid());
+ if (res != mrg::journal::RHM_IORES_SUCCESS)
+ {
+ std::ostringstream oss;
+ oss << "ERROR: " << (commit ? "commit" : "abort") << " operation in journal \"";
+ oss << _jid << "\" returned " << mrg::journal::iores_str(res) << ".";
+ _tcrp->add_exception(oss.str());
+ }
+ }
+}
+
+mrg::journal::data_tok*
+jrnl_instance::prep_txn_dtok(const mrg::journal::data_tok* dtokp)
+{
+ dtok_ptr p(new mrg::journal::data_tok);
+ _dtok_master_txn_list.push_back(p);
+ p->set_xid(dtokp->xid());
+ return p.get();
+}
+
+void
+jrnl_instance::panic()
+{
+ // In the event of a panic or exception condition, release all waiting CVs
+ _rd_aio_cv.broadcast();
+ _wr_full_cv.broadcast();
+ _rd_list_cv.broadcast();
+ _deq_list_cv.broadcast();
+}
+
+// AIO callbacks
+
+void
+jrnl_instance::wr_aio_cb(std::vector<journal::data_tok*>& dtokl)
+{
+ for (std::vector<journal::data_tok*>::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++)
+ {
+ if ((*i)->wstate() == journal::data_tok::ENQ || (*i)->wstate() == journal::data_tok::DEQ)
+ {
+ journal::data_tok* dtokp = *i;
+ if (dtokp->wstate() == journal::data_tok::ENQ)
+ {
+ if (_args_ptr->read_mode.val() == read_arg::NONE)
+ {
+ mrg::journal::slock sl(_deq_list_mutex);
+ _dtok_deq_list.push_back(dtokp);
+ _deq_list_cv.broadcast();
+ }
+ else
+ {
+ mrg::journal::slock sl(_rd_list_mutex);
+ _dtok_rd_list.push_back(dtokp);
+ _rd_list_cv.broadcast();
+ }
+ }
+ else // DEQ
+ {
+ mrg::journal::slock sl(_wr_full_mutex);
+ _wr_full_cv.broadcast();
+ }
+ }
+ }
+}
+
+void
+jrnl_instance::rd_aio_cb(std::vector<u_int16_t>& /*pil*/)
+{
+ mrg::journal::slock sl(_rd_aio_mutex);
+ _rd_aio_cv.broadcast();
+}
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h
new file mode 100644
index 0000000000..5003f39b24
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_jrnl_instance_hpp
+#define mrg_jtt_jrnl_instance_hpp
+
+#include "args.h"
+#include "jrnl_init_params.h"
+#include "test_case.h"
+
+#include <boost/shared_ptr.hpp>
+#include "qpid/legacystore/jrnl/cvar.h"
+#include "qpid/legacystore/jrnl/data_tok.h"
+#include "qpid/legacystore/jrnl/jcntl.h"
+#include "qpid/legacystore/jrnl/slock.h"
+#include "qpid/legacystore/jrnl/smutex.h"
+#include <list>
+#include <vector>
+
+namespace mrg
+{
+namespace jtt
+{
+
+ class jrnl_instance : public mrg::journal::jcntl, public virtual mrg::journal::aio_callback
+ {
+ public:
+ typedef boost::shared_ptr<jrnl_instance> shared_ptr;
+ typedef boost::shared_ptr<journal::data_tok> dtok_ptr;
+
+ private:
+ jrnl_init_params::shared_ptr _jpp;
+ const args* _args_ptr;
+ std::vector<dtok_ptr> _dtok_master_enq_list;
+ std::vector<dtok_ptr> _dtok_master_txn_list;
+ std::list<journal::data_tok*> _dtok_rd_list;
+ std::list<journal::data_tok*> _dtok_deq_list;
+ mrg::journal::smutex _rd_aio_mutex; ///< Mutex for read aio wait conditions
+ mrg::journal::cvar _rd_aio_cv; ///< Condition var for read aio wait conditions
+ mrg::journal::smutex _wr_full_mutex; ///< Mutex for write full conditions
+ mrg::journal::cvar _wr_full_cv; ///< Condition var for write full conditions
+ mrg::journal::smutex _rd_list_mutex; ///< Mutex for _dtok_rd_list
+ mrg::journal::cvar _rd_list_cv; ///< Condition var for _dtok_rd_list
+ mrg::journal::smutex _deq_list_mutex; ///< Mutex for _dtok_deq_list
+ mrg::journal::cvar _deq_list_cv; ///< Condition var for _dtok_deq_list
+ pthread_t _enq_thread;
+ pthread_t _deq_thread;
+ pthread_t _read_thread;
+ test_case::shared_ptr _tcp;
+ test_case_result::shared_ptr _tcrp;
+
+ public:
+ jrnl_instance(const std::string& jid, const std::string& jdir,
+ const std::string& base_filename,
+ const u_int16_t num_jfiles = jrnl_init_params::def_num_jfiles,
+ const bool ae = jrnl_init_params::def_ae,
+ const u_int16_t ae_max_jfiles = jrnl_init_params::def_ae_max_jfiles,
+ const u_int32_t jfsize_sblks = jrnl_init_params::def_jfsize_sblks,
+ const u_int16_t wcache_num_pages = jrnl_init_params::def_wcache_num_pages,
+ const u_int32_t wcache_pgsize_sblks = jrnl_init_params::def_wcache_pgsize_sblks);
+ jrnl_instance(const jrnl_init_params::shared_ptr& params);
+ virtual ~jrnl_instance();
+
+ inline const jrnl_init_params::shared_ptr& params() const { return _jpp; }
+ inline const std::string& jid() const { return _jpp->jid(); }
+
+ void init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw ();
+ void run_tc() throw ();
+ void tc_wait_compl() throw ();
+
+ // AIO callbacks
+ virtual void wr_aio_cb(std::vector<journal::data_tok*>& dtokl);
+ virtual void rd_aio_cb(std::vector<u_int16_t>& pil);
+
+ private:
+ void run_enq() throw ();
+ inline static void* run_enq(void* p)
+ { static_cast<jrnl_instance*>(p)->run_enq(); return 0; }
+
+ void run_read() throw ();
+ inline static void* run_read(void* p)
+ { static_cast<jrnl_instance*>(p)->run_read(); return 0; }
+
+ void run_deq() throw ();
+ inline static void* run_deq(void* p)
+ { static_cast<jrnl_instance*>(p)->run_deq(); return 0; }
+
+ void abort(const mrg::journal::data_tok* dtokp);
+ void commit(const mrg::journal::data_tok* dtokp);
+ void txn(const mrg::journal::data_tok* dtokp, const bool commit);
+ mrg::journal::data_tok* prep_txn_dtok(const mrg::journal::data_tok* dtokp);
+
+ void panic();
+
+// // static callbacks
+// static void aio_rd_callback(jcntl* journal, std::vector<u_int16_t>& pil);
+// static void aio_wr_callback(jcntl* journal, std::vector<journal::data_tok*>& dtokl);
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_jrnl_instance_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv
new file mode 100644
index 0000000000..df523e3f97
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv
@@ -0,0 +1,234 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",,
+"Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment"
+,,,,,,,,,,,,,,,,,,,,
+"Initialize only",,,,,,,,,,,,,,,,,,,,
+0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only"
+,,,,,,,,,,,,,,,,,,,,
+"Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,,
+1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message"
+2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message"
+3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]"
+4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]"
+5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]"
+6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]"
+7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]"
+8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]"
+9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]"
+10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]"
+11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]"
+12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]"
+13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]"
+14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]"
+15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]"
+16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]"
+17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]"
+18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]"
+19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]"
+20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]"
+21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]"
+22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]"
+23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]"
+24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]"
+25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]"
+26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]"
+27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]"
+28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]"
+29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]"
+30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]"
+31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]"
+32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]"
+,,,,,,,,,,,,,,,,,,,,
+"Transition from one d-block to two per message",,,,,,,,,,,,,,,,,,,,
+33,"L",1,10,0,10,0,84,84,0,0,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit"
+34,"L",1,10,0,10,1,85,85,0,0,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte"
+35,"L",1,10,0,10,0,58,58,26,26,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit [txn]"
+36,"L",1,10,0,10,1,59,59,26,26,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte [txn]"
+,,,,,,,,,,,,,,,,,,,,
+"Transition from one s-block to two per message",,,,,,,,,,,,,,,,,,,,
+37,"L",1,10,0,10,0,468,468,0,0,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit"
+38,"L",1,10,0,10,1,469,469,0,0,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte"
+39,"L",1,10,0,10,0,442,442,26,26,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit [txn]"
+40,"L",1,10,0,10,1,443,443,26,26,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte [txn]"
+,,,,,,,,,,,,,,,,,,,,
+"Transition from first page to second",,,,,,,,,,,,,,,,,,,,
+41,"L",1,8,0,8,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page"
+42,"L",1,8,1,9,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page"
+43,"L",1,8,0,8,1,4053,4053,0,0,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte"
+44,"L",1,8,0,8,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]"
+45,"L",1,8,1,9,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]"
+46,"L",1,8,0,8,1,3797,3797,256,256,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte [txn]"
+47,"L",1,8,0,8,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]"
+48,"L",1,8,1,9,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]"
+49,"L",1,8,0,8,1,3925,3925,0,0,TRUE,FALSE,FALSE,3969,32,32,1,0,0,"1/8 page incl deq + 1 byte [deq]"
+50,"L",1,8,0,8,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]"
+51,"L",1,8,1,9,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]"
+52,"L",1,8,0,8,1,3029,3029,256,256,TRUE,FALSE,FALSE,3329,27,300,3,292,3,"1/8 page incl deq & txn + 1 byte [deq txn]"
+,,,,,,,,,,,,,,,,,,,,
+"Page cache rollover (from page 32 back to page 0)",,,,,,,,,,,,,,,,,,,,
+53,"L",1,32,0,32,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page"
+54,"L",1,32,1,33,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page"
+55,"L",1,32,0,32,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte"
+56,"L",1.5,22,0,22,0,49108,49108,0,0,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages"
+57,"L",1,32,0,32,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]"
+58,"L",1,32,1,33,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]"
+59,"L",1,32,0,32,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]"
+60,"L",1.5,22,0,22,0,48852,48852,256,256,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages [txn]"
+61,"L",1,32,0,32,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]"
+62,"L",1,32,1,33,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]"
+63,"L",1,32,0,32,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]"
+64,"L",1.5,22,0,22,0,48980,48980,0,0,TRUE,FALSE,FALSE,49024,383,32,1,0,0,"1.5 pages incl deq [deq]"
+65,"L",1,32,0,32,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]"
+66,"L",1,32,1,33,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]"
+67,"L",1,32,0,32,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]"
+68,"L",1.5,22,0,22,0,48084,48084,256,256,TRUE,FALSE,FALSE,48384,378,300,3,292,3,"1.5 pages incl deq & txn [deq txn]"
+,,,,,,,,,,,,,,,,,,,,
+"File transition (from file 0000 to 0001)",,,,,,,,,,,,,,,,,,,,
+69,"L",1,48,0,48,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page"
+70,"L",1,48,1,49,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page"
+71,"L",1,48,0,48,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte"
+72,"L",2.5,20,0,20,0,81876,81876,0,0,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages"
+73,"L",1,48,0,48,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]"
+74,"L",1,48,1,49,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]"
+75,"L",1,48,0,48,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]"
+76,"L",2.5,20,0,20,0,81620,81620,256,256,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages [txn]"
+77,"L",1,48,0,48,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]"
+78,"L",1,48,1,49,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]"
+79,"L",1,48,0,48,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]"
+80,"L",2.5,20,0,20,0,81748,81748,0,0,TRUE,FALSE,FALSE,81792,639,32,1,0,0,"2.5 pages incl deq [deq]"
+81,"L",1,48,0,48,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]"
+82,"L",1,48,1,49,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]"
+83,"L",1,48,0,48,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]"
+84,"L",2.5,20,0,20,0,80852,80852,256,256,TRUE,FALSE,FALSE,81152,634,300,3,292,3,"2.5 pages incl deq & txn [deq txn]"
+,,,,,,,,,,,,,,,,,,,,
+"File rollover (from file 0007 to 0000) - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,,
+85,"L",0.5,16,0,16,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]"
+86,"L",0.5,16,1,17,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]"
+87,"L",0.5,16,0,16,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]"
+88,"L",0.5,16,0,16,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]"
+89,"L",0.5,16,1,17,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]"
+90,"L",0.5,16,0,16,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]"
+91,"L",0.25,32,0,32,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]"
+92,"L",0.25,32,1,33,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]"
+93,"L",0.25,32,0,32,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]"
+94,"L",0.25,32,0,32,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]"
+95,"L",0.25,32,1,33,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]"
+96,"L",0.25,32,0,32,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]"
+,,,,,,,,,,,,,,,,,,,,
+"Multi-page messages (large messages) - tests various paths in encoder.",,,,,,,,,,,,,,,,,,,,
+97,"L",1,16,0,16,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page"
+98,"L",1,16,0,16,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary)"
+99,"L",1,16,0,16,11,32735,32735,0,0,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary)"
+100,"L",1,16,0,16,12,32736,32736,0,0,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page)"
+101,"L",1,16,0,16,13,32737,32737,0,0,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary)"
+102,"L",1,16,0,16,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page [txn]"
+103,"L",1,16,0,16,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary) [txn]"
+104,"L",1,16,0,16,11,32479,32479,256,256,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary) [txn]"
+105,"L",1,16,0,16,12,32480,32480,256,256,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page) [txn]"
+106,"L",1,16,0,16,13,32481,32481,256,256,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary) [txn]"
+107,"L",2,16,0,16,0,65492,65492,0,0,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages"
+108,"L",2,16,0,16,1,65493,65493,0,0,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary)"
+109,"L",2,16,0,16,11,65503,65503,0,0,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary)"
+110,"L",2,16,0,16,12,65504,65504,0,0,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page)"
+111,"L",2,16,0,16,13,65505,65505,0,0,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary)"
+112,"L",2,16,0,16,0,65236,65236,256,256,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages [txn]"
+113,"L",2,16,0,16,1,65237,65237,256,256,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary) [txn]"
+114,"L",2,16,0,16,11,65247,65247,256,256,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]"
+115,"L",2,16,0,16,12,65248,65248,256,256,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page) [txn]"
+116,"L",2,16,0,16,13,65249,65249,256,256,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary) [txn]"
+117,"L",4,16,0,16,0,131028,131028,0,0,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages"
+118,"L",4,16,0,16,1,131029,131029,0,0,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary)"
+119,"L",4,16,0,16,11,131039,131039,0,0,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary)"
+120,"L",4,16,0,16,12,131040,131040,0,0,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page)"
+121,"L",4,16,0,16,13,131041,131041,0,0,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary)"
+122,"L",4,16,0,16,0,130772,130772,256,256,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages [txn]"
+123,"L",4,16,0,16,1,130773,130773,256,256,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary) [txn]"
+124,"L",4,16,0,16,11,130783,130783,256,256,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]"
+125,"L",4,16,0,16,12,130784,130784,256,256,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page) [txn]"
+126,"L",4,16,0,16,13,130785,130785,256,256,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary) [txn]"
+127,"L",3.5,16,0,16,0,114644,114644,0,0,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages"
+128,"L",3.5,16,0,16,1,114645,114645,0,0,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte"
+129,"L",3.5,16,0,16,0,114388,114388,256,256,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages [txn]"
+130,"L",3.5,16,0,16,1,114389,114389,256,256,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte [txn]"
+131,"L",1,16,0,16,-1,10,10,32735,32735,FALSE,FALSE,FALSE,32789,257,0,0,0,0,"xid 1 page – 1 byte; data 10 bytes (exact fit) [txn]"
+132,"L",1,16,0,16,0,10,10,32736,32736,FALSE,FALSE,FALSE,32790,257,0,0,0,0,"xid 1 page; data 10 bytes (exact fit) [txn]"
+133,"L",1,16,0,16,1,10,10,32737,32737,FALSE,FALSE,FALSE,32791,257,0,0,0,0,"xid 1 page + 1 byte; data 10 bytes (exact fit) [txn]"
+134,"L",2,16,0,16,-1,10,10,65503,65503,FALSE,FALSE,FALSE,65557,513,0,0,0,0,"xid 2 pages – 1 byte; data 10 bytes (exact fit) [txn]"
+135,"L",2,16,0,16,0,10,10,65504,65504,FALSE,FALSE,FALSE,65558,513,0,0,0,0,"xid 2 pages; data 10 bytes (exact fit) [txn]"
+136,"L",2,16,0,16,1,10,10,65505,65505,FALSE,FALSE,FALSE,65559,513,0,0,0,0,"xid 2 pages + 1 byte; data 10 bytes (exact fit) [txn]"
+137,"L",4,16,0,16,-1,10,10,131039,131039,FALSE,FALSE,FALSE,131093,1025,0,0,0,0,"xid 4 pages – 1 byte; data 10 bytes (exact fit) [txn]"
+138,"L",4,16,0,16,0,10,10,131040,131040,FALSE,FALSE,FALSE,131094,1025,0,0,0,0,"xid 4 pages; data 10 bytes (exact fit) [txn]"
+139,"L",4,16,0,16,1,10,10,131041,131041,FALSE,FALSE,FALSE,131095,1025,0,0,0,0,"xid 4 pages + 1 byte; data 10 bytes (exact fit) [txn]"
+140,"L",3.5,16,0,16,0,10,10,114656,114656,FALSE,FALSE,FALSE,114710,897,0,0,0,0,"xid 3.5 pages; data 10 bytes (exact fit) [txn]"
+141,"L",3.5,16,0,16,1,10,10,114657,114657,FALSE,FALSE,FALSE,114711,897,0,0,0,0,"xid 3.5 pages + 1 byte; data 10 bytes (exact fit) [txn]"
+142,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]"
+143,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for enq rec; data 10 bytes (exact fit) [deq, txn]"
+144,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]"
+145,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]"
+146,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for enq rec; data 10 bytes (exact fit) [deq, txn]"
+147,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]"
+148,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]"
+149,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for enq rec; data 10 bytes (exact fit) [deq, txn]"
+150,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]"
+151,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for enq rec; data 10 bytes (exact fit) [deq, txn]"
+152,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]"
+153,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]"
+154,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for deq rec; data 10 bytes (exact fit) [deq, txn]"
+155,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]"
+156,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]"
+157,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for deq rec; data 10 bytes (exact fit) [deq, txn]"
+158,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]"
+159,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]"
+160,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for deq rec; data 10 bytes (exact fit) [deq, txn]"
+161,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]"
+162,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for deq rec; data 10 bytes (exact fit) [deq, txn]"
+163,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]"
+164,"L",1,16,0,16,-1,10,10,32743,32743,TRUE,FALSE,FALSE,32797,257,32787,257,32779,257,"xid 1 page – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]"
+165,"L",1,16,0,16,0,10,10,32744,32744,TRUE,FALSE,FALSE,32798,257,32788,257,32780,257,"xid 1 page for txn rec; data 10 bytes (exact fit) [deq, txn]"
+166,"L",1,16,0,16,1,10,10,32745,32745,TRUE,FALSE,FALSE,32799,257,32789,257,32781,257,"xid 1 page + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]"
+167,"L",2,16,0,16,-1,10,10,65511,65511,TRUE,FALSE,FALSE,65565,513,65555,513,65547,513,"xid 2 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]"
+168,"L",2,16,0,16,0,10,10,65512,65512,TRUE,FALSE,FALSE,65566,513,65556,513,65548,513,"xid 2 pages for txn rec; data 10 bytes (exact fit) [deq, txn]"
+169,"L",2,16,0,16,1,10,10,65513,65513,TRUE,FALSE,FALSE,65567,513,65557,513,65549,513,"xid 2 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]"
+170,"L",4,16,0,16,-1,10,10,131047,131047,TRUE,FALSE,FALSE,131101,1025,131091,1025,131083,1025,"xid 4 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]"
+171,"L",4,16,0,16,0,10,10,131048,131048,TRUE,FALSE,FALSE,131102,1025,131092,1025,131084,1025,"xid 4 pages for txn rec; data 10 bytes (exact fit) [deq, txn]"
+172,"L",4,16,0,16,1,10,10,131049,131049,TRUE,FALSE,FALSE,131103,1025,131093,1025,131085,1025,"xid 4 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]"
+173,"L",3.5,16,0,16,0,10,10,114664,114664,TRUE,FALSE,FALSE,114718,897,114708,897,114700,897,"xid 3.5 pages for txn rec; data 10 bytes (exact fit) [deq, txn]"
+174,"L",3.5,16,0,16,1,10,10,114665,114665,TRUE,FALSE,FALSE,114719,897,114709,897,114701,897,"xid 3.5 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]"
+,,,,,,,,,,,,,,,,,,,,
+"High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,,
+#175,"M",1,5000000,0,5000000,0,0,84,0,0,TRUE,FALSE,FALSE,128,1,32,1,0,0,"1 dblk max [deq]"
+#176,"M",3,3000000,0,3000000,0,0,340,0,0,TRUE,FALSE,FALSE,384,3,32,1,0,0,"3 dblks max [deq]"
+#177,"M",10,1600000,0,1600000,0,0,1236,0,0,TRUE,FALSE,FALSE,1280,10,32,1,0,0,"10 dblks max [deq]"
+#178,"M",30,600000,0,600000,0,0,3796,0,0,TRUE,FALSE,FALSE,3840,30,32,1,0,0,"30 dblks max [deq]"
+#179,"M",100,200000,0,200000,0,0,12756,0,0,TRUE,FALSE,FALSE,12800,100,32,1,0,0,"100 dblks max [deq]"
+#180,"M",300,60000,0,60000,0,0,38356,0,0,TRUE,FALSE,FALSE,38400,300,32,1,0,0,"300 dblks max [deq]"
+#181,"M",1000,20000,0,20000,0,0,127956,0,0,TRUE,FALSE,FALSE,128000,1000,32,1,0,0,"1000 dblks max [deq]"
+#182,"M",1,5000000,0,5000000,0,0,100,1,100,TRUE,FALSE,FALSE,244,2,144,2,136,2,"100 bytes xid max + 100 bytes data max [deq txn]"
+#183,"M",3,3000000,0,3000000,0,0,300,1,300,TRUE,FALSE,FALSE,644,6,344,3,336,3,"300 bytes xid max + 300 bytes data max [deq txn]"
+#184,"M",10,1600000,0,1600000,0,0,1000,1,1000,TRUE,FALSE,FALSE,2044,16,1044,9,1036,9,"1000 bytes xid max + 1000 bytes data max [deq txn]"
+#185,"M",30,600000,0,600000,0,0,3000,1,3000,TRUE,FALSE,FALSE,6044,48,3044,24,3036,24,"3000 bytes xid max + 3000 bytes data max [deq txn]"
+#186,"M",100,200000,0,200000,0,0,10000,1,10000,TRUE,FALSE,FALSE,20044,157,10044,79,10036,79,"10000 bytes xid max + 10000 bytes data max [deq txn]"
+#187,"M",300,60000,0,60000,0,0,30000,1,30000,TRUE,FALSE,FALSE,60044,470,30044,235,30036,235,"30000 bytes xid max + 30000 bytes data max [deq txn]"
+#188,"M",1000,20000,0,20000,0,0,100000,1,100000,TRUE,FALSE,FALSE,200044,1563,100044,782,100036,782,"100000 bytes xid max + 100000 bytes data max [deq txn]"
+,,,,,,,,,,,,,,,,,,,,
+"STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,,
+#189,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)"
+#190,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)"
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp
new file mode 100644
index 0000000000..c8a4642b1c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp
@@ -0,0 +1,57 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_mgr.h"
+
+#include "args.h"
+#include <csignal>
+#include <iostream>
+
+#define PACKAGE_NAME "Journal Test Tool"
+#define VERSION "0.1"
+
+namespace po = boost::program_options;
+
+int main(int argc, char** argv)
+{
+ std::signal(SIGINT, mrg::jtt::test_mgr::signal_handler);
+ std::signal(SIGTERM, mrg::jtt::test_mgr::signal_handler);
+
+ std::cout << PACKAGE_NAME << " v." << VERSION << std::endl;
+
+ std::ostringstream oss;
+ oss << PACKAGE_NAME << " options";
+ mrg::jtt::args args(oss.str());
+ if (args.parse(argc, argv)) return 1;
+
+ try
+ {
+ mrg::jtt::test_mgr tm(args);
+ tm.run();
+ if (tm.error()) return 2; // One or more tests threw exceptions
+ }
+ catch (const std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ return 3;
+ }
+ return 0;
+}
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp
new file mode 100644
index 0000000000..94a07c7005
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp
@@ -0,0 +1,93 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "read_arg.h"
+
+#include <cassert>
+#include <boost/program_options.hpp>
+namespace po = boost::program_options;
+
+namespace mrg
+{
+namespace jtt
+{
+std::map<std::string, read_arg::read_mode_t> read_arg::_map;
+std::string read_arg::_description;
+const bool read_arg::init = __init();
+
+// static init fn
+bool
+read_arg::__init()
+{
+ // Set string versions of each enum option here
+ _map["NONE"] = NONE;
+ _map["ALL"] = ALL;
+ _map["RANDOM"] = RANDOM;
+ _map["LAZYLOAD"] = LAZYLOAD;
+ _description = "Determines if and when messages will be read prior to dequeueing. "
+ "Values: (NONE | ALL | RANDOM | LAZYLOAD)";
+ return true;
+}
+
+void
+read_arg::parse(const std::string& str)
+{
+ std::map<std::string, read_arg::read_mode_t>::const_iterator i = _map.find(str);
+ if (i == _map.end())
+ throw po::invalid_option_value(str);
+ _rm = i->second;
+}
+
+// static fn
+const std::string&
+read_arg::str(const read_mode_t rm)
+{
+ std::map<std::string, read_mode_t>::const_iterator i = _map.begin();
+ while (i->second != rm && i != _map.end()) i++;
+ assert(i != _map.end());
+ return i->first;
+}
+
+// static fn
+const std::string&
+read_arg::descr()
+{
+ return _description;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const read_arg& ra)
+{
+ os << ra.str();
+ return os;
+}
+
+std::istream&
+operator>>(std::istream& is, read_arg& ra)
+{
+ std::string s;
+ is >> s;
+ ra.parse(s);
+ return is;
+}
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h
new file mode 100644
index 0000000000..a8fd6f198e
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_read_arg_hpp
+#define mrg_jtt_read_arg_hpp
+
+#include <string>
+#include <map>
+
+namespace mrg
+{
+namespace jtt
+{
+
+class read_arg
+{
+ public:
+ enum read_mode_t { NONE, ALL, RANDOM, LAZYLOAD};
+ private:
+ static std::map<std::string, read_mode_t> _map;
+ static std::string _description;
+ static const bool init;
+ static bool __init();
+ read_mode_t _rm;
+ public:
+ inline read_arg() : _rm(NONE) {}
+ inline read_arg(read_mode_t rm) : _rm(rm) {}
+
+ inline read_mode_t val() const { return _rm; }
+ inline void set_val(const read_mode_t rm) { _rm = rm; }
+ void parse(const std::string& str);
+
+ inline const std::string& str() const { return str(_rm); }
+ static const std::string& str(const read_mode_t rm);
+ static const std::string& descr();
+
+ friend std::ostream& operator<<(std::ostream& os, const read_arg& ra);
+ friend std::istream& operator>>(std::istream& is, read_arg& ra);
+};
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_read_arg_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp
new file mode 100644
index 0000000000..e06e053504
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp
@@ -0,0 +1,179 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_case.h"
+
+#include <cstdlib>
+#include <iomanip>
+#include <sstream>
+
+namespace mrg
+{
+namespace jtt
+{
+
+test_case::test_case(const unsigned test_case_num, const u_int32_t num_msgs,
+ const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq,
+ const std::size_t min_xid_size, const std::size_t max_xid_size, const transient_t transient,
+ const external_t external, const std::string& comment):
+ _test_case_num(test_case_num),
+ _num_msgs(num_msgs),
+ _min_data_size(min_data_size),
+ _max_data_size(max_data_size),
+ _auto_dequeue(auto_deq),
+ _min_xid_size(min_xid_size),
+ _max_xid_size(max_xid_size),
+ _transient(transient),
+ _external(external),
+ _comment(comment),
+ _result_average(),
+ _result_jmap()
+{}
+
+test_case::~test_case()
+{}
+
+std::size_t
+test_case::this_data_size() const
+{
+ if (_min_data_size == _max_data_size)
+ return _max_data_size;
+ std::size_t size_diff = _max_data_size - _min_data_size;
+ return _min_data_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0));
+}
+
+std::size_t
+test_case::this_xid_size() const
+{
+ // TODO: rework when probabilities are introduced. Assume 50% if _min_xid_size = 0
+ if (_max_xid_size == 0)
+ return std::size_t(0);
+ if (_min_xid_size == 0)
+ {
+ if (1.0 * std::rand() / RAND_MAX < 0.5)
+ return std::size_t(0);
+ }
+ std::size_t size_diff = _max_xid_size - _min_xid_size;
+ return _min_xid_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0));
+}
+
+bool
+test_case::this_transience() const
+{
+ // TODO: rework when probabilities are introduced. Assume 50% if JTT_RANDOM
+ if (_transient == JTT_TRANSIENT)
+ return false;
+ if (_transient == JTT_PERSISTNET)
+ return true;
+ return 1.0 * std::rand() / RAND_MAX < 0.5;
+}
+
+bool
+test_case::this_external() const
+{
+ // TODO: rework when probabilities are introduced. Assume 50% if JDL_RANDOM
+ if (_external == JDL_INTERNAL)
+ return false;
+ if (_external == JDL_EXTERNAL)
+ return true;
+ return 1.0 * std::rand() / RAND_MAX < 0.5;
+}
+
+void
+test_case::add_result(test_case_result::shared_ptr& tcrp)
+{
+ _result_average.add_test_result(tcrp);
+ res_map_citr ari = _result_jmap.find(tcrp->jid());
+ if (ari == _result_jmap.end())
+ {
+ test_case_result_agregation::shared_ptr p(new test_case_result_agregation(tcrp->jid()));
+ p->add_test_result(tcrp);
+ _result_jmap.insert(res_map_pair(tcrp->jid(), p));
+ }
+ else
+ ari->second->add_test_result(tcrp);
+}
+
+void
+test_case::set_fmt_chk_res(const bool res, const std::string& jid)
+{
+ _result_average.set_fmt_chk_res(res);
+ res_map_citr ari = _result_jmap.find(jid);
+ if (ari != _result_jmap.end())
+ ari->second->set_fmt_chk_res(res);
+}
+
+const test_case_result::shared_ptr
+test_case::jmap_last(std::string& jid) const
+{
+ res_map_citr i = _result_jmap.find(jid);
+ if (i == _result_jmap.end())
+ return test_case_result::shared_ptr();
+ u_int32_t num_res = (*i).second->num_results();
+ if (num_res)
+ return (*(*i).second)[num_res - 1];
+ return test_case_result::shared_ptr();
+}
+
+void
+test_case::clear()
+{
+ _result_average.clear();
+ _result_jmap.clear();
+}
+
+const std::string
+test_case::str() const
+{
+ std::ostringstream oss;
+ oss << "Test Parameters: Test case no. " << _test_case_num << ":" << std::endl;
+ oss << " Comment: " << _comment << std::endl;
+ oss << " Number of messages: " << _num_msgs << std::endl;
+ oss << " Data size: " << _min_data_size;
+ if (_min_data_size == _max_data_size)
+ oss << " bytes (fixed)" << std::endl;
+ else
+ oss << " - " << _max_data_size << " bytes" << std::endl;
+ oss << " XID size: " << _min_xid_size;
+ if (_min_xid_size == _max_xid_size)
+ oss << " bytes (fixed)" << std::endl;
+ else
+ oss << " - " << _max_xid_size << " bytes" << std::endl;
+ oss << " Auto-dequeue: " << (_auto_dequeue ? "true" : "false") << std::endl;
+ oss << " Persistence: ";
+ switch (_transient)
+ {
+ case JTT_TRANSIENT: oss << "TRANSIENT" << std::endl; break;
+ case JTT_PERSISTNET: oss << "PERSISTNET" << std::endl; break;
+ case JTT_RANDOM: oss << "RANDOM" << std::endl; break;
+ }
+ oss << " Message Data: ";
+ switch (_external)
+ {
+ case JDL_INTERNAL: oss << "INTERNAL"; break;
+ case JDL_EXTERNAL: oss << "EXTERNAL"; break;
+ case JDL_RANDOM: oss << "RANDOM"; break;
+ }
+ return oss.str();
+}
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h
new file mode 100644
index 0000000000..f72dd05f0c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_test_case_hpp
+#define mrg_jtt_test_case_hpp
+
+#include <boost/shared_ptr.hpp>
+#include <cstddef>
+#include <map>
+#include "test_case_result.h"
+#include "test_case_result_agregation.h"
+#include <vector>
+
+namespace mrg
+{
+namespace jtt
+{
+
+ class test_case
+ {
+ public:
+ enum transient_type { JTT_TRANSIENT = 0, JTT_PERSISTNET, JTT_RANDOM };
+ typedef transient_type transient_t;
+
+ enum data_location { JDL_INTERNAL = 0, JDL_EXTERNAL, JDL_RANDOM };
+ typedef data_location external_t;
+
+ typedef boost::shared_ptr<test_case> shared_ptr;
+
+ typedef std::map<std::string, test_case_result_agregation::shared_ptr> res_map;
+ typedef std::pair<std::string, test_case_result_agregation::shared_ptr> res_map_pair;
+ typedef res_map::const_iterator res_map_citr;
+
+ private:
+ unsigned _test_case_num;
+ u_int32_t _num_msgs;
+ std::size_t _min_data_size;
+ std::size_t _max_data_size;
+ bool _auto_dequeue;
+ // TODO: add probability of transaction to these params
+ std::size_t _min_xid_size;
+ std::size_t _max_xid_size;
+ // TODO: change these enums (transient_t & external_t) to probabilities
+ transient_t _transient;
+ external_t _external;
+ std::string _comment;
+
+ test_case_result_agregation _result_average; // overall average result
+ res_map _result_jmap; // map of per-journal averages
+
+ public:
+ test_case(const unsigned test_case_num, const u_int32_t num_msgs,
+ const std::size_t min_data_size, const std::size_t max_data_size,
+ const bool auto_deq, const std::size_t min_xid_size,
+ const std::size_t max_xid_size, const transient_t transient,
+ const external_t external, const std::string& comment);
+ virtual ~test_case();
+
+ inline unsigned test_case_num() const { return _test_case_num; }
+ inline u_int32_t num_msgs() const { return _num_msgs; }
+ inline std::size_t min_data_size() const { return _min_data_size; }
+ inline std::size_t max_data_size() const { return _max_data_size; }
+ std::size_t this_data_size() const;
+ inline bool auto_deq() const { return _auto_dequeue; }
+ inline std::size_t min_xid_size() const { return _min_xid_size; }
+ inline std::size_t max_xid_size() const { return _max_xid_size; }
+ std::size_t this_xid_size() const;
+ inline transient_t transient() const { return _transient; }
+ bool this_transience() const;
+ inline external_t external() const { return _external; }
+ bool this_external() const;
+ inline const std::string& comment() const { return _comment; }
+
+ void add_result(test_case_result::shared_ptr& p);
+ void set_fmt_chk_res(const bool res, const std::string& jid);
+
+ inline const test_case_result_agregation& average() const { return _result_average; }
+ inline u_int32_t num_results() const { return _result_average.num_results(); }
+ inline unsigned num_jrnls() const { return _result_jmap.size(); }
+ inline res_map_citr jrnl_average(std::string& jid) const { return _result_jmap.find(jid); }
+ inline res_map_citr jmap_begin() const { return _result_jmap.begin(); }
+ inline res_map_citr jmap_end() const { return _result_jmap.end(); }
+ const test_case_result::shared_ptr jmap_last(std::string& jid) const;
+
+ void clear();
+ const std::string str() const;
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_test_case_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp
new file mode 100644
index 0000000000..2f88f265a5
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp
@@ -0,0 +1,201 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_case_result.h"
+
+#include <iomanip>
+#include <sstream>
+
+namespace mrg
+{
+namespace jtt
+{
+
+test_case_result::test_case_result(const std::string& jid):
+ _jid(jid),
+ _num_enq(0),
+ _num_deq(0),
+ _num_read(0),
+ _num_rproc(0),
+ _start_time(),
+ _stop_time(),
+ _stopped(false),
+ _test_time(),
+ _exception_list()
+{}
+
+test_case_result::~test_case_result()
+{}
+
+const std::string
+test_case_result::test_time_str() const
+{
+ return _test_time.str(9);
+}
+
+void
+test_case_result::add_exception(const journal::jexception& e, const bool set_stop_time_flag)
+{
+ if (!_stopped && set_stop_time_flag)
+ {
+ set_stop_time();
+ _stopped = true;
+ }
+ _exception_list.push_back(e.what());
+}
+
+void
+test_case_result::add_exception(const std::string& err_str, const bool set_stop_time_flag)
+{
+ if (!_stopped && set_stop_time_flag)
+ {
+ set_stop_time();
+ _stopped = true;
+ }
+ _exception_list.push_back(err_str);
+}
+
+void
+test_case_result::add_exception(const char* err_str, const bool set_stop_time_flag)
+{
+ if (!_stopped && set_stop_time_flag)
+ {
+ set_stop_time();
+ _stopped = true;
+ }
+ _exception_list.push_back(err_str);
+}
+
+void
+test_case_result::clear()
+{
+ _num_enq = 0;
+ _num_deq = 0;
+ _num_read = 0;
+ _start_time.set_zero();
+ _stop_time.set_zero();
+ _test_time.set_zero();
+ _exception_list.clear();
+}
+
+const std::string
+test_case_result::str(const bool summary) const
+{
+ std::ostringstream oss;
+ if (summary)
+ {
+ oss << _jid << ":";
+ oss << str_summary();
+ if (_exception_list.size())
+ oss << "; fail: " << _exception_list[0] << std::endl;
+ else
+ oss << "; ok" << std::endl;
+ }
+ else
+ {
+ oss << "--- Journal instance: jid=\"" << _jid << "\" ---" << std::endl;
+ oss << str_full();
+ if (_exception_list.size())
+ oss << " exception/error:" << _exception_list[0] << std::endl;
+ }
+ return oss.str();
+}
+
+const std::string
+test_case_result::str_full() const
+{
+ const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9);
+ const bool no_exception = _exception_list.empty();
+ std::ostringstream oss;
+ oss.setf(std::ios::fixed, std::ios::floatfield);
+ oss.precision(2);
+ if (no_exception)
+ {
+ oss.precision(6);
+ oss << " total test time: " << t << "s" << std::endl;
+ }
+ oss.precision(3);
+ oss << " total number enqueues: " << _num_enq;
+ if (no_exception)
+ oss << " (" << (_num_enq / t) << " enq/sec)";
+ oss << std::endl;
+ oss << " total number dequeues: " << _num_deq;
+ if (no_exception)
+ oss << " (" << (_num_deq / t) << " deq/sec)";
+ oss << std::endl;
+ oss << "total write operations: " << (_num_enq + _num_deq);
+ if (no_exception)
+ oss << " (" << ((_num_enq + _num_deq) / t) << " wrops/sec)";
+ oss << std::endl;
+ oss << " total number reads: " << _num_read;
+ if (no_exception)
+ oss << " (" << (_num_read / t) << " rd/sec)";
+ oss << std::endl;
+ oss << " total operations: " << (_num_enq + _num_deq + _num_read);
+ if (no_exception)
+ oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << " ops/sec)";
+ oss << std::endl;
+ oss << " overall result: " << (no_exception ? "PASS" : "*** FAIL ***") << std::endl;
+ return oss.str();
+}
+
+const std::string
+test_case_result::str_summary() const
+{
+ const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9);
+ const bool no_exception = _exception_list.empty();
+ std::ostringstream oss;
+ oss.setf(std::ios::fixed, std::ios::floatfield);
+ if (no_exception)
+ {
+ oss.precision(6);
+ oss << " t=" << t << "s;";
+ }
+ else
+ oss << " exception";
+ oss.precision(3);
+ oss << " enq=" << _num_enq;
+ if (no_exception)
+ oss << " (" << (_num_enq / t) << ")";
+ oss << "; deq=" << _num_deq;
+ if (no_exception)
+ oss << " (" << (_num_deq / t) << ")";
+ oss << "; wr=" << (_num_enq + _num_deq);
+ if (no_exception)
+ oss << " (" << ((_num_enq + _num_deq) / t) << ")";
+ oss << "; rd=" << _num_read;
+ if (no_exception)
+ oss << " (" << (_num_read / t) << ")";
+ oss << "; tot=" << (_num_enq + _num_deq + _num_read);
+ if (no_exception)
+ oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << ")";
+ return oss.str();
+}
+
+void
+test_case_result::calc_test_time()
+{
+ if (!_start_time.is_zero() && _stop_time >= _start_time)
+ _test_time = _stop_time - _start_time;
+}
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h
new file mode 100644
index 0000000000..d15f9d021d
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_test_case_result_hpp
+#define mrg_jtt_test_case_result_hpp
+
+#include <boost/shared_ptr.hpp>
+#include <deque>
+#include "qpid/legacystore/jrnl/jexception.h"
+#include "qpid/legacystore/jrnl/time_ns.h"
+#include <string>
+
+namespace mrg
+{
+namespace jtt
+{
+
+ class test_case_result
+ {
+ public:
+ typedef boost::shared_ptr<test_case_result> shared_ptr;
+
+ typedef std::deque<std::string> elist;
+ typedef elist::const_iterator elist_citr;
+
+ protected:
+ std::string _jid;
+ u_int32_t _num_enq;
+ u_int32_t _num_deq;
+ u_int32_t _num_read; // Messages actually read
+ u_int32_t _num_rproc; // Messages handled by read thread (not all are read)
+ journal::time_ns _start_time;
+ journal::time_ns _stop_time;
+ bool _stopped;
+ journal::time_ns _test_time;
+ elist _exception_list;
+
+ public:
+ test_case_result(const std::string& jid);
+ virtual ~test_case_result();
+
+ inline const std::string& jid() const { return _jid; }
+ inline u_int32_t num_enq() const { return _num_enq; }
+ inline u_int32_t incr_num_enq() { return ++_num_enq; }
+ inline u_int32_t num_deq() const { return _num_deq; }
+ inline u_int32_t incr_num_deq() { return ++_num_deq; }
+ inline u_int32_t num_read() const { return _num_read; }
+ inline u_int32_t incr_num_read() { return ++_num_read; }
+ inline u_int32_t num_rproc() const { return _num_rproc; }
+ inline u_int32_t incr_num_rproc() { return ++_num_rproc; }
+
+ inline const journal::time_ns& start_time() const { return _start_time; }
+ inline void set_start_time() { ::clock_gettime(CLOCK_REALTIME, &_start_time); }
+ inline const journal::time_ns& stop_time() const { return _stop_time; }
+ inline void set_stop_time()
+ { ::clock_gettime(CLOCK_REALTIME, &_stop_time); calc_test_time(); }
+ inline void set_test_time(const journal::time_ns& ts) { _test_time = ts; }
+ inline const journal::time_ns& test_time() const { return _test_time; }
+ const std::string test_time_str() const;
+
+ void add_exception(const journal::jexception& e, const bool set_stop_time_flag = true);
+ void add_exception(const std::string& err_str, const bool set_stop_time_flag = true);
+ void add_exception(const char* err_str, const bool set_stop_time_flag = true);
+ inline bool exception() const { return _exception_list.size() > 0; }
+ inline unsigned exception_count() const { return _exception_list.size(); }
+ inline elist_citr begin() { return _exception_list.begin(); }
+ inline elist_citr end() { return _exception_list.end(); }
+ inline const std::string& operator[](unsigned i) { return _exception_list[i]; }
+
+ void clear();
+ const std::string str(const bool summary) const;
+
+ protected:
+ const std::string str_full() const;
+ const std::string str_summary() const;
+ void calc_test_time();
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_test_case_result_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp
new file mode 100644
index 0000000000..da439e71e8
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.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 "test_case_result_agregation.h"
+
+#include <iomanip>
+#include <sstream>
+
+namespace mrg
+{
+namespace jtt
+{
+
+test_case_result_agregation::test_case_result_agregation():
+ test_case_result("Average"),
+ _tc_average(true),
+ _fmt_chk_done(false),
+ _fmt_chk_err(false),
+ _res_list()
+{
+}
+
+test_case_result_agregation::test_case_result_agregation(const std::string& jid):
+ test_case_result(jid),
+ _tc_average(false),
+ _fmt_chk_done(false),
+ _fmt_chk_err(false),
+ _res_list()
+{}
+
+test_case_result_agregation::~test_case_result_agregation()
+{}
+
+void
+test_case_result_agregation::add_test_result(const test_case_result::shared_ptr& tcrp)
+{
+ if (_tc_average || _jid.compare(tcrp->jid()) == 0)
+ {
+ _num_enq += tcrp->num_enq();
+ _num_deq += tcrp->num_deq();
+ _num_read += tcrp->num_read();
+ add_test_time(tcrp->test_time());
+ _exception_list.insert(_exception_list.end(), tcrp->begin(), tcrp->end());
+ _res_list.push_back(tcrp);
+ }
+}
+
+bool
+test_case_result_agregation::exception() const
+{
+ for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++)
+ if ((*i)->exception())
+ return true;
+ return false;
+}
+
+unsigned
+test_case_result_agregation::exception_count() const
+{
+ unsigned cnt = 0;
+ for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++)
+ cnt += (*i)->exception_count();
+ return cnt;
+}
+
+void
+test_case_result_agregation::clear()
+{
+ test_case_result::clear();
+ _res_list.clear();
+}
+
+const std::string
+test_case_result_agregation::str(const bool last_only, const bool summary) const
+{
+ std::ostringstream oss;
+ if (last_only)
+ oss << " " << _res_list.at(_res_list.size()-1)->str(summary);
+ else
+ {
+ for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++)
+ oss << " " << (*i)->str(summary);
+ }
+ if (_res_list.size() > 1)
+ oss << " " << (summary ? str_summary(last_only) : str_full(last_only));
+ return oss.str();
+}
+
+const std::string
+test_case_result_agregation::str_full(const bool /*last_only*/) const
+{
+ std::ostringstream oss;
+ oss.precision(2);
+ if (_tc_average)
+ oss << "Average across all journal instances:" << std::endl;
+ else
+ oss << "Average for jid=\"" << _jid << "\":" << std::endl;
+ oss << " total number results: " << _res_list.size() << std::endl;
+ oss << " number exceptions: " << _exception_list.size() << " (" <<
+ (100.0 * _res_list.size() / _exception_list.size()) << "%)" << std::endl;
+
+ oss << test_case_result::str_full();
+
+ if (_exception_list.size())
+ {
+ unsigned n = 0;
+ oss << "List of exceptions/errors:" << std::endl;
+ for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++)
+ oss << " " << n << ". " << (*i) << std::endl;
+ }
+
+ if (!_tc_average && _res_list.size() > 1)
+ {
+ oss << "Individual results:" << std::endl;
+ for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++)
+ oss << " " << (*i)->str(false) << std::endl;
+ oss << std::endl;
+ }
+
+ return oss.str();
+}
+
+const std::string
+test_case_result_agregation::str_summary(const bool /*last_only*/) const
+{
+ std::ostringstream oss;
+ if (_tc_average)
+ oss << "overall average [" << _res_list.size() << "]:";
+ else
+ oss << "average (" << _res_list.size() << "):";
+
+ oss << test_case_result::str_summary();
+ if (_fmt_chk_done)
+ oss << " fmt-chk=" << (_fmt_chk_err ? "fail" : "ok");
+
+ if (_exception_list.size())
+ {
+ if (_tc_average)
+ oss << " fail: " << _exception_list.size() << " exception"
+ << (_exception_list.size()>1?"s":"") << std::endl;
+ else
+ {
+ if (_exception_list.size() == 1)
+ oss << " fail: " << *_exception_list.begin() << std::endl;
+ else
+ {
+ oss << std::endl;
+ unsigned n = 0;
+ for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++)
+ oss << " " << n << ". " << (*i) << std::endl;
+ }
+ }
+ }
+ else
+ oss << " ok" << std::endl;
+ return oss.str();
+}
+
+const journal::time_ns&
+test_case_result_agregation::add_test_time(const journal::time_ns& t)
+{
+ _test_time += t;
+ return _test_time;
+}
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h
new file mode 100644
index 0000000000..0b3998176c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 mrg_jtt_test_case_result_agregation_hpp
+#define mrg_jtt_test_case_result_agregation_hpp
+
+#include "test_case_result.h"
+
+#include <iostream>
+#include <vector>
+
+namespace mrg
+{
+namespace jtt
+{
+
+ class test_case_result_agregation : public test_case_result
+ {
+ public:
+ typedef boost::shared_ptr<test_case_result_agregation> shared_ptr;
+
+ typedef std::vector<test_case_result::shared_ptr> tcrp_list;
+ typedef tcrp_list::const_iterator tcrp_list_citr;
+
+ private:
+ bool _tc_average;
+ bool _fmt_chk_done;
+ bool _fmt_chk_err;
+ tcrp_list _res_list;
+
+ public:
+ test_case_result_agregation(); // used for average across jrnl instances
+ test_case_result_agregation(const std::string& jid);
+ virtual ~test_case_result_agregation();
+
+ void add_test_result(const test_case_result::shared_ptr& tcrp);
+
+ inline bool tc_average_mode() const { return _tc_average; }
+ inline bool fmt_chk_done() const { return _fmt_chk_done; }
+ inline bool fmt_chk_res() const { return _fmt_chk_err; }
+ inline void set_fmt_chk_res(const bool err)
+ { _fmt_chk_done = true; _fmt_chk_err |= err; if (err) add_exception("Journal format error"); }
+ inline u_int32_t num_results() const { return _res_list.size(); }
+ inline tcrp_list_citr rlist_begin() const { return _res_list.begin(); }
+ inline tcrp_list_citr rlist_end() const { return _res_list.end(); }
+ inline const test_case_result::shared_ptr& operator[](unsigned i) const
+ { return _res_list[i]; }
+ bool exception() const;
+ unsigned exception_count() const;
+
+ void clear();
+ const std::string str(const bool last_only, const bool summary) const;
+
+ private:
+ const std::string str_full(const bool last_only) const;
+ const std::string str_summary(const bool last_only) const;
+ const journal::time_ns& add_test_time(const journal::time_ns& t);
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_test_case_result_agregation_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp
new file mode 100644
index 0000000000..b818d6c7ae
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp
@@ -0,0 +1,169 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "test_case_set.h"
+
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+
+namespace mrg
+{
+namespace jtt
+{
+
+test_case_set::test_case_set():
+ _tc_list(),
+ _csv_ignored(0)
+{}
+
+test_case_set::test_case_set(const std::string& csv_filename, const bool recover_mode,
+ const csv_map& cols):
+ _tc_list(),
+ _csv_ignored(0)
+{
+ append_from_csv(csv_filename, recover_mode, cols);
+}
+
+test_case_set::~test_case_set()
+{}
+
+void
+test_case_set::append(const unsigned test_case_num, const u_int32_t num_msgs,
+ const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq,
+ const std::size_t min_xid_size, const std::size_t max_xid_size,
+ const test_case::transient_t transient, const test_case::external_t external,
+ const std::string& comment)
+{
+ test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size,
+ max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment));
+ append(tcp);
+}
+
+
+#define CSV_BUFF_SIZE 2048
+void
+test_case_set::append_from_csv(const std::string& csv_filename, const bool recover_mode,
+ const csv_map& cols)
+{
+ char buff[CSV_BUFF_SIZE];
+ std::ifstream ifs(csv_filename.c_str());
+ while (ifs.good())
+ {
+ ifs.getline(buff, (std::streamsize)CSV_BUFF_SIZE);
+ if (ifs.gcount())
+ {
+ test_case::shared_ptr tcp = get_tc_from_csv(buff, cols);
+ if (tcp.get())
+ {
+ if (!recover_mode || tcp->auto_deq())
+ append(tcp);
+ else
+ _csv_ignored++;
+ }
+ }
+ }
+}
+
+test_case::shared_ptr
+test_case_set::get_tc_from_csv(const std::string& csv_line, const csv_map& cols)
+{
+ unsigned test_case_num = 0;
+ u_int32_t num_msgs = 0;
+ std::size_t min_data_size = 0;
+ std::size_t max_data_size = 0;
+ bool auto_deq = false;
+ std::size_t min_xid_size = 0;
+ std::size_t max_xid_size = 0;
+ test_case::transient_t transient = test_case::JTT_TRANSIENT;
+ test_case::external_t external = test_case::JDL_INTERNAL;
+ std::string comment;
+
+ csv_tok t(csv_line);
+ unsigned col_num = 0;
+ for (csv_tok_citr t_itr = t.begin(); t_itr != t.end(); ++t_itr, ++col_num)
+ {
+ const std::string& tok = *t_itr;
+ csv_map_citr m_citr = cols.find(col_num);
+ if (m_citr != cols.end())
+ {
+ switch (m_citr->second)
+ {
+ case CSV_TC_NUM:
+ if (!tok.size() || tok[0] < '0' || tok[0] > '9')
+ return test_case::shared_ptr();
+ test_case_num = unsigned(std::atol(tok.c_str()));
+ break;
+ case CSV_TC_NUM_MSGS: num_msgs = u_int32_t(std::atol(tok.c_str())); break;
+ case CSV_TC_MIN_DATA_SIZE: min_data_size = std::size_t(std::atol(tok.c_str())); break;
+ case CSV_TC_MAX_DATA_SIZE: max_data_size = std::size_t(std::atol(tok.c_str())); break;
+ case CSV_TC_AUTO_DEQ:
+ if (tok == "TRUE" || tok == "1")
+ auto_deq = true;
+ break;
+ case CSV_TC_MIN_XID_SIZE: min_xid_size = std::size_t(std::atol(tok.c_str())); break;
+ case CSV_TC_MAX_XID_SIZE: max_xid_size = std::size_t(std::atol(tok.c_str())); break;
+ case CSV_TC_TRANSIENT:
+ if (tok == "TRUE" || tok == "1")
+ transient = test_case::JTT_PERSISTNET;
+ else if (tok == "RANDOM" || tok == "-1")
+ transient = test_case::JTT_RANDOM;
+ break;
+ case CSV_TC_EXTERNAL:
+ if (tok == "TRUE" || tok == "1")
+ external = test_case::JDL_EXTERNAL;
+ else if (tok == "RANDOM" || tok == "-1")
+ external = test_case::JDL_RANDOM;
+ break;
+ case CSV_TC_COMMENT: comment = *t_itr; break;
+ }
+ }
+ }
+ if (col_num)
+ return test_case::shared_ptr(new test_case(test_case_num, num_msgs, min_data_size,
+ max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment));
+ else
+ return test_case::shared_ptr();
+}
+
+// Static member initializations
+// This csv_map is for use on the standard spreadsheet-derived test case csv files.
+test_case_set::csv_map test_case_set::std_csv_map;
+const bool test_case_set::_map_init = __init();
+
+bool
+test_case_set::__init()
+{
+ std_csv_map.insert(test_case_set::csv_pair(0, test_case_set::CSV_TC_NUM));
+ std_csv_map.insert(test_case_set::csv_pair(5, test_case_set::CSV_TC_NUM_MSGS));
+ std_csv_map.insert(test_case_set::csv_pair(7, test_case_set::CSV_TC_MIN_DATA_SIZE));
+ std_csv_map.insert(test_case_set::csv_pair(8, test_case_set::CSV_TC_MAX_DATA_SIZE));
+ std_csv_map.insert(test_case_set::csv_pair(11, test_case_set::CSV_TC_AUTO_DEQ));
+ std_csv_map.insert(test_case_set::csv_pair(9, test_case_set::CSV_TC_MIN_XID_SIZE));
+ std_csv_map.insert(test_case_set::csv_pair(10, test_case_set::CSV_TC_MAX_XID_SIZE));
+ std_csv_map.insert(test_case_set::csv_pair(12, test_case_set::CSV_TC_TRANSIENT));
+ std_csv_map.insert(test_case_set::csv_pair(13, test_case_set::CSV_TC_EXTERNAL));
+ std_csv_map.insert(test_case_set::csv_pair(20, test_case_set::CSV_TC_COMMENT));
+ return true;
+}
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h
new file mode 100644
index 0000000000..94a1ee3172
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h
@@ -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.
+ *
+ */
+
+#ifndef mrg_jtt_test_case_set_hpp
+#define mrg_jtt_test_case_set_hpp
+
+#include "test_case.h"
+
+#include <cstddef>
+#include <boost/tokenizer.hpp>
+#include <map>
+#include <vector>
+
+namespace mrg
+{
+namespace jtt
+{
+
+ class test_case_set
+ {
+ public:
+ enum csv_col_enum {
+ CSV_TC_NUM = 0,
+ CSV_TC_NUM_MSGS,
+ CSV_TC_MIN_DATA_SIZE,
+ CSV_TC_MAX_DATA_SIZE,
+ CSV_TC_AUTO_DEQ,
+ CSV_TC_MIN_XID_SIZE,
+ CSV_TC_MAX_XID_SIZE,
+ CSV_TC_TRANSIENT,
+ CSV_TC_EXTERNAL,
+ CSV_TC_COMMENT };
+ typedef std::pair<unsigned, csv_col_enum> csv_pair;
+ typedef std::map<unsigned, csv_col_enum> csv_map;
+ typedef csv_map::const_iterator csv_map_citr;
+ static csv_map std_csv_map;
+
+ typedef std::vector<test_case::shared_ptr> tcl;
+ typedef tcl::iterator tcl_itr;
+ typedef tcl::const_iterator tcl_citr;
+
+ typedef boost::tokenizer<boost::escaped_list_separator<char> > csv_tok;
+ typedef csv_tok::const_iterator csv_tok_citr;
+
+ private:
+ tcl _tc_list;
+ static const bool _map_init;
+ unsigned _csv_ignored;
+
+ public:
+ test_case_set();
+ test_case_set(const std::string& csv_filename, const bool recover_mode,
+ const csv_map& cols = std_csv_map);
+ virtual ~test_case_set();
+
+ inline unsigned size() const { return _tc_list.size(); }
+ inline unsigned ignored() const { return _csv_ignored; }
+ inline bool empty() const { return _tc_list.empty(); }
+
+ inline void append(const test_case::shared_ptr& tc) { _tc_list.push_back(tc); }
+ void append(const unsigned test_case_num, const u_int32_t num_msgs,
+ const std::size_t min_data_size, const std::size_t max_data_size,
+ const bool auto_deq, const std::size_t min_xid_size,
+ const std::size_t max_xid_size, const test_case::transient_t transient,
+ const test_case::external_t external, const std::string& comment);
+ void append_from_csv(const std::string& csv_filename, const bool recover_mode,
+ const csv_map& cols = std_csv_map);
+ inline tcl_itr begin() { return _tc_list.begin(); }
+ inline tcl_itr end() { return _tc_list.end(); }
+ inline const test_case::shared_ptr& operator[](unsigned i) { return _tc_list[i]; }
+ inline void clear() { _tc_list.clear(); }
+
+ private:
+ test_case::shared_ptr get_tc_from_csv(const std::string& csv_line, const csv_map& cols);
+ static bool __init();
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_test_case_set_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp
new file mode 100644
index 0000000000..de0b5dbfb9
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp
@@ -0,0 +1,218 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_mgr.h"
+
+#include <cstdlib>
+#include <iostream>
+#include <sys/stat.h>
+#include "test_case_set.h"
+
+namespace mrg
+{
+namespace jtt
+{
+
+test_mgr::test_mgr(args& args):
+ _ji_list(),
+ _args(args),
+ _err_flag(false),
+ _random_fn_ptr(random_fn)
+{
+ if (_args.seed)
+ std::srand(_args.seed);
+}
+
+test_mgr::~test_mgr()
+{}
+
+void
+test_mgr::run()
+{
+ // TODO: complete tidy-up of non-summary (verbose) results, then pull through
+ // a command-line summary to control this.
+ // Idea: --summary: prints short results afterwards
+ // --verbose: prints long version as test progresses
+ // defualt: none of these, similar to current summary = true version.
+ const bool summary = true;
+
+ std::cout << "CSV file: \"" << _args.test_case_csv_file_name << "\"";
+ test_case_set tcs(_args.test_case_csv_file_name, _args.recover_mode);
+
+ if (tcs.size())
+ {
+ std::cout << " (found " << tcs.size() << " test case" << (tcs.size() != 1 ? "s" : "") <<
+ ")" << std::endl;
+ if (tcs.ignored())
+ std::cout << "WARNING: " << tcs.ignored() << " test cases were ignored. (All test "
+ "cases without auto-dequeue are ignored when recover-mode is selected.)" <<
+ std::endl;
+ _args.print_args();
+ }
+ else if(tcs.ignored())
+ {
+ std::cout << " WARNING: All " << tcs.ignored() << " test case(s) were ignored. (All test "
+ "cases without auto-dequeue are ignored when recover-mode is selected.)" <<
+ std::endl;
+ }
+ else
+ std::cout << " (WARNING: This CSV file is empty or does not exist.)" << std::endl;
+
+ do
+ {
+ unsigned u = 0;
+ if (_args.randomize)
+ random_shuffle(tcs.begin(), tcs.end(), _random_fn_ptr);
+ for (test_case_set::tcl_itr tci = tcs.begin(); tci != tcs.end(); tci++, u++)
+ {
+ if (summary)
+ std::cout << "Test case " << (*tci)->test_case_num() << ": \"" <<
+ (*tci)->comment() << "\"" << std::endl;
+ else
+ std::cout << (*tci)->str() << std::endl;
+ if (!_args.reuse_instance || _ji_list.empty())
+ initialize_jrnls();
+ for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++)
+ (*jii)->init_tc(*tci, &_args);
+ for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++)
+ (*jii)->run_tc();
+ for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++)
+ (*jii)->tc_wait_compl();
+
+ if (_args.format_chk)
+ {
+ for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++)
+ {
+ jrnl_init_params::shared_ptr jpp = (*jii)->params();
+ std::string ja = _args.jfile_analyzer;
+ if (ja.empty()) ja = "./jfile_chk.py";
+ if (!exists(ja))
+ {
+ std::ostringstream oss;
+ oss << "ERROR: Validation program \"" << ja << "\" does not exist" << std::endl;
+ throw std::runtime_error(oss.str());
+ }
+ std::ostringstream oss;
+ oss << ja << " -b " << jpp->base_filename();
+ // TODO: When jfile_check.py can handle previously recovered journals for
+ // specific tests, then remove this exclusion.
+ if (!_args.recover_mode)
+ {
+ oss << " -c " << _args.test_case_csv_file_name;
+ oss << " -t " << (*tci)->test_case_num();
+ }
+ oss << " -q " << jpp->jdir();
+ bool res = system(oss.str().c_str()) != 0;
+ (*tci)->set_fmt_chk_res(res, jpp->jid());
+ if (res) _err_flag = true;
+ }
+ }
+
+ if (!_args.recover_mode && !_args.keep_jrnls)
+ for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++)
+ try { mrg::journal::jdir::delete_dir((*jii)->jrnl_dir()); }
+ catch (...) {} // TODO - work out exception strategy for failure here...
+
+ print_results(*tci, summary);
+ if ((*tci)->average().exception())
+ _err_flag = true;
+ if (_abort || (!_args.repeat_flag && _signal))
+ break;
+ if (_args.pause_secs && tci != tcs.end())
+ ::usleep(_args.pause_secs * 1000000);
+ }
+ }
+ while (_args.repeat_flag && !_signal);
+}
+
+// static fn:
+void
+test_mgr::signal_handler(int sig)
+{
+ if (_signal)
+ _abort = true;
+ _signal = sig;
+ std::cout << std::endl;
+ std::cout << "********************************" << std::endl;
+ std::cout << "Caught signal " << sig << std::endl;
+ if (_abort)
+ std::cout << "Aborting..." << std::endl;
+ else
+ std::cout << "Completing current test cycle..." << std::endl;
+ std::cout << "********************************" << std::endl << std::endl;
+}
+
+bool
+test_mgr::exists(std::string fname)
+{
+ struct stat s;
+ if (::stat(fname.c_str(), &s))
+ {
+ if (errno == ENOENT) // No such dir or file
+ return false;
+ // Throw for any other condition
+ std::ostringstream oss;
+ oss << "ERROR: test_mgr::exists(): file=\"" << fname << "\": " << FORMAT_SYSERR(errno);
+ throw std::runtime_error(oss.str());
+ }
+ return true;
+}
+
+void
+test_mgr::initialize_jrnls()
+{
+ _ji_list.clear();
+ for (unsigned i=0; i<_args.num_jrnls; i++)
+ {
+ std::ostringstream jid;
+ jid << std::hex << std::setfill('0');
+ jid << "test_" << std::setw(4) << std::hex << i;
+ std::ostringstream jdir;
+ jdir << _args.journal_dir << "/" << jid.str();
+ jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid.str(), jdir.str(), jid.str()));
+ jrnl_instance::shared_ptr jip(new jrnl_instance(jpp));
+ _ji_list.push_back(jip);
+ }
+}
+
+void
+test_mgr::print_results(test_case::shared_ptr tcp, const bool summary)
+{
+ if (!summary)
+ std::cout << " === Results ===" << std::endl;
+
+// TODO - the reporting is broken when --repeat is used. The following commented-out
+// section was an attempt to fix it, but there are too many side-effects.
+// for (test_case::res_map_citr i=tcp->jmap_begin(); i!=tcp->jmap_end(); i++)
+// std::cout << (*i).second->str(summary, summary);
+// if (tcp->num_jrnls() > 1)
+ std::cout << tcp->average().str(false, summary);
+
+ if (!summary)
+ std::cout << std::endl;
+}
+
+// static instances
+volatile sig_atomic_t test_mgr::_signal = 0;
+volatile bool test_mgr::_abort = false;
+
+} // namespace jtt
+} // namespace mrg
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h
new file mode 100644
index 0000000000..e608ac6280
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef mrg_jtt_test_mgr_hpp
+#define mrg_jtt_test_mgr_hpp
+
+#include "args.h"
+#include <csignal>
+#include <cstdlib>
+#include "jrnl_instance.h"
+
+namespace mrg
+{
+namespace jtt
+{
+ class test_mgr
+ {
+ public:
+ typedef std::vector<jrnl_instance::shared_ptr> ji_list;
+ typedef ji_list::iterator ji_list_itr;
+ typedef ji_list::const_iterator ji_list_citr;
+
+ private:
+ ji_list _ji_list;
+ args& _args;
+ bool _err_flag;
+ ptrdiff_t (*_random_fn_ptr)(const ptrdiff_t i);
+ static volatile std::sig_atomic_t _signal;
+ static volatile bool _abort;
+
+ public:
+ test_mgr(args& args);
+ virtual ~test_mgr();
+ void run();
+ inline bool error() const { return _err_flag; }
+
+ static void signal_handler(int signal);
+
+ private:
+ static bool exists(std::string file_name);
+ void initialize_jrnls();
+ void print_results(test_case::shared_ptr tcp, const bool summary);
+ inline static ptrdiff_t random_fn(const ptrdiff_t i)
+ { return static_cast<ptrdiff_t>(1.0 * i * std::rand() / RAND_MAX); }
+ };
+
+} // namespace jtt
+} // namespace mrg
+
+#endif // ifndef mrg_jtt_test_mgr_hpp
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/prof b/qpid/cpp/src/tests/legacystore/jrnl/prof
new file mode 100755
index 0000000000..2abe7baa4a
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/prof
@@ -0,0 +1,32 @@
+#!/usr/bin/env 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.
+#
+
+mkdir -p profile
+opcontrol --setup --no-vmlinux --separate=library
+opcontrol --start
+# -- Do stuff here --
+./jtest wtests.csv 264
+# -- End of stuff --
+opcontrol --stop
+opcontrol --dump
+opcontrol --shutdown
+opreport -l ./jtest
+opannotate --source --output-dir=profile ./jtest
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests b/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests
new file mode 100755
index 0000000000..e169e39c60
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests
@@ -0,0 +1,47 @@
+#!/usr/bin/env 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.
+#
+
+if test x${TMP_DATA_DIR} == x; then
+ export TMP_DATA_DIR=/tmp
+fi
+fail=0
+num_jrnls=3
+
+# Run jtt using default test set
+echo
+echo "===== Mode 1: New journal instance, no recover ====="
+jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --format-chk --num-jrnls ${num_jrnls} || fail=1
+rm -rf ${TMP_DATA_DIR}/test_0*
+echo
+echo "===== Mode 2: Re-use journal instance, no recover ====="
+jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --format-chk --num-jrnls ${num_jrnls} || fail=1
+rm -rf ${TMP_DATA_DIR}/test_0*
+echo
+echo "===== Mode 3: New journal instance, recover previous test journal ====="
+jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1
+rm -rf ${TMP_DATA_DIR}/test_0*
+echo
+echo "===== Mode 4: Re-use journal instance, recover previous test journal ====="
+jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1
+rm -rf ${TMP_DATA_DIR}/test_0*
+echo
+
+exit $fail
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/tests.ods b/qpid/cpp/src/tests/legacystore/jrnl/tests.ods
new file mode 100644
index 0000000000..d900374321
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/jrnl/tests.ods
Binary files differ
diff --git a/qpid/cpp/src/tests/legacystore/persistence.py b/qpid/cpp/src/tests/legacystore/persistence.py
new file mode 100644
index 0000000000..c4ab712f14
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/persistence.py
@@ -0,0 +1,574 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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, re, traceback, socket
+from getopt import getopt, GetoptError
+
+from qpid.connection import Connection
+from qpid.util import connect
+from qpid.datatypes import Message, RangedSet
+from qpid.queue import Empty
+from qpid.session import SessionException
+from qpid.testlib import TestBase010
+from time import sleep
+
+class PersistenceTest(TestBase010):
+
+ XA_RBROLLBACK = 1
+ XA_RBTIMEOUT = 2
+ XA_OK = 0
+
+ def createMessage(self, **kwargs):
+ session = self.session
+ dp = {}
+ dp['delivery_mode'] = 2
+ mp = {}
+ for k, v in kwargs.iteritems():
+ if k in ['routing_key', 'delivery_mode']: dp[k] = v
+ if k in ['message_id', 'correlation_id', 'application_headers']: mp[k] = v
+ args = []
+ args.append(session.delivery_properties(**dp))
+ if len(mp):
+ args.append(session.message_properties(**mp))
+ if kwargs.has_key('body'): args.append(kwargs['body'])
+ return Message(*args)
+
+ def phase1(self):
+ session = self.session
+
+ session.queue_declare(queue="queue-a", durable=True)
+ session.queue_declare(queue="queue-b", durable=True)
+ session.exchange_bind(queue="queue-a", exchange="amq.direct", binding_key="a")
+ session.exchange_bind(queue="queue-b", exchange="amq.direct", binding_key="b")
+
+ session.message_transfer(destination="amq.direct",
+ message=self.createMessage(routing_key="a", correlation_id="Msg0001", body="A_Message1"))
+ session.message_transfer(destination="amq.direct",
+ message=self.createMessage(routing_key="b", correlation_id="Msg0002", body="B_Message1"))
+
+# session.queue_declare(queue="lvq-test", durable=True, arguments={"qpid.last_value_queue":True})
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B1"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A1"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A2"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B2"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"B"}, body="B3"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C1"))
+
+
+
+ def phase2(self):
+ session = self.session
+
+ #check queues exists
+ session.queue_declare(queue="queue-a", durable=True, passive=True)
+ session.queue_declare(queue="queue-b", durable=True, passive=True)
+
+ #check they are still bound to amq.direct correctly
+ responses = []
+ responses.append(session.exchange_bound(queue="queue-a", exchange="amq.direct", binding_key="a"))
+ responses.append(session.exchange_bound(queue="queue-b", exchange="amq.direct", binding_key="b"))
+ for r in responses:
+ self.assert_(not r.exchange_not_found)
+ self.assert_(not r.queue_not_found)
+ self.assert_(not r.key_not_matched)
+
+
+ #check expected messages are there
+ self.assertMessageOnQueue("queue-a", "Msg0001", "A_Message1")
+ self.assertMessageOnQueue("queue-b", "Msg0002", "B_Message1")
+
+ self.assertEmptyQueue("queue-a")
+ self.assertEmptyQueue("queue-b")
+
+ session.queue_declare(queue="queue-c", durable=True)
+
+ #send a message to a topic such that it reaches all queues
+ session.exchange_bind(queue="queue-a", exchange="amq.topic", binding_key="abc")
+ session.exchange_bind(queue="queue-b", exchange="amq.topic", binding_key="abc")
+ session.exchange_bind(queue="queue-c", exchange="amq.topic", binding_key="abc")
+
+ session.message_transfer(destination="amq.topic",
+ message=self.createMessage(routing_key="abc", correlation_id="Msg0003", body="AB_Message2"))
+
+# #check LVQ exists and has exepected messages:
+# session.queue_declare(queue="lvq-test", durable=True, passive=True)
+# session.message_subscribe(destination="lvq", queue="lvq-test")
+# lvq = session.incoming("lvq")
+# lvq.start()
+# accepted = RangedSet()
+# for m in ["A2", "B3", "C1"]:
+# msg = lvq.get(timeout=1)
+# self.assertEquals(m, msg.body)
+# accepted.add(msg.id)
+# try:
+# extra = lvq.get(timeout=1)
+# self.fail("lvq-test not empty, contains: " + extra.body)
+# except Empty: None
+# #publish some more messages while subscriber is active (no replacement):
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C2"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C3"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A3"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"A"}, body="A4"))
+# session.message_transfer(message=self.createMessage(routing_key="lvq-test", application_headers={"qpid.LVQ_key":"C"}, body="C4"))
+# #check that accepting replaced messages is safe
+# session.message_accept(accepted)
+
+
+ def phase3(self):
+ session = self.session
+
+# #lvq recovery validation
+# session.queue_declare(queue="lvq-test", durable=True, passive=True)
+# session.message_subscribe(destination="lvq", queue="lvq-test")
+# lvq = session.incoming("lvq")
+# lvq.start()
+# accepted = RangedSet()
+# lvq.start()
+# for m in ["C4", "A4"]:
+# msg = lvq.get(timeout=1)
+# self.assertEquals(m, msg.body)
+# accepted.add(msg.id)
+# session.message_accept(accepted)
+# try:
+# extra = lvq.get(timeout=1)
+# self.fail("lvq-test not empty, contains: " + extra.body)
+# except Empty: None
+# session.message_cancel(destination="lvq")
+# session.queue_delete(queue="lvq-test")
+
+
+ #check queues exists
+ session.queue_declare(queue="queue-a", durable=True, passive=True)
+ session.queue_declare(queue="queue-b", durable=True, passive=True)
+ session.queue_declare(queue="queue-c", durable=True, passive=True)
+
+ session.tx_select()
+ #check expected messages are there
+ self.assertMessageOnQueue("queue-a", "Msg0003", "AB_Message2")
+ self.assertMessageOnQueue("queue-b", "Msg0003", "AB_Message2")
+ self.assertMessageOnQueue("queue-c", "Msg0003", "AB_Message2")
+
+ self.assertEmptyQueue("queue-a")
+ self.assertEmptyQueue("queue-b")
+ self.assertEmptyQueue("queue-c")
+
+ #note: default bindings must be restored for this to work
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a", correlation_id="Msg0004", body="A_Message3"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a", correlation_id="Msg0005", body="A_Message4"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a", correlation_id="Msg0006", body="A_Message5"))
+
+ session.tx_commit()
+
+
+ #delete a queue
+ session.queue_delete(queue="queue-c")
+
+ session.message_subscribe(destination="ctag", queue="queue-a", accept_mode=0)
+ session.message_flow(destination="ctag", unit=0, value=0xFFFFFFFF)
+ session.message_flow(destination="ctag", unit=1, value=0xFFFFFFFF)
+ included = session.incoming("ctag")
+ msg1 = included.get(timeout=1)
+ self.assertExpectedContent(msg1, "Msg0004", "A_Message3")
+ msg2 = included.get(timeout=1)
+ self.assertExpectedContent(msg2, "Msg0005", "A_Message4")
+ msg3 = included.get(timeout=1)
+ self.assertExpectedContent(msg3, "Msg0006", "A_Message5")
+ self.ack(msg1, msg2, msg3)
+
+ session.message_transfer(destination="amq.direct", message=self.createMessage(
+ routing_key="queue-b", correlation_id="Msg0007", body="B_Message3"))
+
+ session.tx_rollback()
+
+
+ def phase4(self):
+ session = self.session
+
+ #check queues exists
+ session.queue_declare(queue="queue-a", durable=True, passive=True)
+ session.queue_declare(queue="queue-b", durable=True, passive=True)
+
+ self.assertMessageOnQueue("queue-a", "Msg0004", "A_Message3")
+ self.assertMessageOnQueue("queue-a", "Msg0005", "A_Message4")
+ self.assertMessageOnQueue("queue-a", "Msg0006", "A_Message5")
+
+ self.assertEmptyQueue("queue-a")
+ self.assertEmptyQueue("queue-b")
+
+ #check this queue doesn't exist
+ try:
+ session.queue_declare(queue="queue-c", durable=True, passive=True)
+ raise Exception("Expected queue-c to have been deleted")
+ except SessionException, e:
+ self.assertEquals(404, e.args[0].error_code)
+
+ def phase5(self):
+
+ session = self.session
+ queues = ["queue-a1", "queue-a2", "queue-b1", "queue-b2", "queue-c1", "queue-c2", "queue-d1", "queue-d2"]
+
+ for q in queues:
+ session.queue_declare(queue=q, durable=True)
+ session.queue_purge(queue=q)
+
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-a1", correlation_id="MsgA", body="MessageA"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-b1", correlation_id="MsgB", body="MessageB"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-c1", correlation_id="MsgC", body="MessageC"))
+ session.message_transfer(message=self.createMessage(
+ routing_key="queue-d1", correlation_id="MsgD", body="MessageD"))
+
+ session.dtx_select()
+ txa = self.xid('a')
+ txb = self.xid('b')
+ txc = self.xid('c')
+ txd = self.xid('d')
+
+ self.txswap("queue-a1", "queue-a2", txa)
+ self.txswap("queue-b1", "queue-b2", txb)
+ self.txswap("queue-c1", "queue-c2", txc)
+ self.txswap("queue-d1", "queue-d2", txd)
+
+ #no queue should have any messages accessible
+ for q in queues:
+ self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+
+ self.assertEqual(self.XA_OK, session.dtx_commit(xid=txa, one_phase=True).status)
+ self.assertEqual(self.XA_OK, session.dtx_rollback(xid=txb).status)
+ self.assertEqual(self.XA_OK, session.dtx_prepare(xid=txc).status)
+ self.assertEqual(self.XA_OK, session.dtx_prepare(xid=txd).status)
+
+ #further checks
+ not_empty = ["queue-a2", "queue-b1"]
+ for q in queues:
+ if q in not_empty:
+ self.assertEqual(1, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+ else:
+ self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+
+
+ def phase6(self):
+ session = self.session
+
+ #check prepared transaction are reported correctly by recover
+ txc = self.xid('c')
+ txd = self.xid('d')
+
+ xids = session.dtx_recover().in_doubt
+ ids = [x.global_id for x in xids] #TODO: come up with nicer way to test these
+
+ if txc.global_id not in ids:
+ self.fail("Recovered xids not as expected. missing: %s" % (txc))
+ if txd.global_id not in ids:
+ self.fail("Recovered xids not as expected. missing: %s" % (txd))
+ self.assertEqual(2, len(xids))
+
+
+ queues = ["queue-a1", "queue-a2", "queue-b1", "queue-b2", "queue-c1", "queue-c2", "queue-d1", "queue-d2"]
+ not_empty = ["queue-a2", "queue-b1"]
+
+ #re-check
+ not_empty = ["queue-a2", "queue-b1"]
+ for q in queues:
+ if q in not_empty:
+ self.assertEqual(1, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+ else:
+ self.assertEqual(0, session.queue_query(queue=q).message_count, "Bad count for %s" % (q))
+
+ #complete the prepared transactions
+ self.assertEqual(self.XA_OK, session.dtx_commit(xid=txc).status)
+ self.assertEqual(self.XA_OK, session.dtx_rollback(xid=txd).status)
+ not_empty.append("queue-c2")
+ not_empty.append("queue-d1")
+
+ for q in queues:
+ if q in not_empty:
+ self.assertEqual(1, session.queue_query(queue=q).message_count)
+ else:
+ self.assertEqual(0, session.queue_query(queue=q).message_count)
+
+ def phase7(self):
+ session = self.session
+ session.synchronous = False
+
+ # check xids from phase 6 are gone
+ txc = self.xid('c')
+ txd = self.xid('d')
+
+ xids = session.dtx_recover().in_doubt
+ ids = [x.global_id for x in xids] #TODO: come up with nicer way to test these
+
+ if txc.global_id in ids:
+ self.fail("Xid still present : %s" % (txc))
+ if txd.global_id in ids:
+ self.fail("Xid still present : %s" % (txc))
+ self.assertEqual(0, len(xids))
+
+ #test deletion of queue after publish
+ #create queue
+ session.queue_declare(queue = "q", auto_delete=True, durable=True)
+
+ #send message
+ for i in range(1, 10):
+ session.message_transfer(message=self.createMessage(routing_key = "q", body = "my-message"))
+
+ session.synchronous = True
+ #explicitly delete queue
+ session.queue_delete(queue = "q")
+
+ #test acking of message from auto-deleted queue
+ #create queue
+ session.queue_declare(queue = "q", auto_delete=True, durable=True)
+
+ #send message
+ session.message_transfer(message=self.createMessage(routing_key = "q", body = "my-message"))
+
+ #create consumer
+ session.message_subscribe(queue = "q", destination = "a", accept_mode=0, acquire_mode=0)
+ session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "a")
+ session.message_flow(unit = 0, value = 10, destination = "a")
+ queue = session.incoming("a")
+
+ #consume the message, cancel subscription (triggering auto-delete), then ack it
+ msg = queue.get(timeout = 5)
+ session.message_cancel(destination = "a")
+ self.ack(msg)
+
+ #test implicit deletion of bindings when queue is deleted
+ session.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True)
+ session.exchange_bind(exchange="amq.topic", queue="durable-subscriber-queue", binding_key="xyz")
+ session.message_transfer(destination= "amq.topic", message=self.createMessage(routing_key = "xyz", body = "my-message"))
+ session.queue_delete(queue = "durable-subscriber-queue")
+
+ #test unbind:
+ #create a series of bindings to a queue
+ session.queue_declare(queue = "binding-test-queue", durable=True)
+ session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="abc")
+ session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="pqr")
+ session.exchange_bind(exchange="amq.direct", queue="binding-test-queue", binding_key="xyz")
+ session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="a", arguments={"x-match":"all", "p":"a"})
+ session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="b", arguments={"x-match":"all", "p":"b"})
+ session.exchange_bind(exchange="amq.match", queue="binding-test-queue", binding_key="c", arguments={"x-match":"all", "p":"c"})
+ #then restart broker...
+
+
+ def phase8(self):
+ session = self.session
+
+ #continue testing unbind:
+ #send messages to the queue via each of the bindings
+ for k in ["abc", "pqr", "xyz"]:
+ data = "first %s" % (k)
+ session.message_transfer(destination= "amq.direct", message=self.createMessage(routing_key=k, body=data))
+ for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]:
+ data = "first %s" % (a["p"])
+ session.message_transfer(destination="amq.match", message=self.createMessage(application_headers=a, body=data))
+ #unbind some bindings (using final 0-10 semantics)
+ session.exchange_unbind(exchange="amq.direct", queue="binding-test-queue", binding_key="pqr")
+ session.exchange_unbind(exchange="amq.match", queue="binding-test-queue", binding_key="b")
+ #send messages again
+ for k in ["abc", "pqr", "xyz"]:
+ data = "second %s" % (k)
+ session.message_transfer(destination= "amq.direct", message=self.createMessage(routing_key=k, body=data))
+ for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]:
+ data = "second %s" % (a["p"])
+ session.message_transfer(destination="amq.match", message=self.createMessage(application_headers=a, body=data))
+
+ #check that only the correct messages are received
+ expected = []
+ for k in ["abc", "pqr", "xyz"]:
+ expected.append("first %s" % (k))
+ for a in [{"p":"a"}, {"p":"b"}, {"p":"c"}]:
+ expected.append("first %s" % (a["p"]))
+ for k in ["abc", "xyz"]:
+ expected.append("second %s" % (k))
+ for a in [{"p":"a"}, {"p":"c"}]:
+ expected.append("second %s" % (a["p"]))
+
+ session.message_subscribe(queue = "binding-test-queue", destination = "binding-test")
+ session.message_flow(unit = 1, value = 0xFFFFFFFF, destination = "binding-test")
+ session.message_flow(unit = 0, value = 10, destination = "binding-test")
+ queue = session.incoming("binding-test")
+
+ while len(expected):
+ msg = queue.get(timeout=1)
+ if msg.body not in expected:
+ self.fail("Missing message: %s" % msg.body)
+ expected.remove(msg.body)
+ try:
+ msg = queue.get(timeout=1)
+ self.fail("Got extra message: %s" % msg.body)
+ except Empty: pass
+
+
+
+ session.queue_declare(queue = "durable-subscriber-queue", exclusive=True, durable=True)
+ session.exchange_bind(exchange="amq.topic", queue="durable-subscriber-queue", binding_key="xyz")
+ session.message_transfer(destination= "amq.topic", message=self.createMessage(routing_key = "xyz", body = "my-message"))
+ session.queue_delete(queue = "durable-subscriber-queue")
+
+
+ def xid(self, txid, branchqual = ''):
+ return self.session.xid(format=0, global_id=txid, branch_id=branchqual)
+
+ def txswap(self, src, dest, tx):
+ self.assertEqual(self.XA_OK, self.session.dtx_start(xid=tx).status)
+ self.session.message_subscribe(destination="temp-swap", queue=src, accept_mode=0)
+ self.session.message_flow(destination="temp-swap", unit=0, value=1)
+ self.session.message_flow(destination="temp-swap", unit=1, value=0xFFFFFFFF)
+ msg = self.session.incoming("temp-swap").get(timeout=1)
+ self.session.message_cancel(destination="temp-swap")
+ self.session.message_transfer(message=self.createMessage(routing_key=dest, correlation_id=self.getProperty(msg, 'correlation_id'),
+ body=msg.body))
+ self.ack(msg)
+ self.assertEqual(self.XA_OK, self.session.dtx_end(xid=tx).status)
+
+ def assertEmptyQueue(self, name):
+ self.assertEqual(0, self.session.queue_query(queue=name).message_count)
+
+ def assertConnectionException(self, expectedCode, message):
+ self.assertEqual("connection", message.method.klass.name)
+ self.assertEqual("close", message.method.name)
+ self.assertEqual(expectedCode, message.reply_code)
+
+ def assertExpectedMethod(self, reply, klass, method):
+ self.assertEqual(klass, reply.method.klass.name)
+ self.assertEqual(method, reply.method.name)
+
+ def assertExpectedContent(self, msg, id, body):
+ self.assertEqual(id, self.getProperty(msg, 'correlation_id'))
+ self.assertEqual(body, msg.body)
+ return msg
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def ack(self, *msgs):
+ session = self.session
+ set = RangedSet()
+ for m in msgs:
+ set.add(m.id)
+ #TODO: tidy up completion
+ session.receiver._completed.add(m.id)
+ session.message_accept(set)
+ session.channel.session_completed(session.receiver._completed)
+
+ def assertExpectedGetResult(self, id, body):
+ return self.assertExpectedContent(session.incoming("incoming-gets").get(timeout=1), id, body)
+
+ def assertEqual(self, expected, actual, msg=''):
+ if expected != actual: raise Exception("%s expected: %s actual: %s" % (msg, expected, actual))
+
+ def assertMessageOnQueue(self, queue, id, body):
+ self.session.message_subscribe(destination="incoming-gets", queue=queue, accept_mode=0)
+ self.session.message_flow(destination="incoming-gets", unit=0, value=1)
+ self.session.message_flow(destination="incoming-gets", unit=1, value=0xFFFFFFFF)
+ msg = self.session.incoming("incoming-gets").get(timeout=1)
+ self.assertExpectedContent(msg, id, body)
+ self.ack(msg)
+ self.session.message_cancel(destination="incoming-gets")
+
+
+ def __init__(self):
+ TestBase010.__init__(self, "run")
+ self.setBroker("localhost")
+ self.errata = []
+
+ def connect(self):
+ """ Connects to the broker """
+ self.conn = Connection(connect(self.host, self.port))
+ self.conn.start(timeout=10)
+ self.session = self.conn.session("test-session", timeout=10)
+
+ def run(self, args=sys.argv[1:]):
+ try:
+ opts, extra = getopt(args, "r:s:e:b:p:h", ["retry=", "spec=", "errata=", "broker=", "phase=", "help"])
+ except GetoptError, e:
+ self._die(str(e))
+ phase = 0
+ retry = 0;
+ for opt, value in opts:
+ if opt in ("-h", "--help"): self._die()
+ if opt in ("-s", "--spec"): self.spec = value
+ if opt in ("-e", "--errata"): self.errata.append(value)
+ if opt in ("-b", "--broker"): self.setBroker(value)
+ if opt in ("-p", "--phase"): phase = int(value)
+ if opt in ("-r", "--retry"): retry = int(value)
+
+ if not phase: self._die("please specify the phase to run")
+ phase = "phase%d" % phase
+ self.connect()
+
+ try:
+ getattr(self, phase)()
+ print phase, "succeeded"
+ res = True;
+ except Exception, e:
+ print phase, "failed: ", e
+ traceback.print_exc()
+ res = False
+
+
+ if not self.session.error(): self.session.close(timeout=10)
+ self.conn.close(timeout=10)
+
+ # Crude fix to wait for thread in client to exit after return from session_close()
+ # Reduces occurrences of "Unhandled exception in thread" messages after each test
+ import time
+ time.sleep(1)
+
+ return res
+
+
+ def setBroker(self, broker):
+ rex = re.compile(r"""
+ # [ <user> [ / <password> ] @] <host> [ :<port> ]
+ ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X)
+ match = rex.match(broker)
+ if not match: self._die("'%s' is not a valid broker" % (broker))
+ self.user, self.password, self.host, self.port = match.groups()
+ self.port = int(default(self.port, 5672))
+ self.user = default(self.user, "guest")
+ self.password = default(self.password, "guest")
+
+ def _die(self, message = None):
+ if message: print message
+ print """
+Options:
+ -h/--help : this message
+ -s/--spec <spec.xml> : file containing amqp XML spec
+ -p/--phase : test phase to run
+ -b/--broker [<user>[/<password>]@]<host>[:<port>] : broker to connect to
+ """
+ sys.exit(1)
+
+def default(value, default):
+ if (value == None): return default
+ else: return value
+
+if __name__ == "__main__":
+ test = PersistenceTest()
+ if not test.run(): sys.exit(1)
diff --git a/qpid/cpp/src/tests/legacystore/python_tests/__init__.py b/qpid/cpp/src/tests/legacystore/python_tests/__init__.py
new file mode 100644
index 0000000000..ebb9da8670
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/python_tests/__init__.py
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Do not delete - marks this directory as a python package.
+
+from client_persistence import *
+from resize import *
+
diff --git a/qpid/cpp/src/tests/legacystore/python_tests/client_persistence.py b/qpid/cpp/src/tests/legacystore/python_tests/client_persistence.py
new file mode 100644
index 0000000000..37c12601be
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/python_tests/client_persistence.py
@@ -0,0 +1,239 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os
+
+from brokertest import EXPECT_EXIT_OK
+from store_test import StoreTest, Qmf, store_args
+from qpid.messaging import *
+
+import qpid.messaging, brokertest
+brokertest.qm = qpid.messaging # FIXME aconway 2014-04-04: Tests fail with SWIG client.
+
+class ExchangeQueueTests(StoreTest):
+ """
+ Simple tests of the broker exchange and queue types
+ """
+
+ def test_direct_exchange(self):
+ """Test Direct exchange."""
+ broker = self.broker(store_args(), name="test_direct_exchange", expect=EXPECT_EXIT_OK)
+ msg1 = Message("A_Message1", durable=True, correlation_id="Msg0001")
+ msg2 = Message("B_Message1", durable=True, correlation_id="Msg0002")
+ broker.send_message("a", msg1)
+ broker.send_message("b", msg2)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_direct_exchange")
+ self.check_message(broker, "a", msg1, True)
+ self.check_message(broker, "b", msg2, True)
+
+ def test_topic_exchange(self):
+ """Test Topic exchange."""
+ broker = self.broker(store_args(), name="test_topic_exchange", expect=EXPECT_EXIT_OK)
+ ssn = broker.connect().session()
+ snd1 = ssn.sender("abc/key1; {create:always, node:{type:topic, durable:True}}")
+ snd2 = ssn.sender("abc/key2; {create:always, node:{type:topic, durable:True}}")
+ ssn.receiver("a; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}")
+ ssn.receiver("b; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}")
+ ssn.receiver("c; {create:always, link:{x-bindings:[{exchange:abc, key:key1}, "
+ "{exchange:abc, key: key2}]}, node:{durable:True}}")
+ ssn.receiver("d; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}")
+ ssn.receiver("e; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}")
+ msg1 = Message("Message1", durable=True, correlation_id="Msg0003")
+ snd1.send(msg1)
+ msg2 = Message("Message2", durable=True, correlation_id="Msg0004")
+ snd2.send(msg2)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_topic_exchange")
+ self.check_message(broker, "a", msg1, True)
+ self.check_message(broker, "b", msg1, True)
+ self.check_messages(broker, "c", [msg1, msg2], True)
+ self.check_message(broker, "d", msg2, True)
+ self.check_message(broker, "e", msg2, True)
+
+
+ def test_legacy_lvq(self):
+ """Test legacy LVQ."""
+ broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK)
+ ma1 = Message("A1", durable=True, correlation_id="Msg0005", properties={"qpid.LVQ_key":"A"})
+ ma2 = Message("A2", durable=True, correlation_id="Msg0006", properties={"qpid.LVQ_key":"A"})
+ mb1 = Message("B1", durable=True, correlation_id="Msg0007", properties={"qpid.LVQ_key":"B"})
+ mb2 = Message("B2", durable=True, correlation_id="Msg0008", properties={"qpid.LVQ_key":"B"})
+ mb3 = Message("B3", durable=True, correlation_id="Msg0009", properties={"qpid.LVQ_key":"B"})
+ mc1 = Message("C1", durable=True, correlation_id="Msg0010", properties={"qpid.LVQ_key":"C"})
+ broker.send_messages("lvq-test", [mb1, ma1, ma2, mb2, mb3, mc1],
+ xprops="arguments:{\"qpid.last_value_queue\":True}")
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK)
+ ssn = self.check_messages(broker, "lvq-test", [ma2, mb3, mc1], empty=True, ack=False)
+ # Add more messages while subscriber is active (no replacement):
+ ma3 = Message("A3", durable=True, correlation_id="Msg0011", properties={"qpid.LVQ_key":"A"})
+ ma4 = Message("A4", durable=True, correlation_id="Msg0012", properties={"qpid.LVQ_key":"A"})
+ mc2 = Message("C2", durable=True, correlation_id="Msg0013", properties={"qpid.LVQ_key":"C"})
+ mc3 = Message("C3", durable=True, correlation_id="Msg0014", properties={"qpid.LVQ_key":"C"})
+ mc4 = Message("C4", durable=True, correlation_id="Msg0015", properties={"qpid.LVQ_key":"C"})
+ broker.send_messages("lvq-test", [mc2, mc3, ma3, ma4, mc4], session=ssn)
+ ssn.acknowledge()
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_lvq")
+ self.check_messages(broker, "lvq-test", [ma4, mc4], True)
+
+
+ def test_fanout_exchange(self):
+ """Test Fanout Exchange"""
+ broker = self.broker(store_args(), name="test_fanout_exchange", expect=EXPECT_EXIT_OK)
+ ssn = broker.connect().session()
+ snd = ssn.sender("TestFanoutExchange; {create: always, node: {type: topic, x-declare: {type: fanout}}}")
+ ssn.receiver("TestFanoutExchange; {link: {name: \"q1\", durable: True, reliability:at-least-once}}")
+ ssn.receiver("TestFanoutExchange; {link: {name: \"q2\", durable: True, reliability:at-least-once}}")
+ ssn.receiver("TestFanoutExchange; {link: {name: \"q3\", durable: True, reliability:at-least-once}}")
+ msg1 = Message("Msg1", durable=True, correlation_id="Msg0001")
+ snd.send(msg1)
+ msg2 = Message("Msg2", durable=True, correlation_id="Msg0002")
+ snd.send(msg2)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_fanout_exchange")
+ self.check_messages(broker, "q1", [msg1, msg2], True)
+ self.check_messages(broker, "q2", [msg1, msg2], True)
+ self.check_messages(broker, "q3", [msg1, msg2], True)
+
+
+ def test_message_reject(self):
+ broker = self.broker(store_args(), name="test_message_reject", expect=EXPECT_EXIT_OK)
+ ssn = broker.connect().session()
+ snd = ssn.sender("tmr; {create:always, node:{type:queue, durable:True}}")
+ rcv = ssn.receiver("tmr; {create:always, node:{type:queue, durable:True}}")
+ m1 = Message("test_message_reject", durable=True, correlation_id="Msg0001")
+ snd.send(m1)
+ m2 = rcv.fetch()
+ ssn.acknowledge(message=m2, disposition=Disposition(REJECTED))
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_message_reject")
+ qmf = Qmf(broker)
+ assert qmf.queue_message_count("tmr") == 0
+
+
+ def test_route(self):
+ """ Test the recovery of a route (link and bridge objects."""
+ broker = self.broker(store_args(), name="test_route", expect=EXPECT_EXIT_OK)
+ qmf = Qmf(broker)
+ qmf_broker_obj = qmf.get_objects("broker")[0]
+
+ # create a "link"
+ link_args = {"host":"a.fake.host.com", "port":9999, "durable":True,
+ "authMechanism":"PLAIN", "username":"guest", "password":"guest",
+ "transport":"tcp"}
+ result = qmf_broker_obj.create("link", "test-link", link_args, False)
+ self.assertEqual(result.status, 0, result)
+ link = qmf.get_objects("link")[0]
+
+ # create bridge
+ bridge_args = {"link":"test-link", "src":"amq.direct", "dest":"amq.fanout",
+ "key":"my-key", "durable":True}
+ result = qmf_broker_obj.create("bridge", "test-bridge", bridge_args, False);
+ self.assertEqual(result.status, 0, result)
+ bridge = qmf.get_objects("bridge")[0]
+
+ broker.terminate()
+
+ # recover the link and bridge
+ broker = self.broker(store_args(), name="test_route")
+ qmf = Qmf(broker)
+ qmf_broker_obj = qmf.get_objects("broker")[0]
+ self.assertEqual(len(qmf.get_objects("link")), 1)
+ self.assertEqual(len(qmf.get_objects("bridge")), 1)
+
+
+
+class AlternateExchangePropertyTests(StoreTest):
+ """
+ Test the persistence of the Alternate Exchange property for exchanges and queues.
+ """
+
+ def test_exchange(self):
+ """Exchange alternate exchange property persistence test"""
+ broker = self.broker(store_args(), name="test_exchange", expect=EXPECT_EXIT_OK)
+ qmf = Qmf(broker)
+ qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance
+ qmf.add_exchange("testExch", "direct", durable=True, alt_exchange_name="altExch")
+ qmf.close()
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_exchange")
+ qmf = Qmf(broker)
+ try:
+ qmf.add_exchange("altExch", "direct", passive=True)
+ except Exception, error:
+ self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error)
+ try:
+ qmf.add_exchange("testExch", "direct", passive=True)
+ except Exception, error:
+ self.fail("Test exchange (\"testExch\") instance not recovered: %s" % error)
+ self.assertTrue(qmf.query_exchange("testExch", alt_exchange_name = "altExch"),
+ "Alternate exchange property not found or is incorrect on exchange \"testExch\".")
+ qmf.close()
+
+ def test_queue(self):
+ """Queue alternate exchange property persistexchangeNamece test"""
+ broker = self.broker(store_args(), name="test_queue", expect=EXPECT_EXIT_OK)
+ qmf = Qmf(broker)
+ qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance
+ qmf.add_queue("testQueue", durable=True, alt_exchange_name="altExch")
+ qmf.close()
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_queue")
+ qmf = Qmf(broker)
+ try:
+ qmf.add_exchange("altExch", "direct", passive=True)
+ except Exception, error:
+ self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error)
+ try:
+ qmf.add_queue("testQueue", passive=True)
+ except Exception, error:
+ self.fail("Test queue (\"testQueue\") instance not recovered: %s" % error)
+ self.assertTrue(qmf.query_queue("testQueue", alt_exchange_name = "altExch"),
+ "Alternate exchange property not found or is incorrect on queue \"testQueue\".")
+ qmf.close()
+
+
+class RedeliveredTests(StoreTest):
+ """
+ Test the behavior of the redelivered flag in the context of persistence
+ """
+
+ def test_broker_recovery(self):
+ """Test that the redelivered flag is set on messages after recovery of broker"""
+ broker = self.broker(store_args(), name="test_broker_recovery", expect=EXPECT_EXIT_OK)
+ msg_content = "xyz"*100
+ msg = Message(msg_content, durable=True)
+ broker.send_message("testQueue", msg)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_broker_recovery")
+ rcv_msg = broker.get_message("testQueue")
+ self.assertEqual(msg_content, rcv_msg.content)
+ self.assertTrue(rcv_msg.redelivered)
+
diff --git a/qpid/cpp/src/tests/legacystore/python_tests/resize.py b/qpid/cpp/src/tests/legacystore/python_tests/resize.py
new file mode 100644
index 0000000000..e719b755da
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/python_tests/resize.py
@@ -0,0 +1,170 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import glob
+import os
+import subprocess
+
+from brokertest import EXPECT_EXIT_OK
+from qpid.datatypes import uuid4
+from store_test import StoreTest, store_args
+from qpid.messaging import Message
+
+import qpid.messaging, brokertest
+brokertest.qm = qpid.messaging # TODO aconway 2014-04-04: Tests fail with SWIG client.
+
+class ResizeTest(StoreTest):
+
+ resize_tool = os.getenv("QPID_STORE_RESIZE_TOOL", "qpid-store-resize")
+ print resize_tool
+ def _resize_store(self, store_dir, queue_name, resize_num_files, resize_file_size, exp_fail):
+ for f in glob.glob(os.path.join(store_dir, "*")):
+ final_store_dir = os.path.join(f, queue_name)
+ p = subprocess.Popen([self.resize_tool, final_store_dir, "--num-jfiles", str(resize_num_files),
+ "--jfile-size-pgs", str(resize_file_size), "--quiet"], stdout = subprocess.PIPE,
+ stderr = subprocess.STDOUT)
+ res = p.wait()
+ err_found = False
+ try:
+ for l in p.stdout:
+ if exp_fail:
+ err_found = True
+ print "[Expected error]:",
+ print l,
+ finally:
+ p.stdout.close()
+ return res
+
+ def _resize_test(self, queue_name, num_msgs, msg_size, resize_num_files, resize_file_size, init_num_files = 8,
+ init_file_size = 24, exp_fail = False, wait_time = None):
+ # Using a sender will force the creation of an empty persistent queue which is needed for some tests
+ broker = self.broker(store_args(), name="broker", expect=EXPECT_EXIT_OK, wait=wait_time)
+ ssn = broker.connect().session()
+ snd = ssn.sender("%s; {create:always, node:{durable:True}}" % queue_name)
+
+ msgs = []
+ for index in range(0, num_msgs):
+ msg = Message(self.make_message(index, msg_size), durable=True, id=uuid4(), correlation_id="msg-%04d"%index)
+ msgs.append(msg)
+ snd.send(msg)
+ broker.terminate()
+
+ res = self._resize_store(os.path.join(self.dir, "broker", "rhm", "jrnl"), queue_name, resize_num_files,
+ resize_file_size, exp_fail)
+ if res != 0:
+ if exp_fail:
+ return
+ self.fail("ERROR: Resize operation failed with return code %d" % res)
+ elif exp_fail:
+ self.fail("ERROR: Resize operation succeeded, but a failure was expected")
+
+ broker = self.broker(store_args(), name="broker")
+ self.check_messages(broker, queue_name, msgs, True)
+
+ # TODO: Check the physical files to check number and size are as expected.
+
+
+class SimpleTest(ResizeTest):
+ """
+ Simple tests of the resize utility for resizing a journal to larger and smaller sizes.
+ """
+
+ def test_empty_store_same(self):
+ self._resize_test(queue_name = "empty_store_same",
+ num_msgs = 0, msg_size = 0,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 8, resize_file_size = 24)
+
+ def test_empty_store_up(self):
+ self._resize_test(queue_name = "empty_store_up",
+ num_msgs = 0, msg_size = 0,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 16, resize_file_size = 48)
+
+ def test_empty_store_down(self):
+ self._resize_test(queue_name = "empty_store_down",
+ num_msgs = 0, msg_size = 0,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 6, resize_file_size = 12)
+
+# TODO: Put into long tests, make sure there is > 128GB free disk space
+# def test_empty_store_max(self):
+# self._resize_test(queue_name = "empty_store_max",
+# num_msgs = 0, msg_size = 0,
+# init_num_files = 8, init_file_size = 24,
+# resize_num_files = 64, resize_file_size = 32768,
+# wait_time = 120)
+
+ def test_empty_store_min(self):
+ self._resize_test(queue_name = "empty_store_min",
+ num_msgs = 0, msg_size = 0,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 4, resize_file_size = 1)
+
+ def test_basic_up(self):
+ self._resize_test(queue_name = "basic_up",
+ num_msgs = 100, msg_size = 10000,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 16, resize_file_size = 48)
+
+ def test_basic_down(self):
+ self._resize_test(queue_name = "basic_down",
+ num_msgs = 100, msg_size = 10000,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 4, resize_file_size = 15)
+
+ def test_basic_low(self):
+ self._resize_test(queue_name = "basic_low",
+ num_msgs = 100, msg_size = 10000,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 4, resize_file_size = 4,
+ exp_fail = True)
+
+ def test_basic_under(self):
+ self._resize_test(queue_name = "basic_under",
+ num_msgs = 100, msg_size = 10000,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 4, resize_file_size = 3,
+ exp_fail = True)
+
+ def test_very_large_msg_up(self):
+ self._resize_test(queue_name = "very_large_msg_up",
+ num_msgs = 4, msg_size = 2000000,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 16, resize_file_size = 48)
+
+ def test_very_large_msg_down(self):
+ self._resize_test(queue_name = "very_large_msg_down",
+ num_msgs = 4, msg_size = 2000000,
+ init_num_files = 16, init_file_size = 64,
+ resize_num_files = 16, resize_file_size = 48)
+
+ def test_very_large_msg_low(self):
+ self._resize_test(queue_name = "very_large_msg_low",
+ num_msgs = 4, msg_size = 2000000,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 7, resize_file_size = 20,
+ exp_fail = True)
+
+ def test_very_large_msg_under(self):
+ self._resize_test(queue_name = "very_large_msg_under",
+ num_msgs = 4, msg_size = 2000000,
+ init_num_files = 8, init_file_size = 24,
+ resize_num_files = 6, resize_file_size = 8,
+ exp_fail = True)
diff --git a/qpid/cpp/src/tests/legacystore/python_tests/store_test.py b/qpid/cpp/src/tests/legacystore/python_tests/store_test.py
new file mode 100644
index 0000000000..cc846aefd4
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/python_tests/store_test.py
@@ -0,0 +1,417 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 re
+from brokertest import BrokerTest
+from qpid.messaging import Empty
+from qmf.console import Session
+
+import qpid.messaging, brokertest
+brokertest.qm = qpid.messaging # TODO aconway 2014-04-04: Tests fail with SWIG client.
+
+
+def store_args(store_dir = None):
+ """Return the broker args necessary to load the async store"""
+ assert BrokerTest.store_lib
+ if store_dir == None:
+ return []
+ return ["--store-dir", store_dir]
+
+class Qmf:
+ """
+ QMF functions not yet available in the new QMF API. Remove this and replace with new API when it becomes available.
+ """
+ def __init__(self, broker):
+ self.__session = Session()
+ self.__broker = self.__session.addBroker("amqp://localhost:%d"%broker.port())
+
+ def add_exchange(self, exchange_name, exchange_type, alt_exchange_name=None, passive=False, durable=False,
+ arguments = None):
+ """Add a new exchange"""
+ amqp_session = self.__broker.getAmqpSession()
+ if arguments == None:
+ arguments = {}
+ if alt_exchange_name:
+ amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type,
+ alternate_exchange=alt_exchange_name, passive=passive, durable=durable,
+ arguments=arguments)
+ else:
+ amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type, passive=passive, durable=durable,
+ arguments=arguments)
+
+ def add_queue(self, queue_name, alt_exchange_name=None, passive=False, durable=False, arguments = None):
+ """Add a new queue"""
+ amqp_session = self.__broker.getAmqpSession()
+ if arguments == None:
+ arguments = {}
+ if alt_exchange_name:
+ amqp_session.queue_declare(queue_name, alternate_exchange=alt_exchange_name, passive=passive,
+ durable=durable, arguments=arguments)
+ else:
+ amqp_session.queue_declare(queue_name, passive=passive, durable=durable, arguments=arguments)
+
+ def delete_queue(self, queue_name):
+ """Delete an existing queue"""
+ amqp_session = self.__broker.getAmqpSession()
+ amqp_session.queue_delete(queue_name)
+
+ def _query(self, name, _class, package, alt_exchange_name=None):
+ """Qmf query function which can optionally look for the presence of an alternate exchange name"""
+ try:
+ obj_list = self.__session.getObjects(_class=_class, _package=package)
+ found = False
+ for obj in obj_list:
+ if obj.name == name:
+ found = True
+ if alt_exchange_name != None:
+ alt_exch_list = self.__session.getObjects(_objectId=obj.altExchange)
+ if len(alt_exch_list) == 0 or alt_exch_list[0].name != alt_exchange_name:
+ return False
+ break
+ return found
+ except Exception:
+ return False
+
+
+ def query_exchange(self, exchange_name, alt_exchange_name=None):
+ """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known
+ value."""
+ return self._query(exchange_name, "exchange", "org.apache.qpid.broker", alt_exchange_name)
+
+ def query_queue(self, queue_name, alt_exchange_name=None):
+ """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known
+ value."""
+ return self._query(queue_name, "queue", "org.apache.qpid.broker", alt_exchange_name)
+
+ def queue_message_count(self, queue_name):
+ """Query the number of messages on a queue"""
+ queue_list = self.__session.getObjects(_class="queue", _name=queue_name)
+ if len(queue_list):
+ return queue_list[0].msgDepth
+
+ def queue_empty(self, queue_name):
+ """Check if a queue is empty (has no messages waiting)"""
+ return self.queue_message_count(queue_name) == 0
+
+ def get_objects(self, target_class, target_package="org.apache.qpid.broker"):
+ return self.__session.getObjects(_class=target_class, _package=target_package)
+
+
+ def close(self):
+ self.__session.delBroker(self.__broker)
+ self.__session = None
+
+
+class StoreTest(BrokerTest):
+ """
+ This subclass of BrokerTest adds some convenience test/check functions
+ """
+
+ def _chk_empty(self, queue, receiver):
+ """Check if a queue is empty (has no more messages)"""
+ try:
+ msg = receiver.fetch(timeout=0)
+ self.assert_(False, "Queue \"%s\" not empty: found message: %s" % (queue, msg))
+ except Empty:
+ pass
+
+ @staticmethod
+ def make_message(msg_count, msg_size):
+ """Make message content. Format: 'abcdef....' followed by 'msg-NNNN', where NNNN is the message count"""
+ msg = "msg-%04d" % msg_count
+ msg_len = len(msg)
+ buff = ""
+ if msg_size != None and msg_size > msg_len:
+ for index in range(0, msg_size - msg_len):
+ if index == msg_size - msg_len - 1:
+ buff += "-"
+ else:
+ buff += chr(ord('a') + (index % 26))
+ return buff + msg
+
+ # Functions for formatting address strings
+
+ @staticmethod
+ def _fmt_csv(string_list, list_braces = None):
+ """Format a list using comma-separation. Braces are optionally added."""
+ if len(string_list) == 0:
+ return ""
+ first = True
+ str_ = ""
+ if list_braces != None:
+ str_ += list_braces[0]
+ for string in string_list:
+ if string != None:
+ if first:
+ first = False
+ else:
+ str_ += ", "
+ str_ += string
+ if list_braces != None:
+ str_ += list_braces[1]
+ return str_
+
+ def _fmt_map(self, string_list):
+ """Format a map {l1, l2, l3, ...} from a string list. Each item in the list must be a formatted map
+ element('key:val')."""
+ return self._fmt_csv(string_list, list_braces="{}")
+
+ def _fmt_list(self, string_list):
+ """Format a list [l1, l2, l3, ...] from a string list."""
+ return self._fmt_csv(string_list, list_braces="[]")
+
+ def addr_fmt(self, node_name, **kwargs):
+ """Generic AMQP to new address formatter. Takes common (but not all) AMQP options and formats an address
+ string."""
+ # Get keyword args
+ node_subject = kwargs.get("node_subject")
+ create_policy = kwargs.get("create_policy")
+ delete_policy = kwargs.get("delete_policy")
+ assert_policy = kwargs.get("assert_policy")
+ mode = kwargs.get("mode")
+ link = kwargs.get("link", False)
+ link_name = kwargs.get("link_name")
+ node_type = kwargs.get("node_type")
+ durable = kwargs.get("durable", False)
+ link_reliability = kwargs.get("link_reliability")
+ x_declare_list = kwargs.get("x_declare_list", [])
+ x_bindings_list = kwargs.get("x_bindings_list", [])
+ x_subscribe_list = kwargs.get("x_subscribe_list", [])
+
+ node_flag = not link and (node_type != None or durable or len(x_declare_list) > 0 or len(x_bindings_list) > 0)
+ link_flag = link and (link_name != None or durable or link_reliability != None or len(x_declare_list) > 0 or
+ len(x_bindings_list) > 0 or len(x_subscribe_list) > 0)
+ assert not (node_flag and link_flag)
+
+ opt_str_list = []
+ if create_policy != None:
+ opt_str_list.append("create: %s" % create_policy)
+ if delete_policy != None:
+ opt_str_list.append("delete: %s" % delete_policy)
+ if assert_policy != None:
+ opt_str_list.append("assert: %s" % assert_policy)
+ if mode != None:
+ opt_str_list.append("mode: %s" % mode)
+ if node_flag or link_flag:
+ node_str_list = []
+ if link_name != None:
+ node_str_list.append("name: \"%s\"" % link_name)
+ if node_type != None:
+ node_str_list.append("type: %s" % node_type)
+ if durable:
+ node_str_list.append("durable: True")
+ if link_reliability != None:
+ node_str_list.append("reliability: %s" % link_reliability)
+ if len(x_declare_list) > 0:
+ node_str_list.append("x-declare: %s" % self._fmt_map(x_declare_list))
+ if len(x_bindings_list) > 0:
+ node_str_list.append("x-bindings: %s" % self._fmt_list(x_bindings_list))
+ if len(x_subscribe_list) > 0:
+ node_str_list.append("x-subscribe: %s" % self._fmt_map(x_subscribe_list))
+ if node_flag:
+ opt_str_list.append("node: %s" % self._fmt_map(node_str_list))
+ else:
+ opt_str_list.append("link: %s" % self._fmt_map(node_str_list))
+ addr_str = node_name
+ if node_subject != None:
+ addr_str += "/%s" % node_subject
+ if len(opt_str_list) > 0:
+ addr_str += "; %s" % self._fmt_map(opt_str_list)
+ return addr_str
+
+ def snd_addr(self, node_name, **kwargs):
+ """ Create a send (node) address"""
+ # Get keyword args
+ topic = kwargs.get("topic")
+ topic_flag = kwargs.get("topic_flag", False)
+ auto_create = kwargs.get("auto_create", True)
+ auto_delete = kwargs.get("auto_delete", False)
+ durable = kwargs.get("durable", False)
+ exclusive = kwargs.get("exclusive", False)
+ ftd_count = kwargs.get("ftd_count")
+ ftd_size = kwargs.get("ftd_size")
+ policy = kwargs.get("policy", "flow-to-disk")
+ exchage_type = kwargs.get("exchage_type")
+
+ create_policy = None
+ if auto_create:
+ create_policy = "always"
+ delete_policy = None
+ if auto_delete:
+ delete_policy = "always"
+ node_type = None
+ if topic != None or topic_flag:
+ node_type = "topic"
+ x_declare_list = ["\"exclusive\": %s" % exclusive]
+ if ftd_count != None or ftd_size != None:
+ queue_policy = ["\'qpid.policy_type\': %s" % policy]
+ if ftd_count:
+ queue_policy.append("\'qpid.max_count\': %d" % ftd_count)
+ if ftd_size:
+ queue_policy.append("\'qpid.max_size\': %d" % ftd_size)
+ x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy))
+ if exchage_type != None:
+ x_declare_list.append("type: %s" % exchage_type)
+
+ return self.addr_fmt(node_name, topic=topic, create_policy=create_policy, delete_policy=delete_policy,
+ node_type=node_type, durable=durable, x_declare_list=x_declare_list)
+
+ def rcv_addr(self, node_name, **kwargs):
+ """ Create a receive (link) address"""
+ # Get keyword args
+ auto_create = kwargs.get("auto_create", True)
+ auto_delete = kwargs.get("auto_delete", False)
+ link_name = kwargs.get("link_name")
+ durable = kwargs.get("durable", False)
+ browse = kwargs.get("browse", False)
+ exclusive = kwargs.get("exclusive", False)
+ binding_list = kwargs.get("binding_list", [])
+ ftd_count = kwargs.get("ftd_count")
+ ftd_size = kwargs.get("ftd_size")
+ policy = kwargs.get("policy", "flow-to-disk")
+
+ create_policy = None
+ if auto_create:
+ create_policy = "always"
+ delete_policy = None
+ if auto_delete:
+ delete_policy = "always"
+ mode = None
+ if browse:
+ mode = "browse"
+ x_declare_list = ["\"exclusive\": %s" % exclusive]
+ if ftd_count != None or ftd_size != None:
+ queue_policy = ["\'qpid.policy_type\': %s" % policy]
+ if ftd_count:
+ queue_policy.append("\'qpid.max_count\': %d" % ftd_count)
+ if ftd_size:
+ queue_policy.append("\'qpid.max_size\': %d" % ftd_size)
+ x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy))
+ x_bindings_list = []
+ for binding in binding_list:
+ x_bindings_list.append("{exchange: %s, key: %s}" % binding)
+ if durable: reliability = 'at-least-once'
+ else: reliability = None
+ return self.addr_fmt(node_name, create_policy=create_policy, delete_policy=delete_policy, mode=mode, link=True,
+ link_name=link_name, durable=durable, x_declare_list=x_declare_list,
+ x_bindings_list=x_bindings_list, link_reliability=reliability)
+
+ def check_message(self, broker, queue, exp_msg, transactional=False, empty=False, ack=True, browse=False):
+ """Check that a message is on a queue by dequeuing it and comparing it to the expected message"""
+ return self.check_messages(broker, queue, [exp_msg], transactional, empty, ack, browse)
+
+ def check_messages(self, broker, queue, exp_msg_list, transactional=False, empty=False, ack=True, browse=False,
+ emtpy_flag=False):
+ """Check that messages is on a queue by dequeuing them and comparing them to the expected messages"""
+ if emtpy_flag:
+ num_msgs = 0
+ else:
+ num_msgs = len(exp_msg_list)
+ ssn = broker.connect().session(transactional=transactional)
+ rcvr = ssn.receiver(self.rcv_addr(queue, browse=browse), capacity=num_msgs)
+ if num_msgs > 0:
+ try:
+ recieved_msg_list = [rcvr.fetch(timeout=0) for i in range(num_msgs)]
+ except Empty:
+ self.assert_(False, "Queue \"%s\" is empty, unable to retrieve expected message %d." % (queue, i))
+ for i in range(0, len(recieved_msg_list)):
+ self.assertEqual(recieved_msg_list[i].content, exp_msg_list[i].content)
+ self.assertEqual(recieved_msg_list[i].correlation_id, exp_msg_list[i].correlation_id)
+ if empty:
+ self._chk_empty(queue, rcvr)
+ if ack:
+ ssn.acknowledge()
+ if transactional:
+ ssn.commit()
+ ssn.connection.close()
+ else:
+ if transactional:
+ ssn.commit()
+ return ssn
+
+
+ # Functions for finding strings in the broker log file (or other files)
+
+ @staticmethod
+ def _read_file(file_name):
+ """Returns the content of file named file_name as a string"""
+ file_handle = file(file_name)
+ try:
+ return file_handle.read()
+ finally:
+ file_handle.close()
+
+ def _get_hits(self, broker, search):
+ """Find all occurrences of the search in the broker log (eliminating possible duplicates from msgs on multiple
+ queues)"""
+ # TODO: Use sets when RHEL-4 is no longer supported
+ hits = []
+ for hit in search.findall(self._read_file(broker.log)):
+ if hit not in hits:
+ hits.append(hit)
+ return hits
+
+ def _reconsile_hits(self, broker, ftd_msgs, release_hits):
+ """Remove entries from list release_hits if they match the message id in ftd_msgs. Check for remaining
+ release_hits."""
+ for msg in ftd_msgs:
+ found = False
+ for hit in release_hits:
+ if str(msg.id) in hit:
+ release_hits.remove(hit)
+ #print "Found %s in %s" % (msg.id, broker.log)
+ found = True
+ break
+ if not found:
+ self.assert_(False, "Unable to locate released message %s in log %s" % (msg.id, broker.log))
+ if len(release_hits) > 0:
+ err = "Messages were unexpectedly released in log %s:\n" % broker.log
+ for hit in release_hits:
+ err += " %s\n" % hit
+ self.assert_(False, err)
+
+ def check_msg_release(self, broker, ftd_msgs):
+ """ Check for 'Content released' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content released$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_release_on_commit(self, broker, ftd_msgs):
+ """ Check for 'Content released on commit' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content released on commit$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_release_on_recover(self, broker, ftd_msgs):
+ """ Check for 'Content released after recovery' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content released after recovery$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_block(self, broker, ftd_msgs):
+ """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content release blocked$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_block_on_commit(self, broker, ftd_msgs):
+ """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content release blocked on commit$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
diff --git a/qpid/cpp/src/tests/legacystore/run_long_python_tests b/qpid/cpp/src/tests/legacystore/run_long_python_tests
new file mode 100644
index 0000000000..be6380302c
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/run_long_python_tests
@@ -0,0 +1,21 @@
+#!/usr/bin/env 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_python_tests LONG_TEST
diff --git a/qpid/cpp/src/tests/legacystore/run_python_tests b/qpid/cpp/src/tests/legacystore/run_python_tests
new file mode 100755
index 0000000000..c1d04a28a1
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/run_python_tests
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+source $QPID_TEST_COMMON
+
+ensure_python_tests
+
+#Add our directory to the python path
+export PYTHONPATH=$srcdir/legacystore:$PYTHONPATH
+
+MODULENAME=python_tests
+
+echo "Running Python tests in module ${MODULENAME}..."
+
+QPID_PORT=${QPID_PORT:-5672}
+FAILING=${FAILING:-/dev/null}
+PYTHON_TESTS=${PYTHON_TESTS:-$*}
+
+OUTDIR=${MODULENAME}.tmp
+rm -rf $OUTDIR
+
+# To debug a test, add the following options to the end of the following line:
+# -v DEBUG -c qpid.messaging.io.ops [*.testName]
+${QPID_PYTHON_TEST} -m ${MODULENAME} -I $FAILING -DOUTDIR=$OUTDIR \
+ $PYTHON_TEST || exit 1
+
diff --git a/qpid/cpp/src/tests/legacystore/run_short_python_tests b/qpid/cpp/src/tests/legacystore/run_short_python_tests
new file mode 100644
index 0000000000..9b9e7c59be
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/run_short_python_tests
@@ -0,0 +1,21 @@
+#!/usr/bin/env 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_python_tests SHORT_TEST
diff --git a/qpid/cpp/src/tests/legacystore/system_test.sh b/qpid/cpp/src/tests/legacystore/system_test.sh
new file mode 100644
index 0000000000..52cecbce8a
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/system_test.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env 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.
+#
+
+error() { echo $*; exit 1; }
+
+# Make sure $QPID_DIR contains what we need.
+if ! test -d "$QPID_DIR" ; then
+ echo "WARNING: QPID_DIR is not set skipping system tests."
+ exit
+fi
+STORE_LIB=../lib/.libs/msgstore.so
+
+xml_spec=$QPID_DIR/specs/amqp.0-10-qpid-errata.stripped.xml
+test -f $xml_spec || error "$xml_spec not found: invalid \$QPID_DIR ?"
+export PYTHONPATH=$QPID_DIR/python:$QPID_DIR/extras/qmf/src/py:$QPID_DIR/tools/src/py
+
+echo "Using directory $TMP_DATA_DIR"
+
+fail=0
+
+# Run the tests with a given set of flags
+BROKER_OPTS="--no-module-dir --load-module=$STORE_LIB --data-dir=$TMP_DATA_DIR --auth=no --wcache-page-size 16"
+run_tests() {
+ for p in `seq 1 8`; do
+ $abs_srcdir/start_broker "$@" ${BROKER_OPTS} || { echo "FAIL broker start"; return 1; }
+ python "$abs_srcdir/persistence.py" -s "$xml_spec" -b localhost:`cat qpidd.port` -p $p -r 3 || fail=1;
+ $abs_srcdir/stop_broker
+ done
+}
+
+run_tests || fail=1
+
+exit $fail
diff --git a/qpid/cpp/src/tests/legacystore/unit_test.cpp b/qpid/cpp/src/tests/legacystore/unit_test.cpp
new file mode 100644
index 0000000000..add80a6f91
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/unit_test.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.
+ *
+ */
+
+
+// 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/qpid/cpp/src/tests/legacystore/unit_test.h b/qpid/cpp/src/tests/legacystore/unit_test.h
new file mode 100644
index 0000000000..16b6ae2ffb
--- /dev/null
+++ b/qpid/cpp/src/tests/legacystore/unit_test.h
@@ -0,0 +1,69 @@
+#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.32, 1.33 and boost 1.34.
+// Remove when we no longer need to support 1.32 or 1.33.
+
+#include <boost/version.hpp>
+
+#if (BOOST_VERSION < 103400) // v.1.33 and earlier
+# include <boost/test/auto_unit_test.hpp>
+#else // v.1.34 and later
+# include <boost/test/unit_test.hpp>
+#endif
+
+// Keep the test function for compilation but do not not register it.
+// TODO aconway 2008-04-23: better workaround for expected failures.
+// The following causes the test testUpdateTxState not to run at all.
+# define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \
+ namespace { struct test_name { void test_method(); }; } \
+ void test_name::test_method()
+// The following runs the test testUpdateTxState, but it fails.
+/*#define QPID_AUTO_TEST_CASE_EXPECTED_FAILURES(test_name,n) \
+ namespace { struct test_name { void test_method(); }; } \
+ BOOST_AUTO_TEST_CASE(name)*/
+
+#if (BOOST_VERSION < 103300) // v.1.32 and earlier
+
+# 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) // v.1.33
+
+// Note the trailing ';'
+# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name);
+# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name)
+# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END();
+
+#else // v.1.34 and later
+
+# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name)
+# define QPID_AUTO_TEST_CASE(name) BOOST_AUTO_TEST_CASE(name)
+# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
+
+#endif
+
+#endif /*!QPIPD_TEST_UNIT_TEST_H_*/
diff --git a/qpid/cpp/src/tests/linearstore/CMakeLists.txt b/qpid/cpp/src/tests/linearstore/CMakeLists.txt
new file mode 100644
index 0000000000..bf6c164818
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/CMakeLists.txt
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if(BUILD_LINEARSTORE AND BUILD_TESTING)
+
+message(STATUS "Building linearstore tests")
+
+set(test_wrap ${shell} ${CMAKE_SOURCE_DIR}/src/tests/run_test${test_script_suffix} -buildDir=${CMAKE_BINARY_DIR})
+
+add_test (linearstore_python_tests ${test_wrap} -- ${CMAKE_CURRENT_SOURCE_DIR}/run_python_tests${test_script_suffix})
+
+endif (BUILD_LINEARSTORE AND BUILD_TESTING)
+
diff --git a/qpid/cpp/src/tests/linearstore/linearstoredirsetup.sh b/qpid/cpp/src/tests/linearstore/linearstoredirsetup.sh
new file mode 100755
index 0000000000..ef39767e9b
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/linearstoredirsetup.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env 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.
+#
+
+# This script sets up a test directory which contains both
+# recoverable and non-recoverable files and directories for
+# the empty file pool (EFP).
+
+# NOTE: The following is based on typical development tree paths, not installed paths
+
+BASE_DIR=${HOME}/RedHat
+STORE_DIR=${BASE_DIR}
+PYTHON_TOOLS_DIR=${BASE_DIR}/qpid/tools/src/linearstore
+export PYTHONPATH=${BASE_DIR}/qpid/python:${BASE_DIR}/qpid/extras/qmf/src/py:${BASE_DIR}/qpid/tools/src/py
+
+# Remove old dirs (if present)
+rm -rf ${STORE_DIR}/qls
+rm -rf ${STORE_DIR}/p002
+rm ${STORE_DIR}/p004
+
+# Create new dir tree and links
+mkdir ${STORE_DIR}/p002_ext
+touch ${STORE_DIR}/p004_ext
+mkdir ${STORE_DIR}/qls
+mkdir ${STORE_DIR}/qls/p001
+touch ${STORE_DIR}/qls/p003
+ln -s ${STORE_DIR}/p002_ext ${STORE_DIR}/qls/p002
+ln -s ${STORE_DIR}/p004_ext ${STORE_DIR}/qls/p004
+
+# Populate efp dirs with empty files
+${PYTHON_TOOLS_DIR}/efptool.py $STORE_DIR/qls/ -a -p 1 -s 2048 -n 25
+${PYTHON_TOOLS_DIR}/efptool.py $STORE_DIR/qls/ -a -p 1 -s 512 -n 25
+${PYTHON_TOOLS_DIR}/efptool.py $STORE_DIR/qls/ -a -p 2 -s 2048 -n 25
+
+# Show the result for information
+${LINEARSTOREDIR}/tools/src/py/linearstore/efptool.py $STORE_DIR/qls/ -l
+tree -la $STORE_DIR/qls
+
diff --git a/qpid/cpp/src/tests/linearstore/python_tests/__init__.py b/qpid/cpp/src/tests/linearstore/python_tests/__init__.py
new file mode 100644
index 0000000000..1e59d403e4
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/python_tests/__init__.py
@@ -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.
+#
+
+# Do not delete - marks this directory as a python package.
+
+from client_persistence import *
+
diff --git a/qpid/cpp/src/tests/linearstore/python_tests/client_persistence.py b/qpid/cpp/src/tests/linearstore/python_tests/client_persistence.py
new file mode 100644
index 0000000000..9ff9480c4c
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/python_tests/client_persistence.py
@@ -0,0 +1,239 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os
+
+from brokertest import EXPECT_EXIT_OK
+from store_test import StoreTest, Qmf, store_args
+from qpid.messaging import *
+
+import qpid.messaging, brokertest
+brokertest.qm = qpid.messaging # FIXME aconway 2014-04-04: Tests fail with SWIG client.
+
+class ExchangeQueueTests(StoreTest):
+ """
+ Simple tests of the broker exchange and queue types
+ """
+
+ def test_direct_exchange(self):
+ """Test Direct exchange."""
+ broker = self.broker(store_args(), name="test_direct_exchange", expect=EXPECT_EXIT_OK)
+ msg1 = Message("A_Message1", durable=True, correlation_id="Msg0001")
+ msg2 = Message("B_Message1", durable=True, correlation_id="Msg0002")
+ broker.send_message("a", msg1)
+ broker.send_message("b", msg2)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_direct_exchange")
+ self.check_message(broker, "a", msg1, True)
+ self.check_message(broker, "b", msg2, True)
+
+ def test_topic_exchange(self):
+ """Test Topic exchange."""
+ broker = self.broker(store_args(), name="test_topic_exchange", expect=EXPECT_EXIT_OK)
+ ssn = broker.connect().session()
+ snd1 = ssn.sender("abc/key1; {create:always, node:{type:topic, durable:True}}")
+ snd2 = ssn.sender("abc/key2; {create:always, node:{type:topic, durable:True}}")
+ ssn.receiver("a; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}")
+ ssn.receiver("b; {create:always, link:{x-bindings:[{exchange:abc, key:key1}]}, node:{durable:True}}")
+ ssn.receiver("c; {create:always, link:{x-bindings:[{exchange:abc, key:key1}, "
+ "{exchange:abc, key: key2}]}, node:{durable:True}}")
+ ssn.receiver("d; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}")
+ ssn.receiver("e; {create:always, link:{x-bindings:[{exchange:abc, key:key2}]}, node:{durable:True}}")
+ msg1 = Message("Message1", durable=True, correlation_id="Msg0003")
+ snd1.send(msg1)
+ msg2 = Message("Message2", durable=True, correlation_id="Msg0004")
+ snd2.send(msg2)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_topic_exchange")
+ self.check_message(broker, "a", msg1, True)
+ self.check_message(broker, "b", msg1, True)
+ self.check_messages(broker, "c", [msg1, msg2], True)
+ self.check_message(broker, "d", msg2, True)
+ self.check_message(broker, "e", msg2, True)
+
+
+ def test_legacy_lvq(self):
+ """Test legacy LVQ."""
+ broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK)
+ ma1 = Message("A1", durable=True, correlation_id="Msg0005", properties={"qpid.LVQ_key":"A"})
+ ma2 = Message("A2", durable=True, correlation_id="Msg0006", properties={"qpid.LVQ_key":"A"})
+ mb1 = Message("B1", durable=True, correlation_id="Msg0007", properties={"qpid.LVQ_key":"B"})
+ mb2 = Message("B2", durable=True, correlation_id="Msg0008", properties={"qpid.LVQ_key":"B"})
+ mb3 = Message("B3", durable=True, correlation_id="Msg0009", properties={"qpid.LVQ_key":"B"})
+ mc1 = Message("C1", durable=True, correlation_id="Msg0010", properties={"qpid.LVQ_key":"C"})
+ broker.send_messages("lvq-test", [mb1, ma1, ma2, mb2, mb3, mc1],
+ xprops="arguments:{\"qpid.last_value_queue\":True}")
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_lvq", expect=EXPECT_EXIT_OK)
+ ssn = self.check_messages(broker, "lvq-test", [ma2, mb3, mc1], empty=True, ack=False)
+ # Add more messages while subscriber is active (no replacement):
+ ma3 = Message("A3", durable=True, correlation_id="Msg0011", properties={"qpid.LVQ_key":"A"})
+ ma4 = Message("A4", durable=True, correlation_id="Msg0012", properties={"qpid.LVQ_key":"A"})
+ mc2 = Message("C2", durable=True, correlation_id="Msg0013", properties={"qpid.LVQ_key":"C"})
+ mc3 = Message("C3", durable=True, correlation_id="Msg0014", properties={"qpid.LVQ_key":"C"})
+ mc4 = Message("C4", durable=True, correlation_id="Msg0015", properties={"qpid.LVQ_key":"C"})
+ broker.send_messages("lvq-test", [mc2, mc3, ma3, ma4, mc4], session=ssn)
+ ssn.acknowledge()
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_lvq")
+ self.check_messages(broker, "lvq-test", [ma4, mc4], True)
+
+
+ def test_fanout_exchange(self):
+ """Test Fanout Exchange"""
+ broker = self.broker(store_args(), name="test_fanout_exchange", expect=EXPECT_EXIT_OK)
+ ssn = broker.connect().session()
+ snd = ssn.sender("TestFanoutExchange; {create: always, node: {type: topic, x-declare: {type: fanout}}}")
+ ssn.receiver("TestFanoutExchange; {link: {name: \"q1\", durable: True, reliability:at-least-once}}")
+ ssn.receiver("TestFanoutExchange; {link: {name: \"q2\", durable: True, reliability:at-least-once}}")
+ ssn.receiver("TestFanoutExchange; {link: {name: \"q3\", durable: True, reliability:at-least-once}}")
+ msg1 = Message("Msg1", durable=True, correlation_id="Msg0001")
+ snd.send(msg1)
+ msg2 = Message("Msg2", durable=True, correlation_id="Msg0002")
+ snd.send(msg2)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_fanout_exchange")
+ self.check_messages(broker, "q1", [msg1, msg2], True)
+ self.check_messages(broker, "q2", [msg1, msg2], True)
+ self.check_messages(broker, "q3", [msg1, msg2], True)
+
+
+ def test_message_reject(self):
+ broker = self.broker(store_args(), name="test_message_reject", expect=EXPECT_EXIT_OK)
+ ssn = broker.connect().session()
+ snd = ssn.sender("tmr; {create:always, node:{type:queue, durable:True}}")
+ rcv = ssn.receiver("tmr; {create:always, node:{type:queue, durable:True}}")
+ m1 = Message("test_message_reject", durable=True, correlation_id="Msg0001")
+ snd.send(m1)
+ m2 = rcv.fetch()
+ ssn.acknowledge(message=m2, disposition=Disposition(REJECTED))
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_message_reject")
+ qmf = Qmf(broker)
+ assert qmf.queue_message_count("tmr") == 0
+
+
+ def test_route(self):
+ """ Test the recovery of a route (link and bridge objects."""
+ broker = self.broker(store_args(), name="test_route", expect=EXPECT_EXIT_OK)
+ qmf = Qmf(broker)
+ qmf_broker_obj = qmf.get_objects("broker")[0]
+
+ # create a "link"
+ link_args = {"host":"a.fake.host.com", "port":9999, "durable":True,
+ "authMechanism":"PLAIN", "username":"guest", "password":"guest",
+ "transport":"tcp"}
+ result = qmf_broker_obj.create("link", "test-link", link_args, False)
+ self.assertEqual(result.status, 0, result)
+ link = qmf.get_objects("link")[0]
+
+ # create bridge
+ bridge_args = {"link":"test-link", "src":"amq.direct", "dest":"amq.fanout",
+ "key":"my-key", "durable":True}
+ result = qmf_broker_obj.create("bridge", "test-bridge", bridge_args, False);
+ self.assertEqual(result.status, 0, result)
+ bridge = qmf.get_objects("bridge")[0]
+
+ broker.terminate()
+
+ # recover the link and bridge
+ broker = self.broker(store_args(), name="test_route")
+ qmf = Qmf(broker)
+ qmf_broker_obj = qmf.get_objects("broker")[0]
+ self.assertEqual(len(qmf.get_objects("link")), 1)
+ self.assertEqual(len(qmf.get_objects("bridge")), 1)
+
+
+
+class AlternateExchangePropertyTests(StoreTest):
+ """
+ Test the persistence of the Alternate Exchange property for exchanges and queues.
+ """
+
+ def test_exchange(self):
+ """Exchange alternate exchange property persistence test"""
+ broker = self.broker(store_args(), name="test_exchange", expect=EXPECT_EXIT_OK)
+ qmf = Qmf(broker)
+ qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance
+ qmf.add_exchange("testExch", "direct", durable=True, alt_exchange_name="altExch")
+ qmf.close()
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_exchange")
+ qmf = Qmf(broker)
+ try:
+ qmf.add_exchange("altExch", "direct", passive=True)
+ except Exception, error:
+ self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error)
+ try:
+ qmf.add_exchange("testExch", "direct", passive=True)
+ except Exception, error:
+ self.fail("Test exchange (\"testExch\") instance not recovered: %s" % error)
+ self.assertTrue(qmf.query_exchange("testExch", alt_exchange_name = "altExch"),
+ "Alternate exchange property not found or is incorrect on exchange \"testExch\".")
+ qmf.close()
+
+ def test_queue(self):
+ """Queue alternate exchange property persistexchangeNamece test"""
+ broker = self.broker(store_args(), name="test_queue", expect=EXPECT_EXIT_OK)
+ qmf = Qmf(broker)
+ qmf.add_exchange("altExch", "direct", durable=True) # Serves as alternate exchange instance
+ qmf.add_queue("testQueue", durable=True, alt_exchange_name="altExch")
+ qmf.close()
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_queue")
+ qmf = Qmf(broker)
+ try:
+ qmf.add_exchange("altExch", "direct", passive=True)
+ except Exception, error:
+ self.fail("Alternate exchange (\"altExch\") instance not recovered: %s" % error)
+ try:
+ qmf.add_queue("testQueue", passive=True)
+ except Exception, error:
+ self.fail("Test queue (\"testQueue\") instance not recovered: %s" % error)
+ self.assertTrue(qmf.query_queue("testQueue", alt_exchange_name = "altExch"),
+ "Alternate exchange property not found or is incorrect on queue \"testQueue\".")
+ qmf.close()
+
+
+class RedeliveredTests(StoreTest):
+ """
+ Test the behavior of the redelivered flag in the context of persistence
+ """
+
+ def test_broker_recovery(self):
+ """Test that the redelivered flag is set on messages after recovery of broker"""
+ broker = self.broker(store_args(), name="test_broker_recovery", expect=EXPECT_EXIT_OK)
+ msg_content = "xyz"*100
+ msg = Message(msg_content, durable=True)
+ broker.send_message("testQueue", msg)
+ broker.terminate()
+
+ broker = self.broker(store_args(), name="test_broker_recovery")
+ rcv_msg = broker.get_message("testQueue")
+ self.assertEqual(msg_content, rcv_msg.content)
+ self.assertTrue(rcv_msg.redelivered)
+
diff --git a/qpid/cpp/src/tests/linearstore/python_tests/store_test.py b/qpid/cpp/src/tests/linearstore/python_tests/store_test.py
new file mode 100644
index 0000000000..cc846aefd4
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/python_tests/store_test.py
@@ -0,0 +1,417 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 re
+from brokertest import BrokerTest
+from qpid.messaging import Empty
+from qmf.console import Session
+
+import qpid.messaging, brokertest
+brokertest.qm = qpid.messaging # TODO aconway 2014-04-04: Tests fail with SWIG client.
+
+
+def store_args(store_dir = None):
+ """Return the broker args necessary to load the async store"""
+ assert BrokerTest.store_lib
+ if store_dir == None:
+ return []
+ return ["--store-dir", store_dir]
+
+class Qmf:
+ """
+ QMF functions not yet available in the new QMF API. Remove this and replace with new API when it becomes available.
+ """
+ def __init__(self, broker):
+ self.__session = Session()
+ self.__broker = self.__session.addBroker("amqp://localhost:%d"%broker.port())
+
+ def add_exchange(self, exchange_name, exchange_type, alt_exchange_name=None, passive=False, durable=False,
+ arguments = None):
+ """Add a new exchange"""
+ amqp_session = self.__broker.getAmqpSession()
+ if arguments == None:
+ arguments = {}
+ if alt_exchange_name:
+ amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type,
+ alternate_exchange=alt_exchange_name, passive=passive, durable=durable,
+ arguments=arguments)
+ else:
+ amqp_session.exchange_declare(exchange=exchange_name, type=exchange_type, passive=passive, durable=durable,
+ arguments=arguments)
+
+ def add_queue(self, queue_name, alt_exchange_name=None, passive=False, durable=False, arguments = None):
+ """Add a new queue"""
+ amqp_session = self.__broker.getAmqpSession()
+ if arguments == None:
+ arguments = {}
+ if alt_exchange_name:
+ amqp_session.queue_declare(queue_name, alternate_exchange=alt_exchange_name, passive=passive,
+ durable=durable, arguments=arguments)
+ else:
+ amqp_session.queue_declare(queue_name, passive=passive, durable=durable, arguments=arguments)
+
+ def delete_queue(self, queue_name):
+ """Delete an existing queue"""
+ amqp_session = self.__broker.getAmqpSession()
+ amqp_session.queue_delete(queue_name)
+
+ def _query(self, name, _class, package, alt_exchange_name=None):
+ """Qmf query function which can optionally look for the presence of an alternate exchange name"""
+ try:
+ obj_list = self.__session.getObjects(_class=_class, _package=package)
+ found = False
+ for obj in obj_list:
+ if obj.name == name:
+ found = True
+ if alt_exchange_name != None:
+ alt_exch_list = self.__session.getObjects(_objectId=obj.altExchange)
+ if len(alt_exch_list) == 0 or alt_exch_list[0].name != alt_exchange_name:
+ return False
+ break
+ return found
+ except Exception:
+ return False
+
+
+ def query_exchange(self, exchange_name, alt_exchange_name=None):
+ """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known
+ value."""
+ return self._query(exchange_name, "exchange", "org.apache.qpid.broker", alt_exchange_name)
+
+ def query_queue(self, queue_name, alt_exchange_name=None):
+ """Test for the presence of an exchange, and optionally whether it has an alternate exchange set to a known
+ value."""
+ return self._query(queue_name, "queue", "org.apache.qpid.broker", alt_exchange_name)
+
+ def queue_message_count(self, queue_name):
+ """Query the number of messages on a queue"""
+ queue_list = self.__session.getObjects(_class="queue", _name=queue_name)
+ if len(queue_list):
+ return queue_list[0].msgDepth
+
+ def queue_empty(self, queue_name):
+ """Check if a queue is empty (has no messages waiting)"""
+ return self.queue_message_count(queue_name) == 0
+
+ def get_objects(self, target_class, target_package="org.apache.qpid.broker"):
+ return self.__session.getObjects(_class=target_class, _package=target_package)
+
+
+ def close(self):
+ self.__session.delBroker(self.__broker)
+ self.__session = None
+
+
+class StoreTest(BrokerTest):
+ """
+ This subclass of BrokerTest adds some convenience test/check functions
+ """
+
+ def _chk_empty(self, queue, receiver):
+ """Check if a queue is empty (has no more messages)"""
+ try:
+ msg = receiver.fetch(timeout=0)
+ self.assert_(False, "Queue \"%s\" not empty: found message: %s" % (queue, msg))
+ except Empty:
+ pass
+
+ @staticmethod
+ def make_message(msg_count, msg_size):
+ """Make message content. Format: 'abcdef....' followed by 'msg-NNNN', where NNNN is the message count"""
+ msg = "msg-%04d" % msg_count
+ msg_len = len(msg)
+ buff = ""
+ if msg_size != None and msg_size > msg_len:
+ for index in range(0, msg_size - msg_len):
+ if index == msg_size - msg_len - 1:
+ buff += "-"
+ else:
+ buff += chr(ord('a') + (index % 26))
+ return buff + msg
+
+ # Functions for formatting address strings
+
+ @staticmethod
+ def _fmt_csv(string_list, list_braces = None):
+ """Format a list using comma-separation. Braces are optionally added."""
+ if len(string_list) == 0:
+ return ""
+ first = True
+ str_ = ""
+ if list_braces != None:
+ str_ += list_braces[0]
+ for string in string_list:
+ if string != None:
+ if first:
+ first = False
+ else:
+ str_ += ", "
+ str_ += string
+ if list_braces != None:
+ str_ += list_braces[1]
+ return str_
+
+ def _fmt_map(self, string_list):
+ """Format a map {l1, l2, l3, ...} from a string list. Each item in the list must be a formatted map
+ element('key:val')."""
+ return self._fmt_csv(string_list, list_braces="{}")
+
+ def _fmt_list(self, string_list):
+ """Format a list [l1, l2, l3, ...] from a string list."""
+ return self._fmt_csv(string_list, list_braces="[]")
+
+ def addr_fmt(self, node_name, **kwargs):
+ """Generic AMQP to new address formatter. Takes common (but not all) AMQP options and formats an address
+ string."""
+ # Get keyword args
+ node_subject = kwargs.get("node_subject")
+ create_policy = kwargs.get("create_policy")
+ delete_policy = kwargs.get("delete_policy")
+ assert_policy = kwargs.get("assert_policy")
+ mode = kwargs.get("mode")
+ link = kwargs.get("link", False)
+ link_name = kwargs.get("link_name")
+ node_type = kwargs.get("node_type")
+ durable = kwargs.get("durable", False)
+ link_reliability = kwargs.get("link_reliability")
+ x_declare_list = kwargs.get("x_declare_list", [])
+ x_bindings_list = kwargs.get("x_bindings_list", [])
+ x_subscribe_list = kwargs.get("x_subscribe_list", [])
+
+ node_flag = not link and (node_type != None or durable or len(x_declare_list) > 0 or len(x_bindings_list) > 0)
+ link_flag = link and (link_name != None or durable or link_reliability != None or len(x_declare_list) > 0 or
+ len(x_bindings_list) > 0 or len(x_subscribe_list) > 0)
+ assert not (node_flag and link_flag)
+
+ opt_str_list = []
+ if create_policy != None:
+ opt_str_list.append("create: %s" % create_policy)
+ if delete_policy != None:
+ opt_str_list.append("delete: %s" % delete_policy)
+ if assert_policy != None:
+ opt_str_list.append("assert: %s" % assert_policy)
+ if mode != None:
+ opt_str_list.append("mode: %s" % mode)
+ if node_flag or link_flag:
+ node_str_list = []
+ if link_name != None:
+ node_str_list.append("name: \"%s\"" % link_name)
+ if node_type != None:
+ node_str_list.append("type: %s" % node_type)
+ if durable:
+ node_str_list.append("durable: True")
+ if link_reliability != None:
+ node_str_list.append("reliability: %s" % link_reliability)
+ if len(x_declare_list) > 0:
+ node_str_list.append("x-declare: %s" % self._fmt_map(x_declare_list))
+ if len(x_bindings_list) > 0:
+ node_str_list.append("x-bindings: %s" % self._fmt_list(x_bindings_list))
+ if len(x_subscribe_list) > 0:
+ node_str_list.append("x-subscribe: %s" % self._fmt_map(x_subscribe_list))
+ if node_flag:
+ opt_str_list.append("node: %s" % self._fmt_map(node_str_list))
+ else:
+ opt_str_list.append("link: %s" % self._fmt_map(node_str_list))
+ addr_str = node_name
+ if node_subject != None:
+ addr_str += "/%s" % node_subject
+ if len(opt_str_list) > 0:
+ addr_str += "; %s" % self._fmt_map(opt_str_list)
+ return addr_str
+
+ def snd_addr(self, node_name, **kwargs):
+ """ Create a send (node) address"""
+ # Get keyword args
+ topic = kwargs.get("topic")
+ topic_flag = kwargs.get("topic_flag", False)
+ auto_create = kwargs.get("auto_create", True)
+ auto_delete = kwargs.get("auto_delete", False)
+ durable = kwargs.get("durable", False)
+ exclusive = kwargs.get("exclusive", False)
+ ftd_count = kwargs.get("ftd_count")
+ ftd_size = kwargs.get("ftd_size")
+ policy = kwargs.get("policy", "flow-to-disk")
+ exchage_type = kwargs.get("exchage_type")
+
+ create_policy = None
+ if auto_create:
+ create_policy = "always"
+ delete_policy = None
+ if auto_delete:
+ delete_policy = "always"
+ node_type = None
+ if topic != None or topic_flag:
+ node_type = "topic"
+ x_declare_list = ["\"exclusive\": %s" % exclusive]
+ if ftd_count != None or ftd_size != None:
+ queue_policy = ["\'qpid.policy_type\': %s" % policy]
+ if ftd_count:
+ queue_policy.append("\'qpid.max_count\': %d" % ftd_count)
+ if ftd_size:
+ queue_policy.append("\'qpid.max_size\': %d" % ftd_size)
+ x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy))
+ if exchage_type != None:
+ x_declare_list.append("type: %s" % exchage_type)
+
+ return self.addr_fmt(node_name, topic=topic, create_policy=create_policy, delete_policy=delete_policy,
+ node_type=node_type, durable=durable, x_declare_list=x_declare_list)
+
+ def rcv_addr(self, node_name, **kwargs):
+ """ Create a receive (link) address"""
+ # Get keyword args
+ auto_create = kwargs.get("auto_create", True)
+ auto_delete = kwargs.get("auto_delete", False)
+ link_name = kwargs.get("link_name")
+ durable = kwargs.get("durable", False)
+ browse = kwargs.get("browse", False)
+ exclusive = kwargs.get("exclusive", False)
+ binding_list = kwargs.get("binding_list", [])
+ ftd_count = kwargs.get("ftd_count")
+ ftd_size = kwargs.get("ftd_size")
+ policy = kwargs.get("policy", "flow-to-disk")
+
+ create_policy = None
+ if auto_create:
+ create_policy = "always"
+ delete_policy = None
+ if auto_delete:
+ delete_policy = "always"
+ mode = None
+ if browse:
+ mode = "browse"
+ x_declare_list = ["\"exclusive\": %s" % exclusive]
+ if ftd_count != None or ftd_size != None:
+ queue_policy = ["\'qpid.policy_type\': %s" % policy]
+ if ftd_count:
+ queue_policy.append("\'qpid.max_count\': %d" % ftd_count)
+ if ftd_size:
+ queue_policy.append("\'qpid.max_size\': %d" % ftd_size)
+ x_declare_list.append("arguments: %s" % self._fmt_map(queue_policy))
+ x_bindings_list = []
+ for binding in binding_list:
+ x_bindings_list.append("{exchange: %s, key: %s}" % binding)
+ if durable: reliability = 'at-least-once'
+ else: reliability = None
+ return self.addr_fmt(node_name, create_policy=create_policy, delete_policy=delete_policy, mode=mode, link=True,
+ link_name=link_name, durable=durable, x_declare_list=x_declare_list,
+ x_bindings_list=x_bindings_list, link_reliability=reliability)
+
+ def check_message(self, broker, queue, exp_msg, transactional=False, empty=False, ack=True, browse=False):
+ """Check that a message is on a queue by dequeuing it and comparing it to the expected message"""
+ return self.check_messages(broker, queue, [exp_msg], transactional, empty, ack, browse)
+
+ def check_messages(self, broker, queue, exp_msg_list, transactional=False, empty=False, ack=True, browse=False,
+ emtpy_flag=False):
+ """Check that messages is on a queue by dequeuing them and comparing them to the expected messages"""
+ if emtpy_flag:
+ num_msgs = 0
+ else:
+ num_msgs = len(exp_msg_list)
+ ssn = broker.connect().session(transactional=transactional)
+ rcvr = ssn.receiver(self.rcv_addr(queue, browse=browse), capacity=num_msgs)
+ if num_msgs > 0:
+ try:
+ recieved_msg_list = [rcvr.fetch(timeout=0) for i in range(num_msgs)]
+ except Empty:
+ self.assert_(False, "Queue \"%s\" is empty, unable to retrieve expected message %d." % (queue, i))
+ for i in range(0, len(recieved_msg_list)):
+ self.assertEqual(recieved_msg_list[i].content, exp_msg_list[i].content)
+ self.assertEqual(recieved_msg_list[i].correlation_id, exp_msg_list[i].correlation_id)
+ if empty:
+ self._chk_empty(queue, rcvr)
+ if ack:
+ ssn.acknowledge()
+ if transactional:
+ ssn.commit()
+ ssn.connection.close()
+ else:
+ if transactional:
+ ssn.commit()
+ return ssn
+
+
+ # Functions for finding strings in the broker log file (or other files)
+
+ @staticmethod
+ def _read_file(file_name):
+ """Returns the content of file named file_name as a string"""
+ file_handle = file(file_name)
+ try:
+ return file_handle.read()
+ finally:
+ file_handle.close()
+
+ def _get_hits(self, broker, search):
+ """Find all occurrences of the search in the broker log (eliminating possible duplicates from msgs on multiple
+ queues)"""
+ # TODO: Use sets when RHEL-4 is no longer supported
+ hits = []
+ for hit in search.findall(self._read_file(broker.log)):
+ if hit not in hits:
+ hits.append(hit)
+ return hits
+
+ def _reconsile_hits(self, broker, ftd_msgs, release_hits):
+ """Remove entries from list release_hits if they match the message id in ftd_msgs. Check for remaining
+ release_hits."""
+ for msg in ftd_msgs:
+ found = False
+ for hit in release_hits:
+ if str(msg.id) in hit:
+ release_hits.remove(hit)
+ #print "Found %s in %s" % (msg.id, broker.log)
+ found = True
+ break
+ if not found:
+ self.assert_(False, "Unable to locate released message %s in log %s" % (msg.id, broker.log))
+ if len(release_hits) > 0:
+ err = "Messages were unexpectedly released in log %s:\n" % broker.log
+ for hit in release_hits:
+ err += " %s\n" % hit
+ self.assert_(False, err)
+
+ def check_msg_release(self, broker, ftd_msgs):
+ """ Check for 'Content released' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content released$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_release_on_commit(self, broker, ftd_msgs):
+ """ Check for 'Content released on commit' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content released on commit$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_release_on_recover(self, broker, ftd_msgs):
+ """ Check for 'Content released after recovery' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content released after recovery$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_block(self, broker, ftd_msgs):
+ """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content release blocked$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
+
+ def check_msg_block_on_commit(self, broker, ftd_msgs):
+ """Check for 'Content release blocked' messages in broker log for messages in ftd_msgs"""
+ hits = self._get_hits(broker, re.compile("debug Message id=\"[0-9a-f-]{36}\"; pid=0x[0-9a-f]+: "
+ "Content release blocked on commit$", re.MULTILINE))
+ self._reconsile_hits(broker, ftd_msgs, hits)
diff --git a/qpid/cpp/src/tests/linearstore/run_long_python_tests b/qpid/cpp/src/tests/linearstore/run_long_python_tests
new file mode 100644
index 0000000000..be6380302c
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/run_long_python_tests
@@ -0,0 +1,21 @@
+#!/usr/bin/env 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_python_tests LONG_TEST
diff --git a/qpid/cpp/src/tests/linearstore/run_python_tests b/qpid/cpp/src/tests/linearstore/run_python_tests
new file mode 100755
index 0000000000..4ff212a71c
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/run_python_tests
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+source ${QPID_TEST_COMMON}
+
+ensure_python_tests
+
+#Add our directory to the python path
+export PYTHONPATH=$srcdir/linearstore:${PYTHONPATH}
+
+MODULENAME=python_tests
+
+echo "Running Python tests in module ${MODULENAME}..."
+
+QPID_PORT=${QPID_PORT:-5672}
+FAILING=${FAILING:-/dev/null}
+PYTHON_TESTS=${PYTHON_TESTS:-$*}
+
+OUTDIR=${MODULENAME}.tmp
+rm -rf ${OUTDIR}
+
+# To debug a test, add the following options to the end of the following line:
+# -v DEBUG -c qpid.messaging.io.ops [*.testName]
+${QPID_PYTHON_TEST} -m ${MODULENAME} -I ${FAILING} -DOUTDIR=${OUTDIR} ${PYTHON_TEST} || exit 1
+
diff --git a/qpid/cpp/src/tests/linearstore/run_short_python_tests b/qpid/cpp/src/tests/linearstore/run_short_python_tests
new file mode 100644
index 0000000000..9b9e7c59be
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/run_short_python_tests
@@ -0,0 +1,21 @@
+#!/usr/bin/env 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_python_tests SHORT_TEST
diff --git a/qpid/cpp/src/tests/linearstore/tx-test-soak.sh b/qpid/cpp/src/tests/linearstore/tx-test-soak.sh
new file mode 100755
index 0000000000..7d5581961f
--- /dev/null
+++ b/qpid/cpp/src/tests/linearstore/tx-test-soak.sh
@@ -0,0 +1,275 @@
+#! /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.
+#
+
+# tx-test-soak
+#
+# Basic test methodology:
+# 1. Start broker
+# 2. Run qpid-txtest against broker using randomly generated parameters
+# 3. After some time, kill the broker using SIGKILL
+# 4. Restart broker, recover messages
+# 5. Run qpid-txtest against broker in check mode, which checks that all expected messages are present.
+# 6. Wash, rinse, repeat... The number of runs is determined by ${NUM_RUNS}
+
+# NOTE: The following is based on typical development tree paths, not installed paths
+
+NUM_RUNS=1000
+BASE_DIR=${HOME}/RedHat
+CMAKE_BUILD_DIR=${BASE_DIR}/q.cm
+
+# Infrequently adjusted
+RESULT_BASE_DIR_PREFIX=${BASE_DIR}/results.tx-test-soak
+RECOVER_TIME_PER_QUEUE=1
+STORE_MODULE="linearstore.so"
+BROKER_LOG_LEVEL="info+"
+BROKER_MANAGEMENT="no" # "no" or "yes"
+TRUNCATE_INTERVAL=10
+MAX_DISK_PERC_USED=90
+
+# Constants (don't adjust these)
+export BASE_DIR
+RELATIVE_BASE_DIR=`python -c "import os,os.path; print os.path.relpath(os.environ['BASE_DIR'], os.environ['PWD'])"`
+export PYTHONPATH=${BASE_DIR}/qpid/python:${BASE_DIR}/qpid/extras/qmf/src/py:${BASE_DIR}/qpid/tools/src/py
+LOG_FILE_NAME=log.txt
+QPIDD_FN=qpidd
+QPIDD=${CMAKE_BUILD_DIR}/src/${QPIDD_FN}
+TXTEST_FN=qpid-txtest
+TXTEST=${CMAKE_BUILD_DIR}/src/tests/${TXTEST_FN}
+ANALYZE_FN=qpid_qls_analyze.py
+ANALYZE=${BASE_DIR}/qpid/tools/src/py/${ANALYZE_FN}
+ANALYZE_ARGS="--efp --show-recs --stats"
+QPIDD_BASE_ARGS="--load-module ${STORE_MODULE} -m ${BROKER_MANAGEMENT} --auth no --default-flow-stop-threshold 0 --default-flow-resume-threshold 0 --default-queue-limit 0 --store-dir ${BASE_DIR} --log-enable ${BROKER_LOG_LEVEL} --log-to-stderr no --log-to-stdout no"
+TXTEST_INIT_STR="--init yes --transfer no --check no"
+TXTEST_RUN_STR="--init no --transfer yes --check no"
+TXTEST_CHK_STR="--init no --transfer no --check yes"
+SUCCESS_MSG="All expected messages were retrieved."
+TIMESTAMP_FORMAT="+%Y-%m-%d_%H:%M:%S"
+ANSI_RED="\e[1;31m"
+ANSI_NONE="\e[0m"
+DEFAULT_EFP_DIR=2048k
+DEFAULT_EFP_SIZE=2101248
+SIG_KILL=-9
+SIG_TERM=-15
+
+# Creates a random number into the variable named in string $1 in the range [$2..$3] (both inclusive).
+# $1: variable name as string to which random value is assigned
+# $2: minimum inclusive range of random number
+# $3: maximum inclusive range of random number
+get_random() {
+ eval $1=`python -S -c "import random; print random.randint($2,$3)"`
+}
+
+# Uses anon-uniform distribution to set a random message size.
+# Most messages must be small (0 - 1k), but we need a few medium (10k) and large (100k) ones also.
+# Sets message size into var ${MSG_SIZE}
+set_message_size() {
+ local key=0
+ get_random "key" 1 10
+ if (( "${key}" == "10" )); then # 1 out of 10 - very large
+ get_random "MSG_SIZE" 100000 1000000
+ FILE_SIZE_MULTIPLIER=3
+ elif (( "${key}" >= "8" )); then # 2 out of 10 - large
+ get_random "MSG_SIZE" 10000 100000
+ FILE_SIZE_MULTIPLIER=2
+ elif (( "${key}" >= "6" )); then # 2 out of 10 - medium
+ get_random "MSG_SIZE" 1000 10000
+ FILE_SIZE_MULTIPLIER=1
+ else # 5 out of 10 - small
+ get_random "MSG_SIZE" 10 1000
+ FILE_SIZE_MULTIPLIER=1
+ fi
+}
+
+# Start or restart broker
+# $1: Log suffix: either "A" or "B". If "A", broker is started with truncation, otherwise broker is restarted with recovery.
+# $2: Truncate flag - only used if Log suffix is "A": if true, then truncate store
+# The PID of the broker is returned in ${QPIDD_PID}
+start_broker() {
+ local truncate_val
+ local truncate_str
+ if [[ "$1" == "A" ]]; then
+ if [[ $2 == true ]]; then
+ truncate_val="yes"
+ truncate_str="(Store truncated)"
+ if [[ -e ${BASE_DIR}/qls/p001/efp/${DEFAULT_EFP_DIR} ]]; then
+ for f in ${BASE_DIR}/qls/p001/efp/${DEFAULT_EFP_DIR}/*; do
+ local filesize=`stat -c%s "${f}"`
+ if (( ${filesize} != ${DEFAULT_EFP_SIZE} )); then
+ rm ${f}
+ fi
+ done
+ fi
+ else
+ truncate_val="no"
+ fi
+ else
+ truncate_val="no"
+ fi
+ echo "${QPIDD} ${QPIDD_BASE_ARGS} --truncate ${truncate_val} --log-to-file ${RESULT_DIR}/qpidd.$1.log &" > ${RESULT_DIR}/qpidd.$1.cmd
+ ${QPIDD} ${QPIDD_BASE_ARGS} --truncate ${truncate_val} --log-to-file ${RESULT_DIR}/qpidd.$1.log &
+ QPIDD_PID=$!
+ echo "Broker PID=${QPIDD_PID} ${truncate_str}" | tee -a ${LOG_FILE}
+}
+
+# Start or evaluate results of transaction test client
+# $1: Log suffix flag: either "A" or "B". If "A", client is started in test mode, otherwise client evaluates recovery.
+start_tx_test() {
+ local tx_test_params="--messages-per-tx ${MSGS_PER_TX} --tx-count 1000000 --total-messages ${TOT_MSGS} --size ${MSG_SIZE} --queues ${NUM_QUEUES}"
+ if [[ "$1" == "A" ]]; then
+ # Run in background
+ echo "${TXTEST##*/} parameters: ${tx_test_params}" | tee -a ${LOG_FILE}
+ echo "${TXTEST} ${tx_test_params} ${TXTEST_INIT_STR} &> ${RESULT_DIR}/txtest.$1.log" > ${RESULT_DIR}/txtest.$1.cmd
+ ${TXTEST} ${tx_test_params} ${TXTEST_INIT_STR} &> ${RESULT_DIR}/txtest.$1.log
+ echo "${TXTEST} ${tx_test_params} ${TXTEST_RUN_STR} &> ${RESULT_DIR}/txtest.$1.log &" >> ${RESULT_DIR}/txtest.$1.cmd
+ ${TXTEST} ${tx_test_params} ${TXTEST_RUN_STR} &> ${RESULT_DIR}/txtest.$1.log &
+ else
+ # Run in foreground
+ #echo "${TXTEST##*/} ${tx_test_params} ${TXTEST_CHK_STR}" | tee -a ${LOG_FILE}
+ echo "${TXTEST} ${tx_test_params} ${TXTEST_CHK_STR} &> ${RESULT_DIR}/txtest.$1.log" > ${RESULT_DIR}/txtest.$1.cmd
+ ${TXTEST} ${tx_test_params} ${TXTEST_CHK_STR} &> ${RESULT_DIR}/txtest.$1.log
+ fi
+}
+
+# Search for the presence of core.* files, move them into the current result directory and run gdb against them.
+# No params
+process_core_files() {
+ ls core.* &> /dev/null
+ if (( "$?" == "0" )); then
+ for cf in core.*; do
+ gdb --batch --quiet -ex "thread apply all bt" -ex "quit" ${QPIDD} ${cf} &> ${RESULT_DIR}/${cf##*/}.gdb.txt
+ gdb --batch --quiet -ex "thread apply all bt full" -ex "quit" ${QPIDD} ${cf} &> ${RESULT_DIR}/${cf##*/}.gdb-full.txt
+ cat ${RESULT_DIR}/${cf##*/}.gdb.txt
+ mv ${cf} ${RESULT_DIR}/
+ echo "Core file ${cf##*/} found and recovered"
+ done
+ fi
+}
+
+# Kill a process quietly
+# $1: Signal
+# $2: PID
+kill_process() {
+ kill ${1} ${2} &>> ${LOG_FILE}
+ wait ${2} &>> ${LOG_FILE}
+}
+
+# Check that test can run: No other copy of qpidd running, enough disk space
+check_ready_to_run() {
+ # Check no copy of qpidd is running
+ PID=`pgrep ${QPIDD_FN}`
+ if [[ "$?" == "0" ]]; then
+ echo "ERROR: qpidd running as pid ${PID}"
+ exit 1
+ fi
+ # Check disk is < 90% full
+ local perc_full=`df -h ${HOME} | tail -1 | awk '{print substr($5,0, length($5)-1)}'`
+ if (( ${perc_full} >= ${MAX_DISK_PERC_USED} )); then
+ echo "ERROR: Disk is too close to full (${perc_full}%)"
+ exit 2
+ fi
+}
+
+# Analyze store files
+# $1: Log suffix flag: either "A" or "B". If "A", client is started in test mode, otherwise client evaluates recovery.
+analyze_store() {
+ ${ANALYZE} ${ANALYZE_ARGS} ${BASE_DIR}/qls &> ${RESULT_DIR}/qls_analysis.$1.log
+ echo >> ${RESULT_DIR}/qls_analysis.$1.log
+ echo "----------------------------------------------------------" >> ${RESULT_DIR}/qls_analysis.$1.log
+ echo "With transactional reconsiliation:" >> ${RESULT_DIR}/qls_analysis.$1.log
+ echo >> ${RESULT_DIR}/qls_analysis.$1.log
+ ${ANALYZE} ${ANALYZE_ARGS} --txn ${BASE_DIR}/qls &>> ${RESULT_DIR}/qls_analysis.$1.log
+}
+
+ulimit -c unlimited # Allow core files to be created
+
+RESULT_BASE_DIR_SUFFIX=`date "${TIMESTAMP_FORMAT}"`
+RESULT_BASE_DIR="${RESULT_BASE_DIR_PREFIX}.${RESULT_BASE_DIR_SUFFIX}"
+LOG_FILE=${RESULT_BASE_DIR}/${LOG_FILE_NAME}
+if [[ -n "${RESULT_BASE_DIR}" ]]; then
+ rm -rf ${RESULT_BASE_DIR}
+fi
+
+mkdir -p ${RESULT_BASE_DIR}
+for rn in `seq ${NUM_RUNS}`; do
+ # === Prepare result dir, check ready to run test, set run vars ===
+ RESULT_DIR=${RESULT_BASE_DIR}/run_${rn}
+ mkdir -p ${RESULT_DIR}
+ check_ready_to_run
+ if (( (${rn} - 1) % ${TRUNCATE_INTERVAL} == 0 )) || [[ -n ${ERROR_FLAG} ]]; then
+ TRUNCATE_FLAG=true
+ else
+ TRUNCATE_FLAG=false
+ fi
+ set_message_size
+ get_random "MSGS_PER_TX" 1 20
+ get_random "TOT_MSGS" 100 1000
+ get_random "NUM_QUEUES" 2 15
+ MIN_RUNTIME=$(( 20 * ${FILE_SIZE_MULTIPLIER} ))
+ MAX_RUNTIME=$(( 120 * ${FILE_SIZE_MULTIPLIER} ))
+ get_random "RUN_TIME" ${MIN_RUNTIME} ${MAX_RUNTIME}
+ RECOVER_TIME=$(( ${NUM_QUEUES} * ${RECOVER_TIME_PER_QUEUE} * ${FILE_SIZE_MULTIPLIER} ))
+ echo "Run ${rn} of ${NUM_RUNS} ==============" | tee -a ${LOG_FILE}
+
+ # === PART A: Initial run of qpid-txtest ===
+ start_broker "A" ${TRUNCATE_FLAG}
+ sleep ${RECOVER_TIME} # Need a way to test if broker has started here
+ start_tx_test "A"
+ echo "Running for ${RUN_TIME} secs..." | tee -a ${LOG_FILE}
+ sleep ${RUN_TIME}
+ kill_process ${SIG_KILL} ${QPIDD_PID}
+ sleep 2
+ analyze_store "A"
+ tar -czf ${RESULT_DIR}/qls_A.tar.gz ${RELATIVE_BASE_DIR}/qls
+
+ # === PART B: Recovery and check ===
+ start_broker "B"
+ echo "Recover time=${RECOVER_TIME} secs..." | tee -a ${LOG_FILE}
+ sleep ${RECOVER_TIME} # Need a way to test if broker has started here
+ start_tx_test "B"
+ sleep 1
+ kill_process ${SIG_TERM} ${QPIDD_PID}
+ sleep 2
+ PID=`pgrep ${QPIDD_FN}`
+ if [[ "$?" == "0" ]]; then
+ kill_process ${SIG_KILL} ${PID}
+ sleep 2
+ fi
+ analyze_store "B"
+ tar -czf ${RESULT_DIR}/qls_B.tar.gz ${RELATIVE_BASE_DIR}/qls
+
+ # === Check for errors, cores and exceptions in logs ===
+ grep -Hn "jexception" ${RESULT_DIR}/qpidd.A.log | tee -a ${LOG_FILE}
+ grep -Hn "jexception" ${RESULT_DIR}/qpidd.B.log | tee -a ${LOG_FILE}
+ grep -Hn "Traceback (most recent call last):" ${RESULT_DIR}/qls_analysis.A.log | tee -a ${LOG_FILE}
+ grep -Hn "Traceback (most recent call last):" ${RESULT_DIR}/qls_analysis.B.log | tee -a ${LOG_FILE}
+ grep "${SUCCESS_MSG}" ${RESULT_DIR}/txtest.B.log &> /dev/null
+ if [[ "$?" != "0" ]]; then
+ echo "ERROR in run ${rn}" >> ${LOG_FILE}
+ echo -e "${ANSI_RED}ERROR${ANSI_NONE} in run ${rn}"
+ ERROR_FLAG=true
+ else
+ unset ERROR_FLAG
+ fi
+ sleep 2
+ process_core_files
+ echo | tee -a ${LOG_FILE}
+done
+
diff --git a/qpid/cpp/src/tests/logging.cpp b/qpid/cpp/src/tests/logging.cpp
new file mode 100644
index 0000000000..32cd09d73d
--- /dev/null
+++ b/qpid/cpp/src/tests/logging.cpp
@@ -0,0 +1,512 @@
+/*
+ *
+ * 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)
+# include "qpid/log/windows/SinkOptions.h"
+#else
+# include "qpid/log/posix/SinkOptions.h"
+#endif
+
+#include <boost/test/floating_point_comparison.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/format.hpp>
+#include "unit_test.h"
+
+#include <exception>
+#include <fstream>
+#include <time.h>
+
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(loggingTestSuite)
+
+using namespace std;
+using namespace qpid::log;
+using boost::ends_with;
+using boost::contains;
+using boost::format;
+
+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(testSelector_disable) {
+ Selector s;
+ // Simple enable/disable
+ s.enable(trace,"foo");
+ BOOST_CHECK(s.isEnabled(trace,"foo"));
+ BOOST_CHECK(!s.isDisabled(trace,"foo"));
+ s.disable(trace,"foo");
+ BOOST_CHECK(s.isEnabled(trace,"foo"));
+ BOOST_CHECK(s.isDisabled(trace,"foo"));
+}
+
+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_EQUAL(out->last().find(__FILE__), 0u);
+
+ l.format(Logger::FUNCTION);
+ QPID_LOG(critical, "foo");
+ BOOST_CHECK( ends_with( out->last(), ": foo\n"));
+ string name = out->last().substr(0, out->last().length() - 6);
+ BOOST_CHECK( contains( string(BOOST_CURRENT_FUNCTION), name));
+
+ 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, ::qpid::log::unspecified};
+ 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-disable", "error+:foo",
+ "--log-disable", "debug:bar",
+ "--log-disable", "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("");
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions sinks("test");
+#else
+ qpid::log::posix::SinkOptions sinks("test");
+#endif
+ opts.parse(ARGC(argv), const_cast<char**>(argv));
+ sinks = *opts.sinkOptions;
+ vector<string> expect=list_of("error+:foo")("debug:bar")("info");
+ BOOST_CHECK_EQUAL(expect, opts.selectors);
+ BOOST_CHECK_EQUAL(expect, opts.deselectors);
+ 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) {
+ qpid::log::Options opts("");
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions sinks("test");
+#else
+ qpid::log::posix::SinkOptions sinks("test");
+#endif
+ sinks = *opts.sinkOptions;
+ BOOST_CHECK(sinks.logToStderr);
+ BOOST_CHECK(!sinks.logToStdout);
+ BOOST_CHECK(sinks.logFile.length() == 0);
+ vector<string> expect=list_of("notice+");
+ BOOST_CHECK_EQUAL(expect, opts.selectors);
+ BOOST_CHECK(opts.time && opts.level);
+ BOOST_CHECK(!(opts.source || opts.function || opts.thread));
+}
+
+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(testDeselectorFromOptions) {
+ const char* argv[]={
+ 0,
+ "--log-disable", "error-:foo",
+ "--log-disable", "debug:bar",
+ "--log-disable", "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.deselectors);
+ Selector s(opts);
+ BOOST_CHECK(!s.isDisabled(warning, "x"));
+ BOOST_CHECK(!s.isDisabled(debug, "x"));
+ BOOST_CHECK(s.isDisabled(debug, "bar"));
+ BOOST_CHECK(s.isDisabled(trace, "foo"));
+ BOOST_CHECK(s.isDisabled(debug, "foo"));
+ BOOST_CHECK(s.isDisabled(info, "foo"));
+ BOOST_CHECK(s.isDisabled(notice, "foo"));
+ BOOST_CHECK(s.isDisabled(warning, "foo"));
+ BOOST_CHECK(s.isDisabled(error, "foo"));
+ BOOST_CHECK(!s.isDisabled(critical, "foo"));
+}
+
+QPID_AUTO_TEST_CASE(testMultiConflictingSelectorFromOptions) {
+ const char* argv[]={
+ 0,
+ "--log-enable", "trace+:foo",
+ "--log-disable", "error-:foo",
+ "--log-enable", "debug:bar",
+ "--log-disable", "debug:bar",
+ "--log-enable", "info",
+ "--log-disable", "info",
+ "--log-enable", "debug+:Model",
+ "--log-disable", "info-:Model"
+ };
+ qpid::log::Options opts("");
+ opts.parse(ARGC(argv), const_cast<char**>(argv));
+ Selector s(opts);
+ BOOST_CHECK(!s.isEnabled(warning, "x", log::broker));
+ BOOST_CHECK(!s.isEnabled(debug, "x", log::broker));
+ BOOST_CHECK(!s.isEnabled(trace, "foo", log::broker));
+ BOOST_CHECK(!s.isEnabled(debug, "foo", log::broker));
+ BOOST_CHECK(!s.isEnabled(info, "foo", log::broker));
+ BOOST_CHECK(!s.isEnabled(notice, "foo", log::broker));
+ BOOST_CHECK(!s.isEnabled(warning, "foo", log::broker));
+ BOOST_CHECK(!s.isEnabled(error, "foo", log::broker));
+ BOOST_CHECK(s.isEnabled(critical, "foo", log::broker));
+ BOOST_CHECK(!s.isEnabled(debug, "bar", log::model));
+ BOOST_CHECK(!s.isEnabled(trace, "zaz", log::model));
+ BOOST_CHECK(!s.isEnabled(debug, "zaz", log::model));
+ BOOST_CHECK(!s.isEnabled(info, "zaz", log::model));
+ BOOST_CHECK(s.isEnabled(notice, "zaz", log::model));
+ BOOST_CHECK(s.isEnabled(warning, "zaz", log::model));
+ BOOST_CHECK(s.isEnabled(error, "zaz", log::model));
+ BOOST_CHECK(s.isEnabled(critical, "zaz", log::model));
+}
+
+QPID_AUTO_TEST_CASE(testLoggerStateure) {
+ Logger& l=Logger::instance();
+ ScopedSuppressLogging ls(l);
+ qpid::log::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_CAT(critical, test, "foo"); int srcline=__LINE__;
+ ifstream log("logging.tmp");
+ string line;
+ getline(log, line);
+ string expect=(format("[Test] 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);
+ qpid::log::Options opts("test");
+ opts.time=false;
+#ifdef _WIN32
+ qpid::log::windows::SinkOptions *sinks =
+ dynamic_cast<qpid::log::windows::SinkOptions *>(opts.sinkOptions.get());
+#else
+ qpid::log::posix::SinkOptions *sinks =
+ dynamic_cast<qpid::log::posix::SinkOptions *>(opts.sinkOptions.get());
+#endif
+ sinks->logToStderr = false;
+ sinks->logFile = "logging.tmp";
+ l.configure(opts);
+
+ char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff";
+ string str(s, sizeof(s));
+ QPID_LOG_CAT(critical, test, str);
+ ifstream log("logging.tmp");
+ string line;
+ getline(log, line, '\0');
+ string expect="[Test] 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_CASE(testSelectorElements) {
+ SelectorElement s("debug");
+ BOOST_CHECK_EQUAL(s.levelStr, "debug");
+ BOOST_CHECK_EQUAL(s.patternStr, "");
+ BOOST_CHECK_EQUAL(s.level, debug);
+ BOOST_CHECK(!s.isDisable);
+ BOOST_CHECK(!s.isCategory);
+ BOOST_CHECK(!s.isLevelAndAbove);
+ BOOST_CHECK(!s.isLevelAndBelow);
+
+ SelectorElement t("debug:Broker");
+ BOOST_CHECK_EQUAL(t.levelStr, "debug");
+ BOOST_CHECK_EQUAL(t.patternStr, "Broker");
+ BOOST_CHECK_EQUAL(t.level, debug);
+ BOOST_CHECK_EQUAL(t.category, broker);
+ BOOST_CHECK(!t.isDisable);
+ BOOST_CHECK(t.isCategory);
+ BOOST_CHECK(!t.isLevelAndAbove);
+ BOOST_CHECK(!t.isLevelAndBelow);
+
+ SelectorElement u("info+:qmf::");
+ BOOST_CHECK_EQUAL(u.levelStr, "info");
+ BOOST_CHECK_EQUAL(u.patternStr, "qmf::");
+ BOOST_CHECK_EQUAL(u.level, info);
+ BOOST_CHECK(!u.isDisable);
+ BOOST_CHECK(!u.isCategory);
+ BOOST_CHECK(u.isLevelAndAbove);
+ BOOST_CHECK(!u.isLevelAndBelow);
+
+ SelectorElement v("critical-");
+ BOOST_CHECK_EQUAL(v.levelStr, "critical");
+ BOOST_CHECK_EQUAL(v.patternStr, "");
+ BOOST_CHECK_EQUAL(v.level, critical);
+ BOOST_CHECK(!v.isDisable);
+ BOOST_CHECK(!v.isCategory);
+ BOOST_CHECK(!v.isLevelAndAbove);
+ BOOST_CHECK(v.isLevelAndBelow);
+
+ SelectorElement w("!warning-:Management");
+ BOOST_CHECK_EQUAL(w.levelStr, "warning");
+ BOOST_CHECK_EQUAL(w.patternStr, "Management");
+ BOOST_CHECK_EQUAL(w.level, warning);
+ BOOST_CHECK_EQUAL(w.category, management);
+ BOOST_CHECK(w.isDisable);
+ BOOST_CHECK(w.isCategory);
+ BOOST_CHECK(!w.isLevelAndAbove);
+ BOOST_CHECK(w.isLevelAndBelow);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/misc.py b/qpid/cpp/src/tests/misc.py
new file mode 100644
index 0000000000..257fb9e754
--- /dev/null
+++ b/qpid/cpp/src/tests/misc.py
@@ -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.
+#
+
+from qpid.tests.messaging.implementation import *
+from qpid.tests.messaging import VersionTest
+
+class MiscellaneousTests (VersionTest):
+ """
+ Tests for various aspects of qpidd behaviour
+ """
+ def test_exclusive(self):
+ con = self.create_connection("amqp1.0", True)
+ rcv = con.session().receiver("q; {create:always, node:{properties:{exclusive:True,auto-delete:True}}}")
+
+ other = self.create_connection("amqp1.0", True)
+ try:
+ #can send to the queue
+ snd = other.session().sender("q")
+
+ #can browse the queue
+ browser = other.session().receiver("q; {mode:browse}")
+
+ #can't consume from the queue
+ try:
+ consumer = other.session().receiver("q")
+ assert False, ("Should not be able to consume from exclusively owned queue")
+ except LinkError, e: None
+ try:
+ exclusive = other.session().receiver("q; {create: always, node:{properties:{exclusive:True}}}")
+ assert False, ("Should not be able to consume exclusively from exclusively owned queue")
+ except LinkError, e: None
+ finally:
+ rcv.close()
+ con.close()
+ other.close()
+
+class AutoDeleteExchangeTests(VersionTest):
+ def init_test(self, exchange_type="topic"):
+ rcv = self.ssn.receiver("my-topic; {create:always, node:{type:topic, properties:{'exchange-type':%s, 'auto-delete':True}}}" % exchange_type)
+ snd = self.ssn.sender("my-topic")
+ #send some messages
+ msgs = [Message(content=c) for c in ['a','b','c','d']]
+ for m in msgs: snd.send(m)
+
+ #verify receipt
+ for expected in msgs:
+ msg = rcv.fetch(0)
+ assert msg.content == expected.content
+ self.ssn.acknowledge(msg)
+ return (rcv, snd)
+
+ def on_rcv_detach_test(self, exchange_type="topic"):
+ rcv, snd = self.init_test(exchange_type)
+ rcv.close()
+ #verify exchange is still there
+ snd.send(Message(content="will be dropped"))
+ snd.close()
+ #now verify it is no longer there
+ try:
+ self.ssn.sender("my-topic")
+ assert False, "Attempt to send to deleted exchange should fail"
+ except MessagingError: None
+
+ def on_snd_detach_test(self, exchange_type="topic"):
+ rcv, snd = self.init_test(exchange_type)
+ snd.close()
+ #verify exchange is still there
+ snd = self.ssn.sender("my-topic")
+ snd.send(Message(content="will be dropped"))
+ snd.close()
+ rcv.close()
+ #now verify it is no longer there
+ try:
+ self.ssn.sender("my-topic")
+ assert False, "Attempt to send to deleted exchange should fail"
+ except MessagingError: None
+
+ def test_autodelete_fanout_exchange_on_rcv_detach(self):
+ self.on_rcv_detach_test("fanout")
+
+ def test_autodelete_fanout_exchange_on_snd_detach(self):
+ self.on_snd_detach_test("fanout")
+
+ def test_autodelete_direct_exchange_on_rcv_detach(self):
+ self.on_rcv_detach_test("direct")
+
+ def test_autodelete_direct_exchange_on_snd_detach(self):
+ self.on_snd_detach_test("direct")
+
+ def test_autodelete_topic_exchange_on_rcv_detach(self):
+ self.on_rcv_detach_test("topic")
+
+ def test_autodelete_topic_exchange_on_snd_detach(self):
+ self.on_snd_detach_test("topic")
+
+ def test_autodelete_headers_exchange_on_rcv_detach(self):
+ self.on_rcv_detach_test("headers")
+
+ def test_autodelete_headers_exchange_on_snd_detach(self):
+ self.on_snd_detach_test("headers")
+
+
+
diff --git a/qpid/cpp/src/tests/msg_group_test.cpp b/qpid/cpp/src/tests/msg_group_test.cpp
new file mode 100644
index 0000000000..ca87197ff3
--- /dev/null
+++ b/qpid/cpp/src/tests/msg_group_test.cpp
@@ -0,0 +1,641 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/FailoverUpdates.h>
+#include <qpid/Options.h>
+#include <qpid/log/Logger.h>
+#include <qpid/log/Options.h>
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/SystemInfo.h"
+
+#include <iostream>
+#include <memory>
+#include <stdlib.h>
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Options : public qpid::Options
+{
+ bool help;
+ std::string url;
+ std::string address;
+ std::string connectionOptions;
+ uint messages;
+ uint capacity;
+ uint ackFrequency;
+ bool failoverUpdates;
+ qpid::log::Options log;
+ uint senders;
+ uint receivers;
+ uint groupSize;
+ bool printReport;
+ std::string groupKey;
+ bool durable;
+ bool allowDuplicates;
+ bool randomizeSize;
+ bool stickyConsumer;
+ uint timeout;
+ uint interleave;
+ std::string prefix;
+ uint sendRate;
+
+ Options(const std::string& argv0=std::string())
+ : qpid::Options("Options"),
+ help(false),
+ url("amqp:tcp:127.0.0.1"),
+ messages(10000),
+ capacity(1000),
+ ackFrequency(100),
+ failoverUpdates(false),
+ log(argv0),
+ senders(2),
+ receivers(2),
+ groupSize(10),
+ printReport(false),
+ groupKey("qpid.no_group"),
+ durable(false),
+ allowDuplicates(false),
+ randomizeSize(false),
+ stickyConsumer(false),
+ timeout(10),
+ interleave(1),
+ sendRate(0)
+ {
+ addOptions()
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to send and receive from")
+ ("allow-duplicates", qpid::optValue(allowDuplicates), "Ignore the delivery of duplicated messages")
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
+ ("capacity", qpid::optValue(capacity, "N"), "Pre-fetch window (0 implies no pre-fetch)")
+ ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
+ ("durable", qpid::optValue(durable, "yes|no"), "Mark messages as durable.")
+ ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover")
+ ("group-key", qpid::optValue(groupKey, "KEY"), "Key of the message header containing the group identifier.")
+ ("group-prefix", qpid::optValue(prefix, "STRING"), "Add 'prefix' to the start of all generated group identifiers.")
+ ("group-size", qpid::optValue(groupSize, "N"), "Number of messages per a group.")
+ ("interleave", qpid::optValue(interleave, "N"), "Simultaineously interleave messages from N different groups.")
+ ("messages,m", qpid::optValue(messages, "N"), "Number of messages to send per each sender.")
+ ("receivers,r", qpid::optValue(receivers, "N"), "Number of message consumers.")
+ ("randomize-group-size", qpid::optValue(randomizeSize), "Randomize the number of messages per group to [1...group-size].")
+ ("send-rate", qpid::optValue(sendRate,"N"), "Send at rate of N messages/second. 0 means send as fast as possible.")
+ ("senders,s", qpid::optValue(senders, "N"), "Number of message producers.")
+ ("sticky-consumers", qpid::optValue(stickyConsumer), "If set, verify that all messages in a group are consumed by the same client [TBD].")
+ ("timeout", qpid::optValue(timeout, "N"), "Fail with a stall error should all consumers remain idle for timeout seconds.")
+ ("print-report", qpid::optValue(printReport), "Dump message group statistics to stdout.")
+ ("help", qpid::optValue(help), "print this usage statement");
+ add(log);
+ //("check-redelivered", qpid::optValue(checkRedelivered), "Fails with exception if a duplicate is not marked as redelivered (only relevant when ignore-duplicates is selected)")
+ //("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)")
+ //("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)")
+ }
+
+ bool parse(int argc, char** argv)
+ {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (address.empty()) throw qpid::Exception("Address must be specified!");
+ if (senders == 0 && receivers == 0) throw qpid::Exception("No senders and No receivers?");
+ if (messages == 0) throw qpid::Exception("The message count cannot be zero.");
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::cout << *this << std::endl << std::endl
+ << "Verifies the behavior of grouped messages." << std::endl;
+ return false;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ return false;
+ }
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+
+
+// class that monitors group state across all publishers and consumers. tracks the next
+// expected sequence for each group, and total messages consumed.
+class GroupChecker
+{
+ qpid::sys::Mutex lock;
+
+ uint consumerCt;
+ uint producerCt;
+ uint totalMsgs;
+ uint totalMsgsConsumed;
+ uint totalMsgsPublished;
+ bool allowDuplicates;
+ uint duplicateMsgs;
+
+ typedef std::map<std::string, uint> SequenceMap;
+ SequenceMap sequenceMap;
+
+ // Statistics - for each group, store the names of all clients that consumed messages
+ // from that group, and the number of messages consumed per client.
+ typedef std::map<std::string, uint> ClientCounter;
+ typedef std::map<std::string, ClientCounter> GroupStatistics;
+ GroupStatistics statistics;
+
+public:
+
+ GroupChecker( uint messages, uint consumers, uint producers, bool d) :
+ consumerCt(consumers), producerCt(producers),
+ totalMsgs(0), totalMsgsConsumed(0), totalMsgsPublished(0), allowDuplicates(d),
+ duplicateMsgs(0)
+ {
+ // if consumering only - we a draining a queue of 'messages' queued messages.
+ if (producerCt != 0) {
+ totalMsgs = producers * messages;
+ } else {
+ totalMsgs = messages;
+ }
+ }
+
+ bool checkSequence( const std::string& groupId,
+ uint sequence, const std::string& client )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+
+ QPID_LOG(debug, "Client " << client << " has received " << groupId << ":" << sequence);
+
+ GroupStatistics::iterator gs = statistics.find(groupId);
+ if (gs == statistics.end()) {
+ statistics[groupId][client] = 1;
+ } else {
+ gs->second[client]++;
+ }
+ // now verify
+ SequenceMap::iterator s = sequenceMap.find(groupId);
+ if (s == sequenceMap.end()) {
+ QPID_LOG(debug, "Client " << client << " thinks this is the first message from group " << groupId << ":" << sequence);
+ // if duplication allowed, it is possible that the last msg(s) of an old sequence are redelivered on reconnect.
+ // in this case, set the sequence from the first msg.
+ sequenceMap[groupId] = (allowDuplicates) ? sequence : 0;
+ s = sequenceMap.find(groupId);
+ } else if (sequence < s->second) {
+ duplicateMsgs++;
+ QPID_LOG(debug, "Client " << client << " thinks this message is a duplicate! " << groupId << ":" << sequence);
+ return allowDuplicates;
+ }
+ totalMsgsConsumed++;
+ return sequence == s->second++;
+ }
+
+ void sendingSequence( const std::string& groupId,
+ uint sequence, bool eos,
+ const std::string& client )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ ++totalMsgsPublished;
+
+ QPID_LOG(debug, "Client " << client << " sending " << groupId << ":" << sequence <<
+ ((eos) ? " (last)" : ""));
+ }
+
+ bool eraseGroup( const std::string& groupId, const std::string& name )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ QPID_LOG(debug, "Deleting group " << groupId << " (by client " << name << ")");
+ return sequenceMap.erase( groupId ) == 1;
+ }
+
+ uint getNextExpectedSequence( const std::string& groupId )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return sequenceMap[groupId];
+ }
+
+ bool allMsgsPublished() // true when done publishing msgs
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return (producerCt == 0 || totalMsgsPublished >= totalMsgs);
+ }
+
+ bool allMsgsConsumed() // true when done consuming msgs
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return (consumerCt == 0 ||
+ (totalMsgsConsumed >= totalMsgs && sequenceMap.size() == 0));
+ }
+
+ uint getTotalMessages()
+ {
+ return totalMsgs;
+ }
+
+ uint getConsumedTotal()
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return totalMsgsConsumed;
+ }
+
+ uint getPublishedTotal()
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return totalMsgsPublished;
+ }
+
+ ostream& print(ostream& out)
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ out << "Total Published: " << totalMsgsPublished << ", Total Consumed: " << totalMsgsConsumed <<
+ ", Duplicates detected: " << duplicateMsgs << std::endl;
+ out << "Total Groups: " << statistics.size() << std::endl;
+ unsigned long consumers = 0;
+ for (GroupStatistics::iterator gs = statistics.begin(); gs != statistics.end(); ++gs) {
+ out << " GroupId: " << gs->first;
+ consumers += gs->second.size(); // # of consumers that processed this group
+ if (gs->second.size() == 1)
+ out << " completely consumed by a single client." << std::endl;
+ else
+ out << " consumed by " << gs->second.size() << " different clients." << std::endl;
+
+ for (ClientCounter::iterator cc = gs->second.begin(); cc != gs->second.end(); ++cc) {
+ out << " Client: " << cc->first << " consumed " << cc->second << " messages from the group." << std::endl;
+ }
+ }
+ out << "Average # of consumers per group: " << ((statistics.size() != 0) ? (double(consumers)/statistics.size()) : 0) << std::endl;
+ return out;
+ }
+};
+
+
+namespace {
+ // rand() is not thread safe. Create a singleton obj to hold a lock while calling
+ // rand() so it can be called safely by multiple concurrent clients.
+ class Randomizer {
+ qpid::sys::Mutex lock;
+ public:
+ uint operator()(uint max) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return (rand() % max) + 1;
+ }
+ };
+
+ static Randomizer randomizer;
+}
+
+
+// tag each generated message with a group identifer
+//
+class GroupGenerator {
+
+ const std::string groupPrefix;
+ const uint groupSize;
+ const bool randomizeSize;
+ const uint interleave;
+
+ uint groupSuffix;
+ uint total;
+
+ struct GroupState {
+ std::string id;
+ const uint size;
+ uint count;
+ GroupState( const std::string& i, const uint s )
+ : id(i), size(s), count(0) {}
+ };
+ typedef std::list<GroupState> GroupList;
+ GroupList groups;
+ GroupList::iterator current;
+
+ // add a new group identifier to the list
+ void newGroup() {
+ std::ostringstream groupId(groupPrefix, ios_base::out|ios_base::ate);
+ groupId << std::string(":") << groupSuffix++;
+ uint size = (randomizeSize) ? randomizer(groupSize) : groupSize;
+ QPID_LOG(trace, "New group: GROUPID=[" << groupId.str() << "] size=" << size << " this=" << this);
+ GroupState group( groupId.str(), size );
+ groups.push_back( group );
+ }
+
+public:
+ GroupGenerator( const std::string& prefix,
+ const uint t,
+ const uint size,
+ const bool randomize,
+ const uint i)
+ : groupPrefix(prefix), groupSize(size),
+ randomizeSize(randomize), interleave(i), groupSuffix(0), total(t)
+ {
+ QPID_LOG(trace, "New group generator: PREFIX=[" << prefix << "] total=" << total << " size=" << size << " rand=" << randomize << " interleave=" << interleave << " this=" << this);
+ for (uint i = 0; i < 1 || i < interleave; ++i) {
+ newGroup();
+ }
+ current = groups.begin();
+ }
+
+ bool genGroup(std::string& groupId, uint& seq, bool& eos)
+ {
+ if (!total) return false;
+ --total;
+ if (current == groups.end())
+ current = groups.begin();
+ groupId = current->id;
+ seq = current->count++;
+ if (current->count == current->size) {
+ QPID_LOG(trace, "Last msg for " << current->id << ", " << current->count << " this=" << this);
+ eos = true;
+ if (total >= interleave) { // need a new group to replace this one
+ newGroup();
+ groups.erase(current++);
+ } else ++current;
+ } else {
+ ++current;
+ eos = total < interleave; // mark eos on the last message of each group
+ }
+ QPID_LOG(trace, "SENDING GROUPID=[" << groupId << "] seq=" << seq << " eos=" << eos << " this=" << this);
+ return true;
+ }
+};
+
+
+
+class Client : public qpid::sys::Runnable
+{
+public:
+ typedef boost::shared_ptr<Client> shared_ptr;
+ enum State {ACTIVE, DONE, FAILURE};
+ Client( const std::string& n, const Options& o ) : name(n), opts(o), state(ACTIVE), stopped(false) {}
+ virtual ~Client() {}
+ State getState() { return state; }
+ void testFailed( const std::string& reason ) { state = FAILURE; error << "Client '" << name << "' failed: " << reason; }
+ void clientDone() { if (state == ACTIVE) state = DONE; }
+ qpid::sys::Thread& getThread() { return thread; }
+ const std::string getErrorMsg() { return error.str(); }
+ void stop() {stopped = true;}
+ const std::string& getName() { return name; }
+
+protected:
+ const std::string name;
+ const Options& opts;
+ qpid::sys::Thread thread;
+ ostringstream error;
+ State state;
+ bool stopped;
+};
+
+
+class Consumer : public Client
+{
+ GroupChecker& checker;
+
+public:
+ Consumer(const std::string& n, const Options& o, GroupChecker& c ) : Client(n, o), checker(c) {};
+ virtual ~Consumer() {};
+
+ void run()
+ {
+ Connection connection;
+ try {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = connection.createSession();
+ Receiver receiver = session.createReceiver(opts.address);
+ receiver.setCapacity(opts.capacity);
+ Message msg;
+ uint count = 0;
+
+ while (!stopped) {
+ if (receiver.fetch(msg, Duration::SECOND)) { // msg retrieved
+ qpid::types::Variant::Map& properties = msg.getProperties();
+ std::string groupId = properties[opts.groupKey];
+ uint groupSeq = properties[SN];
+ bool eof = properties[EOS];
+
+ QPID_LOG(trace, "RECVING GROUPID=[" << groupId << "] seq=" << groupSeq << " eos=" << eof << " name=" << name);
+
+ qpid::sys::usleep(10);
+
+ if (!checker.checkSequence( groupId, groupSeq, name )) {
+ ostringstream msg;
+ msg << "Check sequence failed. Group=" << groupId << " rcvd seq=" << groupSeq << " expected=" << checker.getNextExpectedSequence( groupId );
+ testFailed( msg.str() );
+ break;
+ } else if (eof) {
+ if (!checker.eraseGroup( groupId, name )) {
+ ostringstream msg;
+ msg << "Erase group failed. Group=" << groupId << " rcvd seq=" << groupSeq;
+ testFailed( msg.str() );
+ break;
+ }
+ }
+
+ ++count;
+ if (opts.ackFrequency && (count % opts.ackFrequency == 0)) {
+ session.acknowledge();
+ }
+ // Clear out message properties & content for next iteration.
+ msg = Message(); // TODO aconway 2010-12-01: should be done by fetch
+ } else if (checker.allMsgsConsumed()) // timed out, nothing else to do?
+ break;
+ }
+ session.acknowledge();
+ session.close();
+ connection.close();
+ } catch(const std::exception& error) {
+ ostringstream msg;
+ msg << "consumer error: " << error.what();
+ testFailed( msg.str() );
+ connection.close();
+ }
+ clientDone();
+ QPID_LOG(trace, "Consuming client " << name << " completed.");
+ }
+};
+
+
+
+class Producer : public Client
+{
+ GroupChecker& checker;
+ GroupGenerator generator;
+
+public:
+ Producer(const std::string& n, const Options& o, GroupChecker& c)
+ : Client(n, o), checker(c),
+ generator( n, o.messages, o.groupSize, o.randomizeSize, o.interleave )
+ {};
+ virtual ~Producer() {};
+
+ void run()
+ {
+ Connection connection;
+ try {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = connection.createSession();
+ Sender sender = session.createSender(opts.address);
+ if (opts.capacity) sender.setCapacity(opts.capacity);
+ Message msg;
+ msg.setDurable(opts.durable);
+ std::string groupId;
+ uint seq;
+ bool eos;
+ uint sent = 0;
+
+ qpid::sys::AbsTime start = qpid::sys::now();
+ int64_t interval = 0;
+ if (opts.sendRate) interval = qpid::sys::TIME_SEC/opts.sendRate;
+
+ while (!stopped && generator.genGroup(groupId, seq, eos)) {
+ msg.getProperties()[opts.groupKey] = groupId;
+ msg.getProperties()[SN] = seq;
+ msg.getProperties()[EOS] = eos;
+ checker.sendingSequence( groupId, seq, eos, name );
+
+ sender.send(msg);
+ ++sent;
+
+ if (opts.sendRate) {
+ qpid::sys::AbsTime waitTill(start, sent*interval);
+ int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill);
+ if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC);
+ }
+ }
+ session.sync();
+ session.close();
+ connection.close();
+ } catch(const std::exception& error) {
+ ostringstream msg;
+ msg << "producer '" << name << "' error: " << error.what();
+ testFailed(msg.str());
+ connection.close();
+ }
+ clientDone();
+ QPID_LOG(trace, "Producing client " << name << " completed.");
+ }
+};
+
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ int status = 0;
+ try {
+ Options opts;
+ if (opts.parse(argc, argv)) {
+
+ GroupChecker state( opts.messages,
+ opts.receivers,
+ opts.senders,
+ opts.allowDuplicates);
+ std::vector<Client::shared_ptr> clients;
+
+ if (opts.randomizeSize) srand((unsigned int)qpid::sys::SystemInfo::getProcessId());
+
+ // fire off the producers && consumers
+ for (size_t j = 0; j < opts.senders; ++j) {
+ ostringstream name;
+ name << opts.prefix << "P_" << j;
+ clients.push_back(Client::shared_ptr(new Producer( name.str(), opts, state )));
+ clients.back()->getThread() = qpid::sys::Thread(*clients.back());
+ }
+ for (size_t j = 0; j < opts.receivers; ++j) {
+ ostringstream name;
+ name << opts.prefix << "C_" << j;
+ clients.push_back(Client::shared_ptr(new Consumer( name.str(), opts, state )));
+ clients.back()->getThread() = qpid::sys::Thread(*clients.back());
+ }
+
+ // wait for all pubs/subs to finish.... or for consumers to fail or stall.
+ uint stalledTime = 0;
+ bool clientFailed = false;
+ while (!clientFailed && (!state.allMsgsPublished() || !state.allMsgsConsumed())) {
+ uint lastCount;
+
+ lastCount = state.getConsumedTotal();
+ qpid::sys::usleep( 1000000 );
+
+ // check each client for failures
+ for (std::vector<Client::shared_ptr>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
+ QPID_LOG(debug, "Client " << (*i)->getName() << " state=" << (*i)->getState());
+ if ((*i)->getState() == Client::FAILURE) {
+ QPID_LOG(error, argv[0] << ": test failed with client error: " << (*i)->getErrorMsg());
+ clientFailed = true;
+ break; // exit test.
+ }
+ }
+
+ // check for stalled consumers
+ if (!clientFailed && !state.allMsgsConsumed()) {
+ if (lastCount == state.getConsumedTotal()) {
+ if (++stalledTime >= opts.timeout) {
+ clientFailed = true;
+ break; // exit test
+ }
+ } else {
+ stalledTime = 0;
+ }
+ }
+ QPID_LOG(debug, "Consumed to date = " << state.getConsumedTotal() <<
+ " Published to date = " << state.getPublishedTotal() <<
+ " total=" << state.getTotalMessages());
+ }
+
+ if (clientFailed) {
+ if (stalledTime >= opts.timeout) {
+ QPID_LOG(error, argv[0] << ": test failed due to stalled consumer." );
+ status = 2;
+ } else {
+ status = 1;
+ }
+ }
+
+ // Wait for started threads.
+ for (std::vector<Client::shared_ptr>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
+ (*i)->stop();
+ (*i)->getThread().join();
+ }
+
+ if (opts.printReport && !status) state.print(std::cout);
+ } else status = 4;
+ } catch(const std::exception& error) {
+ QPID_LOG(error, argv[0] << ": " << error.what());
+ status = 3;
+ }
+ QPID_LOG(trace, "TEST DONE [" << status << "]");
+
+ return status;
+}
diff --git a/qpid/cpp/src/tests/multiq_perftest b/qpid/cpp/src/tests/multiq_perftest
new file mode 100755
index 0000000000..9673dd2e6d
--- /dev/null
+++ b/qpid/cpp/src/tests/multiq_perftest
@@ -0,0 +1,22 @@
+#!/usr/bin/env 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.
+#
+
+exec `dirname $0`/run_perftest 10000 --mode shared --qt 16
diff --git a/qpid/cpp/src/tests/perfdist b/qpid/cpp/src/tests/perfdist
new file mode 100755
index 0000000000..4049b410ff
--- /dev/null
+++ b/qpid/cpp/src/tests/perfdist
@@ -0,0 +1,87 @@
+#!/usr/bin/env 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/qpid/cpp/src/tests/ping_broker b/qpid/cpp/src/tests/ping_broker
new file mode 100755
index 0000000000..bdf48f3358
--- /dev/null
+++ b/qpid/cpp/src/tests/ping_broker
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os
+from optparse import OptionParser, OptionGroup
+import sys
+import locale
+import socket
+import re
+from qpid.messaging import Connection
+
+home = os.environ.get("QPID_TOOLS_HOME", os.path.normpath("/usr/share/qpid-tools"))
+sys.path.append(os.path.join(home, "python"))
+
+from qpidtoollibs import BrokerAgent
+from qpidtoollibs import Display, Header, Sorter, YN, Commas, TimeLong
+
+
+class Config:
+ def __init__(self):
+ self._host = "localhost"
+ self._connTimeout = 10
+
+config = Config()
+conn_options = {}
+
+def OptionsAndArguments(argv):
+ """ Set global variables for options, return arguments """
+
+ global config
+ global conn_options
+
+ usage = "%prog [options]"
+
+ parser = OptionParser(usage=usage)
+
+ parser.add_option("-b", "--broker", action="store", type="string", default="localhost", metavar="<url>",
+ help="URL of the broker to query")
+ parser.add_option("-t", "--timeout", action="store", type="int", default=10, metavar="<secs>",
+ help="Maximum time to wait for broker connection (in seconds)")
+ parser.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>",
+ help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD5, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.")
+ parser.add_option("--ssl-certificate", action="store", type="string", metavar="<cert>", help="Client SSL certificate (PEM Format)")
+ parser.add_option("--ssl-key", action="store", type="string", metavar="<key>", help="Client SSL private key (PEM Format)")
+ parser.add_option("--ssl-trustfile", action="store", type="string", metavar="<CA>", help="List of trusted CAs (PEM Format)")
+ parser.add_option("--ssl-skip-hostname-check", action="store_true",
+ help="Do not validate hostname in peer certificate")
+ parser.add_option("--ha-admin", action="store_true", help="Allow connection to a HA backup broker.")
+
+ opts, args = parser.parse_args(args=argv)
+
+ config._host = opts.broker
+ config._connTimeout = opts.timeout
+
+ if opts.sasl_mechanism:
+ conn_options['sasl_mechanisms'] = opts.sasl_mechanism
+ if opts.ssl_certificate:
+ conn_options['ssl_certfile'] = opts.ssl_certificate
+ if opts.ssl_key:
+ conn_options['ssl_key'] = opts.ssl_key
+ if opts.ssl_trustfile:
+ conn_options['ssl_trustfile'] = opts.ssl_trustfile
+ if opts.ssl_skip_hostname_check:
+ conn_options['ssl_skip_hostname_check'] = True
+ if opts.ha_admin:
+ conn_options['client_properties'] = {'qpid.ha-admin' : 1}
+ return args
+
+class BrokerManager:
+ def __init__(self):
+ self.brokerName = None
+ self.connection = None
+ self.broker = None
+ self.cluster = None
+
+ def SetBroker(self, brokerUrl):
+ self.url = brokerUrl
+ self.connection = Connection.establish(self.url, **conn_options)
+ self.broker = BrokerAgent(self.connection)
+
+ def Disconnect(self):
+ """ Release any allocated brokers. Ignore any failures as the tool is
+ shutting down.
+ """
+ try:
+ connection.close()
+ except:
+ pass
+
+ def Ping(self, args):
+ for sequence in range(10):
+ result = self.broker.echo(sequence, "ECHO BODY")
+ if result['sequence'] != sequence:
+ raise Exception("Invalid Sequence")
+
+
+def main(argv=None):
+
+ args = OptionsAndArguments(argv)
+ bm = BrokerManager()
+
+ try:
+ bm.SetBroker(config._host)
+ bm.Ping(args)
+ bm.Disconnect()
+ return 0
+ except KeyboardInterrupt:
+ print
+ except Exception,e:
+ print "Failed: %s - %s" % (e.__class__.__name__, e)
+
+ bm.Disconnect() # try to deallocate brokers
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/qpid/cpp/src/tests/policies.py b/qpid/cpp/src/tests/policies.py
new file mode 100644
index 0000000000..ec0191f91e
--- /dev/null
+++ b/qpid/cpp/src/tests/policies.py
@@ -0,0 +1,209 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from qpid.tests.messaging.implementation import *
+from qpid.tests.messaging import VersionTest
+
+class Mgmt:
+ """
+ Simple QMF management utility (qpidtoollibs uses
+ qpid.messaging.Message rather than swigged version)
+ """
+ def __init__(self, conn):
+ self.conn = conn
+ self.sess = self.conn.session()
+ self.reply_to = "qmf.default.topic/direct.%s;{node:{type:topic}, link:{x-declare:{auto-delete:True,exclusive:True}}}" % \
+ str(uuid4())
+ self.reply_rx = self.sess.receiver(self.reply_to)
+ self.reply_rx.capacity = 10
+ self.tx = self.sess.sender("qmf.default.direct/broker")
+ self.next_correlator = 1
+
+ def list(self, class_name):
+ props = {'method' : 'request',
+ 'qmf.opcode' : '_query_request',
+ 'x-amqp-0-10.app-id' : 'qmf2'}
+ correlator = str(self.next_correlator)
+ self.next_correlator += 1
+
+ content = {'_what' : 'OBJECT',
+ '_schema_id' : {'_class_name' : class_name.lower()}}
+
+ message = Message(content, reply_to=self.reply_to, correlation_id=correlator,
+ properties=props, subject="broker")
+ self.tx.send(message)
+
+
+ response = self.reply_rx.fetch(10)
+ if response.properties['qmf.opcode'] != '_query_response':
+ raise Exception("bad response")
+ items = []
+ done = False
+ while not done:
+ for item in response.content:
+ items.append(item['_values'])
+ if 'partial' in response.properties:
+ response = self.reply_rx.fetch(10)
+ else:
+ done = True
+ self.sess.acknowledge()
+ return items
+
+ def do_qmf_method(self, method, arguments, addr="org.apache.qpid.broker:broker:amqp-broker", timeout=10):
+ props = {'method' : 'request',
+ 'qmf.opcode' : '_method_request',
+ 'x-amqp-0-10.app-id' : 'qmf2'}
+ correlator = str(self.next_correlator)
+ self.next_correlator += 1
+
+ content = {'_object_id' : {'_object_name' : addr},
+ '_method_name' : method,
+ '_arguments' : arguments}
+
+ message = Message(content, reply_to=self.reply_to, correlation_id=correlator,
+ properties=props, subject="broker")
+ self.tx.send(message)
+ response = self.reply_rx.fetch(timeout)
+ self.sess.acknowledge()
+ if response.properties['qmf.opcode'] == '_exception':
+ raise Exception("Exception from Agent: %r" % response.content['_values'])
+ if response.properties['qmf.opcode'] != '_method_response':
+ raise Exception("bad response: %r" % response.properties)
+ return response.content['_arguments']
+
+ def create(self, _type, name, properties={}):
+ return self.do_qmf_method('create', {'type': _type, 'name': name, 'properties': properties})
+
+ def delete(self, _type, name):
+ return self.do_qmf_method('delete', {'type': _type, 'name': name})
+
+
+class PoliciesTests (VersionTest):
+ """
+ Tests for node policies with qpidd
+ """
+
+ def do_simple_queue_test(self, pattern, name, properties={}, autodeleted=True):
+ mgmt = self.create_connection("amqp0-10", True)
+ agent = Mgmt(mgmt)
+ agent.create('QueuePolicy', pattern, properties)
+ try:
+ snd = self.ssn.sender(name)
+ msgs = [Message(content=s, subject = s) for s in ['a','b','c','d']]
+ for m in msgs: snd.send(m)
+ snd.close()
+
+ for expected in msgs:
+ rcv = self.ssn.receiver(name)
+ msg = rcv.fetch(0)
+ assert msg.content == expected.content, (msg.content, expected.content)
+ self.ssn.acknowledge()
+ rcv.close() #close after each message to ensure queue isn't deleted with messages in it
+ self.ssn.close()
+ self.conn.close()
+
+ matched = [q for q in agent.list("Queue") if q['name'] == name]
+ if autodeleted:
+ # ensure that queue is no longer there (as empty and unused)
+ assert len(matched) == 0, (matched)
+ else:
+ # ensure that queue is still there though empty and unused
+ assert len(matched) == 1, (matched)
+ finally:
+ agent.delete('QueuePolicy', pattern)
+ mgmt.close()
+
+ def test_queue(self):
+ self.do_simple_queue_test("queue-*", "queue-1")
+
+ def test_queue_not_autodeleted(self):
+ self.do_simple_queue_test("permanent-queue-*", "permanent-queue-1", {'auto-delete':False}, False)
+
+ def test_queue_manual_delete(self):
+ self.do_simple_queue_test("permanent-queue-*", "permanent-queue-1", {'qpid.lifetime-policy':'manual'}, False)
+
+ def test_queue_delete_if_unused_and_empty(self):
+ self.do_simple_queue_test("queue-*", "queue-1", {'qpid.lifetime-policy':'delete-if-unused-and-empty'}, True)
+
+ def do_simple_topic_test(self, pattern, name, properties={}, autodeleted=True):
+ mgmt = self.create_connection("amqp0-10", True)
+ agent = Mgmt(mgmt)
+ agent.create('TopicPolicy', pattern, properties)
+ try:
+ snd = self.ssn.sender(name)
+ rcv1 = self.ssn.receiver(name)
+ rcv2 = self.ssn.receiver(name)
+
+ msgs = [Message(content=s, subject = s) for s in ['a','b','c','d']]
+ for m in msgs: snd.send(m)
+
+ for rcv in [rcv1, rcv2]:
+ for expected in msgs:
+ msg = rcv.fetch(0)
+ assert msg.content == expected.content, (msg.content, expected.content)
+ self.ssn.acknowledge()
+ rcv1.close()
+ rcv2.close()
+ snd.close()
+
+ matched = [e for e in agent.list("Exchange") if e['name'] == name]
+ if autodeleted:
+ # ensure that exchange is no longer there (as it is now unused)
+ assert len(matched) == 0, (matched)
+ else:
+ # ensure that exchange has not been autodeleted in spite of being unused
+ assert len(matched) == 1, (matched)
+ finally:
+ agent.delete('TopicPolicy', pattern)
+ mgmt.close()
+
+ def test_topic(self):
+ self.do_simple_topic_test('fanout-*', 'fanout-1', {'exchange-type':'fanout'})
+
+ def test_topic_not_autodelete(self):
+ self.do_simple_topic_test('permanent-fanout-*', 'permanent-fanout-1', {'exchange-type':'fanout', 'auto-delete':False}, False)
+
+ def test_topic_manual_delete(self):
+ self.do_simple_topic_test('permanent-fanout-*', 'permanent-fanout-1', {'exchange-type':'fanout', 'qpid.lifetime-policy':'manual'}, False)
+
+ def test_topic_delete_if_unused(self):
+ self.do_simple_topic_test('fanout-*', 'fanout-1', {'exchange-type':'fanout', 'qpid.lifetime-policy':'delete-if-unused'}, True)
+
+ def test_mgmt(self):
+ mgmt = self.create_connection("amqp0-10", True)
+ agent = Mgmt(mgmt)
+ agent.create('QueuePolicy', 'queue-*')
+ agent.create('QueuePolicy', 'alt.queue.*')
+ agent.create('TopicPolicy', 'topic-*')
+ try:
+ queues = [q['name'] for q in agent.list("QueuePolicy")]
+ topics = [t['name'] for t in agent.list("TopicPolicy")]
+ assert 'queue-*' in queues, (queues)
+ assert 'alt.queue.*' in queues, (queues)
+
+ try:
+ agent.delete('TopicPolicy', 'queue-*')
+ assert False, ('Deletion of policy using wrong type should fail')
+ except: None
+
+ finally:
+ agent.delete('QueuePolicy', 'queue-*')
+ agent.delete('QueuePolicy', 'alt.queue.*')
+ agent.delete('TopicPolicy', 'topic-*')
+ mgmt.close()
diff --git a/qpid/cpp/src/tests/policy.acl b/qpid/cpp/src/tests/policy.acl
new file mode 100644
index 0000000000..4c13ac75c1
--- /dev/null
+++ b/qpid/cpp/src/tests/policy.acl
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+acl allow all all
diff --git a/qpid/cpp/src/tests/publish.cpp b/qpid/cpp/src/tests/publish.cpp
new file mode 100644
index 0000000000..3f456e7588
--- /dev/null
+++ b/qpid/cpp/src/tests/publish.cpp
@@ -0,0 +1,135 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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;
+
+namespace qpid {
+namespace tests {
+
+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;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+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/qpid/cpp/src/tests/python_tests b/qpid/cpp/src/tests/python_tests
new file mode 100755
index 0000000000..a36839a43c
--- /dev/null
+++ b/qpid/cpp/src/tests/python_tests
@@ -0,0 +1,34 @@
+#!/usr/bin/env 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 python tests.
+source $QPID_TEST_COMMON
+ensure_python_tests
+QPID_PORT=${QPID_PORT:-5672}
+PYTHON_TESTS=${PYTHON_TESTS:-$*}
+FAILING=${FAILING:-/dev/null}
+
+if [ ! -d $QPID_TESTS ]; then
+ echo "SKIPPED python tests: test code not found"
+ exit 0
+fi
+
+python $QPID_PYTHON_TEST -m qpid_tests.broker_0_10 -m qpid.tests -b localhost:$QPID_PORT -I $FAILING $PYTHON_TESTS || exit 1
diff --git a/qpid/cpp/src/tests/python_tests.ps1 b/qpid/cpp/src/tests/python_tests.ps1
new file mode 100644
index 0000000000..f7caa8f75a
--- /dev/null
+++ b/qpid/cpp/src/tests/python_tests.ps1
@@ -0,0 +1,42 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Run the python tests; intended to be run by run_test.ps1 which sets up
+# QPID_PORT
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping python tests as python libs not found"
+ exit 1
+}
+
+. .\test_env.ps1
+
+if (Test-Path env:FAILING) {
+ $fails = "-I $env:FAILING"
+}
+if (Test-Path env:PYTHON_TESTS) {
+ $tests = "$env:PYTHON_TESTS"
+}
+else {
+ $tests = "$args"
+}
+
+python $PYTHON_DIR/qpid-python-test -m qpid_tests.broker_0_10 -m qpid.tests -b localhost:$env:QPID_PORT $fails $tests
+exit $LASTEXITCODE
diff --git a/qpid/cpp/src/tests/qpid-analyze-trace b/qpid/cpp/src/tests/qpid-analyze-trace
new file mode 100755
index 0000000000..009fbc441c
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-analyze-trace
@@ -0,0 +1,258 @@
+#!/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 datetime import datetime
+from optparse import OptionParser
+
+# Version of this tool software
+MAJOR_VERSION = 1
+MINOR_VERSION = 1
+# === Version history ===
+# 2011-11-16 1.1: Bugfixs:
+# QPID-3623 - Incorrect handling of transactions
+# QPID-3624 - Replace argparse lib with optparse so tool can be used on Python 2.6.
+# 2011-11-07 1.0: Initial checkin
+# QPID-3579: Initial version checked in
+
+
+# AMQP 0-10 commands - these increment the command counter
+EXEC_COMMANDS = ["ExecutionSync", "ExecutionResult", "ExecutionException", "MessageTransfer", "MessageAccept",
+ "MessageReject", "MessageRelease", "MessageAcquire", "MessageResume", "MessageSubscribe",
+ "MessageCancel", "MessageSetFlowMode", "MessageFlow", "MessageFlush", "MessageStop", "TxSelect",
+ "TxCommit", "TxRollback", "DtxSelect", "DtxStart", "DtxEnd", "DtxCommit", "DtxForget", "DtxGetTimeout",
+ "DtxPrepare", "DtxRecover", "DtxRollback", "DtxSetTimeout", "ExchangeDeclare", "ExchangeDelete",
+ "ExchangeQuery", "ExchangeBind", "ExchangeUnbind", "ExchangeBound", "QueueDeclare", "QueueDelete",
+ "QueuePurge", "QueueQuery", "FileQos", "FileQosOk", "FileConsume", "FileConsumeOk", "FileCancel",
+ "FileOpen", "FileOpenOk", "FileStage", "FilePublish", "FileReturn", "FileDeliver", "FileAck",
+ "FileReject", "StreamQos", "StreamQosOk", "StreamConsume", "StreamConsumeOk", "StreamCancel",
+ "StreamPublish", "StreamReturn", "StreamDeliver"]
+HEADER_STR = " -line ----------timestamp -----------connection ssn recv send- txn-- operation---------->"
+
+PROGRESS_LINES_PER_DOT = 100000
+
+class LogLevel:
+ CRITICAL = (1, "critical")
+ ERROR = (2, "error")
+ WARNING = (3, "warning")
+ NOTICE = (4, "notice")
+ INFO = (5, "info")
+ DEBUG = (6, "debug")
+ TRACE = (7, "trace")
+ @staticmethod
+ def get_level(level):
+ if level == LogLevel.CRITICAL[1]: return LogLevel.CRITICAL
+ if level == LogLevel.ERROR[1]: return LogLevel.ERROR
+ if level == LogLevel.WARNING[1]: return LogLevel.WARNING
+ if level == LogLevel.NOTICE[1]: return LogLevel.NOTICE
+ if level == LogLevel.INFO[1]: return LogLevel.INFO
+ if level == LogLevel.DEBUG[1]: return LogLevel.DEBUG
+ if level == LogLevel.TRACE[1]: return LogLevel.TRACE
+ raise Exception("Unknown log level: %s" % level)
+
+class LogLine:
+ def __init__(self, line_no, line):
+ self.line_no = line_no
+ self.timestamp = datetime.strptime(line[:19], "%Y-%m-%d %H:%M:%S")
+ self.level = LogLevel.get_level(line[20:].split(" ")[0])
+ self.line = line[21 + len(self.level[1]):].strip()
+ self.cmd_cnt = None
+ self.txn_cnt = None
+ def __str__(self):
+ if self.contains("RECV"): cnt_str = "R"
+ else: cnt_str = " S"
+ if self.cmd_cnt is not None: cnt_str += str(self.cmd_cnt)
+ set_index = self.find("{")
+ header_index = self.find("header")
+ content_index = self.find("content")
+ if self.txn_cnt is None:
+ txn_cnt_str = ""
+ else:
+ txn_cnt_str = "T%d" % self.txn_cnt
+ if header_index != -1 and header_index < set_index: op_str = " + " + self.line[header_index:self.line.rfind("]")]
+ elif content_index != -1 and set_index == -1: op_str = " + " + self.line[content_index:self.line.rfind("]")]
+ else: op_str = self.line[set_index+1:self.line.rfind("}")]
+ return " %7d %19s %22s %3d %-10s %-5s %s" % (self.line_no, self.timestamp.isoformat(" "),
+ self.get_identifier_remote_addr(), self.get_channel(),
+ cnt_str, txn_cnt_str, op_str)
+ def contains(self, string):
+ return self.line.find(string) != -1
+ def find(self, string):
+ return self.line.find(string)
+ def get_channel(self):
+ return int(self.get_named_value("channel"))
+ def get_identifier(self):
+ return self.line.partition("[")[2].partition("]")[0]
+ def get_identifier_remote_addr(self):
+ return self.get_identifier().partition("-")[2]
+ def get_named_value(self, name):
+ return self.line.partition("%s=" % name)[2].partition(";")[0]
+ def get_msg_accept_range(self):
+ str_nums = self.get_named_value("transfers").strip(" {[]}").split(",")
+ return range(int(str_nums[0]), int(str_nums[1]) + 1)
+ def is_log_level(self, level):
+ if self.level is None: return None
+ return level[0] == self.level[0]
+ def is_frame(self):
+ return self.contains("Frame[")
+
+class ConnectionProperty:
+ def __init__(self, line):
+ self.addr = line.get_identifier_remote_addr()
+ self.channel = line.get_channel()
+ self.ops = [line]
+ def add_op(self, line):
+ self.ops.append(line)
+
+class Connection(ConnectionProperty):
+ def __init__(self, line):
+ ConnectionProperty.__init__(self, line)
+ self.session_list = [] # Keeps session creation order
+ self.session_dict = {} # For looking up by channel no.
+ def __str__(self):
+ return "Connection %s (ops=%d; sessions=%d):" % (self.addr, len(self.ops), len(self.session_dict))
+ def add_session(self, session):
+ self.session_list.append(session)
+ self.session_dict[session.channel] = session
+ def get_session(self, channel):
+ return self.session_dict[channel]
+
+class Session(ConnectionProperty):
+ def __init__(self, line):
+ ConnectionProperty.__init__(self, line)
+ self.name = line.get_named_value("name")
+ self.send_cnt = 0
+ self.recv_cnt = 0
+ self.txn_flag = False
+ self.txn_cnt = 0
+ self.recv_cmds = {} # For looking up by cmd no
+ self.send_cmds = {} # For looking up by cmd no
+ def __str__(self):
+ if self.txn_flag:
+ return " + Session %d (name=%s send-cmds=%d recv-cmds=%d txns=%d):" % (self.channel, self.name,
+ self.send_cnt, self.recv_cnt,
+ self.txn_cnt)
+ return " + Session %d (name=%s send-cmds=%d recv-cmds=%d non-txn):" % (self.channel, self.name, self.send_cnt,
+ self.recv_cnt)
+ def incr_recv_cnt(self, line):
+ self.recv_cmds[self.recv_cnt] = line
+ self.recv_cnt += 1
+ def incr_send_cnt(self, line):
+ self.send_cmds[self.send_cnt] = line
+ self.send_cnt += 1
+ def set_send_txn_cnt(self, cmd):
+ self.send_cmds[cmd].txn_cnt = self.txn_cnt
+
+class TraceAnalysis:
+ def __init__(self):
+ self.connection_list = [] # Keeps connection creation order
+ self.connection_dict = {} # For looking up by connection address
+ parser = OptionParser(usage="%prog [options] trace-file", version="%%prog %d.%d" % (MAJOR_VERSION, MINOR_VERSION),
+ description="A tool to structure and display Qpid broker trace logs.")
+ parser.add_option("--connection-summary", action="store_true", default=False, dest="connection_summary",
+ help="Hide connection details, provide one-line summary")
+ parser.add_option("--session-summary", action="store_true", default=False, dest="session_summary",
+ help="Hide session details, provide one-line summary")
+ parser.add_option("--summary", "-s", action="store_true", default=False, dest="summary",
+ help="Hide both connection and session details. Equivalent to --connection-summary and"
+ "--session-summary")
+ self.opts, self.args = parser.parse_args()
+ if len(self.args) == 0: raise Exception("Missing trace-file argument")
+ def analyze_trace(self):
+ lcnt = 0
+ print "Reading trace file %s:" % self.args[0]
+ log_file = open(self.args[0], "r")
+ try:
+ for fline in log_file:
+ lcnt += 1
+ try:
+ lline = LogLine(lcnt, fline)
+ if lline.is_log_level(LogLevel.TRACE) and lline.is_frame():
+ if lline.contains("{ConnectionStartBody"):
+ conn = Connection(lline)
+ self.connection_list.append(conn)
+ self.connection_dict[conn.addr] = conn
+ elif lline.contains("{Connection"):
+ self.connection_dict[lline.get_identifier_remote_addr()].add_op(lline)
+ elif lline.contains("{SessionAttachBody"):
+ ssn = Session(lline)
+ self.connection_dict[ssn.addr].add_session(ssn)
+ else:
+ ssn = self.connection_dict[lline.get_identifier_remote_addr()].get_session(lline.get_channel())
+ ssn.add_op(lline)
+ if lline.line[lline.find("{") + 1 : lline.find("Body")] in EXEC_COMMANDS:
+ if lline.contains("RECV"):
+ lline.cmd_cnt = ssn.recv_cnt
+ if ssn.txn_flag:
+ if lline.contains("MessageAcceptBody"):
+ lline.txn_cnt = ssn.txn_cnt
+ for cmd in lline.get_msg_accept_range():
+ ssn.set_send_txn_cnt(cmd)
+ if lline.contains("MessageTransferBody"): lline.txn_cnt = ssn.txn_cnt
+ ssn.incr_recv_cnt(lline)
+ elif lline.contains("SEND") or lline.contains("SENT"):
+ lline.cmd_cnt = ssn.send_cnt
+ ssn.incr_send_cnt(lline)
+ # TODO: This treatment will probably break down for DTX
+ if lline.contains("xSelectBody"):
+ ssn.txn_flag = True
+ elif lline.contains("xCommitBody") or lline.contains("xRollbackBody"):
+ lline.txn_cnt = ssn.txn_cnt
+ ssn.txn_cnt += 1
+ except KeyboardInterrupt, e: raise e
+ except: pass
+ if (lcnt + 1) % PROGRESS_LINES_PER_DOT == 0:
+ sys.stdout.write(".")
+ sys.stdout.flush()
+ finally: log_file.close()
+ if lcnt > PROGRESS_LINES_PER_DOT: print
+ print "Read and analyzed", lcnt, "lines."
+ def print_analysis(self):
+ if len(self.connection_list) > 0:
+ for c in self.connection_list:
+ print
+ print c
+ if not self.opts.connection_summary and not self.opts.summary:
+ print HEADER_STR
+ for o in c.ops:
+ print o
+ for s in c.session_list:
+ print s
+ if not self.opts.session_summary and not self.opts.summary:
+ print HEADER_STR
+ for o in s.ops:
+ print o
+ else:
+ print "No trace-level entries found in log."
+
+def check_python_version(major, minor, micro):
+ if sys.version_info < (major, minor, micro):
+ print "Incorrect Python version: %s found; >= %d.%d.%d needed." % (sys.version.split()[0], major, minor, micro)
+ sys.exit(-1)
+
+# === Main program ===
+
+if __name__ == '__main__':
+ check_python_version(2, 4, 0)
+ t = TraceAnalysis()
+ t.analyze_trace()
+ t.print_analysis()
+ \ No newline at end of file
diff --git a/qpid/cpp/src/tests/qpid-build-rinstall b/qpid/cpp/src/tests/qpid-build-rinstall
new file mode 100755
index 0000000000..beff7dffba
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-build-rinstall
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under onemake
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 "make install"" locally then copy the install tree to each of $HOSTS
+# Must be run in a configured qpid build directory.
+#
+test -f config.status || { echo "Not in a configured build directory."; usage; }
+. src/tests/install_env.sh
+set -ex
+make && make -j1 install
+rsynchosts $QPID_INSTALL_PREFIX
diff --git a/qpid/cpp/src/tests/qpid-client-test.cpp b/qpid/cpp/src/tests/qpid-client-test.cpp
new file mode 100644
index 0000000000..9198324f93
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-client-test.cpp
@@ -0,0 +1,139 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/**
+ * 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/client/SubscriptionManager.h"
+
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public TestOptions {
+ uint msgSize;
+ bool verbose;
+
+ Args() : TestOptions("Simple test of Qpid c++ client; sends and receives a single message."), msgSize(26), verbose(false)
+ {
+ 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;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+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);
+
+ // Using the SubscriptionManager, get the message from the queue.
+ SubscriptionManager subs(session);
+ Message msgIn = subs.get("MyQueue");
+ if (msgIn.getData() == msgOut.getData())
+ if (opts.verbose) std::cout << "Received the exepected message." << std::endl;
+
+ //close the session & connection
+ session.close();
+ if (opts.verbose) std::cout << "Closed session." << std::endl;
+ connection.close();
+ 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/qpid/cpp/src/tests/qpid-cluster-benchmark b/qpid/cpp/src/tests/qpid-cluster-benchmark
new file mode 100755
index 0000000000..f20ac6ac30
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-cluster-benchmark
@@ -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.
+#
+
+# Benchmark script for comparing cluster performance.
+
+# Default options
+MESSAGES="-m 10000"
+REPEAT="--repeat 10"
+QUEUES="-q 6"
+SENDERS="-s 3"
+RECEIVERS="-r 3"
+BROKERS= # Local broker
+CLIENT_HOSTS= # No ssh, all clients are local
+# Connection options
+TCP_NODELAY=false
+RECONNECT=true
+HEARTBEAT=1
+
+while getopts "m:f:n:b:q:s:r:c:h:i:txyv-" opt; do
+ case $opt in
+ b) BROKERS="-b $OPTARG";;
+ c) CLIENT_HOSTS="-c $OPTARG";;
+ h) HEARTBEAT=$OPTARG;;
+ i) RECONNECT=$OPTARG;;
+ m) MESSAGES="-m $OPTARG";;
+ n) REPEAT="--repeat $OPTARG";;
+ q) QUEUES="-q $OPTARG";;
+ r) RECEIVERS="-r $OPTARG";;
+ s) SENDERS="-s $OPTARG";;
+ t) TCP_NODELAY=true;;
+ v) OPTS="--verbose";;
+ x) SAVE_RECEIVED="--save-received";;
+ y) NO_DELETE="--no-delete";;
+ -) break ;;
+ *) echo "Unknown option"; exit 1;;
+ esac
+done
+shift $(($OPTIND-1))
+
+CONNECTION_OPTIONS="--connection-options {tcp-nodelay:$TCP_NODELAY,reconnect:$RECONNECT,heartbeat:$HEARTBEAT}"
+
+BROKER=$(echo $BROKERS | sed s/,.*//)
+run_test() { echo $*; shift; "$@"; echo; echo; echo; }
+
+OPTS="$OPTS $REPEAT $BROKERS --summarize $QUEUES $SENDERS $RECEIVERS $MESSAGES $CLIENT_HOSTS $SAVE_RECEIVED $CONNECTION_OPTIONS $NO_DELETE"
+
+run_test "Benchmark:" qpid-cpp-benchmark $OPTS "$@"
diff --git a/qpid/cpp/src/tests/qpid-cpp-benchmark b/qpid/cpp/src/tests/qpid-cpp-benchmark
new file mode 100755
index 0000000000..2d5ec711fe
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-cpp-benchmark
@@ -0,0 +1,363 @@
+#!/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 optparse, time, re, os
+
+try:
+ import qpid_messaging as qm
+except ImportError:
+ qpid_messaging = None
+ import qpid.messaging as qm
+
+from threading import Thread
+from subprocess import Popen, PIPE, STDOUT
+
+op = optparse.OptionParser(usage="usage: %prog [options]",
+ description="simple performance benchmarks")
+op.add_option("-b", "--broker", default=[], action="append", type="str",
+ help="url of broker(s) to connect to, round robin on multiple brokers")
+op.add_option("-c", "--client-host", default=[], action="append", type="str",
+ help="host(s) to run clients on via ssh, round robin on mulple hosts")
+op.add_option("-q", "--queues", default=1, type="int", metavar="N",
+ help="create N queues (default %default)")
+op.add_option("-s", "--senders", default=1, type="int", metavar="N",
+ help="start N senders per queue (default %default)")
+op.add_option("-r", "--receivers", default=1, type="int", metavar="N",
+ help="start N receivers per queue (default %default)")
+op.add_option("-m", "--messages", default=100000, type="int", metavar="N",
+ help="send N messages per sender (default %default)")
+op.add_option("--queue-name", default="benchmark", metavar="NAME",
+ help="base name for queues (default %default)")
+op.add_option("--send-rate", default=0, metavar="N",
+ help="send rate limited to N messages/second, 0 means no limit (default %default)")
+op.add_option("--receive-rate", default=0, metavar="N",
+ help="receive rate limited to N messages/second, 0 means no limit (default %default)")
+op.add_option("--content-size", default=1024, type="int", metavar="BYTES",
+ help="message size in bytes (default %default)")
+op.add_option("--ack-frequency", default=100, metavar="N", type="int",
+ help="receiver ack's every N messages, 0 means unconfirmed (default %default)")
+op.add_option("--tx", default=0, metavar="N", type="int",
+ help="Transaction batch size, 0 means no transactions")
+op.add_option("--no-report-header", dest="report_header", default=True,
+ action="store_false", help="don't print header on report")
+op.add_option("--summarize", default=False, action="store_true",
+ help="print summary statistics for multiple senders/receivers: total throughput, average latency")
+op.add_option("--repeat", default=1, metavar="N", help="repeat N times", type="int")
+op.add_option("--send-option", default=[], action="append", type="str",
+ help="Additional option for sending addresses")
+op.add_option("--receive-option", default=[], action="append", type="str",
+ help="Additional option for receiving addresses")
+op.add_option("--create-option", default=[], action="append", type="str",
+ help="Additional option for creating addresses")
+op.add_option("--send-arg", default=[], action="append", type="str",
+ help="Additional argument for qpid-send")
+op.add_option("--receive-arg", default=[], action="append", type="str",
+ help="Additional argument for qpid-receive")
+op.add_option("--no-timestamp", dest="timestamp", default=True,
+ action="store_false", help="don't add a timestamp, no latency results")
+op.add_option("--sequence", dest="sequence", default=False,
+ action="store_true", help="add a sequence number to each message")
+op.add_option("--connection-options", type="str",
+ help="Connection options for senders & receivers")
+op.add_option("--durable", default=False, action="store_true",
+ help="Use durable queues and messages")
+op.add_option("-t", "--timeout", default=1.0, type="float", metavar="SECONDS",
+ help="Timeout for fetch operations (default %default)")
+op.add_option("--save-received", default=False, action="store_true",
+ help="Save received message content to files <queuename>-receiver-<n>.msg")
+op.add_option("--verbose", default=False, action="store_true",
+ help="Show commands executed")
+op.add_option("--fill-drain", default=False, action="store_true",
+ help="First fill the queues, then drain them")
+op.add_option("--qpid-send-path", default="", type="str", metavar="PATH",
+ help="path to qpid-send binary")
+op.add_option("--qpid-receive-path", default="", type="str", metavar="PATH",
+ help="path to qpid-receive binary")
+
+single_quote_re = re.compile("'")
+def posix_quote(string):
+ """ Quote a string for use as an argument in a posix shell"""
+ return "'" + single_quote_re.sub("\\'", string) + "'";
+
+def ssh_command(host, command):
+ """ Convert command into an ssh command on host with quoting"""
+ return ["ssh", host] + [posix_quote(arg) for arg in command]
+
+class Clients:
+ def __init__(self): self.clients=[]
+
+ def add(self, client):
+ self.clients.append(client)
+ return client
+
+ def kill(self):
+ for c in self.clients:
+ try: c.kill()
+ except: pass
+
+class PopenCommand(Popen):
+ """Like Popen but you can query for the command"""
+ def __init__(self, command, *args, **kwargs):
+ self.command = command
+ Popen.__init__(self, command, *args, **kwargs)
+
+clients = Clients()
+
+def start_receive(queue, index, opts, ready_queue, broker, host):
+ address_opts=opts.receive_option
+ if opts.durable: address_opts += ["node:{durable:true}"]
+ address="%s;{%s}"%(queue,",".join(address_opts))
+ msg_total=opts.senders*opts.messages
+ messages = msg_total/opts.receivers;
+ if (index < msg_total%opts.receivers): messages += 1
+ if (messages == 0): return None
+ command = [os.path.join(opts.qpid_receive_path, "qpid-receive"),
+ "-b", broker,
+ "-a", address,
+ "-m", str(messages),
+ "--forever",
+ "--print-content=no",
+ "--receive-rate", str(opts.receive_rate),
+ "--report-total",
+ "--ack-frequency", str(opts.ack_frequency),
+ "--ready-address", "%s;{create:always}"%ready_queue,
+ "--report-header=no",
+ "--tx=%s" % opts.tx
+ ]
+ if opts.save_received:
+ command += ["--save-content=%s-receiver-%s.msg"%(queue,index)]
+ command += opts.receive_arg
+ if opts.connection_options:
+ command += ["--connection-options",opts.connection_options]
+ if host: command = ssh_command(host, command)
+ if opts.verbose: print "Receiver: ", command
+ return clients.add(PopenCommand(command, stdout=PIPE, stderr=PIPE))
+
+def start_send(queue, opts, broker, host):
+ address="%s;{%s}"%(queue,",".join(opts.send_option + ["create:always"]))
+ command = [os.path.join(opts.qpid_send_path, "qpid-send"),
+ "-b", broker,
+ "-a", address,
+ "--messages", str(opts.messages),
+ "--content-size", str(opts.content_size),
+ "--send-rate", str(opts.send_rate),
+ "--report-total",
+ "--report-header=no",
+ "--timestamp=%s"%(opts.timestamp and "yes" or "no"),
+ "--sequence=%s"%(opts.sequence and "yes" or "no"),
+ "--durable=%d" % opts.durable,
+ "--tx=%s" % opts.tx
+ ]
+ command += opts.send_arg
+ if opts.connection_options:
+ command += ["--connection-options",opts.connection_options]
+ if host: command = ssh_command(host, command)
+ if opts.verbose: print "Sender: ", command
+ return clients.add(PopenCommand(command, stdout=PIPE, stderr=PIPE))
+
+def error_msg(out, err):
+ return ("\n[stdout]\n%s\n[stderr]\n%s[end]"%(out, err))
+
+def first_line(p):
+ out,err=p.communicate()
+ if p.returncode != 0:
+ raise Exception("Process exit %d: %s"%(p.returncode, error_msg(out,err)))
+ return out.split("\n")[0]
+
+def connect(broker, opts):
+ if opts.connection_options:
+ copts = dict([kv.strip().split(":") for kv in opts.connection_options.strip("{}").split(",")])
+ else:
+ copts = {}
+ return qm.Connection.establish(broker, **copts)
+
+def drain(queue, session, opts):
+ """
+ Drain a queue to make sure it is empty. Throw away the messages.
+ """
+ if opts.verbose: print "Draining", queue
+ r = session.receiver(queue, capacity=1000)
+ n = 0
+ try:
+ while True:
+ # FIXME aconway 2014-11-21: activemq broker does not respect the drain flag
+ # so fetch on an empty queue will hang forever, use get with timeout instead.
+ # r.fetch(timeout=0)
+ m = qm.Message()
+ r.get(timeout=opts.timeout)
+ n += 1
+ if n % 500 == 0: r.session.acknowledge()
+ r.session.acknowledge()
+ except qm.Empty:
+ pass
+ r.close()
+ if opts.verbose: print "Drained", queue, n
+
+def clear_queues(queues, brokers, opts):
+ c = connect(brokers[0], opts)
+ for q in queues:
+ s = c.session()
+ need_drain = False
+ try:
+ s.sender("%s;{delete:always}"%(q)).close()
+ if opts.verbose: print "Deleted", q
+ except qm.NotFound:
+ s = c.session()
+ except qm.AddressError:
+ need_drain = True # AMQP 1.0 does not support delete, drain instead.
+ s = c.session()
+ address_opts = ["create:always"]
+ if opts.durable: address_opts += ["node:{durable:true}"]
+ address = "%s;{%s}"%(q, ",".join(opts.create_option + address_opts))
+ if opts.verbose: print "Declaring", address
+ s.sender(address)
+ if need_drain: drain(q, s, opts)
+ c.close()
+
+def print_header(timestamp):
+ if timestamp: latency_header="\tl-min\tl-max\tl-avg\ttotal-tp"
+ else: latency_header=""
+ print "send-tp\trecv-tp%s"%latency_header
+
+def parse(parser, lines): # Parse sender/receiver output
+ return [map(lambda p: p[0](p[1]), zip(parser,line.split())) for line in lines]
+
+def parse_senders(senders):
+ return parse([int],[first_line(p) for p in senders])
+
+def parse_receivers(receivers):
+ return parse([int,float,float,float],[first_line(p) for p in receivers if p])
+
+def print_data(send_stats, recv_stats, total_tp):
+ for send,recv in map(None, send_stats, recv_stats):
+ line=""
+ if send: line += "%d"%send[0]
+ if recv:
+ line += "\t%d"%recv[0]
+ if len(recv) == 4: line += "\t%.2f\t%.2f\t%.2f"%tuple(recv[1:])
+ if total_tp is not None:
+ line += "\t%d"%total_tp
+ total_tp = None
+ print line
+
+def print_summary(send_stats, recv_stats, total_tp):
+ def avg(s): sum(s) / len(s)
+ send_tp = sum([l[0] for l in send_stats])
+ recv_tp = sum([l[0] for l in recv_stats])
+ summary = "%d\t%d"%(send_tp, recv_tp)
+ if recv_stats and len(recv_stats[0]) == 4:
+ l_min = sum(l[1] for l in recv_stats)/len(recv_stats)
+ l_max = sum(l[2] for l in recv_stats)/len(recv_stats)
+ l_avg = sum(l[3] for l in recv_stats)/len(recv_stats)
+ summary += "\t%.2f\t%.2f\t%.2f"%(l_min, l_max, l_avg)
+ summary += "\t%d"%total_tp
+ print summary
+
+
+class ReadyReceiver:
+ """A receiver for ready messages"""
+ def __init__(self, queue, broker, opts):
+ self.connection = connect(broker, opts)
+ self.receiver = self.connection.session().receiver(queue)
+ self.receiver.session.sync()
+ self.timeout=opts.timeout
+
+ def wait(self, receivers):
+ try:
+ for i in receivers: self.receiver.fetch(self.timeout)
+ self.receiver.session.acknowledge()
+ self.connection.close()
+ except qm.Empty:
+ for r in receivers:
+ if (r.poll() is not None):
+ out,err=r.communicate()
+ raise Exception("Receiver error: %s\n%s" %
+ (" ".join(r.command), error_msg(out,err)))
+ raise Exception("Timed out waiting for receivers to be ready")
+
+def flatten(l):
+ return sum(map(lambda s: re.split(re.compile("\s*,\s*|\s+"), s), l), [])
+
+class RoundRobin:
+ def __init__(self,items):
+ self.items = items
+ self.index = 0
+
+ def next(self):
+ if not self.items: return None
+ ret = self.items[self.index]
+ self.index = (self.index+1)%len(self.items)
+ return ret
+
+def main():
+ opts, args = op.parse_args()
+ opts.client_host = flatten(opts.client_host)
+ if not opts.broker:
+ if opts.client_host:
+ raise Exception("--broker must be specified if --client_host is.")
+ opts.broker = ["127.0.0.1"] # Deafult to local broker
+ opts.broker = flatten(opts.broker)
+ brokers = RoundRobin(opts.broker)
+ client_hosts = RoundRobin(opts.client_host)
+ send_out = ""
+ receive_out = ""
+ ready_queue="%s-ready"%(opts.queue_name)
+ queues = ["%s-%s"%(opts.queue_name, i) for i in xrange(opts.queues)]
+ try:
+ for i in xrange(opts.repeat):
+ clear_queues(queues+[ready_queue], opts.broker, opts)
+ ready_receiver = ReadyReceiver(ready_queue, opts.broker[0], opts)
+
+ def start_receivers():
+ return [ start_receive(q, j, opts, ready_queue, brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.receivers) ]
+
+
+ def start_senders():
+ return [ start_send(q, opts,brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.senders) ]
+
+ if opts.report_header and i == 0: print_header(opts.timestamp)
+
+ if opts.fill_drain:
+ # First fill the queues, then drain them
+ start = time.time()
+ senders = start_senders()
+ for p in senders: p.wait()
+ receivers = start_receivers()
+ for p in receivers: p.wait()
+ else:
+ # Run senders and receivers in parallel
+ receivers = start_receivers()
+ ready_receiver.wait(filter(None, receivers)) # Wait for receivers ready
+ start = time.time()
+ senders = start_senders()
+ for p in senders + receivers: p.wait()
+
+ total_sent = opts.queues * opts.senders * opts.messages
+ total_tp = total_sent / (time.time()-start)
+ send_stats=parse_senders(senders)
+ recv_stats=parse_receivers(receivers)
+ if opts.summarize: print_summary(send_stats, recv_stats, total_tp)
+ else: print_data(send_stats, recv_stats, total_tp)
+ finally: clients.kill() # No strays
+
+if __name__ == "__main__": main()
+
diff --git a/qpid/cpp/src/tests/qpid-ctrl b/qpid/cpp/src/tests/qpid-ctrl
new file mode 100755
index 0000000000..4246c57898
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-ctrl
@@ -0,0 +1,120 @@
+#!/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 optparse
+from qpid.messaging import *
+from qpid.util import URL
+from qpid.log import enable, DEBUG, WARN
+
+def nameval(st):
+ idx = st.find("=")
+ if idx >= 0:
+ name = st[0:idx]
+ value = st[idx+1:]
+ else:
+ name = st
+ value = None
+ return name, value
+
+def list_map_entries(m):
+ r = ""
+ for t in m:
+ r += "%s=%s " % (t, m[t])
+ return r
+
+def get_qmfv2_result(m):
+ if m.properties['x-amqp-0-10.app-id'] == 'qmf2':
+ if m.properties['qmf.opcode'] == '_method_response':
+ return m.content['_arguments']
+ elif m.properties['qmf.opcode'] == '_exception':
+ raise Exception("Error: %s" % list_map_entries(m.content['_values']))
+ else: raise Exception("Invalid response received, unexpected opcode: %s" % m)
+ else: raise Exception("Invalid response received, not a qmfv2 method: %s" % m)
+
+
+parser = optparse.OptionParser(usage="usage: %prog [options] COMMAND ...",
+ description="Invoke the specified command.")
+parser.add_option("-b", "--broker", default="localhost",
+ help="connect to specified BROKER (default %default)")
+parser.add_option("-c", "--class", dest="qmfclass", default="broker",
+ help="class of object on which command is being invoked (default %default)")
+parser.add_option("-p", "--package", default="org.apache.qpid.broker",
+ help="package of object on which command is being invoked (default %default)")
+parser.add_option("-i", "--id", default="amqp-broker",
+ help="identifier of object on which command is being invoked (default %default)")
+parser.add_option("-a", "--address", default="qmf.default.direct/broker",
+ help="address to send commands to (default %default)")
+parser.add_option("-t", "--timeout", type="float", default=5,
+ help="timeout in seconds to wait for response before exiting (default %default)")
+parser.add_option("-v", dest="verbose", action="store_true",
+ help="enable logging")
+
+opts, args = parser.parse_args()
+
+if opts.verbose:
+ enable("qpid", DEBUG)
+else:
+ enable("qpid", WARN)
+
+if args:
+ command = args.pop(0)
+else:
+ parser.error("command is required")
+
+
+conn = Connection(opts.broker)
+try:
+ conn.open()
+ ssn = conn.session()
+ snd = ssn.sender(opts.address)
+ reply_to = "qmf.default.direct/%s; {node: {type: topic}}" % str(uuid4())
+ rcv = ssn.receiver(reply_to)
+
+ object_name = "%s:%s:%s" % (opts.package, opts.qmfclass, opts.id)
+ method_name = command
+ arguments = {}
+ for a in args:
+ name, val = nameval(a)
+ if val[0] == '{' or val[0] == '[':
+ arguments[name] = eval(val)
+ else:
+ arguments[name] = val
+ content = {
+ "_object_id": {"_object_name": object_name},
+ "_method_name": method_name,
+ "_arguments": arguments
+ }
+ msg = Message(reply_to=reply_to, content=content)
+ msg.properties["x-amqp-0-10.app-id"] = "qmf2"
+ msg.properties["qmf.opcode"] = "_method_request"
+ snd.send(msg)
+
+ try:
+ print list_map_entries(get_qmfv2_result(rcv.fetch(timeout=opts.timeout)))
+ except Empty:
+ print "No response received!"
+ except Exception, e:
+ print e
+except ReceiverError, e:
+ print e
+except KeyboardInterrupt:
+ pass
+
+conn.close()
diff --git a/qpid/cpp/src/tests/qpid-latency-test.cpp b/qpid/cpp/src/tests/qpid-latency-test.cpp
new file mode 100644
index 0000000000..a03963467b
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-latency-test.cpp
@@ -0,0 +1,480 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/sys/Thread.h"
+#include "qpid/client/Connection.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/Time.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+typedef std::vector<std::string> StringSet;
+
+struct Args : public qpid::TestOptions {
+ uint size;
+ uint count;
+ uint rate;
+ bool sync;
+ uint reportFrequency;
+ uint timeLimit;
+ uint concurrentConnections;
+ uint prefetch;
+ uint ack;
+ bool cumulative;
+ bool csv;
+ bool durable;
+ string base;
+ bool singleConnect;
+
+ Args() : size(256), count(1000), rate(0), reportFrequency(1000),
+ timeLimit(0), concurrentConnections(1),
+ prefetch(100), ack(0),
+ durable(false), base("latency-test"), singleConnect(false)
+
+ {
+ addOptions()
+
+ ("size", optValue(size, "N"), "message size")
+ ("concurrentTests", optValue(concurrentConnections, "N"), "number of concurrent test setups, will create another publisher,\
+ subcriber, queue, and connections")
+ ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.")
+ ("count", optValue(count, "N"), "number of messages to send")
+ ("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)")
+ ("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;
+Connection globalConnection;
+
+uint64_t current_time()
+{
+ return Duration::FromEpoch();
+}
+
+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;
+ Connection localConnection;
+ 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)
+{
+ if (opts.singleConnect){
+ connection = &globalConnection;
+ if (!globalConnection.isOpen()) opts.open(globalConnection);
+ }else{
+ connection = &localConnection;
+ opts.open(localConnection);
+ }
+ session = connection->newSession();
+}
+
+void Client::start()
+{
+ thread = Thread(this);
+}
+
+void Client::join()
+{
+ thread.join();
+}
+
+void Client::run()
+{
+ try{
+ test();
+ } catch(const std::exception& e) {
+ std::cout << "Error in receiver: " << e.what() << std::endl;
+ }
+}
+
+Client::~Client()
+{
+ try{
+ session.close();
+ connection->close();
+ } 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);
+ session.sync();
+ }
+ 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 receivedAt = current_time();
+ uint64_t sentAt = msg.getDeliveryProperties().getTimestamp();
+
+ 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) {
+ if (count) {
+ std::cout << "Latency(ms): min=" << minLatency << ", max=" <<
+ maxLatency << ", avg=" << aux_avg;
+ } else {
+ std::cout << "Stalled: no samples for interval";
+ }
+ } else {
+ if (count) {
+ std::cout << value << "," << minLatency << "," << maxLatency <<
+ "," << aux_avg;
+ } else {
+ std::cout << value << "," << minLatency << "," << maxLatency <<
+ ", Stalled";
+ }
+ }
+ } else {
+ if (count) {
+ if (already_have_stats) {
+ c_avg = (c_min + aux_avg) / 2;
+ if (c_min > minLatency) c_min = minLatency;
+ if (c_max < maxLatency) c_max = maxLatency;
+ } else {
+ c_avg = aux_avg;
+ c_min = minLatency;
+ c_max = maxLatency;
+ already_have_stats = true;
+ }
+ std::cout << value << "," << c_min << "," << c_max <<
+ "," << c_avg;
+ } else {
+ std::cout << "Stalled: no samples for interval";
+ }
+ }
+}
+
+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);
+ }
+ uint64_t interval = TIME_SEC/opts.rate;
+ int64_t timeLimit = opts.timeLimit * TIME_SEC;
+ uint64_t sent = 0;
+ AbsTime start = now();
+ AbsTime last = start;
+ while (true) {
+ AbsTime sentAt=now();
+ msg.getDeliveryProperties().setTimestamp(Duration::FromEpoch());
+ async(session).messageTransfer(arg::content=msg, arg::acceptMode=1);
+ if (opts.sync) session.sync();
+ ++sent;
+ if (Duration(last, sentAt) > (opts.reportFrequency*TIME_MSEC)) {
+ Duration t(start, now());
+ //check rate actually achieved thus far
+ if (t/TIME_SEC) {
+ uint actualRate = sent / (t/TIME_SEC);
+ //report inability to stay within 1% of desired rate
+ if (actualRate < opts.rate && opts.rate - actualRate > opts.rate/100) {
+ std::cerr << "WARNING: Desired send rate: " << opts.rate << ", actual send rate: " << actualRate << std::endl;
+ }
+ }
+ last = sentAt;
+ }
+
+ AbsTime waitTill(start, sent*interval);
+ Duration delay(sentAt, waitTill);
+ if (delay > 0)
+ sys::usleep(delay / TIME_USEC);
+ if (timeLimit != 0 && Duration(start, now()) > timeLimit) {
+ session.sync();
+ receiver.stop();
+ break;
+ }
+ }
+}
+
+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();
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ if (opts.cumulative)
+ opts.csv = true;
+
+ Connection localConnection;
+ AsyncSession session;
+
+ boost::ptr_vector<Test> tests(opts.concurrentConnections);
+ for (uint i = 0; i < opts.concurrentConnections; i++) {
+ std::ostringstream out;
+ out << opts.base << "-" << (i+1);
+ tests.push_back(new Test(out.str()));
+ }
+ 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/qpid/cpp/src/tests/qpid-perftest.cpp b/qpid/cpp/src/tests/qpid-perftest.cpp
new file mode 100644
index 0000000000..7b9738772c
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-perftest.cpp
@@ -0,0 +1,760 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/OptionsTemplates.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 "qpid/sys/Thread.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 <math.h>
+
+
+using namespace std;
+using namespace qpid;
+using namespace client;
+using namespace sys;
+using boost::lexical_cast;
+using boost::bind;
+
+namespace qpid {
+namespace tests {
+
+enum Mode { SHARED, FANOUT, TOPIC };
+const char* modeNames[] = { "shared", "fanout", "topic" };
+
+// 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;
+ std::string baseName;
+ bool queueDurable;
+
+ // Publisher
+ size_t pubs;
+ size_t count ;
+ size_t size;
+ size_t headers;
+ bool confirm;
+ bool durable;
+ bool uniqueData;
+ bool syncPub;
+
+ // Subscriber
+ size_t subs;
+ size_t ack;
+
+ // General
+ size_t qt;
+ bool singleConnect;
+ size_t iterations;
+ Mode mode;
+ bool summary;
+ uint32_t intervalSub;
+ uint32_t intervalPub;
+ size_t tx;
+ size_t txPub;
+ size_t txSub;
+ bool commitAsync;
+
+ static const std::string helpText;
+
+ Opts() :
+ TestOptions(helpText),
+ setup(false), control(false), publish(false), subscribe(false), baseName("qpid-perftest"),
+ pubs(1), count(500000), size(1024), headers(0), confirm(true), durable(false), uniqueData(false), syncPub(false),
+ subs(1), ack(0),
+ qt(1),singleConnect(false), iterations(1), mode(SHARED), summary(false),
+ intervalSub(0), intervalPub(0), tx(0), txPub(0), txSub(0), commitAsync(false)
+ {
+ addOptions()
+ ("setup", optValue(setup), "Create shared queues.")
+ ("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.")
+ ("headers", optValue(headers, "N"), "Number of headers to add to each message.")
+ ("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.")
+ ("single-connection", optValue(singleConnect, "yes|no"), "Use one connection for multiple sessions.")
+
+ ("iterations", optValue(iterations, "N"), "Desired number of iterations of the test.")
+ ("summary,s", optValue(summary), "Summary output: pubs/sec subs/sec transfers/sec Mbytes/sec")
+
+ ("queue-max-count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'")
+ ("queue-max-size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'")
+ ("base-name", optValue(baseName, "NAME"), "base name used for queues or topics")
+ ("queue-durable", optValue(queueDurable, "N"), "Make queue durable (implied if durable set)")
+
+ ("interval_sub", optValue(intervalSub, "ms"), ">=0 delay between msg consume")
+ ("interval_pub", optValue(intervalPub, "ms"), ">=0 delay between msg publish")
+
+ ("tx", optValue(tx, "N"), "if non-zero, the transaction batch size 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 next 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 qpid-perftest: single process or multi-process.\n\n"
+"If none of the --setup, --publish, --subscribe or --control options\n"
+"are given qpid-perftest will run a single-process test.\n"
+"For a multi-process test first run:\n"
+" qpid-perftest --setup <other options>\n"
+"and wait for it to complete. The remaining process should run concurrently::\n"
+"Run --npubs times: qpid-perftest --publish <other options>\n"
+"Run --nsubs times: qpid-perftest --subscribe <other options>\n"
+"Run once: qpid-perftest --control <other options>\n"
+"Note the <other options> must be identical for all processes.\n";
+
+Opts opts;
+Connection globalConnection;
+
+std::string fqn(const std::string& name)
+{
+ ostringstream fqn;
+ fqn << opts.baseName << "_" << name;
+ return fqn.str();
+}
+
+struct Client : public Runnable {
+ Connection* connection;
+ Connection localConnection;
+ AsyncSession session;
+ Thread thread;
+
+ Client() {
+ if (opts.singleConnect){
+ connection = &globalConnection;
+ if (!globalConnection.isOpen()) opts.open(globalConnection);
+ }else{
+ connection = &localConnection;
+ opts.open(localConnection);
+ }
+ session = connection->newSession();
+ }
+
+ ~Client() {
+ try {
+ if (connection->isOpen()) {
+ session.close();
+ connection->close();
+ }
+ } catch (const std::exception& e) {
+ std::cerr << "Error in shutdown: " << e.what() << std::endl;
+ }
+ }
+};
+
+struct Setup : public Client {
+
+ void queueInit(string name, bool durable=false, const framing::FieldTable& settings=framing::FieldTable()) {
+ session.queueDeclare(arg::queue=name, arg::durable=durable, arg::arguments=settings);
+ session.queuePurge(arg::queue=name);
+ session.sync();
+ }
+
+ void run() {
+ queueInit(fqn("pub_start"));
+ queueInit(fqn("pub_done"));
+ queueInit(fqn("sub_ready"));
+ queueInit(fqn("sub_done"));
+ if (opts.iterations > 1) queueInit(fqn("sub_iteration"));
+ if (opts.mode==SHARED) {
+ framing::FieldTable settings;//queue policy settings
+ settings.setInt("qpid.max_count", opts.queueMaxCount);
+ settings.setInt("qpid.max_size", opts.queueMaxSize);
+ for (size_t i = 0; i < opts.qt; ++i) {
+ ostringstream qname;
+ qname << opts.baseName << 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, fqn("sub_ready"), boost::bind(expect, _1, "ready"));
+
+ LocalQueue pubDone;
+ LocalQueue subDone;
+ subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false);
+ subs.subscribe(pubDone, fqn("pub_done"));
+ subs.subscribe(subDone, fqn("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, fqn("pub_start"), "start"); // Start publishers
+ if (j) {
+ send(opts.totalSubs, fqn("sub_iteration"), "next"); // Start subscribers on next iteration
+ }
+
+ Stats pubRates;
+ Stats subRates;
+
+ process(opts.totalPubs, pubDone, fqn("pub_done"), boost::ref(pubRates));
+ process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates));
+
+ AbsTime end=now();
+ double time=secs(start, end);
+ if (time <= 0.0) {
+ throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count.");
+ }
+ 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.headers) {
+ for (size_t i = 0; i < opts.headers; ++i) {
+ std::stringstream h;
+ h << "hdr" << i;
+ msg.getMessageProperties().getApplicationHeaders().setString(h.str(), h.str());
+ }
+ }
+
+ if (opts.txPub){
+ session.txSelect();
+ }
+ SubscriptionManager subs(session);
+ LocalQueue lq;
+ subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false);
+ Subscription cs = subs.subscribe(lq, fqn("pub_start"));
+
+ for (size_t j = 0; j < opts.iterations; ++j) {
+ cs.grantMessageCredit(1);
+ 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)
+ qpid::sys::usleep(opts.intervalPub*1000);
+ }
+ if (opts.confirm) session.sync();
+ AbsTime end=now();
+ double time=secs(start,end);
+ if (time <= 0.0) {
+ throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count.");
+ }
+
+ // Send result to controller.
+ Message report(lexical_cast<string>(opts.count/time), fqn("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", fqn("sub_ready")), arg::acceptMode=1);
+ if (opts.txSub) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
+
+ LocalQueue iterationControl;
+ if (opts.iterations > 1) {
+ subs.subscribe(iterationControl, fqn("sub_iteration"), SubscriptionSettings(FlowControl::messageCredit(0)));
+ }
+
+ for (size_t j = 0; j < opts.iterations; ++j) {
+ if (j > 0) {
+ //need to wait here until all subs are done
+ session.messageFlow(fqn("sub_iteration"), 0, 1);
+ iterationControl.pop();
+
+ //need to allocate some more credit for subscription
+ session.messageFlow(queue, 0, opts.subQuota);
+ }
+ Message msg;
+ AbsTime start=now();
+ size_t expect=0;
+ for (size_t i = 0; i < opts.subQuota; ++i) {
+ msg=lq.pop();
+ if (opts.txSub && ((i+1) % opts.txSub == 0)) {
+ if (opts.commitAsync) session.txCommit();
+ else sync(session).txCommit();
+ }
+ if (opts.intervalSub)
+ qpid::sys::usleep(opts.intervalSub*1000);
+ // TODO aconway 2007-11-23: check message order for.
+ // multiple publishers. Need an array of counters,
+ // one per publisher and a publisher ID in the
+ // message. Careful not to introduce a lot of overhead
+ // 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;
+ memcpy (&n, reinterpret_cast<const char*>(msg.getData().data() + offset),
+ sizeof(n));
+ 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)),
+ fqn("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;
+ }
+ }
+};
+
+}
+
+template po::value_semantic* create_value(tests::Mode& val, const std::string& arg);
+
+} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv) {
+ int exitCode = 0;
+ boost::ptr_vector<Client> subs(opts.subs);
+ boost::ptr_vector<Client> pubs(opts.pubs);
+
+ try {
+ opts.parse(argc, argv);
+
+ 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
+
+ // Start pubs/subs for each queue/topic.
+ for (size_t i = 0; i < opts.qt; ++i) {
+ ostringstream key;
+ key << opts.baseName << i; // Queue or topic name.
+ if (opts.publish) {
+ size_t n = singleProcess ? opts.pubs : 1;
+ for (size_t j = 0; j < n; ++j) {
+ 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();
+ }
+ catch (const std::exception& e) {
+ cout << endl << e.what() << endl;
+ exitCode = 1;
+ }
+
+ // Wait for started threads.
+ if (opts.publish) {
+ for (boost::ptr_vector<Client>::iterator i=pubs.begin();
+ i != pubs.end();
+ ++i)
+ i->thread.join();
+ }
+
+ if (opts.subscribe) {
+ for (boost::ptr_vector<Client>::iterator i=subs.begin();
+ i != subs.end();
+ ++i)
+ i->thread.join();
+ }
+ return exitCode;
+}
diff --git a/qpid/cpp/src/tests/qpid-ping.cpp b/qpid/cpp/src/tests/qpid-ping.cpp
new file mode 100644
index 0000000000..40e6a0f671
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-ping.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/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include "qpid/messaging/Duration.h"
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/Msg.h>
+#include <qpid/Options.h>
+#include <qpid/types/Uuid.h>
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace qpid::messaging;
+using qpid::types::Uuid;
+
+namespace {
+
+struct PingOptions : public qpid::Options {
+ string url;
+ string address;
+ string message;
+ string connectionOptions;
+ double timeout; // Timeout in seconds.
+ bool quiet; // No output
+
+ PingOptions() :
+ url("127.0.0.1"),
+ address(Uuid(true).str()+";{create:always}"),
+ message(Uuid(true).str()),
+ timeout(1),
+ quiet(false)
+ {
+ using qpid::optValue;
+ addOptions()
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to.")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to use.")
+ ("message,m", optValue(message, "MESSAGE"), "message text to send.")
+ ("connection-options", optValue(connectionOptions, "OPTIONS"), "options for the connection.")
+ ("timeout,t", optValue(timeout, "SECONDS"), "Max time to wait.")
+ ("quiet,q", optValue(quiet), "Don't print anything to stderr/stdout.");
+ }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ Connection connection;
+ try {
+ PingOptions opts;
+ opts.parse(argc, argv);
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ if (!opts.quiet) cout << "Opened connection." << endl;
+ Session s = connection.createSession();
+ s.createSender(opts.address).send(Message(opts.message));
+ if (!opts.quiet) cout << "Sent message." << endl;
+ Message m = s.createReceiver(opts.address).
+ fetch(Duration(uint64_t(opts.timeout*1000)));
+ if (m.getContent() != opts.message)
+ throw qpid::Exception(qpid::Msg() << "Expected " << opts.message
+ << " but received " << m.getContent());
+ if (!opts.quiet) cout << "Received message." << endl;
+ connection.close();
+ return 0;
+ } catch (const exception& e) {
+ cerr << "Error: " << e.what() << endl;
+ connection.close();
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/qpid-receive.cpp b/qpid/cpp/src/tests/qpid-receive.cpp
new file mode 100644
index 0000000000..a71fd11fa7
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-receive.cpp
@@ -0,0 +1,299 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/FailoverUpdates.h>
+#include <qpid/Options.h>
+#include <qpid/log/Logger.h>
+#include <qpid/log/Options.h>
+#include "qpid/sys/Time.h"
+#include "TestOptions.h"
+#include "Statistics.h"
+
+#include <iostream>
+#include <memory>
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Options : public qpid::Options
+{
+ bool help;
+ std::string url;
+ std::string address;
+ std::string connectionOptions;
+ int64_t timeout;
+ bool forever;
+ uint messages;
+ bool ignoreDuplicates;
+ bool verifySequence;
+ bool checkRedelivered;
+ uint capacity;
+ uint ackFrequency;
+ uint tx;
+ uint rollbackFrequency;
+ bool printContent;
+ bool printContentObjectType;
+ bool printHeaders;
+ bool failoverUpdates;
+ qpid::log::Options log;
+ bool reportTotal;
+ uint reportEvery;
+ bool reportHeader;
+ string readyAddress;
+ uint receiveRate;
+ std::string replyto;
+ bool noReplies;
+
+ Options(const std::string& argv0=std::string())
+ : qpid::Options("Options"),
+ help(false),
+ url("127.0.0.1"),
+ timeout(0),
+ forever(false),
+ messages(0),
+ ignoreDuplicates(false),
+ verifySequence(false),
+ checkRedelivered(false),
+ capacity(1000),
+ ackFrequency(100),
+ tx(0),
+ rollbackFrequency(0),
+ printContent(true),
+ printContentObjectType(false),
+ printHeaders(false),
+ failoverUpdates(false),
+ log(argv0),
+ reportTotal(false),
+ reportEvery(0),
+ reportHeader(true),
+ receiveRate(0),
+ noReplies(false)
+ {
+ addOptions()
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to receive from")
+ ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
+ ("timeout", qpid::optValue(timeout, "TIMEOUT"), "timeout in seconds to wait before exiting")
+ ("forever,f", qpid::optValue(forever), "ignore timeout and wait forever")
+ ("messages,m", 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)")
+ ("verify-sequence", qpid::optValue(verifySequence), "Verify there are no gaps in the message sequence (by checking 'sn' header)")
+ ("check-redelivered", qpid::optValue(checkRedelivered), "Fails with exception if a duplicate is not marked as redelivered (only relevant when ignore-duplicates is selected)")
+ ("capacity", qpid::optValue(capacity, "N"), "Pre-fetch window (0 implies no pre-fetch)")
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
+ ("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)")
+ ("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)")
+ ("print-content", qpid::optValue(printContent, "yes|no"), "print out message content")
+ ("print-object-type", qpid::optValue(printContentObjectType, "yes|no"), "print a description of the content's object type if relevant")
+ ("print-headers", qpid::optValue(printHeaders, "yes|no"), "print out message headers")
+ ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover")
+ ("report-total", qpid::optValue(reportTotal), "Report total throughput and latency statistics")
+ ("report-every", qpid::optValue(reportEvery,"N"), "Report throughput and latency statistics every N messages.")
+ ("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
+ ("ready-address", qpid::optValue(readyAddress, "ADDRESS"), "send a message to this address when ready to receive")
+ ("receive-rate", qpid::optValue(receiveRate,"N"), "Receive at rate of N messages/second. 0 means receive as fast as possible.")
+ ("reply-to", qpid::optValue(replyto, "REPLY-TO"), "specify reply-to address on response messages")
+ ("ignore-reply-to", qpid::optValue(noReplies), "Do not send replies even if reply-to is set")
+ ("help", qpid::optValue(help), "print this usage statement");
+ add(log);
+ }
+
+ Duration getTimeout()
+ {
+ if (forever) return Duration::FOREVER;
+ else return Duration::SECOND*timeout;
+
+ }
+ bool parse(int argc, char** argv)
+ {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (address.empty()) throw qpid::Exception("Address must be specified!");
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::cout << *this << std::endl << std::endl
+ << "Drains messages from the specified address" << std::endl;
+ return false;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ return false;
+ }
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+
+/** Check for duplicate or dropped messages by sequence number */
+class SequenceTracker
+{
+ public:
+ SequenceTracker(const Options& o) : opts(o), lastSn(0) {}
+
+ /** Return true if the message should be procesed, false if it should be ignored. */
+ bool track(Message& message) {
+ if (!(opts.verifySequence || opts.ignoreDuplicates))
+ return true; // Not checking sequence numbers.
+ uint sn = message.getProperties()[SN];
+ bool duplicate = (sn <= lastSn);
+ bool dropped = (sn > lastSn+1);
+ if (opts.verifySequence && dropped)
+ throw Exception(QPID_MSG("Gap in sequence numbers " << lastSn << "-" << sn));
+ bool ignore = duplicate && opts.ignoreDuplicates;
+ if (ignore && opts.checkRedelivered && !message.getRedelivered())
+ throw qpid::Exception("duplicate sequence number received, message not marked as redelivered!");
+ if (!duplicate) lastSn = sn;
+ return !ignore;
+ }
+
+ private:
+ const Options& opts;
+ uint lastSn;
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Connection connection;
+ try {
+ Options opts;
+ if (opts.parse(argc, argv)) {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = opts.tx ? connection.createTransactionalSession() : connection.createSession();
+ Receiver receiver = session.createReceiver(opts.address);
+ receiver.setCapacity(std::min(opts.capacity, opts.messages));
+ Message msg;
+ uint count = 0;
+ uint txCount = 0;
+ SequenceTracker sequenceTracker(opts);
+ Duration timeout = opts.getTimeout();
+ bool done = false;
+ Reporter<ThroughputAndLatency> reporter(std::cout, opts.reportEvery, opts.reportHeader);
+ if (!opts.readyAddress.empty()) {
+ session.createSender(opts.readyAddress).send(msg);
+ if (opts.tx)
+ session.commit();
+ }
+ // For receive rate calculation
+ qpid::sys::AbsTime start = qpid::sys::now();
+ int64_t interval = 0;
+ if (opts.receiveRate) interval = qpid::sys::TIME_SEC/opts.receiveRate;
+
+ std::map<std::string,Sender> replyTo;
+
+ while (!done && receiver.fetch(msg, timeout)) {
+ reporter.message(msg);
+ if (sequenceTracker.track(msg)) {
+ if (msg.getContent() == EOS) {
+ done = true;
+ } else {
+ ++count;
+ if (opts.printHeaders) {
+ if (msg.getSubject().size()) std::cout << "Subject: " << msg.getSubject() << std::endl;
+ if (msg.getReplyTo()) std::cout << "ReplyTo: " << msg.getReplyTo() << std::endl;
+ if (msg.getMessageId().size()) std::cout << "MessageId: " << msg.getMessageId() << std::endl;
+ if (msg.getCorrelationId().size()) std::cout << "CorrelationId: " << msg.getCorrelationId() << std::endl;
+ if (msg.getUserId().size()) std::cout << "UserId: " << msg.getUserId() << std::endl;
+ if (msg.getTtl().getMilliseconds()) std::cout << "TTL: " << msg.getTtl().getMilliseconds() << std::endl;
+ if (msg.getPriority()) std::cout << "Priority: " << ((uint) msg.getPriority()) << std::endl;
+ if (msg.getDurable()) std::cout << "Durable: true" << std::endl;
+ if (msg.getRedelivered()) std::cout << "Redelivered: true" << std::endl;
+ std::cout << "Properties: " << msg.getProperties() << std::endl;
+ if (msg.getContentType().size()) std::cout << "ContentType: " << msg.getContentType() << std::endl;
+ std::cout << std::endl;
+ }
+ if (opts.printContent) {
+ if (!msg.getContentObject().isVoid()) {
+ if (opts.printContentObjectType) {
+ std::cout << "[Object: " << getTypeName(msg.getContentObject().getType()) << "]" << std::endl;
+ }
+ std::cout << msg.getContentObject() << std::endl;
+ } else {
+ std::cout << msg.getContent() << std::endl;
+ }
+ }
+ if (opts.messages && count >= opts.messages) done = true;
+ }
+ }
+ if (opts.tx && (count % opts.tx == 0)) {
+ if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) {
+ session.rollback();
+ } else {
+ session.commit();
+ }
+ } else if (opts.ackFrequency && (count % opts.ackFrequency == 0)) {
+ session.acknowledge();
+ }
+ if (msg.getReplyTo() && !opts.noReplies) { // Echo message back to reply-to address.
+ Sender& s = replyTo[msg.getReplyTo().str()];
+ if (s.isNull()) {
+ s = session.createSender(msg.getReplyTo());
+ s.setCapacity(opts.capacity);
+ replyTo[msg.getReplyTo().str()] = s;
+ }
+ msg.setReplyTo(Address(opts.replyto));
+ s.send(msg);
+ }
+ if (opts.receiveRate) {
+ qpid::sys::AbsTime waitTill(start, count*interval);
+ int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill);
+ if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC);
+ }
+ }
+ if (opts.reportTotal) reporter.report();
+ if (opts.tx) {
+ if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) {
+ session.rollback();
+ } else {
+ session.commit();
+ }
+ } else if (opts.ackFrequency) {
+ session.acknowledge();
+ }
+ session.close();
+ connection.close();
+ return 0;
+ }
+ return 1;
+ } catch(const std::exception& error) {
+ std::cerr << "qpid-receive: " << error.what() << std::endl;
+ connection.close();
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/qpid-send.cpp b/qpid/cpp/src/tests/qpid-send.cpp
new file mode 100644
index 0000000000..bc0ad78601
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-send.cpp
@@ -0,0 +1,465 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/FailoverUpdates.h>
+#include <qpid/sys/Time.h>
+#include <qpid/sys/Monitor.h>
+#include <qpid/sys/SystemInfo.h>
+#include "TestOptions.h"
+#include "Statistics.h"
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+
+using std::string;
+using std::ios_base;
+
+using qpid::messaging::Address;
+using qpid::messaging::Connection;
+using qpid::messaging::Duration;
+using qpid::messaging::FailoverUpdates;
+using qpid::messaging::Message;
+using qpid::messaging::Receiver;
+using qpid::messaging::Session;
+using qpid::messaging::Sender;
+using qpid::types::Exception;
+using qpid::types::Uuid;
+using qpid::types::Variant;
+
+namespace qpid {
+namespace tests {
+
+typedef std::vector<std::string> string_vector;
+
+struct Options : public qpid::Options
+{
+ bool help;
+ std::string url;
+ std::string connectionOptions;
+ std::string address;
+ uint messages;
+ std::string id;
+ std::string replyto;
+ uint sendEos;
+ bool durable;
+ uint ttl;
+ uint priority;
+ std::string userid;
+ std::string correlationid;
+ string_vector properties;
+ string_vector entries;
+ std::string contentString;
+ uint contentSize;
+ bool contentStdin;
+ uint tx;
+ uint rollbackFrequency;
+ uint capacity;
+ bool failoverUpdates;
+ qpid::log::Options log;
+ bool reportTotal;
+ uint reportEvery;
+ bool reportHeader;
+ uint sendRate;
+ bool sequence;
+ bool timestamp;
+ std::string groupKey;
+ std::string groupPrefix;
+ uint groupSize;
+ bool groupRandSize;
+ uint groupInterleave;
+
+ Options(const std::string& argv0=std::string())
+ : qpid::Options("Options"),
+ help(false),
+ url("127.0.0.1"),
+ messages(1),
+ sendEos(0),
+ durable(false),
+ ttl(0),
+ priority(0),
+ contentString(),
+ contentSize(0),
+ contentStdin(false),
+ tx(0),
+ rollbackFrequency(0),
+ capacity(1000),
+ failoverUpdates(false),
+ log(argv0),
+ reportTotal(false),
+ reportEvery(0),
+ reportHeader(true),
+ sendRate(0),
+ sequence(true),
+ timestamp(true),
+ groupPrefix("GROUP-"),
+ groupSize(10),
+ groupRandSize(false),
+ groupInterleave(1)
+ {
+ addOptions()
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to send to")
+ ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
+ ("messages,m", qpid::optValue(messages, "N"), "stop after N messages have been sent, 0 means no limit")
+ ("id,i", qpid::optValue(id, "ID"), "use the supplied id instead of generating one")
+ ("reply-to", qpid::optValue(replyto, "REPLY-TO"), "specify reply-to address")
+ ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input")
+ ("durable", qpid::optValue(durable, "yes|no"), "Mark messages as durable.")
+ ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds")
+ ("priority", qpid::optValue(priority, "PRIORITY"), "Priority for messages (higher value implies higher priority)")
+ ("property,P", qpid::optValue(properties, "NAME=VALUE"), "specify message property")
+ ("correlation-id", qpid::optValue(correlationid, "ID"), "correlation-id for message")
+ ("user-id", qpid::optValue(userid, "USERID"), "userid for message")
+ ("content-string", qpid::optValue(contentString, "CONTENT"), "use CONTENT as message content")
+ ("content-size", qpid::optValue(contentSize, "N"), "create an N-byte message content")
+ ("content-map,M", qpid::optValue(entries, "NAME=VALUE"), "specify entry for map content")
+ ("content-stdin", qpid::optValue(contentStdin), "read message content from stdin, one line per message")
+ ("capacity", qpid::optValue(capacity, "N"), "size of the senders outgoing message queue")
+ ("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)")
+ ("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)")
+ ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover")
+ ("report-total", qpid::optValue(reportTotal), "Report total throughput statistics")
+ ("report-every", qpid::optValue(reportEvery,"N"), "Report throughput statistics every N messages")
+ ("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
+ ("send-rate", qpid::optValue(sendRate,"N"), "Send at rate of N messages/second. 0 means send as fast as possible.")
+ ("sequence", qpid::optValue(sequence, "yes|no"), "Add a sequence number messages property (required for duplicate/lost message detection)")
+ ("timestamp", qpid::optValue(timestamp, "yes|no"), "Add a time stamp messages property (required for latency measurement)")
+ ("group-key", qpid::optValue(groupKey, "KEY"), "Generate groups of messages using message header 'KEY' to hold the group identifier")
+ ("group-prefix", qpid::optValue(groupPrefix, "STRING"), "Generate group identifers with 'STRING' prefix (if group-key specified)")
+ ("group-size", qpid::optValue(groupSize, "N"), "Number of messages per a group (if group-key specified)")
+ ("group-randomize-size", qpid::optValue(groupRandSize), "Randomize the number of messages per group to [1...group-size] (if group-key specified)")
+ ("group-interleave", qpid::optValue(groupInterleave, "N"), "Simultaineously interleave messages from N different groups (if group-key specified)")
+ ("help", qpid::optValue(help), "print this usage statement");
+ add(log);
+ }
+
+ bool parse(int argc, char** argv)
+ {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (address.empty()) throw qpid::Exception("Address must be specified!");
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::cout << *this << std::endl << std::endl
+ << "Sends messages to the specified address" << std::endl;
+ return false;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ return false;
+ }
+ }
+
+ static bool nameval(const std::string& in, std::string& name, std::string& value)
+ {
+ std::string::size_type i = in.find("=");
+ if (i == std::string::npos) {
+ name = in;
+ return false;
+ } else {
+ name = in.substr(0, i);
+ if (i+1 < in.size()) {
+ value = in.substr(i+1);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ static void setProperty(Message& message, const std::string& property)
+ {
+ std::string name;
+ std::string value;
+ if (nameval(property, name, value)) {
+ message.getProperties()[name].parse(value);
+ } else {
+ message.getProperties()[name] = Variant();
+ }
+ }
+
+ void setProperties(Message& message) const
+ {
+ for (string_vector::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ setProperty(message, *i);
+ }
+ }
+
+ void setEntries(Variant::Map& content) const
+ {
+ for (string_vector::const_iterator i = entries.begin(); i != entries.end(); ++i) {
+ std::string name;
+ std::string value;
+ if (nameval(*i, name, value)) {
+ content[name] = value;
+ } else {
+ content[name] = Variant();
+ }
+ }
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+const string TS("ts");
+
+class ContentGenerator {
+ public:
+ virtual ~ContentGenerator() {}
+ virtual bool setContent(Message& msg) = 0;
+ void setContentObject(Message& msg, const std::string& content, const std::string& encoding=std::string("utf8"))
+ {
+ Variant& obj = msg.getContentObject();
+ obj = content;
+ obj.setEncoding(encoding);
+ }
+};
+
+
+class GetlineContentGenerator : public ContentGenerator {
+ public:
+ virtual bool setContent(Message& msg) {
+ string content;
+ bool got = !!getline(std::cin, content);
+ if (got) {
+ setContentObject(msg, content);
+ }
+ return got;
+ }
+};
+
+class FixedContentGenerator : public ContentGenerator {
+ public:
+ FixedContentGenerator(const string& s) : content(s) {}
+ virtual bool setContent(Message& msg) {
+ setContentObject(msg, content);
+ return true;
+ }
+ private:
+ std::string content;
+};
+
+class MapContentGenerator : public ContentGenerator {
+ public:
+ MapContentGenerator(const Options& opt) : opts(opt) {}
+ virtual bool setContent(Message& msg) {
+ msg.getContentObject() = qpid::types::Variant::Map();
+ opts.setEntries(msg.getContentObject().asMap());
+ return true;
+ }
+ private:
+ const Options& opts;
+};
+
+// tag each generated message with a group identifer
+//
+class GroupGenerator {
+ public:
+ GroupGenerator(const std::string& key,
+ const std::string& prefix,
+ const uint size,
+ const bool randomize,
+ const uint interleave)
+ : groupKey(key), groupPrefix(prefix), groupSize(size),
+ randomizeSize(randomize), groupSuffix(0)
+ {
+ if (randomize) srand((unsigned int)qpid::sys::SystemInfo::getProcessId());
+
+ for (uint i = 0; i < 1 || i < interleave; ++i) {
+ newGroup();
+ }
+ current = groups.begin();
+ }
+
+ void setGroupInfo(Message &msg)
+ {
+ if (current == groups.end())
+ current = groups.begin();
+ msg.getProperties()[groupKey] = current->id;
+ // std::cout << "SENDING GROUPID=[" << current->id << "]" << std::endl;
+ if (++(current->count) == current->size) {
+ newGroup();
+ groups.erase(current++);
+ } else
+ ++current;
+ }
+
+ private:
+ const std::string& groupKey;
+ const std::string& groupPrefix;
+ const uint groupSize;
+ const bool randomizeSize;
+
+ uint groupSuffix;
+
+ struct GroupState {
+ std::string id;
+ const uint size;
+ uint count;
+ GroupState( const std::string& i, const uint s )
+ : id(i), size(s), count(0) {}
+ };
+ typedef std::list<GroupState> GroupList;
+ GroupList groups;
+ GroupList::iterator current;
+
+ void newGroup() {
+ std::ostringstream groupId(groupPrefix, ios_base::out|ios_base::ate);
+ groupId << groupSuffix++;
+ uint size = (randomizeSize) ? (rand() % groupSize) + 1 : groupSize;
+ // std::cout << "New group: GROUPID=[" << groupId.str() << "] size=" << size << std::endl;
+ GroupState group( groupId.str(), size );
+ groups.push_back( group );
+ }
+};
+
+}} // namespace qpid::tests
+
+using qpid::tests::Options;
+using qpid::tests::Reporter;
+using qpid::tests::Throughput;
+using qpid::tests::ContentGenerator;
+using qpid::tests::GroupGenerator;
+using qpid::tests::GetlineContentGenerator;
+using qpid::tests::MapContentGenerator;
+using qpid::tests::FixedContentGenerator;
+using qpid::tests::SN;
+using qpid::tests::TS;
+using qpid::tests::EOS;
+
+int main(int argc, char ** argv)
+{
+ Connection connection;
+ try {
+ Options opts;
+ if (opts.parse(argc, argv)) {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = opts.tx ? connection.createTransactionalSession() : connection.createSession();
+ Sender sender = session.createSender(opts.address);
+ if (opts.capacity) sender.setCapacity(opts.capacity);
+ Message msg;
+ msg.setDurable(opts.durable);
+ if (opts.ttl) {
+ msg.setTtl(Duration(opts.ttl));
+ }
+ if (opts.priority) {
+ msg.setPriority(opts.priority);
+ }
+ if (!opts.replyto.empty()) {
+ msg.setReplyTo(Address(opts.replyto));
+ }
+ if (!opts.userid.empty()) msg.setUserId(opts.userid);
+ if (!opts.id.empty()) msg.setMessageId(opts.id);
+ if (!opts.correlationid.empty()) msg.setCorrelationId(opts.correlationid);
+ opts.setProperties(msg);
+ uint sent = 0;
+ uint txCount = 0;
+ Reporter<Throughput> reporter(std::cout, opts.reportEvery, opts.reportHeader);
+
+ std::auto_ptr<ContentGenerator> contentGen;
+ if (opts.contentStdin) {
+ opts.messages = 0; // Don't limit # messages sent.
+ contentGen.reset(new GetlineContentGenerator);
+ }
+ else if (opts.entries.size() > 0)
+ contentGen.reset(new MapContentGenerator(opts));
+ else if (opts.contentSize > 0)
+ contentGen.reset(new FixedContentGenerator(string(opts.contentSize, 'X')));
+ else
+ contentGen.reset(new FixedContentGenerator(opts.contentString));
+
+ std::auto_ptr<GroupGenerator> groupGen;
+ if (!opts.groupKey.empty())
+ groupGen.reset(new GroupGenerator(opts.groupKey,
+ opts.groupPrefix,
+ opts.groupSize,
+ opts.groupRandSize,
+ opts.groupInterleave));
+
+ qpid::sys::AbsTime start = qpid::sys::now();
+ int64_t interval = 0;
+ if (opts.sendRate) interval = qpid::sys::TIME_SEC/opts.sendRate;
+
+ while (contentGen->setContent(msg)) {
+ ++sent;
+ if (opts.sequence)
+ msg.getProperties()[SN] = sent;
+ if (groupGen.get())
+ groupGen->setGroupInfo(msg);
+
+ if (opts.timestamp)
+ msg.getProperties()[TS] = int64_t(
+ qpid::sys::Duration::FromEpoch());
+ sender.send(msg);
+ reporter.message(msg);
+
+ if (opts.tx && (sent % opts.tx == 0)) {
+ if (opts.rollbackFrequency &&
+ (++txCount % opts.rollbackFrequency == 0))
+ session.rollback();
+ else
+ session.commit();
+ }
+ if (opts.messages && sent >= opts.messages) break;
+
+ if (opts.sendRate) {
+ qpid::sys::AbsTime waitTill(start, sent*interval);
+ int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill);
+ if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC);
+ }
+ }
+ if (opts.reportTotal) reporter.report();
+ for (uint i = opts.sendEos; i > 0; --i) {
+ if (opts.sequence)
+ msg.getProperties()[SN] = ++sent;
+ msg.setContent(EOS); //TODO: add in ability to send digest or similar
+ sender.send(msg);
+ }
+ if (opts.tx) {
+ if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) {
+ session.rollback();
+ } else {
+ session.commit();
+ }
+ }
+ session.sync();
+ session.close();
+ connection.close();
+ return 0;
+ }
+ return 1;
+ } catch(const std::exception& error) {
+ std::cerr << "qpid-send: " << error.what() << std::endl;
+ connection.close();
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/qpid-src-rinstall b/qpid/cpp/src/tests/qpid-src-rinstall
new file mode 100755
index 0000000000..c7473e9197
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-src-rinstall
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under onemake
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Copy the source tree and run "make install" on each of $HOSTS
+# Must be run in a configured qpid build directory.
+
+absdir() { echo `cd $1 && pwd`; }
+
+test -f config.status || { echo "Not in a configured build directory."; }
+CONFIGURE=`./config.status -V | grep '^configured by' | sed 's/^configured by \([^,]*\),.*$/\1/'`
+CONFIG_OPTIONS=`./config.status -V | grep 'with options' | sed 's/^.*with options "\([^"]*\)".*$/\1/'`
+set -ex
+rsynchosts `absdir $(dirname $CONFIGURE)/..` # Copy cpp srcdir and siblings.
+allhosts -bo rbuild.log "mkdir -p $PWD && cd $PWD && { test -f config.status || $CONFIGURE $CONFIG_OPTIONS; } && make && make -j1 install"
diff --git a/qpid/cpp/src/tests/qpid-stream.cpp b/qpid/cpp/src/tests/qpid-stream.cpp
new file mode 100644
index 0000000000..f02a484750
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-stream.cpp
@@ -0,0 +1,193 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/sys/Runnable.h>
+#include <qpid/sys/Thread.h>
+#include <qpid/sys/Time.h>
+#include <qpid/Options.h>
+#include <iostream>
+#include <string>
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::Options
+{
+ std::string url;
+ std::string address;
+ uint size;
+ uint rate;
+ bool durable;
+ uint receiverCapacity;
+ uint senderCapacity;
+ uint ackFrequency;
+
+ Args() :
+ url("amqp:tcp:127.0.0.1:5672"),
+ address("test-queue"),
+ size(512),
+ rate(1000),
+ durable(false),
+ receiverCapacity(0),
+ senderCapacity(0),
+ ackFrequency(1)
+ {
+ addOptions()
+ ("url", qpid::optValue(url, "URL"), "Url to connect to.")
+ ("address", qpid::optValue(address, "ADDRESS"), "Address to stream messages through.")
+ ("size", qpid::optValue(size, "bytes"), "Message size in bytes (content only, not headers).")
+ ("rate", qpid::optValue(rate, "msgs/sec"), "Rate at which to stream messages.")
+ ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.")
+ ("sender-capacity", qpid::optValue(senderCapacity, "N"), "Credit window (0 implies infinite window)")
+ ("receiver-capacity", qpid::optValue(receiverCapacity, "N"), "Credit window (0 implies infinite window)")
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"),
+ "Ack frequency (0 implies none of the messages will get accepted)");
+ }
+};
+
+Args opts;
+
+const std::string TS = "ts";
+
+uint64_t timestamp(const qpid::sys::AbsTime& time)
+{
+ qpid::sys::Duration t(qpid::sys::EPOCH, time);
+ return t;
+}
+
+struct Client : qpid::sys::Runnable
+{
+ virtual ~Client() {}
+ virtual void doWork(Session&) = 0;
+
+ void run()
+ {
+ Connection connection(opts.url);
+ try {
+ connection.open();
+ Session session = connection.createSession();
+ doWork(session);
+ session.close();
+ connection.close();
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ connection.close();
+ }
+ }
+
+ qpid::sys::Thread thread;
+
+ void start() { thread = qpid::sys::Thread(this); }
+ void join() { thread.join(); }
+};
+
+struct Publish : Client
+{
+ void doWork(Session& session)
+ {
+ Sender sender = session.createSender(opts.address);
+ if (opts.senderCapacity) sender.setCapacity(opts.senderCapacity);
+ Message msg(std::string(opts.size, 'X'));
+ uint64_t interval = qpid::sys::TIME_SEC / opts.rate;
+ uint64_t sent = 0, missedRate = 0;
+ qpid::sys::AbsTime start = qpid::sys::now();
+ while (true) {
+ qpid::sys::AbsTime sentAt = qpid::sys::now();
+ msg.getProperties()[TS] = timestamp(sentAt);
+ sender.send(msg);
+ ++sent;
+ qpid::sys::AbsTime waitTill(start, sent*interval);
+ qpid::sys::Duration delay(sentAt, waitTill);
+ if (delay < 0) {
+ ++missedRate;
+ } else {
+ qpid::sys::usleep(delay / qpid::sys::TIME_USEC);
+ }
+ }
+ }
+};
+
+struct Consume : Client
+{
+ void doWork(Session& session)
+ {
+ Message msg;
+ uint64_t received = 0;
+ double minLatency = std::numeric_limits<double>::max();
+ double maxLatency = 0;
+ double totalLatency = 0;
+ Receiver receiver = session.createReceiver(opts.address);
+ if (opts.receiverCapacity) receiver.setCapacity(opts.receiverCapacity);
+ while (receiver.fetch(msg)) {
+ ++received;
+ if (opts.ackFrequency && (received % opts.ackFrequency == 0)) {
+ session.acknowledge();
+ }
+ //calculate latency
+ uint64_t receivedAt = timestamp(qpid::sys::now());
+ uint64_t sentAt = msg.getProperties()[TS].asUint64();
+ double latency = ((double) (receivedAt - sentAt)) / qpid::sys::TIME_MSEC;
+
+ //update avg, min & max
+ minLatency = std::min(minLatency, latency);
+ maxLatency = std::max(maxLatency, latency);
+ totalLatency += latency;
+
+ if (received % opts.rate == 0) {
+ std::cout << "count=" << received
+ << ", avg=" << (totalLatency/received)
+ << ", min=" << minLatency
+ << ", max=" << maxLatency << std::endl;
+ }
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ Publish publish;
+ Consume consume;
+ publish.start();
+ consume.start();
+ consume.join();
+ publish.join();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << error.what() << std::endl;
+ }
+ return 1;
+}
+
+
diff --git a/qpid/cpp/src/tests/qpid-topic-listener.cpp b/qpid/cpp/src/tests/qpid-topic-listener.cpp
new file mode 100644
index 0000000000..c42e76d760
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-topic-listener.cpp
@@ -0,0 +1,209 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 qpid-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/AsyncSession.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/SystemInfo.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;
+
+namespace qpid {
+namespace tests {
+
+/**
+ * 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");
+ }
+};
+
+Listener::Listener(const Session& s, SubscriptionManager& m, const string& _responseq, bool tx) :
+ session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){}
+
+void Listener::received(Message& message){
+ if(!init){
+ start = now();
+ count = 0;
+ init = true;
+ cout << "Batch started." << endl;
+ }
+ string type = message.getHeaders().getAsString("TYPE");
+
+ if(string("TERMINATION_REQUEST") == type){
+ shutdown();
+ }else if(string("REPORT_REQUEST") == type){
+ subscription.accept(subscription.getUnaccepted()); // Accept everything upto this point
+ cout <<"Batch ended, sending report." << endl;
+ //send a report:
+ report();
+ init = false;
+ }else if (++count % 1000 == 0){
+ cout <<"Received " << count << " messages." << endl;
+ }
+}
+
+void Listener::shutdown(){
+ mgr.stop();
+}
+
+void Listener::report(){
+ AbsTime finish = now();
+ Duration time(start, finish);
+ stringstream reportstr;
+ reportstr << "Received " << count << " messages in "
+ << time/TIME_MSEC << " ms.";
+ Message msg(reportstr.str(), responseQueue);
+ msg.getHeaders().setString("TYPE", "REPORT");
+ session.messageTransfer(arg::destination="amq.direct", arg::content=msg, arg::acceptMode=1);
+ if(transactional){
+ sync(session).txCommit();
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+/**
+ * The main routine creates a Listener instance and sets it up to
+ * 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 << "qpid-topic-listener: " << qpid::sys::SystemInfo::getProcessId();
+ session.messageTransfer(arg::content=Message(msg_str.str(), args.statusqueue));
+ cout << "Ready status put on queue '" << args.statusqueue << "'" << endl;
+ }
+
+ if (args.transactional) {
+ session.txSelect();
+ }
+
+ cout << "qpid-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 << "qpid-topic-listener: " << error.what() << endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/qpid-topic-publisher.cpp b/qpid/cpp/src/tests/qpid-topic-publisher.cpp
new file mode 100644
index 0000000000..f9107b90d0
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-topic-publisher.cpp
@@ -0,0 +1,230 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 qpid-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 "qpid/sys/Time.h"
+#include <cstdlib>
+#include <iostream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+/**
+ * The publishing logic is defined in this class. It implements
+ * message listener and can therfore be used to receive messages sent
+ * back by the subscribers.
+ */
+class Publisher {
+ 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");
+ }
+};
+
+Publisher::Publisher(const AsyncSession& _session, const string& _controlTopic, bool tx, bool d) :
+ session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d)
+{
+ mgr.subscribe(queue, "response");
+}
+
+int64_t Publisher::publish(int msgs, int listeners, int size){
+ Message msg(generateData(size), controlTopic);
+ if (durable) {
+ msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+ AbsTime start = now();
+
+ for(int i = 0; i < msgs; i++){
+ session.messageTransfer(arg::content=msg, arg::destination="amq.topic", arg::acceptMode=1);
+ }
+ //send report request
+ Message reportRequest("", controlTopic);
+ reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST");
+ session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic", arg::acceptMode=1);
+ if(transactional){
+ sync(session).txCommit();
+ }
+ //wait for a response from each listener (TODO, could log these)
+ for (int i = 0; i < listeners; i++) {
+ Message report = queue.pop();
+ }
+
+ if(transactional){
+ sync(session).txCommit();
+ }
+
+ AbsTime finish = now();
+ return Duration(start, finish);
+}
+
+string Publisher::generateData(int size){
+ string data;
+ for(int i = 0; i < size; i++){
+ data += ('A' + (i / 26));
+ }
+ return data;
+}
+
+void Publisher::terminate(){
+ //send termination request
+ Message terminationRequest("", controlTopic);
+ terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST");
+ session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic", arg::acceptMode=1);
+ if(transactional){
+ session.txCommit();
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv) {
+ try{
+ Args args;
+ 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) qpid::sys::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;
+}
diff --git a/qpid/cpp/src/tests/qpid-txtest.cpp b/qpid/cpp/src/tests/qpid-txtest.cpp
new file mode 100644
index 0000000000..59ab905af7
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-txtest.cpp
@@ -0,0 +1,342 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/framing/FieldValue.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::sys;
+using std::string;
+
+namespace qpid {
+namespace tests {
+
+typedef std::vector<std::string> StringSet;
+
+struct Args : public qpid::TestOptions {
+ 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;
+ 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();
+ std::string& data = in.getData();
+ if (data.size() != opts.size) {
+ std::ostringstream oss;
+ oss << "Message size incorrect: size=" << in.getData().size() << "; expected " << opts.size;
+ throw std::runtime_error(oss.str());
+ }
+ out.setData(data);
+ out.getMessageProperties().setCorrelationId(in.getMessageProperties().getCorrelationId());
+ out.getDeliveryProperties().setDeliveryMode(in.getDeliveryProperties().getDeliveryMode());
+ session.messageTransfer(arg::content=out, arg::acceptMode=1);
+ }
+ 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();
+ }
+ session.sync();
+ }
+ } catch(const std::exception& e) {
+ std::cout << "Transfer interrupted: " << e.what() << std::endl;
+ }
+ }
+
+ void setNewXid(framing::Xid& xid) {
+ framing::Uuid uuid(true);
+ xid.setGlobalId(uuid.str());
+ }
+};
+
+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) {
+ framing::DtxRecoverResult dtxRes = session.dtxRecover().get();
+ const framing::Array& xidArr = dtxRes.getInDoubt();
+ std::vector<std::string> inDoubtXids(xidArr.size());
+ std::transform(xidArr.begin(), xidArr.end(), inDoubtXids.begin(), framing::Array::get<std::string, framing::Array::ValuePtr>);
+
+ 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;
+ }
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ opts.parse(argc, argv);
+ Controller controller;
+ if (opts.init) controller.init();
+ if (opts.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/qpid/cpp/src/tests/qpid-txtest2.cpp b/qpid/cpp/src/tests/qpid-txtest2.cpp
new file mode 100644
index 0000000000..58c48f9a8d
--- /dev/null
+++ b/qpid/cpp/src/tests/qpid-txtest2.cpp
@@ -0,0 +1,363 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "qpid/messaging/Connection.h"
+#include "qpid/messaging/Duration.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+#include <qpid/Options.h>
+#include <qpid/log/Logger.h>
+#include <qpid/log/Options.h>
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid::messaging;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+typedef std::vector<std::string> StringSet;
+
+struct Options : public qpid::Options {
+ bool help;
+ bool init, transfer, check;//actions
+ uint size;
+ bool durable;
+ uint queues;
+ std::string base;
+ uint msgsPerTx;
+ uint txCount;
+ uint totalMsgCount;
+ bool dtx;
+ uint capacity;
+ std::string url;
+ std::string connectionOptions;
+ qpid::log::Options log;
+ uint port;
+ bool quiet;
+ double fetchTimeout;
+
+ Options() : help(false), init(true), transfer(true), check(true),
+ size(256), durable(true), queues(2),
+ base("tx"), msgsPerTx(1), txCount(5), totalMsgCount(10),
+ capacity(1000), url("localhost"), port(0), quiet(false), fetchTimeout(5)
+ {
+ addOptions()
+ ("init", qpid::optValue(init, "yes|no"), "Declare queues and populate one with the initial set of messages.")
+ ("transfer", qpid::optValue(transfer, "yes|no"), "'Move' messages from one queue to another using transactions to ensure no message loss.")
+ ("check", qpid::optValue(check, "yes|no"), "Check that the initial messages are all still available.")
+ ("size", qpid::optValue(size, "N"), "message size")
+ ("durable", qpid::optValue(durable, "yes|no"), "use durable messages")
+ ("queues", qpid::optValue(queues, "N"), "number of queues")
+ ("queue-base-name", qpid::optValue(base, "<name>"), "base name for queues")
+ ("messages-per-tx", qpid::optValue(msgsPerTx, "N"), "number of messages transferred per transaction")
+ ("tx-count", qpid::optValue(txCount, "N"), "number of transactions per 'agent'")
+ ("total-messages", qpid::optValue(totalMsgCount, "N"), "total number of messages in 'circulation'")
+ ("capacity", qpid::optValue(capacity, "N"), "Pre-fetch window (0 implies no pre-fetch)")
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
+ ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
+ ("port,p", qpid::optValue(port, "PORT"), "(for test compatibility only, use broker option instead)")
+ ("quiet", qpid::optValue(quiet), "reduce output from test")
+ ("fetch-timeout", qpid::optValue(fetchTimeout, "SECONDS"), "Timeout for transactional fetch")
+ ("help", qpid::optValue(help), "print this usage statement");
+ add(log);
+ }
+
+ bool parse(int argc, char** argv)
+ {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (port) {
+ if (url == "localhost") {
+ std::stringstream u;
+ u << url << ":" << port;
+ url = u.str();
+ } else {
+ std::cerr << *this << std::endl << std::endl
+ << "--port and --broker should not be specified together; specify full url in --broker option" << std::endl;
+ return false;
+ }
+
+ }
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::cout << *this << std::endl << std::endl
+ << "Transactionally moves messages between queues" << std::endl;
+ return false;
+ }
+ if (totalMsgCount < msgsPerTx) {
+ totalMsgCount = msgsPerTx; // Must have at least msgsPerTx total messages.
+ }
+ return true;
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ return false;
+ }
+ }
+};
+
+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 digits;
+ digits << count;
+ std::ostringstream out;
+ out << base << "-" << std::setw(digits.str().size()) << std::setfill('0') << (i+1);
+ collection.push_back(out.str());
+ }
+}
+
+struct Client
+{
+ const Options& opts;
+ Connection connection;
+ Session session;
+
+ Client(const Options& o, bool transactional=false) : opts(o), connection(opts.url, opts.connectionOptions)
+ {
+ connection.open();
+ session = transactional ? connection.createTransactionalSession() : connection.createSession();
+ }
+
+ virtual ~Client()
+ {
+ try {
+ session.sync();
+ session.close();
+ connection.close();
+ } catch(const std::exception& e) {
+ std::cout << "Client shutdown: " << e.what() << std::endl;
+ }
+ }
+};
+
+struct TransactionalClient : Client
+{
+ TransactionalClient(const Options& o) : Client(o, true) {}
+ virtual ~TransactionalClient() {}
+};
+
+struct Transfer : public TransactionalClient, public Runnable
+{
+ const std::string target;
+ const std::string source;
+ Thread thread;
+ bool failed;
+
+ Transfer(const std::string& to, const std::string& from, const Options& opts) : TransactionalClient(opts), target(to), source(from), failed(false) {}
+
+ void run()
+ {
+ try {
+
+ Sender sender(session.createSender(target));
+ Receiver receiver(session.createReceiver(source));
+ receiver.setCapacity(opts.capacity);
+ for (uint t = 0; t < opts.txCount;) {
+ std::ostringstream id;
+ id << source << ">" << target << ":" << t+1;
+ try {
+ for (uint m = 0; m < opts.msgsPerTx; m++) {
+ Message msg = receiver.fetch(Duration::SECOND*uint64_t(opts.fetchTimeout));
+ if (msg.getContentSize() != opts.size) {
+ std::ostringstream oss;
+ oss << "Message size incorrect: size=" << msg.getContentSize() << "; expected " << opts.size;
+ throw std::runtime_error(oss.str());
+ }
+ sender.send(msg);
+ }
+ session.commit();
+ t++;
+ if (!opts.quiet) std::cout << "Transaction " << id.str() << " of " << opts.txCount << " committed successfully" << std::endl;
+ } catch (const TransactionAborted&) {
+ std::cout << "Transaction " << id.str() << " of " << opts.txCount << " was aborted and will be retried" << std::endl;
+ session = connection.createTransactionalSession();
+ sender = session.createSender(target);
+ receiver = session.createReceiver(source);
+ receiver.setCapacity(opts.capacity);
+ }
+ }
+ sender.close();
+ receiver.close();
+ } catch(const std::exception& e) {
+ failed = true;
+ QPID_LOG(error, "Transfer " << source << " to " << target << " interrupted: " << e.what());
+ }
+ }
+};
+
+namespace {
+const std::string CREATE_DURABLE("; {create:always, node:{durable:True}}");
+const std::string CREATE_NON_DURABLE("; {create:always}");
+}
+
+struct Controller : public Client
+{
+ StringSet ids;
+ StringSet queues;
+
+ Controller(const Options& opts) : Client(opts)
+ {
+ generateSet(opts.base, opts.queues, queues);
+ generateSet("msg", opts.totalMsgCount, ids);
+ }
+
+ void init()
+ {
+ Message msg(generateData(opts.size));
+ msg.setDurable(opts.durable);
+
+ for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) {
+ std::string address = *i + (opts.durable ? CREATE_DURABLE : CREATE_NON_DURABLE);
+
+ // Clear out any garbage on queues.
+ Receiver receiver = session.createReceiver(address);
+ Message rmsg;
+ uint count(0);
+ while (receiver.fetch(rmsg, Duration::IMMEDIATE)) ++count;
+ session.acknowledge();
+ receiver.close();
+ if (!opts.quiet) std::cout << "Cleaned up " << count << " messages from " << *i << std::endl;
+
+ Sender sender = session.createSender(address);
+ if (i == queues.begin()) {
+ for (StringSet::iterator i = ids.begin(); i != ids.end(); i++) {
+ msg.setCorrelationId(*i);
+ sender.send(msg);
+ }
+ }
+ sender.close();
+ }
+ }
+
+ 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, opts));
+ agents.back().thread = Thread(agents.back());
+ }
+
+ for (boost::ptr_vector<Transfer>::iterator i = agents.begin(); i != agents.end(); i++)
+ i->thread.join();
+ for (boost::ptr_vector<Transfer>::iterator i = agents.begin(); i != agents.end(); i++)
+ if (i->failed)
+ throw std::runtime_error("Transfer agents failed");
+ }
+
+ int check()
+ {
+ StringSet drained;
+ //drain each queue and verify the correct set of messages are available
+ for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) {
+ Receiver receiver = session.createReceiver(*i);
+ uint count(0);
+ Message msg;
+ while (receiver.fetch(msg, Duration::IMMEDIATE)) {
+ //add correlation ids of received messages to drained
+ drained.push_back(msg.getCorrelationId());
+ ++count;
+ }
+ session.acknowledge();
+ receiver.close();
+ 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;
+ }
+ }
+};
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ try {
+ Options opts;
+ if (opts.parse(argc, argv)) {
+ Controller controller(opts);
+ if (opts.init) controller.init();
+ if (opts.transfer) controller.transfer();
+ if (opts.check) return controller.check();
+ return 0;
+ }
+ return 1;
+ } catch(const std::exception& e) {
+ std::cerr << argv[0] << ": " << e.what() << std::endl;
+ }
+ return 2;
+}
diff --git a/qpid/cpp/src/tests/qpidd-empty.conf b/qpid/cpp/src/tests/qpidd-empty.conf
new file mode 100644
index 0000000000..cf3f19fba0
--- /dev/null
+++ b/qpid/cpp/src/tests/qpidd-empty.conf
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# An empty configuration file.
+# Used when running tests to avoid picking up configuration
+# installed in the default place.
diff --git a/qpid/cpp/src/tests/qpidd-p0 b/qpid/cpp/src/tests/qpidd-p0
new file mode 100755
index 0000000000..1f7807afd2
--- /dev/null
+++ b/qpid/cpp/src/tests/qpidd-p0
@@ -0,0 +1,46 @@
+#!/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.
+#
+
+# Wrapper script to allocate a port and fork a broker to listen on it.
+#
+# Instead of this:
+# qpidd --port 0 <qpidd-args...>
+# do this:
+# qpidd-p0 <qpidd-args...>
+#
+# The port is bound by python code, and then handed over to the broker via the
+# --socket-fd option. This avoids problems with the qpidd --port 0 option which
+# ocassional fails with an "address in use" error. It's not clear why --port 0
+# doesn't work, it may be to do with the way qpidd binds a port to multiple
+# addresses on a multi-homed host.
+#
+
+import subprocess, socket, time, os, sys
+
+s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+s.bind(("", 0))
+s.listen(5)
+port = s.getsockname()[1]
+print port
+sys.stdout.flush()
+if len(sys.argv) > 1:
+ cmd = sys.argv[1:] + ["--socket-fd", str(s.fileno()), "--listen-disable=tcp"]
+ os.execvp(sys.argv[1], cmd)
diff --git a/qpid/cpp/src/tests/qpidd_qmfv2_tests.py b/qpid/cpp/src/tests/qpidd_qmfv2_tests.py
new file mode 100755
index 0000000000..2b45cb6eea
--- /dev/null
+++ b/qpid/cpp/src/tests/qpidd_qmfv2_tests.py
@@ -0,0 +1,278 @@
+#!/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.
+#
+
+# Runs QMF tests against a broker running with QMFv1 disabled. This forces the
+# broker to use QMFv2 only. This is necessary as if there is a bug in V2, some
+# V1 operations may hide that (esp. asynchonous notifications)
+
+
+import sys, shutil, os
+from time import sleep
+from brokertest import *
+from qpid.messaging import Message
+try: import qmf.console
+except: print "Cannot import module qmf.console, skipping tests"; exit(0);
+
+import qpid.messaging, brokertest
+brokertest.qm = qpid.messaging # TODO aconway 2014-04-04: Tests fail with SWIG client.
+
+class ConsoleTest(BrokerTest):
+ """
+ Test QMFv2 support using the qmf.console library.
+ """
+ PUB_INTERVAL=1
+
+ def setUp(self):
+ BrokerTest.setUp(self)
+
+ def _startBroker(self, QMFv1=False ):
+ self._broker_is_v1 = QMFv1
+ if self._broker_is_v1:
+ args = ["--mgmt-qmf1=yes", "--mgmt-qmf2=no"]
+ else:
+ args = ["--mgmt-qmf1=no", "--mgmt-qmf2=yes"]
+
+ args.append("--mgmt-pub-interval=%d" % self.PUB_INTERVAL)
+ self.broker = BrokerTest.broker(self, args)
+
+
+ def _myStartQmf(self, broker, console=None):
+ # I manually set up the QMF session here rather than call the startQmf
+ # method from BrokerTest as I can guarantee the console library is used
+ # (assuming BrokerTest's implementation of startQmf could change)
+ self.qmf_session = qmf.console.Session(console)
+ self.qmf_broker = self.qmf_session.addBroker("%s:%s" % (broker.host(),
+ broker.port()))
+
+ def _create_queue( self, q_name, args={} ):
+ broker = self.qmf_session.getObjects(_class="broker")[0]
+ result = broker.create("queue", q_name, args, False)
+ self.assertEqual(result.status, 0, result)
+
+
+ def _test_method_call(self):
+ """ Verify method calls work, and check the behavior of getObjects()
+ call
+ """
+ self._myStartQmf( self.broker )
+ self._create_queue( "fleabag", {"auto-delete":True} )
+
+ qObj = None
+ queues = self.qmf_session.getObjects(_class="queue")
+ for q in queues:
+ if q.name == "fleabag":
+ qObj = q
+ break
+ self.assertNotEqual(qObj, None, "Failed to get queue object")
+ #print qObj
+
+ def _test_unsolicited_updates(self):
+ """ Verify that the Console callbacks work
+ """
+
+ class Handler(qmf.console.Console):
+ def __init__(self):
+ self.v1_oids = 0
+ self.v1_events = 0
+ self.v2_oids = 0
+ self.v2_events = 0
+ self.broker_info = []
+ self.broker_conn = []
+ self.newpackage = []
+ self.newclass = []
+ self.agents = []
+ self.events = []
+ self.updates = {} # holds the objects by OID
+ self.heartbeats = []
+
+ def brokerInfo(self, broker):
+ #print "brokerInfo:", broker
+ self.broker_info.append(broker)
+ def brokerConnected(self, broker):
+ #print "brokerConnected:", broker
+ self.broker_conn.append(broker)
+ def newPackage(self, name):
+ #print "newPackage:", name
+ self.newpackage.append(name)
+ def newClass(self, kind, classKey):
+ #print "newClass:", kind, classKey
+ self.newclass.append( (kind, classKey) )
+ def newAgent(self, agent):
+ #print "newAgent:", agent
+ self.agents.append( agent )
+ def event(self, broker, event):
+ #print "EVENT %s" % event
+ self.events.append(event)
+ if event.isV2:
+ self.v2_events += 1
+ else:
+ self.v1_events += 1
+
+ def heartbeat(self, agent, timestamp):
+ #print "Heartbeat %s" % agent
+ self.heartbeats.append( (agent, timestamp) )
+
+ # generic handler for objectProps and objectStats
+ def _handle_obj_update(self, record):
+ oid = record.getObjectId()
+ if oid.isV2:
+ self.v2_oids += 1
+ else:
+ self.v1_oids += 1
+
+ if oid not in self.updates:
+ self.updates[oid] = record
+ else:
+ self.updates[oid].mergeUpdate( record )
+
+ def objectProps(self, broker, record):
+ assert len(record.getProperties()), "objectProps() invoked with no properties?"
+ self._handle_obj_update(record)
+
+ def objectStats(self, broker, record):
+ assert len(record.getStatistics()), "objectStats() invoked with no properties?"
+ self._handle_obj_update(record)
+
+ handler = Handler()
+ self._myStartQmf( self.broker, handler )
+ # this should force objectProps, queueDeclare Event callbacks
+ self._create_queue( "fleabag", {"auto-delete":True} )
+ # this should force objectStats callback
+ self.broker.send_message( "fleabag", Message("Hi") )
+ # and we should get a few heartbeats
+ sleep(self.PUB_INTERVAL)
+ self.broker.send_message( "fleabag", Message("Hi") )
+ sleep(self.PUB_INTERVAL)
+ self.broker.send_message( "fleabag", Message("Hi") )
+ sleep(self.PUB_INTERVAL * 2)
+
+ assert handler.broker_info, "No BrokerInfo callbacks received"
+ assert handler.broker_conn, "No BrokerConnected callbacks received"
+ assert handler.newpackage, "No NewPackage callbacks received"
+ assert handler.newclass, "No NewClass callbacks received"
+ assert handler.agents, "No NewAgent callbacks received"
+ assert handler.events, "No event callbacks received"
+ assert handler.updates, "No updates received"
+ assert handler.heartbeats, "No heartbeat callbacks received"
+
+ # now verify updates for queue "fleabag" were received, and the
+ # msgDepth statistic is correct
+
+ msgs = 0
+ for o in handler.updates.itervalues():
+ key = o.getClassKey()
+ if key and key.getClassName() == "queue" and o.name == "fleabag":
+ assert o.msgDepth, "No update to msgDepth statistic!"
+ msgs = o.msgDepth
+ break
+ assert msgs == 3, "msgDepth statistics not accurate!"
+
+ # verify that the published objects were of the correct QMF version
+ if self._broker_is_v1:
+ assert handler.v1_oids and handler.v2_oids == 0, "QMFv2 updates received while in V1-only mode!"
+ assert handler.v1_events and handler.v2_events == 0, "QMFv2 events received while in V1-only mode!"
+ else:
+ assert handler.v2_oids and handler.v1_oids == 0, "QMFv1 updates received while in V2-only mode!"
+ assert handler.v2_events and handler.v1_events == 0, "QMFv1 events received while in V2-only mode!"
+
+ def _test_async_method(self):
+ class Handler (qmf.console.Console):
+ def __init__(self):
+ self.cv = Condition()
+ self.xmtList = {}
+ self.rcvList = {}
+
+ def methodResponse(self, broker, seq, response):
+ self.cv.acquire()
+ try:
+ self.rcvList[seq] = response
+ finally:
+ self.cv.release()
+
+ def request(self, broker, count):
+ self.count = count
+ for idx in range(count):
+ self.cv.acquire()
+ try:
+ seq = broker.echo(idx, "Echo Message", _async = True)
+ self.xmtList[seq] = idx
+ finally:
+ self.cv.release()
+
+ def check(self):
+ if self.count != len(self.xmtList):
+ return "fail (attempted send=%d, actual sent=%d)" % (self.count, len(self.xmtList))
+ lost = 0
+ mismatched = 0
+ for seq in self.xmtList:
+ value = self.xmtList[seq]
+ if seq in self.rcvList:
+ result = self.rcvList.pop(seq)
+ if result.sequence != value:
+ mismatched += 1
+ else:
+ lost += 1
+ spurious = len(self.rcvList)
+ if lost == 0 and mismatched == 0 and spurious == 0:
+ return "pass"
+ else:
+ return "fail (lost=%d, mismatch=%d, spurious=%d)" % (lost, mismatched, spurious)
+
+ handler = Handler()
+ self._myStartQmf(self.broker, handler)
+ broker = self.qmf_session.getObjects(_class="broker")[0]
+ handler.request(broker, 20)
+ sleep(1)
+ self.assertEqual(handler.check(), "pass")
+
+ def test_method_call(self):
+ self._startBroker()
+ self._test_method_call()
+
+ def test_unsolicited_updates(self):
+ self._startBroker()
+ self._test_unsolicited_updates()
+
+ def test_async_method(self):
+ self._startBroker()
+ self._test_async_method()
+
+ # For now, include "QMFv1 only" tests. Once QMFv1 is deprecated, these can
+ # be removed
+
+ def test_method_call_v1(self):
+ self._startBroker(QMFv1=True)
+ self._test_method_call()
+
+ def test_unsolicited_updates_v1(self):
+ self._startBroker(QMFv1=True)
+ self._test_unsolicited_updates()
+
+ def test_async_method_v1(self):
+ self._startBroker(QMFv1=True)
+ self._test_async_method()
+
+
+
+if __name__ == "__main__":
+ shutil.rmtree("brokertest.tmp", True)
+ os.execvp("qpid-python-test",
+ ["qpid-python-test", "-m", "qpidd_qmfv2_tests"] + sys.argv[1:])
+
diff --git a/qpid/cpp/src/tests/queue_flow_limit_tests.py b/qpid/cpp/src/tests/queue_flow_limit_tests.py
new file mode 100644
index 0000000000..83e31e979b
--- /dev/null
+++ b/qpid/cpp/src/tests/queue_flow_limit_tests.py
@@ -0,0 +1,376 @@
+#!/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
+from qpid.messaging import Connection
+from threading import Thread
+from time import sleep, time
+from os import environ, popen
+
+class QueueFlowLimitTests(TestBase010):
+
+ _timeout = 100
+
+ def __getattr__(self, name):
+ if name == "assertGreater":
+ return lambda a, b: self.failUnless(a > b)
+ else:
+ raise AttributeError
+
+ def _create_queue(self, name,
+ stop_count=None, resume_count=None,
+ stop_size=None, resume_size=None,
+ max_size=None, max_count=None):
+ """ Create a queue with the given flow settings via the queue.declare
+ command.
+ """
+ args={}
+ if (stop_count is not None):
+ args["qpid.flow_stop_count"] = stop_count;
+ if (resume_count is not None):
+ args["qpid.flow_resume_count"] = resume_count;
+ if (stop_size is not None):
+ args["qpid.flow_stop_size"] = stop_size;
+ if (resume_size is not None):
+ args["qpid.flow_resume_size"] = resume_size;
+ if (max_size is not None):
+ args["qpid.max_size"] = max_size;
+ if (max_count is not None):
+ args["qpid.max_count"] = max_count;
+
+ broker = self.qmf.getObjects(_class="broker")[0]
+ rc = broker.create( "queue", name, args, True )
+ self.assertEqual(rc.status, 0, rc)
+
+ qs = self.qmf.getObjects(_class="queue")
+ for i in qs:
+ if i.name == name:
+ # verify flow settings
+ if (stop_count is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_stop_count"), stop_count)
+ if (resume_count is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_resume_count"), resume_count)
+ if (stop_size is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_stop_size"), stop_size)
+ if (resume_size is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_resume_size"), resume_size)
+ if (max_size is not None):
+ self.assertEqual(i.arguments.get("qpid.max_size"), max_size)
+ if (max_count is not None):
+ self.assertEqual(i.arguments.get("qpid.max_count"), max_count)
+ self.failIf(i.flowStopped)
+ return i.getObjectId()
+ self.fail("Unable to create queue '%s'" % name)
+ return None
+
+
+ def _delete_queue(self, name):
+ """ Delete a named queue
+ """
+ broker = self.qmf.getObjects(_class="broker")[0]
+ rc = broker.delete( "queue", name, {} )
+ self.assertEqual(rc.status, 0, rc)
+
+
+ def _start_qpid_send(self, queue, count, content="X", capacity=100):
+ """ Use the qpid-send client to generate traffic to a queue.
+ """
+ command = "qpid-send" + \
+ " -b" + " %s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --content-string " + str(content) \
+ + " --capacity " + str(capacity)
+ return popen(command)
+
+ def _start_qpid_receive(self, queue, count, timeout=5):
+ """ Use the qpid-receive client to consume from a queue.
+ Note well: prints one line of text to stdout for each consumed msg.
+ """
+ command = "qpid-receive" + \
+ " -b " + "%s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --timeout " + str(timeout) \
+ + " --print-content yes"
+ return popen(command)
+
+ def test_qpid_config_cmd(self):
+ """ Test the qpid-config command's ability to configure a queue's flow
+ control thresholds.
+ """
+ tool = environ.get("QPID_CONFIG_EXEC")
+ if tool:
+ command = tool + \
+ " --broker=%s:%s " % (self.broker.host, self.broker.port) \
+ + "add queue test01 --flow-stop-count=999" \
+ + " --flow-resume-count=55 --flow-stop-size=5000000" \
+ + " --flow-resume-size=100000"
+ cmd = popen(command)
+ rc = cmd.close()
+ self.assertEqual(rc, None)
+
+ # now verify the settings
+ self.startQmf();
+ qs = self.qmf.getObjects(_class="queue")
+ for i in qs:
+ if i.name == "test01":
+ self.assertEqual(i.arguments.get("qpid.flow_stop_count"), 999)
+ self.assertEqual(i.arguments.get("qpid.flow_resume_count"), 55)
+ self.assertEqual(i.arguments.get("qpid.flow_stop_size"), 5000000)
+ self.assertEqual(i.arguments.get("qpid.flow_resume_size"), 100000)
+ self.failIf(i.flowStopped)
+ break;
+ self.assertEqual(i.name, "test01")
+ self._delete_queue("test01")
+
+
+ def test_flow_count(self):
+ """ Create a queue with count-based flow limit. Spawn several
+ producers which will exceed the limit. Verify limit exceeded. Consume
+ all messages. Verify flow control released.
+ """
+ self.startQmf();
+ oid = self._create_queue("test-q", stop_count=373, resume_count=229)
+ self.assertEqual(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount, 0)
+
+ sndr1 = self._start_qpid_send("test-q", count=1213, content="XXX", capacity=50);
+ sndr2 = self._start_qpid_send("test-q", count=797, content="Y", capacity=13);
+ sndr3 = self._start_qpid_send("test-q", count=331, content="ZZZZZ", capacity=149);
+ totalMsgs = 1213 + 797 + 331
+
+ # wait until flow control is active
+ deadline = time() + self._timeout
+ while (not self.qmf.getObjects(_objectId=oid)[0].flowStopped) and \
+ time() < deadline:
+ pass
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ depth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(depth, 373)
+
+ # now wait until the enqueues stop happening - ensure that
+ # not all msgs have been sent (senders are blocked)
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ while depth != newDepth:
+ depth = newDepth;
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(totalMsgs, depth)
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("test-q",
+ count=totalMsgs)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+ count += 1;
+ x = rcvr.readline()
+
+ sndr1.close();
+ sndr2.close();
+ sndr3.close();
+ rcvr.close();
+
+ self.assertEqual(count, totalMsgs)
+ self.failIf(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount)
+
+ self._delete_queue("test-q")
+
+
+ def test_flow_size(self):
+ """ Create a queue with size-based flow limit. Spawn several
+ producers which will exceed the limit. Verify limit exceeded. Consume
+ all messages. Verify flow control released.
+ """
+ self.startQmf();
+ oid = self._create_queue("test-q", stop_size=351133, resume_size=251143)
+
+ sndr1 = self._start_qpid_send("test-q", count=1699, content="X"*439, capacity=53);
+ sndr2 = self._start_qpid_send("test-q", count=1129, content="Y"*631, capacity=13);
+ sndr3 = self._start_qpid_send("test-q", count=881, content="Z"*823, capacity=149);
+ totalMsgs = 1699 + 1129 + 881
+
+ # wait until flow control is active
+ deadline = time() + self._timeout
+ while (not self.qmf.getObjects(_objectId=oid)[0].flowStopped) and \
+ time() < deadline:
+ pass
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ self.assertGreater(self.qmf.getObjects(_objectId=oid)[0].byteDepth, 351133)
+
+ # now wait until the enqueues stop happening - ensure that
+ # not all msgs have been sent (senders are blocked)
+ depth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ while depth != newDepth:
+ depth = newDepth;
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(totalMsgs, depth)
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("test-q",
+ count=totalMsgs)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+ count += 1;
+ x = rcvr.readline()
+
+ sndr1.close();
+ sndr2.close();
+ sndr3.close();
+ rcvr.close();
+
+ self.assertEqual(count, totalMsgs)
+ self.failIf(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+
+ self._delete_queue("test-q")
+
+
+ def verify_limit(self, testq):
+ """ run a limit check against the testq object
+ """
+
+ testq.mgmt = self.qmf.getObjects(_objectId=testq.oid)[0]
+
+ # fill up the queue, waiting until flow control is active
+ sndr1 = self._start_qpid_send(testq.mgmt.name, count=testq.sendCount, content=testq.content)
+ deadline = time() + self._timeout
+ while (not testq.mgmt.flowStopped) and time() < deadline:
+ testq.mgmt.update()
+
+ self.failUnless(testq.verifyStopped())
+
+ # now consume enough messages to drop below the flow resume point, and
+ # verify flow control is released.
+ rcvr = self._start_qpid_receive(testq.mgmt.name, count=testq.consumeCount)
+ rcvr.readlines() # prints a line for each received msg
+ rcvr.close();
+
+ # we should now be below the resume threshold
+ self.failUnless(testq.verifyResumed())
+
+ self._delete_queue(testq.mgmt.name)
+ sndr1.close();
+
+
+ def test_default_flow_count(self):
+ """ Create a queue with count-based size limit, and verify the computed
+ thresholds using the broker's default ratios.
+ """
+ class TestQ:
+ def __init__(self, oid):
+ # Use the broker-wide default flow thresholds of 80%/70% (see
+ # run_queue_flow_limit_tests) to base the thresholds off the
+ # queue's max_count configuration parameter
+ # max_count == 1000 -> stop == 800, resume == 700
+ self.oid = oid
+ self.sendCount = 1000
+ self.consumeCount = 301 # (send - resume) + 1 to reenable flow
+ self.content = "X"
+ self.mgmt = None
+ def verifyStopped(self):
+ self.mgmt.update()
+ return self.mgmt.flowStopped and (self.mgmt.msgDepth > 800)
+ def verifyResumed(self):
+ self.mgmt.update()
+ return (not self.mgmt.flowStopped) and (self.mgmt.msgDepth < 700)
+
+ self.startQmf();
+ oid = self._create_queue("test-X", max_count=1000)
+ self.verify_limit(TestQ(oid))
+
+
+ def test_default_flow_size(self):
+ """ Create a queue with byte-based size limit, and verify the computed
+ thresholds using the broker's default ratios.
+ """
+ class TestQ:
+ def __init__(self, oid):
+ # Use the broker-wide default flow thresholds of 80%/70% (see
+ # run_queue_flow_limit_tests) to base the thresholds off the
+ # queue's max_count configuration parameter
+ # max_size == 10000 -> stop == 8000 bytes, resume == 7000 bytes
+ self.oid = oid
+ self.sendCount = 2000
+ self.consumeCount = 601 # (send - resume) + 1 to reenable flow
+ self.content = "XXXXX" # 5 bytes per message sent.
+ self.mgmt = None
+ def verifyStopped(self):
+ self.mgmt.update()
+ return self.mgmt.flowStopped and (self.mgmt.byteDepth > 8000)
+ def verifyResumed(self):
+ self.mgmt.update()
+ return (not self.mgmt.flowStopped) and (self.mgmt.byteDepth < 7000)
+
+ self.startQmf();
+ oid = self._create_queue("test-Y", max_size=10000)
+ self.verify_limit(TestQ(oid))
+
+
+ def test_blocked_queue_delete(self):
+ """ Verify that blocked senders are unblocked when a queue that is flow
+ controlled is deleted.
+ """
+
+ class BlockedSender(Thread):
+ def __init__(self, tester, queue, count, capacity=10):
+ self.tester = tester
+ self.queue = queue
+ self.count = count
+ self.capacity = capacity
+ Thread.__init__(self)
+ self.done = False
+ self.start()
+ def run(self):
+ # spawn qpid-send
+ p = self.tester._start_qpid_send(self.queue,
+ self.count,
+ self.capacity)
+ p.close() # waits for qpid-send to complete
+ self.done = True
+
+ self.startQmf();
+ oid = self._create_queue("kill-q", stop_size=10, resume_size=2)
+ q = self.qmf.getObjects(_objectId=oid)[0]
+ self.failIf(q.flowStopped)
+
+ sender = BlockedSender(self, "kill-q", count=100)
+ # wait for flow control
+ deadline = time() + self._timeout
+ while (not q.flowStopped) and time() < deadline:
+ q.update()
+
+ self.failUnless(q.flowStopped)
+ self.failIf(sender.done) # sender blocked
+
+ self._delete_queue("kill-q")
+ sender.join(5)
+ self.failIf(sender.isAlive())
+ self.failUnless(sender.done)
+
+
+
+
diff --git a/qpid/cpp/src/tests/queue_redirect.py b/qpid/cpp/src/tests/queue_redirect.py
new file mode 100644
index 0000000000..8a7b4c244b
--- /dev/null
+++ b/qpid/cpp/src/tests/queue_redirect.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+import qpid
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import uuid4
+from qpid.testlib import TestBase010
+from qmf.console import Session
+from qpid.datatypes import Message
+import qpid.messaging
+from time import sleep
+from os import environ, popen
+
+class ACLFile:
+ def __init__(self, policy='data_dir/policy.acl'):
+ self.f = open(policy,'w')
+
+ def write(self,line):
+ self.f.write(line)
+
+ def close(self):
+ self.f.close()
+
+class QueueredirectTests(TestBase010):
+
+ def get_session(self, user, passwd):
+ socket = connect(self.broker.host, self.broker.port)
+ connection = Connection (sock=socket, username=user, password=passwd,
+ mechanism="PLAIN")
+ connection.start()
+ return connection.session(str(uuid4()))
+
+ def reload_acl(self):
+ result = None
+ try:
+ self.broker_access.reloadAclFile()
+ except Exception, e:
+ result = str(e)
+ return result
+
+ def get_acl_file(self):
+ return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl"))
+
+ def setUp(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ TestBase010.setUp(self)
+ self.startBrokerAccess()
+ self.reload_acl()
+
+ def tearDown(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ self.reload_acl()
+ TestBase010.tearDown(self)
+
+
+ def redirect(self, srcQueue, tgtQueue, expectPass, failMessage):
+ try:
+ result = {}
+ result = self.broker_access.Redirect(srcQueue, tgtQueue)
+ if not expectPass:
+ self.fail("src:" + srcQueue + ", tgt:" + tgtQueue + " - " + failMessage)
+ except Exception, e:
+ if expectPass:
+ self.fail("src:" + srcQueue + ", tgt:" + tgtQueue + " - " + failMessage)
+
+ def create_queue(self, session, name, autoDelete):
+ try:
+ session.queue_declare(queue=name, auto_delete=autoDelete)
+ except Exception, e:
+ self.fail("Should allow create queue " + name)
+
+ def _start_qpid_send(self, queue, count, content="X", capacity=100):
+ """ Use the qpid-send client to generate traffic to a queue.
+ """
+ command = "qpid-send" + \
+ " -b" + " %s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --content-string " + str(content) \
+ + " --capacity " + str(capacity)
+ return popen(command)
+
+ def _start_qpid_receive(self, queue, count, timeout=5):
+ """ Use the qpid-receive client to consume from a queue.
+ Note well: prints one line of text to stdout for each consumed msg.
+ """
+ command = "qpid-receive" + \
+ " -b " + "%s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --timeout " + str(timeout) \
+ + " --print-content yes"
+ return popen(command)
+
+
+
+ #=====================================
+ # QT queue tests
+ #=====================================
+
+ def test_010_deny_backing_up_a_nonexistant_queue(self):
+ session = self.get_session('bob','bob')
+ self.redirect("A010", "A010", False, "Should not allow redirect to non-existent queue A010")
+ session.close()
+
+ def test_020_deny_destroy_redirect(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A020", False)
+ self.redirect("A020", "", False, "Should not allow destroying redirect")
+ session.close()
+
+ def test_030_deny_redirecting_to_nonexistent_queue(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A030", False)
+ self.redirect("A030", "Axxx", False, "Should not allow redirect with non-existent queue Axxx")
+ session.close()
+
+ def test_040_deny_queue_redirecting_to_itself(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A040", False)
+ self.redirect("A040", "A040", False, "Should not allow redirect with itself")
+ session.close()
+
+ def test_050_deny_redirecting_autodelete_queue(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A050", True)
+ self.create_queue(session, "B050", False)
+ self.redirect("A050", "B050", False, "Should not allow redirect with autodelete source queue")
+ self.redirect("B050", "A050", False, "Should not allow redirect with autodelete target queue")
+ session.close()
+
+ def test_100_create_redirect_queue_pair(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A100", False)
+ self.create_queue(session, "B100", False)
+ self.redirect("A100", "B100", True, "Should allow redirect")
+ session.close()
+
+ def test_110_deny_adding_second_redirect_to_queue(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A110", False)
+ self.create_queue(session, "B110", False)
+ self.create_queue(session, "C110", False)
+ self.redirect("A110", "B110", True, "Should allow redirect")
+ self.redirect("A110", "C110", False, "Should deny second redirect")
+ self.redirect("C110", "B110", False, "Should deny second redirect")
+ session.close()
+
+ def test_120_verify_redirect_to_target(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A120", False)
+ self.create_queue(session, "B120", False)
+
+ # Send messages to original queue
+ sndr1 = self._start_qpid_send("A120", count=5, content="A120-before-rebind");
+ sndr1.close()
+
+ # redirect
+ self.redirect("A120", "B120", True, "Should allow redirect")
+
+ # Send messages to original queue
+ sndr2 = self._start_qpid_send("A120", count=3, content="A120-after-rebind");
+ sndr2.close()
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("A120",
+ count=5)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+# print "Read from A120 " + x
+ count += 1;
+ x = rcvr.readline()
+
+ self.assertEqual(count, 5)
+
+ # drain the queue
+ rcvrB = self._start_qpid_receive("B120",
+ count=3)
+ count = 0;
+ x = rcvrB.readline() # prints a line for each received msg
+ while x:
+# print "Read from B120 " + x
+ count += 1;
+ x = rcvrB.readline()
+
+ self.assertEqual(count, 3)
+
+ ###session.close()
+
+ def test_140_verify_redirect_to_source(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A140", False)
+ self.create_queue(session, "B140", False)
+
+ # Send messages to target queue - these go onto B
+ sndr1 = self._start_qpid_send("B140", count=5, content="B140-before-rebind");
+ sndr1.close()
+
+ # redirect
+ self.redirect("A140", "B140", True, "Should allow redirect")
+
+ # Send messages to target queue - these go onto A
+ sndr2 = self._start_qpid_send("B140", count=3, content="B140-after-rebind");
+ sndr2.close()
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("B140", count=5)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+ # print "Read from B140 " + x
+ count += 1;
+ x = rcvr.readline()
+
+ self.assertEqual(count, 5)
+
+ # drain the queue
+ rcvrB = self._start_qpid_receive("A140", count=3)
+ count = 0;
+ x = rcvrB.readline() # prints a line for each received msg
+ while x:
+ # print "Read from A140 " + x
+ count += 1;
+ x = rcvrB.readline()
+
+ self.assertEqual(count, 3)
+
+ ###session.close()
+
+ def test_150_queue_deletion_destroys_redirect(self):
+ session = self.get_session('bob','bob')
+ self.create_queue(session, "A150", False)
+ self.create_queue(session, "B150", False)
+ self.create_queue(session, "C150", False)
+
+ # redirect
+ self.redirect("A150", "B150", True, "Should allow redirect")
+
+ self.redirect("A150", "C150", False, "A is already redirected")
+
+ alice = BrokerAdmin(self.config.broker, "bob", "bob")
+ alice.delete_queue("B150") #should pass
+
+ self.redirect("A150", "C150", True, "Should allow redirect")
+ session.close()
+
+##############################################################################################
+class BrokerAdmin:
+ def __init__(self, broker, username=None, password=None):
+ self.connection = qpid.messaging.Connection(broker)
+ if username:
+ self.connection.username = username
+ self.connection.password = password
+ self.connection.sasl_mechanisms = "PLAIN"
+ self.connection.open()
+ self.session = self.connection.session()
+ self.sender = self.session.sender("qmf.default.direct/broker")
+ self.reply_to = "responses-#; {create:always}"
+ self.receiver = self.session.receiver(self.reply_to)
+
+ def invoke(self, method, arguments):
+ content = {
+ "_object_id": {"_object_name": "org.apache.qpid.broker:broker:amqp-broker"},
+ "_method_name": method,
+ "_arguments": arguments
+ }
+ request = qpid.messaging.Message(reply_to=self.reply_to, content=content)
+ request.properties["x-amqp-0-10.app-id"] = "qmf2"
+ request.properties["qmf.opcode"] = "_method_request"
+ self.sender.send(request)
+ response = self.receiver.fetch()
+ self.session.acknowledge()
+ if response.properties['x-amqp-0-10.app-id'] == 'qmf2':
+ if response.properties['qmf.opcode'] == '_method_response':
+ return response.content['_arguments']
+ elif response.properties['qmf.opcode'] == '_exception':
+ raise Exception(response.content['_values'])
+ else: raise Exception("Invalid response received, unexpected opcode: %s" % response.properties['qmf.opcode'])
+ else: raise Exception("Invalid response received, not a qmfv2 method: %s" % response.properties['x-amqp-0-10.app-id'])
+
+ def create_exchange(self, name, exchange_type=None, options={}):
+ properties = options
+ if exchange_type: properties["exchange_type"] = exchange_type
+ self.invoke("create", {"type": "exchange", "name":name, "properties":properties})
+
+ def create_queue(self, name, properties={}):
+ self.invoke("create", {"type": "queue", "name":name, "properties":properties})
+
+ def delete_exchange(self, name):
+ self.invoke("delete", {"type": "exchange", "name":name})
+
+ def delete_queue(self, name):
+ self.invoke("delete", {"type": "queue", "name":name})
diff --git a/qpid/cpp/src/tests/quick_perftest b/qpid/cpp/src/tests/quick_perftest
new file mode 100755
index 0000000000..698af60324
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_perftest
@@ -0,0 +1,22 @@
+#!/usr/bin/env 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.
+#
+
+exec `dirname $0`/run_test ./qpid-perftest --summary --count 100
diff --git a/qpid/cpp/src/tests/quick_topictest b/qpid/cpp/src/tests/quick_topictest
new file mode 100755
index 0000000000..e44ec0f477
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_topictest
@@ -0,0 +1,30 @@
+#!/usr/bin/env 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.
+#
+
+
+# Quick and quiet topic test for make check.
+test -z "$srcdir" && srcdir=`dirname $0`
+$srcdir/topictest -s2 -m2 -b1 > topictest.log 2>&1 || {
+ echo $0 FAILED:
+ cat topictest.log
+ exit 1
+}
+rm topictest.log
diff --git a/qpid/cpp/src/tests/quick_topictest.ps1 b/qpid/cpp/src/tests/quick_topictest.ps1
new file mode 100644
index 0000000000..8f5b2caff7
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_topictest.ps1
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Quick and quiet topic test for make check.
+[string]$me = $myInvocation.InvocationName
+$srcdir = Split-Path $me
+Invoke-Expression "$srcdir\topictest.ps1 -subscribers 2 -messages 2 -batches 1" > topictest.log 2>&1
+if (!$?) {
+ "$me FAILED:"
+ cat topictest.log
+ exit 1
+}
+Remove-Item topictest.log
+exit 0
diff --git a/qpid/cpp/src/tests/quick_txtest b/qpid/cpp/src/tests/quick_txtest
new file mode 100755
index 0000000000..77e8556f1d
--- /dev/null
+++ b/qpid/cpp/src/tests/quick_txtest
@@ -0,0 +1,22 @@
+#!/usr/bin/env 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.
+#
+
+exec `dirname $0`/run_test ./qpid-txtest --queues 4 --tx-count 10 --quiet
diff --git a/qpid/cpp/src/tests/receiver.cpp b/qpid/cpp/src/tests/receiver.cpp
new file mode 100644
index 0000000000..f1b462d6e4
--- /dev/null
+++ b/qpid/cpp/src/tests/receiver.cpp
@@ -0,0 +1,140 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/SubscriptionManager.h>
+#include <qpid/client/SubscriptionSettings.h>
+#include "TestOptions.h"
+
+#include <iostream>
+#include <fstream>
+
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string queue;
+ uint messages;
+ bool ignoreDuplicates;
+ uint creditWindow;
+ uint ackFrequency;
+ bool browse;
+
+ Args() : queue("test-queue"), messages(0), ignoreDuplicates(false), creditWindow(0), ackFrequency(1), browse(false)
+ {
+ addOptions()
+ ("queue", qpid::optValue(queue, "QUEUE NAME"), "Queue from which to request messages")
+ ("messages", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely")
+ ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)")
+ ("credit-window", qpid::optValue(creditWindow, "N"), "Credit window (0 implies infinite window)")
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
+ ("browse", qpid::optValue(browse), "Browse rather than consuming");
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+
+class Receiver : public MessageListener, public FailoverManager::Command
+{
+ public:
+ Receiver(const string& queue, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse);
+ void received(Message& message);
+ void execute(AsyncSession& session, bool isRetry);
+ private:
+ const string queue;
+ const uint count;
+ const bool skipDups;
+ SubscriptionSettings settings;
+ Subscription subscription;
+ uint processed;
+ uint lastSn;
+
+ bool isDuplicate(Message& message);
+};
+
+Receiver::Receiver(const string& q, uint messages, bool ignoreDuplicates, uint creditWindow, uint ackFrequency, bool browse) :
+ queue(q), count(messages), skipDups(ignoreDuplicates), processed(0), lastSn(0)
+{
+ if (browse) settings.acquireMode = ACQUIRE_MODE_NOT_ACQUIRED;
+ if (creditWindow) settings.flowControl = FlowControl::messageWindow(creditWindow);
+ settings.autoAck = ackFrequency;
+}
+
+void Receiver::received(Message& message)
+{
+ if (!(skipDups && isDuplicate(message))) {
+ bool eos = message.getData() == EOS;
+ if (!eos) std::cout << message.getData() << std::endl;
+ if (eos || ++processed == count) subscription.cancel();
+ }
+}
+
+bool Receiver::isDuplicate(Message& message)
+{
+ uint sn = message.getHeaders().getAsInt(SN);
+ if (lastSn < sn) {
+ lastSn = sn;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void Receiver::execute(AsyncSession& session, bool /*isRetry*/)
+{
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queue, settings);
+ subs.run();
+ if (settings.autoAck) {
+ subscription.accept(subscription.getUnaccepted());
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ FailoverManager connection(opts.con);
+ Receiver receiver(opts.queue, opts.messages, opts.ignoreDuplicates, opts.creditWindow, opts.ackFrequency, opts.browse);
+ connection.execute(receiver);
+ connection.close();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cerr << "Failure: " << error.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/reject_release.py b/qpid/cpp/src/tests/reject_release.py
new file mode 100644
index 0000000000..d072b8aa78
--- /dev/null
+++ b/qpid/cpp/src/tests/reject_release.py
@@ -0,0 +1,65 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from qpid.tests.messaging.implementation import *
+from qpid.tests.messaging import VersionTest
+
+class RejectReleaseTests (VersionTest):
+ """
+ Tests for reject and release with qpidd
+ """
+ def test_reject(self):
+ name = str(uuid4())
+ snd = self.ssn.sender("%s; {create:always, node:{properties:{alternate-exchange:amq.fanout}}}" % name)
+ rcv = self.ssn.receiver(name)
+ rcv2 = self.ssn.receiver("amq.fanout")
+
+ msgs = [Message(content=s, subject = s) for s in ['a','b','c','d']]
+
+ for m in msgs: snd.send(m)
+
+ for expected in msgs:
+ msg = rcv.fetch(0)
+ assert msg.content == expected.content
+ self.ssn.reject(msg)
+
+ for expected in msgs:
+ msg = rcv2.fetch(0)
+ assert msg.content == expected.content
+
+ def test_release(self):
+ snd = self.ssn.sender("#")
+ rcv = self.ssn.receiver(snd.target)
+
+ msgs = [Message(content=s, subject = s) for s in ['a','b','c','d']]
+
+ for m in msgs: snd.send(m)
+
+ msg = rcv.fetch(0)
+ assert msg.content == "a"
+ self.ssn.release(msg)
+
+ msg = rcv.fetch(0)
+ assert msg.content == "a"
+ self.ssn.acknowledge(msg)
+
+ msg = rcv.fetch(0)
+ assert msg.content == "b"
+ self.ssn.release(msg)
+
diff --git a/qpid/cpp/src/tests/replaying_sender.cpp b/qpid/cpp/src/tests/replaying_sender.cpp
new file mode 100644
index 0000000000..a5549bfdf2
--- /dev/null
+++ b/qpid/cpp/src/tests/replaying_sender.cpp
@@ -0,0 +1,165 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageReplayTracker.h>
+#include <qpid/Exception.h>
+
+#include <iostream>
+#include <sstream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+class Sender : public FailoverManager::Command
+{
+ public:
+ Sender(const std::string& queue, uint count, uint reportFreq);
+ void execute(AsyncSession& session, bool isRetry);
+ uint getSent();
+
+ void setVerbosity ( int v ) { verbosity = v; }
+ void setPersistence ( int p ) { persistence = p; }
+
+ private:
+ MessageReplayTracker sender;
+ const uint count;
+ uint sent;
+ const uint reportFrequency;
+ Message message;
+ int verbosity;
+ int persistence;
+ string queueName;
+};
+
+Sender::Sender(const std::string& queue, uint count_, uint reportFreq )
+ : sender(10),
+ count(count_),
+ sent(0),
+ reportFrequency(reportFreq),
+ verbosity(0),
+ persistence(0),
+ queueName ( queue )
+{
+ message.getDeliveryProperties().setRoutingKey(queueName.c_str());
+}
+
+const string SN("sn");
+
+void Sender::execute(AsyncSession& session, bool isRetry)
+{
+ if (verbosity > 0)
+ std::cout << "replaying_sender " << (isRetry ? "first " : "re-") << "connect." << endl;
+ if (isRetry) sender.replay(session);
+ else sender.init(session);
+ while (sent < count) {
+ stringstream message_data;
+ message_data << ++sent;
+ message.setData(message_data.str());
+ message.getHeaders().setInt(SN, sent);
+ if ( persistence )
+ message.getDeliveryProperties().setDeliveryMode(PERSISTENT);
+
+ sender.send(message);
+ if (count > reportFrequency && !(sent % reportFrequency)) {
+ if ( verbosity > 0 )
+ std::cout << "Sender sent "
+ << sent
+ << " of "
+ << count
+ << " on queue "
+ << queueName
+ << std::endl;
+ }
+ }
+ message.setData("That's all, folks!");
+ sender.send(message);
+
+ if ( verbosity > 0 )
+ std::cout << "SENDER COMPLETED\n";
+}
+
+uint Sender::getSent()
+{
+ return sent;
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+
+ if ( argc != 8 )
+ {
+ std::cerr << "Usage: replaying_sender host port n_messages report_frequency verbosity persistence queue_name\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int n_messages = atoi(argv[3]);
+ int reportFrequency = atoi(argv[4]);
+ int verbosity = atoi(argv[5]);
+ int persistence = atoi(argv[6]);
+ char * queue_name = argv[7];
+
+ FailoverManager connection(settings);
+ Sender sender(queue_name, n_messages, reportFrequency );
+ sender.setVerbosity ( verbosity );
+ sender.setPersistence ( persistence );
+ try {
+ connection.execute ( sender );
+ if ( verbosity > 0 )
+ {
+ std::cout << "Sender finished. Sent "
+ << sender.getSent()
+ << " messages on queue "
+ << queue_name
+ << endl;
+ }
+ connection.close();
+ return 0;
+ }
+ catch(const std::exception& error)
+ {
+ cerr << "Sender (host: "
+ << settings.host
+ << " port: "
+ << settings.port
+ << " ) "
+ << " Failed: "
+ << error.what()
+ << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/resuming_receiver.cpp b/qpid/cpp/src/tests/resuming_receiver.cpp
new file mode 100644
index 0000000000..2e22a7c572
--- /dev/null
+++ b/qpid/cpp/src/tests/resuming_receiver.cpp
@@ -0,0 +1,193 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/SubscriptionManager.h>
+
+#include <iostream>
+#include <fstream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+
+
+namespace qpid {
+namespace tests {
+
+class Listener : public MessageListener,
+ public FailoverManager::Command,
+ public FailoverManager::ReconnectionStrategy
+{
+ public:
+ Listener ( int report_frequency = 1000,
+ int verbosity = 0,
+ char const * queue_name = "message_queue" );
+ void received(Message& message);
+ void execute(AsyncSession& session, bool isRetry);
+ void check();
+ void editUrlList(vector<Url>& urls);
+ private:
+ Subscription subscription;
+ uint count;
+ vector<int> received_twice;
+ uint lastSn;
+ bool gaps;
+ uint reportFrequency;
+ int verbosity;
+ bool done;
+ string queueName;
+};
+
+
+Listener::Listener ( int freq, int verbosity, char const * name )
+ : count(0),
+ lastSn(0),
+ gaps(false),
+ reportFrequency(freq),
+ verbosity(verbosity),
+ done(false),
+ queueName ( name )
+{}
+
+const std::string SN("sn");
+
+void Listener::received(Message & message)
+{
+ if (message.getData() == "That's all, folks!")
+ {
+ done = true;
+ if(verbosity > 0 )
+ {
+ cout << "Shutting down listener for "
+ << message.getDestination() << endl;
+
+ cout << "Listener received "
+ << count
+ << " messages ("
+ << received_twice.size()
+ << " received_twice)"
+ << endl;
+
+ }
+ subscription.cancel();
+ if ( verbosity > 0 )
+ cout << "LISTENER COMPLETED\n";
+
+ if ( ! gaps ) {
+ cout << "no gaps were detected\n";
+ cout << received_twice.size() << " messages were received twice.\n";
+ }
+ else {
+ cout << "gaps detected\n";
+ for ( unsigned int i = 0; i < received_twice.size(); ++ i )
+ cout << "received_twice "
+ << received_twice[i]
+ << endl;
+ }
+ } else {
+ uint sn = message.getHeaders().getAsInt(SN);
+ if (lastSn < sn) {
+ if (sn - lastSn > 1) {
+ cerr << "Error: gap in sequence between " << lastSn << " and " << sn << endl;
+ gaps = true;
+ }
+ lastSn = sn;
+ ++count;
+ if ( ! ( count % reportFrequency ) ) {
+ if ( verbosity > 0 )
+ cout << "Listener has received "
+ << count
+ << " messages on queue "
+ << queueName
+ << endl;
+ }
+ } else {
+ received_twice.push_back ( sn );
+ }
+ }
+}
+
+void Listener::check()
+{
+ if (gaps) throw Exception("Detected gaps in sequence; messages appear to have been lost.");
+}
+
+void Listener::execute(AsyncSession& session, bool isRetry) {
+ if (verbosity > 0)
+ cout << "resuming_receiver " << (isRetry ? "first " : "re-") << "connect." << endl;
+ if (!done) {
+ SubscriptionManager subs(session);
+ subscription = subs.subscribe(*this, queueName);
+ subs.run();
+ }
+}
+
+void Listener::editUrlList(vector<Url>& urls)
+{
+ /**
+ * A more realistic algorithm would be to search through the list
+ * for prefered hosts and ensure they come first in the list.
+ */
+ if (urls.size() > 1) rotate(urls.begin(), urls.begin() + 1, urls.end());
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ ConnectionSettings settings;
+
+ if ( argc != 6 )
+ {
+ cerr << "Usage: resuming_receiver host port report_frequency verbosity queue_name\n";
+ return 1;
+ }
+
+ settings.host = argv[1];
+ settings.port = atoi(argv[2]);
+ int reportFrequency = atoi(argv[3]);
+ int verbosity = atoi(argv[4]);
+ char * queue_name = argv[5];
+
+ Listener listener ( reportFrequency, verbosity, queue_name );
+ FailoverManager connection(settings, &listener);
+
+ try {
+ connection.execute(listener);
+ connection.close();
+ listener.check();
+ return 0;
+ } catch(const exception& error) {
+ cerr << "Receiver failed: " << error.what() << endl;
+ }
+ return 1;
+}
+
+
+
diff --git a/qpid/cpp/src/tests/ring_queue_test b/qpid/cpp/src/tests/ring_queue_test
new file mode 100755
index 0000000000..410ea30eb8
--- /dev/null
+++ b/qpid/cpp/src/tests/ring_queue_test
@@ -0,0 +1,174 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Test script for validating the behaviour of a ring queue
+
+QUEUE_NAME=ring-queue
+LIMIT=100
+DURABLE=0
+MESSAGES=10000
+SENDERS=1
+RECEIVERS=1
+CONCURRENT=0
+BROKER_URL="-b ${QPID_BROKER:-localhost}:${QPID_PORT:-5672}"
+
+setup() {
+ if [[ $DURABLE -gt 0 ]]; then
+ EXTRA_ARGS=" --durable"
+ fi
+ qpid-config $BROKER_URL add queue $QUEUE_NAME --max-queue-count $LIMIT --limit-policy ring $EXTRA_ARGS
+}
+
+send() {
+ datagen --count $MESSAGES | tee sender_${QUEUE_NAME}_${1} | sender --durable $DURABLE --routing-key $QUEUE_NAME
+}
+
+receive() {
+ #TODO: allow a variety of receiver options to be passed in (ack-frequency, credit-window etc)
+ receiver --queue $QUEUE_NAME > receiver_${QUEUE_NAME}_${1}
+}
+
+cleanup() {
+ rm -f sender_${QUEUE_NAME}_* receiver_${QUEUE_NAME}_*
+ qpid-config $BROKER_URL del queue $QUEUE_NAME --force
+}
+
+log() {
+ echo $1
+}
+
+fail() {
+ echo $1
+ FAILED=1
+}
+
+validate() {
+ if [[ $RECEIVERS -eq 0 ]]; then
+ #queue should have $LIMIT messages on it, but need to send an eos also
+ sender --routing-key $QUEUE_NAME --send-eos 1 < /dev/null
+ received=$(receiver --queue $QUEUE_NAME --browse | wc -l)
+ if [[ received -eq $(( $LIMIT - 1)) ]]; then
+ log "queue contains $LIMIT messages as expected"
+ else
+ fail "queue does not contain the expected $LIMIT messages (received $received)"
+ fi
+ elif [[ $CONCURRENT -eq 0 ]]; then
+ #sum of length of all output files should be equal to $LIMIT - $RECEIVERS (1 eos message each)
+ received=$(cat receiver_${QUEUE_NAME}_* | wc -l)
+ expected=$(( $LIMIT - $RECEIVERS ))
+ if [[ $received -eq $expected ]]; then
+ log "received $LIMIT messages as expected"
+ else
+ fail "received $received messages, expected $expected"
+ fi
+ #if there were only a single sender and receiver (executed serially) we can check the
+ #actual received contents
+ if [[ $RECEIVERS -eq 1 ]] && [[ $SENDERS -eq 1 ]]; then
+ tail -n $(($LIMIT - 1)) sender_${QUEUE_NAME}_1 | diff - receiver_${QUEUE_NAME}_1 || FAILED=1
+ if [[ $FAILED -eq 1 ]]; then
+ fail "did not receive expected messages"
+ else
+ log "received messages matched expectations"
+ fi
+ fi
+ else
+ #multiple receivers, concurrent with senders; ring queue functionality cannot be validated in this case
+ if [[ $(cat receiver_${QUEUE_NAME}_* | wc -l) -le $(cat sender_${QUEUE_NAME}_* | wc -l) ]]; then
+ log "sent at least as many messages as were received"
+ else
+ #Note: if any receiver was browsing, this would be valid (would need to add 'sort | uniq')
+ # to pipeline above
+ fail "received more messages than were sent"
+ fi
+ fi
+
+ if [[ $FAILED ]]; then
+ echo $(basename $0): FAILED
+ exit 1
+ else
+ cleanup
+ fi
+}
+
+run_test() {
+ if [[ $CONCURRENT -eq 0 ]]; then
+ echo "Starting $SENDERS senders followed by $RECEIVERS receivers "
+ else
+ echo "Starting $SENDERS senders concurrently with $RECEIVERS receivers"
+ fi
+ for ((i=1; i <= $SENDERS; i++)); do
+ send $i &
+ sender_pids[$i]=$!
+ done
+ if [[ $CONCURRENT -eq 0 ]] && [[ $RECEIVERS -gt 0 ]]; then
+ wait
+ sender --routing-key $QUEUE_NAME --send-eos $RECEIVERS < /dev/null
+ fi
+ for ((i=1; i <= $RECEIVERS; i++)); do
+ receive $i &
+ done
+ if [[ $CONCURRENT -gt 0 ]]; then
+ for ((i=1; i <= $SENDERS; i++)); do
+ wait ${sender_pids[$i]}
+ done
+ sender --routing-key $QUEUE_NAME --send-eos $RECEIVERS < /dev/null
+ fi
+ wait
+}
+
+usage() {
+ cat <<EOF
+$(basename $0): Test script for validating the behaviour of a ring queue
+
+Options:
+ -q <queue> the name of the queue to use
+ -s <senders> the number of senders to start
+ -r <receivers> the number of receivers to start
+ -l <limit> the limit for the ring queue
+ -m <messages> the number of messages to send
+ -c if specified, receivers will run concurrently with senders
+ -d if specified the queue and messages will be durable
+EOF
+ exit 1
+}
+
+while getopts "s:r:m:u:dch" opt ; do
+ case $opt in
+ q) QUEUE_NAME=$OPTARG ;;
+ l) LIMIT=$OPTARG ;;
+ s) SENDERS=$OPTARG ;;
+ r) RECEIVERS=$OPTARG ;;
+ m) MESSAGES=$OPTARG ;;
+ d) DURABLE=1 ;;
+ c) CONCURRENT=1 ;;
+ h) usage;;
+ ?) usage;;
+ esac
+done
+
+if [[ $SENDERS -gt 0 ]]; then
+ setup
+ run_test
+ validate
+else
+ echo "Nothing can be done if there are no senders"
+fi
+
diff --git a/qpid/cpp/src/tests/rsynchosts b/qpid/cpp/src/tests/rsynchosts
new file mode 100755
index 0000000000..10e1081f76
--- /dev/null
+++ b/qpid/cpp/src/tests/rsynchosts
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under onemake
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+absdir() { echo `cd $1 && pwd`; }
+
+abspath() {
+ if test -d "$1"; then absdir "$1";
+ else echo $(absdir $(dirname "$1"))/$(basename "$1")
+ fi
+}
+
+usage() {
+ echo "Usage: $(basename $0) [-l user] file [file...]
+Synchronize the contents of each file or directory to the same absolute path on
+each host in \$HOSTS.
+"
+ exit 1
+}
+
+while getopts "l:" opt; do
+ case $opt in
+ l) RSYNC_USER="$OPTARG@" ;;
+ *) usage ;;
+ esac
+done
+shift `expr $OPTIND - 1`
+
+test "$*" || usage
+
+for f in $*; do FILES="$FILES $(abspath $f)" || exit 1; done
+
+OK_FILE=`mktemp` # Will be deleted if anything goes wrong.
+trap "rm -f $OK_FILE" EXIT
+
+for h in $HOSTS; do
+ rsync -vaRO --delete $FILES $RSYNC_USER$h:/ || { echo "rsync to $h failed"; rm -f $OK_FILE; } &
+done
+wait
+test -f $OK_FILE
+
diff --git a/qpid/cpp/src/tests/run_acl_tests b/qpid/cpp/src/tests/run_acl_tests
new file mode 100755
index 0000000000..4bb9e7aa5d
--- /dev/null
+++ b/qpid/cpp/src/tests/run_acl_tests
@@ -0,0 +1,166 @@
+#!/usr/bin/env 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 acl tests. $srcdir is set by the Makefile.
+source ./test_env.sh
+DATA_DIR=`pwd`/data_dir
+DATA_DIRI=`pwd`/data_diri
+DATA_DIRU=`pwd`/data_diru
+DATA_DIRQ=`pwd`/data_dirq
+
+trap stop_brokers INT TERM QUIT
+
+start_brokers() {
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIR --acl-file policy.acl --auth no --log-enable trace+:acl --log-to-file local.log > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIRI --acl-file policy.acl --auth no --connection-limit-per-ip 2 --log-to-file locali.log > qpiddi.port
+ LOCAL_PORTI=`cat qpiddi.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIRU --acl-file policy.acl --auth no --connection-limit-per-user 2 --log-to-file localu.log > qpiddu.port
+ LOCAL_PORTU=`cat qpiddu.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIRQ --acl-file policy.acl --auth no --max-queues-per-user 2 --log-to-file localq.log > qpiddq.port
+ LOCAL_PORTQ=`cat qpiddq.port`
+}
+
+start_noacl_noauth_brokers() {
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --auth no --log-to-file local.log > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRI --auth no --log-to-file locali.log > qpiddi.port
+ LOCAL_PORTI=`cat qpiddi.port`
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRU --auth no --log-to-file localu.log > qpiddu.port
+ LOCAL_PORTU=`cat qpiddu.port`
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRQ --auth no --log-to-file localq.log > qpiddq.port
+ LOCAL_PORTQ=`cat qpiddq.port`
+}
+
+start_noacl_auth_brokers() {
+ sasl_config_file=$builddir/sasl_config
+ if [ ! -f $sasl_config_file ] ; then
+ echo Creating sasl database
+ . $srcdir/sasl_test_setup.sh
+ fi
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIR --auth yes --sasl-config=$sasl_config_file --log-to-file local.log > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIRI --auth yes --sasl-config=$sasl_config_file --log-to-file locali.log > qpiddi.port
+ LOCAL_PORTI=`cat qpiddi.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIRU --auth yes --sasl-config=$sasl_config_file --log-to-file localu.log > qpiddu.port
+ LOCAL_PORTU=`cat qpiddu.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --data-dir $DATA_DIRQ --auth yes --sasl-config=$sasl_config_file --log-to-file localq.log > qpiddq.port
+ LOCAL_PORTQ=`cat qpiddq.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTI
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTU
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTQ
+}
+
+delete_directories() {
+ rm -rf $DATA_DIR
+ rm -rf $DATA_DIRI
+ rm -rf $DATA_DIRU
+ rm -rf $DATA_DIRQ
+}
+
+delete_logfiles() {
+ rm -rf local.log
+ rm -rf locali.log
+ rm -rf localu.log
+ rm -rf localq.log
+}
+
+create_directories() {
+ mkdir -p $DATA_DIR
+ mkdir -p $DATA_DIRI
+ mkdir -p $DATA_DIRU
+ mkdir -p $DATA_DIRQ
+}
+
+populate_directories() {
+ cp $srcdir/policy.acl $DATA_DIR
+ cp $srcdir/policy.acl $DATA_DIRI
+ cp $srcdir/policy.acl $DATA_DIRU
+ cp $srcdir/policy.acl $DATA_DIRQ
+}
+
+test_loading_acl_from_absolute_path(){
+ POLICY_FILE=$srcdir/policy.acl
+ rm -f temp.log
+ PORT=`../qpidd --daemon --port 0 --interface 127.0.0.1 --no-module-dir --no-data-dir --auth no --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null`
+ ACL_FILE=`grep "notice ACL: Read file" temp.log | sed 's/^.*Read file //'`
+ $QPIDD_EXEC --no-module-dir -q --port $PORT
+ if test "$ACL_FILE" != "\"$POLICY_FILE\""; then
+ echo "unable to load policy file from an absolute path";
+ return 1;
+ fi
+ rm temp.log
+}
+
+test_noacl_deny_create_link() {
+ delete_logfiles
+ start_noacl_noauth_brokers
+ echo "Running no-acl, no-auth tests using brokers on ports $LOCAL_PORT, $LOCAL_PORTI, $LOCAL_PORTU, and $LOCAL_PORTQ"
+ $QPID_CONFIG_EXEC -a localhost:$LOCAL_PORT add exchange topic fed.topic
+ $QPID_CONFIG_EXEC -a localhost:$LOCAL_PORTI add exchange topic fed.topic
+ $QPID_ROUTE_EXEC dynamic add localhost:$LOCAL_PORT localhost:$LOCAL_PORTI fed.topic 2>/dev/null
+ sleep 2
+ stop_brokers
+ grep -q "must specify ACL create link rules" local.log
+ if [ $? -eq 0 ]
+ then
+ echo "Test fail - Broker with auth=no should have allowed link creation";
+ return 1;
+ fi
+
+ delete_logfiles
+ start_noacl_auth_brokers
+ echo "Running no-acl, auth tests using brokers on ports $LOCAL_PORT, $LOCAL_PORTI, $LOCAL_PORTU, and $LOCAL_PORTQ"
+ $QPID_CONFIG_EXEC -a localhost:$LOCAL_PORT add exchange topic fed.topic
+ $QPID_CONFIG_EXEC -a localhost:$LOCAL_PORTI add exchange topic fed.topic
+ $QPID_ROUTE_EXEC dynamic add localhost:$LOCAL_PORT localhost:$LOCAL_PORTI fed.topic 2>/dev/null
+ sleep 2
+ stop_brokers
+ grep -q "must specify ACL create link rules" local.log
+ if [ $? -ne 0 ]
+ then
+ echo "Test fail - Broker with no ACL and --auth=yes file did not deny link creation";
+ return 1;
+ fi
+}
+
+if test -d ${PYTHON_DIR} ; then
+ # run acl.py test file
+ delete_directories
+ create_directories
+ populate_directories
+ delete_logfiles
+ start_brokers
+ echo "Running acl tests using brokers on ports $LOCAL_PORT, $LOCAL_PORTI, $LOCAL_PORTU, and $LOCAL_PORTQ"
+ $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl -Dport-i=$LOCAL_PORTI -Dport-u=$LOCAL_PORTU -Dport-q=$LOCAL_PORTQ || EXITCODE=1
+ stop_brokers || EXITCODE=1
+ #
+ test_loading_acl_from_absolute_path || EXITCODE=1
+ #
+ test_noacl_deny_create_link || EXITCODE=1
+ delete_directories
+ exit $EXITCODE
+fi
+
diff --git a/qpid/cpp/src/tests/run_acl_tests.ps1 b/qpid/cpp/src/tests/run_acl_tests.ps1
new file mode 100644
index 0000000000..8279d87e54
--- /dev/null
+++ b/qpid/cpp/src/tests/run_acl_tests.ps1
@@ -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.
+#
+
+# Run the acl tests.
+
+$srcdir = Split-Path $myInvocation.InvocationName
+. .\test_env.ps1
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping acl tests as python libs not found"
+ exit 1
+}
+
+$Global:BROKER_EXE = ""
+
+Function start_broker($acl_options)
+{
+ # Test runs from the tests directory but the broker executable is one level
+ # up, and most likely in a subdirectory from there based on what build type.
+ # Look around for it before trying to start it.
+ . $srcdir\find_prog.ps1 ..\qpidd.exe
+ if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+ }
+ $Global:BROKER_EXE = $prog
+ if (Test-Path qpidd.port) {
+ Remove-Item qpidd.port
+ }
+ $cmdline = "$prog --auth=no --no-module-dir --port=0 --log-to-file qpidd.log $acl_options | foreach { set-content qpidd.port `$_ }"
+ $cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+ . $srcdir\background.ps1 $cmdblock
+ # Wait for the broker to start
+ $wait_time = 0
+ while (!(Test-Path qpidd.port) -and ($wait_time -lt 30)) {
+ Start-Sleep 2
+ $wait_time += 2
+ }
+ if (!(Test-Path qpidd.port)) {
+ "Timeout waiting for broker to start"
+ exit 1
+ }
+ set-item -path env:BROKER_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+Function stop_broker
+{
+ "Stopping $Global:BROKER_EXE"
+ Invoke-Expression "$Global:BROKER_EXE --no-module-dir -q --port $env:BROKER_PORT" | Write-Output
+ Remove-Item qpidd.port
+}
+
+$DATA_DIR = [IO.Directory]::GetCurrentDirectory() + "\data_dir"
+Remove-Item $DATA_DIR -recurse
+New-Item $DATA_DIR -type directory
+Copy-Item $srcdir\policy.acl $DATA_DIR
+start_broker("--data-dir $DATA_DIR --acl-file policy.acl")
+"Running acl tests using broker on port $env:BROKER_PORT"
+Invoke-Expression "python $PYTHON_DIR/qpid-python-test -m acl -b localhost:$env:BROKER_PORT" | Out-Default
+$RETCODE=$LASTEXITCODE
+stop_broker
+
+# Now try reading the acl file from an absolute path.
+Remove-Item qpidd.log
+$policy_full_path = "$srcdir\policy.acl"
+start_broker("--no-data-dir --acl-file $policy_full_path")
+#test_loading_acl_from_absolute_path(){
+# POLICY_FILE=$srcdir/policy.acl
+# rm -f temp.log
+# PORT=`../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no --load-module $ACL_LIB --acl-file $POLICY_FILE -t --log-to-file temp.log 2>/dev/null`
+# ACL_FILE=`grep "notice Read ACL file" temp.log | sed 's/^.*Read ACL file //'`
+# $QPIDD_EXEC --no-module-dir -q --port $PORT
+# if test "$ACL_FILE" != "\"$POLICY_FILE\""; then
+# echo "unable to load policy file from an absolute path";
+# return 1;
+# fi
+# rm temp.log
+#}
+#
+# test_loading_acl_from_absolute_path || EXITCODE=1
+# rm -rf $DATA_DIR
+# exit $EXITCODE
+stop_broker
+exit $RETCODE
diff --git a/qpid/cpp/src/tests/run_cli_tests b/qpid/cpp/src/tests/run_cli_tests
new file mode 100755
index 0000000000..1db99001a4
--- /dev/null
+++ b/qpid/cpp/src/tests/run_cli_tests
@@ -0,0 +1,81 @@
+#!/usr/bin/env 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 cli-utility tests.
+
+source ./test_env.sh
+CLI_DIR=$PYTHON_COMMANDS
+
+trap stop_brokers INT TERM QUIT
+
+# helper function to create test.xquery in the current directory, so
+# that the python test program can find it. yes, it leaves a turd.
+create_test_xquery() {
+ cat <<EOF > ./test.xquery
+ let \$w := ./weather
+ return \$w/station = 'Raleigh-Durham International Airport (KRDU)'
+ and \$w/temperature_f > 50
+ and \$w/temperature_f - \$w/dewpoint > 5
+ and \$w/wind_speed_mph > 7
+ and \$w/wind_speed_mph < 20
+EOF
+}
+
+start_brokers() {
+ # if the xml lib is present, use it. if not, disable any tests which
+ # look like they're xml related.
+ # if we start supporting xml on windows, it will need something similar
+ # here
+ if [ -f ../xml.so ] ; then
+ xargs="--load-module ../xml.so"
+ if [ ! -f test.xquery ] ; then
+ create_test_xquery
+ fi
+ targs=""
+ else
+ echo "Ignoring XML tests"
+ xargs=""
+ targs="--ignore=*xml*"
+ fi
+
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-data-dir --no-module-dir --mgmt-publish no --auth no $xargs > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-data-dir --no-module-dir --mgmt-publish no --auth no $xargs > qpidd.port
+ REMOTE_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $REMOTE_PORT
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running CLI tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
+ PYTHON_TESTS=${PYTHON_TESTS:-$*}
+ $QPID_PYTHON_TEST -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $targs $PYTHON_TESTS $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL CLI tests"; exit 1;
+ fi
+fi
+
diff --git a/qpid/cpp/src/tests/run_federation_sys_tests b/qpid/cpp/src/tests/run_federation_sys_tests
new file mode 100755
index 0000000000..f5f1ae44d3
--- /dev/null
+++ b/qpid/cpp/src/tests/run_federation_sys_tests
@@ -0,0 +1,71 @@
+#!/usr/bin/env 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 federation system tests.
+
+source ./test_env.sh
+
+MODULENAME=federation_sys
+
+# Test for long test
+if [[ "$1" == "LONG_TEST" ]]; then
+ USE_LONG_TEST=1
+ shift # get rid of this param so it is not treated as a test name
+fi
+
+trap stop_brokers INT TERM QUIT
+
+SKIPTESTS="-i federation_sys.E_* -i federation_sys.F_* -i federation_sys.G_* -i federation_sys.H_*"
+if [ -z ${USE_LONG_TEST} ]; then
+ SKIPTESTS="-i federation_sys.A_Long* -i federation_sys.B_Long* ${SKIPTESTS}"
+fi
+echo "WARNING: Tests using persistence will be ignored."
+SKIPTESTS="${SKIPTESTS} -i federation_sys.C_* -i federation_sys.D_*"
+
+start_brokers() {
+ start_broker() {
+ ${QPIDD_EXEC} --daemon --port 0 --interface 127.0.0.1 --auth no --no-data-dir $1 > qpidd.port
+ PORT=`cat qpidd.port`
+ eval "$2=${PORT}"
+ }
+ start_broker "" LOCAL_PORT
+ start_broker "" REMOTE_PORT
+ rm qpidd.port
+}
+
+stop_brokers() {
+ ${QPIDD_EXEC} -q --port ${LOCAL_PORT}
+ ${QPIDD_EXEC} -q --port ${REMOTE_PORT}
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT} (NOTE: clustering is DISABLED)"
+ if [ -z ${USE_LONG_TEST} ]; then
+ echo "NOTE: To run a full set of federation system tests, use \"make check-long\". To test with persistence, run the store version of this script."
+ fi
+ ${QPID_PYTHON_TEST} -m ${MODULENAME} ${SKIPTESTS} -b localhost:${REMOTE_PORT} -Dlocal-port=${LOCAL_PORT} -Dremote-port=${REMOTE_PORT} $@
+ RETCODE=$?
+ stop_brokers
+ if test x${RETCODE} != x0; then
+ echo "FAIL federation tests"; exit 1;
+ fi
+fi
diff --git a/qpid/cpp/src/tests/run_federation_tests b/qpid/cpp/src/tests/run_federation_tests
new file mode 100755
index 0000000000..8cadd3702f
--- /dev/null
+++ b/qpid/cpp/src/tests/run_federation_tests
@@ -0,0 +1,61 @@
+#!/usr/bin/env 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 federation tests.
+
+source ./test_env.sh
+#set -x
+trap stop_brokers INT TERM QUIT
+
+if [ -f ../xml.so ] ; then
+ MODULES="--load-module xml" # Load the XML exchange and run XML exchange federation tests
+ SKIPTESTS=
+else
+ MODULES="--no-module-dir"
+ SKIPTESTS='-i *_xml' # note: single quotes prevent expansion of *
+fi
+
+QPIDD_CMD="../qpidd --daemon --port 0 --interface 127.0.0.1 --no-data-dir $MODULES --auth no --log-enable=info+ --log-enable=debug+:Bridge --log-to-file"
+start_brokers() {
+ rm -f fed_local.log fed_remote.log fed_b1.log fed_b2.log
+ LOCAL_PORT=$($QPIDD_CMD fed_local.log --federation-tag LOCAL)
+ REMOTE_PORT=$($QPIDD_CMD fed_remote.log --federation-tag REMOTE)
+ REMOTE_B1=$($QPIDD_CMD fed_b1.log --federation-tag B1)
+ REMOTE_B2=$($QPIDD_CMD fed_b2.log --federation-tag B2)
+}
+
+stop_brokers() {
+ $QPIDD_EXEC $MODULES -q --port $LOCAL_PORT
+ $QPIDD_EXEC $MODULES -q --port $REMOTE_PORT
+ $QPIDD_EXEC $MODULES -q --port $REMOTE_B1
+ $QPIDD_EXEC $MODULES -q --port $REMOTE_B2
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT $REMOTE_B1 $REMOTE_B2"
+ $QPID_PYTHON_TEST -m federation ${SKIPTESTS} -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dextra-brokers="$REMOTE_B1 $REMOTE_B2" $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL federation tests"; exit 1;
+ fi
+fi
diff --git a/qpid/cpp/src/tests/run_federation_tests.ps1 b/qpid/cpp/src/tests/run_federation_tests.ps1
new file mode 100644
index 0000000000..803b3eef6f
--- /dev/null
+++ b/qpid/cpp/src/tests/run_federation_tests.ps1
@@ -0,0 +1,83 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Run the federation tests.
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping federation tests as python libs not found"
+ exit 1
+}
+
+. .\test_env.ps1
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+$cmdline = "$prog --auth=no --no-module-dir --no-data-dir --port=0 --ssl-port=0 --log-to-file qpidd.log $args | foreach { set-content qpidd.port `$_ }"
+$cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+
+function start_brokers {
+ # Start 2 brokers, saving the port numbers in LOCAL_PORT, REMOTE_PORT.
+ . $srcdir\background.ps1 $cmdblock
+ while (!(Test-Path qpidd.port)) {
+ Start-Sleep 2
+ }
+ set-item -path env:LOCAL_PORT -value (get-content -path qpidd.port -totalcount 1)
+ Remove-Item qpidd.port
+ . $srcdir\background.ps1 $cmdblock
+ while (!(Test-Path qpidd.port)) {
+ Start-Sleep 2
+ }
+ set-item -path env:REMOTE_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+function stop_brokers {
+ Invoke-Expression "$prog -q --port $env:LOCAL_PORT" | Out-Default
+ Invoke-Expression "$prog -q --port $env:REMOTE_PORT" | Out-Default
+}
+
+trap {
+ &stop_brokers
+ break
+}
+
+&start_brokers
+"Running federation tests using brokers on ports $env:LOCAL_PORT $env:REMOTE_PORT"
+$env:PYTHONPATH="$srcdir;$PYTHON_DIR;$PYTHON_TEST_DIR;$env:PYTHONPATH;$QMF_LIB"
+$tests = "*"
+Invoke-Expression "python $PYTHON_DIR/qpid-python-test -m federation -b localhost:$env:LOCAL_PORT -Dremote-port=$env:REMOTE_PORT $tests" | Out-Default
+$RETCODE=$LASTEXITCODE
+&stop_brokers
+if ($RETCODE -ne 0) {
+ "FAIL federation tests"
+ exit 1
+}
diff --git a/qpid/cpp/src/tests/run_ha_tests b/qpid/cpp/src/tests/run_ha_tests
new file mode 100755
index 0000000000..bb60bea076
--- /dev/null
+++ b/qpid/cpp/src/tests/run_ha_tests
@@ -0,0 +1,29 @@
+#!/usr/bin/env 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.
+#
+
+
+# Make sure the python tools are available. They will be if we are building in
+# a checkoug, they may not be in a distribution.
+test -d $PYTHON_COMMANDS -a -x $PYTHON_COMMANDS/qpid-ha -a -x $PYTHON_COMMANDS/qpid-config || { echo "Skipping HA tests, qpid-ha or qpid-config not available."; exit 0; }
+
+srcdir=`dirname $0`
+$srcdir/ha_tests.py
+
diff --git a/qpid/cpp/src/tests/run_header_test b/qpid/cpp/src/tests/run_header_test
new file mode 100755
index 0000000000..d1edcf6831
--- /dev/null
+++ b/qpid/cpp/src/tests/run_header_test
@@ -0,0 +1,31 @@
+#!/usr/bin/env 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.
+#
+
+# 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
+
+source $QPID_TEST_COMMON
+
+ensure_python_tests
+
+./header_test -p $QPID_PORT
+$srcdir/header_test.py "localhost" $QPID_PORT
diff --git a/qpid/cpp/src/tests/run_header_test.ps1 b/qpid/cpp/src/tests/run_header_test.ps1
new file mode 100644
index 0000000000..344fac9cf9
--- /dev/null
+++ b/qpid/cpp/src/tests/run_header_test.ps1
@@ -0,0 +1,48 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Simple test of encode/decode of a double in application headers
+# TODO: this should be expanded to cover a wider set of types and go
+# in both directions
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping header test as python libs not found"
+ exit 0
+}
+
+. .\test_env.ps1
+
+if (Test-Path qpidd.port) {
+ set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+# Test runs from the tests directory but the test executables are in a
+# subdirectory based on the build type. Look around for it before trying
+# to start it.
+. $srcdir\find_prog.ps1 .\header_test.exe
+if (!(Test-Path $prog)) {
+ "Cannot locate header_test.exe"
+ exit 1
+}
+
+Invoke-Expression "$prog -p $env:QPID_PORT" | Write-Output
+Invoke-Expression "python $srcdir/header_test.py localhost $env:QPID_PORT" | Write-Output
+exit $LASTEXITCODE
diff --git a/qpid/cpp/src/tests/run_headers_federation_tests b/qpid/cpp/src/tests/run_headers_federation_tests
new file mode 100644
index 0000000000..afbbf144ee
--- /dev/null
+++ b/qpid/cpp/src/tests/run_headers_federation_tests
@@ -0,0 +1,49 @@
+#!/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 for the Headers Exchange.
+
+source ./test_env.sh
+
+trap stop_brokers INT TERM QUIT
+
+start_brokers() {
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-data-dir --no-module-dir --auth no > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --interface 127.0.0.1 --no-data-dir --no-module-dir --auth no > qpidd.port
+ REMOTE_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $REMOTE_PORT
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ echo "Running HeadersExchange federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
+ $QPID_PYTHON_TEST -m headers_federation -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT $@
+ RETCODE=$?
+ stop_brokers
+ if test x$RETCODE != x0; then
+ echo "FAIL federation tests"; exit 1;
+ fi
+fi
diff --git a/qpid/cpp/src/tests/run_interlink_tests b/qpid/cpp/src/tests/run_interlink_tests
new file mode 100755
index 0000000000..71482fa7fd
--- /dev/null
+++ b/qpid/cpp/src/tests/run_interlink_tests
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+test -e "$AMQP_LIB" || { echo "Skipping AMQP 1.0 based tests; AMQP 1.0 support not available."; exit 0; }
+
+srcdir=`dirname $0`
+$srcdir/interlink_tests.py
+
diff --git a/qpid/cpp/src/tests/run_long_federation_sys_tests b/qpid/cpp/src/tests/run_long_federation_sys_tests
new file mode 100644
index 0000000000..c2b4e02d81
--- /dev/null
+++ b/qpid/cpp/src/tests/run_long_federation_sys_tests
@@ -0,0 +1,24 @@
+#!/usr/bin/env 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 federation system tests (long version).
+
+./run_federation_sys_tests LONG_TEST $@
diff --git a/qpid/cpp/src/tests/run_msg_group_tests b/qpid/cpp/src/tests/run_msg_group_tests
new file mode 100755
index 0000000000..ee479c23c7
--- /dev/null
+++ b/qpid/cpp/src/tests/run_msg_group_tests
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#script to run a sequence of message group queue tests via make
+
+source $QPID_TEST_COMMON
+
+ensure_python_tests
+
+QUEUE_NAME="group-queue"
+GROUP_KEY="My-Group-Id"
+
+BROKER_URL="${QPID_BROKER:-localhost}:${QPID_PORT:-5672}"
+
+run_test() {
+ "$@"
+}
+
+##set -x
+
+declare -i i=0
+declare -a tests
+tests=("qpid-config -b $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 3"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 7 --randomize-group-size"
+ "qpid-config -b $BROKER_URL add queue ${QUEUE_NAME}-two --group-header=${GROUP_KEY} --shared-groups"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 3 --randomize-group-size"
+ "msg_group_test -b $BROKER_URL -a ${QUEUE_NAME}-two --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 5"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 5 --receivers 2 --senders 3 --capacity 1 --ack-frequency 3 --randomize-group-size"
+ "qpid-config -b $BROKER_URL del queue ${QUEUE_NAME}-two --force"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 3 --receivers 2 --senders 3 --capacity 1 --ack-frequency 1 --randomize-group-size"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 211 --group-size 13 --receivers 2 --senders 3 --capacity 47 --ack-frequency 79 --interleave 53"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10000 --group-size 1 --receivers 0 --senders 1"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10000 --receivers 5 --senders 0"
+ "qpid-config -b $BROKER_URL del queue $QUEUE_NAME --force")
+
+while [ -n "${tests[i]}" ]; do
+ run_test ${tests[i]}
+ RETCODE=$?
+ if test x$RETCODE != x0; then
+ echo "FAILED message group test. Failed command: \"${tests[i]}\"";
+ exit 1;
+ fi
+ i+=1
+done
diff --git a/qpid/cpp/src/tests/run_msg_group_tests.ps1 b/qpid/cpp/src/tests/run_msg_group_tests.ps1
new file mode 100644
index 0000000000..e9cee0a5a0
--- /dev/null
+++ b/qpid/cpp/src/tests/run_msg_group_tests.ps1
@@ -0,0 +1,71 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Simple test of encode/decode of a double in application headers
+# TODO: this should be expanded to cover a wider set of types and go
+# in both directions
+
+$srcdir = Split-Path $myInvocation.InvocationName
+$PYTHON_DIR = "$srcdir\..\..\..\python"
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping msg_group test as python libs not found"
+ exit 0
+}
+
+. .\test_env.ps1
+
+if (Test-Path qpidd.port) {
+ set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1)
+}
+
+# Test runs from the tests directory but the test executables are in a
+# subdirectory based on the build type. Look around for it before trying
+# to start it.
+. $srcdir\find_prog.ps1 .\msg_group_test.exe
+if (!(Test-Path $prog)) {
+ "Cannot locate msg_group_test.exe"
+ exit 1
+}
+
+$QUEUE_NAME="group-queue"
+$GROUP_KEY="My-Group-Id"
+$BROKER_URL="localhost:$env:QPID_PORT"
+
+$tests=@("python $QPID_CONFIG_EXEC -b $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 3",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 7 --randomize-group-size",
+ "python $QPID_CONFIG_EXEC -b $BROKER_URL add queue ${QUEUE_NAME}-two --group-header=${GROUP_KEY} --shared-groups",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 3 --randomize-group-size",
+ "$prog -b $BROKER_URL -a ${QUEUE_NAME}-two --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 5",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 5 --receivers 2 --senders 3 --capacity 1 --ack-frequency 3 --randomize-group-size",
+ "python $QPID_CONFIG_EXEC -b $BROKER_URL del queue ${QUEUE_NAME}-two --force",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 3 --receivers 2 --senders 3 --capacity 1 --ack-frequency 1 --randomize-group-size",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 211 --group-size 13 --receivers 2 --senders 3 --capacity 47 --ack-frequency 79 --interleave 53",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10000 --group-size 1 --receivers 0 --senders 1",
+ "$prog -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10000 --receivers 5 --senders 0",
+ "python $QPID_CONFIG_EXEC -b $BROKER_URL del queue $QUEUE_NAME --force")
+
+foreach ($cmd in $tests)
+{
+ Invoke-Expression "$cmd" | Write-Output
+ $ret = $LASTEXITCODE
+ if ($ret -ne 0) {Write-Host "FAILED message group test. Failed command: $cmd"
+ break}
+}
+exit $ret
diff --git a/qpid/cpp/src/tests/run_msg_group_tests_soak b/qpid/cpp/src/tests/run_msg_group_tests_soak
new file mode 100755
index 0000000000..d87ca16c88
--- /dev/null
+++ b/qpid/cpp/src/tests/run_msg_group_tests_soak
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#script to run a sequence of long-running message group tests via make
+
+#setup path to find qpid-config and msg_group_test test progs
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping message group tests, no python dir."; exit 0; }
+
+export PATH=$PWD:$srcdir:$PYTHON_COMMANDS:$PATH
+
+#set port to connect to via env var
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
+
+#trap cleanup INT TERM QUIT
+
+QUEUE_NAME="group-queue"
+GROUP_KEY="My-Group-Id"
+
+BROKER_URL="${QPID_BROKER:-localhost}:${QPID_PORT:-5672}"
+
+run_test() {
+ $@
+}
+
+##set -x
+
+declare -i i=0
+declare -a tests
+tests=("qpid-config -b $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 47 --ack-frequency 97"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 79 --ack-frequency 79"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 97 --ack-frequency 47"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 40000 --receivers 0 --senders 5 --group-size 13 --randomize-group-size"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 200000 --receivers 3 --senders 0 --capacity 23 --ack-frequency 7"
+ "qpid-config -b $BROKER_URL del queue $QUEUE_NAME --force")
+
+while [ -n "${tests[i]}" ]; do
+ run_test ${tests[i]}
+ RETCODE=$?
+ if test x$RETCODE != x0; then
+ echo "FAILED message group test. Failed command: \"${tests[i]}\"";
+ exit 1;
+ fi
+ i+=1
+done
diff --git a/qpid/cpp/src/tests/run_paged_queue_tests b/qpid/cpp/src/tests/run_paged_queue_tests
new file mode 100755
index 0000000000..2c1e3ae614
--- /dev/null
+++ b/qpid/cpp/src/tests/run_paged_queue_tests
@@ -0,0 +1,50 @@
+#!/usr/bin/env 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.
+#
+
+#setup path to find qpid-config and sender/receiver test progs
+source ./test_env.sh
+trap stop_broker INT TERM QUIT
+
+export PATH=$PWD:$srcdir:$PYTHON_COMMANDS:$PATH
+
+start_broker() {
+ QPID_PORT=$($QPIDD_EXEC --daemon --port 0 --interface 127.0.0.1 --no-data-dir --paging-dir=$PWD/pqtest_data $MODULES --auth no) || { echo "Could not start broker"; exit 1; }
+}
+
+stop_broker() {
+ $QPIDD_EXEC -q --port $QPID_PORT
+}
+
+test_single_page() {
+ msgcount=1000
+ qpid-send --messages $msgcount --content-size 1024 --broker "localhost:$QPID_PORT" --address "onepage; {create: always, node:{x-declare:{arguments:{'qpid.paging':True,'qpid.max_pages_loaded':1}}}}"
+ received=$(qpid-receive --address onepage --broker "localhost:$QPID_PORT" --messages $msgcount | wc -l)
+ if [[ $received -ne $msgcount ]]; then
+ echo "single page test failed: received $received messages, expected $msgcount"
+ exit 1
+ fi
+}
+
+start_broker
+test_single_page
+qpid-cpp-benchmark --broker "localhost:$QPID_PORT" --create-option "node:{x-declare:{arguments:{'qpid.paging':True,'qpid.max_size':0,'qpid.max_count':0,'qpid.flow_stop_size':0,'qpid.flow_resume_size':0,'qpid.flow_stop_count':0,'qpid.flow_resume_count':0}}}"
+qpid-cpp-benchmark --broker "localhost:$QPID_PORT" --create-option "node:{x-declare:{arguments:{'qpid.paging':True,'qpid.max_size':0,'qpid.max_count':0,'qpid.flow_stop_size':0,'qpid.flow_resume_size':0,'qpid.flow_stop_count':0,'qpid.flow_resume_count':0}}}" --fill-drain
+stop_broker
diff --git a/qpid/cpp/src/tests/run_perftest b/qpid/cpp/src/tests/run_perftest
new file mode 100755
index 0000000000..2fadc6cc62
--- /dev/null
+++ b/qpid/cpp/src/tests/run_perftest
@@ -0,0 +1,28 @@
+#!/usr/bin/env 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.
+#
+
+# Args: count [qpid-perftest options...]
+# Run a qpid-perftest with count multiplied.
+#
+MULTIPLIER=3
+COUNT=`expr $1 \* $MULTIPLIER`
+shift
+exec `dirname $0`/run_test ./qpid-perftest --summary --count $COUNT "$@"
diff --git a/qpid/cpp/src/tests/run_queue_flow_limit_tests b/qpid/cpp/src/tests/run_queue_flow_limit_tests
new file mode 100755
index 0000000000..55b3e5d4c5
--- /dev/null
+++ b/qpid/cpp/src/tests/run_queue_flow_limit_tests
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+source $QPID_TEST_COMMON
+
+ensure_python_tests
+
+# Run tests against Queue producer flow control.
+$QPID_PYTHON_TEST -m queue_flow_limit_tests $SKIPTESTS -b localhost:$QPID_PORT
diff --git a/qpid/cpp/src/tests/run_queue_redirect b/qpid/cpp/src/tests/run_queue_redirect
new file mode 100755
index 0000000000..3a0ae5118a
--- /dev/null
+++ b/qpid/cpp/src/tests/run_queue_redirect
@@ -0,0 +1,56 @@
+#!/usr/bin/env 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 queue redirect. $srcdir is set by the Makefile.
+source ./test_env.sh
+DATA_DIR=`pwd`/data_dir
+
+trap stop_brokers INT TERM QUIT
+
+start_brokers() {
+ $QPIDD_EXEC --daemon \
+ --port 0 --interface 127.0.0.1 \
+ --no-module-dir \
+ --data-dir $DATA_DIR \
+ --acl-file policy.acl \
+ --auth no \
+ --log-to-file queue_redirect.log \
+ --log-enable info+ \
+ --log-enable trace+:Model \
+ --log-enable trace+ > qpidd.port
+ LOCAL_PORT=`cat qpidd.port`
+}
+
+stop_brokers() {
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+}
+
+if test -d ${PYTHON_DIR} ; then
+ rm -f queue_redirect.log
+ rm -rf $DATA_DIR
+ mkdir -p $DATA_DIR
+ cp $srcdir/policy.acl $DATA_DIR
+ start_brokers
+ echo "Running queue redirect tests using broker on port $LOCAL_PORT"
+ $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m queue_redirect
+ stop_brokers || EXITCODE=1
+ exit $EXITCODE
+fi
diff --git a/qpid/cpp/src/tests/run_ring_queue_test b/qpid/cpp/src/tests/run_ring_queue_test
new file mode 100755
index 0000000000..69497f9872
--- /dev/null
+++ b/qpid/cpp/src/tests/run_ring_queue_test
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#script to run a sequence of ring queue tests via make
+
+#setup path to find qpid-config and sender/receiver test progs
+source ./test_env.sh
+
+export PATH=$PWD:$srcdir:$PYTHON_COMMANDS:$PATH
+
+#set port to connect to via env var
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
+export QPID_PORT
+
+ring_queue_test -c -s 4 -r 4
+ring_queue_test -s 4 -r 0
+ring_queue_test -s 1 -r 1
+
+
diff --git a/qpid/cpp/src/tests/run_store_tests.ps1 b/qpid/cpp/src/tests/run_store_tests.ps1
new file mode 100644
index 0000000000..0683892393
--- /dev/null
+++ b/qpid/cpp/src/tests/run_store_tests.ps1
@@ -0,0 +1,132 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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 store tests.
+# There are two sets of tests:
+# 1. A subset of the normal broker python tests, dtx and persistence, but
+# run again with the desired store loaded.
+# 2. store.py, which tests recovering things across broker restarts.
+
+$test_store = $args[0]
+if ($test_store -ne "MSSQL" -and $test_store -ne "MSSQL-CLFS") {
+ "Invalid store test type $test_store - must be MSSQL or MSSQL-CLFS"
+ exit 1
+}
+
+$srcdir = Split-Path $myInvocation.InvocationName
+
+. .\test_env.ps1
+
+if (!(Test-Path $PYTHON_DIR -pathType Container)) {
+ "Skipping store tests as python libs not found"
+ exit 1
+}
+
+# Test runs from the tests directory but the broker executable is one level
+# up, and most likely in a subdirectory from there based on what build type.
+# Look around for it before trying to start it.
+$subs = "Debug","Release","MinSizeRel","RelWithDebInfo"
+foreach ($sub in $subs) {
+ $prog = "..\$sub\qpidd.exe"
+ if (Test-Path $prog) {
+ break
+ }
+}
+if (!(Test-Path $prog)) {
+ "Cannot locate qpidd.exe"
+ exit 1
+}
+
+# The store to test is the same build type as the broker.
+$store_dir = "..\qpid\store\$sub"
+if (!([string]::Compare($sub, "Debug", $True))) {
+ $suffix = "d"
+}
+
+$stamp = Get-Date -format %dMMMyyyy_HHmmss
+$env:STORE_LIB="$store_dir\store$suffix.dll"
+if ($test_store -eq "MSSQL") {
+ $test_store_module="$store_dir\mssql_store$suffix.dll"
+ $env:STORE_SQL_LIB=$test_store_module
+ $env:STORE_CATALOG="store_recovery_sql_test_$stamp"
+ $cat1="store_sql_test_$stamp"
+ $out = "sql_store_test_$stamp"
+}
+else {
+ $test_store_module="$store_dir\msclfs_store$suffix.dll"
+ $env:STORE_SQL_CLFS_LIB=$test_store_module
+ $env:STORE_CATALOG="store_recovery_clfs_test_$stamp"
+ $cat1="store_clfs_test_$stamp"
+ $out = "clfs_store_test_$stamp"
+}
+
+$FAILCODE = 0
+
+# Test 1... re-run some of the regular python broker tests against a broker
+# with the store module loaded.
+$cmdline = "$prog --auth=no --port=0 --log-to-file qpidd-store.log --no-module-dir --load-module $env:STORE_LIB --load-module $test_store_module --catalog $cat1 | foreach { set-content qpidd-store.port `$_ }"
+$cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+. $srcdir\background.ps1 $cmdblock
+
+$wait_time = 0
+while (!(Test-Path qpidd-store.port) -and ($wait_time -lt 90)) {
+ Start-Sleep 2
+ $wait_time += 2
+}
+if (!(Test-Path qpidd-store.port)) {
+ "Time out waiting for broker to start"
+ exit 1
+}
+set-item -path env:QPID_PORT -value (get-content -path qpidd-store.port -totalcount 1)
+Remove-Item qpidd-store.port
+
+$PYTHON_TEST_DIR = "$srcdir\..\..\..\tests\src\py\qpid_tests\broker_0_10"
+$env:PYTHONPATH="$PYTHON_TEST_DIR;$srcdir;$env:PYTHONPATH"
+python $PYTHON_DIR/qpid-python-test -m dtx -m persistence -b localhost:$env:QPID_PORT $fails $tests
+$RETCODE=$LASTEXITCODE
+if ($RETCODE -ne 0) {
+ $FAILCODE = 1
+}
+
+# Piping the output makes the script wait for qpidd to finish.
+Invoke-Expression "$prog --quit --port $env:QPID_PORT" | Write-Output
+
+
+# Test 2... store.py starts/stops/restarts its own brokers
+
+$tests = "*"
+$env:QPIDD_EXEC="$prog"
+$env:STORE_LIB="$store_dir\store$suffix.dll"
+if ($test_store -eq "MSSQL") {
+ $env:STORE_SQL_LIB="$store_dir\mssql_store$suffix.dll"
+ $env:STORE_CATALOG="store_recovery_sql_test_$stamp"
+ $out = "sql_store_test_$stamp"
+}
+else {
+ $env:STORE_SQL_CLFS_LIB="$store_dir\msclfs_store$suffix.dll"
+ $env:STORE_CATALOG="store_recovery_clfs_test_$stamp"
+ $out = "clfs_store_test_$stamp"
+}
+Invoke-Expression "python $PYTHON_DIR/qpid-python-test -m store -D OUTDIR=$out $tests" | Out-Default
+$RETCODE=$LASTEXITCODE
+if ($RETCODE -ne 0) {
+ "FAIL $test_store store tests"
+ $FAILCODE = 1
+}
+exit $FAILCODE
diff --git a/qpid/cpp/src/tests/run_test b/qpid/cpp/src/tests/run_test
new file mode 100755
index 0000000000..8e397b3458
--- /dev/null
+++ b/qpid/cpp/src/tests/run_test
@@ -0,0 +1,191 @@
+#!/usr/bin/env 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.
+#
+
+#
+# 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 and is not empty run test with QPID_PORT=`cat qpidd.port`
+#
+# If $VALGRIND if is set run under valgrind. If there are valgrind
+# erros show valgrind output, also leave it in <test>.valgrind for
+# examination.
+#
+
+wrapper="Qpid Test Wrapper"
+function usage {
+ echo "Usage:"
+ echo " -workingDir DIR"
+ echo " -buildDir DIR"
+ echo " -sourceDir DIR"
+ echo " -python - run python script"
+ echo " -boostTest - run boost unit test"
+ echo " -xml - XML output from tests"
+ echo " -startBroker - start/stop broker before/after test"
+ echo " -brokerOptions - use these extra options when starting broker"
+ echo " -help - print this message"
+ echo " -- - This is required to separate the wrapped command"
+ echo " from the test parameters"
+}
+
+function illegal_option {
+ echo "$wrapper: $1 is not an accepted option"
+ usage >&2
+}
+
+function no_command {
+ echo "$wrapper: No wrapped command specified"
+ usage >&2
+}
+
+function ignored_argument {
+ echo "Ignored argument: $1" >&2
+}
+
+working_dir='.'
+
+while true; do
+case "$1" in
+ --) shift; break ;;
+ # Split up any parameters expressed as -blah=foo
+ # and process them next time round the loop
+ -*=*) option=${1%%=*}; param=${1#*=}
+ shift;
+ set -- "$option" "$param" "$@" ;;
+ -workingDir) working_dir=$2; shift 2 ;;
+ -buildDir) build_dir=$2; shift 2 ;;
+ -sourceDir) source_dir=$2; shift 2 ;;
+ -python) run_python=yes; shift ;;
+ -boostTest) boost_test=yes; shift ;;
+ -xml) xml_output=yes; shift ;;
+ -startBroker) start_broker=yes; shift ;;
+ -brokerOptions) qpidd_extra_options=$2; shift 2 ;;
+ -help) usage; exit 0; ;;
+ -*) illegal_option "$1"; exit 1; ;;
+ '') no_command; exit 1; ;;
+ *) ignored_argument "$1"; shift; ;;
+esac
+done
+
+program=$1
+shift
+
+logfilebase=$(pwd -P)/$(basename $program)
+source $build_dir/src/tests/test_env.sh || (echo "Error: Couldn't read test_env.sh (build settings)" ; exit 1)
+source $srcdir/vg_check
+
+# Allow environment to dictate if we output xml test results
+if [ -n "$QPID_XML_TEST_OUTPUT" ] ; then
+ xml_output=yes
+fi
+
+# Use VALGRIND_OPTS="--gen-suppressions=all" to generated suppressions
+VALGRIND_OPTS="$VALGRIND_OPTS
+--leak-check=full
+--demangle=yes
+--suppressions=$srcdir/.valgrind.supp
+--num-callers=25
+"
+
+# Set up environment for running a Qpid test
+if [ -n "$start_broker" ] ; then
+ qpidd_command="$QPIDD_EXEC --auth=no --no-module-dir --daemon --port=0 --interface 127.0.0.1 --log-to-file $logfilebase-qpidd.log $qpidd_extra_options"
+ if [ -n "$VALGRIND" ] ; then
+ if [ -n "$xml_output" ] ; then
+ QPID_PORT=$($VALGRIND $VALGRIND_OPTS --xml=yes --xml-file=$logfilebase-qpidd-vg.xml -- $qpidd_command)
+ else
+ QPID_PORT=$($VALGRIND $VALGRIND_OPTS --log-file=$logfilebase-qpidd.vglog -- $qpidd_command)
+ fi
+ else
+ QPID_PORT=$($qpidd_command)
+ fi
+elif [ -r qpidd.port ]; then
+ QPID_PORT=$(cat qpidd.port)
+fi
+export QPID_PORT
+QPID_LOG_TO_FILE="$logfilebase.log"
+export QPID_LOG_TO_FILE
+
+# Export variables from makefile.
+export srcdir
+
+if [ -n "$VALGRIND" ] ; then
+ if [ -n "$xml_output" ] ; then
+ valgrind_command="$VALGRIND $VALGRIND_OPTS --xml=yes --xml-file=$logfilebase-vg.xml --"
+ else
+ VG_LOG="$logfilebase.vglog"
+ rm -f $VG_LOG*
+ valgrind_command="$VALGRIND $VALGRIND_OPTS --log-file=$VG_LOG --"
+ fi
+fi
+
+ERROR=0
+if [ -n "$run_python" -a -n "$PYTHON" ] ; then
+ (cd $working_dir; $PYTHON $program "$@") || ERROR=1
+elif [ ! -x $program ] ; then
+ echo "Cannot execute $program"
+ ERROR=1
+elif file $program | grep -q ELF; then
+ if [ -n "$boost_test" ] ; then
+ # Set boost unit test environment
+ if [ -n "$xml_output" ] ; then
+ export BOOST_TEST_SHOW_PROGRESS=no
+ export BOOST_TEST_OUTPUT_FORMAT=XML
+ export BOOST_TEST_LOG_LEVEL=test_suite
+ export BOOST_TEST_REPORT_LEVEL=no
+ (cd $working_dir; $valgrind_command $program "$@") > $logfilebase-unittest.xml || ERROR=1
+ else
+ (cd $working_dir; $valgrind_command $program "$@") || ERROR=1
+ fi
+ else
+ # This is a real executable, valgrind it if required
+ # Hide output unless there's an error.
+ (cd $working_dir; $valgrind_command $program "$@" 2>&1) || ERROR=1
+ fi
+ if [ -n "$VG_LOG" ] ; then
+ vg_check $VG_LOG* || ERROR=1
+ fi
+else
+ (cd $working_dir; $program "$@") || ERROR=1
+fi
+
+# Check log
+if [ -r $QPID_LOG_TO_FILE ]; then
+egrep 'warning\|error\|critical' $QPID_LOG_TO_FILE && {
+ echo "WARNING: Suspicious log entries in $QPID_LOG_TO_FILE, above."
+}
+fi
+
+if [ -n "$start_broker" ] ; then
+ $QPIDD_EXEC --no-module-dir --quit || ERROR=1
+
+ # Check qpidd.log.
+ egrep 'warning\|error\|critical' $logfilebase-qpidd.log && {
+ echo "WARNING: Suspicious broker log entries in qpidd.log, above."
+ }
+
+ # Check valgrind log.
+ if [ -n "$VALGRIND" -a -z "$xml_output" ] ; then
+ vg_check $logfilebase-qpidd.vglog || ERROR=1
+ fi
+fi
+exit $ERROR
diff --git a/qpid/cpp/src/tests/run_test.ps1 b/qpid/cpp/src/tests/run_test.ps1
new file mode 100644
index 0000000000..ff103e4556
--- /dev/null
+++ b/qpid/cpp/src/tests/run_test.ps1
@@ -0,0 +1,162 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+param(
+ [string]$workingDir = $pwd,
+ [string]$buildDir = $(throw "-buildDir is required"),
+ [string]$sourceDir,
+ [switch]$python = $false,
+ [switch]$boostTest = $false,
+ [switch]$xml,
+ [switch]$startBroker = $false,
+ [string]$brokerOptions,
+ [switch]$help,
+ [Parameter(Mandatory=$true, ValueFromRemainingArguments=$true, Position=0)]
+ [String[]]$rest
+ )
+
+if ([string]::IsNullOrEmpty($sourceDir)) {
+ $sourceDir = Split-Path $myInvocation.InvocationName
+}
+
+if ([string]::IsNullOrEmpty($xml)) {
+ $xml = Test-Path variable:global:QPID_XML_TEST_OUTPUT
+}
+
+# Set up environment and run a test executable or script.
+. .\test_env.ps1
+
+if ($rest[0] -eq $null) {
+ "No wrapped command specified"
+ exit 1
+}
+# The test exe is probably not in the current binary dir - it's usually
+# placed in a subdirectory based on the configuration built in Visual Studio.
+# So check around to see where it is - when located, set the QPID_LIB_DIR
+# and PATH to look in the corresponding configuration off the src directory,
+# one level up.
+$prog = $rest[0]
+$logfilebase = [System.IO.Path]::GetFileNameWithoutExtension($prog)
+$logfilebase = "$pwd\\$logfilebase"
+# Qpid client lib sees QPID_LOG_TO_FILE; acts like using --log-to-file on
+# command line.
+$env:QPID_LOG_TO_FILE = "$logfilebase.log"
+$is_script = $prog -match ".ps1$"
+if (($is_script -or $python) -and !(Test-Path "$prog")) {
+ "$prog does not exist"
+ exit 1
+}
+if (!$is_script -and !(Test-Path "$prog")) {
+ . $sourceDir\find_prog.ps1 $prog
+ $rest[0] = $prog
+ $env:QPID_LIB_DIR = "..\$sub"
+}
+
+# Set up environment for running a Qpid test. If a broker should be started,
+# do that, else check for a saved port number to use.
+if ($startBroker) {
+ $broker = new-object System.Diagnostics.ProcessStartInfo
+ $broker.WorkingDirectory = $pwd
+ $broker.UseShellExecute = $false
+ $broker.CreateNoWindow = $true
+ $broker.RedirectStandardOutput = $true
+ $broker.FileName = $env:QPIDD_EXEC
+ $broker.Arguments = "--auth=no --no-module-dir --port=0 --interface 127.0.0.1 --log-to-file $logfilebase-qpidd.log $brokerOptions"
+ $broker_process = [System.Diagnostics.Process]::Start($broker)
+ $env:QPID_PORT = $broker_process.StandardOutput.ReadLine()
+}
+else {
+ # If qpidd.port exists and is not empty run test with QPID_PORT set.
+ if (Test-Path qpidd.port) {
+ set-item -path env:QPID_PORT -value (get-content -path qpidd.port -totalcount 1)
+ }
+}
+
+# Now start the real test.
+if ($python) {
+ $to_run = $PYTHON_EXE
+ $skip_args0 = $false
+ $outputfile = ""
+}
+elseif ($boostTest) {
+ if ($xml) {
+ $env:BOOST_TEST_SHOW_PROGRESS=no
+ $env:BOOST_TEST_OUTPUT_FORMAT=XML
+ $env:BOOST_TEST_LOG_LEVEL=test_suite
+ $env:BOOST_TEST_REPORT_LEVEL=no
+ $to_run = $rest[0]
+ $skip_args0 = $true
+ $outputfile = "$logfilebase-unittest.xml"
+ }
+ else {
+ $to_run = $rest[0]
+ $skip_args0 = $true
+ $outputfile = ""
+ }
+}
+else {
+ # Non-boost executable or powershell script
+ $outputfile = ""
+ if ($is_script) {
+ $to_run = (get-command powershell.exe).Definition
+ $skip_args0 = $false
+ }
+ else {
+ $to_run = $rest[0]
+ $skip_args0 = $true
+ }
+}
+
+if ($skip_args0) {
+ $arglist = $rest[1..($rest.length-1)]
+}
+else {
+ $arglist = $rest
+}
+
+if ($outputfile -eq "") {
+ $p = Start-Process -FilePath $to_run -ArgumentList $arglist -NoNewWindow -PassThru
+ $line = ""
+}
+else {
+ $p = Start-Process -FilePath $to_run -ArgumentList $arglist -NoNewWindow -RedirectStandardOutput $outputfile -PassThru
+}
+Wait-Process -InputObject $p
+$status = $p.ExitCode
+
+if (Test-Path $env:QPID_LOG_TO_FILE) {
+ $problems = Select-String -Path $env:QPID_LOG_TO_FILE -pattern " error ", " warning ", " critical "
+ if ($problems -ne $null) {
+ "WARNING: suspicious log entries in $env:QPID_LOG_TO_FILE:\n$problems"
+ $status = 1
+ }
+}
+
+# If a broker was started, stop it.
+if ($startBroker) {
+ & $env:QPIDD_EXEC --no-module-dir --quit
+ # Check qpid log for problems
+ $problems = Select-String -Path $logfilebase-qpidd.log -pattern " error ", " warning ", " critical "
+ if ($problems -ne $null) {
+ "WARNING: suspicious log entries in $logfilebase-qpidd.log:\n$problems"
+ $status = 1
+ }
+}
+
+exit $status
diff --git a/qpid/cpp/src/tests/sasl_fed b/qpid/cpp/src/tests/sasl_fed
new file mode 100755
index 0000000000..38ef43f56f
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed
@@ -0,0 +1,169 @@
+#!/usr/bin/env 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.
+#
+
+# This minimum value corresponds to sasl version 2.1.22
+minimum_sasl_version=131350
+
+sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version`
+
+# This test is necessary becasue this sasl version is the first one that permits
+# redirection of the sasl config file path.
+if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
+ echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
+ exit 0
+fi
+
+# In a distribution, the python tools will be absent.
+if [ ! -f $QPID_CONFIG_EXEC ] || [ ! -f $QPID_ROUTE_EXEC ] ; then
+ echo "python tools absent - skipping sasl_fed."
+ exit 0
+fi
+
+
+sasl_config_file=$QPID_TEST_EXEC_DIR/sasl_config
+
+my_random_number=$RANDOM
+tmp_root=/tmp/sasl_fed_$my_random_number
+mkdir -p $tmp_root
+
+# create ACL file to allow links
+echo acl allow all all > $tmp_root/sasl_fed.acl
+
+
+#--------------------------------------------------
+#echo " Starting broker 1"
+#--------------------------------------------------
+$QPIDD_EXEC \
+ -p 0 --interface 127.0.0.1 \
+ --data-dir $tmp_root/data_1 \
+ --auth=yes \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --log-to-file $tmp_root/qpidd_1.log \
+ --sasl-config=$sasl_config_file \
+ --acl-file $tmp_root/sasl_fed.acl \
+ -d > $tmp_root/broker_1_port
+
+broker_1_port=`cat $tmp_root/broker_1_port`
+
+
+#--------------------------------------------------
+#echo " Starting broker 2"
+#--------------------------------------------------
+$QPIDD_EXEC \
+ -p 0 --interface 127.0.0.1 \
+ --data-dir $tmp_root/data_2 \
+ --auth=yes \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --log-to-file $tmp_root/qpidd_2.log \
+ --sasl-config=$sasl_config_file \
+ --acl-file $tmp_root/sasl_fed.acl \
+ -d > $tmp_root/broker_2_port
+
+broker_2_port=`cat $tmp_root/broker_2_port`
+
+sleep 2
+
+# I am not randomizing these names, because the test creates its own brokers.
+QUEUE_NAME=sasl_fed_queue
+ROUTING_KEY=sasl_fed_queue
+EXCHANGE_NAME=sasl_fedex
+
+#--------------------------------------------------
+#echo " add exchanges"
+#--------------------------------------------------
+$QPID_CONFIG_EXEC -b localhost:$broker_1_port add exchange direct $EXCHANGE_NAME
+$QPID_CONFIG_EXEC -b localhost:$broker_2_port add exchange direct $EXCHANGE_NAME
+
+
+#--------------------------------------------------
+#echo " add queues"
+#--------------------------------------------------
+$QPID_CONFIG_EXEC -b localhost:$broker_1_port add queue $QUEUE_NAME
+$QPID_CONFIG_EXEC -b localhost:$broker_2_port add queue $QUEUE_NAME
+
+sleep 5
+
+#--------------------------------------------------
+#echo " create bindings"
+#--------------------------------------------------
+$QPID_CONFIG_EXEC -b localhost:$broker_1_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+$QPID_CONFIG_EXEC -b localhost:$broker_2_port bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+
+sleep 5
+
+
+#--------------------------------------------------
+#echo " qpid-route route add"
+#--------------------------------------------------
+$QPID_ROUTE_EXEC route add zag/zag@localhost:$broker_2_port zag/zag@localhost:$broker_1_port $EXCHANGE_NAME $ROUTING_KEY "" "" DIGEST-MD5
+
+sleep 5
+
+
+n_messages=100
+#--------------------------------------------------
+#echo " Sending 100 messages to $broker_1_port "
+#--------------------------------------------------
+$QPID_TEST_EXEC_DIR/datagen --count $n_messages | $SENDER_EXEC --mechanism DIGEST-MD5 --username zag --password zag --exchange $EXCHANGE_NAME --routing-key $ROUTING_KEY --port $broker_1_port
+
+sleep 5
+
+#--------------------------------------------------
+#echo " Examine Broker $broker_1_port"
+#--------------------------------------------------
+broker_1_message_count=`$PYTHON_COMMANDS/qpid-stat -q -b localhost:$broker_1_port | grep sasl_fed_queue | awk '{print $2}'`
+#echo " "
+
+#--------------------------------------------------
+#echo " Examine Broker $broker_2_port"
+#--------------------------------------------------
+broker_2_message_count=`$PYTHON_COMMANDS/qpid-stat -q -b localhost:$broker_2_port | grep sasl_fed_queue | awk '{print $2}'`
+#echo " "
+
+#--------------------------------------------------
+#echo " Asking brokers to quit."
+#--------------------------------------------------
+$QPIDD_EXEC --port $broker_1_port --quit
+$QPIDD_EXEC --port $broker_2_port --quit
+
+
+#--------------------------------------------------
+#echo "Removing temporary directory $tmp_root"
+#--------------------------------------------------
+rm -rf $tmp_root
+
+if [ "$broker_2_message_count" -eq "$n_messages" ]; then
+ # echo "good: |$broker_2_message_count| == |$n_messages|"
+ exit 0
+else
+ # echo "not ideal: |$broker_1_message_count| != |$n_messages|"
+ exit 1
+fi
+
+
+
+
+
+
diff --git a/qpid/cpp/src/tests/sasl_fed_ex b/qpid/cpp/src/tests/sasl_fed_ex
new file mode 100755
index 0000000000..e2ee37ba39
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_fed_ex
@@ -0,0 +1,283 @@
+#!/usr/bin/env 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.
+#
+
+#===============================================================================
+# These tests create federated links between two brokers using SASL security.
+# The SASL mechanism used is EXTERNAL, which is satisfied by SSL
+# transport-layer security.
+#===============================================================================
+
+source $QPID_TEST_COMMON
+
+ensure_python_tests
+
+script_name=`basename $0`
+
+if [ $# -lt 1 ] || [ $# -gt 2 ]
+then
+ echo
+ # These are the four different ways of creating links ( or routes+links )
+ # that the qpid-route command provides.
+ echo "Usage: ${script_name} dynamic|link|queue|route"
+ echo
+ exit 1
+fi
+
+qpid_route_method=$1
+
+# Debugging print. --------------------------
+debug=
+function print {
+ if [ "$debug" ]; then
+ echo "${script_name}: $1"
+ fi
+}
+
+print "=========== start sasl_fed_ex $* ============"
+
+
+
+# This minimum value corresponds to sasl version 2.1.22
+minimum_sasl_version=131350
+
+sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version`
+
+# This test is necessary because this sasl version is the first one that permits
+# redirection of the sasl config file path.
+if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
+ echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
+ exit 0
+fi
+
+CERT_DIR=`pwd`/test_cert_db
+CERT_PW_FILE=`pwd`/cert.password
+TEST_HOSTNAME=127.0.0.1
+
+create_certs() {
+ #create certificate and key databases with single, simple, self-signed certificate in it
+ mkdir ${CERT_DIR}
+ certutil -N -d ${CERT_DIR} -f ${CERT_PW_FILE}
+ certutil -S -d ${CERT_DIR} -n ${TEST_HOSTNAME} -s "CN=${TEST_HOSTNAME}" -t "CT,," -x -f ${CERT_PW_FILE} -z /bin/sh 2> /dev/null
+}
+
+delete_certs() {
+ if [[ -e ${CERT_DIR} ]] ; then
+ print "removing cert dir ${CERT_DIR}"
+ rm -rf ${CERT_DIR}
+ fi
+}
+
+
+CERTUTIL=$(type -p certutil)
+if [[ !(-x $CERTUTIL) ]] ; then
+ echo "No certutil, skipping ssl test";
+ exit 0;
+fi
+
+delete_certs
+create_certs 2> /dev/null
+if [ ! $? ]; then
+ error "Could not create test certificate"
+ exit 1
+fi
+
+sasl_config_dir=$QPID_TEST_EXEC_DIR/sasl_config
+
+tmp_root=$QPID_TEST_EXEC_DIR/sasl_fed_ex_temp
+print "results dir is ${tmp_root}"
+rm -rf ${tmp_root}
+mkdir -p $tmp_root
+
+SRC_SSL_PORT=6667
+DST_SSL_PORT=6666
+
+SRC_SSL_PORT_2=6668
+DST_SSL_PORT_2=6669
+
+SRC_TCP_PORT=5801
+DST_TCP_PORT=5807
+
+SRC_TCP_PORT_2=5802
+DST_TCP_PORT_2=5803
+
+export QPID_SSL_CERT_NAME=${TEST_HOSTNAME}
+
+export QPID_NO_MODULE_DIR=1
+export QPID_SSL_CERT_DB=${CERT_DIR}
+export QPID_SSL_CERT_PASSWORD_FILE=${CERT_PW_FILE}
+export QPID_SSL_CERT_NAME=${TEST_HOSTNAME}
+
+
+
+#######################################
+# Understanding this Plumbing
+#######################################
+# 1. when you establish the route with qpid-route,
+# here is the best termiology to use:
+#
+# qpid-route route add DST SRC
+#
+# 2. DST will connect to SRC through the ssl port of SRC.
+#
+# 3. sender client connects to the tcp port of SRC.
+#
+# 4. sender specifies mechanism ANONYMOUS.
+#
+# 5. DST pulls messages off the temp queue on SRC to itself.
+#
+
+COMMON_BROKER_OPTIONS=" \
+ --ssl-sasl-no-dict \
+ --sasl-config=$sasl_config_dir \
+ --ssl-require-client-authentication \
+ --auth yes \
+ --ssl-cert-db $CERT_DIR \
+ --ssl-cert-password-file $CERT_PW_FILE \
+ --ssl-cert-name $TEST_HOSTNAME \
+ --no-data-dir \
+ --no-module-dir \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --daemon "
+
+
+function start_brokers {
+ # vanilla brokers --------------------------------
+ print "Starting SRC broker"
+ $QPIDD_EXEC \
+ --port=${SRC_TCP_PORT} \
+ --ssl-port ${SRC_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
+
+ broker_ports[0]=${SRC_TCP_PORT}
+
+ print "Starting DST broker"
+ $QPIDD_EXEC \
+ --port=${DST_TCP_PORT} \
+ --ssl-port ${DST_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
+
+ broker_ports[1]=${DST_TCP_PORT}
+}
+
+function halt_brokers {
+ n_brokers=${#broker_ports[@]}
+ print "Halting ${n_brokers} brokers."
+ for i in $(seq 0 $((${n_brokers} - 1)))
+ do
+ halt_port=${broker_ports[$i]}
+ print "Halting broker $i on port ${halt_port}"
+ $QPIDD_EXEC --port ${halt_port} --quit
+ done
+
+}
+
+
+start_brokers
+
+
+# I am not randomizing these names, because this test creates its own brokers.
+QUEUE_NAME=sasl_fed_queue
+ROUTING_KEY=sasl_fed_queue
+EXCHANGE_NAME=sasl_fedex
+
+
+print "add exchanges"
+$QPID_CONFIG_EXEC -b localhost:${SRC_TCP_PORT} add exchange direct $EXCHANGE_NAME
+$QPID_CONFIG_EXEC -b localhost:${DST_TCP_PORT} add exchange direct $EXCHANGE_NAME
+
+
+print "add queues"
+$QPID_CONFIG_EXEC -b localhost:${SRC_TCP_PORT} add queue $QUEUE_NAME
+$QPID_CONFIG_EXEC -b localhost:${DST_TCP_PORT} add queue $QUEUE_NAME
+
+
+print "create bindings"
+$QPID_CONFIG_EXEC -b localhost:${SRC_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+$QPID_CONFIG_EXEC -b localhost:${DST_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
+
+
+#
+# NOTE: The SRC broker *must* be referred to as $TEST_HOSTNAME, and not as "localhost".
+# It must be referred to by the exact string given as the Common Name (CN) in the cert,
+# which was created in the function create_certs, above.
+
+
+
+#----------------------------------------------------------------
+# Use qpid-route to create the link, or the link+route, depending
+# on which of its several methods was requested.
+#----------------------------------------------------------------
+if [ ${qpid_route_method} == "dynamic" ]; then
+ print "dynamic add"
+ $QPID_ROUTE_EXEC -t ssl dynamic add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME "" "" EXTERNAL
+elif [ ${qpid_route_method} == "link" ]; then
+ print "link add"
+ $QPID_ROUTE_EXEC -t ssl link add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} EXTERNAL
+elif [ ${qpid_route_method} == "queue" ]; then
+ print "queue add"
+ $QPID_ROUTE_EXEC -t ssl queue add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY EXTERNAL
+elif [ ${qpid_route_method} == "route" ]; then
+ print "route add"
+ $QPID_ROUTE_EXEC -t ssl route add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY "" "" EXTERNAL
+else
+ echo "unknown method: |${qpid_route_method}|"
+ echo " choices are: dynamic|link|queue|route "
+ halt_brokers
+ exit 1
+fi
+
+
+# I don't know how to avoid this sleep yet. It has to come after route-creation
+# to avoid false negatives.
+sleep 5
+
+# Look only at the transport field, which should be "ssl".
+print "check the link"
+link_status=$($QPID_ROUTE_EXEC link list localhost:${DST_TCP_PORT} | tail -1 | awk '{print $3}')
+
+halt_brokers
+
+sleep 1
+
+if [ ! ${link_status} ]; then
+ print "link_status is empty"
+ print "result: fail"
+ exit 2
+fi
+
+if [ ${link_status} == "ssl" ]; then
+ print "result: good"
+ # Only remove the tmp_root on success, to permit debugging.
+ print "Removing temporary directory $tmp_root"
+ rm -rf $tmp_root
+ exit 0
+fi
+
+print "link_status has a bad value: ${link_status}"
+print "result: fail"
+exit 3
+
+
+
diff --git a/qpid/cpp/src/tests/sasl_no_dir b/qpid/cpp/src/tests/sasl_no_dir
new file mode 100755
index 0000000000..b2f5d1668e
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_no_dir
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+script_name=`basename $0`
+
+# This minimum value corresponds to sasl version 2.1.22
+minimum_sasl_version=131350
+
+sasl_version=$($QPID_TEST_EXEC_DIR/sasl_version)
+
+# This test is necessary because this sasl version is the first one that permits
+# redirection of the sasl config file path.
+if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
+ echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
+ exit 0
+fi
+
+
+sasl_config_dir=$QPID_TEST_EXEC_DIR/sasl_config
+
+
+# Debugging print. --------------------------
+debug=
+function print {
+ if [ "$debug" ]; then
+ echo "${script_name}: $1"
+ fi
+}
+
+
+my_random_number=$RANDOM
+tmp_root=/tmp/sasl_fed_$my_random_number
+mkdir -p $tmp_root
+
+
+LOG_FILE=$tmp_root/qpidd.log
+
+# If you want to see this test fail, just comment out this 'mv' command.
+print "Moving sasl configuration dir."
+mv ${sasl_config_dir} ${sasl_config_dir}-
+
+
+#--------------------------------------------------
+print " Starting broker"
+#--------------------------------------------------
+$QPIDD_EXEC \
+ -p 0 --interface 127.0.0.1 \
+ --no-data-dir \
+ --auth=yes \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --log-to-file ${LOG_FILE} \
+ --sasl-config=$sasl_config_dir \
+ -d 2> /dev/null 1> $tmp_root/broker_port
+
+
+
+# If it works right, the output will look something like this: ( two lines long )
+# Daemon startup failed: SASL: sasl_set_path failed: no such directory: /home/mick/trunk/qpid/cpp/src/tests/sasl_config (qpid/broker/SaslAuthenticator.cpp:112)
+# 2011-10-13 14:07:00 critical qpidd.cpp:83: Unexpected error: Daemon startup failed: SASL: sasl_set_path failed: no such directory: /home/mick/trunk/qpid/cpp/src/tests/sasl_config (qpid/broker/SaslAuthenticator.cpp:112)
+
+result=`cat ${LOG_FILE} | grep "sasl_set_path failed: no such directory" | wc -l `
+
+#--------------------------------------------------
+print "Restore the Sasl config dir to its original place."
+#--------------------------------------------------
+mv ${sasl_config_dir}- ${sasl_config_dir}
+
+if [ "2" -eq ${result} ]; then
+ print "result: success"
+ rm -rf $tmp_root
+ exit 0
+fi
+
+
+# If this test fails, the broker is still alive.
+# Kill it.
+broker_port=`cat $tmp_root/broker_port`
+#--------------------------------------------------
+print "Asking broker to quit."
+#--------------------------------------------------
+$QPIDD_EXEC --port $broker_port --quit
+
+rm -rf $tmp_root
+
+print "result: fail"
+exit 1
diff --git a/qpid/cpp/src/tests/sasl_test_setup.sh b/qpid/cpp/src/tests/sasl_test_setup.sh
new file mode 100755
index 0000000000..d41efbe6e5
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_test_setup.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+source ./test_env.sh
+
+test -x $SASL_PW || { echo Skipping SASL test, saslpasswd2 not found; exit 0; }
+
+mkdir -p sasl_config
+
+# Create configuration file.
+cat > sasl_config/qpidd.conf <<EOF
+pwcheck_method: auxprop
+auxprop_plugin: sasldb
+sasldb_path: $PWD/sasl_config/qpidd.sasldb
+sql_select: dummy select
+mech_list: ANONYMOUS PLAIN DIGEST-MD5 EXTERNAL CRAM-MD5
+EOF
+
+# Populate temporary sasl db.
+SASLTEST_DB=./sasl_config/qpidd.sasldb
+rm -f $SASLTEST_DB
+echo guest | $SASL_PW -c -p -f $SASLTEST_DB -u QPID guest
+echo zig | $SASL_PW -c -p -f $SASLTEST_DB -u QPID zig
+echo zag | $SASL_PW -c -p -f $SASLTEST_DB -u QPID zag
+
diff --git a/qpid/cpp/src/tests/sasl_version.cpp b/qpid/cpp/src/tests/sasl_version.cpp
new file mode 100644
index 0000000000..db3efe4181
--- /dev/null
+++ b/qpid/cpp/src/tests/sasl_version.cpp
@@ -0,0 +1,48 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 "sasl/sasl.h"
+
+
+/*
+ Some tests need to distinguish between different versions of
+ SASL. This encodes and outputs the version number as an integer
+ for easy use in testing scripts.
+*/
+
+int
+main ( )
+{
+ // I assume that these are 8-bit quantities....
+ int sasl_version = (SASL_VERSION_MAJOR << 16) +
+ (SASL_VERSION_MINOR << 8) +
+ SASL_VERSION_STEP;
+
+ std::cout << sasl_version << std::endl;
+
+ return 0;
+}
+
+
+
+
diff --git a/qpid/cpp/src/tests/sender.cpp b/qpid/cpp/src/tests/sender.cpp
new file mode 100644
index 0000000000..063b5e87dc
--- /dev/null
+++ b/qpid/cpp/src/tests/sender.cpp
@@ -0,0 +1,157 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <qpid/client/FailoverManager.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/AsyncSession.h>
+#include <qpid/client/Message.h>
+#include <qpid/client/MessageReplayTracker.h>
+#include <qpid/client/QueueOptions.h>
+#include <qpid/Exception.h>
+#include "TestOptions.h"
+
+#include "qpid/messaging/Message.h" // Only for Statistics
+#include "Statistics.h"
+
+#include <fstream>
+#include <iostream>
+
+using namespace qpid;
+using namespace qpid::client;
+using namespace qpid::framing;
+
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ string destination;
+ string key;
+ uint sendEos;
+ bool durable;
+ uint ttl;
+ string lvqMatchValue;
+ string lvqMatchFile;
+ bool reportTotal;
+ int reportEvery;
+ bool reportHeader;
+
+ Args() :
+ key("test-queue"), sendEos(0), durable(false), ttl(0),
+ reportTotal(false),
+ reportEvery(0),
+ reportHeader(true)
+ {
+ addOptions()
+ ("exchange", qpid::optValue(destination, "EXCHANGE"), "Exchange to send messages to")
+ ("routing-key", qpid::optValue(key, "KEY"), "Routing key to add to messages")
+ ("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input")
+ ("durable", qpid::optValue(durable, "true|false"), "Mark messages as durable.")
+ ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds")
+ ("lvq-match-value", qpid::optValue(lvqMatchValue, "KEY"), "The value to set for the LVQ match key property")
+ ("lvq-match-file", qpid::optValue(lvqMatchFile, "FILE"), "A file containing values to set for the LVQ match key property")
+ ("report-total", qpid::optValue(reportTotal), "Report total throughput statistics")
+ ("report-every", qpid::optValue(reportEvery,"N"), "Report throughput statistics every N messages")
+ ("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
+ ;
+ }
+};
+
+const string EOS("eos");
+
+class Sender : public FailoverManager::Command
+{
+ public:
+ Sender(Reporter<Throughput>& reporter, const std::string& destination, const std::string& key, uint sendEos, bool durable, uint ttl,
+ const std::string& lvqMatchValue, const std::string& lvqMatchFile);
+ void execute(AsyncSession& session, bool isRetry);
+
+ private:
+ Reporter<Throughput>& reporter;
+ messaging::Message dummyMessage;
+ const std::string destination;
+ MessageReplayTracker sender;
+ Message message;
+ const uint sendEos;
+ uint sent;
+ std::ifstream lvqMatchValues;
+};
+
+Sender::Sender(Reporter<Throughput>& rep, const std::string& dest, const std::string& key, uint eos, bool durable, uint ttl, const std::string& lvqMatchValue, const std::string& lvqMatchFile) :
+ reporter(rep), destination(dest), sender(10), message("", key), sendEos(eos), sent(0) , lvqMatchValues(lvqMatchFile.c_str())
+{
+ if (durable){
+ message.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT);
+ }
+
+ if (ttl) {
+ message.getDeliveryProperties().setTtl(ttl);
+ }
+
+ if (!lvqMatchValue.empty()) {
+ message.getHeaders().setString(QueueOptions::strLVQMatchProperty, lvqMatchValue);
+ }
+}
+
+void Sender::execute(AsyncSession& session, bool isRetry)
+{
+ if (isRetry) sender.replay(session);
+ else sender.init(session);
+ string data;
+ while (getline(std::cin, data)) {
+ message.setData(data);
+ //message.getHeaders().setInt("SN", ++sent);
+ string matchKey;
+ if (lvqMatchValues && getline(lvqMatchValues, matchKey)) {
+ message.getHeaders().setString(QueueOptions::strLVQMatchProperty, matchKey);
+ }
+ reporter.message(dummyMessage); // For statistics
+ sender.send(message, destination);
+ }
+ for (uint i = sendEos; i > 0; --i) {
+ message.setData(EOS);
+ sender.send(message, destination);
+ }
+}
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ Reporter<Throughput> reporter(std::cout, opts.reportEvery, opts.reportHeader);
+ FailoverManager connection(opts.con);
+ Sender sender(reporter, opts.destination, opts.key, opts.sendEos, opts.durable, opts.ttl, opts.lvqMatchValue, opts.lvqMatchFile);
+ connection.execute(sender);
+ connection.close();
+ if (opts.reportTotal) reporter.report();
+ return 0;
+ } catch(const std::exception& error) {
+ std::cout << "Failed: " << error.what() << std::endl;
+ }
+ return 1;
+}
diff --git a/qpid/cpp/src/tests/shared_perftest b/qpid/cpp/src/tests/shared_perftest
new file mode 100755
index 0000000000..709ffd56b5
--- /dev/null
+++ b/qpid/cpp/src/tests/shared_perftest
@@ -0,0 +1,22 @@
+#!/usr/bin/env 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.
+#
+
+exec `dirname $0`/run_perftest 100000 --mode shared --npubs 16 --nsubs 16
diff --git a/qpid/cpp/src/tests/shlibtest.cpp b/qpid/cpp/src/tests/shlibtest.cpp
new file mode 100644
index 0000000000..5655eb7e64
--- /dev/null
+++ b/qpid/cpp/src/tests/shlibtest.cpp
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+namespace qpid {
+namespace tests {
+
+int* loaderData = 0;
+extern "C"
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+void callMe(int *i) { loaderData=i; }
+
+struct OnUnload { ~OnUnload() { *loaderData=42; } };
+OnUnload unloader; // For destructor.
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/ssl_test b/qpid/cpp/src/tests/ssl_test
new file mode 100755
index 0000000000..d681059495
--- /dev/null
+++ b/qpid/cpp/src/tests/ssl_test
@@ -0,0 +1,331 @@
+#!/usr/bin/env 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 a simple test over SSL
+
+#set -x
+
+CONFIG=$(dirname $0)/config.null
+TEST_CERT_DIR=`pwd`/test_cert_dir
+CERT_DB=${TEST_CERT_DIR}/test_cert_db
+CERT_PW_FILE=`pwd`/cert.password
+TEST_HOSTNAME=127.0.0.1
+TEST_CLIENT_CERT=rumplestiltskin
+CA_PEM_FILE=${TEST_CERT_DIR}/ca_cert.pem
+OTHER_CA_CERT_DB=${TEST_CERT_DIR}/x_ca_cert_db
+OTHER_CA_PEM_FILE=${TEST_CERT_DIR}/other_ca_cert.pem
+PY_PING_BROKER=${QPID_TEST_SRC_DIR}/ping_broker
+COUNT=10
+
+if [[ -a $AMQP_LIB ]] ; then
+ MODULES="--load-module $AMQP_LIB"
+fi
+
+trap cleanup EXIT
+
+error() { echo $*; exit 1; }
+
+# create the test certificate database
+# $1 = string used as Subject in server's certificate
+# $2 = string used as SubjectAlternateName (SAN) in server's certificate
+create_certs() {
+
+ local CERT_SUBJECT=${1:-"CN=${TEST_HOSTNAME},O=MyCo,ST=Massachusetts,C=US"}
+ local CERT_SAN=${2:-"*.server.com"}
+
+ mkdir -p ${TEST_CERT_DIR}
+ rm -rf ${TEST_CERT_DIR}/*
+
+ # Set Up a CA with a self-signed Certificate
+ #
+ mkdir -p ${CERT_DB}
+ certutil -N -d ${CERT_DB} -f ${CERT_PW_FILE}
+ certutil -S -d ${CERT_DB} -n "Test-CA" -s "CN=Test-CA,O=MyCo,ST=Massachusetts,C=US" -t "CT,," -x -f ${CERT_PW_FILE} -z /bin/sh >/dev/null 2>&1
+ certutil -L -d ${CERT_DB} -n "Test-CA" -a -o ${CERT_DB}/rootca.crt -f ${CERT_PW_FILE}
+ #certutil -L -d ${CERT_DB} -f ${CERT_PW_FILE}
+
+ # create server certificate signed by Test-CA
+ #
+ certutil -R -d ${CERT_DB} -s "${CERT_SUBJECT}" -o ${TEST_CERT_DIR}/server.req -f ${CERT_PW_FILE} -z /bin/sh > /dev/null 2>&1
+ certutil -C -d ${CERT_DB} -c "Test-CA" -8 "${CERT_SAN}" -i ${TEST_CERT_DIR}/server.req -o ${TEST_CERT_DIR}/server.crt -f ${CERT_PW_FILE} -m ${RANDOM}
+ certutil -A -d ${CERT_DB} -n ${TEST_HOSTNAME} -i ${TEST_CERT_DIR}/server.crt -t "Pu,,"
+
+ # create a certificate to identify the client
+ #
+ certutil -R -d ${CERT_DB} -s "CN=${TEST_CLIENT_CERT}" -o ${TEST_CERT_DIR}/client.req -f ${CERT_PW_FILE} -z /bin/sh > /dev/null 2>&1
+ certutil -C -d ${CERT_DB} -c "Test-CA" -8 "*.client.com" -i ${TEST_CERT_DIR}/client.req -o ${TEST_CERT_DIR}/client.crt -f ${CERT_PW_FILE} -m ${RANDOM}
+ certutil -A -d ${CERT_DB} -n ${TEST_CLIENT_CERT} -i ${TEST_CERT_DIR}/client.crt -t "Pu,,"
+ ###
+ #certutil -N -d ${SERVER_CERT_DIR} -f ${CERT_PW_FILE}
+ #certutil -S -d ${SERVER_CERT_DIR} -n ${TEST_HOSTNAME} -s "CN=${TEST_HOSTNAME}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil
+ #certutil -S -d ${SERVER_CERT_DIR} -n ${TEST_CLIENT_CERT} -s "CN=${TEST_CLIENT_CERT}" -t "CT,," -x -f ${CERT_PW_FILE} -z /usr/bin/certutil
+
+ # Set up a separate DB with its own CA for testing failure to validate scenario
+ #
+ mkdir -p ${OTHER_CA_CERT_DB}
+ certutil -N -d ${OTHER_CA_CERT_DB} -f ${CERT_PW_FILE}
+ certutil -S -d ${OTHER_CA_CERT_DB} -n "Other-Test-CA" -s "CN=Another Test CA,O=MyCo,ST=Massachusetts,C=US" -t "CT,," -x -f ${CERT_PW_FILE} -z /bin/sh >/dev/null 2>&1
+ certutil -L -d ${OTHER_CA_CERT_DB} -n "Other-Test-CA" -a -o ${OTHER_CA_CERT_DB}/rootca.crt -f ${CERT_PW_FILE}
+ #certutil -L -d ${OTHER_CA_CERT_DB} -f ${CERT_PW_FILE}
+}
+
+delete_certs() {
+ if [[ -e ${TEST_CERT_DIR} ]] ; then
+ rm -rf ${TEST_CERT_DIR}
+ fi
+}
+
+# Don't need --no-module-dir or --no-data-dir as they are set as env vars in test_env.sh
+COMMON_OPTS="--daemon --config $CONFIG --ssl-cert-db $CERT_DB --ssl-cert-password-file $CERT_PW_FILE --ssl-cert-name $TEST_HOSTNAME"
+
+# Start new brokers:
+# $1 must be integer
+# $2 = extra opts
+# Append used ports to PORTS variable
+start_brokers() {
+ local -a ports
+ for (( i=0; $i<$1; i++)) do
+ ports[$i]=$($QPIDD_EXEC --port 0 --interface 127.0.0.1 $COMMON_OPTS $2) || error "Could not start broker $i"
+ done
+ PORTS=( ${PORTS[@]} ${ports[@]} )
+}
+
+# Stop single broker:
+# $1 is number of broker to stop (0 based)
+stop_broker() {
+ $QPIDD_EXEC -qp ${PORTS[$1]}
+
+ # Remove from ports array
+ unset PORTS[$1]
+}
+
+stop_brokers() {
+ for port in "${PORTS[@]}";
+ do
+ $QPIDD_EXEC -qp $port
+ done
+ PORTS=()
+}
+
+pick_port() {
+ # We need a fixed port to set --cluster-url. Use qpidd to pick a free port.
+ PICK=`../qpidd --no-module-dir --listen-disable ssl -dp0`
+ ../qpidd --no-module-dir -qp $PICK
+ echo $PICK
+}
+
+cleanup() {
+ stop_brokers
+ delete_certs
+ rm -f ${CERT_PW_FILE}
+}
+
+start_ssl_broker() {
+ start_brokers 1 "--transport ssl --ssl-port 0 --require-encryption --auth no $MODULES"
+}
+
+start_ssl_mux_broker() {
+ ../qpidd $COMMON_OPTS --port $1 --ssl-port $1 --auth no
+ PORTS=( ${PORTS[@]} $1 )
+}
+
+sasl_config_dir=$QPID_TEST_EXEC_DIR/sasl_config
+
+start_authenticating_broker() {
+ start_brokers 1 "--transport ssl --ssl-port 0 --require-encryption --ssl-sasl-no-dict --ssl-require-client-authentication --auth yes --sasl-config=${sasl_config_dir} $MODULES"
+}
+
+ssl_cluster_broker() { # $1 = port
+ start_brokers 1 "--ssl-port $1 --auth no --load-module $CLUSTER_LIB --cluster-name ssl_test.$HOSTNAME.$$ --cluster-url amqp:ssl:$TEST_HOSTNAME:$1"
+
+ # Wait for broker to be ready
+ qpid-ping -Pssl -b $TEST_HOSTNAME:$1 -q || { echo "Cannot connect to broker on $1"; exit 1; }
+}
+
+CERTUTIL=$(type -p certutil)
+if [[ !(-x $CERTUTIL) ]] ; then
+ echo "No certutil, skipping ssl test";
+ exit 0;
+fi
+
+if [[ !(-e ${CERT_PW_FILE}) ]] ; then
+ echo password > ${CERT_PW_FILE}
+fi
+delete_certs
+create_certs || error "Could not create test certificate database"
+
+start_ssl_broker
+PORT=${PORTS[0]}
+echo "Running SSL test on port $PORT"
+export QPID_NO_MODULE_DIR=1
+export QPID_SSL_CERT_DB=${CERT_DB}
+export QPID_SSL_CERT_PASSWORD_FILE=${CERT_PW_FILE}
+
+## Test connection via connection settings
+./qpid-perftest --count ${COUNT} --port ${PORT} -P ssl -b $TEST_HOSTNAME --summary
+
+## Test connection with a URL
+URL=amqp:ssl:$TEST_HOSTNAME:$PORT
+./qpid-send -b $URL --content-string=hello -a "foo;{create:always}"
+MSG=`./qpid-receive -b $URL -a "foo;{create:always}" --messages 1`
+test "$MSG" = "hello" || { echo "receive failed '$MSG' != 'hello'"; exit 1; }
+
+if [[ -a $AMQP_LIB ]] ; then
+ echo "Testing ssl over AMQP 1.0"
+ ./qpid-send --connection-options '{protocol:amqp1.0}' -b $URL --content-string=hello -a "foo;{create:always}"
+ MSG=`./qpid-receive --connection-options '{protocol:amqp1.0}' -b $URL -a "foo;{create:always}" --messages 1`
+ test "$MSG" = "hello" || { echo "receive failed for AMQP 1.0 '$MSG' != 'hello'"; exit 1; }
+fi
+
+## Test connection with a combination of URL and connection options (in messaging API)
+URL=$TEST_HOSTNAME:$PORT
+./qpid-send -b $URL --connection-options '{transport:ssl,heartbeat:2}' --content-string='hello again' -a "foo;{create:always}"
+MSG=`./qpid-receive -b $URL --connection-options '{transport:ssl,heartbeat:2}' -a "foo;{create:always}" --messages 1`
+test "$MSG" = "hello again" || { echo "receive failed '$MSG' != 'hello again'"; exit 1; }
+
+## Test using the Python client
+if test -d $PYTHON_DIR; then
+ echo "Testing Non-Authenticating with Python Client..."
+ URL=amqps://$TEST_HOSTNAME:$PORT
+ if `$PY_PING_BROKER -b $URL`; then echo " Passed"; else { echo " Failed"; exit 1; }; fi
+else
+ echo "Skipping python part of ssl_test, no python dir."
+fi
+
+#### Client Authentication tests
+
+start_authenticating_broker
+PORT2=${PORTS[1]}
+echo "Running SSL client authentication test on port $PORT2"
+URL=amqp:ssl:$TEST_HOSTNAME:$PORT2
+
+## See if you can set the SSL cert-name for the connection
+./qpid-send -b $URL --connection-options "{ssl-cert-name: $TEST_CLIENT_CERT }" --content-string=hello -a "bar;{create:always}"
+MSG2=`./qpid-receive -b $URL --connection-options "{ssl-cert-name: $TEST_CLIENT_CERT }" -a "bar;{create:always}" --messages 1`
+test "$MSG2" = "hello" || { echo "receive failed '$MSG2' != 'hello'"; exit 1; }
+
+## Make sure that connect fails with an invalid SSL cert-name
+./qpid-send -b $URL --connection-options "{ssl-cert-name: pignose }" --content-string=hello -a "baz;{create:always}" 2>/dev/null 1>/dev/null
+MSG3=`./qpid-receive -b $URL --connection-options "{ssl-cert-name: pignose }" -a "baz;{create:always}" --messages 1 2>/dev/null`
+test "$MSG3" = "" || { echo "receive succeeded without valid ssl cert '$MSG3' != ''"; exit 1; }
+
+stop_brokers
+
+# Test ssl muxed with plain TCP on the same connection
+
+# Test a specified port number - since tcp/ssl are the same port don't need to specify --transport ssl
+PORT=`pick_port`
+start_ssl_mux_broker $PORT || error "Could not start broker"
+echo "Running SSL/TCP mux test on fixed port $PORT"
+
+## Test connection via connection settings
+./qpid-perftest --count ${COUNT} --port ${PORT} -P ssl -b $TEST_HOSTNAME --summary || error "SSL connection failed!"
+./qpid-perftest --count ${COUNT} --port ${PORT} -P tcp -b $TEST_HOSTNAME --summary || error "TCP connection failed!"
+
+# Test a broker chosen port - since ssl chooses port need to use --transport ssl here
+start_ssl_broker
+PORT=${PORTS[0]}
+echo "Running SSL/TCP mux test on random port $PORT"
+
+## Test connection via connection settings
+./qpid-perftest --count ${COUNT} --port ${PORT} -P ssl -b $TEST_HOSTNAME --summary || error "SSL connection failed!"
+./qpid-perftest --count ${COUNT} --port ${PORT} -P tcp -b $TEST_HOSTNAME --summary || error "TCP connection failed!"
+
+stop_brokers
+
+### Additional tests that require 'openssl' and 'pk12util' to be installed (optional)
+
+PK12UTIL=$(type -p pk12util)
+if [[ !(-x $PK12UTIL) ]] ; then
+ echo >&2 "'pk12util' command not available, skipping remaining tests"
+ exit 0
+fi
+
+OPENSSL=$(type -p openssl)
+if [[ !(-x $OPENSSL) ]] ; then
+ echo >&2 "'openssl' command not available, skipping remaining tests"
+ exit 0
+fi
+
+if test -d $PYTHON_DIR; then
+## verify python version > 2.5 (only 2.6+ does certificate checking)
+ PY_VERSION=$(python -c "import sys; print hex(sys.hexversion)")
+ if (( PY_VERSION < 0x02060000 )); then
+ echo >&2 "Detected python version < 2.6 - skipping certificate verification tests"
+ exit 0
+ fi
+
+ echo "Testing Certificate validation and Authentication with the Python Client..."
+
+# extract the CA's certificate as a PEM file
+ get_ca_certs() {
+ $PK12UTIL -o ${TEST_CERT_DIR}/CA_pk12.out -d ${CERT_DB} -n "Test-CA" -w ${CERT_PW_FILE} -k ${CERT_PW_FILE} > /dev/null
+ $OPENSSL pkcs12 -in ${TEST_CERT_DIR}/CA_pk12.out -out ${CA_PEM_FILE} -nokeys -passin file:${CERT_PW_FILE} >/dev/null
+ $PK12UTIL -o ${TEST_CERT_DIR}/other_CA_pk12.out -d ${OTHER_CA_CERT_DB} -n "Other-Test-CA" -w ${CERT_PW_FILE} -k ${CERT_PW_FILE} > /dev/null
+ $OPENSSL pkcs12 -in ${TEST_CERT_DIR}/other_CA_pk12.out -out ${OTHER_CA_PEM_FILE} -nokeys -passin file:${CERT_PW_FILE} >/dev/null
+ }
+
+ get_ca_certs || error "Could not extract CA certificates as PEM files"
+ start_ssl_broker
+ PORT=${PORTS[0]}
+ URL=amqps://$TEST_HOSTNAME:$PORT
+# verify the python client can authenticate the broker using the CA
+ if `${PY_PING_BROKER} -b $URL --ssl-trustfile=${CA_PEM_FILE}`; then echo " Passed"; else { echo " Failed"; exit 1; }; fi
+# verify the python client fails to authenticate the broker when using the other CA
+ if `${PY_PING_BROKER} -b $URL --ssl-trustfile=${OTHER_CA_PEM_FILE} > /dev/null 2>&1`; then { echo " Failed"; exit 1; }; else echo " Passed"; fi
+ stop_brokers
+
+# create a certificate without matching TEST_HOSTNAME, should fail to verify
+
+ create_certs "O=MyCo" "*.${TEST_HOSTNAME}.com" || error "Could not create server test certificate"
+ get_ca_certs || error "Could not extract CA certificates as PEM files"
+ start_ssl_broker
+ PORT=${PORTS[0]}
+ URL=amqps://$TEST_HOSTNAME:$PORT
+ if `${PY_PING_BROKER} -b $URL --ssl-trustfile=${CA_PEM_FILE} > /dev/null 2>&1`; then { echo " Failed"; exit 1; }; else echo " Passed"; fi
+# but disabling the check for the hostname should pass
+ if `${PY_PING_BROKER} -b $URL --ssl-trustfile=${CA_PEM_FILE} --ssl-skip-hostname-check`; then echo " Passed"; else { echo " Failed"; exit 1; }; fi
+ stop_brokers
+
+# test SubjectAltName parsing
+
+ if (( PY_VERSION >= 0x02070300 )); then
+ # python 2.7.3+ supports SubjectAltName extraction
+ # create a certificate with TEST_HOSTNAME only in SAN, should verify OK
+ create_certs "O=MyCo" "*.foo.com,${TEST_HOSTNAME},*xyz.com" || error "Could not create server test certificate"
+ get_ca_certs || error "Could not extract CA certificates as PEM files"
+ start_ssl_broker
+ PORT=${PORTS[0]}
+ URL=amqps://$TEST_HOSTNAME:$PORT
+ if `${PY_PING_BROKER} -b $URL --ssl-trustfile=${CA_PEM_FILE}`; then echo " Passed"; else { echo " Failed"; exit 1; }; fi
+ stop_brokers
+
+ create_certs "O=MyCo" "*${TEST_HOSTNAME}" || error "Could not create server test certificate"
+ get_ca_certs || error "Could not extract CA certificates as PEM files"
+ start_ssl_broker
+ PORT=${PORTS[0]}
+ URL=amqps://$TEST_HOSTNAME:$PORT
+ if `${PY_PING_BROKER} -b $URL --ssl-trustfile=${CA_PEM_FILE}`; then echo " Passed"; else { echo " Failed"; exit 1; }; fi
+ stop_brokers
+ fi
+
+fi
+
diff --git a/qpid/cpp/src/tests/store.py b/qpid/cpp/src/tests/store.py
new file mode 100755
index 0000000000..5c1934dded
--- /dev/null
+++ b/qpid/cpp/src/tests/store.py
@@ -0,0 +1,214 @@
+#!/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 errno, os, time
+from brokertest import *
+from qpid import compat, session
+from qpid.util import connect
+from qpid.connection import Connection
+from qpid.datatypes import Message, uuid4
+from qpid.queue import Empty
+
+class StoreTests(BrokerTest):
+
+ XA_RBROLLBACK = 1
+ XA_RBTIMEOUT = 2
+ XA_OK = 0
+ tx_counter = 0
+
+ def configure(self, config):
+ self.config = config
+ self.defines = self.config.defines
+ BrokerTest.configure(self, config)
+
+ def setup_connection(self):
+ socket = connect(self._broker.host(), self._broker.port())
+ return Connection(sock=socket)
+
+ def setup_session(self):
+ self.conn.start()
+ return self.conn.session(str(uuid4()))
+
+ def start_session(self):
+ self.conn = self.setup_connection()
+ self.ssn = self.setup_session()
+
+ def setUp(self):
+ BrokerTest.setUp(self)
+ self._broker = self.broker()
+ self.start_session()
+
+ def cycle_broker(self):
+ # tearDown resets working dir; change it back after.
+ d = os.getcwd()
+ BrokerTest.tearDown(self)
+ os.chdir(d)
+ self._broker = None
+ self._broker = self.broker()
+ self.conn = self.setup_connection()
+ self.ssn = self.setup_session()
+
+ def xid(self, txid):
+ StoreTests.tx_counter += 1
+ branchqual = "v%s" % StoreTests.tx_counter
+ return self.ssn.xid(format=0, global_id=txid, branch_id=branchqual)
+
+ def testDurableExchange(self):
+ try:
+ self.ssn.exchange_delete(exchange="DE1")
+ except:
+ # restart the session busted from the exception
+ self.start_session()
+
+ self.ssn.exchange_declare(exchange="DE1", type="direct", durable=True)
+ response = self.ssn.exchange_query(name="DE1")
+ self.assert_(response.durable)
+ self.assert_(not response.not_found)
+
+ # Cycle the broker and make sure the exchange recovers
+ self.cycle_broker()
+ response = self.ssn.exchange_query(name="DE1")
+ self.assert_(response.durable)
+ self.assert_(not response.not_found)
+
+ self.ssn.exchange_delete(exchange="DE1")
+
+ def testDurableQueues(self):
+ try:
+ self.ssn.queue_delete(queue="DQ1")
+ except:
+ self.start_session()
+
+ self.ssn.queue_declare(queue="DQ1", durable=True)
+ response = self.ssn.queue_query(queue="DQ1")
+ self.assertEqual("DQ1", response.queue)
+ self.assert_(response.durable)
+
+ # Cycle the broker and make sure the queue recovers
+ self.cycle_broker()
+ response = self.ssn.queue_query(queue="DQ1")
+ self.assertEqual("DQ1", response.queue)
+ self.assert_(response.durable)
+
+ self.ssn.queue_delete(queue="DQ1")
+
+ def testDurableBindings(self):
+ try:
+ self.ssn.exchange_unbind(queue="DB_Q1", exchange="DB_E1", binding_key="K1")
+ except:
+ self.start_session()
+ try:
+ self.ssn.exchange_delete(exchange="DB_E1")
+ except:
+ self.start_session()
+ try:
+ self.ssn.queue_delete(queue="DB_Q1")
+ except:
+ self.start_session()
+
+ self.ssn.queue_declare(queue="DB_Q1", durable=True)
+ self.ssn.exchange_declare(exchange="DB_E1", type="direct", durable=True)
+ self.ssn.exchange_bind(exchange="DB_E1", queue="DB_Q1", binding_key="K1")
+
+ # Queue up 2 messages, one with non-zero body, one with zero-length.
+ # 2 = delivery_mode.persistent
+ dp = self.ssn.delivery_properties(routing_key="DB_Q1", delivery_mode=2)
+ self.ssn.message_transfer(message=Message(dp, "normal message"))
+ self.ssn.message_transfer(message=Message(dp, ""))
+
+ # Cycle the broker and make sure the binding recovers
+ self.cycle_broker()
+ response = self.ssn.exchange_bound(exchange="DB_E1", queue="DB_Q1", binding_key="K1")
+ self.assert_(not response.exchange_not_found)
+ self.assert_(not response.queue_not_found)
+ self.assert_(not response.queue_not_matched)
+ self.assert_(not response.key_not_matched)
+
+ # Are the messages still there?
+ self.ssn.message_subscribe(destination="msgs", queue="DB_Q1", accept_mode=1, acquire_mode=0)
+ self.ssn.message_flow(unit = 1, value = 0xFFFFFFFFL, destination = "msgs")
+ self.ssn.message_flow(unit = 0, value = 10, destination = "msgs")
+ message_arrivals = self.ssn.incoming("msgs")
+ try:
+ message_arrivals.get(timeout=1)
+ message_arrivals.get(timeout=1)
+ except Empty:
+ assert False, 'Durable message(s) not recovered'
+
+ self.ssn.exchange_unbind(queue="DB_Q1", exchange="DB_E1", binding_key="K1")
+ self.ssn.exchange_delete(exchange="DB_E1")
+ self.ssn.queue_delete(queue="DB_Q1")
+
+ def testDtxRecoverPrepared(self):
+ try:
+ self.ssn.exchange_unbind(queue="Dtx_Q", exchange="Dtx_E", binding_key="Dtx")
+ except:
+ self.start_session()
+ try:
+ self.ssn.exchange_delete(exchange="Dtx_E")
+ except:
+ self.start_session()
+ try:
+ self.ssn.queue_delete(queue="Dtx_Q")
+ except:
+ self.start_session()
+
+ self.ssn.queue_declare(queue="Dtx_Q", auto_delete=False, durable=True)
+ self.ssn.exchange_declare(exchange="Dtx_E", type="direct", durable=True)
+ self.ssn.exchange_bind(exchange="Dtx_E", queue="Dtx_Q", binding_key="Dtx")
+ txid = self.xid("DtxRecoverPrepared")
+ self.ssn.dtx_select()
+ self.ssn.dtx_start(xid=txid)
+ # 2 = delivery_mode.persistent
+ dp = self.ssn.delivery_properties(routing_key="Dtx_Q", delivery_mode=2)
+ self.ssn.message_transfer(message=Message(dp, "transactional message"))
+ self.ssn.dtx_end(xid=txid)
+ self.assertEqual(self.XA_OK, self.ssn.dtx_prepare(xid=txid).status)
+ # Cycle the broker and make sure the xid is there, the message is not
+ # queued.
+ self.cycle_broker()
+ # The txid should be recovered and in doubt
+ xids = self.ssn.dtx_recover().in_doubt
+ xid_matched = False
+ for x in xids:
+ self.assertEqual(txid.format, x.format)
+ self.assertEqual(txid.global_id, x.global_id)
+ self.assertEqual(txid.branch_id, x.branch_id)
+ xid_matched = True
+ self.assert_(xid_matched)
+ self.ssn.message_subscribe(destination="dtx_msgs", queue="Dtx_Q", accept_mode=1, acquire_mode=0)
+ self.ssn.message_flow(unit = 1, value = 0xFFFFFFFFL, destination = "dtx_msgs")
+ self.ssn.message_flow(unit = 0, value = 10, destination = "dtx_msgs")
+ message_arrivals = self.ssn.incoming("dtx_msgs")
+ try:
+ message_arrivals.get(timeout=1)
+ assert False, 'Message present in queue before commit'
+ except Empty: pass
+ self.ssn.dtx_select()
+ self.assertEqual(self.XA_OK, self.ssn.dtx_commit(xid=txid, one_phase=False).status)
+ try:
+ msg = message_arrivals.get(timeout=1)
+ self.assertEqual("transactional message", msg.body)
+ except Empty:
+ assert False, 'Message should be present after dtx commit but is not'
+
+ self.ssn.exchange_unbind(queue="Dtx_Q", exchange="Dtx_E", binding_key="Dtx")
+ self.ssn.exchange_delete(exchange="Dtx_E")
+ self.ssn.queue_delete(queue="Dtx_Q")
diff --git a/qpid/cpp/src/tests/swig_python_tests b/qpid/cpp/src/tests/swig_python_tests
new file mode 100755
index 0000000000..40c35ac0fa
--- /dev/null
+++ b/qpid/cpp/src/tests/swig_python_tests
@@ -0,0 +1,66 @@
+#!/usr/bin/env 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 python tests.
+source $QPID_TEST_COMMON
+
+ensure_python_tests
+
+trap stop_broker INT TERM QUIT
+
+if [[ -a $AMQP_LIB ]] ; then
+ echo "Found AMQP support: $AMQP_LIB"
+ MODULES="--load-module $AMQP_LIB"
+fi
+
+fail() {
+ echo "FAIL swigged python tests: $1"; exit 1;
+}
+skip() {
+ echo "SKIPPED swigged python tests: $1"; exit 0;
+}
+
+start_broker() {
+ rm -f swig_python_tests.log
+ QPID_PORT=$($QPIDD_EXEC --daemon --port 0 --interface 127.0.0.1 --no-data-dir $MODULES --auth no --log-to-file swig_python_tests.log) || fail "Could not start broker"
+}
+
+stop_broker() {
+ $QPIDD_EXEC -q --port $QPID_PORT
+}
+
+test -f $PYTHONSWIGMODULE || skip "no swigged python client"
+test -d $QPID_TESTS || skip "test code not found"
+
+start_broker
+echo "Running swigged python tests using broker on port $QPID_PORT"
+
+export PYTHONPATH=$PYTHONPATH:$PYTHONPATH_SWIG
+export QPID_USE_SWIG_CLIENT=1
+$QPID_PYTHON_TEST -m qpid.tests.messaging.message -m qpid_tests.broker_0_10.priority -m qpid_tests.broker_0_10.lvq -m qpid_tests.broker_0_10.new_api -b localhost:$QPID_PORT -I $srcdir/failing-amqp0-10-python-tests $* || FAILED=1
+if [[ -a $AMQP_LIB ]] ; then
+ $QPID_PYTHON_TEST --define="protocol_version=amqp1.0" -m qpid_tests.broker_1_0 -m qpid_tests.broker_0_10.new_api -m assertions -m reject_release -m misc -m policies -b localhost:$QPID_PORT -I $srcdir/failing-amqp1.0-python-tests $* || FAILED=1
+fi
+stop_broker
+if [[ $FAILED -eq 1 ]]; then
+ fail ""
+fi
+
diff --git a/qpid/cpp/src/tests/test.xquery b/qpid/cpp/src/tests/test.xquery
new file mode 100644
index 0000000000..4cfe3af02d
--- /dev/null
+++ b/qpid/cpp/src/tests/test.xquery
@@ -0,0 +1,6 @@
+ let $w := ./weather
+ return $w/station = 'Raleigh-Durham International Airport (KRDU)'
+ and $w/temperature_f > 50
+ and $w/temperature_f - $w/dewpoint > 5
+ and $w/wind_speed_mph > 7
+ and $w/wind_speed_mph < 20
diff --git a/qpid/cpp/src/tests/test_env.ps1.in b/qpid/cpp/src/tests/test_env.ps1.in
new file mode 100644
index 0000000000..94834a4b5e
--- /dev/null
+++ b/qpid/cpp/src/tests/test_env.ps1.in
@@ -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.
+#
+
+# Environment variables substituted by configure/cmake.
+$abs_srcdir="@abs_srcdir@"
+$abs_builddir="@abs_builddir@"
+$top_srcdir="@abs_top_srcdir@"
+$top_builddir="@abs_top_builddir@"
+$moduledir="$top_builddir\src@builddir_lib_suffix@"
+$testmoduledir="$builddir@builddir_lib_suffix@"
+$BOOST_LIBRARYDIR="@BOOST_LIBRARYDIR@"
+
+# Python paths and directories
+$PYTHON_EXE="@PYTHON_EXECUTABLE@"
+$PYTHON_DIR="$builddir\python"
+$QPID_PYTHON_TEST="$PYTHON_DIR\commands\qpid-python-test"
+if ( !(Test-Path "$PYTHON_DIR") -and (Test-Path "$top_srcdir\..\python")) {
+ $PYTHON_DIR="$top_srcdir\..\python"
+ $QPID_PYTHON_TEST="$PYTHON_DIR\qpid-python-test"
+}
+$QPID_TESTS="$top_srcdir\..\tests"
+$QPID_TESTS_PY="$QPID_TESTS\src\py"
+$QPID_TOOLS="$top_srcdir\..\tools"
+$QPID_TOOLS_LIBS="$QPID_TOOLS\src\py"
+$QMF_LIB="$top_srcdir\..\extras\qmf\src\py"
+$PYTHON_COMMANDS="$QPID_TOOLS\src\py"
+$env:PYTHONPATH="$srcdir;$PYTHON_DIR;$PYTHON_COMMANDS;$QPID_TESTS_PY;$QPID_TOOLS_LIBS;$QMF_LIB;$env:PYTHONPATH"
+$QPID_CONFIG_EXEC="$PYTHON_COMMANDS\qpid-config"
+$QPID_ROUTE_EXEC="$PYTHON_COMMANDS\qpid-route"
+$QPID_HA_TOOL_EXEC="$PYTHON_COMMANDS\qpid-ha-tool"
+
+# Executables
+$env:QPIDD_EXEC="$top_builddir\src\@CMAKE_BUILD_TYPE@\qpidd.exe"
+$env:QPID_WATCHDOG_EXEC="$top_builddir\src\qpidd_watchdog"
+
+# Test executables
+$QPID_TEST_EXEC_DIR="$builddir\@CMAKE_BUILD_TYPE@"
+$RECEIVER_EXEC="$QPID_TEST_EXEC_DIR\receiver"
+$SENDER_EXEC="$QPID_TEST_EXEC_DIR\sender"
+
+# Path
+$env:PATH="$top_builddir\src\@CMAKE_BUILD_TYPE@;$builddir\@CMAKE_BUILD_TYPE@;$srcdir;$PYTHON_COMMANDS;$QPID_TEST_EXEC_DIR;@BOOST_LIBRARYDIR@;$env:PATH"
+
+# Modules
+$env:TEST_STORE_LIB="$testmoduledir\test_store.so"
+
+#exportmodule() { test -f $moduledir/$2 && eval "export $1=$moduledir/$2"; }
+#exportmodule ACL_LIB acl.so
+#exportmodule CLUSTER_LIB cluster.so
+#exportmodule SSLCONNECTOR_LIB sslconnector.so
+#exportmodule SSL_LIB ssl.so
+#exportmodule WATCHDOG_LIB watchdog.so
+#exportmodule XML_LIB xml.so
+
+# Qpid options
+$env:QPID_NO_MODULE_DIR="1" # Don't accidentally load installed modules
+$env:QPID_DATA_DIR= # Default to no data dir, not ~/.qpidd
+
+# Options for boost test framework
+$env:BOOST_TEST_SHOW_PROGRESS="yes"
+$env:BOOST_TEST_CATCH_SYSTEM_ERRORS="no"
diff --git a/qpid/cpp/src/tests/test_env.sh.in b/qpid/cpp/src/tests/test_env.sh.in
new file mode 100644
index 0000000000..1c4c117e4b
--- /dev/null
+++ b/qpid/cpp/src/tests/test_env.sh.in
@@ -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.
+#
+
+absdir() { echo `cd $1 && pwd`; }
+
+# Environment variables substituted by cmake.
+export srcdir=`absdir @abs_srcdir@`
+export builddir=`absdir @abs_builddir@`
+export top_srcdir=`absdir @abs_top_srcdir@`
+export top_builddir=`absdir @abs_top_builddir@`
+export moduledir=$top_builddir/src@builddir_lib_suffix@
+export pythonswigdir=$top_builddir/bindings/qpid/python/
+export pythonswiglibdir=$top_builddir/bindings/qpid/python@builddir_lib_suffix@
+export testmoduledir=$builddir@builddir_lib_suffix@
+export QPID_INSTALL_PREFIX=@prefix@
+
+# Tools substituted by cmake
+enable_valgrind=${enable_valgrind-@ENABLE_VALGRIND@}
+if [ "$enable_valgrind" = "ON" ] ; then
+ export VALGRIND=@VALGRIND_EXECUTABLE@
+fi
+export SASL_PW=@SASLPASSWD2_EXECUTABLE@
+export PYTHON=@PYTHON_EXECUTABLE@
+
+# Python paths and directories
+export PYTHON_DIR=$builddir/python
+export QPID_PYTHON_TEST=$PYTHON_DIR/commands/qpid-python-test
+if [ ! -d $PYTHON_DIR -a -d $top_srcdir/../python ]; then
+ export PYTHON_DIR=$top_srcdir/../python
+ export QPID_PYTHON_TEST=$PYTHON_DIR/qpid-python-test
+fi
+export QPID_TESTS=$top_srcdir/../tests
+export QPID_TESTS_PY=$QPID_TESTS/src/py
+export QPID_TOOLS=$top_srcdir/../tools
+export QMF_LIB=$top_srcdir/../extras/qmf/src/py
+export PYTHON_COMMANDS=$QPID_TOOLS/src/py
+export PYTHONPATH_SWIG=$pythonswigdir:$pythonswiglibdir
+export PYTHONPATH=$srcdir:$PYTHON_DIR:$PYTHON_COMMANDS:$QPID_TESTS_PY:$QMF_LIB:$PYTHONPATH_SWIG:$PYTHONPATH
+export QPID_CONFIG_EXEC=$PYTHON_COMMANDS/qpid-config
+export QPID_ROUTE_EXEC=$PYTHON_COMMANDS/qpid-route
+export QPID_HA_EXEC=$PYTHON_COMMANDS/qpid-ha
+export PYTHONSWIGMODULE=$pythonswigdir/qpid_messaging.py
+# Executables
+export QPIDD_EXEC=$top_builddir/src/qpidd
+
+# Test executables
+export QPID_TEST_EXEC_DIR=$builddir
+export QPID_TEST_SRC_DIR=$srcdir
+export RECEIVER_EXEC=$QPID_TEST_EXEC_DIR/receiver
+export SENDER_EXEC=$QPID_TEST_EXEC_DIR/sender
+
+# Path
+export PATH=$top_builddir/src:$builddir:$srcdir:$PYTHON_COMMANDS:$QPID_TEST_EXEC_DIR:$PYTHON_DIR/commands:$PATH
+
+# Modules
+export TEST_STORE_LIB=$testmoduledir/test_store.so
+
+exportmodule() { test -f $moduledir/$2 && eval "export $1=$moduledir/$2"; }
+exportmodule HA_LIB ha.so
+exportmodule XML_LIB xml.so
+test "$STORE_LIB" || exportmodule STORE_LIB linearstore.so
+test "$STORE_LIB" || exportmodule STORE_LIB legacystore.so
+exportmodule AMQP_LIB amqp.so
+
+# Qpid options
+export QPID_NO_MODULE_DIR=1 # Don't accidentally load installed modules
+export QPID_DATA_DIR=
+export QPID_CONFIG=$srcdir/qpidd-empty.conf
+
+# Use temporary directory if $HOME does not exist
+if [ ! -e "$HOME" ]; then
+ export QPID_DATA_DIR=/tmp/qpid
+ export QPID_PID_DIR=/tmp/qpid
+fi
+
+# Options for boost test framework
+test -z "$BOOST_TEST_SHOW_PROGRESS" && export BOOST_TEST_SHOW_PROGRESS=yes
+test -z "$BOOST_TEST_CATCH_SYSTEM_ERRORS" && export BOOST_TEST_CATCH_SYSTEM_ERRORS=no
+
+# Source this for useful common testing functions
+export QPID_TEST_COMMON=$srcdir/test_env_common.sh
+
+# Proton configuration
+export QPID_PROTON_VERSION=@Proton_VERSION@
diff --git a/qpid/cpp/src/tests/test_env_common.sh b/qpid/cpp/src/tests/test_env_common.sh
new file mode 100644
index 0000000000..348664ca76
--- /dev/null
+++ b/qpid/cpp/src/tests/test_env_common.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env 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.
+#
+
+# Ensure that we have python testing tools available
+function ensure_python_tests {
+ if [ ! -d ${PYTHON_DIR} ] ; then
+ echo "Python test code not found: skipping python based test"
+ exit 0;
+ fi
+}
+
diff --git a/qpid/cpp/src/tests/test_store.cpp b/qpid/cpp/src/tests/test_store.cpp
new file mode 100644
index 0000000000..14aee7b648
--- /dev/null
+++ b/qpid/cpp/src/tests/test_store.cpp
@@ -0,0 +1,339 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**@file
+ *
+ * Message store for tests, with two roles:
+ *
+ * 1. Dump store events to a text file that can be compared to expected event
+ * sequence
+ *
+ * 2. Emulate hard-to-recreate conditions such as asynchronous completion delays
+ * or store errors.
+ *
+ * Messages with specially formatted contents trigger various actions.
+ * See class Action below for available actions and message format..
+ *
+ */
+
+#include "qpid/broker/NullMessageStore.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/amqp_0_10/MessageTransfer.h"
+#include "qpid/framing/AMQFrame.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/RefCounted.h"
+#include "qpid/Msg.h"
+#include <boost/cast.hpp>
+#include <boost/lexical_cast.hpp>
+#include <memory>
+#include <ostream>
+#include <fstream>
+#include <sstream>
+
+using namespace std;
+using namespace boost;
+using namespace qpid;
+using namespace qpid::broker;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+namespace {
+
+bool startswith(const string& s, const string& prefix) {
+ return s.compare(0, prefix.size(), prefix) == 0;
+}
+
+void split(const string& s, vector<string>& result, const char* sep=" \t\n") {
+ size_t i = s.find_first_not_of(sep);
+ while (i != string::npos) {
+ size_t j = s.find_first_of(sep, i);
+ if (j == string::npos) {
+ result.push_back(s.substr(i));
+ break;
+ }
+ result.push_back(s.substr(i, j-i));
+ i = s.find_first_not_of(sep, j);
+ }
+}
+
+}
+
+/**
+ * Action message format is TEST_STORE_DO [<name>...]:<action> [<args>...]
+ *
+ * A list of store <name> can be included so the action only executes on one of
+ * the named stores. This is useful in a cluster setting where the same message
+ * is replicated to all broker's stores but should only trigger an action on
+ * specific ones. If no <name> is given, execute on any store.
+ *
+ */
+class Action {
+ public:
+ /** Available actions */
+ enum ActionEnum {
+ NONE,
+ THROW, ///< Throw an exception from enqueue
+ DELAY, ///< Delay completion, takes an ID string to complete.
+ COMPLETE, ///< Complete a previously delayed message, takes ID
+
+ N_ACTIONS // Count of actions, must be last
+ };
+
+ string name;
+ ActionEnum index;
+ vector<string> storeNames, args;
+
+ Action(const string& s) {
+ index = NONE;
+ if (!startswith(s, PREFIX)) return;
+ size_t colon = s.find_first_of(":");
+ if (colon == string::npos) return;
+ assert(colon >= PREFIX.size());
+ split(s.substr(PREFIX.size(), colon-PREFIX.size()), storeNames);
+ split(s.substr(colon+1), args);
+ if (args.empty()) return;
+ for (size_t i = 0; i < N_ACTIONS; ++i) {
+ if (args[0] == ACTION_NAMES[i]) {
+ name = args[0];
+ index = ActionEnum(i);
+ args.erase(args.begin());
+ break;
+ }
+ }
+ }
+
+ bool executeIn(const string& storeName) {
+ return storeNames.empty() ||
+ find(storeNames.begin(), storeNames.end(), storeName) !=storeNames.end();
+ }
+
+ private:
+ static string PREFIX;
+ static const char* ACTION_NAMES[N_ACTIONS];
+};
+
+string Action::PREFIX("TEST_STORE_DO");
+
+const char* Action::ACTION_NAMES[] = { "none", "throw", "delay", "complete" };
+
+
+struct TestStoreOptions : public Options {
+
+ string name;
+ string dump;
+ string events;
+
+ TestStoreOptions() : Options("Test Store Options") {
+ addOptions()
+ ("test-store-name", optValue(name, "NAME"),
+ "Name of test store instance.")
+ ("test-store-dump", optValue(dump, "FILE"),
+ "File to dump enqueued messages.")
+ ("test-store-events", optValue(events, "FILE"),
+ "File to log events, 1 line per event.")
+ ;
+ }
+};
+
+
+class TestStore : public NullMessageStore {
+ public:
+ TestStore(const TestStoreOptions& opts, Broker& broker_)
+ : options(opts), name(opts.name), broker(broker_)
+ {
+ QPID_LOG(info, "TestStore name=" << name
+ << " dump=" << options.dump
+ << " events=" << options.events)
+
+ if (!options.dump.empty())
+ dump.reset(new ofstream(options.dump.c_str()));
+ if (!options.events.empty())
+ events.reset(new ofstream(options.events.c_str()));
+ }
+
+ ~TestStore() {
+ for_each(threads.begin(), threads.end(), boost::bind(&Thread::join, _1));
+ }
+
+ // Dummy transaction context.
+ struct TxContext : public TPCTransactionContext {
+ static int nextId;
+ string id;
+ TxContext() : id(lexical_cast<string>(nextId++)) {}
+ TxContext(string xid) : id(xid) {}
+ };
+
+ static string getId(const TransactionContext& tx) {
+ const TxContext* tc = dynamic_cast<const TxContext*>(&tx);
+ assert(tc);
+ return tc->id;
+ }
+
+
+ bool isNull() const { return false; }
+
+ void log(const string& msg) {
+ QPID_LOG(info, "test_store: " << msg);
+ if (events.get()) *events << msg << endl << std::flush;
+ }
+
+ auto_ptr<TransactionContext> begin() {
+ auto_ptr<TxContext> tx(new TxContext());
+ log(Msg() << "<begin tx " << tx->id << ">");
+ return auto_ptr<TransactionContext>(tx);
+ }
+
+ auto_ptr<TPCTransactionContext> begin(const std::string& xid) {
+ auto_ptr<TxContext> tx(new TxContext(xid));
+ log(Msg() << "<begin tx " << tx->id << ">");
+ return auto_ptr<TPCTransactionContext>(tx);
+ }
+
+ string getContent(const intrusive_ptr<PersistableMessage>& msg) {
+ intrusive_ptr<broker::Message::Encoding> enc(
+ dynamic_pointer_cast<broker::Message::Encoding>(msg));
+ return enc->getContent();
+ }
+
+ void enqueue(TransactionContext* tx,
+ const boost::intrusive_ptr<PersistableMessage>& pmsg,
+ const PersistableQueue& queue)
+ {
+ ostringstream o;
+ string data = getContent(pmsg);
+ o << "<enqueue " << queue.getName() << " " << data;
+ if (tx) o << " tx=" << getId(*tx);
+ o << ">";
+ log(o.str());
+
+ // Dump the message if there is a dump file.
+ if (dump.get()) {
+ *dump << "Message(" << data.size() << "): " << data << endl;
+ }
+ string logPrefix = "TestStore "+name+": ";
+ Action action(data);
+ bool doComplete = true;
+ if (action.index && action.executeIn(name)) {
+ switch (action.index) {
+
+ case Action::THROW:
+ throw Exception(logPrefix + data);
+ break;
+
+ case Action::DELAY: {
+ if (action.args.empty()) {
+ QPID_LOG(error, logPrefix << "async-id needs argument: " << data);
+ break;
+ }
+ asyncIds[action.args[0]] = pmsg;
+ QPID_LOG(debug, logPrefix << "delayed completion " << action.args[0]);
+ doComplete = false;
+ break;
+ }
+
+ case Action::COMPLETE: {
+ if (action.args.empty()) {
+ QPID_LOG(error, logPrefix << "complete-id needs argument: " << data);
+ break;
+ }
+ AsyncIds::iterator i = asyncIds.find(action.args[0]);
+ if (i != asyncIds.end()) {
+ i->second->enqueueComplete();
+ QPID_LOG(debug, logPrefix << "completed " << action.args[0]);
+ asyncIds.erase(i);
+ } else {
+ QPID_LOG(info, logPrefix << "not found for completion " << action.args[0]);
+ }
+ break;
+ }
+
+ default:
+ QPID_LOG(error, logPrefix << "unknown action: " << data);
+ }
+ }
+ if (doComplete) pmsg->enqueueComplete();
+ }
+
+ void dequeue(TransactionContext* tx,
+ const boost::intrusive_ptr<PersistableMessage>& msg,
+ const PersistableQueue& queue)
+ {
+ QPID_LOG(debug, "TestStore dequeue " << queue.getName());
+ ostringstream o;
+ o<< "<dequeue " << queue.getName() << " " << getContent(msg);
+ if (tx) o << " tx=" << getId(*tx);
+ o << ">";
+ log(o.str());
+ }
+
+ void prepare(TPCTransactionContext& txn) {
+ log(Msg() << "<prepare tx=" << getId(txn) << ">");
+ }
+
+ void commit(TransactionContext& txn) {
+ log(Msg() << "<commit tx=" << getId(txn) << ">");
+ }
+
+ void abort(TransactionContext& txn) {
+ log(Msg() << "<abort tx=" << getId(txn) << ">");
+ }
+
+
+ private:
+ typedef map<string, boost::intrusive_ptr<PersistableMessage> > AsyncIds;
+
+ TestStoreOptions options;
+ string name;
+ Broker& broker;
+ vector<Thread> threads;
+ std::auto_ptr<ofstream> dump;
+ std::auto_ptr<ofstream> events;
+ AsyncIds asyncIds;
+};
+
+int TestStore::TxContext::nextId(1);
+
+struct TestStorePlugin : public Plugin {
+
+ TestStoreOptions options;
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize (Plugin::Target& target)
+ {
+ Broker* broker = dynamic_cast<Broker*>(&target);
+ if (!broker) return;
+ boost::shared_ptr<MessageStore> p(new TestStore(options, *broker));
+ broker->setStore (p);
+ }
+
+ void initialize(qpid::Plugin::Target&) {}
+};
+
+static TestStorePlugin pluginInstance;
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/test_tools.h b/qpid/cpp/src/tests/test_tools.h
new file mode 100644
index 0000000000..d006246299
--- /dev/null
+++ b/qpid/cpp/src/tests/test_tools.h
@@ -0,0 +1,106 @@
+#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 <vector>
+#include <set>
+#include <ostream>
+#include <sstream>
+#include <exception>
+#include <stdexcept>
+
+// 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 sets with
+// boost::assign::list_of with BOOST_CHECK_EQUALS
+namespace std { // In namespace std so boost can find them.
+
+template <class T>
+ostream& operator<<(ostream& o, const vector<T>& v) { return seqPrint(o, v); }
+
+template <class T>
+ostream& operator<<(ostream& o, const set<T>& v) { return seqPrint(o, v); }
+
+template <class T>
+ostream& operator<<(ostream& o, const boost::assign_detail::generic_list<T>& l) { return seqPrint(o, l); }
+
+template <class T>
+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); }
+
+template <class T>
+bool operator == (const set<T>& a, const boost::assign_detail::generic_list<T>& b) { return seqEqual(a, b); }
+
+template <class T>
+bool operator == (const boost::assign_detail::generic_list<T>& b, const set<T>& a) { return seqEqual(a, b); }
+}
+
+namespace qpid {
+namespace tests {
+
+/** Check if types of two objects (as given by typeinfo::name()) match. */
+#define BOOST_CHECK_TYPEID_EQUAL(a,b) BOOST_CHECK_EQUAL(typeid(a).name(),typeid(b).name())
+
+/**
+ * Supress all logging in a scope, restore to previous configuration in destructor.
+ */
+struct ScopedSuppressLogging {
+ typedef qpid::log::Logger Logger;
+ ScopedSuppressLogging(Logger& l=Logger::instance()) : logger(l), opts(l.getOptions()) { l.clear(); }
+ ~ScopedSuppressLogging() { logger.configure(opts); }
+ Logger& logger;
+ qpid::log::Options opts;
+};
+
+inline std::string getLibPath(const char* envName, const char* defaultPath = 0) {
+ const char* p = std::getenv(envName);
+ if (p != 0)
+ return p;
+ if (defaultPath == 0) {
+ std::ostringstream msg;
+ msg << "Environment variable " << envName << " not set.";
+ throw std::runtime_error(msg.str());
+ }
+ return defaultPath;
+}
+
+}} // namespace qpid::tests
+
+#endif /*!TEST_TOOLS_H*/
+
diff --git a/qpid/cpp/src/tests/topic_perftest b/qpid/cpp/src/tests/topic_perftest
new file mode 100755
index 0000000000..04e1cdcffb
--- /dev/null
+++ b/qpid/cpp/src/tests/topic_perftest
@@ -0,0 +1,22 @@
+#!/usr/bin/env 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.
+#
+
+exec `dirname $0`/run_perftest 10000 --mode topic --qt 16
diff --git a/qpid/cpp/src/tests/topictest b/qpid/cpp/src/tests/topictest
new file mode 100755
index 0000000000..f4c6e7187d
--- /dev/null
+++ b/qpid/cpp/src/tests/topictest
@@ -0,0 +1,61 @@
+#!/usr/bin/env 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"
+ ./qpid-topic-listener $TRANSACTIONAL > $LOG 2>&1 && rm -f $LOG
+}
+
+publish() {
+ ./qpid-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/qpid/cpp/src/tests/topictest.ps1 b/qpid/cpp/src/tests/topictest.ps1
new file mode 100644
index 0000000000..f15b2d452c
--- /dev/null
+++ b/qpid/cpp/src/tests/topictest.ps1
@@ -0,0 +1,73 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Parameters with default values: s (subscribers) m (messages) b (batches)
+# h (host) t (false; use transactions)
+param (
+ [int]$subscribers = 10,
+ [int]$message_count = 2000,
+ [int]$batches = 10,
+ [string]$broker,
+ [switch] $t # transactional
+)
+
+# Run the C++ topic test
+[string]$me = $myInvocation.InvocationName
+$srcdir = Split-Path $me
+#$srcdir = Split-Path $myInvocation.InvocationName
+
+# Clean up old log files
+Get-Item subscriber_*.log | Remove-Item
+
+if ($t) {
+ $transactional = "--transactional --durable"
+}
+
+# Find which subdir the exes are in
+. $srcdir\find_prog.ps1 .\qpid-topic-listener.exe
+
+function subscribe {
+ param ([int]$num, [string]$sub)
+ "Start subscriber $num"
+ $LOG = "subscriber_$num.log"
+ $cmdline = ".\$sub\qpid-topic-listener $transactional > $LOG 2>&1
+ if (`$LastExitCode -ne 0) { Remove-Item $LOG }"
+ $cmdblock = $executioncontext.invokecommand.NewScriptBlock($cmdline)
+ . $srcdir\background.ps1 $cmdblock
+}
+
+function publish {
+ param ([string]$sub)
+ Invoke-Expression ".\$sub\qpid-topic-publisher --messages $message_count --batches $batches --subscribers $subscribers $host $transactional" 2>&1
+}
+
+if ($broker.length) {
+ $broker = "-h$broker"
+}
+
+$i = $subscribers
+while ($i -gt 0) {
+ subscribe $i $sub
+ $i--
+}
+
+# FIXME aconway 2007-03-27: Hack around startup race. Fix topic test.
+Start-Sleep 2
+publish $sub
+exit $LastExitCode
diff --git a/qpid/cpp/src/tests/txjob.cpp b/qpid/cpp/src/tests/txjob.cpp
new file mode 100644
index 0000000000..29394c3415
--- /dev/null
+++ b/qpid/cpp/src/tests/txjob.cpp
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include "TestOptions.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid::client;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ std::string workQueue;
+ std::string source;
+ std::string dest;
+ uint messages;
+ uint jobs;
+ bool quit;
+ bool declareQueues;
+
+ Args() : workQueue("txshift-control"), source("txshift-1"), dest("txshift-2"), messages(0), jobs(0),
+ quit(false), declareQueues(false)
+ {
+ addOptions()
+ ("messages", qpid::optValue(messages, "N"), "Number of messages to shift")
+ ("jobs", qpid::optValue(jobs, "N"), "Number of shift jobs to request")
+ ("source", qpid::optValue(source, "QUEUE NAME"), "source queue from which messages will be shifted")
+ ("dest", qpid::optValue(dest, "QUEUE NAME"), "dest queue to which messages will be shifted")
+ ("work-queue", qpid::optValue(workQueue, "QUEUE NAME"), "work queue from which to take instructions")
+ ("add-quit", qpid::optValue(quit), "add a 'quit' instruction to the queue (after any other jobs)")
+ ("declare-queues", qpid::optValue(declareQueues), "issue a declare for all queues");
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+//TODO: might be nice to make this capable of failover as well at some
+//point; for now its just for the setup phase.
+int main(int argc, char** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ Connection connection;
+ connection.open(opts.con);
+ Session session = connection.newSession();
+ if (opts.declareQueues) {
+ session.queueDeclare(arg::queue=opts.workQueue);
+ session.queueDeclare(arg::queue=opts.source);
+ session.queueDeclare(arg::queue=opts.dest);
+ }
+ for (uint i = 0; i < opts.jobs; ++i) {
+ Message job("transfer", opts.workQueue);
+ job.getHeaders().setString("src", opts.source);
+ job.getHeaders().setString("dest", opts.dest);
+ job.getHeaders().setInt("count", opts.messages);
+ async(session).messageTransfer(arg::content=job);
+ }
+
+ if (opts.quit) {
+ async(session).messageTransfer(arg::content=Message("quit", opts.workQueue));
+ }
+
+ session.sync();
+ session.close();
+
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/txshift.cpp b/qpid/cpp/src/tests/txshift.cpp
new file mode 100644
index 0000000000..6ec28c7233
--- /dev/null
+++ b/qpid/cpp/src/tests/txshift.cpp
@@ -0,0 +1,193 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include "TestOptions.h"
+#include "qpid/client/AsyncSession.h"
+#include "qpid/client/FailoverManager.h"
+#include "qpid/client/Message.h"
+#include "qpid/client/SubscriptionManager.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Thread.h"
+
+using namespace qpid::client;
+using namespace qpid::sys;
+
+namespace qpid {
+namespace tests {
+
+struct Args : public qpid::TestOptions
+{
+ std::string workQueue;
+ uint workers;
+
+ Args() : workQueue("txshift-control"), workers(1)
+ {
+ addOptions()
+ ("workers", qpid::optValue(workers, "N"), "Number of separate worker sessions to start")
+ ("work-queue", qpid::optValue(workQueue, "NAME"), "work queue from which to take instructions");
+ }
+};
+
+struct Transfer : MessageListener
+{
+ std::string control;
+ std::string source;
+ std::string destination;
+ uint expected;
+ uint transfered;
+ SubscriptionSettings controlSettings;
+ Subscription controlSubscription;
+ SubscriptionSettings sourceSettings;
+ Subscription sourceSubscription;
+
+ Transfer(const std::string control_) : control(control_), expected(0), transfered(0) {}
+
+ void subscribeToSource(SubscriptionManager manager)
+ {
+ sourceSettings.autoAck = 0;//will accept once at the end of the batch
+ sourceSettings.flowControl = FlowControl::messageCredit(expected);
+ sourceSubscription = manager.subscribe(*this, source, sourceSettings);
+ QPID_LOG(info, "Subscribed to source: " << source << " expecting: " << expected);
+ }
+
+ void subscribeToControl(SubscriptionManager manager)
+ {
+ controlSettings.flowControl = FlowControl::messageCredit(1);
+ controlSubscription = manager.subscribe(*this, control, controlSettings);
+ QPID_LOG(info, "Subscribed to job queue");
+ }
+
+ void received(Message& message)
+ {
+ QPID_LOG(debug, "received: " << message.getData() << " for " << message.getDestination());
+ if (message.getDestination() == source) {
+ receivedFromSource(message);
+ } else if (message.getDestination() == control) {
+ receivedFromControl(message);
+ } else {
+ QPID_LOG(error, "Unexpected message: " << message.getData() << " to " << message.getDestination());
+ }
+ }
+
+ void receivedFromSource(Message& message)
+ {
+ QPID_LOG(debug, "transfering " << (transfered+1) << " of " << expected);
+ message.getDeliveryProperties().setRoutingKey(destination);
+ async(sourceSubscription.getSession()).messageTransfer(arg::content=message);
+ if (++transfered == expected) {
+ QPID_LOG(info, "completed job: " << transfered << " messages shifted from " <<
+ source << " to " << destination);
+ sourceSubscription.accept(sourceSubscription.getUnaccepted());
+ sourceSubscription.getSession().txCommit();
+ sourceSubscription.cancel();
+ //grant credit to allow broker to send us another control message
+ controlSubscription.grantMessageCredit(1);
+ }
+ }
+
+ void receivedFromControl(Message& message)
+ {
+ if (message.getData() == "transfer") {
+ source = message.getHeaders().getAsString("src");
+ destination = message.getHeaders().getAsString("dest");
+ expected = message.getHeaders().getAsInt("count");
+ transfered = 0;
+ QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " <<
+ source << " to " << destination);
+ subscribeToSource(controlSubscription.getSubscriptionManager());
+ } else if (message.getData() == "quit") {
+ QPID_LOG(info, "received quit request");
+ controlSubscription.cancel();
+ } else {
+ std::cerr << "Rejecting invalid message: " << message.getData() << std::endl;
+ controlSubscription.getSession().messageReject(SequenceSet(message.getId()));
+ }
+ }
+
+};
+
+struct Worker : FailoverManager::Command, Runnable
+{
+ FailoverManager& connection;
+ Transfer transfer;
+ Thread runner;
+
+ Worker(FailoverManager& c, const std::string& controlQueue) : connection(c), transfer(controlQueue) {}
+
+ void run()
+ {
+ connection.execute(*this);
+ }
+
+ void start()
+ {
+ runner = Thread(this);
+ }
+
+ void join()
+ {
+ runner.join();
+ }
+
+ void execute(AsyncSession& session, bool isRetry)
+ {
+ if (isRetry) QPID_LOG(info, "Retrying...");
+ session.txSelect();
+ SubscriptionManager subs(session);
+ transfer.subscribeToControl(subs);
+ subs.run();
+ session.txCommit();//commit accept of control messages
+ }
+};
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char** argv)
+{
+ Args opts;
+ try {
+ opts.parse(argc, argv);
+ FailoverManager connection(opts.con);
+ connection.connect();
+ if (opts.workers == 1) {
+ Worker worker(connection, opts.workQueue);
+ worker.run();
+ } else {
+ boost::ptr_vector<Worker> workers;
+ for (uint i = 0; i < opts.workers; i++) {
+ workers.push_back(new Worker(connection, opts.workQueue));
+ }
+ std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::start, _1));
+ std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::join, _1));
+ }
+
+ return 0;
+ } catch(const std::exception& e) {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+}
diff --git a/qpid/cpp/src/tests/unit_test.cpp b/qpid/cpp/src/tests/unit_test.cpp
new file mode 100644
index 0000000000..00c61242e4
--- /dev/null
+++ b/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/qpid/cpp/src/tests/unit_test.h b/qpid/cpp/src/tests/unit_test.h
new file mode 100644
index 0000000000..a11df2ff04
--- /dev/null
+++ b/qpid/cpp/src/tests/unit_test.h
@@ -0,0 +1,74 @@
+#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
+
+//
+// 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_FIXTURE_TEST_CASE
+# define QPID_FIXTURE_TEST_CASE(name, f) BOOST_FIXTURE_TEST_CASE(name, f)
+#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/qpid/cpp/src/tests/vg_check b/qpid/cpp/src/tests/vg_check
new file mode 100644
index 0000000000..462f4cb5e4
--- /dev/null
+++ b/qpid/cpp/src/tests/vg_check
@@ -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.
+#
+
+# 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
+}
diff --git a/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
new file mode 100644
index 0000000000..14f1e46606
--- /dev/null
+++ b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
@@ -0,0 +1,78 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file intends to prevent Windows from throwing up error boxes and
+// offering to debug when serious errors happen. The errors are displayed
+// on stderr instead. The purpose of this is to allow the tests to proceed
+// scripted and catch the text for logging. If this behavior is desired,
+// include this file with the executable being built. If the default
+// behaviors are desired, don't include this file in the build.
+
+#if defined(_MSC_VER)
+#include <crtdbg.h>
+#endif
+#include <windows.h>
+#include <iostream>
+
+namespace qpid {
+namespace tests {
+namespace windows {
+
+// Instead of popping up a window for exceptions, just print something out
+LONG _stdcall UnhandledExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo)
+{
+ DWORD dwExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
+
+ if (dwExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ std::cerr << "\nERROR: ACCESS VIOLATION\n" << std::endl;
+ else
+ std::cerr << "\nERROR: UNHANDLED EXCEPTION\n" << std::endl;
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+struct redirect_errors_to_stderr {
+ redirect_errors_to_stderr ();
+};
+
+static redirect_errors_to_stderr block;
+
+redirect_errors_to_stderr::redirect_errors_to_stderr()
+{
+#if defined(_MSC_VER)
+ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+#endif
+
+ // Prevent the system from displaying the critical-error-handler
+ // and can't-open-file message boxes.
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+ SetErrorMode(SEM_NOOPENFILEERRORBOX);
+
+ // And this will catch all unhandled exceptions.
+ SetUnhandledExceptionFilter (&UnhandledExceptionFilter);
+}
+
+}}} // namespace