diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /messagebus/src/tests |
Publish
Diffstat (limited to 'messagebus/src/tests')
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 ¶m) 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 ¶m); + 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 ¶m) +{ + 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 ¶m); +}; + +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 ¶m) +{ + 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 ¶m); +}; + +ReuseReplyPolicyFactory::ReuseReplyPolicyFactory(bool selectOnRetry, + const std::vector<uint32_t> &errorMask) : + _selectOnRetry(selectOnRetry), + _errorMask(errorMask) +{ + // empty +} + +IRoutingPolicy::UP +ReuseReplyPolicyFactory::create(const string ¶m) +{ + 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 ¶m); + void select(RoutingContext &ctx); + void merge(RoutingContext &ctx); +}; + +SetReplyPolicy::SetReplyPolicy(bool selectOnRetry, + const std::vector<uint32_t> &errors, + const string ¶m) : + _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 ¶m); +}; + +SetReplyPolicyFactory::SetReplyPolicyFactory(bool selectOnRetry, + const std::vector<uint32_t> &errors) : + _selectOnRetry(selectOnRetry), + _errors(errors) +{ + // empty +} + +IRoutingPolicy::UP +SetReplyPolicyFactory::create(const string ¶m) +{ + 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 ¶m) { + (void)param; + return IRoutingPolicy::UP(new SelectExceptionPolicy()); + } +}; + +class MergeExceptionPolicy : public IRoutingPolicy { +private: + const string _select; + +public: + MergeExceptionPolicy(const string ¶m) + : _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 ¶m) { + 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 ¶m); + + 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 ¶m) +{ + (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 ¶m); +}; + +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(); +} |