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 /persistence/src/tests/proxy |
Publish
Diffstat (limited to 'persistence/src/tests/proxy')
-rw-r--r-- | persistence/src/tests/proxy/.gitignore | 10 | ||||
-rw-r--r-- | persistence/src/tests/proxy/CMakeLists.txt | 28 | ||||
-rw-r--r-- | persistence/src/tests/proxy/dummy_provider_factory.h | 35 | ||||
-rw-r--r-- | persistence/src/tests/proxy/external_providerproxy_conformancetest.cpp | 43 | ||||
-rw-r--r-- | persistence/src/tests/proxy/mockprovider.h | 172 | ||||
-rw-r--r-- | persistence/src/tests/proxy/providerproxy_conformancetest.cpp | 64 | ||||
-rw-r--r-- | persistence/src/tests/proxy/providerproxy_test.cpp | 402 | ||||
-rw-r--r-- | persistence/src/tests/proxy/providerstub_test.cpp | 538 | ||||
-rw-r--r-- | persistence/src/tests/proxy/proxy_factory_wrapper.h | 59 | ||||
-rw-r--r-- | persistence/src/tests/proxy/proxy_test.sh | 4 | ||||
-rw-r--r-- | persistence/src/tests/proxy/proxyfactory.h | 42 |
11 files changed, 1397 insertions, 0 deletions
diff --git a/persistence/src/tests/proxy/.gitignore b/persistence/src/tests/proxy/.gitignore new file mode 100644 index 00000000000..9bd2934723e --- /dev/null +++ b/persistence/src/tests/proxy/.gitignore @@ -0,0 +1,10 @@ +/.depend +/Makefile +/providerstub_test +/providerproxy_test +/providerproxy_conformancetest +/external_providerproxy_conformancetest +persistence_providerproxy_conformance_test_app +persistence_providerproxy_test_app +persistence_providerstub_test_app +persistence_external_providerproxy_conformancetest_app diff --git a/persistence/src/tests/proxy/CMakeLists.txt b/persistence/src/tests/proxy/CMakeLists.txt new file mode 100644 index 00000000000..c12eaf217a4 --- /dev/null +++ b/persistence/src/tests/proxy/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(persistence_providerstub_test_app + SOURCES + providerstub_test.cpp + DEPENDS + persistence +) +vespa_add_executable(persistence_providerproxy_test_app + SOURCES + providerproxy_test.cpp + DEPENDS + persistence +) +vespa_add_executable(persistence_providerproxy_conformance_test_app + SOURCES + providerproxy_conformancetest.cpp + DEPENDS + persistence + persistence_persistence_conformancetest +) +vespa_add_executable(persistence_external_providerproxy_conformancetest_app + SOURCES + external_providerproxy_conformancetest.cpp + DEPENDS + persistence + persistence_persistence_conformancetest +) +vespa_add_test(NAME persistence_providerproxy_conformance_test_app COMMAND sh proxy_test.sh) diff --git a/persistence/src/tests/proxy/dummy_provider_factory.h b/persistence/src/tests/proxy/dummy_provider_factory.h new file mode 100644 index 00000000000..8330b4a917b --- /dev/null +++ b/persistence/src/tests/proxy/dummy_provider_factory.h @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/vespalib/testkit/test_kit.h> +#include <vespa/persistence/proxy/providerstub.h> +#include <memory> + +namespace storage { +namespace spi { + +/** + * A simple rpc server persistence provider factory that will only + * work once, by returning a precreated persistence provider instance. + **/ +struct DummyProviderFactory : ProviderStub::PersistenceProviderFactory +{ + typedef std::unique_ptr<DummyProviderFactory> UP; + typedef storage::spi::PersistenceProvider Provider; + + mutable std::unique_ptr<Provider> provider; + + DummyProviderFactory(std::unique_ptr<Provider> p) : provider(std::move(p)) {} + + std::unique_ptr<Provider> create() const { + ASSERT_TRUE(provider.get() != 0); + std::unique_ptr<Provider> ret = std::move(provider); + ASSERT_TRUE(provider.get() == 0); + return ret; + } +}; + +} // namespace spi +} // namespace storage + diff --git a/persistence/src/tests/proxy/external_providerproxy_conformancetest.cpp b/persistence/src/tests/proxy/external_providerproxy_conformancetest.cpp new file mode 100644 index 00000000000..f24d81532b7 --- /dev/null +++ b/persistence/src/tests/proxy/external_providerproxy_conformancetest.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/vespalib/testkit/test_kit.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/persistence/conformancetest/conformancetest.h> +#include <vespa/persistence/proxy/providerproxy.h> +#include <vespa/persistence/proxy/providerstub.h> +#include "proxyfactory.h" + +using namespace storage::spi; +typedef document::DocumentTypeRepo Repo; +typedef ConformanceTest::PersistenceFactory Factory; + +namespace { + +struct ConformanceFixture : public ConformanceTest { + ConformanceFixture(Factory::UP f) : ConformanceTest(std::move(f)) { setUp(); } + ~ConformanceFixture() { tearDown(); } +}; + +Factory::UP getFactory() { + return Factory::UP(new ProxyFactory()); +} + +#define CONVERT_TEST(testFunction, makeFactory) \ +namespace ns_ ## testFunction { \ +TEST_F(TEST_STR(testFunction) " " TEST_STR(makeFactory), ConformanceFixture(makeFactory)) { \ + f.testFunction(); \ +} \ +} // namespace testFunction + +#undef CPPUNIT_TEST +#define CPPUNIT_TEST(testFunction) CONVERT_TEST(testFunction, MAKE_FACTORY) + +#define MAKE_FACTORY getFactory() +DEFINE_CONFORMANCE_TESTS(); + +} // namespace + +TEST_MAIN() { + TEST_RUN_ALL(); +} diff --git a/persistence/src/tests/proxy/mockprovider.h b/persistence/src/tests/proxy/mockprovider.h new file mode 100644 index 00000000000..c2fd844a010 --- /dev/null +++ b/persistence/src/tests/proxy/mockprovider.h @@ -0,0 +1,172 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#pragma once + +#include <vespa/persistence/spi/persistenceprovider.h> + +namespace storage { +namespace spi { + +struct MockProvider : PersistenceProvider { + enum Function { NONE, INITIALIZE, GET_PARTITION_STATES, LIST_BUCKETS, + SET_CLUSTER_STATE, + SET_ACTIVE_STATE, GET_BUCKET_INFO, PUT, REMOVE_BY_ID, + REMOVE_IF_FOUND, REPLACE_WITH_REMOVE, UPDATE, FLUSH, GET, + CREATE_ITERATOR, ITERATE, DESTROY_ITERATOR, CREATE_BUCKET, + DELETE_BUCKET, GET_MODIFIED_BUCKETS, SPLIT, JOIN, MOVE, MAINTAIN, + REMOVE_ENTRY }; + + mutable Function last_called; + + MockProvider() : last_called(NONE) {} + + virtual Result initialize() { + last_called = INITIALIZE; + return Result(); + } + + virtual PartitionStateListResult getPartitionStates() const { + last_called = GET_PARTITION_STATES; + return PartitionStateListResult(PartitionStateList(1u)); + } + + virtual BucketIdListResult listBuckets(PartitionId id) const { + last_called = LIST_BUCKETS; + BucketIdListResult::List result; + result.push_back(document::BucketId(id)); + return BucketIdListResult(result); + } + + virtual Result setClusterState(const ClusterState &) { + last_called = SET_CLUSTER_STATE; + return Result(); + } + + virtual Result setActiveState(const Bucket &, + BucketInfo::ActiveState) { + last_called = SET_ACTIVE_STATE; + return Result(); + } + + virtual BucketInfoResult getBucketInfo(const Bucket &bucket) const { + last_called = GET_BUCKET_INFO; + return BucketInfoResult(BucketInfo(BucketChecksum(1), 2, 3, + bucket.getBucketId().getRawId(), + bucket.getPartition(), + BucketInfo::READY, + BucketInfo::ACTIVE)); + } + + virtual Result put(const Bucket &, Timestamp, const Document::SP&, Context&) { + last_called = PUT; + return Result(); + } + + virtual RemoveResult remove(const Bucket &, Timestamp, + const DocumentId &, Context&) { + last_called = REMOVE_BY_ID; + return RemoveResult(true); + } + + virtual RemoveResult removeIfFound(const Bucket &, Timestamp, + const DocumentId &, Context&) { + last_called = REMOVE_IF_FOUND; + return RemoveResult(true); + } + + virtual RemoveResult replaceWithRemove(const Bucket &, Timestamp, + const DocumentId &, Context&) { + last_called = REPLACE_WITH_REMOVE; + return RemoveResult(true); + } + + virtual UpdateResult update(const Bucket &, Timestamp timestamp, + const DocumentUpdate::SP&, Context&) { + last_called = UPDATE; + return UpdateResult(Timestamp(timestamp - 10)); + } + + virtual Result flush(const Bucket&, Context&) { + last_called = FLUSH; + return Result(); + } + + virtual GetResult get(const Bucket &, const document::FieldSet&, + const DocumentId&, Context&) const { + last_called = GET; + return GetResult(Document::UP(new Document), + Timestamp(6u)); + } + + virtual CreateIteratorResult createIterator(const Bucket& bucket, + const document::FieldSet&, + const Selection&, + IncludedVersions, + Context&) + { + last_called = CREATE_ITERATOR; + return CreateIteratorResult(IteratorId(bucket.getPartition())); + } + + virtual IterateResult iterate(IteratorId, uint64_t, Context&) const { + last_called = ITERATE; + IterateResult::List result; + result.push_back(DocEntry::LP(new DocEntry(Timestamp(1), 0))); + return IterateResult(result, true); + } + + virtual Result destroyIterator(IteratorId, Context&) { + last_called = DESTROY_ITERATOR; + return Result(); + } + + virtual Result createBucket(const Bucket&, Context&) { + last_called = CREATE_BUCKET; + return Result(); + } + virtual Result deleteBucket(const Bucket&, Context&) { + last_called = DELETE_BUCKET; + return Result(); + } + + virtual BucketIdListResult getModifiedBuckets() const { + last_called = GET_MODIFIED_BUCKETS; + BucketIdListResult::List list; + list.push_back(document::BucketId(2)); + list.push_back(document::BucketId(3)); + return BucketIdListResult(list); + } + + virtual Result split(const Bucket &, const Bucket &, const Bucket &, + Context&) + { + last_called = SPLIT; + return Result(); + } + + virtual Result join(const Bucket &, const Bucket &, const Bucket &, + Context&) + { + last_called = JOIN; + return Result(); + } + + virtual Result move(const Bucket &, PartitionId, Context&) { + last_called = MOVE; + return Result(); + } + + + virtual Result maintain(const Bucket &, MaintenanceLevel) { + last_called = MAINTAIN; + return Result(); + } + + virtual Result removeEntry(const Bucket &, Timestamp, Context&) { + last_called = REMOVE_ENTRY; + return Result(); + } +}; + +} // namespace spi +} // namespace storage + diff --git a/persistence/src/tests/proxy/providerproxy_conformancetest.cpp b/persistence/src/tests/proxy/providerproxy_conformancetest.cpp new file mode 100644 index 00000000000..cd2f711ffd5 --- /dev/null +++ b/persistence/src/tests/proxy/providerproxy_conformancetest.cpp @@ -0,0 +1,64 @@ +// 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/vespalib/testkit/test_kit.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/persistence/conformancetest/conformancetest.h> +#include <vespa/persistence/dummyimpl/dummypersistence.h> +#include <vespa/persistence/proxy/providerproxy.h> +#include <vespa/persistence/proxy/providerstub.h> +#include "proxy_factory_wrapper.h" + +using namespace storage::spi; +typedef document::DocumentTypeRepo Repo; +typedef ConformanceTest::PersistenceFactory Factory; + +namespace { + +struct DummyFactory : Factory { + PersistenceProvider::UP getPersistenceImplementation(const Repo::SP& repo, + const document::DocumenttypesConfig &) { + return PersistenceProvider::UP(new dummy::DummyPersistence(repo, 4)); + } + + virtual bool + supportsActiveState() const + { + return true; + } +}; + +struct ConformanceFixture : public ConformanceTest { + ConformanceFixture(Factory::UP f) : ConformanceTest(std::move(f)) { setUp(); } + ~ConformanceFixture() { tearDown(); } +}; + +Factory::UP dummyViaProxy(size_t n) { + if (n == 0) { + return Factory::UP(new DummyFactory()); + } + return Factory::UP(new ProxyFactoryWrapper(dummyViaProxy(n - 1))); +} + +#define CONVERT_TEST(testFunction, makeFactory) \ +namespace ns_ ## testFunction { \ +TEST_F(TEST_STR(testFunction) " " TEST_STR(makeFactory), ConformanceFixture(makeFactory)) { \ + f.testFunction(); \ +} \ +} // namespace testFunction + +#undef CPPUNIT_TEST +#define CPPUNIT_TEST(testFunction) CONVERT_TEST(testFunction, MAKE_FACTORY) + +#define MAKE_FACTORY dummyViaProxy(1) +DEFINE_CONFORMANCE_TESTS(); + +#undef MAKE_FACTORY +#define MAKE_FACTORY dummyViaProxy(7) +DEFINE_CONFORMANCE_TESTS(); + +} // namespace + +TEST_MAIN() { + TEST_RUN_ALL(); +} diff --git a/persistence/src/tests/proxy/providerproxy_test.cpp b/persistence/src/tests/proxy/providerproxy_test.cpp new file mode 100644 index 00000000000..34537b170e6 --- /dev/null +++ b/persistence/src/tests/proxy/providerproxy_test.cpp @@ -0,0 +1,402 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Unit tests for providerproxy. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("providerproxy_test"); + +#include "dummy_provider_factory.h" +#include "mockprovider.h" +#include <vespa/document/bucket/bucketid.h> +#include <vespa/document/datatype/datatype.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/persistence/proxy/providerproxy.h> +#include <vespa/persistence/proxy/providerstub.h> +#include <vespa/persistence/spi/abstractpersistenceprovider.h> +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/util/closure.h> +#include <vespa/vespalib/util/closuretask.h> +#include <vespa/vespalib/util/sync.h> +#include <vespa/vespalib/util/threadstackexecutor.h> +#include <vespa/document/fieldset/fieldsets.h> + +using document::BucketId; +using document::DataType; +using document::DocumentTypeRepo; +using std::ostringstream; +using vespalib::Gate; +using vespalib::ThreadStackExecutor; +using vespalib::makeClosure; +using vespalib::makeTask; +using namespace storage::spi; +using namespace storage; + +namespace { + +const int port = 14863; +const string connect_spec = "tcp/localhost:14863"; +LoadType defaultLoadType(0, "default"); + +void startServer(const DocumentTypeRepo *repo, Gate *gate) { + DummyProviderFactory factory(MockProvider::UP(new MockProvider)); + ProviderStub stub(port, 8, *repo, factory); + gate->await(); + EXPECT_TRUE(stub.hasClient()); +} + +TEST("require that client can start connecting before server is up") { + const DocumentTypeRepo repo; + Gate gate; + ThreadStackExecutor executor(1, 65536); + executor.execute(makeTask(makeClosure(startServer, &repo, &gate))); + ProviderProxy proxy(connect_spec, repo); + gate.countDown(); + executor.sync(); +} + +TEST("require that when the server goes down it causes permanent failure.") { + const DocumentTypeRepo repo; + DummyProviderFactory factory(MockProvider::UP(new MockProvider)); + ProviderStub::UP server(new ProviderStub(port, 8, repo, factory)); + ProviderProxy proxy(connect_spec, repo); + server.reset(0); + + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + Result result = proxy.flush(bucket, context); + EXPECT_EQUAL(Result::FATAL_ERROR, result.getErrorCode()); +} + +struct Fixture { + MockProvider &mock_spi; + DummyProviderFactory factory; + DocumentTypeRepo repo; + ProviderStub stub; + ProviderProxy proxy; + + Fixture() + : mock_spi(*(new MockProvider)), + factory(PersistenceProvider::UP(&mock_spi)), + repo(), + stub(port, 8, repo, factory), + proxy(connect_spec, repo) {} +}; + +TEST_F("require that client handles initialize", Fixture) { + Result result = f.proxy.initialize(); + EXPECT_EQUAL(MockProvider::INITIALIZE, f.mock_spi.last_called); +} + +TEST_F("require that client handles getPartitionStates", Fixture) { + PartitionStateListResult result = f.proxy.getPartitionStates(); + EXPECT_EQUAL(MockProvider::GET_PARTITION_STATES, f.mock_spi.last_called); + EXPECT_EQUAL(1u, result.getList().size()); +} + +TEST_F("require that client handles listBuckets", Fixture) { + const PartitionId partition_id(42); + + BucketIdListResult result = f.proxy.listBuckets(partition_id); + EXPECT_EQUAL(MockProvider::LIST_BUCKETS, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + ASSERT_EQUAL(1u, result.getList().size()); +} + +TEST_F("require that client handles setClusterState", Fixture) { + lib::ClusterState s("version:1 storage:3 distributor:3"); + lib::Distribution d(lib::Distribution::getDefaultDistributionConfig(3, 3)); + ClusterState state(s, 0, d); + + Result result = f.proxy.setClusterState(state); + EXPECT_EQUAL(MockProvider::SET_CLUSTER_STATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles setActiveState", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + const BucketInfo::ActiveState bucket_state = BucketInfo::NOT_ACTIVE; + + Result result = f.proxy.setActiveState(bucket, bucket_state); + EXPECT_EQUAL(MockProvider::SET_ACTIVE_STATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles getBucketInfo", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + + BucketInfoResult result = f.proxy.getBucketInfo(bucket); + EXPECT_EQUAL(MockProvider::GET_BUCKET_INFO, f.mock_spi.last_called); + + const BucketInfo& info(result.getBucketInfo()); + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + EXPECT_EQUAL(1u, info.getChecksum()); + EXPECT_EQUAL(2u, info.getDocumentCount()); + EXPECT_EQUAL(3u, info.getDocumentSize()); + EXPECT_EQUAL(bucket_id, info.getEntryCount()); + EXPECT_EQUAL(partition_id, info.getUsedSize()); + EXPECT_EQUAL(true, info.isReady()); + EXPECT_EQUAL(true, info.isActive()); +} + +TEST_F("require that client handles put", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + const Timestamp timestamp(84); + Document::SP doc(new Document()); + + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + Result result = f.proxy.put(bucket, timestamp, doc, context); + EXPECT_EQUAL(MockProvider::PUT, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles remove by id", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + const Timestamp timestamp(84); + const DocumentId id("doc:test:1"); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + RemoveResult result = f.proxy.remove(bucket, timestamp, id, context); + EXPECT_EQUAL(MockProvider::REMOVE_BY_ID, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + EXPECT_EQUAL(true, result.wasFound()); +} + +TEST_F("require that client handles removeIfFound", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + const Timestamp timestamp(84); + const DocumentId id("doc:test:1"); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + RemoveResult result = f.proxy.removeIfFound(bucket, timestamp, id, context); + EXPECT_EQUAL(MockProvider::REMOVE_IF_FOUND, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + EXPECT_EQUAL(true, result.wasFound()); +} + +TEST_F("require that client handles update", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + const Timestamp timestamp(84); + DocumentUpdate::SP update(new DocumentUpdate(*DataType::DOCUMENT, DocumentId("doc:test:1"))); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + UpdateResult result = f.proxy.update(bucket, timestamp, update, context); + EXPECT_EQUAL(MockProvider::UPDATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + EXPECT_EQUAL(timestamp - 10, result.getExistingTimestamp()); +} + +TEST_F("require that client handles flush", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + Result result = f.proxy.flush(bucket, context); + EXPECT_EQUAL(MockProvider::FLUSH, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles get", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + + document::AllFields field_set; + const DocumentId id("doc:test:1"); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + GetResult result = f.proxy.get(bucket, field_set, id, context); + EXPECT_EQUAL(MockProvider::GET, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + EXPECT_EQUAL(6u, result.getTimestamp()); + ASSERT_TRUE(result.hasDocument()); + EXPECT_EQUAL(Document(), result.getDocument()); +} + +TEST_F("require that client handles createIterator", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + const DocumentSelection doc_sel("docsel"); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + document::AllFields field_set; + + Selection selection(doc_sel); + selection.setFromTimestamp(Timestamp(84)); + selection.setToTimestamp(Timestamp(126)); + + CreateIteratorResult result = + f.proxy.createIterator(bucket, field_set, selection, + NEWEST_DOCUMENT_ONLY, context); + + EXPECT_EQUAL(MockProvider::CREATE_ITERATOR, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + EXPECT_EQUAL(partition_id, result.getIteratorId()); +} + +TEST_F("require that client handles iterate", Fixture) { + const IteratorId iterator_id(42); + const uint64_t max_byte_size = 21; + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + IterateResult result = f.proxy.iterate(iterator_id, max_byte_size, context); + EXPECT_EQUAL(MockProvider::ITERATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); + EXPECT_EQUAL(1u, result.getEntries().size()); + EXPECT_TRUE(result.isCompleted()); +} + +TEST_F("require that client handles destroyIterator", Fixture) { + const IteratorId iterator_id(42); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + f.proxy.destroyIterator(iterator_id, context); + EXPECT_EQUAL(MockProvider::DESTROY_ITERATOR, f.mock_spi.last_called); +} + +TEST_F("require that client handles createBucket", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + f.proxy.createBucket(bucket, context); + EXPECT_EQUAL(MockProvider::CREATE_BUCKET, f.mock_spi.last_called); +} + +TEST_F("require that server accepts deleteBucket", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + f.proxy.deleteBucket(bucket, context); + EXPECT_EQUAL(MockProvider::DELETE_BUCKET, f.mock_spi.last_called); +} + +TEST_F("require that client handles getModifiedBuckets", Fixture) { + BucketIdListResult modifiedBuckets = f.proxy.getModifiedBuckets(); + EXPECT_EQUAL(MockProvider::GET_MODIFIED_BUCKETS, f.mock_spi.last_called); + + EXPECT_EQUAL(2u, modifiedBuckets.getList().size()); +} + +TEST_F("require that client handles split", Fixture) { + const uint64_t bucket_id_1 = 21; + const PartitionId partition_id_1(42); + const Bucket bucket_1(BucketId(bucket_id_1), partition_id_1); + const uint64_t bucket_id_2 = 210; + const PartitionId partition_id_2(420); + const Bucket bucket_2(BucketId(bucket_id_2), partition_id_2); + const uint64_t bucket_id_3 = 2100; + const PartitionId partition_id_3(4200); + const Bucket bucket_3(BucketId(bucket_id_3), partition_id_3); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + Result result = f.proxy.split(bucket_1, bucket_2, bucket_3, context); + EXPECT_EQUAL(MockProvider::SPLIT, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles join", Fixture) { + const uint64_t bucket_id_1 = 21; + const PartitionId partition_id_1(42); + const Bucket bucket_1(BucketId(bucket_id_1), partition_id_1); + const uint64_t bucket_id_2 = 210; + const PartitionId partition_id_2(420); + const Bucket bucket_2(BucketId(bucket_id_2), partition_id_2); + const uint64_t bucket_id_3 = 2100; + const PartitionId partition_id_3(4200); + const Bucket bucket_3(BucketId(bucket_id_3), partition_id_3); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + Result result = f.proxy.join(bucket_1, bucket_2, bucket_3, context); + EXPECT_EQUAL(MockProvider::JOIN, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles move", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId from_partition_id(42); + const PartitionId to_partition_id(43); + const Bucket bucket(BucketId(bucket_id), from_partition_id); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + Result result = f.proxy.move(bucket, to_partition_id, context); + EXPECT_EQUAL(MockProvider::MOVE, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles maintain", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + + Result result = f.proxy.maintain(bucket, HIGH); + EXPECT_EQUAL(MockProvider::MAINTAIN, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +TEST_F("require that client handles remove entry", Fixture) { + const uint64_t bucket_id = 21; + const PartitionId partition_id(42); + const Bucket bucket(BucketId(bucket_id), partition_id); + const Timestamp timestamp(345); + Context context(defaultLoadType, Priority(0), Trace::TraceLevel(0)); + + Result result = f.proxy.removeEntry(bucket, timestamp, context); + EXPECT_EQUAL(MockProvider::REMOVE_ENTRY, f.mock_spi.last_called); + + EXPECT_EQUAL(0, result.getErrorCode()); + EXPECT_EQUAL("", result.getErrorMessage()); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/persistence/src/tests/proxy/providerstub_test.cpp b/persistence/src/tests/proxy/providerstub_test.cpp new file mode 100644 index 00000000000..07eed26db19 --- /dev/null +++ b/persistence/src/tests/proxy/providerstub_test.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. +// Unit tests for providerstub. + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("providerstub_test"); + +#include <vespa/document/datatype/datatype.h> +#include <vespa/document/repo/documenttyperepo.h> +#include <vespa/document/serialization/vespadocumentserializer.h> +#include <vespa/document/util/bytebuffer.h> +#include <vespa/persistence/proxy/buildid.h> +#include <vespa/persistence/proxy/providerstub.h> +#include <vespa/persistence/spi/abstractpersistenceprovider.h> +#include <vespa/vespalib/objects/nbostream.h> +#include <vespa/vespalib/testkit/testapp.h> + +using document::BucketId; +using document::ByteBuffer; +using document::DataType; +using document::DocumentTypeRepo; +using document::VespaDocumentSerializer; +using vespalib::nbostream; +using namespace storage::spi; +using namespace storage; + +#include <tests/proxy/mockprovider.h> +#include "dummy_provider_factory.h" + +namespace { + +const int port = 14863; +const char connect_spec[] = "tcp/localhost:14863"; +const string build_id = getBuildId(); + +struct Fixture { + MockProvider &mock_spi; + DummyProviderFactory factory; + DocumentTypeRepo repo; + ProviderStub stub; + FRT_Supervisor supervisor; + FRT_RPCRequest *current_request; + FRT_Target *target; + + Fixture() + : mock_spi(*(new MockProvider())), + factory(PersistenceProvider::UP(&mock_spi)), + repo(), + stub(port, 8, repo, factory), + supervisor(), + current_request(0), + target(supervisor.GetTarget(connect_spec)) + { + supervisor.Start(); + ASSERT_TRUE(target); + } + ~Fixture() { + if (current_request) { + current_request->SubRef(); + } + target->SubRef(); + supervisor.ShutDown(true); + } + FRT_RPCRequest *getRequest(const string &name) { + FRT_RPCRequest *req = supervisor.AllocRPCRequest(current_request); + current_request = req; + req->SetMethodName(name.c_str()); + return req; + } + void callRpc(FRT_RPCRequest *req, const string &return_spec) { + target->InvokeSync(req, 5.0); + req->CheckReturnTypes(return_spec.c_str()); + if (!EXPECT_EQUAL(uint32_t(FRTE_NO_ERROR), req->GetErrorCode())) { + TEST_FATAL(req->GetErrorMessage()); + } + } + void failRpc(FRT_RPCRequest *req, uint32_t error_code) { + target->InvokeSync(req, 5.0); + EXPECT_EQUAL(error_code, req->GetErrorCode()); + } +}; + +struct ConnectedFixture : Fixture { + ConnectedFixture() { + FRT_RPCRequest *req = getRequest("vespa.persistence.connect"); + req->GetParams()->AddString(build_id.data(), build_id.size()); + callRpc(req, ""); + } +}; + +TEST("print build id") { fprintf(stderr, "build id: '%s'\n", getBuildId()); } + +TEST_F("require that server accepts connect", Fixture) { + FRT_RPCRequest *req = f.getRequest("vespa.persistence.connect"); + req->GetParams()->AddString(build_id.data(), build_id.size()); + f.callRpc(req, ""); + EXPECT_TRUE(f.stub.hasClient()); +} + +TEST_F("require that connect can be called twice", ConnectedFixture) { + EXPECT_TRUE(f.stub.hasClient()); + FRT_RPCRequest *req = f.getRequest("vespa.persistence.connect"); + req->GetParams()->AddString(build_id.data(), build_id.size()); + f.callRpc(req, ""); + EXPECT_TRUE(f.stub.hasClient()); +} + +TEST_F("require that connect fails with wrong build id", Fixture) { + FRT_RPCRequest *req = f.getRequest("vespa.persistence.connect"); + const string wrong_id = "wrong build id"; + req->GetParams()->AddString(wrong_id.data(), wrong_id.size()); + f.failRpc(req, FRTE_RPC_METHOD_FAILED); + string prefix("Wrong build id. Got 'wrong build id', required "); + EXPECT_EQUAL(prefix, + string(req->GetErrorMessage()).substr(0, prefix.size())); + EXPECT_FALSE(f.stub.hasClient()); +} + +TEST_F("require that only one client can connect", ConnectedFixture) { + EXPECT_TRUE(f.stub.hasClient()); + FRT_RPCRequest *req = f.getRequest("vespa.persistence.connect"); + req->GetParams()->AddString(build_id.data(), build_id.size()); + FRT_Target *target = f.supervisor.GetTarget(connect_spec); + target->InvokeSync(req, 5.0); + target->SubRef(); + EXPECT_EQUAL(uint32_t(FRTE_RPC_METHOD_FAILED), req->GetErrorCode()); + EXPECT_EQUAL("Server is already connected", + string(req->GetErrorMessage())); +} + +TEST_F("require that server accepts getPartitionStates", ConnectedFixture) { + FRT_RPCRequest *req = f.getRequest("vespa.persistence.getPartitionStates"); + f.callRpc(req, "bsIS"); + EXPECT_EQUAL(MockProvider::GET_PARTITION_STATES, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(2)._int32_array._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(3)._string_array._len); +} + +TEST_F("require that server accepts listBuckets", ConnectedFixture) { + const uint64_t partition_id = 42; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.listBuckets"); + req->GetParams()->AddInt64(partition_id); + f.callRpc(req, "bsL"); + EXPECT_EQUAL(MockProvider::LIST_BUCKETS, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(2)._int64_array._len); + EXPECT_EQUAL(partition_id, + req->GetReturn()->GetValue(2)._int64_array._pt[0]); +} + +TEST_F("require that server accepts setClusterState", ConnectedFixture) { + FRT_RPCRequest *req = f.getRequest("vespa.persistence.setClusterState"); + + lib::ClusterState s("version:1 storage:3 distributor:3"); + lib::Distribution d(lib::Distribution::getDefaultDistributionConfig(3, 3)); + ClusterState state(s, 0, d); + vespalib::nbostream o; + state.serialize(o); + req->GetParams()->AddData(o.c_str(), o.size()); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::SET_CLUSTER_STATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +TEST_F("require that server accepts setActiveState", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const BucketInfo::ActiveState bucket_state = BucketInfo::NOT_ACTIVE; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.setActiveState"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddInt8(bucket_state); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::SET_ACTIVE_STATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +TEST_F("require that server accepts getBucketInfo", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.getBucketInfo"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + f.callRpc(req, "bsiiiiibb"); + EXPECT_EQUAL(MockProvider::GET_BUCKET_INFO, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(2)._intval32); + EXPECT_EQUAL(2u, req->GetReturn()->GetValue(3)._intval32); + EXPECT_EQUAL(3u, req->GetReturn()->GetValue(4)._intval32); + EXPECT_EQUAL(bucket_id, req->GetReturn()->GetValue(5)._intval32); + EXPECT_EQUAL(partition_id, req->GetReturn()->GetValue(6)._intval32); + EXPECT_EQUAL(static_cast<uint8_t>(BucketInfo::READY), + req->GetReturn()->GetValue(7)._intval8); + EXPECT_EQUAL(static_cast<uint8_t>(BucketInfo::ACTIVE), + req->GetReturn()->GetValue(8)._intval8); +} + +TEST_F("require that server accepts put", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const Timestamp timestamp(84); + Document::UP doc(new Document); + nbostream stream; + VespaDocumentSerializer serializer(stream); + serializer.write(*doc, document::COMPLETE); + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.put"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddInt64(timestamp); + req->GetParams()->AddData(stream.c_str(), stream.size()); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::PUT, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +void testRemove(ConnectedFixture &f, const string &rpc_name, + MockProvider::Function func) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const Timestamp timestamp(84); + const DocumentId id("doc:test:1"); + + FRT_RPCRequest *req = f.getRequest(rpc_name); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddInt64(timestamp); + req->GetParams()->AddString(id.toString().data(), id.toString().size()); + f.callRpc(req, "bsb"); + EXPECT_EQUAL(func, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_TRUE(req->GetReturn()->GetValue(2)._intval8); +} + +TEST_F("require that server accepts remove by id", ConnectedFixture) { + testRemove(f, "vespa.persistence.removeById", MockProvider::REMOVE_BY_ID); +} + +TEST_F("require that server accepts removeIfFound", ConnectedFixture) { + testRemove(f, "vespa.persistence.removeIfFound", + MockProvider::REMOVE_IF_FOUND); +} + +TEST_F("require that server accepts update", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const Timestamp timestamp(84); + DocumentUpdate update(*DataType::DOCUMENT, DocumentId("doc:test:1")); + vespalib::nbostream stream; + update.serializeHEAD(stream); + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.update"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddInt64(timestamp); + req->GetParams()->AddData(stream.c_str(), stream.size()); + f.callRpc(req, "bsl"); + EXPECT_EQUAL(MockProvider::UPDATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_EQUAL(timestamp - 10, req->GetReturn()->GetValue(2)._intval64); +} + +TEST_F("require that server accepts flush", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.flush"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::FLUSH, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +TEST_F("require that server accepts get", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const string field_set_1 = "[all]"; + const DocumentId id("doc:test:1"); + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.get"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddString(field_set_1.data(), field_set_1.size()); + req->GetParams()->AddString(id.toString().data(), id.toString().size()); + f.callRpc(req, "bslx"); + EXPECT_EQUAL(MockProvider::GET, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_EQUAL(6u, req->GetReturn()->GetValue(2)._intval64); + EXPECT_EQUAL(25u, req->GetReturn()->GetValue(3)._data._len); +} + +TEST_F("require that server accepts createIterator", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const string doc_sel = "docsel"; + const Timestamp timestamp_from(84); + const Timestamp timestamp_to(126); + const Timestamp timestamp_subset(168); + const string field_set_1 = "[all]"; + const bool include_removes = false; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.createIterator"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddString(field_set_1.data(), field_set_1.size()); + req->GetParams()->AddString(doc_sel.data(), doc_sel.size()); + req->GetParams()->AddInt64(timestamp_from); + req->GetParams()->AddInt64(timestamp_to); + req->GetParams()->AddInt64Array(1)[0] = timestamp_subset; + req->GetParams()->AddInt8(include_removes); + + f.callRpc(req, "bsl"); + EXPECT_EQUAL(MockProvider::CREATE_ITERATOR, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_EQUAL(partition_id, req->GetReturn()->GetValue(2)._intval64); +} + +TEST_F("require that server accepts iterate", ConnectedFixture) { + const uint64_t iterator_id = 42; + const uint64_t max_byte_size = 21; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.iterate"); + req->GetParams()->AddInt64(iterator_id); + req->GetParams()->AddInt64(max_byte_size); + f.callRpc(req, "bsLISXb"); + EXPECT_EQUAL(MockProvider::ITERATE, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(2)._int64_array._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(3)._int32_array._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(4)._string_array._len); + EXPECT_EQUAL(1u, req->GetReturn()->GetValue(5)._data_array._len); + EXPECT_TRUE(req->GetReturn()->GetValue(6)._intval8); +} + +TEST_F("require that server accepts destroyIterator", ConnectedFixture) { + const uint64_t iterator_id = 42; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.destroyIterator"); + req->GetParams()->AddInt64(iterator_id); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::DESTROY_ITERATOR, f.mock_spi.last_called); +} + +TEST_F("require that server accepts createBucket", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.createBucket"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::CREATE_BUCKET, f.mock_spi.last_called); +} + +TEST_F("require that server accepts deleteBucket", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.deleteBucket"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::DELETE_BUCKET, f.mock_spi.last_called); +} + +TEST_F("require that server accepts getModifiedBuckets", ConnectedFixture) { + FRT_RPCRequest *req = f.getRequest("vespa.persistence.getModifiedBuckets"); + f.callRpc(req, "bsL"); + EXPECT_EQUAL(MockProvider::GET_MODIFIED_BUCKETS, f.mock_spi.last_called); + EXPECT_EQUAL(2u, req->GetReturn()->GetValue(2)._int64_array._len); +} + +TEST_F("require that server accepts split", ConnectedFixture) { + const uint64_t bucket_id_1 = 21; + const uint64_t partition_id_1 = 42; + const uint64_t bucket_id_2 = 210; + const uint64_t partition_id_2 = 420; + const uint64_t bucket_id_3 = 2100; + const uint64_t partition_id_3 = 4200; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.split"); + req->GetParams()->AddInt64(bucket_id_1); + req->GetParams()->AddInt64(partition_id_1); + req->GetParams()->AddInt64(bucket_id_2); + req->GetParams()->AddInt64(partition_id_2); + req->GetParams()->AddInt64(bucket_id_3); + req->GetParams()->AddInt64(partition_id_3); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::SPLIT, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +TEST_F("require that server accepts join", ConnectedFixture) { + const uint64_t bucket_id_1 = 21; + const uint64_t partition_id_1 = 42; + const uint64_t bucket_id_2 = 210; + const uint64_t partition_id_2 = 420; + const uint64_t bucket_id_3 = 2100; + const uint64_t partition_id_3 = 4200; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.join"); + req->GetParams()->AddInt64(bucket_id_1); + req->GetParams()->AddInt64(partition_id_1); + req->GetParams()->AddInt64(bucket_id_2); + req->GetParams()->AddInt64(partition_id_2); + req->GetParams()->AddInt64(bucket_id_3); + req->GetParams()->AddInt64(partition_id_3); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::JOIN, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +TEST_F("require that server accepts move", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t from_partition_id = 42; + const uint64_t to_partition_id = 43; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.move"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(from_partition_id); + req->GetParams()->AddInt64(to_partition_id); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::MOVE, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +TEST_F("require that server accepts maintain", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const MaintenanceLevel verification_level = HIGH; + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.maintain"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddInt8(verification_level); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::MAINTAIN, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +TEST_F("require that server accepts remove_entry", ConnectedFixture) { + const uint64_t bucket_id = 21; + const uint64_t partition_id = 42; + const Timestamp timestamp(345); + + FRT_RPCRequest *req = f.getRequest("vespa.persistence.removeEntry"); + req->GetParams()->AddInt64(bucket_id); + req->GetParams()->AddInt64(partition_id); + req->GetParams()->AddInt64(timestamp); + f.callRpc(req, "bs"); + EXPECT_EQUAL(MockProvider::REMOVE_ENTRY, f.mock_spi.last_called); + + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(0)._intval8); + EXPECT_EQUAL(0u, req->GetReturn()->GetValue(1)._string._len); +} + +void checkRpcFails(const string &name, const string ¶m_spec, Fixture &f) { + TEST_STATE(name.c_str()); + FRT_RPCRequest *req = f.getRequest("vespa.persistence." + name); + for (size_t i = 0; i < param_spec.size(); ++i) { + switch(param_spec[i]) { + case 'b': req->GetParams()->AddInt8(0); break; + case 'l': req->GetParams()->AddInt64(0); break; + case 'L': req->GetParams()->AddInt64Array(0); break; + case 's': req->GetParams()->AddString(0, 0); break; + case 'S': req->GetParams()->AddStringArray(0); break; + case 'x': req->GetParams()->AddData(0, 0); break; + } + } + f.failRpc(req, FRTE_RPC_METHOD_FAILED); +} + +TEST_F("require that unconnected server fails all SPI calls.", Fixture) +{ + checkRpcFails("initialize", "", f); + checkRpcFails("getPartitionStates", "", f); + checkRpcFails("listBuckets", "l", f); + checkRpcFails("setClusterState", "x", f); + checkRpcFails("setActiveState", "llb", f); + checkRpcFails("getBucketInfo", "ll", f); + checkRpcFails("put", "lllx", f); + checkRpcFails("removeById", "llls", f); + checkRpcFails("removeIfFound", "llls", f); + checkRpcFails("update", "lllx", f); + checkRpcFails("flush", "ll", f); + checkRpcFails("get", "llss", f); + checkRpcFails("createIterator", "llssllLb", f); + checkRpcFails("iterate", "ll", f); + checkRpcFails("destroyIterator", "l", f); + checkRpcFails("createBucket", "ll", f); + checkRpcFails("deleteBucket", "ll", f); + checkRpcFails("getModifiedBuckets", "", f); + checkRpcFails("split", "llllll", f); + checkRpcFails("join", "llllll", f); + checkRpcFails("maintain", "llb", f); + checkRpcFails("removeEntry", "lll", f); +} + +} // namespace + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/persistence/src/tests/proxy/proxy_factory_wrapper.h b/persistence/src/tests/proxy/proxy_factory_wrapper.h new file mode 100644 index 00000000000..10f251f2beb --- /dev/null +++ b/persistence/src/tests/proxy/proxy_factory_wrapper.h @@ -0,0 +1,59 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/vespalib/util/vstringfmt.h> +#include <vespa/persistence/conformancetest/conformancetest.h> +#include <vespa/persistence/proxy/providerstub.h> +#include <vespa/persistence/proxy/providerproxy.h> +#include "dummy_provider_factory.h" + +namespace storage { +namespace spi { + +/** + * Generic wrapper for persistence conformance test factories. This + * wrapper will take any other factory and expose a factory interface + * that will create persistence instances that communicate with + * persistence instances created by the wrapped factory using the RPC + * persistence Proxy. + **/ +struct ProxyFactoryWrapper : ConformanceTest::PersistenceFactory +{ + typedef storage::spi::ConformanceTest::PersistenceFactory Factory; + typedef storage::spi::PersistenceProvider Provider; + typedef storage::spi::ProviderStub Server; + typedef storage::spi::ProviderProxy Client; + typedef document::DocumentTypeRepo Repo; + + Factory::UP factory; + ProxyFactoryWrapper(Factory::UP f) : factory(std::move(f)) {} + + struct Wrapper : Client { + DummyProviderFactory::UP provider; + Server::UP server; + Wrapper(DummyProviderFactory::UP p, Server::UP s, const Repo &repo) + : Client(vespalib::make_vespa_string("tcp/localhost:%u", s->getPort()), repo), + provider(std::move(p)), + server(std::move(s)) + {} + }; + + virtual Provider::UP + getPersistenceImplementation(const document::DocumentTypeRepo::SP &repo, + const document::DocumenttypesConfig &typesCfg) { + DummyProviderFactory::UP provider(new DummyProviderFactory(factory->getPersistenceImplementation(repo, + typesCfg))); + Server::UP server(new Server(0, 8, *repo, *provider)); + return Provider::UP(new Wrapper(std::move(provider), std::move(server), *repo)); + } + + virtual bool + supportsActiveState() const + { + return factory->supportsActiveState(); + } +}; +} // namespace spi +} // namespace storage + diff --git a/persistence/src/tests/proxy/proxy_test.sh b/persistence/src/tests/proxy/proxy_test.sh new file mode 100644 index 00000000000..a78487831d6 --- /dev/null +++ b/persistence/src/tests/proxy/proxy_test.sh @@ -0,0 +1,4 @@ +#!/bin/sh +$VALGRIND ./persistence_providerstub_test_app +$VALGRIND ./persistence_providerproxy_test_app +$VALGRIND ./persistence_providerproxy_conformance_test_app diff --git a/persistence/src/tests/proxy/proxyfactory.h b/persistence/src/tests/proxy/proxyfactory.h new file mode 100644 index 00000000000..9de9a39e873 --- /dev/null +++ b/persistence/src/tests/proxy/proxyfactory.h @@ -0,0 +1,42 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/vespalib/util/vstringfmt.h> +#include <vespa/persistence/conformancetest/conformancetest.h> +#include <vespa/persistence/proxy/providerstub.h> +#include <vespa/persistence/proxy/providerproxy.h> + +namespace storage { +namespace spi { + +/** + * Generic wrapper for persistence conformance test factories. This + * wrapper will take any other factory and expose a factory interface + * that will create persistence instances that communicate with + * persistence instances created by the wrapped factory using the RPC + * persistence Proxy. + **/ +struct ProxyFactory : ConformanceTest::PersistenceFactory +{ + typedef storage::spi::PersistenceProvider Provider; + typedef storage::spi::ProviderProxy Client; + typedef document::DocumentTypeRepo Repo; + + ProxyFactory() {} + + virtual Provider::UP + getPersistenceImplementation(const document::DocumentTypeRepo::SP &repo, + const document::DocumenttypesConfig &) { + return Provider::UP(new Client("tcp/localhost:3456", *repo)); + } + + virtual bool + supportsActiveState() const + { + return false; + } +}; +} // namespace spi +} // namespace storage + |