summaryrefslogtreecommitdiffstats
path: root/messagebus/src/tests
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /messagebus/src/tests
Publish
Diffstat (limited to 'messagebus/src/tests')
-rw-r--r--messagebus/src/tests/.gitignore4
-rw-r--r--messagebus/src/tests/CMakeLists.txt41
-rw-r--r--messagebus/src/tests/advancedrouting/.gitignore4
-rw-r--r--messagebus/src/tests/advancedrouting/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/advancedrouting/DESC1
-rw-r--r--messagebus/src/tests/advancedrouting/FILES1
-rw-r--r--messagebus/src/tests/advancedrouting/advancedrouting.cpp188
-rw-r--r--messagebus/src/tests/auto-reply/.gitignore4
-rw-r--r--messagebus/src/tests/auto-reply/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/auto-reply/DESC2
-rw-r--r--messagebus/src/tests/auto-reply/FILES1
-rw-r--r--messagebus/src/tests/auto-reply/auto-reply.cpp40
-rw-r--r--messagebus/src/tests/blob/.gitignore4
-rw-r--r--messagebus/src/tests/blob/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/blob/DESC1
-rw-r--r--messagebus/src/tests/blob/FILES1
-rw-r--r--messagebus/src/tests/blob/blob.cpp79
-rw-r--r--messagebus/src/tests/bucketsequence/.gitignore3
-rw-r--r--messagebus/src/tests/bucketsequence/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/bucketsequence/DESC1
-rw-r--r--messagebus/src/tests/bucketsequence/FILES1
-rw-r--r--messagebus/src/tests/bucketsequence/bucketsequence.cpp50
-rw-r--r--messagebus/src/tests/choke/.gitignore4
-rw-r--r--messagebus/src/tests/choke/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/choke/DESC1
-rw-r--r--messagebus/src/tests/choke/FILES1
-rw-r--r--messagebus/src/tests/choke/choke.cpp227
-rw-r--r--messagebus/src/tests/configagent/.gitignore5
-rw-r--r--messagebus/src/tests/configagent/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/configagent/DESC2
-rw-r--r--messagebus/src/tests/configagent/FILES3
-rw-r--r--messagebus/src/tests/configagent/configagent.cpp122
-rw-r--r--messagebus/src/tests/configagent/full.cfg44
-rw-r--r--messagebus/src/tests/configagent/half.cfg23
-rw-r--r--messagebus/src/tests/context/.gitignore4
-rw-r--r--messagebus/src/tests/context/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/context/DESC1
-rw-r--r--messagebus/src/tests/context/FILES1
-rw-r--r--messagebus/src/tests/context/context.cpp102
-rwxr-xr-xmessagebus/src/tests/create-test.sh74
-rw-r--r--messagebus/src/tests/emptyreply/.gitignore4
-rw-r--r--messagebus/src/tests/emptyreply/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/emptyreply/DESC1
-rw-r--r--messagebus/src/tests/emptyreply/FILES1
-rw-r--r--messagebus/src/tests/emptyreply/emptyreply.cpp21
-rw-r--r--messagebus/src/tests/error/.gitignore4
-rw-r--r--messagebus/src/tests/error/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/error/DESC1
-rw-r--r--messagebus/src/tests/error/FILES1
-rw-r--r--messagebus/src/tests/error/error.cpp83
-rw-r--r--messagebus/src/tests/identity/.gitignore4
-rw-r--r--messagebus/src/tests/identity/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/identity/DESC1
-rw-r--r--messagebus/src/tests/identity/FILES2
-rw-r--r--messagebus/src/tests/identity/identity.cpp43
-rw-r--r--messagebus/src/tests/loadbalance/.gitignore4
-rw-r--r--messagebus/src/tests/loadbalance/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/loadbalance/DESC2
-rw-r--r--messagebus/src/tests/loadbalance/FILES1
-rw-r--r--messagebus/src/tests/loadbalance/loadbalance.cpp90
-rw-r--r--messagebus/src/tests/messagebus/.gitignore4
-rw-r--r--messagebus/src/tests/messagebus/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/messagebus/DESC1
-rw-r--r--messagebus/src/tests/messagebus/FILES1
-rw-r--r--messagebus/src/tests/messagebus/messagebus.cpp538
-rw-r--r--messagebus/src/tests/messageordering/.gitignore3
-rw-r--r--messagebus/src/tests/messageordering/CMakeLists.txt12
-rw-r--r--messagebus/src/tests/messageordering/DESC1
-rw-r--r--messagebus/src/tests/messageordering/FILES1
-rw-r--r--messagebus/src/tests/messageordering/messageordering.cpp178
-rw-r--r--messagebus/src/tests/messenger/.gitignore3
-rw-r--r--messagebus/src/tests/messenger/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/messenger/DESC1
-rw-r--r--messagebus/src/tests/messenger/FILES1
-rw-r--r--messagebus/src/tests/messenger/messenger.cpp61
-rw-r--r--messagebus/src/tests/oos/.gitignore4
-rw-r--r--messagebus/src/tests/oos/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/oos/DESC1
-rw-r--r--messagebus/src/tests/oos/FILES1
-rw-r--r--messagebus/src/tests/oos/oos.cpp231
-rw-r--r--messagebus/src/tests/oospolicy/.gitignore3
-rw-r--r--messagebus/src/tests/protocolrepository/.gitignore3
-rw-r--r--messagebus/src/tests/protocolrepository/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/protocolrepository/DESC1
-rw-r--r--messagebus/src/tests/protocolrepository/FILES1
-rw-r--r--messagebus/src/tests/protocolrepository/protocolrepository.cpp75
-rw-r--r--messagebus/src/tests/queue/.gitignore4
-rw-r--r--messagebus/src/tests/queue/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/queue/DESC1
-rw-r--r--messagebus/src/tests/queue/FILES1
-rw-r--r--messagebus/src/tests/queue/queue.cpp90
-rw-r--r--messagebus/src/tests/replygate/.gitignore4
-rw-r--r--messagebus/src/tests/replygate/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/replygate/DESC1
-rw-r--r--messagebus/src/tests/replygate/FILES1
-rw-r--r--messagebus/src/tests/replygate/replygate.cpp91
-rw-r--r--messagebus/src/tests/replyset/.gitignore3
-rw-r--r--messagebus/src/tests/resender/.gitignore4
-rw-r--r--messagebus/src/tests/resender/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/resender/DESC1
-rw-r--r--messagebus/src/tests/resender/FILES1
-rw-r--r--messagebus/src/tests/resender/resender.cpp310
-rw-r--r--messagebus/src/tests/result/.gitignore4
-rw-r--r--messagebus/src/tests/result/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/result/DESC1
-rw-r--r--messagebus/src/tests/result/FILES1
-rw-r--r--messagebus/src/tests/result/result.cpp76
-rw-r--r--messagebus/src/tests/retrypolicy/.gitignore4
-rw-r--r--messagebus/src/tests/retrypolicy/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/retrypolicy/DESC1
-rw-r--r--messagebus/src/tests/retrypolicy/FILES1
-rw-r--r--messagebus/src/tests/retrypolicy/retrypolicy.cpp39
-rw-r--r--messagebus/src/tests/routable/.gitignore4
-rw-r--r--messagebus/src/tests/routable/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/routable/DESC1
-rw-r--r--messagebus/src/tests/routable/FILES1
-rw-r--r--messagebus/src/tests/routable/routable.cpp94
-rw-r--r--messagebus/src/tests/routablequeue/.gitignore4
-rw-r--r--messagebus/src/tests/routablequeue/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/routablequeue/DESC1
-rw-r--r--messagebus/src/tests/routablequeue/FILES1
-rw-r--r--messagebus/src/tests/routablequeue/routablequeue.cpp110
-rw-r--r--messagebus/src/tests/routeparser/.gitignore4
-rw-r--r--messagebus/src/tests/routeparser/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/routeparser/DESC1
-rw-r--r--messagebus/src/tests/routeparser/FILES1
-rw-r--r--messagebus/src/tests/routeparser/routeparser.cpp280
-rw-r--r--messagebus/src/tests/routing/.gitignore4
-rw-r--r--messagebus/src/tests/routing/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/routing/DESC1
-rw-r--r--messagebus/src/tests/routing/FILES1
-rw-r--r--messagebus/src/tests/routing/routing.cpp1580
-rw-r--r--messagebus/src/tests/routingcontext/.gitignore4
-rw-r--r--messagebus/src/tests/routingcontext/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/routingcontext/DESC1
-rw-r--r--messagebus/src/tests/routingcontext/FILES1
-rw-r--r--messagebus/src/tests/routingcontext/routingcontext.cpp389
-rw-r--r--messagebus/src/tests/routingspec/.gitignore4
-rw-r--r--messagebus/src/tests/routingspec/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/routingspec/DESC1
-rw-r--r--messagebus/src/tests/routingspec/FILES1
-rw-r--r--messagebus/src/tests/routingspec/routingspec.cpp251
-rw-r--r--messagebus/src/tests/rpcserviceaddress/.gitignore4
-rw-r--r--messagebus/src/tests/rpcserviceaddress/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/rpcserviceaddress/DESC1
-rw-r--r--messagebus/src/tests/rpcserviceaddress/FILES1
-rw-r--r--messagebus/src/tests/rpcserviceaddress/rpcserviceaddress.cpp44
-rw-r--r--messagebus/src/tests/selector/.gitignore3
-rw-r--r--messagebus/src/tests/sendadapter/.gitignore4
-rw-r--r--messagebus/src/tests/sendadapter/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/sendadapter/DESC1
-rw-r--r--messagebus/src/tests/sendadapter/FILES1
-rw-r--r--messagebus/src/tests/sendadapter/sendadapter.cpp252
-rw-r--r--messagebus/src/tests/sequencer/.gitignore4
-rw-r--r--messagebus/src/tests/sequencer/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/sequencer/DESC1
-rw-r--r--messagebus/src/tests/sequencer/FILES1
-rw-r--r--messagebus/src/tests/sequencer/sequencer.cpp194
-rw-r--r--messagebus/src/tests/serviceaddress/.gitignore4
-rw-r--r--messagebus/src/tests/serviceaddress/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/serviceaddress/DESC1
-rw-r--r--messagebus/src/tests/serviceaddress/FILES1
-rw-r--r--messagebus/src/tests/serviceaddress/serviceaddress.cpp137
-rw-r--r--messagebus/src/tests/servicepool/.gitignore4
-rw-r--r--messagebus/src/tests/servicepool/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/servicepool/DESC1
-rw-r--r--messagebus/src/tests/servicepool/FILES1
-rw-r--r--messagebus/src/tests/servicepool/servicepool.cpp71
-rw-r--r--messagebus/src/tests/shutdown/.gitignore4
-rw-r--r--messagebus/src/tests/shutdown/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/shutdown/DESC1
-rw-r--r--messagebus/src/tests/shutdown/FILES1
-rw-r--r--messagebus/src/tests/shutdown/shutdown.cpp159
-rw-r--r--messagebus/src/tests/simple-roundtrip/.gitignore4
-rw-r--r--messagebus/src/tests/simple-roundtrip/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/simple-roundtrip/DESC1
-rw-r--r--messagebus/src/tests/simple-roundtrip/FILES1
-rw-r--r--messagebus/src/tests/simple-roundtrip/simple-roundtrip.cpp101
-rw-r--r--messagebus/src/tests/simpleprotocol/.gitignore4
-rw-r--r--messagebus/src/tests/simpleprotocol/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/simpleprotocol/DESC3
-rw-r--r--messagebus/src/tests/simpleprotocol/FILES1
-rw-r--r--messagebus/src/tests/simpleprotocol/simpleprotocol.cpp72
-rw-r--r--messagebus/src/tests/slobrok/.gitignore4
-rw-r--r--messagebus/src/tests/slobrok/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/slobrok/DESC2
-rw-r--r--messagebus/src/tests/slobrok/FILES1
-rw-r--r--messagebus/src/tests/slobrok/slobrok.cpp134
-rw-r--r--messagebus/src/tests/sourcesession/.gitignore4
-rw-r--r--messagebus/src/tests/sourcesession/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/sourcesession/DESC3
-rw-r--r--messagebus/src/tests/sourcesession/FILES1
-rw-r--r--messagebus/src/tests/sourcesession/sourcesession.cpp339
-rw-r--r--messagebus/src/tests/targetpool/.gitignore4
-rw-r--r--messagebus/src/tests/targetpool/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/targetpool/DESC1
-rw-r--r--messagebus/src/tests/targetpool/FILES1
-rw-r--r--messagebus/src/tests/targetpool/targetpool.cpp100
-rw-r--r--messagebus/src/tests/throttling/.gitignore4
-rw-r--r--messagebus/src/tests/throttling/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/throttling/DESC1
-rw-r--r--messagebus/src/tests/throttling/FILES1
-rw-r--r--messagebus/src/tests/throttling/throttling.cpp362
-rw-r--r--messagebus/src/tests/timeout/.gitignore4
-rw-r--r--messagebus/src/tests/timeout/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/timeout/DESC1
-rw-r--r--messagebus/src/tests/timeout/FILES1
-rw-r--r--messagebus/src/tests/timeout/timeout.cpp83
-rw-r--r--messagebus/src/tests/trace-roundtrip/.gitignore4
-rw-r--r--messagebus/src/tests/trace-roundtrip/CMakeLists.txt9
-rw-r--r--messagebus/src/tests/trace-roundtrip/DESC1
-rw-r--r--messagebus/src/tests/trace-roundtrip/FILES1
-rw-r--r--messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp127
213 files changed, 8434 insertions, 0 deletions
diff --git a/messagebus/src/tests/.gitignore b/messagebus/src/tests/.gitignore
new file mode 100644
index 00000000000..c473b24b98a
--- /dev/null
+++ b/messagebus/src/tests/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+testrunner
+*_test
diff --git a/messagebus/src/tests/CMakeLists.txt b/messagebus/src/tests/CMakeLists.txt
new file mode 100644
index 00000000000..9991e8a8e8c
--- /dev/null
+++ b/messagebus/src/tests/CMakeLists.txt
@@ -0,0 +1,41 @@
+add_subdirectory(advancedrouting)
+add_subdirectory(auto-reply)
+add_subdirectory(blob)
+add_subdirectory(bucketsequence)
+add_subdirectory(choke)
+add_subdirectory(configagent)
+add_subdirectory(context)
+add_subdirectory(emptyreply)
+add_subdirectory(error)
+add_subdirectory(identity)
+add_subdirectory(loadbalance)
+add_subdirectory(messagebus)
+add_subdirectory(messageordering)
+add_subdirectory(messenger)
+add_subdirectory(oos)
+add_subdirectory(protocolrepository)
+add_subdirectory(queue)
+add_subdirectory(replygate)
+add_subdirectory(resender)
+add_subdirectory(result)
+add_subdirectory(retrypolicy)
+add_subdirectory(routable)
+add_subdirectory(routablequeue)
+add_subdirectory(routeparser)
+add_subdirectory(routing)
+add_subdirectory(routingcontext)
+add_subdirectory(routingspec)
+add_subdirectory(rpcserviceaddress)
+add_subdirectory(sendadapter)
+add_subdirectory(sequencer)
+add_subdirectory(serviceaddress)
+add_subdirectory(servicepool)
+add_subdirectory(shutdown)
+add_subdirectory(simple-roundtrip)
+add_subdirectory(simpleprotocol)
+add_subdirectory(slobrok)
+add_subdirectory(sourcesession)
+add_subdirectory(targetpool)
+add_subdirectory(throttling)
+add_subdirectory(timeout)
+add_subdirectory(trace-roundtrip)
diff --git a/messagebus/src/tests/advancedrouting/.gitignore b/messagebus/src/tests/advancedrouting/.gitignore
new file mode 100644
index 00000000000..570db59096d
--- /dev/null
+++ b/messagebus/src/tests/advancedrouting/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+advancedrouting_test
+messagebus_advancedrouting_test_app
diff --git a/messagebus/src/tests/advancedrouting/CMakeLists.txt b/messagebus/src/tests/advancedrouting/CMakeLists.txt
new file mode 100644
index 00000000000..99f5b037b69
--- /dev/null
+++ b/messagebus/src/tests/advancedrouting/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_advancedrouting_test_app
+ SOURCES
+ advancedrouting.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_advancedrouting_test_app COMMAND messagebus_advancedrouting_test_app)
diff --git a/messagebus/src/tests/advancedrouting/DESC b/messagebus/src/tests/advancedrouting/DESC
new file mode 100644
index 00000000000..735d63dcdc3
--- /dev/null
+++ b/messagebus/src/tests/advancedrouting/DESC
@@ -0,0 +1 @@
+advancedrouting test. Take a look at advancedrouting.cpp for details.
diff --git a/messagebus/src/tests/advancedrouting/FILES b/messagebus/src/tests/advancedrouting/FILES
new file mode 100644
index 00000000000..61eb026ac3a
--- /dev/null
+++ b/messagebus/src/tests/advancedrouting/FILES
@@ -0,0 +1 @@
+advancedrouting.cpp
diff --git a/messagebus/src/tests/advancedrouting/advancedrouting.cpp b/messagebus/src/tests/advancedrouting/advancedrouting.cpp
new file mode 100644
index 00000000000..976746b0738
--- /dev/null
+++ b/messagebus/src/tests/advancedrouting/advancedrouting.cpp
@@ -0,0 +1,188 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routing_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routing/errordirective.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/testlib/custompolicy.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/vstringfmt.h>
+
+using namespace mbus;
+
+class TestData {
+public:
+ Slobrok _slobrok;
+ RetryTransientErrorsPolicy::SP _retryPolicy;
+ TestServer _srcServer;
+ SourceSession::UP _srcSession;
+ Receptor _srcHandler;
+ TestServer _dstServer;
+ DestinationSession::UP _fooSession;
+ Receptor _fooHandler;
+ DestinationSession::UP _barSession;
+ Receptor _barHandler;
+ DestinationSession::UP _bazSession;
+ Receptor _bazHandler;
+
+public:
+ TestData();
+ bool start();
+};
+
+class Test : public vespalib::TestApp {
+private:
+ Message::UP createMessage(const string &msg);
+ bool testTrace(const std::vector<string> &expected, const Trace &trace);
+
+public:
+ int Main();
+ void testAdvanced(TestData &data);
+};
+
+TEST_APPHOOK(Test);
+
+TestData::TestData() :
+ _slobrok(),
+ _retryPolicy(new RetryTransientErrorsPolicy()),
+ _srcServer(MessageBusParams().setRetryPolicy(_retryPolicy).addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams().setSlobrokConfig(_slobrok.config())),
+ _srcSession(),
+ _srcHandler(),
+ _dstServer(MessageBusParams().addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams().setIdentity(Identity("dst")).setSlobrokConfig(_slobrok.config())),
+ _fooSession(),
+ _fooHandler(),
+ _barSession(),
+ _barHandler(),
+ _bazSession(),
+ _bazHandler()
+{
+ _retryPolicy->setBaseDelay(0);
+}
+
+bool
+TestData::start()
+{
+ _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams().setReplyHandler(_srcHandler));
+ if (_srcSession.get() == NULL) {
+ LOG(error, "Could not create source session.");
+ return false;
+ }
+ _fooSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("foo").setMessageHandler(_fooHandler));
+ if (_fooSession.get() == NULL) {
+ LOG(error, "Could not create foo session.");
+ return false;
+ }
+ _barSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("bar").setMessageHandler(_barHandler));
+ if (_barSession.get() == NULL) {
+ LOG(error, "Could not create bar session.");
+ return false;
+ }
+ _bazSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("baz").setMessageHandler(_bazHandler));
+ if (_bazSession.get() == NULL) {
+ LOG(error, "Could not create baz session.");
+ return false;
+ }
+ if (!_srcServer.waitSlobrok("dst/*", 3u)) {
+ return false;
+ }
+ return true;
+}
+
+Message::UP
+Test::createMessage(const string &msg)
+{
+ Message::UP ret(new SimpleMessage(msg));
+ ret->getTrace().setLevel(9);
+ return ret;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("routing_test");
+
+ TestData data;
+ ASSERT_TRUE(data.start());
+
+ testAdvanced(data); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+void
+Test::testAdvanced(TestData &data)
+{
+ const double TIMEOUT=60;
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(false, ErrorCode::NO_ADDRESS_FOR_SERVICE)));
+ data._srcServer.mb.putProtocol(protocol);
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addHop(HopSpec("bar", "dst/bar"))
+ .addHop(HopSpec("baz", "dst/baz"))
+ .addRoute(RouteSpec("baz").addHop("baz"))));
+ string route = vespalib::make_vespa_string("[Custom:%s,bar,route:baz,dst/cox,?dst/unknown]",
+ data._fooSession->getConnectionSpec().c_str());
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse(route)).isAccepted());
+
+ // Initial send.
+ Message::UP msg = data._fooHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._fooSession->acknowledge(std::move(msg));
+ msg = data._barHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::TRANSIENT_ERROR, "bar"));
+ data._barSession->reply(std::move(reply));
+ msg = data._bazHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ reply.reset(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::TRANSIENT_ERROR, "baz1"));
+ data._bazSession->reply(std::move(reply));
+
+ // First retry.
+ msg = data._fooHandler.getMessage(0);
+ ASSERT_TRUE(msg.get() == NULL);
+ msg = data._barHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._barSession->acknowledge(std::move(msg));
+ msg = data._bazHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ reply.reset(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::TRANSIENT_ERROR, "baz2"));
+ data._bazSession->reply(std::move(reply));
+
+ // Second retry.
+ msg = data._fooHandler.getMessage(0);
+ ASSERT_TRUE(msg.get() == NULL);
+ msg = data._barHandler.getMessage(0);
+ ASSERT_TRUE(msg.get() == NULL);
+ msg = data._bazHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ reply.reset(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::FATAL_ERROR, "baz3"));
+ data._bazSession->reply(std::move(reply));
+
+ // Done.
+ reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(2u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::FATAL_ERROR, reply->getError(0).getCode());
+ EXPECT_EQUAL((uint32_t)ErrorCode::NO_ADDRESS_FOR_SERVICE, reply->getError(1).getCode());
+}
diff --git a/messagebus/src/tests/auto-reply/.gitignore b/messagebus/src/tests/auto-reply/.gitignore
new file mode 100644
index 00000000000..061d2f262bd
--- /dev/null
+++ b/messagebus/src/tests/auto-reply/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+auto-reply_test
+messagebus_auto-reply_test_app
diff --git a/messagebus/src/tests/auto-reply/CMakeLists.txt b/messagebus/src/tests/auto-reply/CMakeLists.txt
new file mode 100644
index 00000000000..950ade550b7
--- /dev/null
+++ b/messagebus/src/tests/auto-reply/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_auto-reply_test_app
+ SOURCES
+ auto-reply.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_auto-reply_test_app COMMAND messagebus_auto-reply_test_app)
diff --git a/messagebus/src/tests/auto-reply/DESC b/messagebus/src/tests/auto-reply/DESC
new file mode 100644
index 00000000000..2aec186bfac
--- /dev/null
+++ b/messagebus/src/tests/auto-reply/DESC
@@ -0,0 +1,2 @@
+Test that a deleted Message or Reply with a non-empty call-stack will
+generate an automatic Reply.
diff --git a/messagebus/src/tests/auto-reply/FILES b/messagebus/src/tests/auto-reply/FILES
new file mode 100644
index 00000000000..29f5dbbc1bb
--- /dev/null
+++ b/messagebus/src/tests/auto-reply/FILES
@@ -0,0 +1 @@
+auto-reply.cpp
diff --git a/messagebus/src/tests/auto-reply/auto-reply.cpp b/messagebus/src/tests/auto-reply/auto-reply.cpp
new file mode 100644
index 00000000000..876755dfd0e
--- /dev/null
+++ b/messagebus/src/tests/auto-reply/auto-reply.cpp
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("auto-reply_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("auto-reply_test");
+ RoutableQueue q;
+ {
+ Message::UP msg(new SimpleMessage("test"));
+ }
+ EXPECT_TRUE(q.size() == 0);
+ {
+ Message::UP msg(new SimpleMessage("test"));
+ msg->pushHandler(q);
+ }
+ EXPECT_TRUE(q.size() == 1);
+ {
+ Reply::UP reply(new SimpleReply("test"));
+ }
+ EXPECT_TRUE(q.size() == 1);
+ {
+ Reply::UP reply(new SimpleReply("test"));
+ reply->pushHandler(q);
+ }
+ EXPECT_TRUE(q.size() == 2);
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/blob/.gitignore b/messagebus/src/tests/blob/.gitignore
new file mode 100644
index 00000000000..8602aa42ade
--- /dev/null
+++ b/messagebus/src/tests/blob/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+blob_test
+messagebus_blob_test_app
diff --git a/messagebus/src/tests/blob/CMakeLists.txt b/messagebus/src/tests/blob/CMakeLists.txt
new file mode 100644
index 00000000000..d9a865519cb
--- /dev/null
+++ b/messagebus/src/tests/blob/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_blob_test_app
+ SOURCES
+ blob.cpp
+ DEPENDS
+ messagebus
+ messagebus_messagebus-test
+)
+vespa_add_test(NAME messagebus_blob_test_app COMMAND messagebus_blob_test_app)
diff --git a/messagebus/src/tests/blob/DESC b/messagebus/src/tests/blob/DESC
new file mode 100644
index 00000000000..b2ba59c187f
--- /dev/null
+++ b/messagebus/src/tests/blob/DESC
@@ -0,0 +1 @@
+Test the Blob and BlobRef classes.
diff --git a/messagebus/src/tests/blob/FILES b/messagebus/src/tests/blob/FILES
new file mode 100644
index 00000000000..fd1396e55e3
--- /dev/null
+++ b/messagebus/src/tests/blob/FILES
@@ -0,0 +1 @@
+blob.cpp
diff --git a/messagebus/src/tests/blob/blob.cpp b/messagebus/src/tests/blob/blob.cpp
new file mode 100644
index 00000000000..9b0df1ae476
--- /dev/null
+++ b/messagebus/src/tests/blob/blob.cpp
@@ -0,0 +1,79 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("blob_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/blob.h>
+#include <vespa/messagebus/blobref.h>
+
+using mbus::Blob;
+using mbus::BlobRef;
+
+TEST_SETUP(Test);
+
+Blob makeBlob(const char *txt) {
+ Blob b(strlen(txt) + 1);
+ strcpy(b.data(), txt);
+ return b;
+}
+
+BlobRef makeBlobRef(const Blob &b) {
+ return BlobRef(b.data(), b.size());
+}
+
+Blob returnBlob(Blob b) {
+ return b;
+}
+
+BlobRef returnBlobRef(BlobRef br) {
+ return br;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("blob_test");
+
+ // create a blob
+ Blob b = makeBlob("test");
+ EXPECT_TRUE(b.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", b.data()) == 0);
+
+ // create a ref to a blob
+ BlobRef br = makeBlobRef(b);
+ EXPECT_TRUE(br.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", br.data()) == 0);
+ EXPECT_TRUE(b.data() == br.data());
+
+ // non-destructive copy of ref
+ BlobRef br2 = returnBlobRef(br);
+ EXPECT_TRUE(br.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", br.data()) == 0);
+ EXPECT_TRUE(b.data() == br.data());
+ EXPECT_TRUE(br2.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", br2.data()) == 0);
+ EXPECT_TRUE(b.data() == br2.data());
+
+ br = br2;
+ EXPECT_TRUE(br.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", br.data()) == 0);
+ EXPECT_TRUE(b.data() == br.data());
+ EXPECT_TRUE(br2.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", br2.data()) == 0);
+ EXPECT_TRUE(b.data() == br2.data());
+
+ // destructive copy of blob
+ Blob b2 = returnBlob(std::move(b));
+ EXPECT_EQUAL(0u, b.size());
+ EXPECT_TRUE(b.data() == 0);
+ EXPECT_TRUE(b2.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", b2.data()) == 0);
+
+ b.swap(b2);
+ EXPECT_EQUAL(0u, b2.size());
+ EXPECT_TRUE(b2.data() == 0);
+ EXPECT_TRUE(b.size() == strlen("test") + 1);
+ EXPECT_TRUE(strcmp("test", b.data()) == 0);
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/bucketsequence/.gitignore b/messagebus/src/tests/bucketsequence/.gitignore
new file mode 100644
index 00000000000..cca77fed742
--- /dev/null
+++ b/messagebus/src/tests/bucketsequence/.gitignore
@@ -0,0 +1,3 @@
+/.depend
+/Makefile
+messagebus_bucketsequence_test_app
diff --git a/messagebus/src/tests/bucketsequence/CMakeLists.txt b/messagebus/src/tests/bucketsequence/CMakeLists.txt
new file mode 100644
index 00000000000..5ab01524d51
--- /dev/null
+++ b/messagebus/src/tests/bucketsequence/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_bucketsequence_test_app
+ SOURCES
+ bucketsequence.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_bucketsequence_test_app COMMAND messagebus_bucketsequence_test_app)
diff --git a/messagebus/src/tests/bucketsequence/DESC b/messagebus/src/tests/bucketsequence/DESC
new file mode 100644
index 00000000000..b2e8d79519b
--- /dev/null
+++ b/messagebus/src/tests/bucketsequence/DESC
@@ -0,0 +1 @@
+bucketsequence test. Take a look at bucketsequence.cpp for details.
diff --git a/messagebus/src/tests/bucketsequence/FILES b/messagebus/src/tests/bucketsequence/FILES
new file mode 100644
index 00000000000..6db6cc0a2cd
--- /dev/null
+++ b/messagebus/src/tests/bucketsequence/FILES
@@ -0,0 +1 @@
+bucketsequence.cpp
diff --git a/messagebus/src/tests/bucketsequence/bucketsequence.cpp b/messagebus/src/tests/bucketsequence/bucketsequence.cpp
new file mode 100644
index 00000000000..6036c43f659
--- /dev/null
+++ b/messagebus/src/tests/bucketsequence/bucketsequence.cpp
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("bucketsequence_test");
+
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+class MyMessage : public SimpleMessage {
+public:
+ MyMessage() : SimpleMessage("foo") { }
+ bool hasBucketSequence() { return true; }
+};
+
+int
+Test::Main()
+{
+ TEST_INIT("bucketsequence_test");
+
+ Slobrok slobrok;
+ TestServer server(MessageBusParams()
+ .addProtocol(IProtocol::SP(new SimpleProtocol()))
+ .setRetryPolicy(IRetryPolicy::SP(new RetryTransientErrorsPolicy())),
+ RPCNetworkParams()
+ .setSlobrokConfig(slobrok.config()));
+ Receptor receptor;
+ SourceSession::UP session = server.mb.createSourceSession(
+ SourceSessionParams()
+ .setReplyHandler(receptor));
+ Message::UP msg(new MyMessage());
+ msg->setRoute(Route::parse("foo"));
+ ASSERT_TRUE(session->send(std::move(msg)).isAccepted());
+ Reply::UP reply = receptor.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::SEQUENCE_ERROR, reply->getError(0).getCode());
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/choke/.gitignore b/messagebus/src/tests/choke/.gitignore
new file mode 100644
index 00000000000..320ee997500
--- /dev/null
+++ b/messagebus/src/tests/choke/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+choke_test
+messagebus_choke_test_app
diff --git a/messagebus/src/tests/choke/CMakeLists.txt b/messagebus/src/tests/choke/CMakeLists.txt
new file mode 100644
index 00000000000..02e2c14b943
--- /dev/null
+++ b/messagebus/src/tests/choke/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_choke_test_app
+ SOURCES
+ choke.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_choke_test_app NO_VALGRIND COMMAND messagebus_choke_test_app)
diff --git a/messagebus/src/tests/choke/DESC b/messagebus/src/tests/choke/DESC
new file mode 100644
index 00000000000..fd1d4965b7d
--- /dev/null
+++ b/messagebus/src/tests/choke/DESC
@@ -0,0 +1 @@
+choke test. Take a look at choke.cpp for details.
diff --git a/messagebus/src/tests/choke/FILES b/messagebus/src/tests/choke/FILES
new file mode 100644
index 00000000000..7a0d95feb52
--- /dev/null
+++ b/messagebus/src/tests/choke/FILES
@@ -0,0 +1 @@
+choke.cpp
diff --git a/messagebus/src/tests/choke/choke.cpp b/messagebus/src/tests/choke/choke.cpp
new file mode 100644
index 00000000000..567ec52db79
--- /dev/null
+++ b/messagebus/src/tests/choke/choke.cpp
@@ -0,0 +1,227 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("choke_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/reply.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Setup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class TestData {
+public:
+ Slobrok _slobrok;
+ TestServer _srcServer;
+ SourceSession::UP _srcSession;
+ Receptor _srcHandler;
+ TestServer _dstServer;
+ DestinationSession::UP _dstSession;
+ Receptor _dstHandler;
+
+public:
+ TestData();
+ bool start();
+};
+
+class Test : public vespalib::TestApp {
+private:
+ Message::UP createMessage(const string &msg);
+
+public:
+ int Main();
+ void testMaxCount(TestData &data);
+ void testMaxSize(TestData &data);
+};
+
+TEST_APPHOOK(Test);
+
+TestData::TestData() :
+ _slobrok(),
+ _srcServer(MessageBusParams()
+ .setRetryPolicy(IRetryPolicy::SP())
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setSlobrokConfig(_slobrok.config())),
+ _srcSession(),
+ _srcHandler(),
+ _dstServer(MessageBusParams()
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setIdentity(Identity("dst"))
+ .setSlobrokConfig(_slobrok.config())),
+ _dstSession(),
+ _dstHandler()
+{
+ // empty
+}
+
+bool
+TestData::start()
+{
+ _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams()
+ .setThrottlePolicy(IThrottlePolicy::SP())
+ .setReplyHandler(_srcHandler));
+ if (_srcSession.get() == NULL) {
+ return false;
+ }
+ _dstSession = _dstServer.mb.createDestinationSession(DestinationSessionParams()
+ .setName("session")
+ .setMessageHandler(_dstHandler));
+ if (_dstSession.get() == NULL) {
+ return false;
+ }
+ if (!_srcServer.waitSlobrok("dst/session", 1u)) {
+ return false;
+ }
+ return true;
+}
+
+Message::UP
+Test::createMessage(const string &msg)
+{
+ Message::UP ret(new SimpleMessage(msg));
+ ret->getTrace().setLevel(9);
+ return ret;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("choke_test");
+
+ TestData data;
+ ASSERT_TRUE(data.start());
+
+ testMaxCount(data); TEST_FLUSH();
+ testMaxSize(data); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+static const double TIMEOUT = 120;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tests
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void
+Test::testMaxCount(TestData &data)
+{
+ uint32_t max = 10;
+ data._dstServer.mb.setMaxPendingCount(max);
+ std::vector<Message*> lst;
+ for (uint32_t i = 0; i < max * 2; ++i) {
+ if (i < max) {
+ EXPECT_EQUAL(i, data._dstServer.mb.getPendingCount());
+ } else {
+ EXPECT_EQUAL(max, data._dstServer.mb.getPendingCount());
+ }
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ if (i < max) {
+ Message::UP msg = data._dstHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ lst.push_back(msg.release());
+ } else {
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::SESSION_BUSY, reply->getError(0).getCode());
+ }
+ }
+ for (uint32_t i = 0; i < 5; ++i) {
+ Message::UP msg(lst[0]);
+ lst.erase(lst.begin());
+ data._dstSession->acknowledge(std::move(msg));
+
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(!reply->hasErrors());
+ msg = reply->getMessage();
+ ASSERT_TRUE(msg.get() != NULL);
+ EXPECT_TRUE(data._srcSession->send(std::move(msg), Route::parse("dst/session")).isAccepted());
+
+ msg = data._dstHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ lst.push_back(msg.release());
+ }
+ while (!lst.empty()) {
+ EXPECT_EQUAL(lst.size(), data._dstServer.mb.getPendingCount());
+ Message::UP msg(lst[0]);
+ lst.erase(lst.begin());
+ data._dstSession->acknowledge(std::move(msg));
+
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(!reply->hasErrors());
+ }
+ EXPECT_EQUAL(0u, data._dstServer.mb.getPendingCount());
+}
+
+void
+Test::testMaxSize(TestData &data)
+{
+ uint32_t size = createMessage("msg")->getApproxSize();
+ uint32_t max = size * 10;
+ data._dstServer.mb.setMaxPendingSize(max);
+ std::vector<Message*> lst;
+ for (uint32_t i = 0; i < max * 2; i += size) {
+ if (i < max) {
+ EXPECT_EQUAL(i, data._dstServer.mb.getPendingSize());
+ } else {
+ EXPECT_EQUAL(max, data._dstServer.mb.getPendingSize());
+ }
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ if (i < max) {
+ Message::UP msg = data._dstHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ lst.push_back(msg.release());
+ } else {
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::SESSION_BUSY, reply->getError(0).getCode());
+ }
+ }
+ for (uint32_t i = 0; i < 5; ++i) {
+ Message::UP msg(lst[0]);
+ lst.erase(lst.begin());
+ data._dstSession->acknowledge(std::move(msg));
+
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(!reply->hasErrors());
+ msg = reply->getMessage();
+ ASSERT_TRUE(msg.get() != NULL);
+ EXPECT_TRUE(data._srcSession->send(std::move(msg), Route::parse("dst/session")).isAccepted());
+
+ msg = data._dstHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ lst.push_back(msg.release());
+ }
+ while (!lst.empty()) {
+ EXPECT_EQUAL(size * lst.size(), data._dstServer.mb.getPendingSize());
+ Message::UP msg(lst[0]);
+ lst.erase(lst.begin());
+ data._dstSession->acknowledge(std::move(msg));
+
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(!reply->hasErrors());
+ }
+ EXPECT_EQUAL(0u, data._dstServer.mb.getPendingSize());
+}
diff --git a/messagebus/src/tests/configagent/.gitignore b/messagebus/src/tests/configagent/.gitignore
new file mode 100644
index 00000000000..240f433156c
--- /dev/null
+++ b/messagebus/src/tests/configagent/.gitignore
@@ -0,0 +1,5 @@
+.depend
+Makefile
+configagent_test
+test.cfg
+messagebus_configagent_test_app
diff --git a/messagebus/src/tests/configagent/CMakeLists.txt b/messagebus/src/tests/configagent/CMakeLists.txt
new file mode 100644
index 00000000000..04170cc9d05
--- /dev/null
+++ b/messagebus/src/tests/configagent/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_configagent_test_app
+ SOURCES
+ configagent.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_configagent_test_app COMMAND messagebus_configagent_test_app)
diff --git a/messagebus/src/tests/configagent/DESC b/messagebus/src/tests/configagent/DESC
new file mode 100644
index 00000000000..b4db2789b01
--- /dev/null
+++ b/messagebus/src/tests/configagent/DESC
@@ -0,0 +1,2 @@
+Test that the config agent is able to configure a config handler using
+config files.
diff --git a/messagebus/src/tests/configagent/FILES b/messagebus/src/tests/configagent/FILES
new file mode 100644
index 00000000000..49fd8684ac1
--- /dev/null
+++ b/messagebus/src/tests/configagent/FILES
@@ -0,0 +1,3 @@
+configagent.cpp
+full.cfg
+half.cfg
diff --git a/messagebus/src/tests/configagent/configagent.cpp b/messagebus/src/tests/configagent/configagent.cpp
new file mode 100644
index 00000000000..c08fdd25be0
--- /dev/null
+++ b/messagebus/src/tests/configagent/configagent.cpp
@@ -0,0 +1,122 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("configagent_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/config/print/fileconfigreader.h>
+#include <vespa/messagebus/configagent.h>
+#include <vespa/messagebus/iconfighandler.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/config-messagebus.h>
+
+using namespace mbus;
+using namespace messagebus;
+using namespace config;
+
+class Test : public vespalib::TestApp, public IConfigHandler {
+private:
+ RoutingSpec _spec;
+ bool checkHalf();
+ bool checkFull();
+ bool checkTables(uint32_t numTables);
+
+public:
+ int Main();
+ bool setupRouting(const RoutingSpec &spec);
+};
+
+TEST_APPHOOK(Test);
+
+bool
+Test::setupRouting(const RoutingSpec &spec)
+{
+ _spec = spec;
+ return true;
+}
+
+bool
+Test::checkTables(uint32_t numTables)
+{
+ if (!EXPECT_EQUAL(numTables, _spec.getNumTables())) return false;
+ if (numTables > 0) {
+ if (!EXPECT_EQUAL("foo", _spec.getTable(0).getProtocol())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(0).getNumHops())) return false;
+ if (!EXPECT_EQUAL("foo-h1", _spec.getTable(0).getHop(0).getName())) return false;
+ if (!EXPECT_EQUAL("foo-h1-sel", _spec.getTable(0).getHop(0).getSelector())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(0).getHop(0).getNumRecipients())) return false;
+ if (!EXPECT_EQUAL("foo-h1-r1", _spec.getTable(0).getHop(0).getRecipient(0))) return false;
+ if (!EXPECT_EQUAL("foo-h1-r2", _spec.getTable(0).getHop(0).getRecipient(1))) return false;
+ if (!EXPECT_EQUAL(true, _spec.getTable(0).getHop(0).getIgnoreResult())) return false;
+ if (!EXPECT_EQUAL("foo-h2", _spec.getTable(0).getHop(1).getName())) return false;
+ if (!EXPECT_EQUAL("foo-h2-sel", _spec.getTable(0).getHop(1).getSelector())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(0).getHop(1).getNumRecipients())) return false;
+ if (!EXPECT_EQUAL("foo-h2-r1", _spec.getTable(0).getHop(1).getRecipient(0))) return false;
+ if (!EXPECT_EQUAL("foo-h2-r2", _spec.getTable(0).getHop(1).getRecipient(1))) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(0).getNumRoutes())) return false;
+ if (!EXPECT_EQUAL("foo-r1", _spec.getTable(0).getRoute(0).getName())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(0).getRoute(0).getNumHops())) return false;
+ if (!EXPECT_EQUAL("foo-h1", _spec.getTable(0).getRoute(0).getHop(0))) return false;
+ if (!EXPECT_EQUAL("foo-h2", _spec.getTable(0).getRoute(0).getHop(1))) return false;
+ if (!EXPECT_EQUAL("foo-r2", _spec.getTable(0).getRoute(1).getName())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(0).getRoute(1).getNumHops())) return false;
+ if (!EXPECT_EQUAL("foo-h2", _spec.getTable(0).getRoute(1).getHop(0))) return false;
+ if (!EXPECT_EQUAL("foo-h1", _spec.getTable(0).getRoute(1).getHop(1))) return false;
+ }
+ if (numTables > 1) {
+ if (!EXPECT_EQUAL("bar", _spec.getTable(1).getProtocol())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(1).getNumHops())) return false;
+ if (!EXPECT_EQUAL("bar-h1", _spec.getTable(1).getHop(0).getName())) return false;
+ if (!EXPECT_EQUAL("bar-h1-sel", _spec.getTable(1).getHop(0).getSelector())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(1).getHop(0).getNumRecipients())) return false;
+ if (!EXPECT_EQUAL("bar-h1-r1", _spec.getTable(1).getHop(0).getRecipient(0))) return false;
+ if (!EXPECT_EQUAL("bar-h1-r2", _spec.getTable(1).getHop(0).getRecipient(1))) return false;
+ if (!EXPECT_EQUAL("bar-h2", _spec.getTable(1).getHop(1).getName())) return false;
+ if (!EXPECT_EQUAL("bar-h2-sel", _spec.getTable(1).getHop(1).getSelector())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(1).getHop(1).getNumRecipients())) return false;
+ if (!EXPECT_EQUAL("bar-h2-r1", _spec.getTable(1).getHop(1).getRecipient(0))) return false;
+ if (!EXPECT_EQUAL("bar-h2-r2", _spec.getTable(1).getHop(1).getRecipient(1))) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(1).getNumRoutes())) return false;
+ if (!EXPECT_EQUAL("bar-r1", _spec.getTable(1).getRoute(0).getName())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(1).getRoute(0).getNumHops())) return false;
+ if (!EXPECT_EQUAL("bar-h1", _spec.getTable(1).getRoute(0).getHop(0))) return false;
+ if (!EXPECT_EQUAL("bar-h2", _spec.getTable(1).getRoute(0).getHop(1))) return false;
+ if (!EXPECT_EQUAL("bar-r2", _spec.getTable(1).getRoute(1).getName())) return false;
+ if (!EXPECT_EQUAL(2u, _spec.getTable(1).getRoute(1).getNumHops())) return false;
+ if (!EXPECT_EQUAL("bar-h2", _spec.getTable(1).getRoute(1).getHop(0))) return false;
+ if (!EXPECT_EQUAL("bar-h1", _spec.getTable(1).getRoute(1).getHop(1))) return false;
+ }
+ return true;
+}
+
+bool
+Test::checkHalf()
+{
+ return _spec.getNumTables() == 1 && EXPECT_TRUE(checkTables(1));
+}
+
+bool
+Test::checkFull()
+{
+ return _spec.getNumTables() == 2 && EXPECT_TRUE(checkTables(2));
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("configagent_test");
+ EXPECT_TRUE(!checkHalf());
+ EXPECT_TRUE(!checkFull());
+ ConfigAgent agent(*this);
+ EXPECT_TRUE(!checkHalf());
+ EXPECT_TRUE(!checkFull());
+ agent.configure(FileConfigReader<MessagebusConfig>("full.cfg").read());
+ EXPECT_TRUE(!checkHalf());
+ EXPECT_TRUE(checkFull());
+ agent.configure(FileConfigReader<MessagebusConfig>("half.cfg").read());
+ EXPECT_TRUE(checkHalf());
+ EXPECT_TRUE(!checkFull());
+ agent.configure(FileConfigReader<MessagebusConfig>("full.cfg").read());
+ EXPECT_TRUE(checkFull());
+ EXPECT_TRUE(!checkHalf());
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/configagent/full.cfg b/messagebus/src/tests/configagent/full.cfg
new file mode 100644
index 00000000000..addfcd3c080
--- /dev/null
+++ b/messagebus/src/tests/configagent/full.cfg
@@ -0,0 +1,44 @@
+routingtable[2]
+routingtable[0].protocol "foo"
+routingtable[0].hop[2]
+routingtable[0].hop[0].name "foo-h1"
+routingtable[0].hop[0].selector "foo-h1-sel"
+routingtable[0].hop[0].recipient[2]
+routingtable[0].hop[0].recipient[0] "foo-h1-r1"
+routingtable[0].hop[0].recipient[1] "foo-h1-r2"
+routingtable[0].hop[0].ignoreresult true
+routingtable[0].hop[1].name "foo-h2"
+routingtable[0].hop[1].selector "foo-h2-sel"
+routingtable[0].hop[1].recipient[2]
+routingtable[0].hop[1].recipient[0] "foo-h2-r1"
+routingtable[0].hop[1].recipient[1] "foo-h2-r2"
+routingtable[0].route[2]
+routingtable[0].route[0].name "foo-r1"
+routingtable[0].route[0].hop[2]
+routingtable[0].route[0].hop[0] "foo-h1"
+routingtable[0].route[0].hop[1] "foo-h2"
+routingtable[0].route[1].name "foo-r2"
+routingtable[0].route[1].hop[2]
+routingtable[0].route[1].hop[0] "foo-h2"
+routingtable[0].route[1].hop[1] "foo-h1"
+routingtable[1].protocol "bar"
+routingtable[1].hop[2]
+routingtable[1].hop[0].name "bar-h1"
+routingtable[1].hop[0].selector "bar-h1-sel"
+routingtable[1].hop[0].recipient[2]
+routingtable[1].hop[0].recipient[0] "bar-h1-r1"
+routingtable[1].hop[0].recipient[1] "bar-h1-r2"
+routingtable[1].hop[1].name "bar-h2"
+routingtable[1].hop[1].selector "bar-h2-sel"
+routingtable[1].hop[1].recipient[2]
+routingtable[1].hop[1].recipient[0] "bar-h2-r1"
+routingtable[1].hop[1].recipient[1] "bar-h2-r2"
+routingtable[1].route[2]
+routingtable[1].route[0].name "bar-r1"
+routingtable[1].route[0].hop[2]
+routingtable[1].route[0].hop[0] "bar-h1"
+routingtable[1].route[0].hop[1] "bar-h2"
+routingtable[1].route[1].name "bar-r2"
+routingtable[1].route[1].hop[2]
+routingtable[1].route[1].hop[0] "bar-h2"
+routingtable[1].route[1].hop[1] "bar-h1"
diff --git a/messagebus/src/tests/configagent/half.cfg b/messagebus/src/tests/configagent/half.cfg
new file mode 100644
index 00000000000..12570a9a557
--- /dev/null
+++ b/messagebus/src/tests/configagent/half.cfg
@@ -0,0 +1,23 @@
+routingtable[1]
+routingtable[0].protocol "foo"
+routingtable[0].hop[2]
+routingtable[0].hop[0].name "foo-h1"
+routingtable[0].hop[0].selector "foo-h1-sel"
+routingtable[0].hop[0].recipient[2]
+routingtable[0].hop[0].recipient[0] "foo-h1-r1"
+routingtable[0].hop[0].recipient[1] "foo-h1-r2"
+routingtable[0].hop[0].ignoreresult true
+routingtable[0].hop[1].name "foo-h2"
+routingtable[0].hop[1].selector "foo-h2-sel"
+routingtable[0].hop[1].recipient[2]
+routingtable[0].hop[1].recipient[0] "foo-h2-r1"
+routingtable[0].hop[1].recipient[1] "foo-h2-r2"
+routingtable[0].route[2]
+routingtable[0].route[0].name "foo-r1"
+routingtable[0].route[0].hop[2]
+routingtable[0].route[0].hop[0] "foo-h1"
+routingtable[0].route[0].hop[1] "foo-h2"
+routingtable[0].route[1].name "foo-r2"
+routingtable[0].route[1].hop[2]
+routingtable[0].route[1].hop[0] "foo-h2"
+routingtable[0].route[1].hop[1] "foo-h1"
diff --git a/messagebus/src/tests/context/.gitignore b/messagebus/src/tests/context/.gitignore
new file mode 100644
index 00000000000..9b9771b64f0
--- /dev/null
+++ b/messagebus/src/tests/context/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+context_test
+messagebus_context_test_app
diff --git a/messagebus/src/tests/context/CMakeLists.txt b/messagebus/src/tests/context/CMakeLists.txt
new file mode 100644
index 00000000000..d9a85a1b8c9
--- /dev/null
+++ b/messagebus/src/tests/context/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_context_test_app
+ SOURCES
+ context.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_context_test_app COMMAND messagebus_context_test_app)
diff --git a/messagebus/src/tests/context/DESC b/messagebus/src/tests/context/DESC
new file mode 100644
index 00000000000..5a40cc4f9a1
--- /dev/null
+++ b/messagebus/src/tests/context/DESC
@@ -0,0 +1 @@
+context test. Take a look at context.cpp for details.
diff --git a/messagebus/src/tests/context/FILES b/messagebus/src/tests/context/FILES
new file mode 100644
index 00000000000..a4c148657b9
--- /dev/null
+++ b/messagebus/src/tests/context/FILES
@@ -0,0 +1 @@
+context.cpp
diff --git a/messagebus/src/tests/context/context.cpp b/messagebus/src/tests/context/context.cpp
new file mode 100644
index 00000000000..ccb136e7b81
--- /dev/null
+++ b/messagebus/src/tests/context/context.cpp
@@ -0,0 +1,102 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("context_test");
+
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+struct Handler : public IMessageHandler
+{
+ DestinationSession::UP session;
+
+ Handler(MessageBus &mb) : session() {
+ session = mb.createDestinationSession("session", true, *this);
+ }
+ ~Handler() {
+ session.reset();
+ }
+ virtual void handleMessage(Message::UP msg) {
+ session->acknowledge(std::move(msg));
+ }
+};
+
+RoutingSpec getRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("test", "test/session"))
+ .addRoute(RouteSpec("test").addHop("test")));
+}
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("context_test");
+
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+ TestServer dst(Identity("test"), getRouting(), slobrok);
+ Handler handler(dst.mb);
+
+ ASSERT_TRUE(src.waitSlobrok("test/session"));
+
+ RoutableQueue queue;
+ SourceSessionParams params;
+ params.setThrottlePolicy(IThrottlePolicy::SP());
+ SourceSession::UP ss = src.mb.createSourceSession(queue, params);
+
+ {
+ Message::UP msg(new SimpleMessage("test", true, 1));
+ msg->setContext(Context((uint64_t)10));
+ ss->send(std::move(msg), "test");
+ }
+ {
+ Message::UP msg(new SimpleMessage("test", true, 1));
+ msg->setContext(Context((uint64_t)20));
+ ss->send(std::move(msg), "test");
+ }
+ {
+ Message::UP msg(new SimpleMessage("test", true, 1));
+ msg->setContext(Context((uint64_t)30));
+ ss->send(std::move(msg), "test");
+ }
+ for (uint32_t i = 0; i < 1000; ++i) {
+ if (queue.size() == 3) {
+ break;
+ }
+ FastOS_Thread::Sleep(10);
+ }
+ EXPECT_EQUAL(queue.size(), 3u);
+ {
+ Reply::UP reply = Reply::UP((Reply*)queue.dequeue(0).release());
+ ASSERT_TRUE(reply.get() != 0);
+ EXPECT_EQUAL(reply->getContext().value.UINT64, 10u);
+ }
+ {
+ Reply::UP reply = Reply::UP((Reply*)queue.dequeue(0).release());
+ ASSERT_TRUE(reply.get() != 0);
+ EXPECT_EQUAL(reply->getContext().value.UINT64, 20u);
+ }
+ {
+ Reply::UP reply = Reply::UP((Reply*)queue.dequeue(0).release());
+ ASSERT_TRUE(reply.get() != 0);
+ EXPECT_EQUAL(reply->getContext().value.UINT64, 30u);
+ }
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/create-test.sh b/messagebus/src/tests/create-test.sh
new file mode 100755
index 00000000000..b5406dd24bd
--- /dev/null
+++ b/messagebus/src/tests/create-test.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+gen_ignore_file() {
+ echo "generating '$1' ..."
+ echo ".depend" > $1
+ echo "Makefile" >> $1
+ echo "${test}_test" >> $1
+}
+
+gen_project_file() {
+ echo "generating '$1' ..."
+ echo "APPLICATION ${test}_test" > $1
+ echo "OBJS $test" >> $1
+ echo "LIBS messagebus/testlib/messagebus-test" >> $1
+ echo "LIBS messagebus/messagebus" >> $1
+ echo "EXTERNALLIBS slobrokserver slobrok fnet vespalib config vespalog" >> $1
+ echo "" >> $1
+ echo "CUSTOMMAKE" >> $1
+ echo "test: depend ${test}_test" >> $1
+ echo -e "\t@./${test}_test" >> $1
+}
+
+gen_source() {
+ echo "generating '$1' ..."
+ echo "#include <vespa/log/log.h>" > $1
+ echo "LOG_SETUP(\"${test}_test\");" >> $1
+ echo "#include <vespa/fastos/fastos.h>" >> $1
+ echo "#include <vespa/vespalib/testkit/testapp.h>" >> $1
+ echo "" >> $1
+ echo "// using namespace mbus;" >> $1
+ echo "" >> $1
+ echo "TEST_SETUP(Test);" >> $1
+ echo "" >> $1
+ echo "int" >> $1
+ echo "Test::Main()" >> $1
+ echo "{" >> $1
+ echo " TEST_INIT(\"${test}_test\");" >> $1
+ echo " TEST_DONE();" >> $1
+ echo "}" >> $1
+}
+
+gen_desc() {
+ echo "generating '$1' ..."
+ echo "$test test. Take a look at $test.cpp for details." > $1
+}
+
+gen_file_list() {
+ echo "generating '$1' ..."
+ echo "$test.cpp" > $1
+}
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 <name>"
+ echo " name: name of the test to create"
+ exit 1
+fi
+
+test=$1
+if [ -e $test ]; then
+ echo "$test already present, don't want to mess it up..."
+ exit 1
+fi
+
+echo "creating directory '$test' ..."
+mkdir -p $test || exit 1
+cd $test || exit 1
+test=`basename $test`
+
+gen_ignore_file .cvsignore
+gen_project_file fastos.project
+gen_source $test.cpp
+gen_desc DESC
+gen_file_list FILES
diff --git a/messagebus/src/tests/emptyreply/.gitignore b/messagebus/src/tests/emptyreply/.gitignore
new file mode 100644
index 00000000000..bfaf7f812cf
--- /dev/null
+++ b/messagebus/src/tests/emptyreply/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+emptyreply_test
+messagebus_emptyreply_test_app
diff --git a/messagebus/src/tests/emptyreply/CMakeLists.txt b/messagebus/src/tests/emptyreply/CMakeLists.txt
new file mode 100644
index 00000000000..17f26719396
--- /dev/null
+++ b/messagebus/src/tests/emptyreply/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_emptyreply_test_app
+ SOURCES
+ emptyreply.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_emptyreply_test_app COMMAND messagebus_emptyreply_test_app)
diff --git a/messagebus/src/tests/emptyreply/DESC b/messagebus/src/tests/emptyreply/DESC
new file mode 100644
index 00000000000..4db41c3c671
--- /dev/null
+++ b/messagebus/src/tests/emptyreply/DESC
@@ -0,0 +1 @@
+Simple test of the EmptyReply class.
diff --git a/messagebus/src/tests/emptyreply/FILES b/messagebus/src/tests/emptyreply/FILES
new file mode 100644
index 00000000000..5fbc80bb05c
--- /dev/null
+++ b/messagebus/src/tests/emptyreply/FILES
@@ -0,0 +1 @@
+emptyreply.cpp
diff --git a/messagebus/src/tests/emptyreply/emptyreply.cpp b/messagebus/src/tests/emptyreply/emptyreply.cpp
new file mode 100644
index 00000000000..711a21edd17
--- /dev/null
+++ b/messagebus/src/tests/emptyreply/emptyreply.cpp
@@ -0,0 +1,21 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("emptyreply_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/emptyreply.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("emptyreply_test");
+ Reply::UP empty(new EmptyReply());
+ EXPECT_TRUE(empty->isReply());
+ EXPECT_TRUE(empty->getProtocol() == "");
+ EXPECT_TRUE(empty->getType() == 0);
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/error/.gitignore b/messagebus/src/tests/error/.gitignore
new file mode 100644
index 00000000000..3023dc1cb7a
--- /dev/null
+++ b/messagebus/src/tests/error/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+error_test
+messagebus_error_test_app
diff --git a/messagebus/src/tests/error/CMakeLists.txt b/messagebus/src/tests/error/CMakeLists.txt
new file mode 100644
index 00000000000..7a5ea78fab3
--- /dev/null
+++ b/messagebus/src/tests/error/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_error_test_app
+ SOURCES
+ error.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_error_test_app COMMAND messagebus_error_test_app)
diff --git a/messagebus/src/tests/error/DESC b/messagebus/src/tests/error/DESC
new file mode 100644
index 00000000000..87bd0cc23fa
--- /dev/null
+++ b/messagebus/src/tests/error/DESC
@@ -0,0 +1 @@
+error test. Take a look at error.cpp for details.
diff --git a/messagebus/src/tests/error/FILES b/messagebus/src/tests/error/FILES
new file mode 100644
index 00000000000..779aee64a2c
--- /dev/null
+++ b/messagebus/src/tests/error/FILES
@@ -0,0 +1 @@
+error.cpp
diff --git a/messagebus/src/tests/error/error.cpp b/messagebus/src/tests/error/error.cpp
new file mode 100644
index 00000000000..d5779aadeeb
--- /dev/null
+++ b/messagebus/src/tests/error/error.cpp
@@ -0,0 +1,83 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("error_test");
+
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+RoutingSpec getRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("pxy", "test/pxy/session"))
+ .addHop(HopSpec("dst", "test/dst/session"))
+ .addRoute(RouteSpec("test").addHop("pxy").addHop("dst")));
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("error_test");
+
+ Slobrok slobrok;
+ TestServer srcNet(Identity("test/src"), getRouting(), slobrok);
+ TestServer pxyNet(Identity("test/pxy"), getRouting(), slobrok);
+ TestServer dstNet(Identity("test/dst"), getRouting(), slobrok);
+
+ Receptor src;
+ Receptor pxy;
+ Receptor dst;
+
+ SourceSession::UP ss = srcNet.mb.createSourceSession(src, SourceSessionParams());
+ IntermediateSession::UP is = pxyNet.mb.createIntermediateSession("session", true, pxy, pxy);
+ DestinationSession::UP ds = dstNet.mb.createDestinationSession("session", true, dst);
+
+ ASSERT_TRUE(srcNet.waitSlobrok("test/pxy/session"));
+ ASSERT_TRUE(srcNet.waitSlobrok("test/dst/session"));
+ ASSERT_TRUE(pxyNet.waitSlobrok("test/dst/session"));
+
+ for (int i = 0; i < 5; i++) {
+ ASSERT_TRUE(ss->send(SimpleMessage::UP(new SimpleMessage("test message")), "test").isAccepted());
+ Message::UP msg = pxy.getMessage();
+ ASSERT_TRUE(msg.get() != 0);
+ is->forward(std::move(msg));
+
+ msg = dst.getMessage();
+ ASSERT_TRUE(msg.get() != 0);
+ Reply::UP reply(new EmptyReply());
+ msg->swapState(*reply);
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR, "fatality"));
+ ds->reply(std::move(reply));
+
+ reply = pxy.getReply();
+ ASSERT_TRUE(reply.get() != 0);
+ EXPECT_EQUAL(reply->getNumErrors(), 1u);
+ EXPECT_EQUAL(reply->getError(0).getService(), "test/dst/session");
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR, "fatality"));
+ is->forward(std::move(reply));
+
+ reply = src.getReply();
+ ASSERT_TRUE(reply.get() != 0);
+ EXPECT_EQUAL(reply->getNumErrors(), 2u);
+ EXPECT_EQUAL(reply->getError(0).getService(), "test/dst/session");
+ EXPECT_EQUAL(reply->getError(1).getService(), "test/pxy/session");
+ }
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/identity/.gitignore b/messagebus/src/tests/identity/.gitignore
new file mode 100644
index 00000000000..9dd069cdb7a
--- /dev/null
+++ b/messagebus/src/tests/identity/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+identity_test
+messagebus_identity_test_app
diff --git a/messagebus/src/tests/identity/CMakeLists.txt b/messagebus/src/tests/identity/CMakeLists.txt
new file mode 100644
index 00000000000..66aea485746
--- /dev/null
+++ b/messagebus/src/tests/identity/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_identity_test_app
+ SOURCES
+ identity.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_identity_test_app COMMAND messagebus_identity_test_app)
diff --git a/messagebus/src/tests/identity/DESC b/messagebus/src/tests/identity/DESC
new file mode 100644
index 00000000000..a1fdfb95f8b
--- /dev/null
+++ b/messagebus/src/tests/identity/DESC
@@ -0,0 +1 @@
+Test that the network identity may be obtained from config.
diff --git a/messagebus/src/tests/identity/FILES b/messagebus/src/tests/identity/FILES
new file mode 100644
index 00000000000..484caddcac7
--- /dev/null
+++ b/messagebus/src/tests/identity/FILES
@@ -0,0 +1,2 @@
+identity.cpp
+test.cfg
diff --git a/messagebus/src/tests/identity/identity.cpp b/messagebus/src/tests/identity/identity.cpp
new file mode 100644
index 00000000000..95c499e3ff2
--- /dev/null
+++ b/messagebus/src/tests/identity/identity.cpp
@@ -0,0 +1,43 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("identity_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/network/identity.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("identity_test");
+ Identity ident("foo/bar/baz");
+ EXPECT_TRUE(ident.getServicePrefix() == "foo/bar/baz");
+ {
+ std::vector<string> tmp = Identity::split("foo/bar/baz");
+ ASSERT_TRUE(tmp.size() == 3);
+ EXPECT_TRUE(tmp[0] == "foo");
+ EXPECT_TRUE(tmp[1] == "bar");
+ EXPECT_TRUE(tmp[2] == "baz");
+ }
+ {
+ std::vector<string> tmp = Identity::split("//");
+ ASSERT_TRUE(tmp.size() == 3);
+ EXPECT_TRUE(tmp[0] == "");
+ EXPECT_TRUE(tmp[1] == "");
+ EXPECT_TRUE(tmp[2] == "");
+ }
+ {
+ std::vector<string> tmp = Identity::split("foo");
+ ASSERT_TRUE(tmp.size() == 1);
+ EXPECT_TRUE(tmp[0] == "foo");
+ }
+ {
+ std::vector<string> tmp = Identity::split("");
+ ASSERT_TRUE(tmp.size() == 1);
+ EXPECT_TRUE(tmp[0] == "");
+ }
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/loadbalance/.gitignore b/messagebus/src/tests/loadbalance/.gitignore
new file mode 100644
index 00000000000..d1cbb5977f1
--- /dev/null
+++ b/messagebus/src/tests/loadbalance/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+loadbalance_test
+messagebus_loadbalance_test_app
diff --git a/messagebus/src/tests/loadbalance/CMakeLists.txt b/messagebus/src/tests/loadbalance/CMakeLists.txt
new file mode 100644
index 00000000000..68d0483ce5d
--- /dev/null
+++ b/messagebus/src/tests/loadbalance/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_loadbalance_test_app
+ SOURCES
+ loadbalance.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_loadbalance_test_app COMMAND messagebus_loadbalance_test_app)
diff --git a/messagebus/src/tests/loadbalance/DESC b/messagebus/src/tests/loadbalance/DESC
new file mode 100644
index 00000000000..67009371472
--- /dev/null
+++ b/messagebus/src/tests/loadbalance/DESC
@@ -0,0 +1,2 @@
+Test that service patterns with '*' performs load balancing between
+the services the pattern resolves to.
diff --git a/messagebus/src/tests/loadbalance/FILES b/messagebus/src/tests/loadbalance/FILES
new file mode 100644
index 00000000000..6b28cce1716
--- /dev/null
+++ b/messagebus/src/tests/loadbalance/FILES
@@ -0,0 +1 @@
+loadbalance.cpp
diff --git a/messagebus/src/tests/loadbalance/loadbalance.cpp b/messagebus/src/tests/loadbalance/loadbalance.cpp
new file mode 100644
index 00000000000..f49ca13708c
--- /dev/null
+++ b/messagebus/src/tests/loadbalance/loadbalance.cpp
@@ -0,0 +1,90 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("loadbalance_test");
+
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+
+using namespace mbus;
+
+struct Handler : public IMessageHandler
+{
+ DestinationSession::UP session;
+ uint32_t cnt;
+
+ Handler(MessageBus &mb) : session(), cnt(0) {
+ session = mb.createDestinationSession("session", true, *this);
+ }
+ ~Handler() {
+ session.reset();
+ }
+ virtual void handleMessage(Message::UP msg) {
+ ++cnt;
+ session->acknowledge(std::move(msg));
+ }
+};
+
+RoutingSpec getRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("dst", "test/*/session"))
+ .addRoute(RouteSpec("test").addHop("dst")));
+}
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("loadbalance_test");
+
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+ TestServer dst1(Identity("test/dst1"), getRouting(), slobrok);
+ TestServer dst2(Identity("test/dst2"), getRouting(), slobrok);
+ TestServer dst3(Identity("test/dst3"), getRouting(), slobrok);
+
+ Handler h1(dst1.mb);
+ Handler h2(dst2.mb);
+ Handler h3(dst3.mb);
+
+ ASSERT_TRUE(src.waitSlobrok("test/dst1/session"));
+ ASSERT_TRUE(src.waitSlobrok("test/dst2/session"));
+ ASSERT_TRUE(src.waitSlobrok("test/dst3/session"));
+
+ RoutableQueue queue;
+ SourceSessionParams params;
+ params.setTimeout(30.0);
+ params.setThrottlePolicy(IThrottlePolicy::SP());
+ SourceSession::UP ss = src.mb.createSourceSession(queue, params);
+
+ uint32_t msgCnt = 90;
+ ASSERT_TRUE(msgCnt % 3 == 0);
+ for (uint32_t i = 0; i < msgCnt; ++i) {
+ ss->send(Message::UP(new SimpleMessage("test")), "test");
+ }
+ for (uint32_t i = 0; i < 1000; ++i) {
+ if (queue.size() == msgCnt) {
+ break;
+ }
+ FastOS_Thread::Sleep(10);
+ }
+ EXPECT_TRUE(queue.size() == msgCnt);
+ EXPECT_TRUE(h1.cnt == msgCnt / 3);
+ EXPECT_TRUE(h2.cnt == msgCnt / 3);
+ EXPECT_TRUE(h3.cnt == msgCnt / 3);
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/messagebus/.gitignore b/messagebus/src/tests/messagebus/.gitignore
new file mode 100644
index 00000000000..b8b2aa5313c
--- /dev/null
+++ b/messagebus/src/tests/messagebus/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+messagebus_test
+messagebus_messagebus_test_app
diff --git a/messagebus/src/tests/messagebus/CMakeLists.txt b/messagebus/src/tests/messagebus/CMakeLists.txt
new file mode 100644
index 00000000000..fc44bb60069
--- /dev/null
+++ b/messagebus/src/tests/messagebus/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_messagebus_test_app
+ SOURCES
+ messagebus.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_messagebus_test_app COMMAND messagebus_messagebus_test_app)
diff --git a/messagebus/src/tests/messagebus/DESC b/messagebus/src/tests/messagebus/DESC
new file mode 100644
index 00000000000..19eb03c7048
--- /dev/null
+++ b/messagebus/src/tests/messagebus/DESC
@@ -0,0 +1 @@
+Generic messagebus test ported from Java.
diff --git a/messagebus/src/tests/messagebus/FILES b/messagebus/src/tests/messagebus/FILES
new file mode 100644
index 00000000000..0430f52149a
--- /dev/null
+++ b/messagebus/src/tests/messagebus/FILES
@@ -0,0 +1 @@
+messagebus.cpp
diff --git a/messagebus/src/tests/messagebus/messagebus.cpp b/messagebus/src/tests/messagebus/messagebus.cpp
new file mode 100644
index 00000000000..a887759ac02
--- /dev/null
+++ b/messagebus/src/tests/messagebus/messagebus.cpp
@@ -0,0 +1,538 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("messagebus_test");
+
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/error.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/routing/route.h>
+#include <vespa/messagebus/routing/routingcontext.h>
+#include <vespa/messagebus/routing/routingnodeiterator.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/vstringfmt.h>
+
+using namespace mbus;
+
+struct Base {
+ RoutableQueue queue;
+ Base() : queue() {}
+ virtual ~Base() {
+ while (queue.size() > 0) {
+ Routable::UP r = queue.dequeue(0);
+ r->getCallStack().discard();
+ }
+ }
+ RoutingSpec getRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("DocProc", "docproc/*/session"))
+ .addHop(HopSpec("Search", "search/[All]/[Hash]/session")
+ .addRecipient("search/r.0/c.0/session")
+ .addRecipient("search/r.0/c.1/session")
+ .addRecipient("search/r.1/c.0/session")
+ .addRecipient("search/r.1/c.1/session"))
+ .addRoute(RouteSpec("Index").addHop("DocProc").addHop("Search"))
+ .addRoute(RouteSpec("DocProc").addHop("DocProc"))
+ .addRoute(RouteSpec("Search").addHop("Search")));
+ }
+ bool waitQueueSize(uint32_t size) {
+ for (uint32_t i = 0; i < 1000; ++i) {
+ if (queue.size() == size) {
+ return true;
+ }
+ FastOS_Thread::Sleep(10);
+ }
+ return false;
+ }
+};
+
+struct Client : public Base {
+ typedef std::unique_ptr<Client> UP;
+ TestServer server;
+ SourceSession::UP session;
+ Client(Slobrok &slobrok)
+ : Base(), server(Identity(""), getRouting(), slobrok), session()
+ {
+ SourceSessionParams params;
+ params.setThrottlePolicy(IThrottlePolicy::SP());
+ session = server.mb.createSourceSession(queue, params);
+
+ }
+};
+
+struct Server : public Base {
+ TestServer server;
+ Server(const string &name, Slobrok &slobrok)
+ : Base(), server(Identity(name), getRouting(), slobrok)
+ {
+ // empty
+ }
+};
+
+struct DocProc : public Server {
+ typedef std::unique_ptr<DocProc> UP;
+ IntermediateSession::UP session;
+ DocProc(const string &name, Slobrok &slobrok)
+ : Server(name, slobrok), session()
+ {
+ session = server.mb.createIntermediateSession("session", true, queue, queue);
+ }
+};
+
+struct Search : public Server {
+ typedef std::unique_ptr<Search> UP;
+ DestinationSession::UP session;
+ Search(const string &name, Slobrok &slobrok)
+ : Server(name, slobrok), session()
+ {
+ session = server.mb.createDestinationSession("session", true, queue);
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+class Test : public vespalib::TestApp {
+private:
+ Slobrok::UP slobrok;
+ Client::UP client;
+ DocProc::UP dp0;
+ DocProc::UP dp1;
+ DocProc::UP dp2;
+ Search::UP search00;
+ Search::UP search01;
+ Search::UP search10;
+ Search::UP search11;
+ std::vector<DocProc*> dpVec;
+ std::vector<Search*> searchVec;
+
+public:
+ int Main();
+ void testSendToAny();
+ void testSendToCol();
+ void testSendToAnyThenCol();
+ void testDirectHop();
+ void testDirectRoute();
+ void testRoutingPolicyCache();
+ void debugTrace();
+
+private:
+ void setup();
+ void teardown();
+
+ void assertSrc(Client& src);
+ void assertItr(DocProc& itr);
+ void assertDst(Search& dst);
+};
+
+TEST_APPHOOK(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("messagebus_test");
+
+ testSendToAny(); TEST_FLUSH();
+ testSendToCol(); TEST_FLUSH();
+ testSendToAnyThenCol(); TEST_FLUSH();
+ testDirectHop(); TEST_FLUSH();
+ testDirectRoute(); TEST_FLUSH();
+ testRoutingPolicyCache(); TEST_FLUSH();
+ debugTrace(); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+void
+Test::setup()
+{
+ slobrok.reset(new Slobrok());
+ client.reset(new Client(*slobrok));
+ dp0.reset(new DocProc("docproc/0", *slobrok));
+ dp1.reset(new DocProc("docproc/1", *slobrok));
+ dp2.reset(new DocProc("docproc/2", *slobrok));
+ search00.reset(new Search("search/r.0/c.0", *slobrok));
+ search01.reset(new Search("search/r.0/c.1", *slobrok));
+ search10.reset(new Search("search/r.1/c.0", *slobrok));
+ search11.reset(new Search("search/r.1/c.1", *slobrok));
+ dpVec.push_back(dp0.get());
+ dpVec.push_back(dp1.get());
+ dpVec.push_back(dp2.get());
+ searchVec.push_back(search00.get());
+ searchVec.push_back(search01.get());
+ searchVec.push_back(search10.get());
+ searchVec.push_back(search11.get());
+ ASSERT_TRUE(client->server.waitSlobrok("docproc/0/session"));
+ ASSERT_TRUE(client->server.waitSlobrok("docproc/1/session"));
+ ASSERT_TRUE(client->server.waitSlobrok("docproc/2/session"));
+ ASSERT_TRUE(client->server.waitSlobrok("search/r.0/c.0/session"));
+ ASSERT_TRUE(client->server.waitSlobrok("search/r.0/c.1/session"));
+ ASSERT_TRUE(client->server.waitSlobrok("search/r.1/c.0/session"));
+ ASSERT_TRUE(client->server.waitSlobrok("search/r.1/c.1/session"));
+ ASSERT_TRUE(dp0->server.waitSlobrok("search/r.0/c.0/session"));
+ ASSERT_TRUE(dp0->server.waitSlobrok("search/r.0/c.1/session"));
+ ASSERT_TRUE(dp0->server.waitSlobrok("search/r.1/c.0/session"));
+ ASSERT_TRUE(dp0->server.waitSlobrok("search/r.1/c.1/session"));
+ ASSERT_TRUE(dp1->server.waitSlobrok("search/r.0/c.0/session"));
+ ASSERT_TRUE(dp1->server.waitSlobrok("search/r.0/c.1/session"));
+ ASSERT_TRUE(dp1->server.waitSlobrok("search/r.1/c.0/session"));
+ ASSERT_TRUE(dp1->server.waitSlobrok("search/r.1/c.1/session"));
+ ASSERT_TRUE(dp2->server.waitSlobrok("search/r.0/c.0/session"));
+ ASSERT_TRUE(dp2->server.waitSlobrok("search/r.0/c.1/session"));
+ ASSERT_TRUE(dp2->server.waitSlobrok("search/r.1/c.0/session"));
+ ASSERT_TRUE(dp2->server.waitSlobrok("search/r.1/c.1/session"));
+}
+
+void Test::teardown()
+{
+ dpVec.clear();
+ searchVec.clear();
+ search11.reset();
+ search10.reset();
+ search01.reset();
+ search00.reset();
+ dp2.reset();
+ dp1.reset();
+ dp0.reset();
+ client.reset();
+ slobrok.reset();
+}
+
+void
+Test::testSendToAny()
+{
+ setup();
+ for (uint32_t i = 0; i < 300; ++i) {
+ Message::UP msg(new SimpleMessage("test"));
+ EXPECT_TRUE(client->session->send(std::move(msg), "DocProc").isAccepted());
+ }
+ EXPECT_TRUE(dp0->waitQueueSize(100));
+ EXPECT_TRUE(dp1->waitQueueSize(100));
+ EXPECT_TRUE(dp2->waitQueueSize(100));
+ for (uint32_t i = 0; i < dpVec.size(); ++i) {
+ DocProc *p = dpVec[i];
+ while (p->queue.size() > 0) {
+ Routable::UP msg = p->queue.dequeue(0);
+ ASSERT_TRUE(msg.get() != 0);
+ Reply::UP reply(new EmptyReply());
+ msg->swapState(*reply);
+ reply->addError(Error(ErrorCode::FATAL_ERROR, ""));
+ p->session->forward(std::move(reply));
+ }
+ }
+ EXPECT_TRUE(client->waitQueueSize(300));
+ while (client->queue.size() > 0) {
+ Routable::UP reply = client->queue.dequeue(0);
+ ASSERT_TRUE(reply.get() != 0);
+ ASSERT_TRUE(reply->isReply());
+ EXPECT_TRUE(static_cast<Reply&>(*reply).getNumErrors() == 1);
+ }
+ teardown();
+}
+
+void
+Test::testSendToCol()
+{
+ setup();
+ ASSERT_TRUE(SimpleMessage("msg").getHash() % 2 == 0);
+ for (uint32_t i = 0; i < 150; ++i) {
+ Message::UP msg(new SimpleMessage("msg"));
+ EXPECT_TRUE(client->session->send(std::move(msg), "Search").isAccepted());
+ }
+ EXPECT_TRUE(search00->waitQueueSize(150));
+ EXPECT_TRUE(search01->waitQueueSize(0));
+ EXPECT_TRUE(search10->waitQueueSize(150));
+ EXPECT_TRUE(search11->waitQueueSize(0));
+ ASSERT_TRUE(SimpleMessage("msh").getHash() % 2 == 1);
+ for (uint32_t i = 0; i < 150; ++i) {
+ Message::UP msg(new SimpleMessage("msh"));
+ ASSERT_TRUE(client->session->send(std::move(msg), "Search").isAccepted());
+ }
+ EXPECT_TRUE(search00->waitQueueSize(150));
+ EXPECT_TRUE(search01->waitQueueSize(150));
+ EXPECT_TRUE(search10->waitQueueSize(150));
+ EXPECT_TRUE(search11->waitQueueSize(150));
+ for (uint32_t i = 0; i < searchVec.size(); ++i) {
+ Search *s = searchVec[i];
+ while (s->queue.size() > 0) {
+ Routable::UP msg = s->queue.dequeue(0);
+ ASSERT_TRUE(msg.get() != 0);
+ Reply::UP reply(new EmptyReply());
+ msg->swapState(*reply);
+ s->session->reply(std::move(reply));
+ }
+ }
+ client->waitQueueSize(300);
+ FastOS_Thread::Sleep(100);
+ client->waitQueueSize(300);
+ while (client->queue.size() > 0) {
+ Routable::UP reply = client->queue.dequeue(0);
+ ASSERT_TRUE(reply.get() != 0);
+ ASSERT_TRUE(reply->isReply());
+ EXPECT_TRUE(static_cast<Reply&>(*reply).getNumErrors() == 0);
+ }
+ teardown();
+}
+
+void
+Test::testSendToAnyThenCol()
+{
+ setup();
+ ASSERT_TRUE(SimpleMessage("msg").getHash() % 2 == 0);
+ for (uint32_t i = 0; i < 150; ++i) {
+ Message::UP msg(new SimpleMessage("msg"));
+ EXPECT_TRUE(client->session->send(std::move(msg), "Index").isAccepted());
+ }
+ EXPECT_TRUE(dp0->waitQueueSize(50));
+ EXPECT_TRUE(dp1->waitQueueSize(50));
+ EXPECT_TRUE(dp2->waitQueueSize(50));
+ for (uint32_t i = 0; i < dpVec.size(); ++i) {
+ DocProc *p = dpVec[i];
+ while (p->queue.size() > 0) {
+ Routable::UP r = p->queue.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ p->session->forward(std::move(r));
+ }
+ }
+ EXPECT_TRUE(search00->waitQueueSize(150));
+ EXPECT_TRUE(search01->waitQueueSize(0));
+ EXPECT_TRUE(search10->waitQueueSize(150));
+ EXPECT_TRUE(search11->waitQueueSize(0));
+ ASSERT_TRUE(SimpleMessage("msh").getHash() % 2 == 1);
+ for (uint32_t i = 0; i < 150; ++i) {
+ Message::UP msg(new SimpleMessage("msh"));
+ ASSERT_TRUE(client->session->send(std::move(msg), "Index").isAccepted());
+ }
+ EXPECT_TRUE(dp0->waitQueueSize(50));
+ EXPECT_TRUE(dp1->waitQueueSize(50));
+ EXPECT_TRUE(dp2->waitQueueSize(50));
+ for (uint32_t i = 0; i < dpVec.size(); ++i) {
+ DocProc *p = dpVec[i];
+ while (p->queue.size() > 0) {
+ Routable::UP r = p->queue.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ p->session->forward(std::move(r));
+ }
+ }
+ EXPECT_TRUE(search00->waitQueueSize(150));
+ EXPECT_TRUE(search01->waitQueueSize(150));
+ EXPECT_TRUE(search10->waitQueueSize(150));
+ EXPECT_TRUE(search11->waitQueueSize(150));
+ for (uint32_t i = 0; i < searchVec.size(); ++i) {
+ Search *s = searchVec[i];
+ while (s->queue.size() > 0) {
+ Routable::UP msg = s->queue.dequeue(0);
+ ASSERT_TRUE(msg.get() != 0);
+ Reply::UP reply(new EmptyReply());
+ msg->swapState(*reply);
+ s->session->reply(std::move(reply));
+ }
+ }
+ EXPECT_TRUE(dp0->waitQueueSize(100));
+ EXPECT_TRUE(dp1->waitQueueSize(100));
+ EXPECT_TRUE(dp2->waitQueueSize(100));
+ for (uint32_t i = 0; i < dpVec.size(); ++i) {
+ DocProc *p = dpVec[i];
+ while (p->queue.size() > 0) {
+ Routable::UP r = p->queue.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ p->session->forward(std::move(r));
+ }
+ }
+ client->waitQueueSize(300);
+ FastOS_Thread::Sleep(100);
+ client->waitQueueSize(300);
+ while (client->queue.size() > 0) {
+ Routable::UP reply = client->queue.dequeue(0);
+ ASSERT_TRUE(reply.get() != 0);
+ ASSERT_TRUE(reply->isReply());
+ EXPECT_TRUE(static_cast<Reply&>(*reply).getNumErrors() == 0);
+ }
+ teardown();
+}
+
+void
+Test::testDirectHop()
+{
+ setup();
+ for (int row = 0; row < 2; row++) {
+ for (int col = 0; col < 2; col++) {
+ Search* dst = searchVec[row * 2 + col];
+
+ // Send using name.
+ ASSERT_TRUE(client->session->send(
+ Message::UP(new SimpleMessage("empty")),
+ Route().addHop(vespalib::make_vespa_string("search/r.%d/c.%d/session", row, col)))
+ .isAccepted());
+ assertDst(*dst);
+ assertSrc(*client);
+
+ // Send using address.
+ ASSERT_TRUE(client->session->send(
+ Message::UP(new SimpleMessage("empty")),
+ Route().addHop(Hop(dst->session->getConnectionSpec().c_str())))
+ .isAccepted());
+ assertDst(*dst);
+ assertSrc(*client);
+ }
+ }
+ teardown();
+}
+
+void
+Test::testDirectRoute()
+{
+ setup();
+ ASSERT_TRUE(client->session->send(
+ Message::UP(new SimpleMessage("empty")),
+ Route()
+ .addHop(Hop("docproc/0/session"))
+ .addHop(Hop(dp0->session->getConnectionSpec()))
+ .addHop(Hop("docproc/1/session"))
+ .addHop(Hop(dp1->session->getConnectionSpec()))
+ .addHop(Hop("docproc/2/session"))
+ .addHop(Hop(dp2->session->getConnectionSpec()))
+ .addHop(Hop("search/r.0/c.0/session")))
+ .isAccepted());
+ assertItr(*dp0);
+ assertItr(*dp0);
+ assertItr(*dp1);
+ assertItr(*dp1);
+ assertItr(*dp2);
+ assertItr(*dp2);
+ assertDst(*search00);
+ assertItr(*dp2);
+ assertItr(*dp2);
+ assertItr(*dp1);
+ assertItr(*dp1);
+ assertItr(*dp0);
+ assertItr(*dp0);
+ assertSrc(*client);
+
+ teardown();
+}
+
+void
+Test::assertDst(Search& dst)
+{
+ ASSERT_TRUE(dst.waitQueueSize(1));
+ Routable::UP msg = dst.queue.dequeue(0);
+ ASSERT_TRUE(msg.get() != 0);
+ dst.session->acknowledge(Message::UP(static_cast<Message*>(msg.release())));
+}
+
+void
+Test::assertItr(DocProc& itr)
+{
+ ASSERT_TRUE(itr.waitQueueSize(1));
+ Routable::UP msg = itr.queue.dequeue(0);
+ ASSERT_TRUE(msg.get() != 0);
+ itr.session->forward(std::move(msg));
+}
+
+void
+Test::assertSrc(Client& src)
+{
+ ASSERT_TRUE(src.waitQueueSize(1));
+ Routable::UP msg = src.queue.dequeue(0);
+ ASSERT_TRUE(msg.get() != 0);
+}
+
+void
+Test::testRoutingPolicyCache()
+{
+ setup();
+ MessageBus &bus = client->server.mb;
+
+ IRoutingPolicy::SP all = bus.getRoutingPolicy(SimpleProtocol::NAME, "All", "");
+ ASSERT_TRUE(all.get() != NULL);
+
+ IRoutingPolicy::SP ref = bus.getRoutingPolicy(SimpleProtocol::NAME, "All", "");
+ ASSERT_TRUE(ref.get() != NULL);
+ ASSERT_TRUE(all.get() == ref.get());
+
+ IRoutingPolicy::SP allArg = bus.getRoutingPolicy(SimpleProtocol::NAME, "All", "Arg");
+ ASSERT_TRUE(allArg.get() != NULL);
+ ASSERT_TRUE(all.get() != allArg.get());
+
+ IRoutingPolicy::SP refArg = bus.getRoutingPolicy(SimpleProtocol::NAME, "All", "Arg");
+ ASSERT_TRUE(refArg.get() != NULL);
+ ASSERT_TRUE(allArg.get() == refArg.get());
+
+ teardown();
+}
+
+void
+Test::debugTrace()
+{
+ setup();
+ ASSERT_TRUE(SimpleMessage("msg").getHash() % 2 == 0);
+ for (uint32_t i = 0; i < 3; ++i) {
+ Message::UP msg(new SimpleMessage("msg"));
+ msg->getTrace().setLevel(4 + i);
+ EXPECT_TRUE(client->session->send(std::move(msg), "Index").isAccepted());
+ }
+ EXPECT_TRUE(dp0->waitQueueSize(1));
+ EXPECT_TRUE(dp1->waitQueueSize(1));
+ EXPECT_TRUE(dp2->waitQueueSize(1));
+ for (uint32_t i = 0; i < dpVec.size(); ++i) {
+ DocProc *p = dpVec[i];
+ while (p->queue.size() > 0) {
+ Routable::UP r = p->queue.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ p->session->forward(std::move(r));
+ }
+ }
+ EXPECT_TRUE(search00->waitQueueSize(3));
+ EXPECT_TRUE(search01->waitQueueSize(0));
+ EXPECT_TRUE(search10->waitQueueSize(3));
+ EXPECT_TRUE(search11->waitQueueSize(0));
+ for (uint32_t i = 0; i < searchVec.size(); ++i) {
+ Search *s = searchVec[i];
+ while (s->queue.size() > 0) {
+ Routable::UP msg = s->queue.dequeue(0);
+ ASSERT_TRUE(msg.get() != 0);
+ Reply::UP reply(new EmptyReply());
+ msg->swapState(*reply);
+ s->session->reply(std::move(reply));
+ }
+ }
+ EXPECT_TRUE(dp0->waitQueueSize(1));
+ EXPECT_TRUE(dp1->waitQueueSize(1));
+ EXPECT_TRUE(dp2->waitQueueSize(1));
+ for (uint32_t i = 0; i < dpVec.size(); ++i) {
+ DocProc *p = dpVec[i];
+ while (p->queue.size() > 0) {
+ Routable::UP r = p->queue.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ p->session->forward(std::move(r));
+ }
+ }
+ client->waitQueueSize(3);
+ Routable::UP reply = client->queue.dequeue(0);
+ fprintf(stderr, "\nTRACE DUMP(level=%d):\n%s\n\n",
+ reply->getTrace().getLevel(),
+ reply->getTrace().toString().c_str());
+ reply = client->queue.dequeue(0);
+ fprintf(stderr, "\nTRACE DUMP(level=%d):\n%s\n\n",
+ reply->getTrace().getLevel(),
+ reply->getTrace().toString().c_str());
+ reply = client->queue.dequeue(0);
+ fprintf(stderr, "\nTRACE DUMP(level=%d):\n%s\n\n",
+ reply->getTrace().getLevel(),
+ reply->getTrace().toString().c_str());
+ teardown();
+}
diff --git a/messagebus/src/tests/messageordering/.gitignore b/messagebus/src/tests/messageordering/.gitignore
new file mode 100644
index 00000000000..1e4e97670de
--- /dev/null
+++ b/messagebus/src/tests/messageordering/.gitignore
@@ -0,0 +1,3 @@
+/.depend
+/Makefile
+messagebus_messageordering_test_app
diff --git a/messagebus/src/tests/messageordering/CMakeLists.txt b/messagebus/src/tests/messageordering/CMakeLists.txt
new file mode 100644
index 00000000000..b3af6386684
--- /dev/null
+++ b/messagebus/src/tests/messageordering/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_messageordering_test_app
+ SOURCES
+ messageordering.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(
+ NAME messagebus_messageordering_test_app
+ COMMAND messagebus_messageordering_test_app
+)
diff --git a/messagebus/src/tests/messageordering/DESC b/messagebus/src/tests/messageordering/DESC
new file mode 100644
index 00000000000..a4e636441ac
--- /dev/null
+++ b/messagebus/src/tests/messageordering/DESC
@@ -0,0 +1 @@
+messageordering test. Take a look at messageordering.cpp for details.
diff --git a/messagebus/src/tests/messageordering/FILES b/messagebus/src/tests/messageordering/FILES
new file mode 100644
index 00000000000..51c47a40211
--- /dev/null
+++ b/messagebus/src/tests/messageordering/FILES
@@ -0,0 +1 @@
+messageordering.cpp
diff --git a/messagebus/src/tests/messageordering/messageordering.cpp b/messagebus/src/tests/messageordering/messageordering.cpp
new file mode 100644
index 00000000000..97daee80d99
--- /dev/null
+++ b/messagebus/src/tests/messageordering/messageordering.cpp
@@ -0,0 +1,178 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("messageordering_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/vespalib/util/vstringfmt.h>
+#include <stdexcept>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+RoutingSpec
+getRouting()
+{
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("dst", "test/dst/session"))
+ .addRoute(RouteSpec("test").addHop("dst")));
+}
+
+class MultiReceptor : public IMessageHandler
+{
+private:
+ vespalib::Monitor _mon;
+ DestinationSession* _destinationSession;
+ int _messageCounter;
+
+ MultiReceptor(const Receptor &);
+ MultiReceptor &operator=(const Receptor &);
+public:
+ MultiReceptor()
+ : _mon(),
+ _destinationSession(0),
+ _messageCounter(0)
+ {}
+ virtual void handleMessage(Message::UP msg)
+ {
+ SimpleMessage& simpleMsg(dynamic_cast<SimpleMessage&>(*msg));
+ LOG(spam, "Attempting to acquire lock for %s",
+ simpleMsg.getValue().c_str());
+
+ vespalib::MonitorGuard lock(_mon);
+
+ vespalib::string expected(vespalib::make_vespa_string("%d", _messageCounter));
+ LOG(debug, "Got message %p with %s, expecting %s",
+ msg.get(),
+ simpleMsg.getValue().c_str(),
+ expected.c_str());
+
+ SimpleReply::UP sr(new SimpleReply("test reply"));
+ msg->swapState(*sr);
+
+ if (simpleMsg.getValue() != expected) {
+ std::stringstream ss;
+ ss << "Received out-of-sequence message! Expected "
+ << expected
+ << ", but got "
+ << simpleMsg.getValue();
+ //LOG(warning, "%s", ss.str().c_str());
+ sr->addError(Error(ErrorCode::FATAL_ERROR, ss.str()));
+ }
+ sr->setValue(simpleMsg.getValue());
+
+ ++_messageCounter;
+ _destinationSession->reply(Reply::UP(sr.release()));
+ }
+ void setDestinationSession(DestinationSession& sess) {
+ _destinationSession = &sess;
+ }
+};
+
+class VerifyReplyReceptor : public IReplyHandler
+{
+ vespalib::Monitor _mon;
+ std::string _failure;
+ int _replyCount;
+public:
+ VerifyReplyReceptor()
+ : _mon(),
+ _failure(),
+ _replyCount(0)
+ {}
+ void handleReply(Reply::UP reply)
+ {
+ vespalib::MonitorGuard lock(_mon);
+ if (reply->hasErrors()) {
+ std::ostringstream ss;
+ ss << "Reply failed with "
+ << reply->getError(0).getMessage()
+ << "\n"
+ << reply->getTrace().toString();
+ if (_failure.empty()) {
+ _failure = ss.str();
+ }
+ LOG(warning, "%s", ss.str().c_str());
+ } else {
+ vespalib::string expected(vespalib::make_vespa_string("%d", _replyCount));
+ SimpleReply& simpleReply(static_cast<SimpleReply&>(*reply));
+ if (simpleReply.getValue() != expected) {
+ std::stringstream ss;
+ ss << "Received out-of-sequence reply! Expected "
+ << expected
+ << ", but got "
+ << simpleReply.getValue();
+ LOG(warning, "%s", ss.str().c_str());
+ if (_failure.empty()) {
+ _failure = ss.str();
+ }
+ }
+ }
+ ++_replyCount;
+ lock.broadcast();
+ }
+ void waitUntilDone(int waitForCount) const
+ {
+ vespalib::MonitorGuard lock(_mon);
+ while (_replyCount < waitForCount) {
+ lock.wait(1000);
+ }
+ }
+ const std::string& getFailure() const { return _failure; }
+};
+
+int
+Test::Main()
+{
+ TEST_INIT("messageordering_test");
+
+ Slobrok slobrok;
+ TestServer srcNet(Identity("test/src"), getRouting(), slobrok);
+ TestServer dstNet(Identity("test/dst"), getRouting(), slobrok);
+
+ VerifyReplyReceptor src;
+ MultiReceptor dst;
+
+ SourceSessionParams ssp;
+ ssp.setThrottlePolicy(IThrottlePolicy::SP());
+ ssp.setTimeout(400);
+ SourceSession::UP ss = srcNet.mb.createSourceSession(src, ssp);
+ DestinationSession::UP ds = dstNet.mb.createDestinationSession("session", true, dst);
+ ASSERT_EQUAL(400u, ssp.getTimeout());
+
+ // wait for slobrok registration
+ ASSERT_TRUE(srcNet.waitSlobrok("test/dst/session"));
+
+ // same message id for all messages in order to guarantee ordering
+ int commonMessageId = 42;
+
+ // send messages on client
+ const int messageCount = 10000;
+ for (int i = 0; i < messageCount; ++i) {
+ vespalib::string str(vespalib::make_vespa_string("%d", i));
+ //FastOS_Thread::Sleep(1);
+ SimpleMessage::UP msg(new SimpleMessage(str, true, commonMessageId));
+ msg->getTrace().setLevel(9);
+ //LOG(debug, "Sending message %p for %d", msg.get(), i);
+ ASSERT_EQUAL(uint32_t(ErrorCode::NONE),
+ ss->send(std::move(msg), "test").getError().getCode());
+ }
+ src.waitUntilDone(messageCount);
+
+ ASSERT_EQUAL(std::string(), src.getFailure());
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/messenger/.gitignore b/messagebus/src/tests/messenger/.gitignore
new file mode 100644
index 00000000000..a7a74282fc7
--- /dev/null
+++ b/messagebus/src/tests/messenger/.gitignore
@@ -0,0 +1,3 @@
+/.depend
+/Makefile
+messagebus_messenger_test_app
diff --git a/messagebus/src/tests/messenger/CMakeLists.txt b/messagebus/src/tests/messenger/CMakeLists.txt
new file mode 100644
index 00000000000..6dfab750bf4
--- /dev/null
+++ b/messagebus/src/tests/messenger/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_messenger_test_app
+ SOURCES
+ messenger.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_messenger_test_app COMMAND messagebus_messenger_test_app)
diff --git a/messagebus/src/tests/messenger/DESC b/messagebus/src/tests/messenger/DESC
new file mode 100644
index 00000000000..f4b52840a14
--- /dev/null
+++ b/messagebus/src/tests/messenger/DESC
@@ -0,0 +1 @@
+messenger test. Take a look at messenger.cpp for details.
diff --git a/messagebus/src/tests/messenger/FILES b/messagebus/src/tests/messenger/FILES
new file mode 100644
index 00000000000..620b105632e
--- /dev/null
+++ b/messagebus/src/tests/messenger/FILES
@@ -0,0 +1 @@
+messenger.cpp
diff --git a/messagebus/src/tests/messenger/messenger.cpp b/messagebus/src/tests/messenger/messenger.cpp
new file mode 100644
index 00000000000..a736814aa3d
--- /dev/null
+++ b/messagebus/src/tests/messenger/messenger.cpp
@@ -0,0 +1,61 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("messagebus_test");
+
+#include <vespa/messagebus/messenger.h>
+#include <vespa/vespalib/util/barrier.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+class ThrowException : public Messenger::ITask {
+public:
+ void run() {
+ throw std::exception();
+ }
+
+ uint8_t priority() const {
+ return 0;
+ }
+};
+
+class BarrierTask : public Messenger::ITask {
+private:
+ vespalib::Barrier &_barrier;
+
+public:
+ BarrierTask(vespalib::Barrier &barrier)
+ : _barrier(barrier)
+ {
+ // empty
+ }
+
+ void run() {
+ _barrier.await();
+ }
+
+ uint8_t priority() const {
+ return 0;
+ }
+};
+
+int
+Test::Main()
+{
+ TEST_INIT("messenger_test");
+
+ Messenger msn;
+ msn.start();
+
+ vespalib::Barrier barrier(2);
+ msn.enqueue(Messenger::ITask::UP(new ThrowException()));
+ msn.enqueue(Messenger::ITask::UP(new BarrierTask(barrier)));
+
+ barrier.await();
+ ASSERT_TRUE(msn.isEmpty());
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/oos/.gitignore b/messagebus/src/tests/oos/.gitignore
new file mode 100644
index 00000000000..a4771a9176b
--- /dev/null
+++ b/messagebus/src/tests/oos/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+oos_test
+messagebus_oos_test_app
diff --git a/messagebus/src/tests/oos/CMakeLists.txt b/messagebus/src/tests/oos/CMakeLists.txt
new file mode 100644
index 00000000000..1d037ef6f6e
--- /dev/null
+++ b/messagebus/src/tests/oos/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_oos_test_app
+ SOURCES
+ oos.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_oos_test_app NO_VALGRIND COMMAND messagebus_oos_test_app)
diff --git a/messagebus/src/tests/oos/DESC b/messagebus/src/tests/oos/DESC
new file mode 100644
index 00000000000..16cd7a2f30d
--- /dev/null
+++ b/messagebus/src/tests/oos/DESC
@@ -0,0 +1 @@
+oos test. Take a look at oos.cpp for details.
diff --git a/messagebus/src/tests/oos/FILES b/messagebus/src/tests/oos/FILES
new file mode 100644
index 00000000000..08cf509e1fd
--- /dev/null
+++ b/messagebus/src/tests/oos/FILES
@@ -0,0 +1 @@
+oos.cpp
diff --git a/messagebus/src/tests/oos/oos.cpp b/messagebus/src/tests/oos/oos.cpp
new file mode 100644
index 00000000000..0ff93cecd7d
--- /dev/null
+++ b/messagebus/src/tests/oos/oos.cpp
@@ -0,0 +1,231 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("oos_test");
+
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/oosserver.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+struct Handler : public IMessageHandler
+{
+ DestinationSession::UP session;
+ Handler(MessageBus &mb) : session() {
+ session = mb.createDestinationSession("session", true, *this);
+ }
+ ~Handler() {
+ session.reset();
+ }
+ virtual void handleMessage(Message::UP msg) {
+ session->acknowledge(std::move(msg));
+ }
+};
+
+
+class Test : public vespalib::TestApp {
+private:
+ SourceSession::UP _session;
+ RoutableQueue _handler;
+
+ bool checkError(const string &dst, uint32_t error);
+
+public:
+ Test();
+ int Main();
+};
+
+TEST_APPHOOK(Test);
+
+Test::Test() :
+ _session(),
+ _handler()
+{
+ // empty
+}
+
+bool
+Test::checkError(const string &dst, uint32_t error)
+{
+ if (!EXPECT_TRUE(_session.get() != NULL)) {
+ return false;
+ }
+ Message::UP msg(new SimpleMessage("msg"));
+ msg->getTrace().setLevel(9);
+ if (!EXPECT_TRUE(_session->send(std::move(msg), Route::parse(dst)).isAccepted())) {
+ return false;
+ }
+ Routable::UP reply = _handler.dequeue(10000);
+ if (!EXPECT_TRUE(reply.get() != NULL)) {
+ return false;
+ }
+ if (!EXPECT_TRUE(reply->isReply())) {
+ return false;
+ }
+ Reply &ref = static_cast<Reply&>(*reply);
+ printf("%s", ref.getTrace().toString().c_str());
+ if (error == ErrorCode::NONE) {
+ if (!EXPECT_TRUE(!ref.hasErrors())) {
+ return false;
+ }
+ } else {
+ if (!EXPECT_TRUE(ref.hasErrors())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(error, ref.getError(0).getCode())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("oos_test");
+
+ Slobrok slobrok;
+ TestServer src(Identity(""), RoutingSpec(), slobrok, "oos/*");
+ TestServer dst1(Identity("dst1"), RoutingSpec(), slobrok);
+ TestServer dst2(Identity("dst2"), RoutingSpec(), slobrok);
+ TestServer dst3(Identity("dst3"), RoutingSpec(), slobrok);
+ TestServer dst4(Identity("dst4"), RoutingSpec(), slobrok);
+ TestServer dst5(Identity("dst5"), RoutingSpec(), slobrok);
+ Handler h1(dst1.mb);
+ Handler h2(dst2.mb);
+ Handler h3(dst3.mb);
+ Handler h4(dst4.mb);
+ Handler h5(dst5.mb);
+ EXPECT_TRUE(src.waitSlobrok("*/session", 5));
+
+ _session = src.mb.createSourceSession(_handler);
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::NONE));
+ TEST_FLUSH();
+ OOSServer oosServer(slobrok, "oos/1", OOSState()
+ .add("dst2/session")
+ .add("dst3/session"));
+ EXPECT_TRUE(src.waitSlobrok("oos/*", 1));
+ EXPECT_TRUE(src.waitState(OOSState()
+ .add("dst2/session")
+ .add("dst3/session")));
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::NONE)); // test 9
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS)); // return without reply?!?
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::NONE));
+ TEST_FLUSH();
+ oosServer.setState(OOSState()
+ .add("dst2/session"));
+ EXPECT_TRUE(src.waitState(OOSState()
+ .add("dst2/session", true)
+ .add("dst3/session", false)));
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::NONE));
+ TEST_FLUSH();
+ {
+ OOSServer oosServer2(slobrok, "oos/2", OOSState()
+ .add("dst4/session")
+ .add("dst5/session"));
+ EXPECT_TRUE(src.waitSlobrok("oos/*", 2));
+ EXPECT_TRUE(src.waitState(OOSState()
+ .add("dst2/session")
+ .add("dst4/session")
+ .add("dst5/session")));
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::SERVICE_OOS));
+ TEST_FLUSH();
+ }
+ EXPECT_TRUE(src.waitSlobrok("oos/*", 1));
+ EXPECT_TRUE(src.waitState(OOSState()
+ .add("dst1/session", false)
+ .add("dst2/session", true)
+ .add("dst3/session", false)
+ .add("dst4/session", false)
+ .add("dst5/session", false)));
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::NONE));
+ TEST_FLUSH();
+ {
+ OOSServer oosServer3(slobrok, "oos/3", OOSState()
+ .add("dst2/session")
+ .add("dst4/session"));
+ OOSServer oosServer4(slobrok, "oos/4", OOSState()
+ .add("dst2/session")
+ .add("dst3/session")
+ .add("dst5/session"));
+ EXPECT_TRUE(src.waitSlobrok("oos/*", 3));
+ EXPECT_TRUE(src.waitState(OOSState()
+ .add("dst2/session")
+ .add("dst3/session")
+ .add("dst4/session")
+ .add("dst5/session")));
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::SERVICE_OOS));
+ TEST_FLUSH();
+ oosServer3.setState(OOSState()
+ .add("dst2/session"));
+ oosServer4.setState(OOSState()
+ .add("dst1/session"));
+ EXPECT_TRUE(src.waitState(OOSState()
+ .add("dst1/session", true)
+ .add("dst2/session", true)
+ .add("dst3/session", false)
+ .add("dst4/session", false)
+ .add("dst5/session", false)));
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::NONE));
+ TEST_FLUSH();
+ }
+ EXPECT_TRUE(src.waitSlobrok("oos/*", 1));
+ EXPECT_TRUE(src.waitState(OOSState()
+ .add("dst1/session", false)
+ .add("dst2/session", true)
+ .add("dst3/session", false)
+ .add("dst4/session", false)
+ .add("dst5/session", false)));
+ EXPECT_TRUE(checkError("dst1/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS));
+ EXPECT_TRUE(checkError("dst3/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst4/session", ErrorCode::NONE));
+ EXPECT_TRUE(checkError("dst5/session", ErrorCode::NONE));
+
+ h2.session.reset();
+ EXPECT_TRUE(src.waitSlobrok("*/session", 4));
+ EXPECT_TRUE(checkError("dst2/session", ErrorCode::SERVICE_OOS));
+
+ _session.reset();
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/oospolicy/.gitignore b/messagebus/src/tests/oospolicy/.gitignore
new file mode 100644
index 00000000000..3bd6e47e0bc
--- /dev/null
+++ b/messagebus/src/tests/oospolicy/.gitignore
@@ -0,0 +1,3 @@
+.depend
+Makefile
+oospolicy_test
diff --git a/messagebus/src/tests/protocolrepository/.gitignore b/messagebus/src/tests/protocolrepository/.gitignore
new file mode 100644
index 00000000000..824d12fc43f
--- /dev/null
+++ b/messagebus/src/tests/protocolrepository/.gitignore
@@ -0,0 +1,3 @@
+/.depend
+/Makefile
+messagebus_protocolrepository_test_app
diff --git a/messagebus/src/tests/protocolrepository/CMakeLists.txt b/messagebus/src/tests/protocolrepository/CMakeLists.txt
new file mode 100644
index 00000000000..68156e5eee9
--- /dev/null
+++ b/messagebus/src/tests/protocolrepository/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_protocolrepository_test_app
+ SOURCES
+ protocolrepository.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_protocolrepository_test_app NO_VALGRIND COMMAND messagebus_protocolrepository_test_app)
diff --git a/messagebus/src/tests/protocolrepository/DESC b/messagebus/src/tests/protocolrepository/DESC
new file mode 100644
index 00000000000..98e3dc9ef6e
--- /dev/null
+++ b/messagebus/src/tests/protocolrepository/DESC
@@ -0,0 +1 @@
+protocolrepository test. Take a look at protocolrepository.cpp for details.
diff --git a/messagebus/src/tests/protocolrepository/FILES b/messagebus/src/tests/protocolrepository/FILES
new file mode 100644
index 00000000000..2fc199b4aef
--- /dev/null
+++ b/messagebus/src/tests/protocolrepository/FILES
@@ -0,0 +1 @@
+protocolrepository.cpp
diff --git a/messagebus/src/tests/protocolrepository/protocolrepository.cpp b/messagebus/src/tests/protocolrepository/protocolrepository.cpp
new file mode 100644
index 00000000000..e1b1d1402dd
--- /dev/null
+++ b/messagebus/src/tests/protocolrepository/protocolrepository.cpp
@@ -0,0 +1,75 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("protocolrepository_test");
+
+#include <vespa/messagebus/protocolrepository.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+class TestProtocol : public IProtocol {
+private:
+ const string _name;
+
+public:
+
+ TestProtocol(const string &name)
+ : _name(name)
+ {
+ // empty
+ }
+
+ const string &
+ getName() const
+ {
+ return _name;
+ }
+
+ IRoutingPolicy::UP
+ createPolicy(const string &name, const string &param) const
+ {
+ (void)name;
+ (void)param;
+ throw std::exception();
+ }
+
+ Blob
+ encode(const vespalib::Version &version, const Routable &routable) const
+ {
+ (void)version;
+ (void)routable;
+ throw std::exception();
+ }
+
+ Routable::UP
+ decode(const vespalib::Version &version, BlobRef data) const
+ {
+ (void)version;
+ (void)data;
+ throw std::exception();
+ }
+};
+
+int
+Test::Main()
+{
+ TEST_INIT("protocolrepository_test");
+
+ ProtocolRepository repo;
+ IProtocol::SP prev;
+ prev = repo.putProtocol(IProtocol::SP(new TestProtocol("foo")));
+ ASSERT_TRUE(prev.get() == NULL);
+
+ IRoutingPolicy::SP policy = repo.getRoutingPolicy("foo", "bar", "baz");
+ prev = repo.putProtocol(IProtocol::SP(new TestProtocol("foo")));
+ ASSERT_TRUE(prev.get() != NULL);
+ ASSERT_NOT_EQUAL(prev.get(), repo.getProtocol("foo").get());
+
+ policy = repo.getRoutingPolicy("foo", "bar", "baz");
+ ASSERT_TRUE(policy.get() == NULL);
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/queue/.gitignore b/messagebus/src/tests/queue/.gitignore
new file mode 100644
index 00000000000..7b5371ea913
--- /dev/null
+++ b/messagebus/src/tests/queue/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+queue_test
+messagebus_queue_test_app
diff --git a/messagebus/src/tests/queue/CMakeLists.txt b/messagebus/src/tests/queue/CMakeLists.txt
new file mode 100644
index 00000000000..c330421ac8a
--- /dev/null
+++ b/messagebus/src/tests/queue/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_queue_test_app
+ SOURCES
+ queue.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_queue_test_app COMMAND messagebus_queue_test_app)
diff --git a/messagebus/src/tests/queue/DESC b/messagebus/src/tests/queue/DESC
new file mode 100644
index 00000000000..1d795755700
--- /dev/null
+++ b/messagebus/src/tests/queue/DESC
@@ -0,0 +1 @@
+queue test. Take a look at queue.cpp for details.
diff --git a/messagebus/src/tests/queue/FILES b/messagebus/src/tests/queue/FILES
new file mode 100644
index 00000000000..6fb01ca3173
--- /dev/null
+++ b/messagebus/src/tests/queue/FILES
@@ -0,0 +1 @@
+queue.cpp
diff --git a/messagebus/src/tests/queue/queue.cpp b/messagebus/src/tests/queue/queue.cpp
new file mode 100644
index 00000000000..78bf09e2c48
--- /dev/null
+++ b/messagebus/src/tests/queue/queue.cpp
@@ -0,0 +1,90 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("queue_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/queue.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("queue_test");
+ Queue<int> q;
+ EXPECT_TRUE(q.size() == 0);
+ q.push(1);
+ EXPECT_TRUE(q.size() == 1);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(2);
+ EXPECT_TRUE(q.size() == 2);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(3);
+ EXPECT_TRUE(q.size() == 3);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(4);
+ EXPECT_TRUE(q.size() == 4);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(5);
+ EXPECT_TRUE(q.size() == 5);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(6);
+ EXPECT_TRUE(q.size() == 6);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(7);
+ EXPECT_TRUE(q.size() == 7);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(8);
+ EXPECT_TRUE(q.size() == 8);
+ EXPECT_TRUE(q.front() == 1);
+ q.push(9);
+ EXPECT_TRUE(q.size() == 9);
+ EXPECT_TRUE(q.front() == 1);
+ q.pop();
+ EXPECT_TRUE(q.size() == 8);
+ EXPECT_TRUE(q.front() == 2);
+ q.pop();
+ EXPECT_TRUE(q.size() == 7);
+ EXPECT_TRUE(q.front() == 3);
+ q.pop();
+ EXPECT_TRUE(q.size() == 6);
+ EXPECT_TRUE(q.front() == 4);
+ q.push(1);
+ EXPECT_TRUE(q.size() == 7);
+ EXPECT_TRUE(q.front() == 4);
+ q.push(2);
+ EXPECT_TRUE(q.size() == 8);
+ EXPECT_TRUE(q.front() == 4);
+ q.push(3);
+ EXPECT_TRUE(q.size() == 9);
+ EXPECT_TRUE(q.front() == 4);
+ q.pop();
+ EXPECT_TRUE(q.size() == 8);
+ EXPECT_TRUE(q.front() == 5);
+ q.pop();
+ EXPECT_TRUE(q.size() == 7);
+ EXPECT_TRUE(q.front() == 6);
+ q.pop();
+ EXPECT_TRUE(q.size() == 6);
+ EXPECT_TRUE(q.front() == 7);
+ q.pop();
+ EXPECT_TRUE(q.size() == 5);
+ EXPECT_TRUE(q.front() == 8);
+ q.pop();
+ EXPECT_TRUE(q.size() == 4);
+ EXPECT_TRUE(q.front() == 9);
+ q.pop();
+ EXPECT_TRUE(q.size() == 3);
+ EXPECT_TRUE(q.front() == 1);
+ q.pop();
+ EXPECT_TRUE(q.size() == 2);
+ EXPECT_TRUE(q.front() == 2);
+ q.pop();
+ EXPECT_TRUE(q.size() == 1);
+ EXPECT_TRUE(q.front() == 3);
+ q.pop();
+ EXPECT_TRUE(q.size() == 0);
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/replygate/.gitignore b/messagebus/src/tests/replygate/.gitignore
new file mode 100644
index 00000000000..65ec6c9a372
--- /dev/null
+++ b/messagebus/src/tests/replygate/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+replygate_test
+messagebus_replygate_test_app
diff --git a/messagebus/src/tests/replygate/CMakeLists.txt b/messagebus/src/tests/replygate/CMakeLists.txt
new file mode 100644
index 00000000000..8844ef862dd
--- /dev/null
+++ b/messagebus/src/tests/replygate/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_replygate_test_app
+ SOURCES
+ replygate.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_replygate_test_app COMMAND messagebus_replygate_test_app)
diff --git a/messagebus/src/tests/replygate/DESC b/messagebus/src/tests/replygate/DESC
new file mode 100644
index 00000000000..2bb86cc490b
--- /dev/null
+++ b/messagebus/src/tests/replygate/DESC
@@ -0,0 +1 @@
+replygate test. Take a look at replygate.cpp for details.
diff --git a/messagebus/src/tests/replygate/FILES b/messagebus/src/tests/replygate/FILES
new file mode 100644
index 00000000000..3169994c2ca
--- /dev/null
+++ b/messagebus/src/tests/replygate/FILES
@@ -0,0 +1 @@
+replygate.cpp
diff --git a/messagebus/src/tests/replygate/replygate.cpp b/messagebus/src/tests/replygate/replygate.cpp
new file mode 100644
index 00000000000..09368da3974
--- /dev/null
+++ b/messagebus/src/tests/replygate/replygate.cpp
@@ -0,0 +1,91 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("replygate_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/imessagehandler.h>
+#include <vespa/messagebus/replygate.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+struct MyGate : public ReplyGate
+{
+ static int ctorCnt;
+ static int dtorCnt;
+ MyGate(IMessageHandler &sender) : ReplyGate(sender) {
+ ++ctorCnt;
+ }
+ virtual ~MyGate() {
+ ++dtorCnt;
+ }
+};
+int MyGate::ctorCnt = 0;
+int MyGate::dtorCnt = 0;
+
+struct MyReply : public EmptyReply
+{
+ static int ctorCnt;
+ static int dtorCnt;
+ MyReply() : EmptyReply() {
+ ++ctorCnt;
+ }
+ virtual ~MyReply() {
+ ++dtorCnt;
+ }
+};
+int MyReply::ctorCnt = 0;
+int MyReply::dtorCnt = 0;
+
+struct MySender : public IMessageHandler
+{
+ // giving a sync reply here is against the API contract, but it is
+ // ok for testing.
+ virtual void handleMessage(Message::UP msg) {
+ Reply::UP reply(new MyReply());
+ msg->swapState(*reply);
+ IReplyHandler &handler = reply->getCallStack().pop(*reply);
+ handler.handleReply(std::move(reply));
+ }
+};
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("replygate_test");
+ {
+ RoutableQueue q;
+ MySender sender;
+ MyGate *gate = new MyGate(sender);
+ {
+ Message::UP msg(new SimpleMessage("test"));
+ msg->pushHandler(q);
+ gate->handleMessage(std::move(msg));
+ }
+ EXPECT_TRUE(q.size() == 1);
+ EXPECT_TRUE(MyReply::ctorCnt == 1);
+ EXPECT_TRUE(MyReply::dtorCnt == 0);
+ gate->close();
+ {
+ Message::UP msg(new SimpleMessage("test"));
+ msg->pushHandler(q);
+ gate->handleMessage(std::move(msg));
+ }
+ EXPECT_TRUE(q.size() == 1);
+ EXPECT_TRUE(MyReply::ctorCnt == 2);
+ EXPECT_TRUE(MyReply::dtorCnt == 1);
+ EXPECT_TRUE(MyGate::ctorCnt == 1);
+ EXPECT_TRUE(MyGate::dtorCnt == 0);
+ gate->subRef();
+ EXPECT_TRUE(MyGate::ctorCnt == 1);
+ EXPECT_TRUE(MyGate::dtorCnt == 1);
+ }
+ EXPECT_TRUE(MyReply::ctorCnt == 2);
+ EXPECT_TRUE(MyReply::dtorCnt == 2);
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/replyset/.gitignore b/messagebus/src/tests/replyset/.gitignore
new file mode 100644
index 00000000000..0efd61559c9
--- /dev/null
+++ b/messagebus/src/tests/replyset/.gitignore
@@ -0,0 +1,3 @@
+.depend
+Makefile
+replyset_test
diff --git a/messagebus/src/tests/resender/.gitignore b/messagebus/src/tests/resender/.gitignore
new file mode 100644
index 00000000000..ec4ca62b805
--- /dev/null
+++ b/messagebus/src/tests/resender/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+resender_test
+messagebus_resender_test_app
diff --git a/messagebus/src/tests/resender/CMakeLists.txt b/messagebus/src/tests/resender/CMakeLists.txt
new file mode 100644
index 00000000000..b253e582af8
--- /dev/null
+++ b/messagebus/src/tests/resender/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_resender_test_app
+ SOURCES
+ resender.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_resender_test_app COMMAND messagebus_resender_test_app)
diff --git a/messagebus/src/tests/resender/DESC b/messagebus/src/tests/resender/DESC
new file mode 100644
index 00000000000..0b234bf57b9
--- /dev/null
+++ b/messagebus/src/tests/resender/DESC
@@ -0,0 +1 @@
+resender test. Take a look at resender.cpp for details.
diff --git a/messagebus/src/tests/resender/FILES b/messagebus/src/tests/resender/FILES
new file mode 100644
index 00000000000..16b7c7fe76b
--- /dev/null
+++ b/messagebus/src/tests/resender/FILES
@@ -0,0 +1 @@
+resender.cpp
diff --git a/messagebus/src/tests/resender/resender.cpp b/messagebus/src/tests/resender/resender.cpp
new file mode 100644
index 00000000000..a067616d1ba
--- /dev/null
+++ b/messagebus/src/tests/resender/resender.cpp
@@ -0,0 +1,310 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routing_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routing/errordirective.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/testlib/custompolicy.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Utilities
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class StringList : public std::vector<string> {
+public:
+ StringList &add(const string &str);
+};
+
+StringList &
+StringList::add(const string &str)
+{
+ std::vector<string>::push_back(str); return *this;
+}
+
+static const double GET_MESSAGE_TIMEOUT = 60.0;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Setup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class TestData {
+public:
+ Slobrok _slobrok;
+ RetryTransientErrorsPolicy::SP _retryPolicy;
+ TestServer _srcServer;
+ SourceSession::UP _srcSession;
+ Receptor _srcHandler;
+ TestServer _dstServer;
+ DestinationSession::UP _dstSession;
+ Receptor _dstHandler;
+
+public:
+ TestData();
+ bool start();
+};
+
+class Test : public vespalib::TestApp {
+private:
+ Message::UP createMessage(const string &msg);
+ void replyFromDestination(TestData &data, Message::UP msg, uint32_t errorCode, double retryDelay);
+
+public:
+ int Main();
+ void testRetryTag(TestData &data);
+ void testRetryEnabledTag(TestData &data);
+ void testTransientError(TestData &data);
+ void testFatalError(TestData &data);
+ void testDisableRetry(TestData &data);
+ void testRetryDelay(TestData &data);
+ void testRequestRetryDelay(TestData &data);
+};
+
+TEST_APPHOOK(Test);
+
+TestData::TestData() :
+ _slobrok(),
+ _retryPolicy(new RetryTransientErrorsPolicy()),
+ _srcServer(MessageBusParams().setRetryPolicy(_retryPolicy).addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams().setSlobrokConfig(_slobrok.config())),
+ _srcSession(),
+ _srcHandler(),
+ _dstServer(MessageBusParams().addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams().setIdentity(Identity("dst")).setSlobrokConfig(_slobrok.config())),
+ _dstSession(),
+ _dstHandler()
+{
+ // empty
+}
+
+bool
+TestData::start()
+{
+ _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams().setReplyHandler(_srcHandler));
+ if (_srcSession.get() == NULL) {
+ return false;
+ }
+ _dstSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("session").setMessageHandler(_dstHandler));
+ if (_dstSession.get() == NULL) {
+ return false;
+ }
+ if (!_srcServer.waitSlobrok("dst/session", 1u)) {
+ return false;
+ }
+ return true;
+}
+
+Message::UP
+Test::createMessage(const string &msg)
+{
+ Message::UP ret(new SimpleMessage(msg));
+ ret->getTrace().setLevel(9);
+ return ret;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("resender_test");
+
+ TestData data;
+ ASSERT_TRUE(data.start());
+
+ testRetryTag(data); TEST_FLUSH();
+ testRetryEnabledTag(data); TEST_FLUSH();
+ testTransientError(data); TEST_FLUSH();
+ testFatalError(data); TEST_FLUSH();
+ testDisableRetry(data); TEST_FLUSH();
+ testRetryDelay(data); TEST_FLUSH();
+ testRequestRetryDelay(data); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+void
+Test::replyFromDestination(TestData &data, Message::UP msg, uint32_t errorCode, double retryDelay)
+{
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ if (errorCode != ErrorCode::NONE) {
+ reply->addError(Error(errorCode, "err"));
+ }
+ reply->setRetryDelay(retryDelay);
+ data._dstSession->reply(std::move(reply));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tests
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void
+Test::testRetryTag(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ for (uint32_t i = 0; i < 5; ++i) {
+ EXPECT_EQUAL(i, msg->getRetry());
+ EXPECT_EQUAL(true, msg->getRetryEnabled());
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_TRANSIENT_ERROR, 0);
+ msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ }
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(!reply->hasErrors());
+ msg = data._dstHandler.getMessage(0);
+ EXPECT_TRUE(msg.get() == NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+}
+
+void
+Test::testRetryEnabledTag(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ Message::UP msg = createMessage("msg");
+ msg->setRetryEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(std::move(msg), Route::parse("dst/session")).isAccepted());
+ msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ EXPECT_EQUAL(false, msg->getRetryEnabled());
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_TRANSIENT_ERROR, 0);
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(reply->hasErrors());
+ msg = data._dstHandler.getMessage(0);
+ EXPECT_TRUE(msg.get() == NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+}
+
+void
+Test::testTransientError(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_TRANSIENT_ERROR, 0);
+ msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_FATAL_ERROR, 0);
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(reply->hasFatalErrors());
+ msg = data._dstHandler.getMessage(0);
+ EXPECT_TRUE(msg.get() == NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+}
+
+void
+Test::testFatalError(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_FATAL_ERROR, 0);
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(reply->hasFatalErrors());
+ msg = data._dstHandler.getMessage(0);
+ EXPECT_TRUE(msg.get() == NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+}
+
+void
+Test::testDisableRetry(TestData &data)
+{
+ data._retryPolicy->setEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_TRANSIENT_ERROR, 0);
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(reply->hasErrors());
+ EXPECT_TRUE(!reply->hasFatalErrors());
+ msg = data._dstHandler.getMessage(0);
+ EXPECT_TRUE(msg.get() == NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+}
+
+void
+Test::testRetryDelay(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ data._retryPolicy->setBaseDelay(0.01);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ for (uint32_t i = 0; i < 5; ++i) {
+ EXPECT_EQUAL(i, msg->getRetry());
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_TRANSIENT_ERROR, -1);
+ msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ }
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_FATAL_ERROR, 0);
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(reply->hasFatalErrors());
+ msg = data._dstHandler.getMessage(0);
+ EXPECT_TRUE(msg.get() == NULL);
+
+ string trace = reply->getTrace().toString();
+ printf("%s", trace.c_str());
+ EXPECT_TRUE(trace.find("retry 1 in 0.01") != string::npos);
+ EXPECT_TRUE(trace.find("retry 2 in 0.02") != string::npos);
+ EXPECT_TRUE(trace.find("retry 3 in 0.03") != string::npos);
+ EXPECT_TRUE(trace.find("retry 4 in 0.04") != string::npos);
+ EXPECT_TRUE(trace.find("retry 5 in 0.05") != string::npos);
+}
+
+void
+Test::testRequestRetryDelay(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ data._retryPolicy->setBaseDelay(1);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ for (uint32_t i = 0; i < 5; ++i) {
+ EXPECT_EQUAL(i, msg->getRetry());
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_TRANSIENT_ERROR, i / 50.0);
+ msg = data._dstHandler.getMessage(GET_MESSAGE_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ }
+ replyFromDestination(data, std::move(msg), ErrorCode::APP_FATAL_ERROR, 0);
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_TRUE(reply->hasFatalErrors());
+ msg = data._dstHandler.getMessage(0);
+ EXPECT_TRUE(msg.get() == NULL);
+
+ string trace = reply->getTrace().toString();
+ printf("%s", trace.c_str());
+ EXPECT_TRUE(trace.find("retry 1 in 0") != string::npos);
+ EXPECT_TRUE(trace.find("retry 2 in 0.02") != string::npos);
+ EXPECT_TRUE(trace.find("retry 3 in 0.04") != string::npos);
+ EXPECT_TRUE(trace.find("retry 4 in 0.06") != string::npos);
+ EXPECT_TRUE(trace.find("retry 5 in 0.08") != string::npos);
+}
+
diff --git a/messagebus/src/tests/result/.gitignore b/messagebus/src/tests/result/.gitignore
new file mode 100644
index 00000000000..2f5999ef7d0
--- /dev/null
+++ b/messagebus/src/tests/result/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+result_test
+messagebus_result_test_app
diff --git a/messagebus/src/tests/result/CMakeLists.txt b/messagebus/src/tests/result/CMakeLists.txt
new file mode 100644
index 00000000000..bd1ed96ab5f
--- /dev/null
+++ b/messagebus/src/tests/result/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_result_test_app
+ SOURCES
+ result.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_result_test_app COMMAND messagebus_result_test_app)
diff --git a/messagebus/src/tests/result/DESC b/messagebus/src/tests/result/DESC
new file mode 100644
index 00000000000..8192ff0830c
--- /dev/null
+++ b/messagebus/src/tests/result/DESC
@@ -0,0 +1 @@
+Simple test of the Result class.
diff --git a/messagebus/src/tests/result/FILES b/messagebus/src/tests/result/FILES
new file mode 100644
index 00000000000..55a888fd93f
--- /dev/null
+++ b/messagebus/src/tests/result/FILES
@@ -0,0 +1 @@
+result.cpp
diff --git a/messagebus/src/tests/result/result.cpp b/messagebus/src/tests/result/result.cpp
new file mode 100644
index 00000000000..1b081f5fc9e
--- /dev/null
+++ b/messagebus/src/tests/result/result.cpp
@@ -0,0 +1,76 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("result_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/result.h>
+#include <vespa/messagebus/error.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+
+using namespace mbus;
+
+struct MyMessage : public SimpleMessage
+{
+ static int ctorCnt;
+ static int dtorCnt;
+ MyMessage(const string &str) : SimpleMessage(str) {
+ ++ctorCnt;
+ }
+ virtual ~MyMessage() {
+ ++dtorCnt;
+ }
+};
+int MyMessage::ctorCnt = 0;
+int MyMessage::dtorCnt = 0;
+
+struct Test : public vespalib::TestApp
+{
+ Result sendOk(Message::UP msg);
+ Result sendFail(Message::UP msg);
+ int Main();
+};
+
+Result
+Test::sendOk(Message::UP msg) {
+ (void) msg;
+ return Result();
+}
+
+Result
+Test::sendFail(Message::UP msg) {
+ return Result(Error(ErrorCode::FATAL_ERROR, "error"), std::move(msg));
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("result_test");
+ { // test accepted
+ Message::UP msg(new MyMessage("test"));
+ Result res = sendOk(std::move(msg));
+ EXPECT_TRUE(msg.get() == 0);
+ EXPECT_TRUE(res.isAccepted());
+ EXPECT_TRUE(res.getError().getCode() == ErrorCode::NONE);
+ EXPECT_TRUE(res.getError().getMessage() == "");
+ Message::UP back = res.getMessage();
+ EXPECT_TRUE(back.get() == 0);
+ }
+ { // test failed
+ Message::UP msg(new MyMessage("test"));
+ Message *raw = msg.get();
+ EXPECT_TRUE(raw != 0);
+ Result res = sendFail(std::move(msg));
+ EXPECT_TRUE(msg.get() == 0);
+ EXPECT_TRUE(!res.isAccepted());
+ EXPECT_TRUE(res.getError().getCode() == ErrorCode::FATAL_ERROR);
+ EXPECT_TRUE(res.getError().getMessage() == "error");
+ Message::UP back = res.getMessage();
+ EXPECT_TRUE(back.get() == raw);
+ }
+ EXPECT_TRUE(MyMessage::ctorCnt == 2);
+ EXPECT_TRUE(MyMessage::dtorCnt == 2);
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test);
diff --git a/messagebus/src/tests/retrypolicy/.gitignore b/messagebus/src/tests/retrypolicy/.gitignore
new file mode 100644
index 00000000000..672e4f58b21
--- /dev/null
+++ b/messagebus/src/tests/retrypolicy/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+retrypolicy_test
+messagebus_retrypolicy_test_app
diff --git a/messagebus/src/tests/retrypolicy/CMakeLists.txt b/messagebus/src/tests/retrypolicy/CMakeLists.txt
new file mode 100644
index 00000000000..2a2a2a20e6e
--- /dev/null
+++ b/messagebus/src/tests/retrypolicy/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_retrypolicy_test_app
+ SOURCES
+ retrypolicy.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_retrypolicy_test_app COMMAND messagebus_retrypolicy_test_app)
diff --git a/messagebus/src/tests/retrypolicy/DESC b/messagebus/src/tests/retrypolicy/DESC
new file mode 100644
index 00000000000..0037ca01c5c
--- /dev/null
+++ b/messagebus/src/tests/retrypolicy/DESC
@@ -0,0 +1 @@
+retrypolicy test. Take a look at retrypolicy.cpp for details.
diff --git a/messagebus/src/tests/retrypolicy/FILES b/messagebus/src/tests/retrypolicy/FILES
new file mode 100644
index 00000000000..11a520524fb
--- /dev/null
+++ b/messagebus/src/tests/retrypolicy/FILES
@@ -0,0 +1 @@
+retrypolicy.cpp
diff --git a/messagebus/src/tests/retrypolicy/retrypolicy.cpp b/messagebus/src/tests/retrypolicy/retrypolicy.cpp
new file mode 100644
index 00000000000..5426a1123cd
--- /dev/null
+++ b/messagebus/src/tests/retrypolicy/retrypolicy.cpp
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("retrypolicy_test");
+
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("retrypolicy_test");
+
+ RetryTransientErrorsPolicy policy;
+ for (uint32_t i = 0; i < 5; ++i) {
+ double delay = i / 3.0;
+ policy.setBaseDelay(delay);
+ for (uint32_t j = 0; j < 5; ++j) {
+ EXPECT_EQUAL((int)(j * delay), (int)policy.getRetryDelay(j));
+ }
+ for (uint32_t j = ErrorCode::NONE; j < ErrorCode::ERROR_LIMIT; ++j) {
+ policy.setEnabled(true);
+ if (j < ErrorCode::FATAL_ERROR) {
+ EXPECT_TRUE(policy.canRetry(j));
+ } else {
+ EXPECT_TRUE(!policy.canRetry(j));
+ }
+ policy.setEnabled(false);
+ EXPECT_TRUE(!policy.canRetry(j));
+ }
+ }
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/routable/.gitignore b/messagebus/src/tests/routable/.gitignore
new file mode 100644
index 00000000000..beb78afca54
--- /dev/null
+++ b/messagebus/src/tests/routable/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+routable_test
+messagebus_routable_test_app
diff --git a/messagebus/src/tests/routable/CMakeLists.txt b/messagebus/src/tests/routable/CMakeLists.txt
new file mode 100644
index 00000000000..d44eca1dc71
--- /dev/null
+++ b/messagebus/src/tests/routable/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_routable_test_app
+ SOURCES
+ routable.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_routable_test_app COMMAND messagebus_routable_test_app)
diff --git a/messagebus/src/tests/routable/DESC b/messagebus/src/tests/routable/DESC
new file mode 100644
index 00000000000..2aa64df8271
--- /dev/null
+++ b/messagebus/src/tests/routable/DESC
@@ -0,0 +1 @@
+routable test. Take a look at routable.cpp for details.
diff --git a/messagebus/src/tests/routable/FILES b/messagebus/src/tests/routable/FILES
new file mode 100644
index 00000000000..bacc8b159f8
--- /dev/null
+++ b/messagebus/src/tests/routable/FILES
@@ -0,0 +1 @@
+routable.cpp
diff --git a/messagebus/src/tests/routable/routable.cpp b/messagebus/src/tests/routable/routable.cpp
new file mode 100644
index 00000000000..adde39dc51c
--- /dev/null
+++ b/messagebus/src/tests/routable/routable.cpp
@@ -0,0 +1,94 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routable_test");
+#include <vespa/messagebus/error.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/message.h>
+#include <vespa/messagebus/reply.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("routable_test");
+
+ {
+ // Test message swap state.
+ SimpleMessage foo("foo");
+ Route fooRoute = Route::parse("foo");
+ foo.setRoute(fooRoute);
+ foo.setRetry(1);
+ foo.setTimeReceivedNow();
+ foo.setTimeRemaining(2);
+
+ SimpleMessage bar("bar");
+ Route barRoute = Route::parse("bar");
+ bar.setRoute(barRoute);
+ bar.setRetry(3);
+ bar.setTimeReceivedNow();
+ bar.setTimeRemaining(4);
+
+ foo.swapState(bar);
+ EXPECT_EQUAL(barRoute.toString(), foo.getRoute().toString());
+ EXPECT_EQUAL(fooRoute.toString(), bar.getRoute().toString());
+ EXPECT_EQUAL(3u, foo.getRetry());
+ EXPECT_EQUAL(1u, bar.getRetry());
+ EXPECT_TRUE(foo.getTimeReceived() >= bar.getTimeReceived());
+ EXPECT_EQUAL(4u, foo.getTimeRemaining());
+ EXPECT_EQUAL(2u, bar.getTimeRemaining());
+ }
+ {
+ // Test reply swap state.
+ SimpleReply foo("foo");
+ foo.setMessage(Message::UP(new SimpleMessage("foo")));
+ foo.setRetryDelay(1);
+ foo.addError(Error(ErrorCode::APP_FATAL_ERROR, "fatal"));
+ foo.addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "transient"));
+
+ SimpleReply bar("bar");
+ bar.setMessage(Message::UP(new SimpleMessage("bar")));
+ bar.setRetryDelay(2);
+ bar.addError(Error(ErrorCode::ERROR_LIMIT, "err"));
+
+ foo.swapState(bar);
+ EXPECT_EQUAL("bar", static_cast<SimpleMessage&>(*foo.getMessage()).getValue());
+ EXPECT_EQUAL("foo", static_cast<SimpleMessage&>(*bar.getMessage()).getValue());
+ EXPECT_EQUAL(2.0, foo.getRetryDelay());
+ EXPECT_EQUAL(1.0, bar.getRetryDelay());
+ EXPECT_EQUAL(1u, foo.getNumErrors());
+ EXPECT_EQUAL(2u, bar.getNumErrors());
+ }
+ {
+ // Test message discard logic.
+ Receptor handler;
+ SimpleMessage msg("foo");
+ msg.pushHandler(handler);
+ msg.discard();
+
+ Reply::UP reply = handler.getReply(0);
+ ASSERT_TRUE(reply.get() == NULL);
+ }
+ {
+ // Test reply discard logic.
+ Receptor handler;
+ SimpleMessage msg("foo");
+ msg.pushHandler(handler);
+
+ SimpleReply reply("bar");
+ reply.swapState(msg);
+ reply.discard();
+
+ Reply::UP ap = handler.getReply(0);
+ ASSERT_TRUE(ap.get() == NULL);
+ }
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/routablequeue/.gitignore b/messagebus/src/tests/routablequeue/.gitignore
new file mode 100644
index 00000000000..19c961345c9
--- /dev/null
+++ b/messagebus/src/tests/routablequeue/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+routablequeue_test
+messagebus_routablequeue_test_app
diff --git a/messagebus/src/tests/routablequeue/CMakeLists.txt b/messagebus/src/tests/routablequeue/CMakeLists.txt
new file mode 100644
index 00000000000..18e2be0a748
--- /dev/null
+++ b/messagebus/src/tests/routablequeue/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_routablequeue_test_app
+ SOURCES
+ routablequeue.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_routablequeue_test_app COMMAND messagebus_routablequeue_test_app)
diff --git a/messagebus/src/tests/routablequeue/DESC b/messagebus/src/tests/routablequeue/DESC
new file mode 100644
index 00000000000..b1613b4b2f2
--- /dev/null
+++ b/messagebus/src/tests/routablequeue/DESC
@@ -0,0 +1 @@
+routablequeue test. Take a look at routablequeue.cpp for details.
diff --git a/messagebus/src/tests/routablequeue/FILES b/messagebus/src/tests/routablequeue/FILES
new file mode 100644
index 00000000000..44a342e00a3
--- /dev/null
+++ b/messagebus/src/tests/routablequeue/FILES
@@ -0,0 +1 @@
+routablequeue.cpp
diff --git a/messagebus/src/tests/routablequeue/routablequeue.cpp b/messagebus/src/tests/routablequeue/routablequeue.cpp
new file mode 100644
index 00000000000..09d14b03983
--- /dev/null
+++ b/messagebus/src/tests/routablequeue/routablequeue.cpp
@@ -0,0 +1,110 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routablequeue_test");
+
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+class TestMessage : public SimpleMessage {
+private:
+ uint32_t _id;
+ static uint32_t _cnt;
+public:
+ TestMessage(uint32_t id) : SimpleMessage(""), _id(id) { ++_cnt; }
+ virtual ~TestMessage() { --_cnt; }
+ virtual uint32_t getType() const { return _id; }
+ static uint32_t getCnt() { return _cnt; }
+};
+uint32_t TestMessage::_cnt = 0;
+
+class TestReply : public SimpleReply {
+private:
+ uint32_t _id;
+ static uint32_t _cnt;
+public:
+ TestReply(uint32_t id) : SimpleReply(""), _id(id) { ++_cnt; }
+ virtual ~TestReply() { --_cnt; }
+ virtual uint32_t getType() const { return _id; }
+ static uint32_t getCnt() { return _cnt; }
+};
+uint32_t TestReply::_cnt = 0;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("routablequeue_test");
+ {
+ RoutableQueue rq;
+ EXPECT_TRUE(rq.size() == 0);
+ EXPECT_TRUE(rq.dequeue(0).get() == 0);
+ EXPECT_TRUE(rq.dequeue(100).get() == 0);
+ EXPECT_TRUE(TestMessage::getCnt() == 0);
+ EXPECT_TRUE(TestReply::getCnt() == 0);
+ rq.enqueue(Routable::UP(new TestMessage(101)));
+ EXPECT_TRUE(rq.size() == 1);
+ EXPECT_TRUE(TestMessage::getCnt() == 1);
+ EXPECT_TRUE(TestReply::getCnt() == 0);
+ rq.enqueue(Routable::UP(new TestReply(201)));
+ EXPECT_TRUE(rq.size() == 2);
+ EXPECT_TRUE(TestMessage::getCnt() == 1);
+ EXPECT_TRUE(TestReply::getCnt() == 1);
+ rq.handleMessage(Message::UP(new TestMessage(102)));
+ EXPECT_TRUE(rq.size() == 3);
+ EXPECT_TRUE(TestMessage::getCnt() == 2);
+ EXPECT_TRUE(TestReply::getCnt() == 1);
+ rq.handleReply(Reply::UP(new TestReply(202)));
+ EXPECT_TRUE(rq.size() == 4);
+ EXPECT_TRUE(TestMessage::getCnt() == 2);
+ EXPECT_TRUE(TestReply::getCnt() == 2);
+ {
+ Routable::UP r = rq.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ EXPECT_TRUE(rq.size() == 3);
+ EXPECT_TRUE(r->getType() == 101);
+ }
+ EXPECT_TRUE(TestMessage::getCnt() == 1);
+ EXPECT_TRUE(TestReply::getCnt() == 2);
+ {
+ Routable::UP r = rq.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ EXPECT_TRUE(rq.size() == 2);
+ EXPECT_TRUE(r->getType() == 201);
+ }
+ EXPECT_TRUE(TestMessage::getCnt() == 1);
+ EXPECT_TRUE(TestReply::getCnt() == 1);
+ rq.handleMessage(Message::UP(new TestMessage(103)));
+ EXPECT_TRUE(rq.size() == 3);
+ EXPECT_TRUE(TestMessage::getCnt() == 2);
+ EXPECT_TRUE(TestReply::getCnt() == 1);
+ rq.handleReply(Reply::UP(new TestReply(203)));
+ EXPECT_TRUE(rq.size() == 4);
+ EXPECT_TRUE(TestMessage::getCnt() == 2);
+ EXPECT_TRUE(TestReply::getCnt() == 2);
+ {
+ Routable::UP r = rq.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ EXPECT_TRUE(rq.size() == 3);
+ EXPECT_TRUE(r->getType() == 102);
+ }
+ EXPECT_TRUE(TestMessage::getCnt() == 1);
+ EXPECT_TRUE(TestReply::getCnt() == 2);
+ {
+ Routable::UP r = rq.dequeue(0);
+ ASSERT_TRUE(r.get() != 0);
+ EXPECT_TRUE(rq.size() == 2);
+ EXPECT_TRUE(r->getType() == 202);
+ }
+ EXPECT_TRUE(TestMessage::getCnt() == 1);
+ EXPECT_TRUE(TestReply::getCnt() == 1);
+ }
+ EXPECT_TRUE(TestMessage::getCnt() == 0);
+ EXPECT_TRUE(TestReply::getCnt() == 0);
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/routeparser/.gitignore b/messagebus/src/tests/routeparser/.gitignore
new file mode 100644
index 00000000000..8df44f3b268
--- /dev/null
+++ b/messagebus/src/tests/routeparser/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+routeparser_test
+messagebus_routeparser_test_app
diff --git a/messagebus/src/tests/routeparser/CMakeLists.txt b/messagebus/src/tests/routeparser/CMakeLists.txt
new file mode 100644
index 00000000000..712672c6b20
--- /dev/null
+++ b/messagebus/src/tests/routeparser/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_routeparser_test_app
+ SOURCES
+ routeparser.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_routeparser_test_app COMMAND messagebus_routeparser_test_app)
diff --git a/messagebus/src/tests/routeparser/DESC b/messagebus/src/tests/routeparser/DESC
new file mode 100644
index 00000000000..4f38ec8b2bd
--- /dev/null
+++ b/messagebus/src/tests/routeparser/DESC
@@ -0,0 +1 @@
+routeparser test. Take a look at routeparser.cpp for details.
diff --git a/messagebus/src/tests/routeparser/FILES b/messagebus/src/tests/routeparser/FILES
new file mode 100644
index 00000000000..3a562440161
--- /dev/null
+++ b/messagebus/src/tests/routeparser/FILES
@@ -0,0 +1 @@
+routeparser.cpp
diff --git a/messagebus/src/tests/routeparser/routeparser.cpp b/messagebus/src/tests/routeparser/routeparser.cpp
new file mode 100644
index 00000000000..d5522a7167a
--- /dev/null
+++ b/messagebus/src/tests/routeparser/routeparser.cpp
@@ -0,0 +1,280 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routeparser_test");
+
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/routing/errordirective.h>
+#include <vespa/messagebus/routing/policydirective.h>
+#include <vespa/messagebus/routing/route.h>
+#include <vespa/messagebus/routing/routedirective.h>
+#include <vespa/messagebus/routing/tcpdirective.h>
+#include <vespa/messagebus/routing/verbatimdirective.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+class Test : public vespalib::TestApp {
+public:
+ int Main();
+ void testHopParser();
+ void testHopParserErrors();
+ void testRouteParser();
+ void testRouteParserErrors();
+
+private:
+ bool testError(const Route &route, const string &msg);
+ bool testError(const Hop &hop, const string &msg);
+ bool testErrorDirective(IHopDirective::SP dir, const string &msg);
+ bool testPolicyDirective(IHopDirective::SP dir, const string &name, const string &param);
+ bool testRouteDirective(IHopDirective::SP dir, const string &name);
+ bool testTcpDirective(IHopDirective::SP dir, const string &host, uint32_t port, const string &session);
+ bool testVerbatimDirective(IHopDirective::SP dir, const string &image);
+};
+
+TEST_APPHOOK(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("routeparser_test");
+
+ testHopParser(); TEST_FLUSH();
+ testHopParserErrors(); TEST_FLUSH();
+ testRouteParser(); TEST_FLUSH();
+ testRouteParserErrors(); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+bool
+Test::testError(const Route &route, const string &msg)
+{
+ if (!EXPECT_EQUAL(1u, route.getNumHops())) {
+ return false;
+ }
+ if (!testError(route.getHop(0), msg)) {
+ return false;
+ }
+ return true;
+}
+
+bool
+Test::testError(const Hop &hop, const string &msg)
+{
+ LOG(info, "%s", hop.toDebugString().c_str());
+ if (!EXPECT_EQUAL(1u, hop.getNumDirectives())) {
+ return false;
+ }
+ if (!testErrorDirective(hop.getDirective(0), msg)) {
+ return false;
+ }
+ return true;
+}
+
+bool
+Test::testErrorDirective(IHopDirective::SP dir, const string &msg)
+{
+ if (!EXPECT_TRUE(dir.get() != NULL)) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(IHopDirective::TYPE_ERROR, dir->getType())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(msg, static_cast<const ErrorDirective&>(*dir).getMessage())) {
+ return false;
+ }
+ return true;
+}
+
+bool
+Test::testPolicyDirective(IHopDirective::SP dir, const string &name, const string &param)
+{
+ if (!EXPECT_TRUE(dir.get() != NULL)) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(IHopDirective::TYPE_POLICY, dir->getType())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(name, static_cast<const PolicyDirective&>(*dir).getName())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(param, static_cast<const PolicyDirective&>(*dir).getParam())) {
+ return false;
+ }
+ return true;
+}
+
+bool
+Test::testRouteDirective(IHopDirective::SP dir, const string &name)
+{
+ if (!EXPECT_TRUE(dir.get() != NULL)) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(IHopDirective::TYPE_ROUTE, dir->getType())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(name, static_cast<const RouteDirective&>(*dir).getName())) {
+ return false;
+ }
+ return true;
+}
+
+bool
+Test::testTcpDirective(IHopDirective::SP dir, const string &host, uint32_t port, const string &session)
+{
+ if (!EXPECT_TRUE(dir.get() != NULL)) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(IHopDirective::TYPE_TCP, dir->getType())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(host, static_cast<const TcpDirective&>(*dir).getHost())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(port, static_cast<const TcpDirective&>(*dir).getPort())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(session, static_cast<const TcpDirective&>(*dir).getSession())) {
+ return false;
+ }
+ return true;
+}
+
+bool
+Test::testVerbatimDirective(IHopDirective::SP dir, const string &image)
+{
+ if (!EXPECT_TRUE(dir.get() != NULL)) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(IHopDirective::TYPE_VERBATIM, dir->getType())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(image, static_cast<const VerbatimDirective&>(*dir).getImage())) {
+ return false;
+ }
+ return true;
+}
+
+void
+Test::testHopParser()
+{
+ {
+ Hop hop = Hop::parse("foo");
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testVerbatimDirective(hop.getDirective(0), "foo"));
+ }
+ {
+ Hop hop = Hop::parse("foo/bar");
+ EXPECT_EQUAL(2u, hop.getNumDirectives());
+ EXPECT_TRUE(testVerbatimDirective(hop.getDirective(0), "foo"));
+ EXPECT_TRUE(testVerbatimDirective(hop.getDirective(1), "bar"));
+ }
+ {
+ Hop hop = Hop::parse("tcp/foo:666/bar");
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testTcpDirective(hop.getDirective(0), "foo", 666, "bar"));
+ }
+ {
+ Hop hop = Hop::parse("route:foo");
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testRouteDirective(hop.getDirective(0), "foo"));
+ }
+ {
+ Hop hop = Hop::parse("[Extern:tcp/localhost:3619;foo/bar]");
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testPolicyDirective(hop.getDirective(0), "Extern", "tcp/localhost:3619;foo/bar"));
+ }
+ {
+ Hop hop = Hop::parse("[AND:foo bar]");
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testPolicyDirective(hop.getDirective(0), "AND", "foo bar"));
+ }
+ {
+ Hop hop = Hop::parse("[DocumentRouteSelector:raw:route[2]\n"
+ "route[0].name \"foo\"\n"
+ "route[0].selector \"testdoc\"\n"
+ "route[0].feed \"myfeed\"\n"
+ "route[1].name \"bar\"\n"
+ "route[1].selector \"other\"\n"
+ "route[1].feed \"myfeed\"\n"
+ "]");
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testPolicyDirective(hop.getDirective(0), "DocumentRouteSelector",
+ "raw:route[2]\n"
+ "route[0].name \"foo\"\n"
+ "route[0].selector \"testdoc\"\n"
+ "route[0].feed \"myfeed\"\n"
+ "route[1].name \"bar\"\n"
+ "route[1].selector \"other\"\n"
+ "route[1].feed \"myfeed\"\n"));
+ }
+ {
+ Hop hop = Hop::parse("[DocumentRouteSelector:raw:route[1]\n"
+ "route[0].name \"docproc/cluster.foo\"\n"
+ "route[0].selector \"testdoc\"\n"
+ "route[0].feed \"myfeed\"\n"
+ "]");
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testPolicyDirective(hop.getDirective(0), "DocumentRouteSelector",
+ "raw:route[1]\n"
+ "route[0].name \"docproc/cluster.foo\"\n"
+ "route[0].selector \"testdoc\"\n"
+ "route[0].feed \"myfeed\"\n"));
+ }
+}
+
+void
+Test::testHopParserErrors()
+{
+ EXPECT_TRUE(testError(Hop::parse(""), "Failed to parse empty string."));
+ EXPECT_TRUE(testError(Hop::parse("[foo"), "Unexpected token '': syntax error"));
+ EXPECT_TRUE(testError(Hop::parse("foo/[bar]]"), "Unexpected token ']': syntax error"));
+ EXPECT_TRUE(testError(Hop::parse("foo bar"), "Failed to completely parse 'foo bar'."));
+}
+
+void
+Test::testRouteParser()
+{
+ {
+ Route route = Route::parse("foo bar/baz");
+ EXPECT_EQUAL(2u, route.getNumHops());
+ {
+ const Hop &hop = route.getHop(0);
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testVerbatimDirective(hop.getDirective(0), "foo"));
+ }
+ {
+ const Hop &hop = route.getHop(1);
+ EXPECT_EQUAL(2u, hop.getNumDirectives());
+ EXPECT_TRUE(testVerbatimDirective(hop.getDirective(0), "bar"));
+ EXPECT_TRUE(testVerbatimDirective(hop.getDirective(1), "baz"));
+ }
+ }
+ {
+ Route route = Route::parse("[Extern:tcp/localhost:3633;itr/session] default");
+ EXPECT_EQUAL(2u, route.getNumHops());
+ {
+ const Hop &hop = route.getHop(0);
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testPolicyDirective(hop.getDirective(0), "Extern", "tcp/localhost:3633;itr/session"));
+ }
+ {
+ const Hop &hop = route.getHop(1);
+ EXPECT_EQUAL(1u, hop.getNumDirectives());
+ EXPECT_TRUE(testVerbatimDirective(hop.getDirective(0), "default"));
+ }
+ }
+}
+
+void
+Test::testRouteParserErrors()
+{
+ EXPECT_TRUE(testError(Route::parse(""), "Failed to parse empty string."));
+ EXPECT_TRUE(testError(Route::parse("foo [bar"), "Unexpected token '': syntax error"));
+ EXPECT_TRUE(testError(Route::parse("foo bar/[baz]]"), "Unexpected token ']': syntax error"));
+}
diff --git a/messagebus/src/tests/routing/.gitignore b/messagebus/src/tests/routing/.gitignore
new file mode 100644
index 00000000000..9a8bd74d953
--- /dev/null
+++ b/messagebus/src/tests/routing/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+routing_test
+messagebus_routing_test_app
diff --git a/messagebus/src/tests/routing/CMakeLists.txt b/messagebus/src/tests/routing/CMakeLists.txt
new file mode 100644
index 00000000000..c2b38e4c4b4
--- /dev/null
+++ b/messagebus/src/tests/routing/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_routing_test_app
+ SOURCES
+ routing.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_routing_test_app COMMAND messagebus_routing_test_app)
diff --git a/messagebus/src/tests/routing/DESC b/messagebus/src/tests/routing/DESC
new file mode 100644
index 00000000000..bb46d61d6cc
--- /dev/null
+++ b/messagebus/src/tests/routing/DESC
@@ -0,0 +1 @@
+routing test. Take a look at routing.cpp for details.
diff --git a/messagebus/src/tests/routing/FILES b/messagebus/src/tests/routing/FILES
new file mode 100644
index 00000000000..fec1f48186a
--- /dev/null
+++ b/messagebus/src/tests/routing/FILES
@@ -0,0 +1 @@
+routing.cpp
diff --git a/messagebus/src/tests/routing/routing.cpp b/messagebus/src/tests/routing/routing.cpp
new file mode 100644
index 00000000000..b9c673263b5
--- /dev/null
+++ b/messagebus/src/tests/routing/routing.cpp
@@ -0,0 +1,1580 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routing_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routing/errordirective.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/routing/routingcontext.h>
+#include <vespa/messagebus/testlib/custompolicy.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/messagebus/vtag.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Utilities
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class StringList : public std::vector<string> {
+public:
+ StringList &add(const string &str);
+};
+
+StringList &
+StringList::add(const string &str)
+{
+ std::vector<string>::push_back(str);
+ return *this;
+}
+
+class UIntList : public std::vector<uint32_t> {
+public:
+ UIntList &add(uint32_t i);
+};
+
+UIntList &
+UIntList::add(uint32_t i)
+{
+ std::vector<uint32_t>::push_back(i);
+ return *this;
+}
+
+class RemoveReplyPolicy : public CustomPolicy {
+private:
+ uint32_t _idxRemove;
+public:
+ RemoveReplyPolicy(bool selectOnRetry,
+ const std::vector<uint32_t> consumableErrors,
+ const std::vector<Route> routes,
+ uint32_t idxRemove);
+ void merge(RoutingContext &ctx);
+};
+
+RemoveReplyPolicy::RemoveReplyPolicy(bool selectOnRetry,
+ const std::vector<uint32_t> consumableErrors,
+ const std::vector<Route> routes,
+ uint32_t idxRemove) :
+ CustomPolicy::CustomPolicy(selectOnRetry, consumableErrors, routes),
+ _idxRemove(idxRemove)
+{
+ // empty
+}
+
+void
+RemoveReplyPolicy::merge(RoutingContext &ctx)
+{
+ ctx.setReply(ctx.getChildIterator().skip(_idxRemove).removeReply());
+}
+
+class RemoveReplyPolicyFactory : public SimpleProtocol::IPolicyFactory {
+private:
+ bool _selectOnRetry;
+ std::vector<uint32_t> _consumableErrors;
+ uint32_t _idxRemove;
+public:
+ RemoveReplyPolicyFactory(bool selectOnRetry,
+ const std::vector<uint32_t> &consumableErrors,
+ uint32_t idxRemove);
+ IRoutingPolicy::UP create(const string &param);
+};
+
+RemoveReplyPolicyFactory::RemoveReplyPolicyFactory(bool selectOnRetry,
+ const std::vector<uint32_t> &consumableErrors,
+ uint32_t idxRemove) :
+ _selectOnRetry(selectOnRetry),
+ _consumableErrors(consumableErrors),
+ _idxRemove(idxRemove)
+{
+ // empty
+}
+
+IRoutingPolicy::UP
+RemoveReplyPolicyFactory::create(const string &param)
+{
+ std::vector<Route> routes;
+ CustomPolicyFactory::parseRoutes(param, routes);
+ return IRoutingPolicy::UP(new RemoveReplyPolicy(_selectOnRetry, _consumableErrors, routes, _idxRemove));
+}
+
+class ReuseReplyPolicy : public CustomPolicy {
+private:
+ std::vector<uint32_t> _errorMask;
+public:
+ ReuseReplyPolicy(bool selectOnRetry,
+ const std::vector<uint32_t> &errorMask,
+ const std::vector<Route> &routes);
+ void merge(RoutingContext &ctx);
+};
+
+ReuseReplyPolicy::ReuseReplyPolicy(bool selectOnRetry,
+ const std::vector<uint32_t> &errorMask,
+ const std::vector<Route> &routes) :
+ CustomPolicy::CustomPolicy(selectOnRetry, errorMask, routes),
+ _errorMask(errorMask)
+{
+ // empty
+}
+
+void
+ReuseReplyPolicy::merge(RoutingContext &ctx)
+{
+ Reply::UP ret(new EmptyReply());
+ uint32_t idx = 0;
+ int idxFirstOk = -1;
+ for (RoutingNodeIterator it = ctx.getChildIterator();
+ it.isValid(); it.next(), ++idx)
+ {
+ const Reply &ref = it.getReplyRef();
+ if (!ref.hasErrors()) {
+ if (idxFirstOk < 0) {
+ idxFirstOk = idx;
+ }
+ } else {
+ for (uint32_t i = 0; i < ref.getNumErrors(); ++i) {
+ Error err = ref.getError(i);
+ if (find(_errorMask.begin(), _errorMask.end(), err.getCode()) == _errorMask.end()) {
+ ret->addError(err);
+ }
+ }
+ }
+ }
+ if (ret->hasErrors()) {
+ ctx.setReply(std::move(ret));
+ } else {
+ ctx.setReply(ctx.getChildIterator().skip(idxFirstOk).removeReply());
+ }
+}
+
+class ReuseReplyPolicyFactory : public SimpleProtocol::IPolicyFactory {
+private:
+ bool _selectOnRetry;
+ std::vector<uint32_t> _errorMask;
+public:
+ ReuseReplyPolicyFactory(bool selectOnRetry,
+ const std::vector<uint32_t> &errorMask);
+ IRoutingPolicy::UP create(const string &param);
+};
+
+ReuseReplyPolicyFactory::ReuseReplyPolicyFactory(bool selectOnRetry,
+ const std::vector<uint32_t> &errorMask) :
+ _selectOnRetry(selectOnRetry),
+ _errorMask(errorMask)
+{
+ // empty
+}
+
+IRoutingPolicy::UP
+ReuseReplyPolicyFactory::create(const string &param)
+{
+ std::vector<Route> routes;
+ CustomPolicyFactory::parseRoutes(param, routes);
+ return IRoutingPolicy::UP(new ReuseReplyPolicy(_selectOnRetry, _errorMask, routes));
+}
+
+class SetReplyPolicy : public IRoutingPolicy {
+private:
+ bool _selectOnRetry;
+ std::vector<uint32_t> _errors;
+ string _param;
+ uint32_t _idx;
+public:
+ SetReplyPolicy(bool selectOnRetry,
+ const std::vector<uint32_t> &errors,
+ const string &param);
+ void select(RoutingContext &ctx);
+ void merge(RoutingContext &ctx);
+};
+
+SetReplyPolicy::SetReplyPolicy(bool selectOnRetry,
+ const std::vector<uint32_t> &errors,
+ const string &param) :
+ _selectOnRetry(selectOnRetry),
+ _errors(errors),
+ _param(param),
+ _idx(0)
+{
+ // empty
+}
+
+void
+SetReplyPolicy::select(RoutingContext &ctx)
+{
+ uint32_t idx = _idx++;
+ uint32_t err = _errors[idx < _errors.size() ? idx : _errors.size() - 1];
+ if (err != ErrorCode::NONE) {
+ ctx.setError(err, _param);
+ } else {
+ ctx.setReply(Reply::UP(new EmptyReply()));
+ }
+ ctx.setSelectOnRetry(_selectOnRetry);
+}
+
+void
+SetReplyPolicy::merge(RoutingContext &ctx)
+{
+ Reply::UP reply(new EmptyReply());
+ reply->addError(Error(ErrorCode::FATAL_ERROR, "Merge should not be called when select() sets a reply."));
+ ctx.setReply(std::move(reply));
+}
+
+class SetReplyPolicyFactory : public SimpleProtocol::IPolicyFactory {
+private:
+ bool _selectOnRetry;
+ std::vector<uint32_t> _errors;
+public:
+ SetReplyPolicyFactory(bool selectOnRetry,
+ const std::vector<uint32_t> &errors);
+ IRoutingPolicy::UP create(const string &param);
+};
+
+SetReplyPolicyFactory::SetReplyPolicyFactory(bool selectOnRetry,
+ const std::vector<uint32_t> &errors) :
+ _selectOnRetry(selectOnRetry),
+ _errors(errors)
+{
+ // empty
+}
+
+IRoutingPolicy::UP
+SetReplyPolicyFactory::create(const string &param)
+{
+ return IRoutingPolicy::UP(new SetReplyPolicy(_selectOnRetry, _errors, param));
+}
+
+class TestException : public std::exception {
+ virtual const char* what() const throw() {
+ return "{test exception}";
+ }
+};
+
+class SelectExceptionPolicy : public IRoutingPolicy {
+public:
+ void select(RoutingContext &ctx) {
+ (void)ctx;
+ throw TestException();
+ }
+
+ void merge(RoutingContext &ctx) {
+ (void)ctx;
+ }
+};
+
+class SelectExceptionPolicyFactory : public SimpleProtocol::IPolicyFactory {
+public:
+ IRoutingPolicy::UP create(const string &param) {
+ (void)param;
+ return IRoutingPolicy::UP(new SelectExceptionPolicy());
+ }
+};
+
+class MergeExceptionPolicy : public IRoutingPolicy {
+private:
+ const string _select;
+
+public:
+ MergeExceptionPolicy(const string &param)
+ : _select(param)
+ {
+ // empty
+ }
+
+ void select(RoutingContext &ctx) {
+ ctx.addChild(Route::parse(_select));
+ }
+
+ void merge(RoutingContext &ctx) {
+ (void)ctx;
+ throw TestException();
+ }
+};
+
+class MergeExceptionPolicyFactory : public SimpleProtocol::IPolicyFactory {
+public:
+ IRoutingPolicy::UP create(const string &param) {
+ return IRoutingPolicy::UP(new MergeExceptionPolicy(param));
+ }
+};
+
+class MyPolicyFactory : public SimpleProtocol::IPolicyFactory {
+private:
+ string _selectRoute;
+ uint32_t _selectError;
+ bool _selectException;
+ bool _mergeFromChild;
+ uint32_t _mergeError;
+ bool _mergeException;
+
+public:
+ friend class MyPolicy;
+
+ MyPolicyFactory(const string &selectRoute,
+ uint32_t &selectError,
+ bool selectException,
+ bool mergeFromChild,
+ uint32_t mergeError,
+ bool mergeException) :
+ _selectRoute(selectRoute),
+ _selectError(selectError),
+ _selectException(selectException),
+ _mergeFromChild(mergeFromChild),
+ _mergeError(mergeError),
+ _mergeException(mergeException)
+ {
+ // empty
+ }
+
+ IRoutingPolicy::UP
+ create(const string &param);
+
+ static MyPolicyFactory::SP
+ newInstance(const string &selectRoute,
+ uint32_t selectError,
+ bool selectException,
+ bool mergeFromChild,
+ uint32_t mergeError,
+ bool mergeException)
+ {
+ MyPolicyFactory::SP ptr;
+ ptr.reset(new MyPolicyFactory(selectRoute, selectError, selectException,
+ mergeFromChild, mergeError, mergeException));
+ return ptr;
+ }
+
+ static MyPolicyFactory::SP
+ newSelectAndMerge(const string &select)
+ {
+ return newInstance(select, ErrorCode::NONE, false, true, ErrorCode::NONE, false);
+ }
+
+ static MyPolicyFactory::SP
+ newEmptySelection()
+ {
+ return newInstance("", ErrorCode::NONE, false, false, ErrorCode::NONE, false);
+ }
+
+ static MyPolicyFactory::SP
+ newSelectError(uint32_t errCode)
+ {
+ return newInstance("", errCode, false, false, ErrorCode::NONE, false);
+ }
+
+ static MyPolicyFactory::SP
+ newSelectException()
+ {
+ return newInstance("", ErrorCode::NONE, true, false, ErrorCode::NONE, false);
+ }
+
+ static MyPolicyFactory::SP
+ newSelectAndThrow(const string &select)
+ {
+ return newInstance(select, ErrorCode::NONE, true, false, ErrorCode::NONE, false);
+ }
+
+ static MyPolicyFactory::SP
+ newEmptyMerge(const string &select)
+ {
+ return newInstance(select, ErrorCode::NONE, false, false, ErrorCode::NONE, false);
+ }
+
+ static MyPolicyFactory::SP
+ newMergeError(const string &select, int errCode)
+ {
+ return newInstance(select, ErrorCode::NONE, false, false, errCode, false);
+ }
+
+ static MyPolicyFactory::SP
+ newMergeException(const string &select)
+ {
+ return newInstance(select, ErrorCode::NONE, false, false, ErrorCode::NONE, true);
+ }
+
+ static MyPolicyFactory::SP
+ newMergeAndThrow(const string &select)
+ {
+ return newInstance(select, ErrorCode::NONE, false, true, ErrorCode::NONE, true);
+ }
+};
+
+class MyPolicy : public IRoutingPolicy {
+private:
+ const MyPolicyFactory &_parent;
+
+public:
+ MyPolicy(const MyPolicyFactory &parent) :
+ _parent(parent)
+ {
+ // empty
+ }
+
+ virtual void
+ select(RoutingContext &ctx)
+ {
+ if (!_parent._selectRoute.empty()) {
+ ctx.addChild(Route::parse(_parent._selectRoute));
+ }
+ if (_parent._selectError != ErrorCode::NONE) {
+ Reply::UP reply(new EmptyReply());
+ reply->addError(Error(_parent._selectError, "err"));
+ ctx.setReply(std::move(reply));
+ }
+ if (_parent._selectException) {
+ throw TestException();
+ }
+ }
+
+ virtual void
+ merge(RoutingContext &ctx)
+ {
+ if (_parent._mergeError != ErrorCode::NONE) {
+ Reply::UP reply(new EmptyReply());
+ reply->addError(Error(_parent._mergeError, "err"));
+ ctx.setReply(std::move(reply));
+ } else if (_parent._mergeFromChild) {
+ ctx.setReply(ctx.getChildIterator().removeReply());
+ }
+ if (_parent._mergeException) {
+ throw TestException();
+ }
+ }
+};
+
+IRoutingPolicy::UP
+MyPolicyFactory::create(const string &param)
+{
+ (void)param;
+ return IRoutingPolicy::UP(new MyPolicy(*this));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Setup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class TestData {
+public:
+ Slobrok _slobrok;
+ RetryTransientErrorsPolicy::SP _retryPolicy;
+ TestServer _srcServer;
+ SourceSession::UP _srcSession;
+ Receptor _srcHandler;
+ TestServer _dstServer;
+ DestinationSession::UP _dstSession;
+ Receptor _dstHandler;
+
+public:
+ TestData();
+ bool start();
+};
+
+class Test : public vespalib::TestApp {
+private:
+ Message::UP createMessage(const string &msg, uint32_t level = 9);
+ void setupRouting(TestData &data, const RoutingTableSpec &spec);
+ void setupPolicy(TestData &data, const string &policyName,
+ SimpleProtocol::IPolicyFactory::SP policy);
+ bool testAcknowledge(TestData &data);
+ bool testSend(TestData &data, const string &route, uint32_t level = 9);
+ bool testTrace(TestData &data, const std::vector<string> &expected);
+ bool testTrace(const std::vector<string> &expected, const Trace &trace);
+
+ static const double RECEPTOR_TIMEOUT;
+
+public:
+ int Main();
+ void testNoRoutingTable(TestData &data);
+ void testUnknownRoute(TestData &data);
+ void testNoRoute(TestData &data);
+ void testRecognizeHopName(TestData &data);
+ void testRecognizeRouteDirective(TestData &data);
+ void testRecognizeRouteName(TestData &data);
+ void testHopResolutionOverflow(TestData &data);
+ void testRouteResolutionOverflow(TestData &data);
+ void testInsertRoute(TestData &data);
+ void testErrorDirective(TestData &data);
+ void testSelectError(TestData &data);
+ void testSelectNone(TestData &data);
+ void testSelectOne(TestData &data);
+ void testResend1(TestData &data);
+ void testResend2(TestData &data);
+ void testNoResend(TestData &data);
+ void testSelectOnResend(TestData &data);
+ void testNoSelectOnResend(TestData &data);
+ void testCanConsumeError(TestData &data);
+ void testCantConsumeError(TestData &data);
+ void testNestedPolicies(TestData &data);
+ void testRemoveReply(TestData &data);
+ void testSetReply(TestData &data);
+ void testResendSetAndReuseReply(TestData &data);
+ void testResendSetAndRemoveReply(TestData &data);
+ void testHopIgnoresReply(TestData &data);
+ void testHopBlueprintIgnoresReply(TestData &data);
+ void testAcceptEmptyRoute(TestData &data);
+ void testAbortOnlyActiveNodes(TestData &data);
+ void testTimeout(TestData &data);
+ void testUnknownPolicy(TestData &data);
+ void testSelectException(TestData &data);
+ void testMergeException(TestData &data);
+
+ void requireThatIgnoreFlagPersistsThroughHopLookup(TestData &data);
+ void requireThatIgnoreFlagPersistsThroughRouteLookup(TestData &data);
+ void requireThatIgnoreFlagPersistsThroughPolicySelect(TestData &data);
+ void requireThatIgnoreFlagIsSerializedWithMessage(TestData &data);
+ void requireThatIgnoreFlagDoesNotInterfere(TestData &data);
+ void requireThatEmptySelectionCanBeIgnored(TestData &data);
+ void requireThatSelectErrorCanBeIgnored(TestData &data);
+ void requireThatSelectExceptionCanBeIgnored(TestData &data);
+ void requireThatSelectAndThrowCanBeIgnored(TestData &data);
+ void requireThatEmptyMergeCanBeIgnored(TestData &data);
+ void requireThatMergeErrorCanBeIgnored(TestData &data);
+ void requireThatMergeExceptionCanBeIgnored(TestData &data);
+ void requireThatMergeAndThrowCanBeIgnored(TestData &data);
+ void requireThatAllocServiceCanBeIgnored(TestData &data);
+ void requireThatDepthLimitCanBeIgnored(TestData &data);
+};
+
+const double Test::RECEPTOR_TIMEOUT = 120.0;
+
+TEST_APPHOOK(Test);
+
+TestData::TestData() :
+ _slobrok(),
+ _retryPolicy(new RetryTransientErrorsPolicy()),
+ _srcServer(MessageBusParams()
+ .setRetryPolicy(_retryPolicy)
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setSlobrokConfig(_slobrok.config())),
+ _srcSession(),
+ _srcHandler(),
+ _dstServer(MessageBusParams()
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setIdentity(Identity("dst"))
+ .setSlobrokConfig(_slobrok.config())),
+ _dstSession(),
+ _dstHandler()
+{
+ _retryPolicy->setBaseDelay(0);
+}
+
+bool
+TestData::start()
+{
+ _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams()
+ .setThrottlePolicy(IThrottlePolicy::SP())
+ .setReplyHandler(_srcHandler));
+ if (_srcSession.get() == NULL) {
+ return false;
+ }
+ _dstSession = _dstServer.mb.createDestinationSession(DestinationSessionParams()
+ .setName("session")
+ .setMessageHandler(_dstHandler));
+ if (_dstSession.get() == NULL) {
+ return false;
+ }
+ if (!_srcServer.waitSlobrok("dst/session", 1u)) {
+ return false;
+ }
+ return true;
+}
+
+Message::UP
+Test::createMessage(const string &msg, uint32_t level)
+{
+ Message::UP ret(new SimpleMessage(msg));
+ ret->getTrace().setLevel(level);
+ return ret;
+}
+
+void
+Test::setupRouting(TestData &data, const RoutingTableSpec &spec)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(spec));
+}
+
+void
+Test::setupPolicy(TestData &data, const string &policyName,
+ SimpleProtocol::IPolicyFactory::SP policy)
+{
+ IProtocol::SP ptr(new SimpleProtocol());
+ static_cast<SimpleProtocol&>(*ptr).addPolicyFactory(policyName, policy);
+ data._srcServer.mb.putProtocol(ptr);
+}
+
+bool
+Test::testAcknowledge(TestData &data)
+{
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ if (!EXPECT_TRUE(msg.get() != NULL)) {
+ return false;
+ }
+ data._dstSession->acknowledge(std::move(msg));
+ return true;
+}
+
+bool
+Test::testSend(TestData &data, const string &route, uint32_t level)
+{
+ Message::UP msg = createMessage("msg", level);
+ msg->setRoute(Route::parse(route));
+ return data._srcSession->send(std::move(msg)).isAccepted();
+}
+
+bool
+Test::testTrace(TestData &data, const std::vector<string> &expected)
+{
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ if (!EXPECT_TRUE(reply.get() != NULL)) {
+ return false;
+ }
+ printf("%s", reply->getTrace().toString().c_str());
+ if (!EXPECT_TRUE(!reply->hasErrors())) {
+ return false;
+ }
+ return testTrace(expected, reply->getTrace());
+}
+
+bool
+Test::testTrace(const std::vector<string> &expected, const Trace &trace)
+{
+ string version = Vtag::currentVersion.toString();
+ string actual = trace.toString();
+ size_t pos = 0;
+ for (uint32_t i = 0; i < expected.size(); ++i) {
+ string line = expected[i];
+ size_t versionIdx = line.find("${VERSION}");
+ if (versionIdx != string::npos) {
+ line = line.replace(versionIdx, 10, version);
+ }
+ if (line[0] == '-') {
+ string str = line.substr(1);
+ if (!EXPECT_TRUE(actual.find(str, pos) == string::npos)) {
+ LOG(error, "Line %d '%s' not expected.", i, str.c_str());
+ return false;
+ }
+ } else {
+ pos = actual.find(line, pos);
+ if (!EXPECT_TRUE(pos != string::npos)) {
+ LOG(error, "Line %d '%s' missing.", i, line.c_str());
+ return false;
+ }
+ ++pos;
+ }
+ }
+ return true;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("routing_test");
+
+ TestData data;
+ ASSERT_TRUE(data.start());
+
+ testNoRoutingTable(data); TEST_FLUSH();
+ testUnknownRoute(data); TEST_FLUSH();
+ testNoRoute(data); TEST_FLUSH();
+ testRecognizeHopName(data); TEST_FLUSH();
+ testRecognizeRouteDirective(data); TEST_FLUSH();
+ testRecognizeRouteName(data); TEST_FLUSH();
+ testHopResolutionOverflow(data); TEST_FLUSH();
+ testRouteResolutionOverflow(data); TEST_FLUSH();
+ testInsertRoute(data); TEST_FLUSH();
+ testErrorDirective(data); TEST_FLUSH();
+ testSelectError(data); TEST_FLUSH();
+ testSelectNone(data); TEST_FLUSH();
+ testSelectOne(data); TEST_FLUSH();
+ testResend1(data); TEST_FLUSH();
+ testResend2(data); TEST_FLUSH();
+ testNoResend(data); TEST_FLUSH();
+ testSelectOnResend(data); TEST_FLUSH();
+ testNoSelectOnResend(data); TEST_FLUSH();
+ testCanConsumeError(data); TEST_FLUSH();
+ testCantConsumeError(data); TEST_FLUSH();
+ testNestedPolicies(data); TEST_FLUSH();
+ testRemoveReply(data); TEST_FLUSH();
+ testSetReply(data); TEST_FLUSH();
+ testResendSetAndReuseReply(data); TEST_FLUSH();
+ testResendSetAndRemoveReply(data); TEST_FLUSH();
+ testHopIgnoresReply(data); TEST_FLUSH();
+ testHopBlueprintIgnoresReply(data); TEST_FLUSH();
+ testAcceptEmptyRoute(data); TEST_FLUSH();
+ testAbortOnlyActiveNodes(data); TEST_FLUSH();
+ testUnknownPolicy(data); TEST_FLUSH();
+ testSelectException(data); TEST_FLUSH();
+ testMergeException(data); TEST_FLUSH();
+
+ requireThatIgnoreFlagPersistsThroughHopLookup(data); TEST_FLUSH();
+ requireThatIgnoreFlagPersistsThroughRouteLookup(data); TEST_FLUSH();
+ requireThatIgnoreFlagPersistsThroughPolicySelect(data); TEST_FLUSH();
+ requireThatIgnoreFlagIsSerializedWithMessage(data); TEST_FLUSH();
+ requireThatIgnoreFlagDoesNotInterfere(data); TEST_FLUSH();
+ requireThatEmptySelectionCanBeIgnored(data); TEST_FLUSH();
+ requireThatSelectErrorCanBeIgnored(data); TEST_FLUSH();
+ requireThatSelectExceptionCanBeIgnored(data); TEST_FLUSH();
+ requireThatSelectAndThrowCanBeIgnored(data); TEST_FLUSH();
+ requireThatEmptyMergeCanBeIgnored(data); TEST_FLUSH();
+ requireThatMergeErrorCanBeIgnored(data); TEST_FLUSH();
+ requireThatMergeExceptionCanBeIgnored(data); TEST_FLUSH();
+ requireThatMergeAndThrowCanBeIgnored(data); TEST_FLUSH();
+ requireThatAllocServiceCanBeIgnored(data); TEST_FLUSH();
+ requireThatDepthLimitCanBeIgnored(data); TEST_FLUSH();
+
+ // This needs to be last because it changes timeouts:
+ testTimeout(data); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tests
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void
+Test::testNoRoutingTable(TestData &data)
+{
+ Result res = data._srcSession->send(createMessage("msg"), "foo");
+ EXPECT_TRUE(!res.isAccepted());
+ EXPECT_EQUAL((uint32_t)ErrorCode::ILLEGAL_ROUTE, res.getError().getCode());
+ printf("%s\n", res.getError().getMessage().c_str());
+ Message::UP msg = res.getMessage();
+ EXPECT_TRUE(msg.get() != NULL);
+}
+
+void
+Test::testUnknownRoute(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addHop(HopSpec("foo", "bar"))));
+ Result res = data._srcSession->send(createMessage("msg"), "baz");
+ EXPECT_TRUE(!res.isAccepted());
+ EXPECT_EQUAL((uint32_t)ErrorCode::ILLEGAL_ROUTE, res.getError().getCode());
+ printf("%s\n", res.getError().getMessage().c_str());
+ Message::UP msg = res.getMessage();
+ EXPECT_TRUE(msg.get() != NULL);
+}
+
+void
+Test::testNoRoute(TestData &data)
+{
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route()).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::ILLEGAL_ROUTE, reply->getError(0).getCode());
+}
+
+void
+Test::testRecognizeHopName(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addHop(HopSpec("dst", "dst/session"))));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+}
+
+void
+Test::testRecognizeRouteDirective(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("dst").addHop("dst/session"))
+ .addHop(HopSpec("dir", "route:dst"))));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dir")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+}
+
+void
+Test::testRecognizeRouteName(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("dst").addHop("dst/session"))));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+}
+
+void
+Test::testHopResolutionOverflow(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addHop(HopSpec("foo", "bar"))
+ .addHop(HopSpec("bar", "foo"))));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("foo")).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::ILLEGAL_ROUTE, reply->getError(0).getCode());
+}
+
+void
+Test::testRouteResolutionOverflow(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("foo").addHop("route:foo"))));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), "foo").isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::ILLEGAL_ROUTE, reply->getError(0).getCode());
+}
+
+void
+Test::testInsertRoute(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("foo").addHop("dst/session").addHop("bar"))));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("route:foo baz")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ EXPECT_EQUAL(2u, msg->getRoute().getNumHops());
+ EXPECT_EQUAL("bar", msg->getRoute().getHop(0).toString());
+ EXPECT_EQUAL("baz", msg->getRoute().getHop(1).toString());
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+}
+
+void
+Test::testErrorDirective(TestData &data)
+{
+ Route route = Route::parse("foo/bar/baz");
+ route.getHop(0).setDirective(1, IHopDirective::SP(new ErrorDirective("err")));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), route).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::ILLEGAL_ROUTE, reply->getError(0).getCode());
+ EXPECT_EQUAL("err", reply->getError(0).getMessage());
+}
+
+void
+Test::testSelectError(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom: ]")).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ LOG(info, "testSelectError trace=%s", reply->getTrace().toString().c_str());
+ LOG(info, "testSelectError error=%s", reply->getError(0).toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::ILLEGAL_ROUTE, reply->getError(0).getCode());
+}
+
+void
+Test::testSelectNone(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom]")).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::NO_SERVICES_FOR_ROUTE, reply->getError(0).getCode());
+}
+
+void
+Test::testSelectOne(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:dst/session]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+}
+
+void
+Test::testResend1(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "err1"));
+ data._dstSession->reply(std::move(reply));
+ msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ reply.reset(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "err2"));
+ data._dstSession->reply(std::move(reply));
+ msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("[APP_TRANSIENT_ERROR @ localhost]: err1")
+ .add("-[APP_TRANSIENT_ERROR @ localhost]: err1")
+ .add("[APP_TRANSIENT_ERROR @ localhost]: err2")
+ .add("-[APP_TRANSIENT_ERROR @ localhost]: err2"),
+ reply->getTrace()));
+}
+
+void
+Test::testResend2(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:dst/session]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "err1"));
+ data._dstSession->reply(std::move(reply));
+ msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ reply.reset(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "err2"));
+ data._dstSession->reply(std::move(reply));
+ msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Source session accepted a 3 byte message. 1 message(s) now pending.")
+ .add("Running routing policy 'Custom'.")
+ .add("Selecting { 'dst/session' }.")
+ .add("Component 'dst/session' selected by policy 'Custom'.")
+ .add("Resolving 'dst/session'.")
+ .add("Sending message (version ${VERSION}) from client to 'dst/session'")
+ .add("Message (type 1) received at 'dst' for session 'session'.")
+ .add("[APP_TRANSIENT_ERROR @ localhost]: err1")
+ .add("Sending reply (version ${VERSION}) from 'dst'.")
+ .add("Reply (type 0) received at client.")
+ .add("Routing policy 'Custom' merging replies.")
+ .add("Merged { 'dst/session' }.")
+ .add("Message scheduled for retry 1 in 0.00 seconds.")
+ .add("Resender resending message.")
+ .add("Running routing policy 'Custom'.")
+ .add("Selecting { 'dst/session' }.")
+ .add("Component 'dst/session' selected by policy 'Custom'.")
+ .add("Resolving 'dst/session'.")
+ .add("Sending message (version ${VERSION}) from client to 'dst/session'")
+ .add("Message (type 1) received at 'dst' for session 'session'.")
+ .add("[APP_TRANSIENT_ERROR @ localhost]: err2")
+ .add("Sending reply (version ${VERSION}) from 'dst'.")
+ .add("Reply (type 0) received at client.")
+ .add("Routing policy 'Custom' merging replies.")
+ .add("Merged { 'dst/session' }.")
+ .add("Message scheduled for retry 2 in 0.00 seconds.")
+ .add("Resender resending message.")
+ .add("Running routing policy 'Custom'.")
+ .add("Selecting { 'dst/session' }.")
+ .add("Component 'dst/session' selected by policy 'Custom'.")
+ .add("Resolving 'dst/session'.")
+ .add("Sending message (version ${VERSION}) from client to 'dst/session'")
+ .add("Message (type 1) received at 'dst' for session 'session'.")
+ .add("Sending reply (version ${VERSION}) from 'dst'.")
+ .add("Reply (type 0) received at client.")
+ .add("Routing policy 'Custom' merging replies.")
+ .add("Merged { 'dst/session' }.")
+ .add("Source session received reply. 0 message(s) now pending."),
+ reply->getTrace()));
+}
+
+void
+Test::testNoResend(TestData &data)
+{
+ data._retryPolicy->setEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "err1"));
+ data._dstSession->reply(std::move(reply));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::APP_TRANSIENT_ERROR, reply->getError(0).getCode());
+}
+
+void
+Test::testSelectOnResend(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:dst/session]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "err"));
+ data._dstSession->reply(std::move(reply));
+ msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Selecting { 'dst/session' }.")
+ .add("[APP_TRANSIENT_ERROR @ localhost]")
+ .add("-[APP_TRANSIENT_ERROR @ localhost]")
+ .add("Merged { 'dst/session' }.")
+ .add("Selecting { 'dst/session' }.")
+ .add("Sending reply")
+ .add("Merged { 'dst/session' }."),
+ reply->getTrace()));
+}
+
+void
+Test::testNoSelectOnResend(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(false)));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:dst/session]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "err"));
+ data._dstSession->reply(std::move(reply));
+ msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Selecting { 'dst/session' }.")
+ .add("[APP_TRANSIENT_ERROR @ localhost]")
+ .add("-[APP_TRANSIENT_ERROR @ localhost]")
+ .add("Merged { 'dst/session' }.")
+ .add("-Selecting { 'dst/session' }.")
+ .add("Sending reply")
+ .add("Merged { 'dst/session' }."),
+ reply->getTrace()));
+}
+
+void
+Test::testCanConsumeError(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(true, ErrorCode::NO_ADDRESS_FOR_SERVICE)));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:dst/session,dst/unknown]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::NO_ADDRESS_FOR_SERVICE, reply->getError(0).getCode());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Selecting { 'dst/session', 'dst/unknown' }.")
+ .add("[NO_ADDRESS_FOR_SERVICE @ localhost]")
+ .add("Sending reply")
+ .add("Merged { 'dst/session', 'dst/unknown' }."),
+ reply->getTrace()));
+}
+
+void
+Test::testCantConsumeError(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:dst/unknown]")).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::NO_ADDRESS_FOR_SERVICE, reply->getError(0).getCode());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Selecting { 'dst/unknown' }.")
+ .add("[NO_ADDRESS_FOR_SERVICE @ localhost]")
+ .add("Merged { 'dst/unknown' }."),
+ reply->getTrace()));
+}
+
+void
+Test::testNestedPolicies(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(true, ErrorCode::NO_ADDRESS_FOR_SERVICE)));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:[Custom:dst/session],[Custom:dst/unknown]]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::NO_ADDRESS_FOR_SERVICE, reply->getError(0).getCode());
+}
+
+void
+Test::testRemoveReply(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new RemoveReplyPolicyFactory(
+ true,
+ UIntList().add(ErrorCode::NO_ADDRESS_FOR_SERVICE),
+ 0)));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:[Custom:dst/session],[Custom:dst/unknown]]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("[NO_ADDRESS_FOR_SERVICE @ localhost]")
+ .add("-[NO_ADDRESS_FOR_SERVICE @ localhost]")
+ .add("Sending message")
+ .add("-Sending message"),
+ reply->getTrace()));
+}
+
+void
+Test::testSetReply(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Select", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(true, ErrorCode::APP_FATAL_ERROR)));
+ simple.addPolicyFactory("SetReply", SimpleProtocol::IPolicyFactory::SP(new SetReplyPolicyFactory(true, UIntList().add(ErrorCode::APP_FATAL_ERROR))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(false);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Select:[SetReply:foo],dst/session]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::APP_FATAL_ERROR, reply->getError(0).getCode());
+ EXPECT_EQUAL("foo", reply->getError(0).getMessage());
+}
+
+void
+Test::testResendSetAndReuseReply(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("ReuseReply", SimpleProtocol::IPolicyFactory::SP(new ReuseReplyPolicyFactory(
+ false,
+ UIntList().add(ErrorCode::APP_FATAL_ERROR))));
+ simple.addPolicyFactory("SetReply", SimpleProtocol::IPolicyFactory::SP(new SetReplyPolicyFactory(
+ false,
+ UIntList().add(ErrorCode::APP_FATAL_ERROR))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[ReuseReply:[SetReply:foo],dst/session]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_TRANSIENT_ERROR, "dst"));
+ data._dstSession->reply(std::move(reply));
+ msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+}
+
+void
+Test::testResendSetAndRemoveReply(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("RemoveReply", SimpleProtocol::IPolicyFactory::SP(new RemoveReplyPolicyFactory(
+ false,
+ UIntList().add(ErrorCode::APP_TRANSIENT_ERROR),
+ 0)));
+ simple.addPolicyFactory("SetReply", SimpleProtocol::IPolicyFactory::SP(new SetReplyPolicyFactory(
+ false,
+ UIntList().add(ErrorCode::APP_TRANSIENT_ERROR).add(ErrorCode::APP_FATAL_ERROR))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[RemoveReply:[SetReply:foo],dst/session]")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::APP_FATAL_ERROR, reply->getError(0).getCode());
+ EXPECT_EQUAL("foo", reply->getError(0).getMessage());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Resolving '[SetReply:foo]'.")
+ .add("Resolving 'dst/session'.")
+ .add("Resender resending message.")
+ .add("Resolving 'dst/session'.")
+ .add("Resolving '[SetReply:foo]'."),
+ reply->getTrace()));
+}
+
+void
+Test::testHopIgnoresReply(TestData &data)
+{
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("?dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR, "dst"));
+ data._dstSession->reply(std::move(reply));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Not waiting for a reply from 'dst/session'."),
+ reply->getTrace()));
+}
+
+void
+Test::testHopBlueprintIgnoresReply(TestData &data)
+{
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addHop(HopSpec("foo", "dst/session").setIgnoreResult(true))));
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("foo")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR, "dst"));
+ data._dstSession->reply(std::move(reply));
+ reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ EXPECT_TRUE(testTrace(StringList()
+ .add("Not waiting for a reply from 'dst/session'."),
+ reply->getTrace()));
+}
+
+void
+Test::testAcceptEmptyRoute(TestData &data)
+{
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/session")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ const Route &route = msg->getRoute();
+ EXPECT_EQUAL(0u, route.getNumHops());
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+}
+
+void
+Test::testAbortOnlyActiveNodes(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(false)));
+ simple.addPolicyFactory("SetReply", SimpleProtocol::IPolicyFactory::SP(new SetReplyPolicyFactory(
+ false,
+ UIntList()
+ .add(ErrorCode::APP_TRANSIENT_ERROR)
+ .add(ErrorCode::APP_TRANSIENT_ERROR)
+ .add(ErrorCode::APP_FATAL_ERROR))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._retryPolicy->setEnabled(true);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Custom:[SetReply:foo],?bar,dst/session]")).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(2u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::APP_FATAL_ERROR, reply->getError(0).getCode());
+ EXPECT_EQUAL((uint32_t)ErrorCode::SEND_ABORTED, reply->getError(1).getCode());
+}
+
+void
+Test::testUnknownPolicy(TestData &data)
+{
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("[Unknown]")).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::UNKNOWN_POLICY, reply->getError(0).getCode());
+}
+
+void
+Test::testSelectException(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("SelectException",
+ SimpleProtocol::IPolicyFactory::SP(
+ new SelectExceptionPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"),
+ Route::parse("[SelectException]"))
+ .isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::POLICY_ERROR,
+ reply->getError(0).getCode());
+ EXPECT_EQUAL("Policy 'SelectException' threw an exception; {test exception}",
+ reply->getError(0).getMessage());
+}
+
+void
+Test::testMergeException(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("MergeException",
+ SimpleProtocol::IPolicyFactory::SP(
+ new MergeExceptionPolicyFactory()));
+ data._srcServer.mb.putProtocol(protocol);
+ Route route = Route::parse("[MergeException:dst/session]");
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), route)
+ .isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::POLICY_ERROR,
+ reply->getError(0).getCode());
+ EXPECT_EQUAL("Policy 'MergeException' threw an exception; {test exception}",
+ reply->getError(0).getMessage());
+}
+
+void
+Test::requireThatIgnoreFlagPersistsThroughHopLookup(TestData &data)
+{
+ setupRouting(data, RoutingTableSpec(SimpleProtocol::NAME).addHop(HopSpec("foo", "dst/unknown")));
+ ASSERT_TRUE(testSend(data, "?foo"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatIgnoreFlagPersistsThroughRouteLookup(TestData &data)
+{
+ setupRouting(data, RoutingTableSpec(SimpleProtocol::NAME).addRoute(RouteSpec("foo").addHop("dst/unknown")));
+ ASSERT_TRUE(testSend(data, "?foo"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatIgnoreFlagPersistsThroughPolicySelect(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newSelectAndMerge("dst/unknown"));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatIgnoreFlagIsSerializedWithMessage(TestData &data)
+{
+ ASSERT_TRUE(testSend(data, "dst/session foo ?bar"));
+ Message::UP msg = data._dstHandler.getMessage(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ Route route = msg->getRoute();
+ EXPECT_EQUAL(2u, route.getNumHops());
+ Hop hop = route.getHop(0);
+ EXPECT_EQUAL("foo", hop.toString());
+ EXPECT_TRUE(!hop.getIgnoreResult());
+ hop = route.getHop(1);
+ EXPECT_EQUAL("?bar", hop.toString());
+ EXPECT_TRUE(hop.getIgnoreResult());
+ data._dstSession->acknowledge(std::move(msg));
+ ASSERT_TRUE(testTrace(data, StringList().add("-Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatIgnoreFlagDoesNotInterfere(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newSelectAndMerge("dst/session"));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testTrace(data, StringList().add("-Ignoring errors in reply.")));
+ ASSERT_TRUE(testAcknowledge(data));
+}
+
+void
+Test::requireThatEmptySelectionCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newEmptySelection());
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatSelectErrorCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newSelectError(ErrorCode::APP_FATAL_ERROR));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatSelectExceptionCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newSelectException());
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatSelectAndThrowCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newSelectAndThrow("dst/session"));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatEmptyMergeCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newEmptyMerge("dst/session"));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testAcknowledge(data));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatMergeErrorCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newMergeError("dst/session", ErrorCode::APP_FATAL_ERROR));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testAcknowledge(data));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatMergeExceptionCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newMergeException("dst/session"));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testAcknowledge(data));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatMergeAndThrowCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newMergeAndThrow("dst/session"));
+ ASSERT_TRUE(testSend(data, "?[Custom]"));
+ ASSERT_TRUE(testAcknowledge(data));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatAllocServiceCanBeIgnored(TestData &data)
+{
+ ASSERT_TRUE(testSend(data, "?dst/unknown"));
+ ASSERT_TRUE(testTrace(data, StringList().add("Ignoring errors in reply.")));
+}
+
+void
+Test::requireThatDepthLimitCanBeIgnored(TestData &data)
+{
+ setupPolicy(data, "Custom", MyPolicyFactory::newSelectAndMerge("[Custom]"));
+ ASSERT_TRUE(testSend(data, "?[Custom]", 0));
+ ASSERT_TRUE(testTrace(data, StringList()));
+}
+
+void
+Test::testTimeout(TestData &data)
+{
+ data._retryPolicy->setEnabled(true);
+ data._retryPolicy->setBaseDelay(0.01);
+ data._srcSession->setTimeout(0.5);
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("dst/unknown")).isAccepted());
+ Reply::UP reply = data._srcHandler.getReply(RECEPTOR_TIMEOUT);
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_EQUAL(2u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::NO_ADDRESS_FOR_SERVICE, reply->getError(0).getCode());
+ EXPECT_EQUAL((uint32_t)ErrorCode::TIMEOUT, reply->getError(1).getCode());
+}
diff --git a/messagebus/src/tests/routingcontext/.gitignore b/messagebus/src/tests/routingcontext/.gitignore
new file mode 100644
index 00000000000..7c3133e4bca
--- /dev/null
+++ b/messagebus/src/tests/routingcontext/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+routingcontext_test
+messagebus_routingcontext_test_app
diff --git a/messagebus/src/tests/routingcontext/CMakeLists.txt b/messagebus/src/tests/routingcontext/CMakeLists.txt
new file mode 100644
index 00000000000..064487d2d71
--- /dev/null
+++ b/messagebus/src/tests/routingcontext/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_routingcontext_test_app
+ SOURCES
+ routingcontext.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_routingcontext_test_app COMMAND messagebus_routingcontext_test_app)
diff --git a/messagebus/src/tests/routingcontext/DESC b/messagebus/src/tests/routingcontext/DESC
new file mode 100644
index 00000000000..9e52d1d8055
--- /dev/null
+++ b/messagebus/src/tests/routingcontext/DESC
@@ -0,0 +1 @@
+routingcontext test. Take a look at routingcontext.cpp for details.
diff --git a/messagebus/src/tests/routingcontext/FILES b/messagebus/src/tests/routingcontext/FILES
new file mode 100644
index 00000000000..8eb1e780a73
--- /dev/null
+++ b/messagebus/src/tests/routingcontext/FILES
@@ -0,0 +1 @@
+routingcontext.cpp
diff --git a/messagebus/src/tests/routingcontext/routingcontext.cpp b/messagebus/src/tests/routingcontext/routingcontext.cpp
new file mode 100644
index 00000000000..02c7ef6dd72
--- /dev/null
+++ b/messagebus/src/tests/routingcontext/routingcontext.cpp
@@ -0,0 +1,389 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routingcontext_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/routing/routingcontext.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/vstringfmt.h>
+
+using namespace mbus;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Utilities
+//
+////////////////////////////////////////////////////////////////////////////////
+
+using vespalib::make_vespa_string;
+
+static const double TIMEOUT = 120;
+
+class StringList : public std::vector<string> {
+public:
+ StringList &add(const string &str);
+};
+
+StringList &
+StringList::add(const string &str)
+{
+ std::vector<string>::push_back(str); return *this;
+}
+
+class CustomPolicyFactory : public SimpleProtocol::IPolicyFactory {
+private:
+ friend class CustomPolicy;
+
+ bool _forward;
+ std::vector<string> _expectedAll;
+ std::vector<string> _expectedMatched;
+
+public:
+ CustomPolicyFactory(bool forward,
+ const std::vector<string> &all,
+ const std::vector<string> &matched);
+ IRoutingPolicy::UP create(const string &param);
+};
+
+class CustomPolicy : public IRoutingPolicy {
+private:
+ CustomPolicyFactory &_factory;
+
+public:
+ CustomPolicy(CustomPolicyFactory &factory);
+ void select(RoutingContext &ctx);
+ void merge(RoutingContext &ctx);
+};
+
+CustomPolicy::CustomPolicy(CustomPolicyFactory &factory) :
+ _factory(factory)
+{
+ // empty
+}
+
+void
+CustomPolicy::select(RoutingContext &ctx)
+{
+ Reply::UP reply(new EmptyReply());
+ reply->getTrace().setLevel(9);
+
+ const std::vector<Route> &all = ctx.getAllRecipients();
+ if (_factory._expectedAll.size() == all.size()) {
+ ctx.trace(1, make_vespa_string("Got %d expected recipients.", (uint32_t)all.size()));
+ for (std::vector<Route>::const_iterator it = all.begin();
+ it != all.end(); ++it)
+ {
+ if (find(_factory._expectedAll.begin(), _factory._expectedAll.end(), it->toString()) != _factory._expectedAll.end()) {
+ ctx.trace(1, make_vespa_string("Got expected recipient '%s'.", it->toString().c_str()));
+ } else {
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR,
+ make_vespa_string("Matched recipient '%s' not expected.",
+ it->toString().c_str())));
+ }
+ }
+ } else {
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR,
+ make_vespa_string("Expected %d recipients, got %d.",
+ (uint32_t)_factory._expectedAll.size(),
+ (uint32_t)all.size())));
+ }
+
+ if (ctx.getNumRecipients() == all.size()) {
+ for (uint32_t i = 0; i < all.size(); ++i) {
+ if (all[i].toString() == ctx.getRecipient(i).toString()) {
+ ctx.trace(1, make_vespa_string("getRecipient(%d) matches getAllRecipients()[%d]", i, i));
+ } else {
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR,
+ make_vespa_string("getRecipient(%d) differs from getAllRecipients()[%d]", i, i)));
+ }
+ }
+ } else {
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR,
+ "getNumRecipients() differs from getAllRecipients().size()"));
+ }
+
+ std::vector<Route> matched;
+ ctx.getMatchedRecipients(matched);
+ if (_factory._expectedMatched.size() == matched.size()) {
+ ctx.trace(1, make_vespa_string("Got %d expected recipients.", (uint32_t)matched.size()));
+ for (std::vector<Route>::iterator it = matched.begin();
+ it != matched.end(); ++it)
+ {
+ if (find(_factory._expectedMatched.begin(), _factory._expectedMatched.end(), it->toString()) != _factory._expectedMatched.end()) {
+ ctx.trace(1, make_vespa_string("Got matched recipient '%s'.", it->toString().c_str()));
+ } else {
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR,
+ make_vespa_string("Matched recipient '%s' not expected.",
+ it->toString().c_str())));
+ }
+ }
+ } else {
+ reply->addError(Error(ErrorCode::APP_FATAL_ERROR,
+ make_vespa_string("Expected %d matched recipients, got %d.",
+ (uint32_t)_factory._expectedMatched.size(),
+ (uint32_t)matched.size())));
+ }
+
+ if (!reply->hasErrors() && _factory._forward) {
+ for (std::vector<Route>::iterator it = matched.begin();
+ it != matched.end(); ++it)
+ {
+ ctx.addChild(*it);
+ }
+ } else {
+ ctx.setReply(std::move(reply));
+ }
+}
+
+void
+CustomPolicy::merge(RoutingContext &ctx)
+{
+ Reply::UP ret(new EmptyReply());
+ for (RoutingNodeIterator it = ctx.getChildIterator();
+ it.isValid(); it.next())
+ {
+ const Reply &reply = it.getReplyRef();
+ for (uint32_t i = 0; i < reply.getNumErrors(); ++i) {
+ ret->addError(reply.getError(i));
+ }
+ }
+ ctx.setReply(std::move(ret));
+}
+
+CustomPolicyFactory::CustomPolicyFactory(bool forward,
+ const std::vector<string> &all,
+ const std::vector<string> &matched) :
+ _forward(forward),
+ _expectedAll(all),
+ _expectedMatched(matched)
+{
+ // empty
+}
+
+IRoutingPolicy::UP
+CustomPolicyFactory::create(const string &)
+{
+ return IRoutingPolicy::UP(new CustomPolicy(*this));
+}
+
+Message::UP
+createMessage(const string &msg)
+{
+ Message::UP ret(new SimpleMessage(msg));
+ ret->getTrace().setLevel(9);
+ return ret;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Setup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class TestData {
+public:
+ Slobrok _slobrok;
+ RetryTransientErrorsPolicy::SP _retryPolicy;
+ TestServer _srcServer;
+ SourceSession::UP _srcSession;
+ Receptor _srcHandler;
+ TestServer _dstServer;
+ DestinationSession::UP _dstSession;
+ Receptor _dstHandler;
+
+public:
+ TestData();
+ bool start();
+};
+
+class Test : public vespalib::TestApp {
+private:
+ Message::UP createMessage(const string &msg);
+
+public:
+ int Main();
+ void testSingleDirective(TestData &data);
+ void testMoreDirectives(TestData &data);
+ void testRecipientsRemain(TestData &data);
+ void testConstRoute(TestData &data);
+};
+
+TEST_APPHOOK(Test);
+
+TestData::TestData() :
+ _slobrok(),
+ _retryPolicy(new RetryTransientErrorsPolicy()),
+ _srcServer(MessageBusParams().setRetryPolicy(_retryPolicy).addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams().setSlobrokConfig(_slobrok.config())),
+ _srcSession(),
+ _srcHandler(),
+ _dstServer(MessageBusParams().addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams().setIdentity(Identity("dst")).setSlobrokConfig(_slobrok.config())),
+ _dstSession(),
+ _dstHandler()
+{
+ _retryPolicy->setBaseDelay(0);
+}
+
+bool
+TestData::start()
+{
+ _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams().setReplyHandler(_srcHandler));
+ if (_srcSession.get() == NULL) {
+ return false;
+ }
+ _dstSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("session").setMessageHandler(_dstHandler));
+ if (_dstSession.get() == NULL) {
+ return false;
+ }
+ if (!_srcServer.waitSlobrok("dst/session", 1u)) {
+ return false;
+ }
+ return true;
+}
+
+Message::UP
+Test::createMessage(const string &msg)
+{
+ Message::UP ret(new SimpleMessage(msg));
+ ret->getTrace().setLevel(9);
+ return ret;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("routingcontext_test");
+
+ TestData data;
+ ASSERT_TRUE(data.start());
+
+ testSingleDirective(data); TEST_FLUSH();
+ testMoreDirectives(data); TEST_FLUSH();
+ testRecipientsRemain(data); TEST_FLUSH();
+ testConstRoute(data); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tests
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void
+Test::testSingleDirective(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(
+ false,
+ StringList().add("foo").add("bar").add("baz/cox"),
+ StringList().add("foo").add("bar"))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("myroute").addHop("myhop"))
+ .addHop(HopSpec("myhop", "[Custom]")
+ .addRecipient("foo")
+ .addRecipient("bar")
+ .addRecipient("baz/cox"))));
+ for (uint32_t i = 0; i < 2; ++i) {
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), "myroute").isAccepted());
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ }
+}
+
+void
+Test::testMoreDirectives(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("Custom", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(
+ false,
+ StringList().add("foo").add("foo/bar").add("foo/bar0/baz").add("foo/bar1/baz").add("foo/bar/baz/cox"),
+ StringList().add("foo/bar0/baz").add("foo/bar1/baz"))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("myroute").addHop("myhop"))
+ .addHop(HopSpec("myhop", "foo/[Custom]/baz")
+ .addRecipient("foo")
+ .addRecipient("foo/bar")
+ .addRecipient("foo/bar0/baz")
+ .addRecipient("foo/bar1/baz")
+ .addRecipient("foo/bar/baz/cox"))));
+ for (uint32_t i = 0; i < 2; ++i) {
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), "myroute").isAccepted());
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ }
+}
+
+void
+Test::testRecipientsRemain(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("First", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(
+ true,
+ StringList().add("foo/bar"),
+ StringList().add("foo/[Second]"))));
+ simple.addPolicyFactory("Second", SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(
+ false,
+ StringList().add("foo/bar"),
+ StringList().add("foo/bar"))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("myroute").addHop("myhop"))
+ .addHop(HopSpec("myhop", "[First]/[Second]")
+ .addRecipient("foo/bar"))));
+ for (uint32_t i = 0; i < 2; ++i) {
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), "myroute").isAccepted());
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ }
+}
+
+void
+Test::testConstRoute(TestData &data)
+{
+ IProtocol::SP protocol(new SimpleProtocol());
+ SimpleProtocol &simple = static_cast<SimpleProtocol&>(*protocol);
+ simple.addPolicyFactory("DocumentRouteSelector",
+ SimpleProtocol::IPolicyFactory::SP(new CustomPolicyFactory(
+ true,
+ StringList().add("dst"),
+ StringList().add("dst"))));
+ data._srcServer.mb.putProtocol(protocol);
+ data._srcServer.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addRoute(RouteSpec("default").addHop("indexing"))
+ .addHop(HopSpec("indexing", "[DocumentRouteSelector]").addRecipient("dst"))
+ .addHop(HopSpec("dst", "dst/session"))));
+ for (uint32_t i = 0; i < 2; ++i) {
+ EXPECT_TRUE(data._srcSession->send(createMessage("msg"), Route::parse("route:default")).isAccepted());
+ Message::UP msg = data._dstHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ data._dstSession->acknowledge(std::move(msg));
+ Reply::UP reply = data._srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ printf("%s", reply->getTrace().toString().c_str());
+ EXPECT_TRUE(!reply->hasErrors());
+ }
+}
+
diff --git a/messagebus/src/tests/routingspec/.gitignore b/messagebus/src/tests/routingspec/.gitignore
new file mode 100644
index 00000000000..cd168e7016f
--- /dev/null
+++ b/messagebus/src/tests/routingspec/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+routingspec_test
+messagebus_routingspec_test_app
diff --git a/messagebus/src/tests/routingspec/CMakeLists.txt b/messagebus/src/tests/routingspec/CMakeLists.txt
new file mode 100644
index 00000000000..43539e07af5
--- /dev/null
+++ b/messagebus/src/tests/routingspec/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_routingspec_test_app
+ SOURCES
+ routingspec.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_routingspec_test_app COMMAND messagebus_routingspec_test_app)
diff --git a/messagebus/src/tests/routingspec/DESC b/messagebus/src/tests/routingspec/DESC
new file mode 100644
index 00000000000..28d7f54decc
--- /dev/null
+++ b/messagebus/src/tests/routingspec/DESC
@@ -0,0 +1 @@
+routingspec test. Take a look at routingspec.cpp for details.
diff --git a/messagebus/src/tests/routingspec/FILES b/messagebus/src/tests/routingspec/FILES
new file mode 100644
index 00000000000..4ae228ad5b9
--- /dev/null
+++ b/messagebus/src/tests/routingspec/FILES
@@ -0,0 +1 @@
+routingspec.cpp
diff --git a/messagebus/src/tests/routingspec/routingspec.cpp b/messagebus/src/tests/routingspec/routingspec.cpp
new file mode 100644
index 00000000000..d5317dc3bb0
--- /dev/null
+++ b/messagebus/src/tests/routingspec/routingspec.cpp
@@ -0,0 +1,251 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("routingspec_test");
+
+#include <vespa/config/config.h>
+#include <vespa/messagebus/configagent.h>
+#include <vespa/messagebus/iconfighandler.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/config-messagebus.h>
+
+using namespace mbus;
+using namespace messagebus;
+using namespace config;
+
+class ConfigStore : public IConfigHandler {
+private:
+ RoutingSpec _routing;
+
+public:
+ ConfigStore() : _routing() {
+ // empty
+ }
+
+ bool setupRouting(const RoutingSpec &spec) {
+ _routing = spec;
+ return true;
+ }
+
+ const RoutingSpec &getRoutingSpec() {
+ return _routing;
+ }
+};
+
+class Test : public vespalib::TestApp {
+private:
+ bool testRouting(const RoutingSpec &spec);
+ bool testConfig(const RoutingSpec &spec);
+
+public:
+ void testConstructors();
+ void testConfigGeneration();
+ int Main();
+};
+
+TEST_APPHOOK(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("routingspec_test");
+
+ testConstructors(); TEST_FLUSH();
+ testConfigGeneration(); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+void
+Test::testConstructors()
+{
+ {
+ RoutingSpec spec;
+ spec.addTable(RoutingTableSpec("foo"));
+ spec.getTable(0).addHop(HopSpec("foo-h1", "foo-h1-sel"));
+ spec.getTable(0).getHop(0).addRecipient("foo-h1-r1");
+ spec.getTable(0).getHop(0).addRecipient("foo-h1-r2");
+ spec.getTable(0).addHop(HopSpec("foo-h2", "foo-h2-sel"));
+ spec.getTable(0).getHop(1).addRecipient("foo-h2-r1");
+ spec.getTable(0).getHop(1).addRecipient("foo-h2-r2");
+ spec.getTable(0).addRoute(RouteSpec("foo-r1"));
+ spec.getTable(0).getRoute(0).addHop("foo-h1");
+ spec.getTable(0).getRoute(0).addHop("foo-h2");
+ spec.getTable(0).addRoute(RouteSpec("foo-r2"));
+ spec.getTable(0).getRoute(1).addHop("foo-h2");
+ spec.getTable(0).getRoute(1).addHop("foo-h1");
+ spec.addTable(RoutingTableSpec("bar"));
+ spec.getTable(1).addHop(HopSpec("bar-h1", "bar-h1-sel"));
+ spec.getTable(1).getHop(0).addRecipient("bar-h1-r1");
+ spec.getTable(1).getHop(0).addRecipient("bar-h1-r2");
+ spec.getTable(1).addHop(HopSpec("bar-h2", "bar-h2-sel"));
+ spec.getTable(1).getHop(1).addRecipient("bar-h2-r1");
+ spec.getTable(1).getHop(1).addRecipient("bar-h2-r2");
+ spec.getTable(1).addRoute(RouteSpec("bar-r1"));
+ spec.getTable(1).getRoute(0).addHop("bar-h1");
+ spec.getTable(1).getRoute(0).addHop("bar-h2");
+ spec.getTable(1).addRoute(RouteSpec("bar-r2"));
+ spec.getTable(1).getRoute(1).addHop("bar-h2");
+ spec.getTable(1).getRoute(1).addHop("bar-h1");
+ EXPECT_TRUE(testRouting(spec));
+
+ RoutingSpec specCopy = spec;
+ EXPECT_TRUE(testRouting(specCopy));
+ }
+ {
+ RoutingSpec spec = RoutingSpec()
+ .addTable(RoutingTableSpec("foo")
+ .addHop(HopSpec("foo-h1", "foo-h1-sel")
+ .addRecipient("foo-h1-r1")
+ .addRecipient("foo-h1-r2"))
+ .addHop(HopSpec("foo-h2", "foo-h2-sel")
+ .addRecipient("foo-h2-r1")
+ .addRecipient("foo-h2-r2"))
+ .addRoute(RouteSpec("foo-r1")
+ .addHop("foo-h1")
+ .addHop("foo-h2"))
+ .addRoute(RouteSpec("foo-r2")
+ .addHop("foo-h2")
+ .addHop("foo-h1")))
+ .addTable(RoutingTableSpec("bar")
+ .addHop(HopSpec("bar-h1", "bar-h1-sel")
+ .addRecipient("bar-h1-r1")
+ .addRecipient("bar-h1-r2"))
+ .addHop(HopSpec("bar-h2", "bar-h2-sel")
+ .addRecipient("bar-h2-r1")
+ .addRecipient("bar-h2-r2"))
+ .addRoute(RouteSpec("bar-r1")
+ .addHop("bar-h1")
+ .addHop("bar-h2"))
+ .addRoute(RouteSpec("bar-r2")
+ .addHop("bar-h2")
+ .addHop("bar-h1")));
+ EXPECT_TRUE(testRouting(spec));
+
+ RoutingSpec specCopy = spec;
+ EXPECT_TRUE(testRouting(specCopy));
+ }
+}
+
+bool
+Test::testRouting(const RoutingSpec &spec)
+{
+ if (!ASSERT_TRUE(spec.getNumTables() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getProtocol() == "foo")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(0).getNumHops() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(0).getName() == "foo-h1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(0).getSelector() == "foo-h1-sel")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(0).getHop(0).getNumRecipients() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(0).getRecipient(0) == "foo-h1-r1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(0).getRecipient(1) == "foo-h1-r2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(1).getName() == "foo-h2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(1).getSelector() == "foo-h2-sel")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(0).getHop(1).getNumRecipients() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(1).getRecipient(0) == "foo-h2-r1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getHop(1).getRecipient(1) == "foo-h2-r2")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(0).getNumRoutes() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getRoute(0).getName() == "foo-r1")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(0).getRoute(0).getNumHops() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getRoute(0).getHop(0) == "foo-h1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getRoute(0).getHop(1) == "foo-h2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getRoute(1).getName() == "foo-r2")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(0).getRoute(1).getNumHops() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getRoute(1).getHop(0) == "foo-h2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(0).getRoute(1).getHop(1) == "foo-h1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getProtocol() == "bar")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(1).getNumHops() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(0).getName() == "bar-h1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(0).getSelector() == "bar-h1-sel")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(1).getHop(0).getNumRecipients() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(0).getRecipient(0) == "bar-h1-r1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(0).getRecipient(1) == "bar-h1-r2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(1).getName() == "bar-h2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(1).getSelector() == "bar-h2-sel")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(1).getHop(1).getNumRecipients() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(1).getRecipient(0) == "bar-h2-r1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getHop(1).getRecipient(1) == "bar-h2-r2")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(1).getNumRoutes() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getRoute(0).getName() == "bar-r1")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(1).getRoute(0).getNumHops() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getRoute(0).getHop(0) == "bar-h1")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getRoute(0).getHop(1) == "bar-h2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getRoute(1).getName() == "bar-r2")) { return false; }
+ if (!ASSERT_TRUE(spec.getTable(1).getRoute(1).getNumHops() == 2)) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getRoute(1).getHop(0) == "bar-h2")) { return false; }
+ if (!EXPECT_TRUE(spec.getTable(1).getRoute(1).getHop(1) == "bar-h1")) { return false; }
+ return true;
+}
+
+void
+Test::testConfigGeneration()
+{
+ EXPECT_TRUE(testConfig(RoutingSpec()));
+ EXPECT_TRUE(testConfig(RoutingSpec().addTable(RoutingTableSpec("mytable1"))));
+ EXPECT_TRUE(testConfig(RoutingSpec().addTable(RoutingTableSpec("mytable1")
+ .addHop(HopSpec("myhop1", "myselector1")))));
+ EXPECT_TRUE(testConfig(RoutingSpec().addTable(RoutingTableSpec("mytable1")
+ .addHop(HopSpec("myhop1", "myselector1"))
+ .addRoute(RouteSpec("myroute1").addHop("myhop1")))));
+ EXPECT_TRUE(testConfig(RoutingSpec().addTable(RoutingTableSpec("mytable1")
+ .addHop(HopSpec("myhop1", "myselector1"))
+ .addHop(HopSpec("myhop2", "myselector2"))
+ .addRoute(RouteSpec("myroute1").addHop("myhop1"))
+ .addRoute(RouteSpec("myroute2").addHop("myhop2"))
+ .addRoute(RouteSpec("myroute12").addHop("myhop1").addHop("myhop2")))));
+ EXPECT_TRUE(testConfig(RoutingSpec()
+ .addTable(RoutingTableSpec("mytable1")
+ .addHop(HopSpec("myhop1", "myselector1"))
+ .addHop(HopSpec("myhop2", "myselector2"))
+ .addRoute(RouteSpec("myroute1").addHop("myhop1"))
+ .addRoute(RouteSpec("myroute2").addHop("myhop2"))
+ .addRoute(RouteSpec("myroute12").addHop("myhop1").addHop("myhop2")))
+ .addTable(RoutingTableSpec("mytable2"))));
+
+ EXPECT_EQUAL("routingtable[2]\n"
+ "routingtable[0].protocol \"mytable1\"\n"
+ "routingtable[1].protocol \"mytable2\"\n"
+ "routingtable[1].hop[3]\n"
+ "routingtable[1].hop[0].name \"myhop1\"\n"
+ "routingtable[1].hop[0].selector \"myselector1\"\n"
+ "routingtable[1].hop[1].name \"myhop2\"\n"
+ "routingtable[1].hop[1].selector \"myselector2\"\n"
+ "routingtable[1].hop[1].ignoreresult true\n"
+ "routingtable[1].hop[2].name \"myhop1\"\n"
+ "routingtable[1].hop[2].selector \"myselector3\"\n"
+ "routingtable[1].hop[2].recipient[2]\n"
+ "routingtable[1].hop[2].recipient[0] \"myrecipient1\"\n"
+ "routingtable[1].hop[2].recipient[1] \"myrecipient2\"\n"
+ "routingtable[1].route[1]\n"
+ "routingtable[1].route[0].name \"myroute1\"\n"
+ "routingtable[1].route[0].hop[1]\n"
+ "routingtable[1].route[0].hop[0] \"myhop1\"\n",
+ RoutingSpec()
+ .addTable(RoutingTableSpec("mytable1"))
+ .addTable(RoutingTableSpec("mytable2")
+ .addHop(HopSpec("myhop1", "myselector1"))
+ .addHop(HopSpec("myhop2", "myselector2").setIgnoreResult(true))
+ .addHop(HopSpec("myhop1", "myselector3")
+ .addRecipient("myrecipient1")
+ .addRecipient("myrecipient2"))
+ .addRoute(RouteSpec("myroute1").addHop("myhop1"))).toString());
+}
+
+bool
+Test::testConfig(const RoutingSpec &spec)
+{
+ if (!EXPECT_TRUE(spec == spec)) {
+ return false;
+ }
+ if (!EXPECT_TRUE(spec == RoutingSpec(spec))) {
+ return false;
+ }
+ ConfigStore store;
+ ConfigAgent agent(store);
+ agent.configure(ConfigGetter<MessagebusConfig>().getConfig("", RawSpec(spec.toString())));
+ if (!EXPECT_TRUE(store.getRoutingSpec() == spec)) {
+ return false;
+ }
+ return true;
+}
+
diff --git a/messagebus/src/tests/rpcserviceaddress/.gitignore b/messagebus/src/tests/rpcserviceaddress/.gitignore
new file mode 100644
index 00000000000..fcb95aca804
--- /dev/null
+++ b/messagebus/src/tests/rpcserviceaddress/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+rpcserviceaddress_test
+messagebus_rpcserviceaddress_test_app
diff --git a/messagebus/src/tests/rpcserviceaddress/CMakeLists.txt b/messagebus/src/tests/rpcserviceaddress/CMakeLists.txt
new file mode 100644
index 00000000000..e4ada1c8c1b
--- /dev/null
+++ b/messagebus/src/tests/rpcserviceaddress/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_rpcserviceaddress_test_app
+ SOURCES
+ rpcserviceaddress.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_rpcserviceaddress_test_app COMMAND messagebus_rpcserviceaddress_test_app)
diff --git a/messagebus/src/tests/rpcserviceaddress/DESC b/messagebus/src/tests/rpcserviceaddress/DESC
new file mode 100644
index 00000000000..2c0f5565509
--- /dev/null
+++ b/messagebus/src/tests/rpcserviceaddress/DESC
@@ -0,0 +1 @@
+rpcserviceaddress test. Take a look at rpcserviceaddress.cpp for details.
diff --git a/messagebus/src/tests/rpcserviceaddress/FILES b/messagebus/src/tests/rpcserviceaddress/FILES
new file mode 100644
index 00000000000..ea9edf09a87
--- /dev/null
+++ b/messagebus/src/tests/rpcserviceaddress/FILES
@@ -0,0 +1 @@
+rpcserviceaddress.cpp
diff --git a/messagebus/src/tests/rpcserviceaddress/rpcserviceaddress.cpp b/messagebus/src/tests/rpcserviceaddress/rpcserviceaddress.cpp
new file mode 100644
index 00000000000..d5a002adf89
--- /dev/null
+++ b/messagebus/src/tests/rpcserviceaddress/rpcserviceaddress.cpp
@@ -0,0 +1,44 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("rpcserviceaddress_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/network/rpcserviceaddress.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("rpcserviceaddress_test");
+ {
+ EXPECT_TRUE(RPCServiceAddress("", "bar").isMalformed());
+ EXPECT_TRUE(RPCServiceAddress("foo", "bar").isMalformed());
+ EXPECT_TRUE(RPCServiceAddress("foo/", "bar").isMalformed());
+ EXPECT_TRUE(RPCServiceAddress("/foo", "bar").isMalformed());
+ }
+ {
+ RPCServiceAddress addr("foo/bar/baz", "tcp/foo.com:42");
+ EXPECT_TRUE(!addr.isMalformed());
+ EXPECT_TRUE(addr.getServiceName() == "foo/bar/baz");
+ EXPECT_TRUE(addr.getConnectionSpec() == "tcp/foo.com:42");
+ EXPECT_TRUE(addr.getSessionName() == "baz");
+ }
+ {
+ RPCServiceAddress addr("foo/bar", "tcp/foo.com:42");
+ EXPECT_TRUE(!addr.isMalformed());
+ EXPECT_TRUE(addr.getServiceName() == "foo/bar");
+ EXPECT_TRUE(addr.getConnectionSpec() == "tcp/foo.com:42");
+ EXPECT_TRUE(addr.getSessionName() == "bar");
+ }
+ {
+ RPCServiceAddress addr("", "tcp/foo.com:42");
+ EXPECT_TRUE(addr.isMalformed());
+ EXPECT_TRUE(addr.getServiceName() == "");
+ EXPECT_TRUE(addr.getConnectionSpec() == "tcp/foo.com:42");
+ EXPECT_TRUE(addr.getSessionName() == "");
+ }
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/selector/.gitignore b/messagebus/src/tests/selector/.gitignore
new file mode 100644
index 00000000000..8ffe3b2ac91
--- /dev/null
+++ b/messagebus/src/tests/selector/.gitignore
@@ -0,0 +1,3 @@
+.depend
+Makefile
+selector_test
diff --git a/messagebus/src/tests/sendadapter/.gitignore b/messagebus/src/tests/sendadapter/.gitignore
new file mode 100644
index 00000000000..7a13e0d4ee1
--- /dev/null
+++ b/messagebus/src/tests/sendadapter/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+sendadapter_test
+messagebus_sendadapter_test_app
diff --git a/messagebus/src/tests/sendadapter/CMakeLists.txt b/messagebus/src/tests/sendadapter/CMakeLists.txt
new file mode 100644
index 00000000000..32c41b40c3c
--- /dev/null
+++ b/messagebus/src/tests/sendadapter/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_sendadapter_test_app
+ SOURCES
+ sendadapter.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_sendadapter_test_app COMMAND messagebus_sendadapter_test_app)
diff --git a/messagebus/src/tests/sendadapter/DESC b/messagebus/src/tests/sendadapter/DESC
new file mode 100644
index 00000000000..35a50283921
--- /dev/null
+++ b/messagebus/src/tests/sendadapter/DESC
@@ -0,0 +1 @@
+sendadapter test. Take a look at sendadapter.cpp for details.
diff --git a/messagebus/src/tests/sendadapter/FILES b/messagebus/src/tests/sendadapter/FILES
new file mode 100644
index 00000000000..c43cbb6a53c
--- /dev/null
+++ b/messagebus/src/tests/sendadapter/FILES
@@ -0,0 +1 @@
+sendadapter.cpp
diff --git a/messagebus/src/tests/sendadapter/sendadapter.cpp b/messagebus/src/tests/sendadapter/sendadapter.cpp
new file mode 100644
index 00000000000..b25240acdac
--- /dev/null
+++ b/messagebus/src/tests/sendadapter/sendadapter.cpp
@@ -0,0 +1,252 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("sendadapter_test");
+
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Setup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class TestProtocol : public mbus::SimpleProtocol {
+private:
+ mutable vespalib::Version _lastVersion;
+
+public:
+ typedef std::shared_ptr<TestProtocol> SP;
+ mbus::Blob encode(const vespalib::Version &version, const mbus::Routable &routable) const {
+ _lastVersion = version;
+ return mbus::SimpleProtocol::encode(version, routable);
+ }
+ mbus::Routable::UP decode(const vespalib::Version &version, mbus::BlobRef blob) const {
+ _lastVersion = version;
+ return mbus::SimpleProtocol::decode(version, blob);
+ }
+ const vespalib::Version &getLastVersion() { return _lastVersion; }
+};
+
+class TestData {
+public:
+ Slobrok _slobrok;
+ TestProtocol::SP _srcProtocol;
+ TestServer _srcServer;
+ SourceSession::UP _srcSession;
+ Receptor _srcHandler;
+ TestProtocol::SP _itrProtocol;
+ TestServer _itrServer;
+ IntermediateSession::UP _itrSession;
+ Receptor _itrHandler;
+ TestProtocol::SP _dstProtocol;
+ TestServer _dstServer;
+ DestinationSession::UP _dstSession;
+ Receptor _dstHandler;
+
+public:
+ TestData();
+ bool start();
+};
+
+class Test : public vespalib::TestApp {
+private:
+ static const int TIMEOUT_SECS = 60;
+
+ bool testVersionedSend(TestData &data,
+ const vespalib::Version &srcVersion,
+ const vespalib::Version &itrVersion,
+ const vespalib::Version &dstVersion);
+ void testSendAdapters(TestData &data);
+
+public:
+ int Main();
+};
+
+TEST_APPHOOK(Test);
+
+TestData::TestData() :
+ _slobrok(),
+ _srcProtocol(new TestProtocol()),
+ _srcServer(MessageBusParams().setRetryPolicy(IRetryPolicy::SP()).addProtocol(_srcProtocol),
+ RPCNetworkParams().setSlobrokConfig(_slobrok.config())),
+ _srcSession(),
+ _srcHandler(),
+ _itrProtocol(new TestProtocol()),
+ _itrServer(MessageBusParams().addProtocol(_itrProtocol),
+ RPCNetworkParams().setIdentity(Identity("itr")).setSlobrokConfig(_slobrok.config())),
+ _itrSession(),
+ _itrHandler(),
+ _dstProtocol(new TestProtocol()),
+ _dstServer(MessageBusParams().addProtocol(_dstProtocol),
+ RPCNetworkParams().setIdentity(Identity("dst")).setSlobrokConfig(_slobrok.config())),
+ _dstSession(),
+ _dstHandler()
+{
+ // empty
+}
+
+bool
+TestData::start()
+{
+ _srcSession = _srcServer.mb.createSourceSession(SourceSessionParams().setReplyHandler(_srcHandler));
+ if (_srcSession.get() == NULL) {
+ return false;
+ }
+ _itrSession = _itrServer.mb.createIntermediateSession(IntermediateSessionParams().setName("session").setMessageHandler(_itrHandler).setReplyHandler(_itrHandler));
+ if (_itrSession.get() == NULL) {
+ return false;
+ }
+ _dstSession = _dstServer.mb.createDestinationSession(DestinationSessionParams().setName("session").setMessageHandler(_dstHandler));
+ if (_dstSession.get() == NULL) {
+ return false;
+ }
+ if (!_srcServer.waitSlobrok("*/session", 2u)) {
+ return false;
+ }
+ return true;
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("sendadapter_test");
+
+ TestData data;
+ ASSERT_TRUE(data.start());
+
+ testSendAdapters(data); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tests
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void
+Test::testSendAdapters(TestData &data)
+{
+ std::vector<vespalib::Version> versions;
+ versions.push_back(vespalib::Version(5, 0));
+ versions.push_back(vespalib::Version(5, 1));
+
+ for (std::vector<vespalib::Version>::const_iterator srcVersion = versions.begin();
+ srcVersion != versions.end(); ++srcVersion)
+ {
+ for (std::vector<vespalib::Version>::const_iterator itrVersion = versions.begin();
+ itrVersion != versions.end(); ++itrVersion)
+ {
+ for (std::vector<vespalib::Version>::const_iterator dstVersion = versions.begin();
+ dstVersion != versions.end(); ++dstVersion)
+ {
+ EXPECT_TRUE(testVersionedSend(data, *srcVersion, *itrVersion, *dstVersion));
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Utilities
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool
+Test::testVersionedSend(TestData &data,
+ const vespalib::Version &srcVersion,
+ const vespalib::Version &itrVersion,
+ const vespalib::Version &dstVersion)
+{
+ LOG(info, "Sending from %s through %s to %s.",
+ srcVersion.toString().c_str(), itrVersion.toString().c_str(), dstVersion.toString().c_str());
+ data._srcServer.net.setVersion(srcVersion);
+ data._itrServer.net.setVersion(itrVersion);
+ data._dstServer.net.setVersion(dstVersion);
+
+ Message::UP msg(new SimpleMessage("foo"));
+ msg->getTrace().setLevel(9);
+ if (!EXPECT_TRUE(data._srcSession->send(std::move(msg), Route::parse("itr/session dst/session")).isAccepted())) {
+ return false;
+ }
+ msg = data._itrHandler.getMessage(TIMEOUT_SECS);
+ if (!EXPECT_TRUE(msg.get() != NULL)) {
+ return false;
+ }
+ LOG(info, "Message version %s serialized at source.",
+ data._srcProtocol->getLastVersion().toString().c_str());
+ vespalib::Version minVersion = std::min(srcVersion, itrVersion);
+ if (!EXPECT_TRUE(minVersion == data._srcProtocol->getLastVersion())) {
+ return false;
+ }
+
+ LOG(info, "Message version %s reached intermediate.",
+ data._itrProtocol->getLastVersion().toString().c_str());
+ if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
+ return false;
+ }
+ data._itrSession->forward(std::move(msg));
+ msg = data._dstHandler.getMessage(TIMEOUT_SECS);
+ if (!EXPECT_TRUE(msg.get() != NULL)) {
+ return false;
+ }
+ LOG(info, "Message version %s serialized at intermediate.",
+ data._itrProtocol->getLastVersion().toString().c_str());
+ minVersion = std::min(itrVersion, dstVersion);
+ if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
+ return false;
+ }
+
+ LOG(info, "Message version %s reached destination.",
+ data._dstProtocol->getLastVersion().toString().c_str());
+ if (!EXPECT_TRUE(minVersion == data._dstProtocol->getLastVersion())) {
+ return false;
+ }
+ Reply::UP reply(new SimpleReply("bar"));
+ reply->swapState(*msg);
+ data._dstSession->reply(std::move(reply));
+ reply = data._itrHandler.getReply();
+ if (!EXPECT_TRUE(reply.get() != NULL)) {
+ return false;
+ }
+ LOG(info, "Reply version %s serialized at destination.",
+ data._dstProtocol->getLastVersion().toString().c_str());
+ if (!EXPECT_TRUE(minVersion == data._dstProtocol->getLastVersion())) {
+ return false;
+ }
+
+ LOG(info, "Reply version %s reached intermediate.",
+ data._itrProtocol->getLastVersion().toString().c_str());
+ if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
+ return false;
+ }
+ data._itrSession->forward(std::move(reply));
+ reply = data._srcHandler.getReply();
+ if (!EXPECT_TRUE(reply.get() != NULL)) {
+ return false;
+ }
+ LOG(info, "Reply version %s serialized at intermediate.",
+ data._dstProtocol->getLastVersion().toString().c_str());
+ minVersion = std::min(srcVersion, itrVersion);
+ if (!EXPECT_TRUE(minVersion == data._itrProtocol->getLastVersion())) {
+ return false;
+ }
+
+ LOG(info, "Reply version %s reached source.",
+ data._srcProtocol->getLastVersion().toString().c_str());
+ if (!EXPECT_TRUE(minVersion == data._srcProtocol->getLastVersion())) {
+ return false;
+ }
+ return true;
+}
diff --git a/messagebus/src/tests/sequencer/.gitignore b/messagebus/src/tests/sequencer/.gitignore
new file mode 100644
index 00000000000..cc673f20668
--- /dev/null
+++ b/messagebus/src/tests/sequencer/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+sequencer_test
+messagebus_sequencer_test_app
diff --git a/messagebus/src/tests/sequencer/CMakeLists.txt b/messagebus/src/tests/sequencer/CMakeLists.txt
new file mode 100644
index 00000000000..dab54431722
--- /dev/null
+++ b/messagebus/src/tests/sequencer/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_sequencer_test_app
+ SOURCES
+ sequencer.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_sequencer_test_app COMMAND messagebus_sequencer_test_app)
diff --git a/messagebus/src/tests/sequencer/DESC b/messagebus/src/tests/sequencer/DESC
new file mode 100644
index 00000000000..761c069aa92
--- /dev/null
+++ b/messagebus/src/tests/sequencer/DESC
@@ -0,0 +1 @@
+sequencer test. Take a look at sequencer.cpp for details.
diff --git a/messagebus/src/tests/sequencer/FILES b/messagebus/src/tests/sequencer/FILES
new file mode 100644
index 00000000000..a8d6aeae540
--- /dev/null
+++ b/messagebus/src/tests/sequencer/FILES
@@ -0,0 +1 @@
+sequencer.cpp
diff --git a/messagebus/src/tests/sequencer/sequencer.cpp b/messagebus/src/tests/sequencer/sequencer.cpp
new file mode 100644
index 00000000000..b2818cfa57d
--- /dev/null
+++ b/messagebus/src/tests/sequencer/sequencer.cpp
@@ -0,0 +1,194 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("sequencer_test");
+
+#include <vespa/messagebus/sequencer.h>
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+// --------------------------------------------------------------------------------
+//
+// Setup.
+//
+// --------------------------------------------------------------------------------
+
+struct MyQueue : public RoutableQueue {
+
+ virtual ~MyQueue() {
+ while (size() > 0) {
+ Routable::UP obj = dequeue(0);
+ obj->getCallStack().discard();
+ }
+ }
+
+ bool checkReply(bool hasSeqId, uint64_t seqId) {
+ if (size() == 0) {
+ LOG(error, "checkReply(): No reply in queue.");
+ return false;
+ }
+ Routable::UP obj = dequeue(0);
+ if (!obj->isReply()) {
+ LOG(error, "checkReply(): Got message when expecting reply.");
+ return false;
+ }
+ Reply::UP reply(static_cast<Reply*>(obj.release()));
+ Message::UP msg = reply->getMessage();
+ if (msg.get() == NULL) {
+ LOG(error, "checkReply(): Reply has no message attached.");
+ return false;
+ }
+ if (hasSeqId) {
+ if (!msg->hasSequenceId()) {
+ LOG(error, "checkReply(): Expected sequence id %" PRIu64 ", got none.",
+ seqId);
+ return false;
+ }
+ if (msg->getSequenceId() != seqId) {
+ LOG(error, "checkReply(): Expected sequence id %" PRIu64 ", got %" PRIu64 ".",
+ seqId, msg->getSequenceId());
+ return false;
+ }
+ } else {
+ if (msg->hasSequenceId()) {
+ LOG(error, "checkReply(): Message has unexpected sequence id %" PRIu64 ".",
+ msg->getSequenceId());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void replyNext() {
+ Routable::UP obj = dequeue(0);
+ Message::UP msg(static_cast<Message*>(obj.release()));
+
+ Reply::UP reply(new EmptyReply());
+ reply->swapState(*msg);
+ reply->setMessage(std::move(msg));
+ IReplyHandler &handler = reply->getCallStack().pop(*reply);
+ handler.handleReply(std::move(reply));
+ }
+
+ Message::UP createMessage(bool hasSeqId, uint64_t seqId) {
+ Message::UP ret(new SimpleMessage("foo", hasSeqId, seqId));
+ ret->pushHandler(*this);
+ return ret;
+ }
+};
+
+class Test : public vespalib::TestApp {
+private:
+ void testSyncNone();
+ void testSyncId();
+
+public:
+ int Main() {
+ TEST_INIT("sequencer_test");
+
+ testSyncNone(); TEST_FLUSH();
+ testSyncId(); TEST_FLUSH();
+
+ TEST_DONE();
+ }
+};
+
+TEST_APPHOOK(Test);
+
+// --------------------------------------------------------------------------------
+//
+// Tests.
+//
+// --------------------------------------------------------------------------------
+
+void
+Test::testSyncNone()
+{
+ MyQueue src;
+ MyQueue dst;
+ Sequencer seq(dst);
+
+ seq.handleMessage(src.createMessage(false, 0));
+ seq.handleMessage(src.createMessage(false, 0));
+ seq.handleMessage(src.createMessage(false, 0));
+ seq.handleMessage(src.createMessage(false, 0));
+ seq.handleMessage(src.createMessage(false, 0));
+ EXPECT_EQUAL(0u, src.size());
+ EXPECT_EQUAL(5u, dst.size());
+
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ EXPECT_EQUAL(5u, src.size());
+ EXPECT_EQUAL(0u, dst.size());
+
+ EXPECT_TRUE(src.checkReply(false, 0));
+ EXPECT_TRUE(src.checkReply(false, 0));
+ EXPECT_TRUE(src.checkReply(false, 0));
+ EXPECT_TRUE(src.checkReply(false, 0));
+ EXPECT_TRUE(src.checkReply(false, 0));
+ EXPECT_EQUAL(0u, src.size());
+ EXPECT_EQUAL(0u, dst.size());
+}
+
+void
+Test::testSyncId()
+{
+ MyQueue src;
+ MyQueue dst;
+ Sequencer seq(dst);
+
+ seq.handleMessage(src.createMessage(true, 1));
+ seq.handleMessage(src.createMessage(true, 2));
+ seq.handleMessage(src.createMessage(true, 3));
+ seq.handleMessage(src.createMessage(true, 4));
+ seq.handleMessage(src.createMessage(true, 5));
+ EXPECT_EQUAL(0u, src.size());
+ EXPECT_EQUAL(5u, dst.size());
+
+ seq.handleMessage(src.createMessage(true, 1));
+ seq.handleMessage(src.createMessage(true, 5));
+ seq.handleMessage(src.createMessage(true, 2));
+ seq.handleMessage(src.createMessage(true, 10));
+ seq.handleMessage(src.createMessage(true, 4));
+ seq.handleMessage(src.createMessage(true, 3));
+ EXPECT_EQUAL(0u, src.size());
+ EXPECT_EQUAL(6u, dst.size());
+
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ EXPECT_EQUAL(5u, src.size());
+ EXPECT_EQUAL(6u, dst.size());
+
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ dst.replyNext();
+ EXPECT_EQUAL(11u, src.size());
+ EXPECT_EQUAL(0u, dst.size());
+
+ EXPECT_TRUE(src.checkReply(true, 1));
+ EXPECT_TRUE(src.checkReply(true, 2));
+ EXPECT_TRUE(src.checkReply(true, 3));
+ EXPECT_TRUE(src.checkReply(true, 4));
+ EXPECT_TRUE(src.checkReply(true, 5));
+ EXPECT_TRUE(src.checkReply(true, 10));
+ EXPECT_TRUE(src.checkReply(true, 1));
+ EXPECT_TRUE(src.checkReply(true, 2));
+ EXPECT_TRUE(src.checkReply(true, 3));
+ EXPECT_TRUE(src.checkReply(true, 4));
+ EXPECT_TRUE(src.checkReply(true, 5));
+ EXPECT_EQUAL(0u, src.size());
+ EXPECT_EQUAL(0u, dst.size());
+}
diff --git a/messagebus/src/tests/serviceaddress/.gitignore b/messagebus/src/tests/serviceaddress/.gitignore
new file mode 100644
index 00000000000..62275eb8c04
--- /dev/null
+++ b/messagebus/src/tests/serviceaddress/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+serviceaddress_test
+messagebus_serviceaddress_test_app
diff --git a/messagebus/src/tests/serviceaddress/CMakeLists.txt b/messagebus/src/tests/serviceaddress/CMakeLists.txt
new file mode 100644
index 00000000000..630e28dc94f
--- /dev/null
+++ b/messagebus/src/tests/serviceaddress/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_serviceaddress_test_app
+ SOURCES
+ serviceaddress.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_serviceaddress_test_app COMMAND messagebus_serviceaddress_test_app)
diff --git a/messagebus/src/tests/serviceaddress/DESC b/messagebus/src/tests/serviceaddress/DESC
new file mode 100644
index 00000000000..38fa7c16b1a
--- /dev/null
+++ b/messagebus/src/tests/serviceaddress/DESC
@@ -0,0 +1 @@
+serviceaddress test. Take a look at serviceaddress.cpp for details.
diff --git a/messagebus/src/tests/serviceaddress/FILES b/messagebus/src/tests/serviceaddress/FILES
new file mode 100644
index 00000000000..37e17b66b5e
--- /dev/null
+++ b/messagebus/src/tests/serviceaddress/FILES
@@ -0,0 +1 @@
+serviceaddress.cpp
diff --git a/messagebus/src/tests/serviceaddress/serviceaddress.cpp b/messagebus/src/tests/serviceaddress/serviceaddress.cpp
new file mode 100644
index 00000000000..36d7776f732
--- /dev/null
+++ b/messagebus/src/tests/serviceaddress/serviceaddress.cpp
@@ -0,0 +1,137 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("serviceaddress_test");
+
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/vstringfmt.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/error.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/network/rpcservice.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+
+using namespace mbus;
+
+class Test : public vespalib::TestApp {
+public:
+ int Main();
+ void testAddrServiceAddress();
+ void testNameServiceAddress();
+
+private:
+ bool waitSlobrok(RPCNetwork &network, const string &pattern, size_t num);
+ bool testAddress(RPCNetwork& network, const string &pattern,
+ const string &expectedSpec, const string &expectedSession);
+ bool testNullAddress(RPCNetwork &network, const string &pattern);
+};
+
+int
+Test::Main()
+{
+ TEST_INIT("serviceaddress_test");
+
+ testAddrServiceAddress(); TEST_FLUSH();
+ testNameServiceAddress(); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test);
+
+void
+Test::testAddrServiceAddress()
+{
+ Slobrok slobrok;
+ RPCNetwork network(RPCNetworkParams()
+ .setIdentity(Identity("foo"))
+ .setSlobrokConfig(slobrok.config()));
+ ASSERT_TRUE(network.start());
+
+ EXPECT_TRUE(testNullAddress(network, "tcp"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/localhost"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/localhost:"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/localhost:1977"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/localhost:1977/"));
+ EXPECT_TRUE(testAddress(network, "tcp/localhost:1977/session", "tcp/localhost:1977", "session"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/localhost:/session"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/:1977/session"));
+ EXPECT_TRUE(testNullAddress(network, "tcp/:/session"));
+
+ network.shutdown();
+}
+
+void
+Test::testNameServiceAddress()
+{
+ Slobrok slobrok;
+ RPCNetwork network(RPCNetworkParams()
+ .setIdentity(Identity("foo"))
+ .setSlobrokConfig(slobrok.config()));
+ ASSERT_TRUE(network.start());
+
+ network.unregisterSession("session");
+ ASSERT_TRUE(waitSlobrok(network, "foo/session", 0));
+ EXPECT_TRUE(testNullAddress(network, "foo/session"));
+
+ network.registerSession("session");
+ ASSERT_TRUE(waitSlobrok(network, "foo/session", 1));
+ EXPECT_TRUE(testAddress(network, "foo/session", network.getConnectionSpec().c_str(), "session"));
+
+ network.shutdown();
+}
+
+bool
+Test::waitSlobrok(RPCNetwork &network, const string &pattern, size_t num)
+{
+ for (int i = 0; i < 1000; i++) {
+ slobrok::api::MirrorAPI::SpecList res = network.getMirror().lookup(pattern);
+ if (res.size() == num) {
+ return true;
+ }
+ FastOS_Thread::Sleep(10);
+ }
+ return false;
+}
+
+bool
+Test::testNullAddress(RPCNetwork &network, const string &pattern)
+{
+ RPCService service(network.getMirror(), pattern);
+ RPCServiceAddress::UP obj = service.resolve();
+ if (!EXPECT_TRUE(obj.get() == NULL)) {
+ return false;
+ }
+ return true;
+}
+
+bool
+Test::testAddress(RPCNetwork &network, const string &pattern,
+ const string &expectedSpec, const string &expectedSession)
+{
+ RPCService service(network.getMirror(), pattern);
+ RPCServiceAddress::UP obj = service.resolve();
+ if (!EXPECT_TRUE(obj.get() != NULL)) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(expectedSpec, obj->getConnectionSpec())) {
+ return false;
+ }
+ if (!EXPECT_EQUAL(expectedSession, obj->getSessionName())) {
+ return false;
+ }
+ return true;
+}
+
diff --git a/messagebus/src/tests/servicepool/.gitignore b/messagebus/src/tests/servicepool/.gitignore
new file mode 100644
index 00000000000..d8b2f3b9b09
--- /dev/null
+++ b/messagebus/src/tests/servicepool/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+servicepool_test
+messagebus_servicepool_test_app
diff --git a/messagebus/src/tests/servicepool/CMakeLists.txt b/messagebus/src/tests/servicepool/CMakeLists.txt
new file mode 100644
index 00000000000..0d6cbc54862
--- /dev/null
+++ b/messagebus/src/tests/servicepool/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_servicepool_test_app
+ SOURCES
+ servicepool.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_servicepool_test_app COMMAND messagebus_servicepool_test_app)
diff --git a/messagebus/src/tests/servicepool/DESC b/messagebus/src/tests/servicepool/DESC
new file mode 100644
index 00000000000..21484039b7a
--- /dev/null
+++ b/messagebus/src/tests/servicepool/DESC
@@ -0,0 +1 @@
+servicepool test. Take a look at servicepool.cpp for details.
diff --git a/messagebus/src/tests/servicepool/FILES b/messagebus/src/tests/servicepool/FILES
new file mode 100644
index 00000000000..22d1bbb2ba8
--- /dev/null
+++ b/messagebus/src/tests/servicepool/FILES
@@ -0,0 +1 @@
+servicepool.cpp
diff --git a/messagebus/src/tests/servicepool/servicepool.cpp b/messagebus/src/tests/servicepool/servicepool.cpp
new file mode 100644
index 00000000000..5cf4b8b6132
--- /dev/null
+++ b/messagebus/src/tests/servicepool/servicepool.cpp
@@ -0,0 +1,71 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("servicepool_test");
+
+#include <vespa/messagebus/network/rpcnetwork.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+class Test : public vespalib::TestApp {
+private:
+ void testMaxSize();
+
+public:
+ int Main() {
+ TEST_INIT("servicepool_test");
+
+ testMaxSize(); TEST_FLUSH();
+
+ TEST_DONE();
+ }
+};
+
+TEST_APPHOOK(Test);
+
+void
+Test::testMaxSize()
+{
+ Slobrok slobrok;
+ RPCNetwork net(RPCNetworkParams().setSlobrokConfig(slobrok.config()));
+ RPCServicePool pool(net, 2);
+ net.start();
+
+ pool.resolve("foo");
+ EXPECT_EQUAL(1u, pool.getSize());
+ EXPECT_TRUE(pool.hasService("foo"));
+ EXPECT_TRUE(!pool.hasService("bar"));
+ EXPECT_TRUE(!pool.hasService("baz"));
+
+ pool.resolve("foo");
+ EXPECT_EQUAL(1u, pool.getSize());
+ EXPECT_TRUE(pool.hasService("foo"));
+ EXPECT_TRUE(!pool.hasService("bar"));
+ EXPECT_TRUE(!pool.hasService("baz"));
+
+ pool.resolve("bar");
+ EXPECT_EQUAL(2u, pool.getSize());
+ EXPECT_TRUE(pool.hasService("foo"));
+ EXPECT_TRUE(pool.hasService("bar"));
+ EXPECT_TRUE(!pool.hasService("baz"));
+
+ pool.resolve("baz");
+ EXPECT_EQUAL(2u, pool.getSize());
+ EXPECT_TRUE(!pool.hasService("foo"));
+ EXPECT_TRUE(pool.hasService("bar"));
+ EXPECT_TRUE(pool.hasService("baz"));
+
+ pool.resolve("bar");
+ EXPECT_EQUAL(2u, pool.getSize());
+ EXPECT_TRUE(!pool.hasService("foo"));
+ EXPECT_TRUE(pool.hasService("bar"));
+ EXPECT_TRUE(pool.hasService("baz"));
+
+ pool.resolve("foo");
+ EXPECT_EQUAL(2u, pool.getSize());
+ EXPECT_TRUE(pool.hasService("foo"));
+ EXPECT_TRUE(pool.hasService("bar"));
+ EXPECT_TRUE(!pool.hasService("baz"));
+}
diff --git a/messagebus/src/tests/shutdown/.gitignore b/messagebus/src/tests/shutdown/.gitignore
new file mode 100644
index 00000000000..f3a6f7e0061
--- /dev/null
+++ b/messagebus/src/tests/shutdown/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+shutdown_test
+messagebus_shutdown_test_app
diff --git a/messagebus/src/tests/shutdown/CMakeLists.txt b/messagebus/src/tests/shutdown/CMakeLists.txt
new file mode 100644
index 00000000000..69c849ae5fb
--- /dev/null
+++ b/messagebus/src/tests/shutdown/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_shutdown_test_app
+ SOURCES
+ shutdown.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_shutdown_test_app COMMAND messagebus_shutdown_test_app)
diff --git a/messagebus/src/tests/shutdown/DESC b/messagebus/src/tests/shutdown/DESC
new file mode 100644
index 00000000000..1f289ba9c23
--- /dev/null
+++ b/messagebus/src/tests/shutdown/DESC
@@ -0,0 +1 @@
+shutdown test. Take a look at shutdown.cpp for details.
diff --git a/messagebus/src/tests/shutdown/FILES b/messagebus/src/tests/shutdown/FILES
new file mode 100644
index 00000000000..ce150a62325
--- /dev/null
+++ b/messagebus/src/tests/shutdown/FILES
@@ -0,0 +1 @@
+shutdown.cpp
diff --git a/messagebus/src/tests/shutdown/shutdown.cpp b/messagebus/src/tests/shutdown/shutdown.cpp
new file mode 100644
index 00000000000..d4c5544d469
--- /dev/null
+++ b/messagebus/src/tests/shutdown/shutdown.cpp
@@ -0,0 +1,159 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("shutdown_test");
+
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routing/errordirective.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/vespalib/util/vstringfmt.h>
+
+using namespace mbus;
+
+class Test : public vespalib::TestApp {
+private:
+ void requireThatListenFailedIsExceptionSafe();
+ void requireThatShutdownOnSourceWithPendingIsSafe();
+ void requireThatShutdownOnIntermediateWithPendingIsSafe();
+
+public:
+ int Main() {
+ TEST_INIT("shutdown_test");
+
+ requireThatListenFailedIsExceptionSafe(); TEST_FLUSH();
+ requireThatShutdownOnSourceWithPendingIsSafe(); TEST_FLUSH();
+ requireThatShutdownOnIntermediateWithPendingIsSafe(); TEST_FLUSH();
+
+ TEST_DONE();
+ }
+};
+
+static const double TIMEOUT = 120;
+
+TEST_APPHOOK(Test);
+
+void
+Test::requireThatListenFailedIsExceptionSafe()
+{
+ FRT_Supervisor orb;
+ ASSERT_TRUE(orb.Listen(0));
+ ASSERT_TRUE(orb.Start());
+
+ Slobrok slobrok;
+ try {
+ TestServer bar(MessageBusParams(),
+ RPCNetworkParams()
+ .setListenPort(orb.GetListenPort())
+ .setSlobrokConfig(slobrok.config()));
+ EXPECT_TRUE(false);
+ } catch (vespalib::Exception &e) {
+ EXPECT_EQUAL("Failed to start network.",
+ e.getMessage());
+ }
+ orb.ShutDown(true);
+}
+
+void
+Test::requireThatShutdownOnSourceWithPendingIsSafe()
+{
+ Slobrok slobrok;
+ TestServer dstServer(MessageBusParams()
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setIdentity(Identity("dst"))
+ .setSlobrokConfig(slobrok.config()));
+ Receptor dstHandler;
+ DestinationSession::UP dstSession = dstServer.mb.createDestinationSession(
+ DestinationSessionParams()
+ .setName("session")
+ .setMessageHandler(dstHandler));
+ ASSERT_TRUE(dstSession.get() != NULL);
+
+ for (uint32_t i = 0; i < 10; ++i) {
+ Message::UP msg(new SimpleMessage("msg"));
+ {
+ TestServer srcServer(MessageBusParams()
+ .setRetryPolicy(IRetryPolicy::SP(new RetryTransientErrorsPolicy()))
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setSlobrokConfig(slobrok.config()));
+ Receptor srcHandler;
+ SourceSession::UP srcSession = srcServer.mb.createSourceSession(SourceSessionParams()
+ .setThrottlePolicy(IThrottlePolicy::SP())
+ .setReplyHandler(srcHandler));
+ ASSERT_TRUE(srcSession.get() != NULL);
+ ASSERT_TRUE(srcServer.waitSlobrok("dst/session", 1));
+ ASSERT_TRUE(srcSession->send(std::move(msg), "dst/session", true).isAccepted());
+ msg = dstHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ }
+ dstSession->acknowledge(std::move(msg));
+ }
+}
+
+void
+Test::requireThatShutdownOnIntermediateWithPendingIsSafe()
+{
+ Slobrok slobrok;
+ TestServer dstServer(MessageBusParams()
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setIdentity(Identity("dst"))
+ .setSlobrokConfig(slobrok.config()));
+ Receptor dstHandler;
+ DestinationSession::UP dstSession = dstServer.mb.createDestinationSession(
+ DestinationSessionParams()
+ .setName("session")
+ .setMessageHandler(dstHandler));
+ ASSERT_TRUE(dstSession.get() != NULL);
+
+ TestServer srcServer(MessageBusParams()
+ .setRetryPolicy(IRetryPolicy::SP())
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setSlobrokConfig(slobrok.config()));
+ Receptor srcHandler;
+ SourceSession::UP srcSession = srcServer.mb.createSourceSession(SourceSessionParams()
+ .setThrottlePolicy(IThrottlePolicy::SP())
+ .setReplyHandler(srcHandler));
+ ASSERT_TRUE(srcSession.get() != NULL);
+ ASSERT_TRUE(srcServer.waitSlobrok("dst/session", 1));
+
+ for (uint32_t i = 0; i < 10; ++i) {
+ Message::UP msg(new SimpleMessage("msg"));
+ {
+ TestServer itrServer(MessageBusParams()
+ .setRetryPolicy(IRetryPolicy::SP(new RetryTransientErrorsPolicy()))
+ .addProtocol(IProtocol::SP(new SimpleProtocol())),
+ RPCNetworkParams()
+ .setIdentity(Identity("itr"))
+ .setSlobrokConfig(slobrok.config()));
+ Receptor itrHandler;
+ IntermediateSession::UP itrSession = itrServer.mb.createIntermediateSession(
+ IntermediateSessionParams()
+ .setName("session")
+ .setMessageHandler(itrHandler)
+ .setReplyHandler(itrHandler));
+ ASSERT_TRUE(itrSession.get() != NULL);
+ ASSERT_TRUE(srcServer.waitSlobrok("itr/session", 1));
+ ASSERT_TRUE(srcSession->send(std::move(msg), "itr/session dst/session", true).isAccepted());
+ msg = itrHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ itrSession->forward(std::move(msg));
+ msg = dstHandler.getMessage(TIMEOUT);
+ ASSERT_TRUE(msg.get() != NULL);
+ }
+ ASSERT_TRUE(srcServer.waitSlobrok("itr/session", 0));
+ dstSession->acknowledge(std::move(msg));
+ dstServer.mb.sync();
+ }
+}
diff --git a/messagebus/src/tests/simple-roundtrip/.gitignore b/messagebus/src/tests/simple-roundtrip/.gitignore
new file mode 100644
index 00000000000..c34bf4e6ca8
--- /dev/null
+++ b/messagebus/src/tests/simple-roundtrip/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+simple-roundtrip_test
+messagebus_simple-roundtrip_test_app
diff --git a/messagebus/src/tests/simple-roundtrip/CMakeLists.txt b/messagebus/src/tests/simple-roundtrip/CMakeLists.txt
new file mode 100644
index 00000000000..dff6ebf3e55
--- /dev/null
+++ b/messagebus/src/tests/simple-roundtrip/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_simple-roundtrip_test_app
+ SOURCES
+ simple-roundtrip.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_simple-roundtrip_test_app COMMAND messagebus_simple-roundtrip_test_app)
diff --git a/messagebus/src/tests/simple-roundtrip/DESC b/messagebus/src/tests/simple-roundtrip/DESC
new file mode 100644
index 00000000000..ad88203d593
--- /dev/null
+++ b/messagebus/src/tests/simple-roundtrip/DESC
@@ -0,0 +1 @@
+simple-roundtrip test. Take a look at simple-roundtrip.cpp for details.
diff --git a/messagebus/src/tests/simple-roundtrip/FILES b/messagebus/src/tests/simple-roundtrip/FILES
new file mode 100644
index 00000000000..c6a24435fe2
--- /dev/null
+++ b/messagebus/src/tests/simple-roundtrip/FILES
@@ -0,0 +1 @@
+simple-roundtrip.cpp
diff --git a/messagebus/src/tests/simple-roundtrip/simple-roundtrip.cpp b/messagebus/src/tests/simple-roundtrip/simple-roundtrip.cpp
new file mode 100644
index 00000000000..c59f072bd09
--- /dev/null
+++ b/messagebus/src/tests/simple-roundtrip/simple-roundtrip.cpp
@@ -0,0 +1,101 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("simple-roundtrip_test");
+
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+RoutingSpec getRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("pxy", "test/pxy/session"))
+ .addHop(HopSpec("dst", "test/dst/session"))
+ .addRoute(RouteSpec("test")
+ .addHop("pxy")
+ .addHop("dst")));
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("simple-roundtrip_test");
+
+ Slobrok slobrok;
+ TestServer srcNet(Identity("test/src"), getRouting(), slobrok);
+ TestServer pxyNet(Identity("test/pxy"), getRouting(), slobrok);
+ TestServer dstNet(Identity("test/dst"), getRouting(), slobrok);
+
+ Receptor src;
+ Receptor pxy;
+ Receptor dst;
+
+ SourceSession::UP ss = srcNet.mb.createSourceSession(src, SourceSessionParams());
+ IntermediateSession::UP is = pxyNet.mb.createIntermediateSession("session", true, pxy, pxy);
+ DestinationSession::UP ds = dstNet.mb.createDestinationSession("session", true, dst);
+
+ // wait for slobrok registration
+ ASSERT_TRUE(srcNet.waitSlobrok("test/pxy/session"));
+ ASSERT_TRUE(srcNet.waitSlobrok("test/dst/session"));
+ ASSERT_TRUE(pxyNet.waitSlobrok("test/dst/session"));
+
+ // send message on client
+ ss->send(SimpleMessage::UP(new SimpleMessage("test message")), "test");
+
+ // check message on proxy
+ Message::UP msg = pxy.getMessage();
+ ASSERT_TRUE(msg.get() != 0);
+ EXPECT_TRUE(msg->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(msg->getType() == SimpleProtocol::MESSAGE);
+ EXPECT_TRUE(static_cast<SimpleMessage&>(*msg).getValue() == "test message");
+
+ // forward message on proxy
+ static_cast<SimpleMessage&>(*msg).setValue("test message pxy");
+ is->forward(std::move(msg));
+
+ // check message on server
+ msg = dst.getMessage();
+ ASSERT_TRUE(msg.get() != 0);
+ EXPECT_TRUE(msg->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(msg->getType() == SimpleProtocol::MESSAGE);
+ EXPECT_TRUE(static_cast<SimpleMessage&>(*msg).getValue() == "test message pxy");
+
+ // send reply on server
+ SimpleReply::UP sr(new SimpleReply("test reply"));
+ msg->swapState(*sr);
+ ds->reply(Reply::UP(sr.release()));
+
+ // check reply on proxy
+ Reply::UP reply = pxy.getReply();
+ ASSERT_TRUE(reply.get() != 0);
+ EXPECT_TRUE(reply->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(reply->getType() == SimpleProtocol::REPLY);
+ EXPECT_TRUE(static_cast<SimpleReply&>(*reply).getValue() == "test reply");
+
+ // forward reply on proxy
+ static_cast<SimpleReply&>(*reply).setValue("test reply pxy");
+ is->forward(std::move(reply));
+
+ // check reply on client
+ reply = src.getReply();
+ ASSERT_TRUE(reply.get() != 0);
+ EXPECT_TRUE(reply->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(reply->getType() == SimpleProtocol::REPLY);
+ EXPECT_TRUE(static_cast<SimpleReply&>(*reply).getValue() == "test reply pxy");
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/simpleprotocol/.gitignore b/messagebus/src/tests/simpleprotocol/.gitignore
new file mode 100644
index 00000000000..8a096b651d4
--- /dev/null
+++ b/messagebus/src/tests/simpleprotocol/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+simpleprotocol_test
+messagebus_simpleprotocol_test_app
diff --git a/messagebus/src/tests/simpleprotocol/CMakeLists.txt b/messagebus/src/tests/simpleprotocol/CMakeLists.txt
new file mode 100644
index 00000000000..4b4e777ea57
--- /dev/null
+++ b/messagebus/src/tests/simpleprotocol/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_simpleprotocol_test_app
+ SOURCES
+ simpleprotocol.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_simpleprotocol_test_app COMMAND messagebus_simpleprotocol_test_app)
diff --git a/messagebus/src/tests/simpleprotocol/DESC b/messagebus/src/tests/simpleprotocol/DESC
new file mode 100644
index 00000000000..91d0fa36c57
--- /dev/null
+++ b/messagebus/src/tests/simpleprotocol/DESC
@@ -0,0 +1,3 @@
+Small test of the simple protocol defined in the test library. The
+protocol will be used to test other messagebus features, including
+cross-language compatibility.
diff --git a/messagebus/src/tests/simpleprotocol/FILES b/messagebus/src/tests/simpleprotocol/FILES
new file mode 100644
index 00000000000..f3c58f7d66e
--- /dev/null
+++ b/messagebus/src/tests/simpleprotocol/FILES
@@ -0,0 +1 @@
+simpleprotocol.cpp
diff --git a/messagebus/src/tests/simpleprotocol/simpleprotocol.cpp b/messagebus/src/tests/simpleprotocol/simpleprotocol.cpp
new file mode 100644
index 00000000000..eaf609a2be1
--- /dev/null
+++ b/messagebus/src/tests/simpleprotocol/simpleprotocol.cpp
@@ -0,0 +1,72 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("simpleprotocol_test");
+
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/ireplyhandler.h>
+#include <vespa/messagebus/network/identity.h>
+#include <vespa/messagebus/routing/routingcontext.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/messagebus/vtag.h>
+
+using namespace mbus;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("simpleprotocol_test");
+
+ vespalib::Version version = Vtag::currentVersion;
+ SimpleProtocol protocol;
+ EXPECT_TRUE(protocol.getName() == "Simple");
+
+ {
+ // test protocol
+ IRoutingPolicy::UP bogus = protocol.createPolicy("bogus", "");
+ EXPECT_TRUE(bogus.get() == 0);
+ }
+ TEST_FLUSH();
+ {
+ // test SimpleMessage
+ Message::UP msg(new SimpleMessage("test"));
+ EXPECT_TRUE(!msg->isReply());
+ EXPECT_TRUE(msg->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(msg->getType() == SimpleProtocol::MESSAGE);
+ EXPECT_TRUE(static_cast<SimpleMessage&>(*msg).getValue() == "test");
+ Blob b = protocol.encode(version, *msg);
+ EXPECT_TRUE(b.size() > 0);
+ Routable::UP tmp = protocol.decode(version, BlobRef(b));
+ ASSERT_TRUE(tmp.get() != 0);
+ EXPECT_TRUE(!tmp->isReply());
+ EXPECT_TRUE(tmp->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(tmp->getType() == SimpleProtocol::MESSAGE);
+ EXPECT_TRUE(static_cast<SimpleMessage&>(*tmp).getValue() == "test");
+ }
+ TEST_FLUSH();
+ {
+ // test SimpleReply
+ Reply::UP reply(new SimpleReply("reply"));
+ EXPECT_TRUE(reply->isReply());
+ EXPECT_TRUE(reply->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(reply->getType() == SimpleProtocol::REPLY);
+ EXPECT_TRUE(static_cast<SimpleReply&>(*reply).getValue() == "reply");
+ Blob b = protocol.encode(version, *reply);
+ EXPECT_TRUE(b.size() > 0);
+ Routable::UP tmp = protocol.decode(version, BlobRef(b));
+ ASSERT_TRUE(tmp.get() != 0);
+ EXPECT_TRUE(tmp->isReply());
+ EXPECT_TRUE(tmp->getProtocol() == SimpleProtocol::NAME);
+ EXPECT_TRUE(tmp->getType() == SimpleProtocol::REPLY);
+ EXPECT_TRUE(static_cast<SimpleReply&>(*tmp).getValue() == "reply");
+ }
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/slobrok/.gitignore b/messagebus/src/tests/slobrok/.gitignore
new file mode 100644
index 00000000000..6176a4876be
--- /dev/null
+++ b/messagebus/src/tests/slobrok/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+slobrok_test
+messagebus_slobrok_test_app
diff --git a/messagebus/src/tests/slobrok/CMakeLists.txt b/messagebus/src/tests/slobrok/CMakeLists.txt
new file mode 100644
index 00000000000..d21768c1f6b
--- /dev/null
+++ b/messagebus/src/tests/slobrok/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_slobrok_test_app
+ SOURCES
+ slobrok.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_slobrok_test_app COMMAND messagebus_slobrok_test_app)
diff --git a/messagebus/src/tests/slobrok/DESC b/messagebus/src/tests/slobrok/DESC
new file mode 100644
index 00000000000..7d68f120d91
--- /dev/null
+++ b/messagebus/src/tests/slobrok/DESC
@@ -0,0 +1,2 @@
+A simple test to ensure we are able to perform
+register/unregister/lookup of messagebus networks against the slobrok.
diff --git a/messagebus/src/tests/slobrok/FILES b/messagebus/src/tests/slobrok/FILES
new file mode 100644
index 00000000000..3fc79ffa0cb
--- /dev/null
+++ b/messagebus/src/tests/slobrok/FILES
@@ -0,0 +1 @@
+slobrok.cpp
diff --git a/messagebus/src/tests/slobrok/slobrok.cpp b/messagebus/src/tests/slobrok/slobrok.cpp
new file mode 100644
index 00000000000..d51a5330cd5
--- /dev/null
+++ b/messagebus/src/tests/slobrok/slobrok.cpp
@@ -0,0 +1,134 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("slobrok_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <string>
+#include <sstream>
+#include <vespa/slobrok/sbmirror.h>
+#include <vespa/messagebus/network/rpcnetwork.h>
+#include <vespa/vespalib/util/host_name.h>
+
+using slobrok::api::IMirrorAPI;
+
+using namespace mbus;
+
+string
+createSpec(int port)
+{
+ std::ostringstream str;
+ str << "tcp/";
+ str << vespalib::HostName::get();
+ str << ":";
+ str << port;
+ return str.str();
+}
+
+struct SpecList
+{
+ IMirrorAPI::SpecList _specList;
+ SpecList() : _specList() {}
+ SpecList(IMirrorAPI::SpecList input) : _specList(input) {}
+ SpecList &add(const string &name, const string &spec) {
+ _specList.push_back(std::make_pair(string(name),
+ string(spec)));
+ return *this;
+ }
+ void sort() {
+ std::sort(_specList.begin(), _specList.end());
+ }
+ bool operator==(SpecList &rhs) { // NB: MUTATE!
+ sort();
+ rhs.sort();
+ return _specList == rhs._specList;
+ }
+};
+
+bool
+compare(const IMirrorAPI &api, const string &pattern, SpecList expect)
+{
+ for (int i = 0; i < 250; ++i) {
+ SpecList actual(api.lookup(pattern));
+ if (actual == expect) {
+ return true;
+ }
+ FastOS_Thread::Sleep(100);
+ }
+ return false;
+}
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("slobrok_test");
+ Slobrok slobrok;
+ RPCNetwork net1(RPCNetworkParams()
+ .setIdentity(Identity("net/a"))
+ .setSlobrokConfig(slobrok.config()));
+ RPCNetwork net2(RPCNetworkParams()
+ .setIdentity(Identity("net/b"))
+ .setSlobrokConfig(slobrok.config()));
+ RPCNetwork net3(RPCNetworkParams()
+ .setIdentity(Identity("net/c"))
+ .setSlobrokConfig(slobrok.config()));
+ ASSERT_TRUE(net1.start());
+ ASSERT_TRUE(net2.start());
+ ASSERT_TRUE(net3.start());
+ string spec1 = createSpec(net1.getPort());
+ string spec2 = createSpec(net2.getPort());
+ string spec3 = createSpec(net3.getPort());
+
+ net1.registerSession("foo");
+ net2.registerSession("foo");
+ net2.registerSession("bar");
+ net3.registerSession("foo");
+ net3.registerSession("bar");
+ net3.registerSession("baz");
+
+ EXPECT_TRUE(compare(net1.getMirror(), "*/*/*", SpecList()
+ .add("net/a/foo", spec1)
+ .add("net/b/foo", spec2)
+ .add("net/b/bar", spec2)
+ .add("net/c/foo", spec3)
+ .add("net/c/bar", spec3)
+ .add("net/c/baz", spec3)));
+ EXPECT_TRUE(compare(net2.getMirror(), "*/*/*", SpecList()
+ .add("net/a/foo", spec1)
+ .add("net/b/foo", spec2)
+ .add("net/b/bar", spec2)
+ .add("net/c/foo", spec3)
+ .add("net/c/bar", spec3)
+ .add("net/c/baz", spec3)));
+ EXPECT_TRUE(compare(net3.getMirror(), "*/*/*", SpecList()
+ .add("net/a/foo", spec1)
+ .add("net/b/foo", spec2)
+ .add("net/b/bar", spec2)
+ .add("net/c/foo", spec3)
+ .add("net/c/bar", spec3)
+ .add("net/c/baz", spec3)));
+
+ net2.unregisterSession("bar");
+ net3.unregisterSession("bar");
+ net3.unregisterSession("baz");
+
+ EXPECT_TRUE(compare(net1.getMirror(), "*/*/*", SpecList()
+ .add("net/a/foo", spec1)
+ .add("net/b/foo", spec2)
+ .add("net/c/foo", spec3)));
+ EXPECT_TRUE(compare(net2.getMirror(), "*/*/*", SpecList()
+ .add("net/a/foo", spec1)
+ .add("net/b/foo", spec2)
+ .add("net/c/foo", spec3)));
+ EXPECT_TRUE(compare(net3.getMirror(), "*/*/*", SpecList()
+ .add("net/a/foo", spec1)
+ .add("net/b/foo", spec2)
+ .add("net/c/foo", spec3)));
+
+ net3.shutdown();
+ net2.shutdown();
+ net1.shutdown();
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/sourcesession/.gitignore b/messagebus/src/tests/sourcesession/.gitignore
new file mode 100644
index 00000000000..c6400268d94
--- /dev/null
+++ b/messagebus/src/tests/sourcesession/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+sourcesession_test
+messagebus_sourcesession_test_app
diff --git a/messagebus/src/tests/sourcesession/CMakeLists.txt b/messagebus/src/tests/sourcesession/CMakeLists.txt
new file mode 100644
index 00000000000..c2cf4f59682
--- /dev/null
+++ b/messagebus/src/tests/sourcesession/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_sourcesession_test_app
+ SOURCES
+ sourcesession.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_sourcesession_test_app COMMAND messagebus_sourcesession_test_app)
diff --git a/messagebus/src/tests/sourcesession/DESC b/messagebus/src/tests/sourcesession/DESC
new file mode 100644
index 00000000000..3417f6b602b
--- /dev/null
+++ b/messagebus/src/tests/sourcesession/DESC
@@ -0,0 +1,3 @@
+Simple test to verify the basic behavior of the resender and sequencer
+components in a full setup. This test is complemented by the resender
+and sequencer tests.
diff --git a/messagebus/src/tests/sourcesession/FILES b/messagebus/src/tests/sourcesession/FILES
new file mode 100644
index 00000000000..b7d8703e7f9
--- /dev/null
+++ b/messagebus/src/tests/sourcesession/FILES
@@ -0,0 +1 @@
+sourcesession.cpp
diff --git a/messagebus/src/tests/sourcesession/sourcesession.cpp b/messagebus/src/tests/sourcesession/sourcesession.cpp
new file mode 100644
index 00000000000..f70789d81af
--- /dev/null
+++ b/messagebus/src/tests/sourcesession/sourcesession.cpp
@@ -0,0 +1,339 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("sourcesession_test");
+
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/error.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/routing/routingcontext.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+struct DelayedHandler : public IMessageHandler
+{
+ DestinationSession::UP session;
+ uint32_t delay;
+
+ DelayedHandler(MessageBus &mb, uint32_t d) : session(), delay(d) {
+ session = mb.createDestinationSession("session", true, *this);
+ }
+ ~DelayedHandler() {
+ session.reset();
+ }
+ virtual void handleMessage(Message::UP msg) {
+ // this will block the transport thread in the server messagebus,
+ // but that should be ok, as we only want to test the timing in the
+ // client messagebus...
+ FastOS_Thread::Sleep(delay);
+ session->acknowledge(std::move(msg));
+ }
+};
+
+RoutingSpec getRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("dst", "dst/session"))
+ .addRoute(RouteSpec("dst").addHop("dst")));
+}
+
+RoutingSpec getBadRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("dst", "dst/session"))
+ .addRoute(RouteSpec("dst").addHop("dst")));
+}
+
+bool waitQueueSize(RoutableQueue &queue, uint32_t size) {
+ for (uint32_t i = 0; i < 60000; ++i) {
+ if (queue.size() == size) {
+ return true;
+ }
+ FastOS_Thread::Sleep(1);
+ }
+ return false;
+}
+
+class Test : public vespalib::TestApp
+{
+public:
+ void testSequencing();
+ void testResendError();
+ void testResendConnDown();
+ void testIllegalRoute();
+ void testNoServices();
+ void testBlockingClose();
+ void testNonBlockingClose();
+ int Main();
+};
+
+void
+Test::testSequencing()
+{
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+ TestServer dst(Identity("dst"), getRouting(), slobrok);
+
+ RoutableQueue srcQ;
+ RoutableQueue dstQ;
+
+ SourceSessionParams params;
+ params.setThrottlePolicy(IThrottlePolicy::SP());
+
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ, params);
+ DestinationSession::UP ds = dst.mb.createDestinationSession("session", true, dstQ);
+
+ ASSERT_TRUE(src.waitSlobrok("dst/session"));
+
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("foo", true, 1)), "dst").isAccepted());
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("foo", true, 2)), "dst").isAccepted());
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("foo", true, 1)), "dst").isAccepted());
+ EXPECT_TRUE(waitQueueSize(dstQ, 2));
+ FastOS_Thread::Sleep(250);
+ EXPECT_TRUE(waitQueueSize(dstQ, 2));
+ EXPECT_TRUE(waitQueueSize(srcQ, 0));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ EXPECT_TRUE(waitQueueSize(srcQ, 2));
+ EXPECT_TRUE(waitQueueSize(dstQ, 1));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 3));
+ ASSERT_TRUE(waitQueueSize(dstQ, 0));
+}
+
+void
+Test::testResendError()
+{
+ Slobrok slobrok;
+ RetryTransientErrorsPolicy::SP retryPolicy(new RetryTransientErrorsPolicy());
+ retryPolicy->setBaseDelay(0);
+ TestServer src(MessageBusParams().addProtocol(IProtocol::SP(new SimpleProtocol())).setRetryPolicy(retryPolicy),
+ RPCNetworkParams().setSlobrokConfig(slobrok.config()));
+ src.mb.setupRouting(getRouting());
+ TestServer dst(Identity("dst"), getRouting(), slobrok);
+
+ RoutableQueue srcQ;
+ RoutableQueue dstQ;
+
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ);
+ DestinationSession::UP ds = dst.mb.createDestinationSession("session", true, dstQ);
+
+ ASSERT_TRUE(src.waitSlobrok("dst/session"));
+
+ {
+ Message::UP msg(new SimpleMessage("foo"));
+ msg->getTrace().setLevel(9);
+ EXPECT_TRUE(ss->send(std::move(msg), "dst").isAccepted());
+ }
+ EXPECT_TRUE(waitQueueSize(dstQ, 1));
+ {
+ Routable::UP r = dstQ.dequeue(0);
+ Reply::UP reply(new EmptyReply());
+ r->swapState(*reply);
+ reply->addError(Error(ErrorCode::FATAL_ERROR, "error"));
+ ds->reply(std::move(reply));
+ }
+ EXPECT_TRUE(waitQueueSize(srcQ, 1));
+ EXPECT_TRUE(waitQueueSize(dstQ, 0));
+
+ {
+ Message::UP msg(new SimpleMessage("foo"));
+ msg->getTrace().setLevel(9);
+ EXPECT_TRUE(ss->send(std::move(msg), "dst").isAccepted());
+ }
+ EXPECT_TRUE(waitQueueSize(dstQ, 1));
+ {
+ Routable::UP r = dstQ.dequeue(0);
+ Reply::UP reply(new EmptyReply());
+ r->swapState(*reply);
+ reply->addError(Error(ErrorCode::TRANSIENT_ERROR, "error"));
+ ds->reply(std::move(reply));
+ }
+ EXPECT_TRUE(waitQueueSize(dstQ, 1));
+ EXPECT_TRUE(waitQueueSize(srcQ, 1));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 2));
+ ASSERT_TRUE(waitQueueSize(dstQ, 0));
+ {
+ string trace1 = srcQ.dequeue(0)->getTrace().toString();
+ string trace2 = srcQ.dequeue(0)->getTrace().toString();
+ fprintf(stderr, "\nTRACE DUMP:\n%s\n\n", trace1.c_str());
+ fprintf(stderr, "\nTRACE DUMP:\n%s\n\n", trace2.c_str());
+ }
+}
+
+void
+Test::testResendConnDown()
+{
+ Slobrok slobrok;
+ RetryTransientErrorsPolicy::SP retryPolicy(new RetryTransientErrorsPolicy());
+ retryPolicy->setBaseDelay(0);
+ TestServer src(MessageBusParams().addProtocol(IProtocol::SP(new SimpleProtocol())).setRetryPolicy(retryPolicy),
+ RPCNetworkParams().setSlobrokConfig(slobrok.config()));
+ src.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addHop(HopSpec("dst", "dst2/session"))
+ .addHop(HopSpec("pxy", "[All]").addRecipient("dst"))
+ .addRoute(RouteSpec("dst").addHop("pxy"))));
+ RoutableQueue srcQ;
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ);
+
+ TestServer dst(Identity("dst"), RoutingSpec(), slobrok);
+ RoutableQueue dstQ;
+ DestinationSession::UP ds = dst.mb.createDestinationSession("session", true, dstQ);
+ ASSERT_TRUE(src.waitSlobrok("dst/session", 1));
+
+ {
+ TestServer dst2(Identity("dst2"), RoutingSpec(), slobrok);
+ RoutableQueue dst2Q;
+ DestinationSession::UP ds2 = dst2.mb.createDestinationSession("session", true, dst2Q);
+ ASSERT_TRUE(src.waitSlobrok("dst2/session", 1));
+
+ Message::UP msg(new SimpleMessage("foo"));
+ msg->getTrace().setLevel(9);
+ EXPECT_TRUE(ss->send(std::move(msg), "dst").isAccepted());
+ EXPECT_TRUE(waitQueueSize(dst2Q, 1));
+ Routable::UP obj = dst2Q.dequeue(0);
+ obj->discard();
+ src.mb.setupRouting(RoutingSpec().addTable(RoutingTableSpec(SimpleProtocol::NAME)
+ .addHop(HopSpec("dst", "dst/session"))));
+ } // dst2 goes down, resend with new config
+
+ ASSERT_TRUE(waitQueueSize(dstQ, 1)); // fails
+ ASSERT_TRUE(waitQueueSize(srcQ, 0));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 1));
+ ASSERT_TRUE(waitQueueSize(dstQ, 0));
+
+ string trace = srcQ.dequeue(0)->getTrace().toString();
+ fprintf(stderr, "\nTRACE DUMP:\n%s\n\n", trace.c_str());
+}
+
+void
+Test::testIllegalRoute()
+{
+ Slobrok slobrok;
+ TestServer src(MessageBusParams()
+ .addProtocol(IProtocol::SP(new SimpleProtocol()))
+ .setRetryPolicy(IRetryPolicy::SP()),
+ RPCNetworkParams()
+ .setSlobrokConfig(slobrok.config()));
+ src.mb.setupRouting(getRouting());
+
+ RoutableQueue srcQ;
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ, SourceSessionParams());
+ {
+ // no such hop
+ Message::UP msg(new SimpleMessage("foo"));
+ msg->getTrace().setLevel(9);
+ msg->setRoute(Route::parse("bogus"));
+ EXPECT_TRUE(ss->send(std::move(msg)).isAccepted());
+ }
+ ASSERT_TRUE(waitQueueSize(srcQ, 1));
+ {
+ while (srcQ.size() > 0) {
+ Routable::UP routable = srcQ.dequeue(0);
+ ASSERT_TRUE(routable->isReply());
+ Reply::UP r(static_cast<Reply*>(routable.release()));
+ EXPECT_EQUAL(1u, r->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::NO_ADDRESS_FOR_SERVICE, r->getError(0).getCode());
+ string trace = r->getTrace().toString();
+ fprintf(stderr, "\nTRACE DUMP:\n%s\n\n", trace.c_str());
+ }
+ }
+}
+
+void
+Test::testNoServices()
+{
+ Slobrok slobrok;
+ TestServer src(MessageBusParams()
+ .addProtocol(IProtocol::SP(new SimpleProtocol()))
+ .setRetryPolicy(IRetryPolicy::SP()),
+ RPCNetworkParams()
+ .setSlobrokConfig(slobrok.config()));
+ src.mb.setupRouting(getBadRouting());
+
+ RoutableQueue srcQ;
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ);
+ {
+ // no services for hop
+ Message::UP msg(new SimpleMessage("foo"));
+ msg->getTrace().setLevel(9);
+ EXPECT_TRUE(ss->send(std::move(msg), "dst").isAccepted());
+ }
+ ASSERT_TRUE(waitQueueSize(srcQ, 1));
+ {
+ while (srcQ.size() > 0) {
+ Routable::UP routable = srcQ.dequeue(0);
+ ASSERT_TRUE(routable->isReply());
+ Reply::UP r(static_cast<Reply*>(routable.release()));
+ EXPECT_TRUE(r->getNumErrors() == 1);
+ EXPECT_TRUE(r->getError(0).getCode() == ErrorCode::NO_ADDRESS_FOR_SERVICE);
+ string trace = r->getTrace().toString();
+ fprintf(stderr, "\nTRACE DUMP:\n%s\n\n", trace.c_str());
+ }
+ }
+}
+
+void
+Test::testBlockingClose()
+{
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+ TestServer dst(Identity("dst"), getRouting(), slobrok);
+
+ RoutableQueue srcQ;
+ DelayedHandler dstH(dst.mb, 1000);
+ ASSERT_TRUE(src.waitSlobrok("dst/session"));
+
+ SourceSessionParams params;
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ, params);
+
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("foo")), "dst").isAccepted());
+ ss->close();
+ srcQ.handleMessage(Message::UP(new SimpleMessage("bogus")));
+ Routable::UP routable = srcQ.dequeue(0);
+ EXPECT_TRUE(routable->isReply());
+}
+
+void
+Test::testNonBlockingClose()
+{
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+
+ RoutableQueue srcQ;
+
+ SourceSessionParams params;
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ, params);
+ ss->close(); // this should not hang
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("sourcesession_test");
+ testSequencing(); TEST_FLUSH();
+ testResendError(); TEST_FLUSH();
+ testResendConnDown(); TEST_FLUSH();
+ testIllegalRoute(); TEST_FLUSH();
+ testNoServices(); TEST_FLUSH();
+ testBlockingClose(); TEST_FLUSH();
+ testNonBlockingClose(); TEST_FLUSH();
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test);
diff --git a/messagebus/src/tests/targetpool/.gitignore b/messagebus/src/tests/targetpool/.gitignore
new file mode 100644
index 00000000000..d6736ff12f1
--- /dev/null
+++ b/messagebus/src/tests/targetpool/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+targetpool_test
+messagebus_targetpool_test_app
diff --git a/messagebus/src/tests/targetpool/CMakeLists.txt b/messagebus/src/tests/targetpool/CMakeLists.txt
new file mode 100644
index 00000000000..2fedf07d03d
--- /dev/null
+++ b/messagebus/src/tests/targetpool/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_targetpool_test_app
+ SOURCES
+ targetpool.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_targetpool_test_app COMMAND messagebus_targetpool_test_app)
diff --git a/messagebus/src/tests/targetpool/DESC b/messagebus/src/tests/targetpool/DESC
new file mode 100644
index 00000000000..8ba567d0efd
--- /dev/null
+++ b/messagebus/src/tests/targetpool/DESC
@@ -0,0 +1 @@
+targetpool test. Take a look at targetpool.cpp for details.
diff --git a/messagebus/src/tests/targetpool/FILES b/messagebus/src/tests/targetpool/FILES
new file mode 100644
index 00000000000..5fb34e2994b
--- /dev/null
+++ b/messagebus/src/tests/targetpool/FILES
@@ -0,0 +1 @@
+targetpool.cpp
diff --git a/messagebus/src/tests/targetpool/targetpool.cpp b/messagebus/src/tests/targetpool/targetpool.cpp
new file mode 100644
index 00000000000..0e63be19547
--- /dev/null
+++ b/messagebus/src/tests/targetpool/targetpool.cpp
@@ -0,0 +1,100 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("targetpool_test");
+
+#include <vespa/messagebus/vtag.h>
+#include <vespa/messagebus/network/rpctargetpool.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace mbus;
+
+class PoolTimer : public ITimer {
+public:
+ uint64_t millis;
+
+ PoolTimer() : millis(0) {
+ // empty
+ }
+
+ uint64_t getMilliTime() const {
+ return millis;
+ }
+};
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("targetpool_test");
+
+ // Necessary setup to be able to resolve targets.
+ Slobrok slobrok;
+ TestServer srv1(Identity("srv1"), RoutingSpec(), slobrok);
+ RPCServiceAddress adr1("", srv1.mb.getConnectionSpec());
+ TestServer srv2(Identity("srv2"), RoutingSpec(), slobrok);
+ RPCServiceAddress adr2("", srv2.mb.getConnectionSpec());
+ TestServer srv3(Identity("srv3"), RoutingSpec(), slobrok);
+ RPCServiceAddress adr3("", srv3.mb.getConnectionSpec());
+
+ FRT_Supervisor orb(1024u, 1);
+ ASSERT_TRUE(orb.Start());
+ std::unique_ptr<PoolTimer> ptr(new PoolTimer());
+ PoolTimer &timer = *ptr;
+ RPCTargetPool pool(std::move(ptr), 0.666);
+
+ // Assert that all connections expire.
+ RPCTarget::SP target;
+ ASSERT_TRUE((target = pool.getTarget(orb, adr1)).get() != NULL); target.reset();
+ ASSERT_TRUE((target = pool.getTarget(orb, adr2)).get() != NULL); target.reset();
+ ASSERT_TRUE((target = pool.getTarget(orb, adr3)).get() != NULL); target.reset();
+ EXPECT_EQUAL(3u, pool.size());
+ for (uint32_t i = 0; i < 10; ++i) {
+ pool.flushTargets(false);
+ EXPECT_EQUAL(3u, pool.size());
+ }
+ timer.millis += 999;
+ pool.flushTargets(false);
+ EXPECT_EQUAL(0u, pool.size());
+
+ // Assert that only idle connections expire.
+ ASSERT_TRUE((target = pool.getTarget(orb, adr1)).get() != NULL); target.reset();
+ ASSERT_TRUE((target = pool.getTarget(orb, adr2)).get() != NULL); target.reset();
+ ASSERT_TRUE((target = pool.getTarget(orb, adr3)).get() != NULL); target.reset();
+ EXPECT_EQUAL(3u, pool.size());
+ timer.millis += 444;
+ pool.flushTargets(false);
+ EXPECT_EQUAL(3u, pool.size());
+ ASSERT_TRUE((target = pool.getTarget(orb, adr2)).get() != NULL); target.reset();
+ ASSERT_TRUE((target = pool.getTarget(orb, adr3)).get() != NULL); target.reset();
+ timer.millis += 444;
+ pool.flushTargets(false);
+ EXPECT_EQUAL(2u, pool.size());
+ ASSERT_TRUE((target = pool.getTarget(orb, adr3)).get() != NULL); target.reset();
+ timer.millis += 444;
+ pool.flushTargets(false);
+ EXPECT_EQUAL(1u, pool.size());
+ timer.millis += 444;
+ pool.flushTargets(false);
+ EXPECT_EQUAL(0u, pool.size());
+
+ // Assert that connections never expire while they are referenced.
+ ASSERT_TRUE((target = pool.getTarget(orb, adr1)).get() != NULL);
+ EXPECT_EQUAL(1u, pool.size());
+ for (int i = 0; i < 10; ++i) {
+ timer.millis += 999;
+ pool.flushTargets(false);
+ EXPECT_EQUAL(1u, pool.size());
+ }
+ target.reset();
+ timer.millis += 999;
+ pool.flushTargets(false);
+ EXPECT_EQUAL(0u, pool.size());
+
+ orb.ShutDown(true);
+
+ TEST_DONE();
+}
diff --git a/messagebus/src/tests/throttling/.gitignore b/messagebus/src/tests/throttling/.gitignore
new file mode 100644
index 00000000000..86ed5cc9f60
--- /dev/null
+++ b/messagebus/src/tests/throttling/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+throttling_test
+messagebus_throttling_test_app
diff --git a/messagebus/src/tests/throttling/CMakeLists.txt b/messagebus/src/tests/throttling/CMakeLists.txt
new file mode 100644
index 00000000000..28c27971e6f
--- /dev/null
+++ b/messagebus/src/tests/throttling/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_throttling_test_app
+ SOURCES
+ throttling.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_throttling_test_app COMMAND messagebus_throttling_test_app)
diff --git a/messagebus/src/tests/throttling/DESC b/messagebus/src/tests/throttling/DESC
new file mode 100644
index 00000000000..4e8dfc56357
--- /dev/null
+++ b/messagebus/src/tests/throttling/DESC
@@ -0,0 +1 @@
+throttling test. Take a look at throttling.cpp for details.
diff --git a/messagebus/src/tests/throttling/FILES b/messagebus/src/tests/throttling/FILES
new file mode 100644
index 00000000000..037f6cd99b9
--- /dev/null
+++ b/messagebus/src/tests/throttling/FILES
@@ -0,0 +1 @@
+throttling.cpp
diff --git a/messagebus/src/tests/throttling/throttling.cpp b/messagebus/src/tests/throttling/throttling.cpp
new file mode 100644
index 00000000000..6d543318559
--- /dev/null
+++ b/messagebus/src/tests/throttling/throttling.cpp
@@ -0,0 +1,362 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("throttling_test");
+
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/dynamicthrottlepolicy.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/routablequeue.h>
+#include <vespa/messagebus/routing/retrytransienterrorspolicy.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/staticthrottlepolicy.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/testserver.h>
+
+using namespace mbus;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Utilities
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class DynamicTimer : public ITimer {
+public:
+ uint64_t _millis;
+
+ DynamicTimer() : _millis(0) {
+ // empty
+ }
+
+ uint64_t getMilliTime() const {
+ return _millis;
+ }
+};
+
+RoutingSpec getRouting()
+{
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("dst", "dst/session"))
+ .addRoute(RouteSpec("dst").addHop("dst")));
+}
+
+bool waitQueueSize(RoutableQueue &queue, uint32_t size)
+{
+ for (uint32_t i = 0; i < 10000; ++i) {
+ if (queue.size() == size) {
+ return true;
+ }
+ FastOS_Thread::Sleep(10);
+ }
+ return false;
+}
+
+bool waitPending(SourceSession& session, uint32_t size)
+{
+ for (uint32_t i = 0; i < 60000; ++i) {
+ if (session.getPendingCount() == size) {
+ return true;
+ }
+ FastOS_Thread::Sleep(1);
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Setup
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class Test : public vespalib::TestApp {
+private:
+ uint32_t getWindowSize(DynamicThrottlePolicy &policy, DynamicTimer &timer, uint32_t maxPending);
+
+protected:
+ void testMaxPendingCount();
+ void testMaxPendingSize();
+ void testMinOne();
+ void testDynamicWindowSize();
+ void testIdleTimePeriod();
+ void testMinWindowSize();
+ void testMaxWindowSize();
+
+public:
+ int Main();
+};
+
+int
+Test::Main()
+{
+ TEST_INIT("throttling_test");
+
+ testMaxPendingCount(); TEST_FLUSH();
+ testMaxPendingSize(); TEST_FLUSH();
+ testMinOne(); TEST_FLUSH();
+ testDynamicWindowSize(); TEST_FLUSH();
+ testIdleTimePeriod(); TEST_FLUSH();
+ testMinWindowSize(); TEST_FLUSH();
+ testMaxWindowSize(); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+TEST_APPHOOK(Test);
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tests
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void
+Test::testMaxPendingCount()
+{
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+ TestServer dst(Identity("dst"), getRouting(), slobrok);
+
+ RoutableQueue srcQ;
+ RoutableQueue dstQ;
+
+ SourceSessionParams params;
+ StaticThrottlePolicy::SP policy(new StaticThrottlePolicy());
+ policy->setMaxPendingCount(5);
+ policy->setMaxPendingSize(0); // unlimited
+ params.setThrottlePolicy(policy);
+
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ, params);
+ DestinationSession::UP ds = dst.mb.createDestinationSession("session", true, dstQ);
+
+ ASSERT_TRUE(src.waitSlobrok("dst/session"));
+
+ for (uint32_t i = 0; i < 5; ++i) {
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+ }
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+
+ EXPECT_TRUE(waitQueueSize(dstQ, 5));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 1));
+
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+
+ EXPECT_TRUE(waitQueueSize(dstQ, 5));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 3));
+
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+
+ EXPECT_TRUE(waitQueueSize(dstQ, 5));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 8));
+ ASSERT_TRUE(waitQueueSize(dstQ, 0));
+}
+
+void
+Test::testMaxPendingSize()
+{
+ ASSERT_TRUE(SimpleMessage("1234567890").getApproxSize() == 10);
+ ASSERT_TRUE(SimpleMessage("123456").getApproxSize() == 6);
+ ASSERT_TRUE(SimpleMessage("12345").getApproxSize() == 5);
+ ASSERT_TRUE(SimpleMessage("1").getApproxSize() == 1);
+ ASSERT_TRUE(SimpleMessage("").getApproxSize() == 0);
+
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+ TestServer dst(Identity("dst"), getRouting(), slobrok);
+
+ RoutableQueue srcQ;
+ RoutableQueue dstQ;
+
+ SourceSessionParams params;
+ StaticThrottlePolicy::SP policy(new StaticThrottlePolicy());
+ policy->setMaxPendingCount(0); // unlimited
+ policy->setMaxPendingSize(2);
+ params.setThrottlePolicy(policy);
+
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ, params);
+ DestinationSession::UP ds = dst.mb.createDestinationSession("session", true, dstQ);
+
+ ASSERT_TRUE(src.waitSlobrok("dst/session"));
+ EXPECT_EQUAL(1u, SimpleMessage("1").getApproxSize());
+ EXPECT_EQUAL(2u, SimpleMessage("12").getApproxSize());
+
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("1")), "dst").isAccepted());
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("12")), "dst").isAccepted());
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("1")), "dst").isAccepted());
+
+ EXPECT_TRUE(waitQueueSize(dstQ, 2));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 1));
+
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("1")), "dst").isAccepted());
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 2));
+
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("12")), "dst").isAccepted());
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("1")), "dst").isAccepted());
+ EXPECT_TRUE(waitQueueSize(dstQ, 1));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 3));
+}
+
+void
+Test::testMinOne()
+{
+ ASSERT_TRUE(SimpleMessage("1234567890").getApproxSize() == 10);
+ ASSERT_TRUE(SimpleMessage("").getApproxSize() == 0);
+
+ Slobrok slobrok;
+ TestServer src(Identity(""), getRouting(), slobrok);
+ TestServer dst(Identity("dst"), getRouting(), slobrok);
+
+ RoutableQueue srcQ;
+ RoutableQueue dstQ;
+
+ SourceSessionParams params;
+ StaticThrottlePolicy::SP policy(new StaticThrottlePolicy());
+ policy->setMaxPendingCount(0); // unlimited
+ policy->setMaxPendingSize(5);
+ params.setThrottlePolicy(policy);
+
+ SourceSession::UP ss = src.mb.createSourceSession(srcQ, params);
+ DestinationSession::UP ds = dst.mb.createDestinationSession("session", true, dstQ);
+
+ ASSERT_TRUE(src.waitSlobrok("dst/session"));
+
+ EXPECT_TRUE(ss->send(Message::UP(new SimpleMessage("1234567890")), "dst").isAccepted());
+ EXPECT_TRUE(!ss->send(Message::UP(new SimpleMessage("")), "dst").isAccepted());
+
+ EXPECT_TRUE(waitQueueSize(dstQ, 1));
+ ds->acknowledge(Message::UP((Message*)dstQ.dequeue(0).release()));
+ ASSERT_TRUE(waitQueueSize(srcQ, 1));
+ EXPECT_TRUE(waitQueueSize(dstQ, 0));
+}
+
+
+void
+Test::testDynamicWindowSize()
+{
+ std::unique_ptr<DynamicTimer> ptr(new DynamicTimer());
+ DynamicTimer *timer = ptr.get();
+ DynamicThrottlePolicy policy(std::move(ptr));
+
+ policy.setWindowSizeIncrement(5);
+
+ double windowSize = getWindowSize(policy, *timer, 100);
+ ASSERT_TRUE(windowSize >= 90 && windowSize <= 110);
+
+ windowSize = getWindowSize(policy, *timer, 200);
+ ASSERT_TRUE(windowSize >= 90 && windowSize <= 210);
+
+ windowSize = getWindowSize(policy, *timer, 50);
+ ASSERT_TRUE(windowSize >= 9 && windowSize <= 55);
+
+ windowSize = getWindowSize(policy, *timer, 500);
+ ASSERT_TRUE(windowSize >= 90 && windowSize <= 505);
+
+ windowSize = getWindowSize(policy, *timer, 100);
+ ASSERT_TRUE(windowSize >= 90 && windowSize <= 110);
+}
+
+void
+Test::testIdleTimePeriod()
+{
+ ITimer::UP ptr(new DynamicTimer());
+ DynamicTimer *timer = static_cast<DynamicTimer*>(ptr.get());
+ DynamicThrottlePolicy policy(std::move(ptr));
+
+ policy.setWindowSizeIncrement(5);
+
+ double windowSize = getWindowSize(policy, *timer, 100);
+ ASSERT_TRUE(windowSize >= 90 && windowSize <= 110);
+
+ SimpleMessage msg("foo");
+ timer->_millis += 30001;
+ ASSERT_TRUE(policy.canSend(msg, 0));
+ ASSERT_TRUE(windowSize >= 90 && windowSize <= 110);
+
+ timer->_millis += 60001;
+ ASSERT_TRUE(policy.canSend(msg, 50));
+ EXPECT_EQUAL(55u, policy.getMaxPendingCount());
+
+ timer->_millis += 60001;
+ ASSERT_TRUE(policy.canSend(msg, 0));
+ EXPECT_EQUAL(5u, policy.getMaxPendingCount());
+}
+
+void
+Test::testMinWindowSize()
+{
+ ITimer::UP ptr(new DynamicTimer());
+ DynamicTimer *timer = static_cast<DynamicTimer*>(ptr.get());
+ DynamicThrottlePolicy policy(std::move(ptr));
+
+ policy.setWindowSizeIncrement(5);
+ policy.setMinWindowSize(150);
+
+ double windowSize = getWindowSize(policy, *timer, 200);
+ ASSERT_TRUE(windowSize >= 150 && windowSize <= 210);
+}
+
+void
+Test::testMaxWindowSize()
+{
+ ITimer::UP ptr(new DynamicTimer());
+ DynamicTimer *timer = static_cast<DynamicTimer*>(ptr.get());
+ DynamicThrottlePolicy policy(std::move(ptr));
+
+ policy.setWindowSizeIncrement(5);
+ policy.setMaxWindowSize(50);
+
+ double windowSize = getWindowSize(policy, *timer, 100);
+ ASSERT_TRUE(windowSize >= 40 && windowSize <= 50);
+
+ policy.setMaxPendingCount(15);
+ windowSize = getWindowSize(policy, *timer, 100);
+ ASSERT_TRUE(windowSize >= 10 && windowSize <= 15);
+
+}
+
+uint32_t
+Test::getWindowSize(DynamicThrottlePolicy &policy, DynamicTimer &timer, uint32_t maxPending)
+{
+ SimpleMessage msg("foo");
+ SimpleReply reply("bar");
+
+ for (uint32_t i = 0; i < 999; ++i) {
+ uint32_t numPending = 0;
+ while (policy.canSend(msg, numPending)) {
+ policy.processMessage(msg);
+ ++numPending;
+ }
+
+ uint64_t tripTime = (numPending < maxPending) ? 1000 : 1000 + (numPending - maxPending) * 1000;
+ timer._millis += tripTime;
+
+ for( ; numPending > 0 ; --numPending) {
+ policy.processReply(reply);
+ }
+ }
+ uint32_t ret = policy.getMaxPendingCount();
+ printf("getWindowSize() = %d\n", ret);
+ return ret;
+}
diff --git a/messagebus/src/tests/timeout/.gitignore b/messagebus/src/tests/timeout/.gitignore
new file mode 100644
index 00000000000..c63e63d1685
--- /dev/null
+++ b/messagebus/src/tests/timeout/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+timeout_test
+messagebus_timeout_test_app
diff --git a/messagebus/src/tests/timeout/CMakeLists.txt b/messagebus/src/tests/timeout/CMakeLists.txt
new file mode 100644
index 00000000000..f50b81ff03f
--- /dev/null
+++ b/messagebus/src/tests/timeout/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_timeout_test_app
+ SOURCES
+ timeout.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_timeout_test_app COMMAND messagebus_timeout_test_app)
diff --git a/messagebus/src/tests/timeout/DESC b/messagebus/src/tests/timeout/DESC
new file mode 100644
index 00000000000..c90169db16e
--- /dev/null
+++ b/messagebus/src/tests/timeout/DESC
@@ -0,0 +1 @@
+timeout test. Take a look at timeout.cpp for details.
diff --git a/messagebus/src/tests/timeout/FILES b/messagebus/src/tests/timeout/FILES
new file mode 100644
index 00000000000..b36cdeb4ddf
--- /dev/null
+++ b/messagebus/src/tests/timeout/FILES
@@ -0,0 +1 @@
+timeout.cpp
diff --git a/messagebus/src/tests/timeout/timeout.cpp b/messagebus/src/tests/timeout/timeout.cpp
new file mode 100644
index 00000000000..8d6b1739776
--- /dev/null
+++ b/messagebus/src/tests/timeout/timeout.cpp
@@ -0,0 +1,83 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("timeout_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/errorcode.h>
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/network/identity.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+
+using namespace mbus;
+
+class Test : public vespalib::TestApp {
+public:
+ int Main();
+ void testZeroTimeout();
+ void testMessageExpires();
+};
+
+TEST_APPHOOK(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("timeout_test");
+
+ testZeroTimeout(); TEST_FLUSH();
+ testMessageExpires(); TEST_FLUSH();
+
+ TEST_DONE();
+}
+
+void
+Test::testZeroTimeout()
+{
+ Slobrok slobrok;
+ TestServer srcServer(Identity("src"), RoutingSpec(), slobrok);
+ TestServer dstServer(Identity("dst"), RoutingSpec(), slobrok);
+
+ Receptor srcHandler;
+ SourceSession::UP srcSession = srcServer.mb.createSourceSession(srcHandler, SourceSessionParams().setTimeout(0));
+ Receptor dstHandler;
+ DestinationSession::UP dstSession = dstServer.mb.createDestinationSession("session", true, dstHandler);
+
+ ASSERT_TRUE(srcServer.waitSlobrok("dst/session", 1));
+ ASSERT_TRUE(srcSession->send(Message::UP(new SimpleMessage("msg")), "dst/session", true).isAccepted());
+
+ Reply::UP reply = srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::TIMEOUT, reply->getError(0).getCode());
+}
+
+void
+Test::testMessageExpires()
+{
+ Slobrok slobrok;
+ TestServer srcServer(Identity("src"), RoutingSpec(), slobrok);
+ TestServer dstServer(Identity("dst"), RoutingSpec(), slobrok);
+
+ Receptor srcHandler, dstHandler;
+ SourceSession::UP srcSession = srcServer.mb.createSourceSession(srcHandler, SourceSessionParams().setTimeout(1));
+ DestinationSession::UP dstSession = dstServer.mb.createDestinationSession("session", true, dstHandler);
+
+ ASSERT_TRUE(srcServer.waitSlobrok("dst/session", 1));
+ ASSERT_TRUE(srcSession->send(Message::UP(new SimpleMessage("msg")), "dst/session", true).isAccepted());
+
+ Reply::UP reply = srcHandler.getReply();
+ ASSERT_TRUE(reply.get() != NULL);
+ EXPECT_EQUAL(1u, reply->getNumErrors());
+ EXPECT_EQUAL((uint32_t)ErrorCode::TIMEOUT, reply->getError(0).getCode());
+
+ Message::UP msg = dstHandler.getMessage(1);
+ if (msg.get() != NULL) {
+ msg->discard();
+ }
+}
diff --git a/messagebus/src/tests/trace-roundtrip/.gitignore b/messagebus/src/tests/trace-roundtrip/.gitignore
new file mode 100644
index 00000000000..cd1669fd7a8
--- /dev/null
+++ b/messagebus/src/tests/trace-roundtrip/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+trace-roundtrip_test
+messagebus_trace-roundtrip_test_app
diff --git a/messagebus/src/tests/trace-roundtrip/CMakeLists.txt b/messagebus/src/tests/trace-roundtrip/CMakeLists.txt
new file mode 100644
index 00000000000..94ed14c8d99
--- /dev/null
+++ b/messagebus/src/tests/trace-roundtrip/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(messagebus_trace-roundtrip_test_app
+ SOURCES
+ trace-roundtrip.cpp
+ DEPENDS
+ messagebus_messagebus-test
+ messagebus
+)
+vespa_add_test(NAME messagebus_trace-roundtrip_test_app COMMAND messagebus_trace-roundtrip_test_app)
diff --git a/messagebus/src/tests/trace-roundtrip/DESC b/messagebus/src/tests/trace-roundtrip/DESC
new file mode 100644
index 00000000000..eb0d3b38e63
--- /dev/null
+++ b/messagebus/src/tests/trace-roundtrip/DESC
@@ -0,0 +1 @@
+trace-roundtrip test. Take a look at trace-roundtrip.cpp for details.
diff --git a/messagebus/src/tests/trace-roundtrip/FILES b/messagebus/src/tests/trace-roundtrip/FILES
new file mode 100644
index 00000000000..13b3374345f
--- /dev/null
+++ b/messagebus/src/tests/trace-roundtrip/FILES
@@ -0,0 +1 @@
+trace-roundtrip.cpp
diff --git a/messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp b/messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp
new file mode 100644
index 00000000000..72158383807
--- /dev/null
+++ b/messagebus/src/tests/trace-roundtrip/trace-roundtrip.cpp
@@ -0,0 +1,127 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/log/log.h>
+LOG_SETUP("simple-roundtrip_test");
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/messagebus/emptyreply.h>
+#include <vespa/messagebus/messagebus.h>
+#include <vespa/messagebus/sourcesession.h>
+#include <vespa/messagebus/intermediatesession.h>
+#include <vespa/messagebus/destinationsession.h>
+#include <vespa/messagebus/testlib/slobrok.h>
+#include <vespa/messagebus/testlib/testserver.h>
+#include <vespa/messagebus/routing/routingspec.h>
+#include <vespa/messagebus/testlib/receptor.h>
+#include <vespa/messagebus/sourcesessionparams.h>
+#include <vespa/messagebus/testlib/simplemessage.h>
+#include <vespa/messagebus/testlib/simplereply.h>
+#include <vespa/messagebus/testlib/simpleprotocol.h>
+
+using namespace mbus;
+
+//-----------------------------------------------------------------------------
+
+class Proxy : public IMessageHandler,
+ public IReplyHandler
+{
+private:
+ IntermediateSession::UP _session;
+public:
+ Proxy(MessageBus &bus);
+ void handleMessage(Message::UP msg);
+ void handleReply(Reply::UP reply);
+};
+
+Proxy::Proxy(MessageBus &bus)
+ : _session(bus.createIntermediateSession("session", true, *this, *this))
+{
+}
+
+void
+Proxy::handleMessage(Message::UP msg) {
+ msg->getTrace().trace(1, "Proxy message", false);
+ _session->forward(std::move(msg));
+}
+
+void
+Proxy::handleReply(Reply::UP reply) {
+ reply->getTrace().trace(1, "Proxy reply", false);
+ _session->forward(std::move(reply));
+}
+
+//-----------------------------------------------------------------------------
+
+class Server : public IMessageHandler
+{
+private:
+ DestinationSession::UP _session;
+public:
+ Server(MessageBus &bus);
+ void handleMessage(Message::UP msg);
+};
+
+Server::Server(MessageBus &bus)
+ : _session(bus.createDestinationSession("session", true, *this))
+{
+}
+
+void
+Server::handleMessage(Message::UP msg) {
+ msg->getTrace().trace(1, "Server message", false);
+ Reply::UP reply(new EmptyReply());
+ msg->swapState(*reply);
+ reply->getTrace().trace(1, "Server reply", false);
+ _session->reply(std::move(reply));
+}
+
+//-----------------------------------------------------------------------------
+
+TEST_SETUP(Test);
+
+RoutingSpec getRouting() {
+ return RoutingSpec()
+ .addTable(RoutingTableSpec("Simple")
+ .addHop(HopSpec("pxy", "test/pxy/session"))
+ .addHop(HopSpec("dst", "test/dst/session"))
+ .addRoute(RouteSpec("test").addHop("pxy").addHop("dst")));
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("simple-roundtrip_test");
+
+ Slobrok slobrok;
+ TestServer srcNet(Identity("test/src"), getRouting(), slobrok);
+ TestServer pxyNet(Identity("test/pxy"), getRouting(), slobrok);
+ TestServer dstNet(Identity("test/dst"), getRouting(), slobrok);
+
+ Receptor src;
+ Proxy pxy(pxyNet.mb);
+ Server dst(dstNet.mb);
+
+ SourceSession::UP ss = srcNet.mb.createSourceSession(src, SourceSessionParams());
+
+ // wait for slobrok registration
+ ASSERT_TRUE(srcNet.waitSlobrok("test/pxy/session"));
+ ASSERT_TRUE(srcNet.waitSlobrok("test/dst/session"));
+ ASSERT_TRUE(pxyNet.waitSlobrok("test/dst/session"));
+
+ Message::UP msg(new SimpleMessage(""));
+ msg->getTrace().setLevel(1);
+ msg->getTrace().trace(1, "Client message", false);
+ ss->send(std::move(msg), "test");
+ Reply::UP reply = src.getReply();
+ reply->getTrace().trace(1, "Client reply", false);
+ EXPECT_TRUE(reply->getNumErrors() == 0);
+
+ TraceNode t = TraceNode()
+ .addChild("Client message")
+ .addChild("Proxy message")
+ .addChild("Server message")
+ .addChild("Server reply")
+ .addChild("Proxy reply")
+ .addChild("Client reply");
+ EXPECT_TRUE(reply->getTrace().getRoot().encode() == t.encode());
+ TEST_DONE();
+}