You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 line
9.0 KiB

  1. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4. #include "db/log_reader.h"
  5. #include "db/log_writer.h"
  6. #include "leveldb/env.h"
  7. #include "util/coding.h"
  8. #include "util/crc32c.h"
  9. #include "util/random.h"
  10. #include "util/testharness.h"
  11. namespace leveldb {
  12. namespace log {
  13. // Construct a string of the specified length made out of the supplied
  14. // partial string.
  15. static std::string BigString(const std::string& partial_string, size_t n) {
  16. std::string result;
  17. while (result.size() < n) {
  18. result.append(partial_string);
  19. }
  20. result.resize(n);
  21. return result;
  22. }
  23. // Construct a string from a number
  24. static std::string NumberString(int n) {
  25. char buf[50];
  26. snprintf(buf, sizeof(buf), "%d.", n);
  27. return std::string(buf);
  28. }
  29. // Return a skewed potentially long string
  30. static std::string RandomSkewedString(int i, Random* rnd) {
  31. return BigString(NumberString(i), rnd->Skewed(17));
  32. }
  33. class LogTest {
  34. private:
  35. class StringDest : public WritableFile {
  36. public:
  37. std::string contents_;
  38. virtual Status Close() { return Status::OK(); }
  39. virtual Status Flush() { return Status::OK(); }
  40. virtual Status Sync() { return Status::OK(); }
  41. virtual Status Append(const Slice& slice) {
  42. contents_.append(slice.data(), slice.size());
  43. return Status::OK();
  44. }
  45. };
  46. class StringSource : public SequentialFile {
  47. public:
  48. Slice contents_;
  49. bool force_error_;
  50. bool returned_partial_;
  51. StringSource() : force_error_(false), returned_partial_(false) { }
  52. virtual Status Read(size_t n, Slice* result, char* scratch) {
  53. ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error";
  54. ASSERT_EQ(kBlockSize, n);
  55. if (force_error_) {
  56. force_error_ = false;
  57. returned_partial_ = true;
  58. return Status::Corruption("read error");
  59. }
  60. if (contents_.size() < n) {
  61. n = contents_.size();
  62. returned_partial_ = true;
  63. }
  64. *result = Slice(contents_.data(), n);
  65. contents_.remove_prefix(n);
  66. return Status::OK();
  67. }
  68. };
  69. class ReportCollector : public Reader::Reporter {
  70. public:
  71. size_t dropped_bytes_;
  72. std::string message_;
  73. ReportCollector() : dropped_bytes_(0) { }
  74. virtual void Corruption(size_t bytes, const Status& status) {
  75. dropped_bytes_ += bytes;
  76. message_.append(status.ToString());
  77. }
  78. };
  79. StringDest dest_;
  80. StringSource source_;
  81. ReportCollector report_;
  82. bool reading_;
  83. Writer writer_;
  84. Reader reader_;
  85. public:
  86. LogTest() : reading_(false),
  87. writer_(&dest_),
  88. reader_(&source_, &report_, true/*checksum*/) {
  89. }
  90. void Write(const std::string& msg) {
  91. ASSERT_TRUE(!reading_) << "Write() after starting to read";
  92. writer_.AddRecord(Slice(msg));
  93. }
  94. size_t WrittenBytes() const {
  95. return dest_.contents_.size();
  96. }
  97. std::string Read() {
  98. if (!reading_) {
  99. reading_ = true;
  100. source_.contents_ = Slice(dest_.contents_);
  101. }
  102. std::string scratch;
  103. Slice record;
  104. if (reader_.ReadRecord(&record, &scratch)) {
  105. return record.ToString();
  106. } else {
  107. return "EOF";
  108. }
  109. }
  110. void IncrementByte(int offset, int delta) {
  111. dest_.contents_[offset] += delta;
  112. }
  113. void SetByte(int offset, char new_byte) {
  114. dest_.contents_[offset] = new_byte;
  115. }
  116. void ShrinkSize(int bytes) {
  117. dest_.contents_.resize(dest_.contents_.size() - bytes);
  118. }
  119. void FixChecksum(int header_offset, int len) {
  120. // Compute crc of type/len/data
  121. uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len);
  122. crc = crc32c::Mask(crc);
  123. EncodeFixed32(&dest_.contents_[header_offset], crc);
  124. }
  125. void ForceError() {
  126. source_.force_error_ = true;
  127. }
  128. size_t DroppedBytes() const {
  129. return report_.dropped_bytes_;
  130. }
  131. // Returns OK iff recorded error message contains "msg"
  132. std::string MatchError(const std::string& msg) const {
  133. if (report_.message_.find(msg) == std::string::npos) {
  134. return report_.message_;
  135. } else {
  136. return "OK";
  137. }
  138. }
  139. };
  140. TEST(LogTest, Empty) {
  141. ASSERT_EQ("EOF", Read());
  142. }
  143. TEST(LogTest, ReadWrite) {
  144. Write("foo");
  145. Write("bar");
  146. Write("");
  147. Write("xxxx");
  148. ASSERT_EQ("foo", Read());
  149. ASSERT_EQ("bar", Read());
  150. ASSERT_EQ("", Read());
  151. ASSERT_EQ("xxxx", Read());
  152. ASSERT_EQ("EOF", Read());
  153. ASSERT_EQ("EOF", Read()); // Make sure reads at eof work
  154. }
  155. TEST(LogTest, ManyBlocks) {
  156. for (int i = 0; i < 100000; i++) {
  157. Write(NumberString(i));
  158. }
  159. for (int i = 0; i < 100000; i++) {
  160. ASSERT_EQ(NumberString(i), Read());
  161. }
  162. ASSERT_EQ("EOF", Read());
  163. }
  164. TEST(LogTest, Fragmentation) {
  165. Write("small");
  166. Write(BigString("medium", 50000));
  167. Write(BigString("large", 100000));
  168. ASSERT_EQ("small", Read());
  169. ASSERT_EQ(BigString("medium", 50000), Read());
  170. ASSERT_EQ(BigString("large", 100000), Read());
  171. ASSERT_EQ("EOF", Read());
  172. }
  173. TEST(LogTest, MarginalTrailer) {
  174. // Make a trailer that is exactly the same length as an empty record.
  175. const int n = kBlockSize - 2*kHeaderSize;
  176. Write(BigString("foo", n));
  177. ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes());
  178. Write("");
  179. Write("bar");
  180. ASSERT_EQ(BigString("foo", n), Read());
  181. ASSERT_EQ("", Read());
  182. ASSERT_EQ("bar", Read());
  183. ASSERT_EQ("EOF", Read());
  184. }
  185. TEST(LogTest, ShortTrailer) {
  186. const int n = kBlockSize - 2*kHeaderSize + 4;
  187. Write(BigString("foo", n));
  188. ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
  189. Write("");
  190. Write("bar");
  191. ASSERT_EQ(BigString("foo", n), Read());
  192. ASSERT_EQ("", Read());
  193. ASSERT_EQ("bar", Read());
  194. ASSERT_EQ("EOF", Read());
  195. }
  196. TEST(LogTest, AlignedEof) {
  197. const int n = kBlockSize - 2*kHeaderSize + 4;
  198. Write(BigString("foo", n));
  199. ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
  200. ASSERT_EQ(BigString("foo", n), Read());
  201. ASSERT_EQ("EOF", Read());
  202. }
  203. TEST(LogTest, RandomRead) {
  204. const int N = 500;
  205. Random write_rnd(301);
  206. for (int i = 0; i < N; i++) {
  207. Write(RandomSkewedString(i, &write_rnd));
  208. }
  209. Random read_rnd(301);
  210. for (int i = 0; i < N; i++) {
  211. ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read());
  212. }
  213. ASSERT_EQ("EOF", Read());
  214. }
  215. // Tests of all the error paths in log_reader.cc follow:
  216. TEST(LogTest, ReadError) {
  217. Write("foo");
  218. ForceError();
  219. ASSERT_EQ("EOF", Read());
  220. ASSERT_EQ(kBlockSize, DroppedBytes());
  221. ASSERT_EQ("OK", MatchError("read error"));
  222. }
  223. TEST(LogTest, BadRecordType) {
  224. Write("foo");
  225. // Type is stored in header[6]
  226. IncrementByte(6, 100);
  227. FixChecksum(0, 3);
  228. ASSERT_EQ("EOF", Read());
  229. ASSERT_EQ(3, DroppedBytes());
  230. ASSERT_EQ("OK", MatchError("unknown record type"));
  231. }
  232. TEST(LogTest, TruncatedTrailingRecord) {
  233. Write("foo");
  234. ShrinkSize(4); // Drop all payload as well as a header byte
  235. ASSERT_EQ("EOF", Read());
  236. ASSERT_EQ(kHeaderSize - 1, DroppedBytes());
  237. ASSERT_EQ("OK", MatchError("truncated record at end of file"));
  238. }
  239. TEST(LogTest, BadLength) {
  240. Write("foo");
  241. ShrinkSize(1);
  242. ASSERT_EQ("EOF", Read());
  243. ASSERT_EQ(kHeaderSize + 2, DroppedBytes());
  244. ASSERT_EQ("OK", MatchError("bad record length"));
  245. }
  246. TEST(LogTest, ChecksumMismatch) {
  247. Write("foo");
  248. IncrementByte(0, 10);
  249. ASSERT_EQ("EOF", Read());
  250. ASSERT_EQ(10, DroppedBytes());
  251. ASSERT_EQ("OK", MatchError("checksum mismatch"));
  252. }
  253. TEST(LogTest, UnexpectedMiddleType) {
  254. Write("foo");
  255. SetByte(6, kMiddleType);
  256. FixChecksum(0, 3);
  257. ASSERT_EQ("EOF", Read());
  258. ASSERT_EQ(3, DroppedBytes());
  259. ASSERT_EQ("OK", MatchError("missing start"));
  260. }
  261. TEST(LogTest, UnexpectedLastType) {
  262. Write("foo");
  263. SetByte(6, kLastType);
  264. FixChecksum(0, 3);
  265. ASSERT_EQ("EOF", Read());
  266. ASSERT_EQ(3, DroppedBytes());
  267. ASSERT_EQ("OK", MatchError("missing start"));
  268. }
  269. TEST(LogTest, UnexpectedFullType) {
  270. Write("foo");
  271. Write("bar");
  272. SetByte(6, kFirstType);
  273. FixChecksum(0, 3);
  274. ASSERT_EQ("bar", Read());
  275. ASSERT_EQ("EOF", Read());
  276. ASSERT_EQ(3, DroppedBytes());
  277. ASSERT_EQ("OK", MatchError("partial record without end"));
  278. }
  279. TEST(LogTest, UnexpectedFirstType) {
  280. Write("foo");
  281. Write(BigString("bar", 100000));
  282. SetByte(6, kFirstType);
  283. FixChecksum(0, 3);
  284. ASSERT_EQ(BigString("bar", 100000), Read());
  285. ASSERT_EQ("EOF", Read());
  286. ASSERT_EQ(3, DroppedBytes());
  287. ASSERT_EQ("OK", MatchError("partial record without end"));
  288. }
  289. TEST(LogTest, ErrorJoinsRecords) {
  290. // Consider two fragmented records:
  291. // first(R1) last(R1) first(R2) last(R2)
  292. // where the middle two fragments disappear. We do not want
  293. // first(R1),last(R2) to get joined and returned as a valid record.
  294. // Write records that span two blocks
  295. Write(BigString("foo", kBlockSize));
  296. Write(BigString("bar", kBlockSize));
  297. Write("correct");
  298. // Wipe the middle block
  299. for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) {
  300. SetByte(offset, 'x');
  301. }
  302. ASSERT_EQ("correct", Read());
  303. ASSERT_EQ("EOF", Read());
  304. const int dropped = DroppedBytes();
  305. ASSERT_LE(dropped, 2*kBlockSize + 100);
  306. ASSERT_GE(dropped, 2*kBlockSize);
  307. }
  308. }
  309. }
  310. int main(int argc, char** argv) {
  311. return leveldb::test::RunAllTests();
  312. }