diff options
Diffstat (limited to 'src/test/rgw/test_rgw_putobj.cc')
-rw-r--r-- | src/test/rgw/test_rgw_putobj.cc | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/src/test/rgw/test_rgw_putobj.cc b/src/test/rgw/test_rgw_putobj.cc new file mode 100644 index 00000000..91661f45 --- /dev/null +++ b/src/test/rgw/test_rgw_putobj.cc @@ -0,0 +1,196 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "rgw/rgw_putobj.h" +#include <gtest/gtest.h> + +inline bufferlist string_buf(const char* buf) { + bufferlist bl; + bl.append(buffer::create_static(strlen(buf), (char*)buf)); + return bl; +} + +struct Op { + std::string data; + uint64_t offset; +}; +inline bool operator==(const Op& lhs, const Op& rhs) { + return lhs.data == rhs.data && lhs.offset == rhs.offset; +} +inline std::ostream& operator<<(std::ostream& out, const Op& op) { + return out << "{off=" << op.offset << " data='" << op.data << "'}"; +} + +struct MockProcessor : rgw::putobj::DataProcessor { + std::vector<Op> ops; + + int process(bufferlist&& data, uint64_t offset) override { + ops.push_back({data.to_str(), offset}); + return {}; + } +}; + +TEST(PutObj_Chunk, FlushHalf) +{ + MockProcessor mock; + rgw::putobj::ChunkProcessor chunk(&mock, 4); + + ASSERT_EQ(0, chunk.process(string_buf("22"), 0)); + ASSERT_TRUE(mock.ops.empty()); // no writes + + ASSERT_EQ(0, chunk.process({}, 2)); // flush + ASSERT_EQ(2u, mock.ops.size()); + EXPECT_EQ(Op({"22", 0}), mock.ops[0]); + EXPECT_EQ(Op({"", 2}), mock.ops[1]); +} + +TEST(PutObj_Chunk, One) +{ + MockProcessor mock; + rgw::putobj::ChunkProcessor chunk(&mock, 4); + + ASSERT_EQ(0, chunk.process(string_buf("4444"), 0)); + ASSERT_EQ(1u, mock.ops.size()); + EXPECT_EQ(Op({"4444", 0}), mock.ops[0]); + + ASSERT_EQ(0, chunk.process({}, 4)); // flush + ASSERT_EQ(2u, mock.ops.size()); + EXPECT_EQ(Op({"", 4}), mock.ops[1]); +} + +TEST(PutObj_Chunk, OneAndFlushHalf) +{ + MockProcessor mock; + rgw::putobj::ChunkProcessor chunk(&mock, 4); + + ASSERT_EQ(0, chunk.process(string_buf("22"), 0)); + ASSERT_TRUE(mock.ops.empty()); + + ASSERT_EQ(0, chunk.process(string_buf("4444"), 2)); + ASSERT_EQ(1u, mock.ops.size()); + EXPECT_EQ(Op({"2244", 0}), mock.ops[0]); + + ASSERT_EQ(0, chunk.process({}, 6)); // flush + ASSERT_EQ(3u, mock.ops.size()); + EXPECT_EQ(Op({"44", 4}), mock.ops[1]); + EXPECT_EQ(Op({"", 6}), mock.ops[2]); +} + +TEST(PutObj_Chunk, Two) +{ + MockProcessor mock; + rgw::putobj::ChunkProcessor chunk(&mock, 4); + + ASSERT_EQ(0, chunk.process(string_buf("88888888"), 0)); + ASSERT_EQ(2u, mock.ops.size()); + EXPECT_EQ(Op({"8888", 0}), mock.ops[0]); + EXPECT_EQ(Op({"8888", 4}), mock.ops[1]); + + ASSERT_EQ(0, chunk.process({}, 8)); // flush + ASSERT_EQ(3u, mock.ops.size()); + EXPECT_EQ(Op({"", 8}), mock.ops[2]); +} + +TEST(PutObj_Chunk, TwoAndFlushHalf) +{ + MockProcessor mock; + rgw::putobj::ChunkProcessor chunk(&mock, 4); + + ASSERT_EQ(0, chunk.process(string_buf("22"), 0)); + ASSERT_TRUE(mock.ops.empty()); + + ASSERT_EQ(0, chunk.process(string_buf("88888888"), 2)); + ASSERT_EQ(2u, mock.ops.size()); + EXPECT_EQ(Op({"2288", 0}), mock.ops[0]); + EXPECT_EQ(Op({"8888", 4}), mock.ops[1]); + + ASSERT_EQ(0, chunk.process({}, 10)); // flush + ASSERT_EQ(4u, mock.ops.size()); + EXPECT_EQ(Op({"88", 8}), mock.ops[2]); + EXPECT_EQ(Op({"", 10}), mock.ops[3]); +} + + +using StripeMap = std::map<uint64_t, uint64_t>; // offset, stripe_size + +class StripeMapGen : public rgw::putobj::StripeGenerator { + const StripeMap& stripes; + public: + StripeMapGen(const StripeMap& stripes) : stripes(stripes) {} + + int next(uint64_t offset, uint64_t *stripe_size) override { + auto i = stripes.find(offset); + if (i == stripes.end()) { + return -ENOENT; + } + *stripe_size = i->second; + return 0; + } +}; + +TEST(PutObj_Stripe, DifferentStripeSize) +{ + MockProcessor mock; + StripeMap stripes{ + { 0, 4}, + { 4, 6}, + {10, 2} + }; + StripeMapGen gen(stripes); + rgw::putobj::StripeProcessor processor(&mock, &gen, stripes.begin()->second); + + ASSERT_EQ(0, processor.process(string_buf("22"), 0)); + ASSERT_EQ(1u, mock.ops.size()); + EXPECT_EQ(Op({"22", 0}), mock.ops[0]); + + ASSERT_EQ(0, processor.process(string_buf("4444"), 2)); + ASSERT_EQ(4u, mock.ops.size()); + EXPECT_EQ(Op({"44", 2}), mock.ops[1]); + EXPECT_EQ(Op({"", 4}), mock.ops[2]); // flush + EXPECT_EQ(Op({"44", 0}), mock.ops[3]); + + ASSERT_EQ(0, processor.process(string_buf("666666"), 6)); + ASSERT_EQ(7u, mock.ops.size()); + EXPECT_EQ(Op({"6666", 2}), mock.ops[4]); + EXPECT_EQ(Op({"", 6}), mock.ops[5]); // flush + EXPECT_EQ(Op({"66", 0}), mock.ops[6]); + + ASSERT_EQ(0, processor.process({}, 12)); + ASSERT_EQ(8u, mock.ops.size()); + EXPECT_EQ(Op({"", 2}), mock.ops[7]); // flush + + // gen returns an error past this + ASSERT_EQ(-ENOENT, processor.process(string_buf("1"), 12)); +} + +TEST(PutObj_Stripe, SkipFirstChunk) +{ + MockProcessor mock; + StripeMap stripes{ + {0, 4}, + {4, 4}, + }; + StripeMapGen gen(stripes); + rgw::putobj::StripeProcessor processor(&mock, &gen, stripes.begin()->second); + + ASSERT_EQ(0, processor.process(string_buf("666666"), 2)); + ASSERT_EQ(3u, mock.ops.size()); + EXPECT_EQ(Op({"66", 2}), mock.ops[0]); + EXPECT_EQ(Op({"", 4}), mock.ops[1]); // flush + EXPECT_EQ(Op({"6666", 0}), mock.ops[2]); + + ASSERT_EQ(0, processor.process({}, 8)); + ASSERT_EQ(4u, mock.ops.size()); + EXPECT_EQ(Op({"", 4}), mock.ops[3]); // flush +} |