// Copyright (c) Meta Platforms, Inc. and affiliates. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). #include "db/wide/wide_column_serialization.h" #include "test_util/testharness.h" #include "util/coding.h" namespace ROCKSDB_NAMESPACE { TEST(WideColumnSerializationTest, Construct) { constexpr char foo[] = "foo"; constexpr char bar[] = "bar"; const std::string foo_str(foo); const std::string bar_str(bar); const Slice foo_slice(foo_str); const Slice bar_slice(bar_str); { WideColumn column(foo, bar); ASSERT_EQ(column.name(), foo); ASSERT_EQ(column.value(), bar); } { WideColumn column(foo_str, bar); ASSERT_EQ(column.name(), foo_str); ASSERT_EQ(column.value(), bar); } { WideColumn column(foo_slice, bar); ASSERT_EQ(column.name(), foo_slice); ASSERT_EQ(column.value(), bar); } { WideColumn column(foo, bar_str); ASSERT_EQ(column.name(), foo); ASSERT_EQ(column.value(), bar_str); } { WideColumn column(foo_str, bar_str); ASSERT_EQ(column.name(), foo_str); ASSERT_EQ(column.value(), bar_str); } { WideColumn column(foo_slice, bar_str); ASSERT_EQ(column.name(), foo_slice); ASSERT_EQ(column.value(), bar_str); } { WideColumn column(foo, bar_slice); ASSERT_EQ(column.name(), foo); ASSERT_EQ(column.value(), bar_slice); } { WideColumn column(foo_str, bar_slice); ASSERT_EQ(column.name(), foo_str); ASSERT_EQ(column.value(), bar_slice); } { WideColumn column(foo_slice, bar_slice); ASSERT_EQ(column.name(), foo_slice); ASSERT_EQ(column.value(), bar_slice); } { constexpr char foo_name[] = "foo_name"; constexpr char bar_value[] = "bar_value"; WideColumn column(std::piecewise_construct, std::forward_as_tuple(foo_name, sizeof(foo) - 1), std::forward_as_tuple(bar_value, sizeof(bar) - 1)); ASSERT_EQ(column.name(), foo); ASSERT_EQ(column.value(), bar); } } TEST(WideColumnSerializationTest, SerializeDeserialize) { WideColumns columns{{"foo", "bar"}, {"hello", "world"}}; std::string output; ASSERT_OK(WideColumnSerialization::Serialize(columns, output)); Slice input(output); WideColumns deserialized_columns; ASSERT_OK(WideColumnSerialization::Deserialize(input, deserialized_columns)); ASSERT_EQ(columns, deserialized_columns); { const auto it = WideColumnSerialization::Find(deserialized_columns, "foo"); ASSERT_NE(it, deserialized_columns.cend()); ASSERT_EQ(*it, deserialized_columns.front()); } { const auto it = WideColumnSerialization::Find(deserialized_columns, "hello"); ASSERT_NE(it, deserialized_columns.cend()); ASSERT_EQ(*it, deserialized_columns.back()); } { const auto it = WideColumnSerialization::Find(deserialized_columns, "fubar"); ASSERT_EQ(it, deserialized_columns.cend()); } { const auto it = WideColumnSerialization::Find(deserialized_columns, "snafu"); ASSERT_EQ(it, deserialized_columns.cend()); } } TEST(WideColumnSerializationTest, SerializeDuplicateError) { WideColumns columns{{"foo", "bar"}, {"foo", "baz"}}; std::string output; ASSERT_TRUE( WideColumnSerialization::Serialize(columns, output).IsCorruption()); } TEST(WideColumnSerializationTest, SerializeOutOfOrderError) { WideColumns columns{{"hello", "world"}, {"foo", "bar"}}; std::string output; ASSERT_TRUE( WideColumnSerialization::Serialize(columns, output).IsCorruption()); } TEST(WideColumnSerializationTest, DeserializeVersionError) { // Can't decode version std::string buf; Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "version")); } TEST(WideColumnSerializationTest, DeserializeUnsupportedVersion) { // Unsupported version constexpr uint32_t future_version = 1000; std::string buf; PutVarint32(&buf, future_version); Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsNotSupported()); ASSERT_TRUE(std::strstr(s.getState(), "version")); } TEST(WideColumnSerializationTest, DeserializeNumberOfColumnsError) { // Can't decode number of columns std::string buf; PutVarint32(&buf, WideColumnSerialization::kCurrentVersion); Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "number")); } TEST(WideColumnSerializationTest, DeserializeColumnsError) { std::string buf; PutVarint32(&buf, WideColumnSerialization::kCurrentVersion); constexpr uint32_t num_columns = 2; PutVarint32(&buf, num_columns); // Can't decode the first column name { Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "name")); } constexpr char first_column_name[] = "foo"; PutLengthPrefixedSlice(&buf, first_column_name); // Can't decode the size of the first column value { Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "value size")); } constexpr uint32_t first_value_size = 16; PutVarint32(&buf, first_value_size); // Can't decode the second column name { Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "name")); } constexpr char second_column_name[] = "hello"; PutLengthPrefixedSlice(&buf, second_column_name); // Can't decode the size of the second column value { Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "value size")); } constexpr uint32_t second_value_size = 64; PutVarint32(&buf, second_value_size); // Can't decode the payload of the first column { Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "payload")); } buf.append(first_value_size, '0'); // Can't decode the payload of the second column { Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "payload")); } buf.append(second_value_size, 'x'); // Success { Slice input(buf); WideColumns columns; ASSERT_OK(WideColumnSerialization::Deserialize(input, columns)); } } TEST(WideColumnSerializationTest, DeserializeColumnsOutOfOrder) { std::string buf; PutVarint32(&buf, WideColumnSerialization::kCurrentVersion); constexpr uint32_t num_columns = 2; PutVarint32(&buf, num_columns); constexpr char first_column_name[] = "b"; PutLengthPrefixedSlice(&buf, first_column_name); constexpr uint32_t first_value_size = 16; PutVarint32(&buf, first_value_size); constexpr char second_column_name[] = "a"; PutLengthPrefixedSlice(&buf, second_column_name); Slice input(buf); WideColumns columns; const Status s = WideColumnSerialization::Deserialize(input, columns); ASSERT_TRUE(s.IsCorruption()); ASSERT_TRUE(std::strstr(s.getState(), "order")); } } // namespace ROCKSDB_NAMESPACE int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }