1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <sys/stat.h>
16
17 #include <cstdio>
18 #include <iostream>
19 #include <memory>
20 #include <string_view>
21
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <gtest/gtest.h>
25 #include <libsnapshot/cow_reader.h>
26 #include <libsnapshot/cow_writer.h>
27
28 using testing::AssertionFailure;
29 using testing::AssertionResult;
30 using testing::AssertionSuccess;
31
32 namespace android {
33 namespace snapshot {
34
35 class CowTest : public ::testing::Test {
36 protected:
SetUp()37 virtual void SetUp() override {
38 cow_ = std::make_unique<TemporaryFile>();
39 ASSERT_GE(cow_->fd, 0) << strerror(errno);
40 }
41
TearDown()42 virtual void TearDown() override { cow_ = nullptr; }
43
44 std::unique_ptr<TemporaryFile> cow_;
45 };
46
47 // Sink that always appends to the end of a string.
48 class StringSink : public IByteSink {
49 public:
GetBuffer(size_t requested,size_t * actual)50 void* GetBuffer(size_t requested, size_t* actual) override {
51 size_t old_size = stream_.size();
52 stream_.resize(old_size + requested, '\0');
53 *actual = requested;
54 return stream_.data() + old_size;
55 }
ReturnData(void *,size_t)56 bool ReturnData(void*, size_t) override { return true; }
Reset()57 void Reset() { stream_.clear(); }
58
stream()59 std::string& stream() { return stream_; }
60
61 private:
62 std::string stream_;
63 };
64
TEST_F(CowTest,ReadWrite)65 TEST_F(CowTest, ReadWrite) {
66 CowOptions options;
67 options.cluster_ops = 0;
68 CowWriter writer(options);
69
70 ASSERT_TRUE(writer.Initialize(cow_->fd));
71
72 std::string data = "This is some data, believe it";
73 data.resize(options.block_size, '\0');
74
75 ASSERT_TRUE(writer.AddCopy(10, 20));
76 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
77 ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
78 ASSERT_TRUE(writer.Finalize());
79
80 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
81
82 CowReader reader;
83 CowHeader header;
84 CowFooter footer;
85 ASSERT_TRUE(reader.Parse(cow_->fd));
86 ASSERT_TRUE(reader.GetHeader(&header));
87 ASSERT_TRUE(reader.GetFooter(&footer));
88 ASSERT_EQ(header.magic, kCowMagicNumber);
89 ASSERT_EQ(header.major_version, kCowVersionMajor);
90 ASSERT_EQ(header.minor_version, kCowVersionMinor);
91 ASSERT_EQ(header.block_size, options.block_size);
92 ASSERT_EQ(footer.op.num_ops, 4);
93
94 auto iter = reader.GetOpIter();
95 ASSERT_NE(iter, nullptr);
96 ASSERT_FALSE(iter->Done());
97 auto op = &iter->Get();
98
99 ASSERT_EQ(op->type, kCowCopyOp);
100 ASSERT_EQ(op->compression, kCowCompressNone);
101 ASSERT_EQ(op->data_length, 0);
102 ASSERT_EQ(op->new_block, 10);
103 ASSERT_EQ(op->source, 20);
104
105 StringSink sink;
106
107 iter->Next();
108 ASSERT_FALSE(iter->Done());
109 op = &iter->Get();
110
111 ASSERT_EQ(op->type, kCowReplaceOp);
112 ASSERT_EQ(op->compression, kCowCompressNone);
113 ASSERT_EQ(op->data_length, 4096);
114 ASSERT_EQ(op->new_block, 50);
115 ASSERT_TRUE(reader.ReadData(*op, &sink));
116 ASSERT_EQ(sink.stream(), data);
117
118 iter->Next();
119 ASSERT_FALSE(iter->Done());
120 op = &iter->Get();
121
122 // Note: the zero operation gets split into two blocks.
123 ASSERT_EQ(op->type, kCowZeroOp);
124 ASSERT_EQ(op->compression, kCowCompressNone);
125 ASSERT_EQ(op->data_length, 0);
126 ASSERT_EQ(op->new_block, 51);
127 ASSERT_EQ(op->source, 0);
128
129 iter->Next();
130 ASSERT_FALSE(iter->Done());
131 op = &iter->Get();
132
133 ASSERT_EQ(op->type, kCowZeroOp);
134 ASSERT_EQ(op->compression, kCowCompressNone);
135 ASSERT_EQ(op->data_length, 0);
136 ASSERT_EQ(op->new_block, 52);
137 ASSERT_EQ(op->source, 0);
138
139 iter->Next();
140 ASSERT_TRUE(iter->Done());
141 }
142
TEST_F(CowTest,ReadWriteXor)143 TEST_F(CowTest, ReadWriteXor) {
144 CowOptions options;
145 options.cluster_ops = 0;
146 CowWriter writer(options);
147
148 ASSERT_TRUE(writer.Initialize(cow_->fd));
149
150 std::string data = "This is some data, believe it";
151 data.resize(options.block_size, '\0');
152
153 ASSERT_TRUE(writer.AddCopy(10, 20));
154 ASSERT_TRUE(writer.AddXorBlocks(50, data.data(), data.size(), 24, 10));
155 ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
156 ASSERT_TRUE(writer.Finalize());
157
158 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
159
160 CowReader reader;
161 CowHeader header;
162 CowFooter footer;
163 ASSERT_TRUE(reader.Parse(cow_->fd));
164 ASSERT_TRUE(reader.GetHeader(&header));
165 ASSERT_TRUE(reader.GetFooter(&footer));
166 ASSERT_EQ(header.magic, kCowMagicNumber);
167 ASSERT_EQ(header.major_version, kCowVersionMajor);
168 ASSERT_EQ(header.minor_version, kCowVersionMinor);
169 ASSERT_EQ(header.block_size, options.block_size);
170 ASSERT_EQ(footer.op.num_ops, 4);
171
172 auto iter = reader.GetOpIter();
173 ASSERT_NE(iter, nullptr);
174 ASSERT_FALSE(iter->Done());
175 auto op = &iter->Get();
176
177 ASSERT_EQ(op->type, kCowCopyOp);
178 ASSERT_EQ(op->compression, kCowCompressNone);
179 ASSERT_EQ(op->data_length, 0);
180 ASSERT_EQ(op->new_block, 10);
181 ASSERT_EQ(op->source, 20);
182
183 StringSink sink;
184
185 iter->Next();
186 ASSERT_FALSE(iter->Done());
187 op = &iter->Get();
188
189 ASSERT_EQ(op->type, kCowXorOp);
190 ASSERT_EQ(op->compression, kCowCompressNone);
191 ASSERT_EQ(op->data_length, 4096);
192 ASSERT_EQ(op->new_block, 50);
193 ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10
194 ASSERT_TRUE(reader.ReadData(*op, &sink));
195 ASSERT_EQ(sink.stream(), data);
196
197 iter->Next();
198 ASSERT_FALSE(iter->Done());
199 op = &iter->Get();
200
201 // Note: the zero operation gets split into two blocks.
202 ASSERT_EQ(op->type, kCowZeroOp);
203 ASSERT_EQ(op->compression, kCowCompressNone);
204 ASSERT_EQ(op->data_length, 0);
205 ASSERT_EQ(op->new_block, 51);
206 ASSERT_EQ(op->source, 0);
207
208 iter->Next();
209 ASSERT_FALSE(iter->Done());
210 op = &iter->Get();
211
212 ASSERT_EQ(op->type, kCowZeroOp);
213 ASSERT_EQ(op->compression, kCowCompressNone);
214 ASSERT_EQ(op->data_length, 0);
215 ASSERT_EQ(op->new_block, 52);
216 ASSERT_EQ(op->source, 0);
217
218 iter->Next();
219 ASSERT_TRUE(iter->Done());
220 }
221
TEST_F(CowTest,CompressGz)222 TEST_F(CowTest, CompressGz) {
223 CowOptions options;
224 options.cluster_ops = 0;
225 options.compression = "gz";
226 CowWriter writer(options);
227
228 ASSERT_TRUE(writer.Initialize(cow_->fd));
229
230 std::string data = "This is some data, believe it";
231 data.resize(options.block_size, '\0');
232
233 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
234 ASSERT_TRUE(writer.Finalize());
235
236 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
237
238 CowReader reader;
239 ASSERT_TRUE(reader.Parse(cow_->fd));
240
241 auto iter = reader.GetOpIter();
242 ASSERT_NE(iter, nullptr);
243 ASSERT_FALSE(iter->Done());
244 auto op = &iter->Get();
245
246 StringSink sink;
247
248 ASSERT_EQ(op->type, kCowReplaceOp);
249 ASSERT_EQ(op->compression, kCowCompressGz);
250 ASSERT_EQ(op->data_length, 56); // compressed!
251 ASSERT_EQ(op->new_block, 50);
252 ASSERT_TRUE(reader.ReadData(*op, &sink));
253 ASSERT_EQ(sink.stream(), data);
254
255 iter->Next();
256 ASSERT_TRUE(iter->Done());
257 }
258
TEST_F(CowTest,ClusterCompressGz)259 TEST_F(CowTest, ClusterCompressGz) {
260 CowOptions options;
261 options.compression = "gz";
262 options.cluster_ops = 2;
263 CowWriter writer(options);
264
265 ASSERT_TRUE(writer.Initialize(cow_->fd));
266
267 std::string data = "This is some data, believe it";
268 data.resize(options.block_size, '\0');
269 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
270
271 std::string data2 = "More data!";
272 data2.resize(options.block_size, '\0');
273 ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
274
275 ASSERT_TRUE(writer.Finalize());
276
277 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
278
279 CowReader reader;
280 ASSERT_TRUE(reader.Parse(cow_->fd));
281
282 auto iter = reader.GetOpIter();
283 ASSERT_NE(iter, nullptr);
284 ASSERT_FALSE(iter->Done());
285 auto op = &iter->Get();
286
287 StringSink sink;
288
289 ASSERT_EQ(op->type, kCowReplaceOp);
290 ASSERT_EQ(op->compression, kCowCompressGz);
291 ASSERT_EQ(op->data_length, 56); // compressed!
292 ASSERT_EQ(op->new_block, 50);
293 ASSERT_TRUE(reader.ReadData(*op, &sink));
294 ASSERT_EQ(sink.stream(), data);
295
296 iter->Next();
297 ASSERT_FALSE(iter->Done());
298 op = &iter->Get();
299
300 ASSERT_EQ(op->type, kCowClusterOp);
301
302 iter->Next();
303 ASSERT_FALSE(iter->Done());
304 op = &iter->Get();
305
306 sink.Reset();
307 ASSERT_EQ(op->compression, kCowCompressGz);
308 ASSERT_EQ(op->data_length, 41); // compressed!
309 ASSERT_EQ(op->new_block, 51);
310 ASSERT_TRUE(reader.ReadData(*op, &sink));
311 ASSERT_EQ(sink.stream(), data2);
312
313 iter->Next();
314 ASSERT_FALSE(iter->Done());
315 op = &iter->Get();
316
317 ASSERT_EQ(op->type, kCowClusterOp);
318
319 iter->Next();
320 ASSERT_TRUE(iter->Done());
321 }
322
TEST_F(CowTest,CompressTwoBlocks)323 TEST_F(CowTest, CompressTwoBlocks) {
324 CowOptions options;
325 options.compression = "gz";
326 options.cluster_ops = 0;
327 CowWriter writer(options);
328
329 ASSERT_TRUE(writer.Initialize(cow_->fd));
330
331 std::string data = "This is some data, believe it";
332 data.resize(options.block_size * 2, '\0');
333
334 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
335 ASSERT_TRUE(writer.Finalize());
336
337 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
338
339 CowReader reader;
340 ASSERT_TRUE(reader.Parse(cow_->fd));
341
342 auto iter = reader.GetOpIter();
343 ASSERT_NE(iter, nullptr);
344 ASSERT_FALSE(iter->Done());
345 iter->Next();
346 ASSERT_FALSE(iter->Done());
347
348 StringSink sink;
349
350 auto op = &iter->Get();
351 ASSERT_EQ(op->type, kCowReplaceOp);
352 ASSERT_EQ(op->compression, kCowCompressGz);
353 ASSERT_EQ(op->new_block, 51);
354 ASSERT_TRUE(reader.ReadData(*op, &sink));
355 }
356
357 // Only return 1-byte buffers, to stress test the partial read logic in
358 // CowReader.
359 class HorribleStringSink : public StringSink {
360 public:
GetBuffer(size_t,size_t * actual)361 void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
362 };
363
364 class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
365
TEST_P(CompressionTest,HorribleSink)366 TEST_P(CompressionTest, HorribleSink) {
367 CowOptions options;
368 options.compression = GetParam();
369 options.cluster_ops = 0;
370 CowWriter writer(options);
371
372 ASSERT_TRUE(writer.Initialize(cow_->fd));
373
374 std::string data = "This is some data, believe it";
375 data.resize(options.block_size, '\0');
376
377 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
378 ASSERT_TRUE(writer.Finalize());
379
380 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
381
382 CowReader reader;
383 ASSERT_TRUE(reader.Parse(cow_->fd));
384
385 auto iter = reader.GetOpIter();
386 ASSERT_NE(iter, nullptr);
387 ASSERT_FALSE(iter->Done());
388
389 HorribleStringSink sink;
390 auto op = &iter->Get();
391 ASSERT_TRUE(reader.ReadData(*op, &sink));
392 ASSERT_EQ(sink.stream(), data);
393 }
394
395 INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
396
TEST_F(CowTest,GetSize)397 TEST_F(CowTest, GetSize) {
398 CowOptions options;
399 options.cluster_ops = 0;
400 CowWriter writer(options);
401 if (ftruncate(cow_->fd, 0) < 0) {
402 perror("Fails to set temp file size");
403 FAIL();
404 }
405 ASSERT_TRUE(writer.Initialize(cow_->fd));
406
407 std::string data = "This is some data, believe it";
408 data.resize(options.block_size, '\0');
409
410 ASSERT_TRUE(writer.AddCopy(10, 20));
411 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
412 ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
413 auto size_before = writer.GetCowSize();
414 ASSERT_TRUE(writer.Finalize());
415 auto size_after = writer.GetCowSize();
416 ASSERT_EQ(size_before, size_after);
417 struct stat buf;
418
419 ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);
420 ASSERT_EQ(buf.st_size, writer.GetCowSize());
421 }
422
TEST_F(CowTest,AppendLabelSmall)423 TEST_F(CowTest, AppendLabelSmall) {
424 CowOptions options;
425 options.cluster_ops = 0;
426 auto writer = std::make_unique<CowWriter>(options);
427 ASSERT_TRUE(writer->Initialize(cow_->fd));
428
429 std::string data = "This is some data, believe it";
430 data.resize(options.block_size, '\0');
431 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
432 ASSERT_TRUE(writer->AddLabel(3));
433 ASSERT_TRUE(writer->Finalize());
434
435 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
436
437 writer = std::make_unique<CowWriter>(options);
438 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 3));
439
440 std::string data2 = "More data!";
441 data2.resize(options.block_size, '\0');
442 ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
443 ASSERT_TRUE(writer->Finalize());
444
445 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
446
447 struct stat buf;
448 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
449 ASSERT_EQ(buf.st_size, writer->GetCowSize());
450
451 // Read back both operations, and label.
452 CowReader reader;
453 uint64_t label;
454 ASSERT_TRUE(reader.Parse(cow_->fd));
455 ASSERT_TRUE(reader.GetLastLabel(&label));
456 ASSERT_EQ(label, 3);
457
458 StringSink sink;
459
460 auto iter = reader.GetOpIter();
461 ASSERT_NE(iter, nullptr);
462
463 ASSERT_FALSE(iter->Done());
464 auto op = &iter->Get();
465 ASSERT_EQ(op->type, kCowReplaceOp);
466 ASSERT_TRUE(reader.ReadData(*op, &sink));
467 ASSERT_EQ(sink.stream(), data);
468
469 iter->Next();
470 sink.Reset();
471
472 ASSERT_FALSE(iter->Done());
473 op = &iter->Get();
474 ASSERT_EQ(op->type, kCowLabelOp);
475 ASSERT_EQ(op->source, 3);
476
477 iter->Next();
478
479 ASSERT_FALSE(iter->Done());
480 op = &iter->Get();
481 ASSERT_EQ(op->type, kCowReplaceOp);
482 ASSERT_TRUE(reader.ReadData(*op, &sink));
483 ASSERT_EQ(sink.stream(), data2);
484
485 iter->Next();
486 ASSERT_TRUE(iter->Done());
487 }
488
TEST_F(CowTest,AppendLabelMissing)489 TEST_F(CowTest, AppendLabelMissing) {
490 CowOptions options;
491 options.cluster_ops = 0;
492 auto writer = std::make_unique<CowWriter>(options);
493 ASSERT_TRUE(writer->Initialize(cow_->fd));
494
495 ASSERT_TRUE(writer->AddLabel(0));
496 std::string data = "This is some data, believe it";
497 data.resize(options.block_size, '\0');
498 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
499 ASSERT_TRUE(writer->AddLabel(1));
500 // Drop the tail end of the last op header, corrupting it.
501 ftruncate(cow_->fd, writer->GetCowSize() - sizeof(CowFooter) - 3);
502
503 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
504
505 writer = std::make_unique<CowWriter>(options);
506 ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 1));
507 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 0));
508
509 ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
510 ASSERT_TRUE(writer->Finalize());
511
512 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
513
514 struct stat buf;
515 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
516 ASSERT_EQ(buf.st_size, writer->GetCowSize());
517
518 // Read back both operations.
519 CowReader reader;
520 ASSERT_TRUE(reader.Parse(cow_->fd));
521
522 StringSink sink;
523
524 auto iter = reader.GetOpIter();
525 ASSERT_NE(iter, nullptr);
526
527 ASSERT_FALSE(iter->Done());
528 auto op = &iter->Get();
529 ASSERT_EQ(op->type, kCowLabelOp);
530 ASSERT_EQ(op->source, 0);
531
532 iter->Next();
533
534 ASSERT_FALSE(iter->Done());
535 op = &iter->Get();
536 ASSERT_EQ(op->type, kCowZeroOp);
537
538 iter->Next();
539
540 ASSERT_TRUE(iter->Done());
541 }
542
TEST_F(CowTest,AppendExtendedCorrupted)543 TEST_F(CowTest, AppendExtendedCorrupted) {
544 CowOptions options;
545 options.cluster_ops = 0;
546 auto writer = std::make_unique<CowWriter>(options);
547 ASSERT_TRUE(writer->Initialize(cow_->fd));
548
549 ASSERT_TRUE(writer->AddLabel(5));
550
551 std::string data = "This is some data, believe it";
552 data.resize(options.block_size * 2, '\0');
553 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
554 ASSERT_TRUE(writer->AddLabel(6));
555
556 // fail to write the footer. Cow Format does not know if Label 6 is valid
557
558 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
559
560 // Get the last known good label
561 CowReader label_reader;
562 uint64_t label;
563 ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));
564 ASSERT_TRUE(label_reader.GetLastLabel(&label));
565 ASSERT_EQ(label, 5);
566
567 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
568
569 writer = std::make_unique<CowWriter>(options);
570 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
571
572 ASSERT_TRUE(writer->Finalize());
573
574 struct stat buf;
575 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
576 ASSERT_EQ(buf.st_size, writer->GetCowSize());
577
578 // Read back all valid operations
579 CowReader reader;
580 ASSERT_TRUE(reader.Parse(cow_->fd));
581
582 StringSink sink;
583
584 auto iter = reader.GetOpIter();
585 ASSERT_NE(iter, nullptr);
586
587 ASSERT_FALSE(iter->Done());
588 auto op = &iter->Get();
589 ASSERT_EQ(op->type, kCowLabelOp);
590 ASSERT_EQ(op->source, 5);
591
592 iter->Next();
593 ASSERT_TRUE(iter->Done());
594 }
595
TEST_F(CowTest,AppendbyLabel)596 TEST_F(CowTest, AppendbyLabel) {
597 CowOptions options;
598 options.cluster_ops = 0;
599 auto writer = std::make_unique<CowWriter>(options);
600 ASSERT_TRUE(writer->Initialize(cow_->fd));
601
602 std::string data = "This is some data, believe it";
603 data.resize(options.block_size * 2, '\0');
604 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
605
606 ASSERT_TRUE(writer->AddLabel(4));
607
608 ASSERT_TRUE(writer->AddZeroBlocks(50, 2));
609
610 ASSERT_TRUE(writer->AddLabel(5));
611
612 ASSERT_TRUE(writer->AddCopy(5, 6));
613
614 ASSERT_TRUE(writer->AddLabel(6));
615
616 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
617
618 writer = std::make_unique<CowWriter>(options);
619 ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 12));
620 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
621
622 // This should drop label 6
623 ASSERT_TRUE(writer->Finalize());
624
625 struct stat buf;
626 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
627 ASSERT_EQ(buf.st_size, writer->GetCowSize());
628
629 // Read back all ops
630 CowReader reader;
631 ASSERT_TRUE(reader.Parse(cow_->fd));
632
633 StringSink sink;
634
635 auto iter = reader.GetOpIter();
636 ASSERT_NE(iter, nullptr);
637
638 ASSERT_FALSE(iter->Done());
639 auto op = &iter->Get();
640 ASSERT_EQ(op->type, kCowReplaceOp);
641 ASSERT_TRUE(reader.ReadData(*op, &sink));
642 ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
643
644 iter->Next();
645 sink.Reset();
646
647 ASSERT_FALSE(iter->Done());
648 op = &iter->Get();
649 ASSERT_EQ(op->type, kCowReplaceOp);
650 ASSERT_TRUE(reader.ReadData(*op, &sink));
651 ASSERT_EQ(sink.stream(), data.substr(options.block_size, 2 * options.block_size));
652
653 iter->Next();
654 sink.Reset();
655
656 ASSERT_FALSE(iter->Done());
657 op = &iter->Get();
658 ASSERT_EQ(op->type, kCowLabelOp);
659 ASSERT_EQ(op->source, 4);
660
661 iter->Next();
662
663 ASSERT_FALSE(iter->Done());
664 op = &iter->Get();
665 ASSERT_EQ(op->type, kCowZeroOp);
666
667 iter->Next();
668
669 ASSERT_FALSE(iter->Done());
670 op = &iter->Get();
671 ASSERT_EQ(op->type, kCowZeroOp);
672
673 iter->Next();
674 ASSERT_FALSE(iter->Done());
675 op = &iter->Get();
676 ASSERT_EQ(op->type, kCowLabelOp);
677 ASSERT_EQ(op->source, 5);
678
679 iter->Next();
680
681 ASSERT_TRUE(iter->Done());
682 }
683
TEST_F(CowTest,ClusterTest)684 TEST_F(CowTest, ClusterTest) {
685 CowOptions options;
686 options.cluster_ops = 4;
687 auto writer = std::make_unique<CowWriter>(options);
688 ASSERT_TRUE(writer->Initialize(cow_->fd));
689
690 std::string data = "This is some data, believe it";
691 data.resize(options.block_size, '\0');
692 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
693
694 ASSERT_TRUE(writer->AddLabel(4));
695
696 ASSERT_TRUE(writer->AddZeroBlocks(50, 2)); // Cluster split in middle
697
698 ASSERT_TRUE(writer->AddLabel(5));
699
700 ASSERT_TRUE(writer->AddCopy(5, 6));
701
702 // Cluster split
703
704 ASSERT_TRUE(writer->AddLabel(6));
705
706 ASSERT_TRUE(writer->Finalize()); // No data for cluster, so no cluster split needed
707
708 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
709
710 // Read back all ops
711 CowReader reader;
712 ASSERT_TRUE(reader.Parse(cow_->fd));
713
714 StringSink sink;
715
716 auto iter = reader.GetOpIter();
717 ASSERT_NE(iter, nullptr);
718
719 ASSERT_FALSE(iter->Done());
720 auto op = &iter->Get();
721 ASSERT_EQ(op->type, kCowReplaceOp);
722 ASSERT_TRUE(reader.ReadData(*op, &sink));
723 ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
724
725 iter->Next();
726 sink.Reset();
727
728 ASSERT_FALSE(iter->Done());
729 op = &iter->Get();
730 ASSERT_EQ(op->type, kCowLabelOp);
731 ASSERT_EQ(op->source, 4);
732
733 iter->Next();
734
735 ASSERT_FALSE(iter->Done());
736 op = &iter->Get();
737 ASSERT_EQ(op->type, kCowZeroOp);
738
739 iter->Next();
740
741 ASSERT_FALSE(iter->Done());
742 op = &iter->Get();
743 ASSERT_EQ(op->type, kCowClusterOp);
744
745 iter->Next();
746
747 ASSERT_FALSE(iter->Done());
748 op = &iter->Get();
749 ASSERT_EQ(op->type, kCowZeroOp);
750
751 iter->Next();
752
753 ASSERT_FALSE(iter->Done());
754 op = &iter->Get();
755 ASSERT_EQ(op->type, kCowLabelOp);
756 ASSERT_EQ(op->source, 5);
757
758 iter->Next();
759
760 ASSERT_FALSE(iter->Done());
761 op = &iter->Get();
762 ASSERT_EQ(op->type, kCowCopyOp);
763
764 iter->Next();
765
766 ASSERT_FALSE(iter->Done());
767 op = &iter->Get();
768 ASSERT_EQ(op->type, kCowClusterOp);
769
770 iter->Next();
771
772 ASSERT_FALSE(iter->Done());
773 op = &iter->Get();
774 ASSERT_EQ(op->type, kCowLabelOp);
775 ASSERT_EQ(op->source, 6);
776
777 iter->Next();
778
779 ASSERT_TRUE(iter->Done());
780 }
781
TEST_F(CowTest,ClusterAppendTest)782 TEST_F(CowTest, ClusterAppendTest) {
783 CowOptions options;
784 options.cluster_ops = 3;
785 auto writer = std::make_unique<CowWriter>(options);
786 ASSERT_TRUE(writer->Initialize(cow_->fd));
787
788 ASSERT_TRUE(writer->AddLabel(50));
789 ASSERT_TRUE(writer->Finalize()); // Adds a cluster op, should be dropped on append
790
791 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
792
793 writer = std::make_unique<CowWriter>(options);
794 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
795
796 std::string data2 = "More data!";
797 data2.resize(options.block_size, '\0');
798 ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
799 ASSERT_TRUE(writer->Finalize()); // Adds a cluster op
800
801 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
802
803 struct stat buf;
804 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
805 ASSERT_EQ(buf.st_size, writer->GetCowSize());
806
807 // Read back both operations, plus cluster op at end
808 CowReader reader;
809 uint64_t label;
810 ASSERT_TRUE(reader.Parse(cow_->fd));
811 ASSERT_TRUE(reader.GetLastLabel(&label));
812 ASSERT_EQ(label, 50);
813
814 StringSink sink;
815
816 auto iter = reader.GetOpIter();
817 ASSERT_NE(iter, nullptr);
818
819 ASSERT_FALSE(iter->Done());
820 auto op = &iter->Get();
821 ASSERT_EQ(op->type, kCowLabelOp);
822 ASSERT_EQ(op->source, 50);
823
824 iter->Next();
825
826 ASSERT_FALSE(iter->Done());
827 op = &iter->Get();
828 ASSERT_EQ(op->type, kCowReplaceOp);
829 ASSERT_TRUE(reader.ReadData(*op, &sink));
830 ASSERT_EQ(sink.stream(), data2);
831
832 iter->Next();
833
834 ASSERT_FALSE(iter->Done());
835 op = &iter->Get();
836 ASSERT_EQ(op->type, kCowClusterOp);
837
838 iter->Next();
839
840 ASSERT_TRUE(iter->Done());
841 }
842
TEST_F(CowTest,AppendAfterFinalize)843 TEST_F(CowTest, AppendAfterFinalize) {
844 CowOptions options;
845 options.cluster_ops = 0;
846 auto writer = std::make_unique<CowWriter>(options);
847 ASSERT_TRUE(writer->Initialize(cow_->fd));
848
849 std::string data = "This is some data, believe it";
850 data.resize(options.block_size, '\0');
851 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
852 ASSERT_TRUE(writer->AddLabel(3));
853 ASSERT_TRUE(writer->Finalize());
854
855 std::string data2 = "More data!";
856 data2.resize(options.block_size, '\0');
857 ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
858 ASSERT_TRUE(writer->Finalize());
859
860 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
861
862 // COW should be valid.
863 CowReader reader;
864 ASSERT_TRUE(reader.Parse(cow_->fd));
865 }
866
WriteDataBlock(CowWriter * writer,uint64_t new_block,std::string data)867 AssertionResult WriteDataBlock(CowWriter* writer, uint64_t new_block, std::string data) {
868 data.resize(writer->options().block_size, '\0');
869 if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {
870 return AssertionFailure() << "Failed to add raw block";
871 }
872 return AssertionSuccess();
873 }
874
CompareDataBlock(CowReader * reader,const CowOperation & op,const std::string & data)875 AssertionResult CompareDataBlock(CowReader* reader, const CowOperation& op,
876 const std::string& data) {
877 CowHeader header;
878 reader->GetHeader(&header);
879
880 std::string cmp = data;
881 cmp.resize(header.block_size, '\0');
882
883 StringSink sink;
884 if (!reader->ReadData(op, &sink)) {
885 return AssertionFailure() << "Failed to read data block";
886 }
887 if (cmp != sink.stream()) {
888 return AssertionFailure() << "Data blocks did not match, expected " << cmp << ", got "
889 << sink.stream();
890 }
891
892 return AssertionSuccess();
893 }
894
TEST_F(CowTest,ResumeMidCluster)895 TEST_F(CowTest, ResumeMidCluster) {
896 CowOptions options;
897 options.cluster_ops = 7;
898 auto writer = std::make_unique<CowWriter>(options);
899 ASSERT_TRUE(writer->Initialize(cow_->fd));
900
901 ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
902 ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
903 ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
904 ASSERT_TRUE(writer->AddLabel(1));
905 ASSERT_TRUE(writer->Finalize());
906 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
907 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
908
909 writer = std::make_unique<CowWriter>(options);
910 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
911 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
912 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
913 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
914 ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
915 ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
916 ASSERT_TRUE(writer->AddLabel(2));
917 ASSERT_TRUE(writer->Finalize());
918 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
919
920 CowReader reader;
921 ASSERT_TRUE(reader.Parse(cow_->fd));
922
923 auto iter = reader.GetOpIter();
924 size_t num_replace = 0;
925 size_t max_in_cluster = 0;
926 size_t num_in_cluster = 0;
927 size_t num_clusters = 0;
928 while (!iter->Done()) {
929 const auto& op = iter->Get();
930
931 num_in_cluster++;
932 max_in_cluster = std::max(max_in_cluster, num_in_cluster);
933
934 if (op.type == kCowReplaceOp) {
935 num_replace++;
936
937 ASSERT_EQ(op.new_block, num_replace);
938 ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
939 } else if (op.type == kCowClusterOp) {
940 num_in_cluster = 0;
941 num_clusters++;
942 }
943
944 iter->Next();
945 }
946 ASSERT_EQ(num_replace, 8);
947 ASSERT_EQ(max_in_cluster, 7);
948 ASSERT_EQ(num_clusters, 2);
949 }
950
TEST_F(CowTest,ResumeEndCluster)951 TEST_F(CowTest, ResumeEndCluster) {
952 CowOptions options;
953 int cluster_ops = 5;
954 options.cluster_ops = cluster_ops;
955 auto writer = std::make_unique<CowWriter>(options);
956 ASSERT_TRUE(writer->Initialize(cow_->fd));
957
958 ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
959 ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
960 ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
961 ASSERT_TRUE(writer->AddLabel(1));
962 ASSERT_TRUE(writer->Finalize());
963 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
964 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
965 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
966 ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
967 ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
968 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
969
970 writer = std::make_unique<CowWriter>(options);
971 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
972 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
973 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
974 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
975 ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
976 ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
977 ASSERT_TRUE(writer->AddLabel(2));
978 ASSERT_TRUE(writer->Finalize());
979 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
980
981 CowReader reader;
982 ASSERT_TRUE(reader.Parse(cow_->fd));
983
984 auto iter = reader.GetOpIter();
985 size_t num_replace = 0;
986 size_t max_in_cluster = 0;
987 size_t num_in_cluster = 0;
988 size_t num_clusters = 0;
989 while (!iter->Done()) {
990 const auto& op = iter->Get();
991
992 num_in_cluster++;
993 max_in_cluster = std::max(max_in_cluster, num_in_cluster);
994
995 if (op.type == kCowReplaceOp) {
996 num_replace++;
997
998 ASSERT_EQ(op.new_block, num_replace);
999 ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
1000 } else if (op.type == kCowClusterOp) {
1001 num_in_cluster = 0;
1002 num_clusters++;
1003 }
1004
1005 iter->Next();
1006 }
1007 ASSERT_EQ(num_replace, 8);
1008 ASSERT_EQ(max_in_cluster, cluster_ops);
1009 ASSERT_EQ(num_clusters, 3);
1010 }
1011
TEST_F(CowTest,DeleteMidCluster)1012 TEST_F(CowTest, DeleteMidCluster) {
1013 CowOptions options;
1014 options.cluster_ops = 7;
1015 auto writer = std::make_unique<CowWriter>(options);
1016 ASSERT_TRUE(writer->Initialize(cow_->fd));
1017
1018 ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
1019 ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
1020 ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
1021 ASSERT_TRUE(writer->AddLabel(1));
1022 ASSERT_TRUE(writer->Finalize());
1023 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
1024 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
1025 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
1026 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1027
1028 writer = std::make_unique<CowWriter>(options);
1029 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
1030 ASSERT_TRUE(writer->Finalize());
1031 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1032
1033 CowReader reader;
1034 ASSERT_TRUE(reader.Parse(cow_->fd));
1035
1036 auto iter = reader.GetOpIter();
1037 size_t num_replace = 0;
1038 size_t max_in_cluster = 0;
1039 size_t num_in_cluster = 0;
1040 size_t num_clusters = 0;
1041 while (!iter->Done()) {
1042 const auto& op = iter->Get();
1043
1044 num_in_cluster++;
1045 max_in_cluster = std::max(max_in_cluster, num_in_cluster);
1046 if (op.type == kCowReplaceOp) {
1047 num_replace++;
1048
1049 ASSERT_EQ(op.new_block, num_replace);
1050 ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
1051 } else if (op.type == kCowClusterOp) {
1052 num_in_cluster = 0;
1053 num_clusters++;
1054 }
1055
1056 iter->Next();
1057 }
1058 ASSERT_EQ(num_replace, 3);
1059 ASSERT_EQ(max_in_cluster, 5); // 3 data, 1 label, 1 cluster op
1060 ASSERT_EQ(num_clusters, 1);
1061 }
1062
TEST_F(CowTest,BigSeqOp)1063 TEST_F(CowTest, BigSeqOp) {
1064 CowOptions options;
1065 CowWriter writer(options);
1066 const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
1067 uint32_t sequence[seq_len];
1068 for (int i = 0; i < seq_len; i++) {
1069 sequence[i] = i + 1;
1070 }
1071
1072 ASSERT_TRUE(writer.Initialize(cow_->fd));
1073
1074 ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
1075 ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));
1076 ASSERT_TRUE(writer.Finalize());
1077
1078 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1079
1080 CowReader reader;
1081 ASSERT_TRUE(reader.Parse(cow_->fd));
1082 auto iter = reader.GetRevMergeOpIter();
1083
1084 for (int i = 0; i < seq_len; i++) {
1085 ASSERT_TRUE(!iter->Done());
1086 const auto& op = iter->Get();
1087
1088 ASSERT_EQ(op.new_block, seq_len - i);
1089
1090 iter->Next();
1091 }
1092 ASSERT_TRUE(iter->Done());
1093 }
1094
TEST_F(CowTest,MissingSeqOp)1095 TEST_F(CowTest, MissingSeqOp) {
1096 CowOptions options;
1097 CowWriter writer(options);
1098 const int seq_len = 10;
1099 uint32_t sequence[seq_len];
1100 for (int i = 0; i < seq_len; i++) {
1101 sequence[i] = i + 1;
1102 }
1103
1104 ASSERT_TRUE(writer.Initialize(cow_->fd));
1105
1106 ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
1107 ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
1108 ASSERT_TRUE(writer.Finalize());
1109
1110 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1111
1112 CowReader reader;
1113 ASSERT_FALSE(reader.Parse(cow_->fd));
1114 }
1115
TEST_F(CowTest,ResumeSeqOp)1116 TEST_F(CowTest, ResumeSeqOp) {
1117 CowOptions options;
1118 auto writer = std::make_unique<CowWriter>(options);
1119 const int seq_len = 10;
1120 uint32_t sequence[seq_len];
1121 for (int i = 0; i < seq_len; i++) {
1122 sequence[i] = i + 1;
1123 }
1124
1125 ASSERT_TRUE(writer->Initialize(cow_->fd));
1126
1127 ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
1128 ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
1129 ASSERT_TRUE(writer->AddLabel(1));
1130 ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
1131
1132 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1133 auto reader = std::make_unique<CowReader>();
1134 ASSERT_TRUE(reader->Parse(cow_->fd, 1));
1135 auto itr = reader->GetRevMergeOpIter();
1136 ASSERT_TRUE(itr->Done());
1137
1138 writer = std::make_unique<CowWriter>(options);
1139 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
1140 ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
1141 ASSERT_TRUE(writer->Finalize());
1142
1143 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1144
1145 reader = std::make_unique<CowReader>();
1146 ASSERT_TRUE(reader->Parse(cow_->fd));
1147
1148 auto iter = reader->GetRevMergeOpIter();
1149
1150 uint64_t expected_block = 10;
1151 while (!iter->Done() && expected_block > 0) {
1152 ASSERT_FALSE(iter->Done());
1153 const auto& op = iter->Get();
1154
1155 ASSERT_EQ(op.new_block, expected_block);
1156
1157 iter->Next();
1158 expected_block--;
1159 }
1160 ASSERT_EQ(expected_block, 0);
1161 ASSERT_TRUE(iter->Done());
1162 }
1163
TEST_F(CowTest,RevMergeOpItrTest)1164 TEST_F(CowTest, RevMergeOpItrTest) {
1165 CowOptions options;
1166 options.cluster_ops = 5;
1167 options.num_merge_ops = 1;
1168 CowWriter writer(options);
1169 uint32_t sequence[] = {2, 10, 6, 7, 3, 5};
1170
1171 ASSERT_TRUE(writer.Initialize(cow_->fd));
1172
1173 ASSERT_TRUE(writer.AddSequenceData(6, sequence));
1174 ASSERT_TRUE(writer.AddCopy(6, 13));
1175 ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
1176 ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
1177 ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
1178 ASSERT_TRUE(writer.AddCopy(3, 15));
1179 ASSERT_TRUE(writer.AddCopy(2, 11));
1180 ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
1181 ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
1182 ASSERT_TRUE(writer.AddCopy(5, 16));
1183 ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
1184 ASSERT_TRUE(writer.AddCopy(10, 12));
1185 ASSERT_TRUE(writer.AddCopy(7, 14));
1186 ASSERT_TRUE(writer.Finalize());
1187
1188 // New block in cow order is 6, 12, 8, 11, 3, 2, 4, 9, 5, 1, 10, 7
1189 // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
1190 // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
1191 // new block 2 is "already merged", so will be left out.
1192
1193 std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
1194
1195 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1196
1197 CowReader reader;
1198 ASSERT_TRUE(reader.Parse(cow_->fd));
1199 auto iter = reader.GetRevMergeOpIter();
1200 auto expected_new_block = revMergeOpSequence.begin();
1201
1202 while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
1203 const auto& op = iter->Get();
1204
1205 ASSERT_EQ(op.new_block, *expected_new_block);
1206
1207 iter->Next();
1208 expected_new_block++;
1209 }
1210 ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
1211 ASSERT_TRUE(iter->Done());
1212 }
1213
TEST_F(CowTest,LegacyRevMergeOpItrTest)1214 TEST_F(CowTest, LegacyRevMergeOpItrTest) {
1215 CowOptions options;
1216 options.cluster_ops = 5;
1217 options.num_merge_ops = 1;
1218 CowWriter writer(options);
1219
1220 ASSERT_TRUE(writer.Initialize(cow_->fd));
1221
1222 ASSERT_TRUE(writer.AddCopy(2, 11));
1223 ASSERT_TRUE(writer.AddCopy(10, 12));
1224 ASSERT_TRUE(writer.AddCopy(6, 13));
1225 ASSERT_TRUE(writer.AddCopy(7, 14));
1226 ASSERT_TRUE(writer.AddCopy(3, 15));
1227 ASSERT_TRUE(writer.AddCopy(5, 16));
1228 ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
1229 ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
1230 ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
1231 ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
1232 ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
1233 ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
1234
1235 ASSERT_TRUE(writer.Finalize());
1236
1237 // New block in cow order is 2, 10, 6, 7, 3, 5, 12, 8, 11, 4, 9, 1
1238 // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
1239 // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
1240 // new block 2 is "already merged", so will be left out.
1241
1242 std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
1243
1244 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
1245
1246 CowReader reader;
1247 ASSERT_TRUE(reader.Parse(cow_->fd));
1248 auto iter = reader.GetRevMergeOpIter();
1249 auto expected_new_block = revMergeOpSequence.begin();
1250
1251 while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
1252 const auto& op = iter->Get();
1253
1254 ASSERT_EQ(op.new_block, *expected_new_block);
1255
1256 iter->Next();
1257 expected_new_block++;
1258 }
1259 ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
1260 ASSERT_TRUE(iter->Done());
1261 }
1262
TEST_F(CowTest,InvalidMergeOrderTest)1263 TEST_F(CowTest, InvalidMergeOrderTest) {
1264 CowOptions options;
1265 options.cluster_ops = 5;
1266 options.num_merge_ops = 1;
1267 std::string data = "This is some data, believe it";
1268 data.resize(options.block_size, '\0');
1269 auto writer = std::make_unique<CowWriter>(options);
1270 CowReader reader;
1271
1272 ASSERT_TRUE(writer->Initialize(cow_->fd));
1273
1274 ASSERT_TRUE(writer->AddCopy(3, 2));
1275 ASSERT_TRUE(writer->AddCopy(2, 1));
1276 ASSERT_TRUE(writer->AddLabel(1));
1277 ASSERT_TRUE(writer->Finalize());
1278 ASSERT_TRUE(reader.Parse(cow_->fd));
1279 ASSERT_TRUE(reader.VerifyMergeOps());
1280
1281 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
1282 ASSERT_TRUE(writer->AddCopy(4, 2));
1283 ASSERT_TRUE(writer->Finalize());
1284 ASSERT_TRUE(reader.Parse(cow_->fd));
1285 ASSERT_FALSE(reader.VerifyMergeOps());
1286
1287 writer = std::make_unique<CowWriter>(options);
1288 ASSERT_TRUE(writer->Initialize(cow_->fd));
1289 ASSERT_TRUE(writer->AddCopy(2, 1));
1290 ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
1291 ASSERT_TRUE(writer->Finalize());
1292 ASSERT_TRUE(reader.Parse(cow_->fd));
1293 ASSERT_FALSE(reader.VerifyMergeOps());
1294 }
1295
1296 } // namespace snapshot
1297 } // namespace android
1298
main(int argc,char ** argv)1299 int main(int argc, char** argv) {
1300 ::testing::InitGoogleTest(&argc, argv);
1301 return RUN_ALL_TESTS();
1302 }
1303