#include "test_util/sync_point.h"
#include "test_util/testharness.h"
#include "util/coding.h"
+#include "util/string_util.h"
namespace ROCKSDB_NAMESPACE {
TestEncodeDecode(edit);
}
+TEST_F(VersionEditTest, BlobFileAdditionAndGarbage) {
+ VersionEdit edit;
+
+ const std::string checksum_method_prefix = "Hash";
+ const std::string checksum_value_prefix = "Value";
+
+ for (uint64_t blob_file_number = 1; blob_file_number <= 10;
+ ++blob_file_number) {
+ const uint64_t total_blob_count = blob_file_number << 10;
+ const uint64_t total_blob_bytes = blob_file_number << 20;
+
+ std::string checksum_method(checksum_method_prefix);
+ AppendNumberTo(&checksum_method, blob_file_number);
+
+ std::string checksum_value(checksum_value_prefix);
+ AppendNumberTo(&checksum_value, blob_file_number);
+
+ edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
+ checksum_method, checksum_value);
+
+ const uint64_t garbage_blob_count = total_blob_count >> 2;
+ const uint64_t garbage_blob_bytes = total_blob_bytes >> 1;
+
+ edit.AddBlobFileGarbage(blob_file_number, garbage_blob_count,
+ garbage_blob_bytes);
+ }
+
+ TestEncodeDecode(edit);
+}
+
+TEST_F(VersionEditTest, AddWalEncodeDecode) {
+ VersionEdit edit;
+ for (uint64_t log_number = 1; log_number <= 20; log_number++) {
+ WalMetadata meta;
+ bool has_size = rand() % 2 == 0;
+ if (has_size) {
+ meta.SetSyncedSizeInBytes(rand() % 1000);
+ }
+ edit.AddWal(log_number, meta);
+ }
+ TestEncodeDecode(edit);
+}
+
+TEST_F(VersionEditTest, AddWalDecodeBadLogNumber) {
+ std::string encoded;
+ PutVarint32(&encoded, Tag::kWalAddition);
+
+ {
+ // No log number.
+ VersionEdit edit;
+ Status s = edit.DecodeFrom(encoded);
+ ASSERT_TRUE(s.IsCorruption());
+ ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
+ std::string::npos)
+ << s.ToString();
+ }
+
+ {
+ // log number should be varint64,
+ // but we only encode 128 which is not a valid representation of varint64.
+ char c = 0;
+ unsigned char* ptr = reinterpret_cast<unsigned char*>(&c);
+ *ptr = 128;
+ encoded.append(1, c);
+ VersionEdit edit;
+ Status s = edit.DecodeFrom(encoded);
+ ASSERT_TRUE(s.IsCorruption());
+ ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
+ std::string::npos)
+ << s.ToString();
+ }
+}
+
+TEST_F(VersionEditTest, AddWalDecodeBadTag) {
+ constexpr WalNumber kLogNumber = 100;
+ constexpr uint64_t kSizeInBytes = 100;
+
+ std::string encoded_without_tag;
+ PutVarint32(&encoded_without_tag, Tag::kWalAddition);
+ PutVarint64(&encoded_without_tag, kLogNumber);
+
+ {
+ // No tag.
+ VersionEdit edit;
+ Status s = edit.DecodeFrom(encoded_without_tag);
+ ASSERT_TRUE(s.IsCorruption());
+ ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
+ << s.ToString();
+ }
+
+ {
+ // Only has size tag, no terminate tag.
+ std::string encoded_with_size = encoded_without_tag;
+ PutVarint32(&encoded_with_size,
+ static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
+ PutVarint64(&encoded_with_size, kSizeInBytes);
+ VersionEdit edit;
+ Status s = edit.DecodeFrom(encoded_with_size);
+ ASSERT_TRUE(s.IsCorruption());
+ ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
+ << s.ToString();
+ }
+
+ {
+ // Only has terminate tag.
+ std::string encoded_with_terminate = encoded_without_tag;
+ PutVarint32(&encoded_with_terminate,
+ static_cast<uint32_t>(WalAdditionTag::kTerminate));
+ VersionEdit edit;
+ ASSERT_OK(edit.DecodeFrom(encoded_with_terminate));
+ auto& wal_addition = edit.GetWalAdditions()[0];
+ ASSERT_EQ(wal_addition.GetLogNumber(), kLogNumber);
+ ASSERT_FALSE(wal_addition.GetMetadata().HasSyncedSize());
+ }
+}
+
+TEST_F(VersionEditTest, AddWalDecodeNoSize) {
+ constexpr WalNumber kLogNumber = 100;
+
+ std::string encoded;
+ PutVarint32(&encoded, Tag::kWalAddition);
+ PutVarint64(&encoded, kLogNumber);
+ PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
+ // No real size after the size tag.
+
+ {
+ // Without terminate tag.
+ VersionEdit edit;
+ Status s = edit.DecodeFrom(encoded);
+ ASSERT_TRUE(s.IsCorruption());
+ ASSERT_TRUE(s.ToString().find("Error decoding WAL file size") !=
+ std::string::npos)
+ << s.ToString();
+ }
+
+ {
+ // With terminate tag.
+ PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kTerminate));
+ VersionEdit edit;
+ Status s = edit.DecodeFrom(encoded);
+ ASSERT_TRUE(s.IsCorruption());
+ // The terminate tag is misunderstood as the size.
+ ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
+ << s.ToString();
+ }
+}
+
+TEST_F(VersionEditTest, AddWalDebug) {
+ constexpr int n = 2;
+ constexpr std::array<uint64_t, n> kLogNumbers{{10, 20}};
+ constexpr std::array<uint64_t, n> kSizeInBytes{{100, 200}};
+
+ VersionEdit edit;
+ for (int i = 0; i < n; i++) {
+ edit.AddWal(kLogNumbers[i], WalMetadata(kSizeInBytes[i]));
+ }
+
+ const WalAdditions& wals = edit.GetWalAdditions();
+
+ ASSERT_TRUE(edit.IsWalAddition());
+ ASSERT_EQ(wals.size(), n);
+ for (int i = 0; i < n; i++) {
+ const WalAddition& wal = wals[i];
+ ASSERT_EQ(wal.GetLogNumber(), kLogNumbers[i]);
+ ASSERT_EQ(wal.GetMetadata().GetSyncedSizeInBytes(), kSizeInBytes[i]);
+ }
+
+ std::string expected_str = "VersionEdit {\n";
+ for (int i = 0; i < n; i++) {
+ std::stringstream ss;
+ ss << " WalAddition: log_number: " << kLogNumbers[i]
+ << " synced_size_in_bytes: " << kSizeInBytes[i] << "\n";
+ expected_str += ss.str();
+ }
+ expected_str += " ColumnFamily: 0\n}\n";
+ ASSERT_EQ(edit.DebugString(true), expected_str);
+
+ std::string expected_json = "{\"EditNumber\": 4, \"WalAdditions\": [";
+ for (int i = 0; i < n; i++) {
+ std::stringstream ss;
+ ss << "{\"LogNumber\": " << kLogNumbers[i] << ", "
+ << "\"SyncedSizeInBytes\": " << kSizeInBytes[i] << "}";
+ if (i < n - 1) ss << ", ";
+ expected_json += ss.str();
+ }
+ expected_json += "], \"ColumnFamily\": 0}";
+ ASSERT_EQ(edit.DebugJSON(4, true), expected_json);
+}
+
+TEST_F(VersionEditTest, DeleteWalEncodeDecode) {
+ VersionEdit edit;
+ edit.DeleteWalsBefore(rand() % 100);
+ TestEncodeDecode(edit);
+}
+
+TEST_F(VersionEditTest, DeleteWalDebug) {
+ constexpr int n = 2;
+ constexpr std::array<uint64_t, n> kLogNumbers{{10, 20}};
+
+ VersionEdit edit;
+ edit.DeleteWalsBefore(kLogNumbers[n - 1]);
+
+ const WalDeletion& wal = edit.GetWalDeletion();
+
+ ASSERT_TRUE(edit.IsWalDeletion());
+ ASSERT_EQ(wal.GetLogNumber(), kLogNumbers[n - 1]);
+
+ std::string expected_str = "VersionEdit {\n";
+ {
+ std::stringstream ss;
+ ss << " WalDeletion: log_number: " << kLogNumbers[n - 1] << "\n";
+ expected_str += ss.str();
+ }
+ expected_str += " ColumnFamily: 0\n}\n";
+ ASSERT_EQ(edit.DebugString(true), expected_str);
+
+ std::string expected_json = "{\"EditNumber\": 4, \"WalDeletion\": ";
+ {
+ std::stringstream ss;
+ ss << "{\"LogNumber\": " << kLogNumbers[n - 1] << "}";
+ expected_json += ss.str();
+ }
+ expected_json += ", \"ColumnFamily\": 0}";
+ ASSERT_EQ(edit.DebugJSON(4, true), expected_json);
+}
+
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {