]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
2 | // This source code is licensed under both the GPLv2 (found in the |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
7c673cae FG |
5 | // |
6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
7 | // Use of this source code is governed by a BSD-style license that can be | |
8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
9 | ||
10 | #pragma once | |
11 | #include <algorithm> | |
12 | #include <deque> | |
13 | #include <string> | |
14 | #include <vector> | |
15 | ||
f67539c2 TL |
16 | #include "env/composite_env_wrapper.h" |
17 | #include "file/writable_file_writer.h" | |
7c673cae FG |
18 | #include "rocksdb/compaction_filter.h" |
19 | #include "rocksdb/env.h" | |
20 | #include "rocksdb/iterator.h" | |
21 | #include "rocksdb/merge_operator.h" | |
22 | #include "rocksdb/options.h" | |
23 | #include "rocksdb/slice.h" | |
24 | #include "rocksdb/table.h" | |
7c673cae | 25 | #include "table/internal_iterator.h" |
7c673cae | 26 | #include "util/mutexlock.h" |
7c673cae | 27 | |
f67539c2 | 28 | namespace ROCKSDB_NAMESPACE { |
20effc67 | 29 | class Random; |
7c673cae FG |
30 | class SequentialFile; |
31 | class SequentialFileReader; | |
32 | ||
33 | namespace test { | |
34 | ||
11fdf7f2 TL |
35 | extern const uint32_t kDefaultFormatVersion; |
36 | extern const uint32_t kLatestFormatVersion; | |
37 | ||
7c673cae FG |
38 | // Return a random key with the specified length that may contain interesting |
39 | // characters (e.g. \x00, \xff, etc.). | |
40 | enum RandomKeyType : char { RANDOM, LARGEST, SMALLEST, MIDDLE }; | |
41 | extern std::string RandomKey(Random* rnd, int len, | |
42 | RandomKeyType type = RandomKeyType::RANDOM); | |
43 | ||
44 | // Store in *dst a string of length "len" that will compress to | |
45 | // "N*compressed_fraction" bytes and return a Slice that references | |
46 | // the generated data. | |
47 | extern Slice CompressibleString(Random* rnd, double compressed_fraction, | |
48 | int len, std::string* dst); | |
49 | ||
50 | // A wrapper that allows injection of errors. | |
51 | class ErrorEnv : public EnvWrapper { | |
52 | public: | |
53 | bool writable_file_error_; | |
54 | int num_writable_file_errors_; | |
55 | ||
56 | ErrorEnv() : EnvWrapper(Env::Default()), | |
57 | writable_file_error_(false), | |
58 | num_writable_file_errors_(0) { } | |
59 | ||
60 | virtual Status NewWritableFile(const std::string& fname, | |
494da23a | 61 | std::unique_ptr<WritableFile>* result, |
7c673cae FG |
62 | const EnvOptions& soptions) override { |
63 | result->reset(); | |
64 | if (writable_file_error_) { | |
65 | ++num_writable_file_errors_; | |
66 | return Status::IOError(fname, "fake error"); | |
67 | } | |
68 | return target()->NewWritableFile(fname, result, soptions); | |
69 | } | |
70 | }; | |
71 | ||
11fdf7f2 | 72 | #ifndef NDEBUG |
7c673cae FG |
73 | // An internal comparator that just forward comparing results from the |
74 | // user comparator in it. Can be used to test entities that have no dependency | |
75 | // on internal key structure but consumes InternalKeyComparator, like | |
76 | // BlockBasedTable. | |
77 | class PlainInternalKeyComparator : public InternalKeyComparator { | |
78 | public: | |
79 | explicit PlainInternalKeyComparator(const Comparator* c) | |
80 | : InternalKeyComparator(c) {} | |
81 | ||
82 | virtual ~PlainInternalKeyComparator() {} | |
83 | ||
84 | virtual int Compare(const Slice& a, const Slice& b) const override { | |
85 | return user_comparator()->Compare(a, b); | |
86 | } | |
7c673cae | 87 | }; |
11fdf7f2 | 88 | #endif |
7c673cae FG |
89 | |
90 | // A test comparator which compare two strings in this way: | |
91 | // (1) first compare prefix of 8 bytes in alphabet order, | |
92 | // (2) if two strings share the same prefix, sort the other part of the string | |
93 | // in the reverse alphabet order. | |
94 | // This helps simulate the case of compounded key of [entity][timestamp] and | |
95 | // latest timestamp first. | |
96 | class SimpleSuffixReverseComparator : public Comparator { | |
97 | public: | |
98 | SimpleSuffixReverseComparator() {} | |
99 | ||
100 | virtual const char* Name() const override { | |
101 | return "SimpleSuffixReverseComparator"; | |
102 | } | |
103 | ||
104 | virtual int Compare(const Slice& a, const Slice& b) const override { | |
105 | Slice prefix_a = Slice(a.data(), 8); | |
106 | Slice prefix_b = Slice(b.data(), 8); | |
107 | int prefix_comp = prefix_a.compare(prefix_b); | |
108 | if (prefix_comp != 0) { | |
109 | return prefix_comp; | |
110 | } else { | |
111 | Slice suffix_a = Slice(a.data() + 8, a.size() - 8); | |
112 | Slice suffix_b = Slice(b.data() + 8, b.size() - 8); | |
113 | return -(suffix_a.compare(suffix_b)); | |
114 | } | |
115 | } | |
11fdf7f2 TL |
116 | virtual void FindShortestSeparator(std::string* /*start*/, |
117 | const Slice& /*limit*/) const override {} | |
7c673cae | 118 | |
11fdf7f2 | 119 | virtual void FindShortSuccessor(std::string* /*key*/) const override {} |
7c673cae FG |
120 | }; |
121 | ||
122 | // Returns a user key comparator that can be used for comparing two uint64_t | |
123 | // slices. Instead of comparing slices byte-wise, it compares all the 8 bytes | |
124 | // at once. Assumes same endian-ness is used though the database's lifetime. | |
125 | // Symantics of comparison would differ from Bytewise comparator in little | |
126 | // endian machines. | |
127 | extern const Comparator* Uint64Comparator(); | |
128 | ||
129 | // Iterator over a vector of keys/values | |
130 | class VectorIterator : public InternalIterator { | |
131 | public: | |
132 | explicit VectorIterator(const std::vector<std::string>& keys) | |
133 | : keys_(keys), current_(keys.size()) { | |
134 | std::sort(keys_.begin(), keys_.end()); | |
135 | values_.resize(keys.size()); | |
136 | } | |
137 | ||
138 | VectorIterator(const std::vector<std::string>& keys, | |
139 | const std::vector<std::string>& values) | |
140 | : keys_(keys), values_(values), current_(keys.size()) { | |
141 | assert(keys_.size() == values_.size()); | |
142 | } | |
143 | ||
144 | virtual bool Valid() const override { return current_ < keys_.size(); } | |
145 | ||
146 | virtual void SeekToFirst() override { current_ = 0; } | |
147 | virtual void SeekToLast() override { current_ = keys_.size() - 1; } | |
148 | ||
149 | virtual void Seek(const Slice& target) override { | |
150 | current_ = std::lower_bound(keys_.begin(), keys_.end(), target.ToString()) - | |
151 | keys_.begin(); | |
152 | } | |
153 | ||
154 | virtual void SeekForPrev(const Slice& target) override { | |
155 | current_ = std::upper_bound(keys_.begin(), keys_.end(), target.ToString()) - | |
156 | keys_.begin(); | |
157 | if (!Valid()) { | |
158 | SeekToLast(); | |
159 | } else { | |
160 | Prev(); | |
161 | } | |
162 | } | |
163 | ||
164 | virtual void Next() override { current_++; } | |
165 | virtual void Prev() override { current_--; } | |
166 | ||
167 | virtual Slice key() const override { return Slice(keys_[current_]); } | |
168 | virtual Slice value() const override { return Slice(values_[current_]); } | |
169 | ||
170 | virtual Status status() const override { return Status::OK(); } | |
171 | ||
11fdf7f2 TL |
172 | virtual bool IsKeyPinned() const override { return true; } |
173 | virtual bool IsValuePinned() const override { return true; } | |
174 | ||
7c673cae FG |
175 | private: |
176 | std::vector<std::string> keys_; | |
177 | std::vector<std::string> values_; | |
178 | size_t current_; | |
179 | }; | |
11fdf7f2 TL |
180 | extern WritableFileWriter* GetWritableFileWriter(WritableFile* wf, |
181 | const std::string& fname); | |
7c673cae FG |
182 | |
183 | extern RandomAccessFileReader* GetRandomAccessFileReader(RandomAccessFile* raf); | |
184 | ||
11fdf7f2 TL |
185 | extern SequentialFileReader* GetSequentialFileReader(SequentialFile* se, |
186 | const std::string& fname); | |
7c673cae FG |
187 | |
188 | class StringSink: public WritableFile { | |
189 | public: | |
190 | std::string contents_; | |
191 | ||
192 | explicit StringSink(Slice* reader_contents = nullptr) : | |
193 | WritableFile(), | |
194 | contents_(""), | |
195 | reader_contents_(reader_contents), | |
196 | last_flush_(0) { | |
197 | if (reader_contents_ != nullptr) { | |
198 | *reader_contents_ = Slice(contents_.data(), 0); | |
199 | } | |
200 | } | |
201 | ||
202 | const std::string& contents() const { return contents_; } | |
203 | ||
204 | virtual Status Truncate(uint64_t size) override { | |
205 | contents_.resize(static_cast<size_t>(size)); | |
206 | return Status::OK(); | |
207 | } | |
208 | virtual Status Close() override { return Status::OK(); } | |
209 | virtual Status Flush() override { | |
210 | if (reader_contents_ != nullptr) { | |
211 | assert(reader_contents_->size() <= last_flush_); | |
212 | size_t offset = last_flush_ - reader_contents_->size(); | |
213 | *reader_contents_ = Slice( | |
214 | contents_.data() + offset, | |
215 | contents_.size() - offset); | |
216 | last_flush_ = contents_.size(); | |
217 | } | |
218 | ||
219 | return Status::OK(); | |
220 | } | |
221 | virtual Status Sync() override { return Status::OK(); } | |
222 | virtual Status Append(const Slice& slice) override { | |
223 | contents_.append(slice.data(), slice.size()); | |
224 | return Status::OK(); | |
225 | } | |
226 | void Drop(size_t bytes) { | |
227 | if (reader_contents_ != nullptr) { | |
228 | contents_.resize(contents_.size() - bytes); | |
229 | *reader_contents_ = Slice( | |
230 | reader_contents_->data(), reader_contents_->size() - bytes); | |
231 | last_flush_ = contents_.size(); | |
232 | } | |
233 | } | |
234 | ||
235 | private: | |
236 | Slice* reader_contents_; | |
237 | size_t last_flush_; | |
238 | }; | |
239 | ||
240 | // A wrapper around a StringSink to give it a RandomRWFile interface | |
241 | class RandomRWStringSink : public RandomRWFile { | |
242 | public: | |
243 | explicit RandomRWStringSink(StringSink* ss) : ss_(ss) {} | |
244 | ||
11fdf7f2 | 245 | Status Write(uint64_t offset, const Slice& data) override { |
7c673cae | 246 | if (offset + data.size() > ss_->contents_.size()) { |
11fdf7f2 | 247 | ss_->contents_.resize(static_cast<size_t>(offset) + data.size(), '\0'); |
7c673cae FG |
248 | } |
249 | ||
250 | char* pos = const_cast<char*>(ss_->contents_.data() + offset); | |
251 | memcpy(pos, data.data(), data.size()); | |
252 | return Status::OK(); | |
253 | } | |
254 | ||
11fdf7f2 TL |
255 | Status Read(uint64_t offset, size_t n, Slice* result, |
256 | char* /*scratch*/) const override { | |
7c673cae FG |
257 | *result = Slice(nullptr, 0); |
258 | if (offset < ss_->contents_.size()) { | |
259 | size_t str_res_sz = | |
260 | std::min(static_cast<size_t>(ss_->contents_.size() - offset), n); | |
261 | *result = Slice(ss_->contents_.data() + offset, str_res_sz); | |
262 | } | |
263 | return Status::OK(); | |
264 | } | |
265 | ||
11fdf7f2 | 266 | Status Flush() override { return Status::OK(); } |
7c673cae | 267 | |
11fdf7f2 | 268 | Status Sync() override { return Status::OK(); } |
7c673cae | 269 | |
11fdf7f2 | 270 | Status Close() override { return Status::OK(); } |
7c673cae FG |
271 | |
272 | const std::string& contents() const { return ss_->contents(); } | |
273 | ||
274 | private: | |
275 | StringSink* ss_; | |
276 | }; | |
277 | ||
278 | // Like StringSink, this writes into a string. Unlink StringSink, it | |
279 | // has some initial content and overwrites it, just like a recycled | |
280 | // log file. | |
281 | class OverwritingStringSink : public WritableFile { | |
282 | public: | |
283 | explicit OverwritingStringSink(Slice* reader_contents) | |
284 | : WritableFile(), | |
285 | contents_(""), | |
286 | reader_contents_(reader_contents), | |
287 | last_flush_(0) {} | |
288 | ||
289 | const std::string& contents() const { return contents_; } | |
290 | ||
291 | virtual Status Truncate(uint64_t size) override { | |
292 | contents_.resize(static_cast<size_t>(size)); | |
293 | return Status::OK(); | |
294 | } | |
295 | virtual Status Close() override { return Status::OK(); } | |
296 | virtual Status Flush() override { | |
297 | if (last_flush_ < contents_.size()) { | |
298 | assert(reader_contents_->size() >= contents_.size()); | |
299 | memcpy((char*)reader_contents_->data() + last_flush_, | |
300 | contents_.data() + last_flush_, contents_.size() - last_flush_); | |
301 | last_flush_ = contents_.size(); | |
302 | } | |
303 | return Status::OK(); | |
304 | } | |
305 | virtual Status Sync() override { return Status::OK(); } | |
306 | virtual Status Append(const Slice& slice) override { | |
307 | contents_.append(slice.data(), slice.size()); | |
308 | return Status::OK(); | |
309 | } | |
310 | void Drop(size_t bytes) { | |
311 | contents_.resize(contents_.size() - bytes); | |
312 | if (last_flush_ > contents_.size()) last_flush_ = contents_.size(); | |
313 | } | |
314 | ||
315 | private: | |
316 | std::string contents_; | |
317 | Slice* reader_contents_; | |
318 | size_t last_flush_; | |
319 | }; | |
320 | ||
321 | class StringSource: public RandomAccessFile { | |
322 | public: | |
323 | explicit StringSource(const Slice& contents, uint64_t uniq_id = 0, | |
324 | bool mmap = false) | |
325 | : contents_(contents.data(), contents.size()), | |
326 | uniq_id_(uniq_id), | |
327 | mmap_(mmap), | |
328 | total_reads_(0) {} | |
329 | ||
330 | virtual ~StringSource() { } | |
331 | ||
332 | uint64_t Size() const { return contents_.size(); } | |
333 | ||
334 | virtual Status Read(uint64_t offset, size_t n, Slice* result, | |
335 | char* scratch) const override { | |
336 | total_reads_++; | |
337 | if (offset > contents_.size()) { | |
338 | return Status::InvalidArgument("invalid Read offset"); | |
339 | } | |
340 | if (offset + n > contents_.size()) { | |
341 | n = contents_.size() - static_cast<size_t>(offset); | |
342 | } | |
343 | if (!mmap_) { | |
344 | memcpy(scratch, &contents_[static_cast<size_t>(offset)], n); | |
345 | *result = Slice(scratch, n); | |
346 | } else { | |
347 | *result = Slice(&contents_[static_cast<size_t>(offset)], n); | |
348 | } | |
349 | return Status::OK(); | |
350 | } | |
351 | ||
352 | virtual size_t GetUniqueId(char* id, size_t max_size) const override { | |
353 | if (max_size < 20) { | |
354 | return 0; | |
355 | } | |
356 | ||
357 | char* rid = id; | |
358 | rid = EncodeVarint64(rid, uniq_id_); | |
359 | rid = EncodeVarint64(rid, 0); | |
360 | return static_cast<size_t>(rid-id); | |
361 | } | |
362 | ||
363 | int total_reads() const { return total_reads_; } | |
364 | ||
365 | void set_total_reads(int tr) { total_reads_ = tr; } | |
366 | ||
367 | private: | |
368 | std::string contents_; | |
369 | uint64_t uniq_id_; | |
370 | bool mmap_; | |
371 | mutable int total_reads_; | |
372 | }; | |
373 | ||
f67539c2 TL |
374 | inline StringSink* GetStringSinkFromLegacyWriter( |
375 | const WritableFileWriter* writer) { | |
376 | LegacyWritableFileWrapper* file = | |
377 | static_cast<LegacyWritableFileWrapper*>(writer->writable_file()); | |
378 | return static_cast<StringSink*>(file->target()); | |
379 | } | |
380 | ||
7c673cae FG |
381 | class NullLogger : public Logger { |
382 | public: | |
383 | using Logger::Logv; | |
11fdf7f2 | 384 | virtual void Logv(const char* /*format*/, va_list /*ap*/) override {} |
7c673cae FG |
385 | virtual size_t GetLogFileSize() const override { return 0; } |
386 | }; | |
387 | ||
388 | // Corrupts key by changing the type | |
389 | extern void CorruptKeyType(InternalKey* ikey); | |
390 | ||
391 | extern std::string KeyStr(const std::string& user_key, | |
392 | const SequenceNumber& seq, const ValueType& t, | |
393 | bool corrupt = false); | |
394 | ||
20effc67 TL |
395 | extern std::string KeyStr(uint64_t ts, const std::string& user_key, |
396 | const SequenceNumber& seq, const ValueType& t, | |
397 | bool corrupt = false); | |
398 | ||
7c673cae FG |
399 | class SleepingBackgroundTask { |
400 | public: | |
401 | SleepingBackgroundTask() | |
402 | : bg_cv_(&mutex_), | |
403 | should_sleep_(true), | |
404 | done_with_sleep_(false), | |
405 | sleeping_(false) {} | |
406 | ||
407 | bool IsSleeping() { | |
408 | MutexLock l(&mutex_); | |
409 | return sleeping_; | |
410 | } | |
411 | void DoSleep() { | |
412 | MutexLock l(&mutex_); | |
413 | sleeping_ = true; | |
414 | bg_cv_.SignalAll(); | |
415 | while (should_sleep_) { | |
416 | bg_cv_.Wait(); | |
417 | } | |
418 | sleeping_ = false; | |
419 | done_with_sleep_ = true; | |
420 | bg_cv_.SignalAll(); | |
421 | } | |
422 | void WaitUntilSleeping() { | |
423 | MutexLock l(&mutex_); | |
424 | while (!sleeping_ || !should_sleep_) { | |
425 | bg_cv_.Wait(); | |
426 | } | |
427 | } | |
f67539c2 TL |
428 | // Waits for the status to change to sleeping, |
429 | // otherwise times out. | |
430 | // wait_time is in microseconds. | |
431 | // Returns true when times out, false otherwise. | |
432 | bool TimedWaitUntilSleeping(uint64_t wait_time) { | |
433 | auto abs_time = Env::Default()->NowMicros() + wait_time; | |
434 | MutexLock l(&mutex_); | |
435 | while (!sleeping_ || !should_sleep_) { | |
436 | if (bg_cv_.TimedWait(abs_time)) { | |
437 | return true; | |
438 | } | |
439 | } | |
440 | return false; | |
441 | } | |
7c673cae FG |
442 | void WakeUp() { |
443 | MutexLock l(&mutex_); | |
444 | should_sleep_ = false; | |
445 | bg_cv_.SignalAll(); | |
446 | } | |
447 | void WaitUntilDone() { | |
448 | MutexLock l(&mutex_); | |
449 | while (!done_with_sleep_) { | |
450 | bg_cv_.Wait(); | |
451 | } | |
452 | } | |
f67539c2 TL |
453 | // Similar to TimedWaitUntilSleeping. |
454 | // Waits until the task is done. | |
455 | bool TimedWaitUntilDone(uint64_t wait_time) { | |
456 | auto abs_time = Env::Default()->NowMicros() + wait_time; | |
457 | MutexLock l(&mutex_); | |
458 | while (!done_with_sleep_) { | |
459 | if (bg_cv_.TimedWait(abs_time)) { | |
460 | return true; | |
461 | } | |
462 | } | |
463 | return false; | |
464 | } | |
7c673cae FG |
465 | bool WokenUp() { |
466 | MutexLock l(&mutex_); | |
467 | return should_sleep_ == false; | |
468 | } | |
469 | ||
470 | void Reset() { | |
471 | MutexLock l(&mutex_); | |
472 | should_sleep_ = true; | |
473 | done_with_sleep_ = false; | |
474 | } | |
475 | ||
476 | static void DoSleepTask(void* arg) { | |
477 | reinterpret_cast<SleepingBackgroundTask*>(arg)->DoSleep(); | |
478 | } | |
479 | ||
480 | private: | |
481 | port::Mutex mutex_; | |
482 | port::CondVar bg_cv_; // Signalled when background work finishes | |
483 | bool should_sleep_; | |
484 | bool done_with_sleep_; | |
485 | bool sleeping_; | |
486 | }; | |
487 | ||
488 | // Filters merge operands and values that are equal to `num`. | |
489 | class FilterNumber : public CompactionFilter { | |
490 | public: | |
491 | explicit FilterNumber(uint64_t num) : num_(num) {} | |
492 | ||
493 | std::string last_merge_operand_key() { return last_merge_operand_key_; } | |
494 | ||
f67539c2 TL |
495 | bool Filter(int /*level*/, const ROCKSDB_NAMESPACE::Slice& /*key*/, |
496 | const ROCKSDB_NAMESPACE::Slice& value, std::string* /*new_value*/, | |
11fdf7f2 | 497 | bool* /*value_changed*/) const override { |
7c673cae FG |
498 | if (value.size() == sizeof(uint64_t)) { |
499 | return num_ == DecodeFixed64(value.data()); | |
500 | } | |
501 | return true; | |
502 | } | |
503 | ||
f67539c2 TL |
504 | bool FilterMergeOperand( |
505 | int /*level*/, const ROCKSDB_NAMESPACE::Slice& key, | |
506 | const ROCKSDB_NAMESPACE::Slice& value) const override { | |
7c673cae FG |
507 | last_merge_operand_key_ = key.ToString(); |
508 | if (value.size() == sizeof(uint64_t)) { | |
509 | return num_ == DecodeFixed64(value.data()); | |
510 | } | |
511 | return true; | |
512 | } | |
513 | ||
514 | const char* Name() const override { return "FilterBadMergeOperand"; } | |
515 | ||
516 | private: | |
517 | mutable std::string last_merge_operand_key_; | |
518 | uint64_t num_; | |
519 | }; | |
520 | ||
521 | inline std::string EncodeInt(uint64_t x) { | |
522 | std::string result; | |
523 | PutFixed64(&result, x); | |
524 | return result; | |
525 | } | |
526 | ||
7c673cae FG |
527 | class SeqStringSource : public SequentialFile { |
528 | public: | |
f67539c2 TL |
529 | SeqStringSource(const std::string& data, std::atomic<int>* read_count) |
530 | : data_(data), offset_(0), read_count_(read_count) {} | |
531 | ~SeqStringSource() override {} | |
7c673cae FG |
532 | Status Read(size_t n, Slice* result, char* scratch) override { |
533 | std::string output; | |
534 | if (offset_ < data_.size()) { | |
535 | n = std::min(data_.size() - offset_, n); | |
536 | memcpy(scratch, data_.data() + offset_, n); | |
537 | offset_ += n; | |
538 | *result = Slice(scratch, n); | |
539 | } else { | |
540 | return Status::InvalidArgument( | |
541 | "Attemp to read when it already reached eof."); | |
542 | } | |
f67539c2 | 543 | (*read_count_)++; |
7c673cae FG |
544 | return Status::OK(); |
545 | } | |
546 | Status Skip(uint64_t n) override { | |
547 | if (offset_ >= data_.size()) { | |
548 | return Status::InvalidArgument( | |
549 | "Attemp to read when it already reached eof."); | |
550 | } | |
551 | // TODO(yhchiang): Currently doesn't handle the overflow case. | |
11fdf7f2 | 552 | offset_ += static_cast<size_t>(n); |
7c673cae FG |
553 | return Status::OK(); |
554 | } | |
555 | ||
556 | private: | |
557 | std::string data_; | |
558 | size_t offset_; | |
f67539c2 | 559 | std::atomic<int>* read_count_; |
7c673cae FG |
560 | }; |
561 | ||
f67539c2 | 562 | class StringEnv : public EnvWrapper { |
7c673cae | 563 | public: |
f67539c2 TL |
564 | class StringSink : public WritableFile { |
565 | public: | |
566 | explicit StringSink(std::string* contents) | |
567 | : WritableFile(), contents_(contents) {} | |
568 | virtual Status Truncate(uint64_t size) override { | |
569 | contents_->resize(static_cast<size_t>(size)); | |
570 | return Status::OK(); | |
571 | } | |
572 | virtual Status Close() override { return Status::OK(); } | |
573 | virtual Status Flush() override { return Status::OK(); } | |
574 | virtual Status Sync() override { return Status::OK(); } | |
575 | virtual Status Append(const Slice& slice) override { | |
576 | contents_->append(slice.data(), slice.size()); | |
577 | return Status::OK(); | |
578 | } | |
7c673cae | 579 | |
f67539c2 TL |
580 | private: |
581 | std::string* contents_; | |
582 | }; | |
7c673cae | 583 | |
f67539c2 TL |
584 | explicit StringEnv(Env* t) : EnvWrapper(t) {} |
585 | ~StringEnv() override {} | |
7c673cae | 586 | |
f67539c2 | 587 | const std::string& GetContent(const std::string& f) { return files_[f]; } |
7c673cae | 588 | |
f67539c2 TL |
589 | const Status WriteToNewFile(const std::string& file_name, |
590 | const std::string& content) { | |
591 | std::unique_ptr<WritableFile> r; | |
592 | auto s = NewWritableFile(file_name, &r, EnvOptions()); | |
20effc67 TL |
593 | if (s.ok()) { |
594 | s = r->Append(content); | |
f67539c2 | 595 | } |
20effc67 TL |
596 | if (s.ok()) { |
597 | s = r->Flush(); | |
598 | } | |
599 | if (s.ok()) { | |
600 | s = r->Close(); | |
601 | } | |
602 | assert(!s.ok() || files_[file_name] == content); | |
603 | return s; | |
7c673cae | 604 | } |
7c673cae | 605 | |
f67539c2 TL |
606 | // The following text is boilerplate that forwards all methods to target() |
607 | Status NewSequentialFile(const std::string& f, | |
608 | std::unique_ptr<SequentialFile>* r, | |
609 | const EnvOptions& /*options*/) override { | |
610 | auto iter = files_.find(f); | |
611 | if (iter == files_.end()) { | |
612 | return Status::NotFound("The specified file does not exist", f); | |
613 | } | |
614 | r->reset(new SeqStringSource(iter->second, &num_seq_file_read_)); | |
615 | return Status::OK(); | |
616 | } | |
617 | Status NewRandomAccessFile(const std::string& /*f*/, | |
618 | std::unique_ptr<RandomAccessFile>* /*r*/, | |
619 | const EnvOptions& /*options*/) override { | |
620 | return Status::NotSupported(); | |
621 | } | |
622 | Status NewWritableFile(const std::string& f, | |
623 | std::unique_ptr<WritableFile>* r, | |
11fdf7f2 | 624 | const EnvOptions& /*options*/) override { |
f67539c2 TL |
625 | auto iter = files_.find(f); |
626 | if (iter != files_.end()) { | |
627 | return Status::IOError("The specified file already exists", f); | |
628 | } | |
629 | r->reset(new StringSink(&files_[f])); | |
630 | return Status::OK(); | |
7c673cae | 631 | } |
f67539c2 TL |
632 | virtual Status NewDirectory( |
633 | const std::string& /*name*/, | |
634 | std::unique_ptr<Directory>* /*result*/) override { | |
635 | return Status::NotSupported(); | |
7c673cae | 636 | } |
f67539c2 TL |
637 | Status FileExists(const std::string& f) override { |
638 | if (files_.find(f) == files_.end()) { | |
639 | return Status::NotFound(); | |
640 | } | |
641 | return Status::OK(); | |
7c673cae | 642 | } |
f67539c2 TL |
643 | Status GetChildren(const std::string& /*dir*/, |
644 | std::vector<std::string>* /*r*/) override { | |
645 | return Status::NotSupported(); | |
646 | } | |
647 | Status DeleteFile(const std::string& f) override { | |
648 | files_.erase(f); | |
649 | return Status::OK(); | |
650 | } | |
651 | Status CreateDir(const std::string& /*d*/) override { | |
652 | return Status::NotSupported(); | |
653 | } | |
654 | Status CreateDirIfMissing(const std::string& /*d*/) override { | |
655 | return Status::NotSupported(); | |
656 | } | |
657 | Status DeleteDir(const std::string& /*d*/) override { | |
658 | return Status::NotSupported(); | |
659 | } | |
660 | Status GetFileSize(const std::string& f, uint64_t* s) override { | |
661 | auto iter = files_.find(f); | |
662 | if (iter == files_.end()) { | |
663 | return Status::NotFound("The specified file does not exist:", f); | |
664 | } | |
665 | *s = iter->second.size(); | |
666 | return Status::OK(); | |
7c673cae | 667 | } |
7c673cae | 668 | |
f67539c2 TL |
669 | Status GetFileModificationTime(const std::string& /*fname*/, |
670 | uint64_t* /*file_mtime*/) override { | |
671 | return Status::NotSupported(); | |
672 | } | |
673 | ||
674 | Status RenameFile(const std::string& /*s*/, | |
675 | const std::string& /*t*/) override { | |
676 | return Status::NotSupported(); | |
677 | } | |
7c673cae | 678 | |
f67539c2 | 679 | Status LinkFile(const std::string& /*s*/, |
11fdf7f2 | 680 | const std::string& /*t*/) override { |
f67539c2 TL |
681 | return Status::NotSupported(); |
682 | } | |
7c673cae | 683 | |
f67539c2 TL |
684 | Status LockFile(const std::string& /*f*/, FileLock** /*l*/) override { |
685 | return Status::NotSupported(); | |
686 | } | |
7c673cae | 687 | |
f67539c2 TL |
688 | Status UnlockFile(FileLock* /*l*/) override { |
689 | return Status::NotSupported(); | |
690 | } | |
7c673cae | 691 | |
f67539c2 | 692 | std::atomic<int> num_seq_file_read_; |
7c673cae | 693 | |
f67539c2 TL |
694 | protected: |
695 | std::unordered_map<std::string, std::string> files_; | |
696 | }; | |
7c673cae FG |
697 | |
698 | // Randomly initialize the given DBOptions | |
699 | void RandomInitDBOptions(DBOptions* db_opt, Random* rnd); | |
700 | ||
701 | // Randomly initialize the given ColumnFamilyOptions | |
702 | // Note that the caller is responsible for releasing non-null | |
703 | // cf_opt->compaction_filter. | |
f67539c2 | 704 | void RandomInitCFOptions(ColumnFamilyOptions* cf_opt, DBOptions&, Random* rnd); |
7c673cae FG |
705 | |
706 | // A dummy merge operator which can change its name | |
707 | class ChanglingMergeOperator : public MergeOperator { | |
708 | public: | |
709 | explicit ChanglingMergeOperator(const std::string& name) | |
710 | : name_(name + "MergeOperator") {} | |
711 | ~ChanglingMergeOperator() {} | |
712 | ||
713 | void SetName(const std::string& name) { name_ = name; } | |
714 | ||
11fdf7f2 TL |
715 | virtual bool FullMergeV2(const MergeOperationInput& /*merge_in*/, |
716 | MergeOperationOutput* /*merge_out*/) const override { | |
7c673cae FG |
717 | return false; |
718 | } | |
11fdf7f2 TL |
719 | virtual bool PartialMergeMulti(const Slice& /*key*/, |
720 | const std::deque<Slice>& /*operand_list*/, | |
721 | std::string* /*new_value*/, | |
722 | Logger* /*logger*/) const override { | |
7c673cae FG |
723 | return false; |
724 | } | |
725 | virtual const char* Name() const override { return name_.c_str(); } | |
726 | ||
727 | protected: | |
728 | std::string name_; | |
729 | }; | |
730 | ||
731 | // Returns a dummy merge operator with random name. | |
732 | MergeOperator* RandomMergeOperator(Random* rnd); | |
733 | ||
734 | // A dummy compaction filter which can change its name | |
735 | class ChanglingCompactionFilter : public CompactionFilter { | |
736 | public: | |
737 | explicit ChanglingCompactionFilter(const std::string& name) | |
738 | : name_(name + "CompactionFilter") {} | |
739 | ~ChanglingCompactionFilter() {} | |
740 | ||
741 | void SetName(const std::string& name) { name_ = name; } | |
742 | ||
11fdf7f2 TL |
743 | bool Filter(int /*level*/, const Slice& /*key*/, |
744 | const Slice& /*existing_value*/, std::string* /*new_value*/, | |
745 | bool* /*value_changed*/) const override { | |
7c673cae FG |
746 | return false; |
747 | } | |
748 | ||
749 | const char* Name() const override { return name_.c_str(); } | |
750 | ||
751 | private: | |
752 | std::string name_; | |
753 | }; | |
754 | ||
755 | // Returns a dummy compaction filter with a random name. | |
756 | CompactionFilter* RandomCompactionFilter(Random* rnd); | |
757 | ||
758 | // A dummy compaction filter factory which can change its name | |
759 | class ChanglingCompactionFilterFactory : public CompactionFilterFactory { | |
760 | public: | |
761 | explicit ChanglingCompactionFilterFactory(const std::string& name) | |
762 | : name_(name + "CompactionFilterFactory") {} | |
763 | ~ChanglingCompactionFilterFactory() {} | |
764 | ||
765 | void SetName(const std::string& name) { name_ = name; } | |
766 | ||
767 | std::unique_ptr<CompactionFilter> CreateCompactionFilter( | |
11fdf7f2 | 768 | const CompactionFilter::Context& /*context*/) override { |
7c673cae FG |
769 | return std::unique_ptr<CompactionFilter>(); |
770 | } | |
771 | ||
772 | // Returns a name that identifies this compaction filter factory. | |
773 | const char* Name() const override { return name_.c_str(); } | |
774 | ||
775 | protected: | |
776 | std::string name_; | |
777 | }; | |
778 | ||
20effc67 TL |
779 | extern const Comparator* ComparatorWithU64Ts(); |
780 | ||
7c673cae FG |
781 | CompressionType RandomCompressionType(Random* rnd); |
782 | ||
783 | void RandomCompressionTypeVector(const size_t count, | |
784 | std::vector<CompressionType>* types, | |
785 | Random* rnd); | |
786 | ||
787 | CompactionFilterFactory* RandomCompactionFilterFactory(Random* rnd); | |
788 | ||
789 | const SliceTransform* RandomSliceTransform(Random* rnd, int pre_defined = -1); | |
790 | ||
791 | TableFactory* RandomTableFactory(Random* rnd, int pre_defined = -1); | |
792 | ||
793 | std::string RandomName(Random* rnd, const size_t len); | |
794 | ||
494da23a TL |
795 | bool IsDirectIOSupported(Env* env, const std::string& dir); |
796 | ||
f67539c2 TL |
797 | // Return the number of lines where a given pattern was found in a file. |
798 | size_t GetLinesCount(const std::string& fname, const std::string& pattern); | |
799 | ||
20effc67 TL |
800 | // TEST_TMPDIR may be set to /dev/shm in Makefile, |
801 | // but /dev/shm does not support direct IO. | |
802 | // Tries to set TEST_TMPDIR to a directory supporting direct IO. | |
803 | void ResetTmpDirForDirectIO(); | |
804 | ||
805 | Status CorruptFile(Env* env, const std::string& fname, int offset, | |
806 | int bytes_to_corrupt, bool verify_checksum = true); | |
807 | Status TruncateFile(Env* env, const std::string& fname, uint64_t length); | |
808 | ||
7c673cae | 809 | } // namespace test |
f67539c2 | 810 | } // namespace ROCKSDB_NAMESPACE |