summaryrefslogtreecommitdiff
path: root/xdelta3
diff options
context:
space:
mode:
authorjosh.macdonald <jmacd@users.noreply.github.com>2008-07-10 04:10:18 +0000
committerjosh.macdonald <jmacd@users.noreply.github.com>2008-07-10 04:10:18 +0000
commitcac3ff51853873ef6aa0d5b7e6d9d720218f38f6 (patch)
treec2a47a05a6acbde359d7971e63e863ee46a7d74c /xdelta3
parentbd594c150533173d0340df8204824c9d6f00966b (diff)
Adds a test for the fix for issue 70. The new regression test
framework's ability to craft specific inputs is very handy.
Diffstat (limited to 'xdelta3')
-rwxr-xr-xxdelta3/testing/Makefile4
-rw-r--r--xdelta3/testing/file.h12
-rw-r--r--xdelta3/testing/modify.h27
-rw-r--r--xdelta3/testing/regtest.cc115
-rw-r--r--xdelta3/xdelta3.c22
5 files changed, 123 insertions, 57 deletions
diff --git a/xdelta3/testing/Makefile b/xdelta3/testing/Makefile
index 814f477..df9854d 100755
--- a/xdelta3/testing/Makefile
+++ b/xdelta3/testing/Makefile
@@ -1,5 +1,5 @@
1#CFLAGS = -g -Wall -I.. -DXD3_DEBUG=1 -DNDEBUG=0 1CFLAGS = -g -Wall -I.. -DXD3_DEBUG=1 -DNDEBUG=0
2CFLAGS = -g -Wall -I.. -DXD3_DEBUG=2 -DNDEBUG=0 2#CFLAGS = -g -Wall -I.. -DXD3_DEBUG=2 -DNDEBUG=0
3#CFLAGS = -O2 -Wall -I.. -DXD3_DEBUG=0 -DNDEBUG=1 3#CFLAGS = -O2 -Wall -I.. -DXD3_DEBUG=0 -DNDEBUG=1
4 4
5DEPS = ../*.h ../*.c *.cc *.h 5DEPS = ../*.h ../*.c *.cc *.h
diff --git a/xdelta3/testing/file.h b/xdelta3/testing/file.h
index d38a720..4153e5f 100644
--- a/xdelta3/testing/file.h
+++ b/xdelta3/testing/file.h
@@ -53,6 +53,18 @@ class FileSpec {
53 FileSpec *modify) const { 53 FileSpec *modify) const {
54 modify->Reset(); 54 modify->Reset();
55 mutator.Mutate(&modify->table_, &table_, rand_); 55 mutator.Mutate(&modify->table_, &table_, rand_);
56 modify->CheckSegments();
57 }
58
59 void CheckSegments() const {
60 for (SegmentMap::const_iterator iter(table_.begin());
61 iter != table_.end(); ) {
62 SegmentMap::const_iterator iter0(iter++);
63 if (iter == table_.end()) {
64 break;
65 }
66 CHECK_EQ(iter0->first + iter0->second.Size(), iter->first);
67 }
56 } 68 }
57 69
58 void Reset() { 70 void Reset() {
diff --git a/xdelta3/testing/modify.h b/xdelta3/testing/modify.h
index 8b81baf..67cccd9 100644
--- a/xdelta3/testing/modify.h
+++ b/xdelta3/testing/modify.h
@@ -17,6 +17,7 @@ public:
17 DELETE = 3, 17 DELETE = 3,
18 MOVE = 4, 18 MOVE = 4,
19 COPY = 5, 19 COPY = 5,
20 OVERWRITE = 6,
20 }; 21 };
21 22
22 // Constructor for modify, add, delete. 23 // Constructor for modify, add, delete.
@@ -25,7 +26,7 @@ public:
25 size(size), 26 size(size),
26 addr1(addr1), 27 addr1(addr1),
27 insert(NULL) { 28 insert(NULL) {
28 CHECK(kind != MOVE && kind != COPY); 29 CHECK(kind != MOVE && kind != COPY && kind != OVERWRITE);
29 } 30 }
30 31
31 // Constructor for modify, add w/ provided data. 32 // Constructor for modify, add w/ provided data.
@@ -34,7 +35,7 @@ public:
34 size(size), 35 size(size),
35 addr1(addr1), 36 addr1(addr1),
36 insert(insert) { 37 insert(insert) {
37 CHECK(kind != MOVE && kind != COPY); 38 CHECK(kind != MOVE && kind != COPY && kind != OVERWRITE);
38 } 39 }
39 40
40 // Constructor for move 41 // Constructor for move
@@ -44,7 +45,7 @@ public:
44 addr1(addr1), 45 addr1(addr1),
45 addr2(addr2), 46 addr2(addr2),
46 insert(NULL) { 47 insert(NULL) {
47 CHECK(kind == MOVE || kind == COPY); 48 CHECK(kind == MOVE || kind == COPY || kind == OVERWRITE);
48 } 49 }
49 50
50 Kind kind; 51 Kind kind;
@@ -92,6 +93,11 @@ public:
92 const SegmentMap *source_table, 93 const SegmentMap *source_table,
93 MTRandom *rand); 94 MTRandom *rand);
94 95
96 static void OverwriteChange(const Change &ch,
97 SegmentMap *table,
98 const SegmentMap *source_table,
99 MTRandom *rand);
100
95 static void CopyChange(const Change &ch, 101 static void CopyChange(const Change &ch,
96 SegmentMap *table, 102 SegmentMap *table,
97 const SegmentMap *source_table, 103 const SegmentMap *source_table,
@@ -124,6 +130,7 @@ void ChangeListMutator::Mutate(SegmentMap *table,
124 130
125 for (ChangeList::const_iterator iter(cl_.begin()); iter != cl_.end(); ++iter) { 131 for (ChangeList::const_iterator iter(cl_.begin()); iter != cl_.end(); ++iter) {
126 const Change &ch = *iter; 132 const Change &ch = *iter;
133 tmp.clear();
127 Mutate(ch, &tmp, source_table, rand); 134 Mutate(ch, &tmp, source_table, rand);
128 tmp.swap(*table); 135 tmp.swap(*table);
129 source_table = table; 136 source_table = table;
@@ -150,6 +157,9 @@ void ChangeListMutator::Mutate(const Change &ch,
150 case Change::MOVE: 157 case Change::MOVE:
151 MoveChange(ch, table, source_table, rand); 158 MoveChange(ch, table, source_table, rand);
152 break; 159 break;
160 case Change::OVERWRITE:
161 OverwriteChange(ch, table, source_table, rand);
162 break;
153 } 163 }
154} 164}
155 165
@@ -311,6 +321,17 @@ void ChangeListMutator::MoveChange(const Change &ch,
311 DeleteChange(d, table, &tmp, rand); 321 DeleteChange(d, table, &tmp, rand);
312} 322}
313 323
324void ChangeListMutator::OverwriteChange(const Change &ch,
325 SegmentMap *table,
326 const SegmentMap *source_table,
327 MTRandom *rand) {
328 SegmentMap tmp;
329 CHECK_NE(ch.addr1, ch.addr2);
330 CopyChange(ch, &tmp, source_table, rand);
331 Change d(Change::DELETE, ch.size, ch.addr2 + ch.size);
332 DeleteChange(d, table, &tmp, rand);
333}
334
314void ChangeListMutator::CopyChange(const Change &ch, 335void ChangeListMutator::CopyChange(const Change &ch,
315 SegmentMap *table, 336 SegmentMap *table,
316 const SegmentMap *source_table, 337 const SegmentMap *source_table,
diff --git a/xdelta3/testing/regtest.cc b/xdelta3/testing/regtest.cc
index fe4983b..d615196 100644
--- a/xdelta3/testing/regtest.cc
+++ b/xdelta3/testing/regtest.cc
@@ -29,6 +29,7 @@ struct TestOptions {
29 size_t large_cksum_size; 29 size_t large_cksum_size;
30}; 30};
31 31
32// TODO! the smatcher setup isn't working,
32void InMemoryEncodeDecode(const TestOptions &options, 33void InMemoryEncodeDecode(const TestOptions &options,
33 FileSpec &source_file, 34 FileSpec &source_file,
34 FileSpec &target_file, 35 FileSpec &target_file,
@@ -173,7 +174,9 @@ void InMemoryEncodeDecode(const TestOptions &options,
173 } 174 }
174 } 175 }
175 176
176 xd3_close_stream(&encode_stream); 177 CHECK_EQ(0, xd3_close_stream(&decode_stream));
178 CHECK_EQ(0, xd3_close_stream(&encode_stream));
179 xd3_free_stream(&encode_stream);
177 xd3_free_stream(&decode_stream); 180 xd3_free_stream(&decode_stream);
178} 181}
179 182
@@ -477,6 +480,7 @@ void TestMoveMutator() {
477} 480}
478 481
479void FindCksumCollision() { 482void FindCksumCollision() {
483 // TODO! This is not being used.
480 if (golden_cksum_bytes[0] != 0) { 484 if (golden_cksum_bytes[0] != 0) {
481 CHECK(memcmp(golden_cksum_bytes, collision_cksum_bytes, CKSUM_SIZE) != 0); 485 CHECK(memcmp(golden_cksum_bytes, collision_cksum_bytes, CKSUM_SIZE) != 0);
482 CHECK_EQ(xd3_lcksum(golden_cksum_bytes, CKSUM_SIZE), 486 CHECK_EQ(xd3_lcksum(golden_cksum_bytes, CKSUM_SIZE),
@@ -516,56 +520,80 @@ void FindCksumCollision() {
516 b2.Print(); 520 b2.Print();
517} 521}
518 522
519void TestNonBlockingCollisionProgress() { 523void TestOverwriteMutator() {
520 MTRandom rand; 524 MTRandom rand;
521 FileSpec spec0(&rand); 525 FileSpec spec0(&rand);
522 FileSpec spec1(&rand); 526 FileSpec spec1(&rand);
523 FileSpec spec2(&rand);
524 TestOptions options; 527 TestOptions options;
525 528
526 FindCksumCollision(); 529 spec0.GenerateFixedSize(Constants::BLOCK_SIZE);
527 530
528 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 2); 531 ChangeList cl1;
532 cl1.push_back(Change(Change::OVERWRITE, 10, 0, 20));
533 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
534 CHECK_EQ(spec0.Size(), spec1.Size());
529 535
530 Segment gcksum(CKSUM_SIZE, golden_cksum_bytes); 536 Block b0, b1;
531 Segment ccksum(CKSUM_SIZE, collision_cksum_bytes); 537 BlockIterator(spec0).Get(&b0);
538 BlockIterator(spec1).Get(&b1);
539
540 CHECK(memcmp(b0.Data(), b1.Data() + 20, 10) == 0);
541 CHECK(memcmp(b0.Data(), b1.Data(), 20) == 0);
542 CHECK(memcmp(b0.Data() + 30, b1.Data() + 30, Constants::BLOCK_SIZE - 30) == 0);
532 543
533 uint32_t v1 = xd3_lcksum(golden_cksum_bytes, CKSUM_SIZE); 544 cl1.clear();
534 uint32_t v2 = xd3_lcksum(collision_cksum_bytes, CKSUM_SIZE); 545 cl1.push_back(Change(Change::OVERWRITE, 10, 20, (xoff_t)0));
535 CHECK_EQ(v1, v2); 546 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
547 CHECK_EQ(spec0.Size(), spec1.Size());
536 548
537 // TODO! the smatcher setup isn't working, the default is 9/3 549 BlockIterator(spec0).Get(&b0);
550 BlockIterator(spec1).Get(&b1);
551
552 CHECK(memcmp(b0.Data() + 20, b1.Data(), 10) == 0);
553 CHECK(memcmp(b0.Data() + 10, b1.Data() + 10, Constants::BLOCK_SIZE - 10) == 0);
554}
538 555
539 // Place the collision just before the 1st block end, place the 556void TestNonBlockingProgress() {
540 // matching bytes at the start of the next block. Note that 557 MTRandom rand;
541 // source checksums are computed in reverse from the end of the 558 FileSpec spec0(&rand);
542 // block, so placing the collision at the end of the block works. 559 FileSpec spec1(&rand);
560 FileSpec spec2(&rand);
561 TestOptions options;
543 562
544 xoff_t false_offset = Constants::BLOCK_SIZE - CKSUM_SIZE; 563 // TODO! this test assumes block_size == 128
545 xoff_t modify_offset = Constants::BLOCK_SIZE + 20;
546 564
547 // Fixed content for a backward match prior to the collision, which 565 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 2);
548 // makes the total match length long enough for the min_match.
549 Segment preamble(20, 0xabcd);
550 566
551 Change c1(Change::MODIFY, CKSUM_SIZE, false_offset, &gcksum); 567 // This is a lazy target match
552 Change c2(Change::MODIFY, CKSUM_SIZE, modify_offset, &ccksum); 568 Change ct(Change::OVERWRITE, 22,
569 Constants::BLOCK_SIZE + 50,
570 Constants::BLOCK_SIZE + 20);
553 571
554 Change cp1(Change::MODIFY, 20, false_offset - 20, &preamble); 572 // This is a source match just after the block boundary, shorter
555 Change cp2(Change::MODIFY, 20, modify_offset - 20, &preamble); 573 // than the lazy target match.
574 Change cs1(Change::OVERWRITE, 16,
575 Constants::BLOCK_SIZE + 51,
576 Constants::BLOCK_SIZE - 1);
556 577
557 ChangeList cl1; 578 // This overwrites the original source bytes.
558 ChangeList cl2; 579 Change cs2(Change::MODIFY, 108,
559 cl1.push_back(c1); 580 Constants::BLOCK_SIZE + 20);
560 cl1.push_back(cp1);
561 cl2.push_back(c2);
562 cl2.push_back(cp2);
563 581
564 spec0.ModifyTo(ChangeListMutator(cl1), &spec1); 582 // This changes the first blocks
565 spec0.ModifyTo(ChangeListMutator(cl2), &spec2); 583 Change c1st(Change::MODIFY, Constants::BLOCK_SIZE - 2, 0);
584
585 ChangeList csl;
586 csl.push_back(cs1);
587 csl.push_back(cs2);
588 csl.push_back(c1st);
589
590 spec0.ModifyTo(ChangeListMutator(csl), &spec1);
591
592 ChangeList ctl;
593 ctl.push_back(ct);
594 ctl.push_back(c1st);
566 595
567 spec1.Print(); 596 spec0.ModifyTo(ChangeListMutator(ctl), &spec2);
568 spec2.Print();
569 597
570 InMemoryEncodeDecode(options, spec1, spec2, NULL); 598 InMemoryEncodeDecode(options, spec1, spec2, NULL);
571} 599}
@@ -574,15 +602,16 @@ void TestNonBlockingCollisionProgress() {
574 602
575int main(int argc, char **argv) { 603int main(int argc, char **argv) {
576#define TEST(x) cerr << #x << "..." << endl; x() 604#define TEST(x) cerr << #x << "..." << endl; x()
577// TEST(TestRandomNumbers); 605 TEST(TestRandomNumbers);
578// TEST(TestRandomFile); 606 TEST(TestRandomFile);
579// TEST(TestFirstByte); 607 TEST(TestFirstByte);
580// TEST(TestModifyMutator); 608 TEST(TestModifyMutator);
581// TEST(TestAddMutator); 609 TEST(TestAddMutator);
582// TEST(TestDeleteMutator); 610 TEST(TestDeleteMutator);
583// TEST(TestCopyMutator); 611 TEST(TestCopyMutator);
584// TEST(TestMoveMutator); 612 TEST(TestMoveMutator);
585 TEST(TestNonBlockingCollisionProgress); 613 TEST(TestOverwriteMutator);
614 TEST(TestNonBlockingProgress);
586 return 0; 615 return 0;
587} 616}
588 617
diff --git a/xdelta3/xdelta3.c b/xdelta3/xdelta3.c
index c2061a0..bdf02af 100644
--- a/xdelta3/xdelta3.c
+++ b/xdelta3/xdelta3.c
@@ -2103,7 +2103,7 @@ xd3_alloc (xd3_stream *stream,
2103 if (a != NULL) 2103 if (a != NULL)
2104 { 2104 {
2105 IF_DEBUG (stream->alloc_cnt += 1); 2105 IF_DEBUG (stream->alloc_cnt += 1);
2106 IF_DEBUG1 (DP(RINT "[stream %p malloc] size %u ptr %p\n", 2106 IF_DEBUG2 (DP(RINT "[stream %p malloc] size %u ptr %p\n",
2107 stream, elts * size, a)); 2107 stream, elts * size, a));
2108 } 2108 }
2109 else 2109 else
@@ -2122,7 +2122,7 @@ xd3_free (xd3_stream *stream,
2122 { 2122 {
2123 IF_DEBUG (stream->free_cnt += 1); 2123 IF_DEBUG (stream->free_cnt += 1);
2124 XD3_ASSERT (stream->free_cnt <= stream->alloc_cnt); 2124 XD3_ASSERT (stream->free_cnt <= stream->alloc_cnt);
2125 IF_DEBUG1 (DP(RINT "[stream %p free] %p\n", 2125 IF_DEBUG2 (DP(RINT "[stream %p free] %p\n",
2126 stream, ptr)); 2126 stream, ptr));
2127 stream->free (stream->opaque, ptr); 2127 stream->free (stream->opaque, ptr);
2128 } 2128 }
@@ -3228,7 +3228,7 @@ xd3_emit_single (xd3_stream *stream, xd3_rinst *single, usize_t code)
3228 3228
3229 IF_DEBUG1 (DP(RINT "[emit1] %u %s (%u) code %u\n", 3229 IF_DEBUG1 (DP(RINT "[emit1] %u %s (%u) code %u\n",
3230 single->pos, 3230 single->pos,
3231 xd3_rtype_to_string (single->type, 0), 3231 xd3_rtype_to_string ((xd3_rtype) single->type, 0),
3232 single->size, 3232 single->size,
3233 code)); 3233 code));
3234 3234
@@ -3266,9 +3266,9 @@ xd3_emit_double (xd3_stream *stream, xd3_rinst *first,
3266 3266
3267 IF_DEBUG1 (DP(RINT "[emit2]: %u %s (%u) %s (%u) code %u\n", 3267 IF_DEBUG1 (DP(RINT "[emit2]: %u %s (%u) %s (%u) code %u\n",
3268 first->pos, 3268 first->pos,
3269 xd3_rtype_to_string (first->type, 0), 3269 xd3_rtype_to_string ((xd3_rtype) first->type, 0),
3270 first->size, 3270 first->size,
3271 xd3_rtype_to_string (second->type, 0), 3271 xd3_rtype_to_string ((xd3_rtype) second->type, 0),
3272 second->size, 3272 second->size,
3273 code)); 3273 code));
3274 3274
@@ -4329,10 +4329,14 @@ xd3_source_match_setup (xd3_stream *stream, xoff_t srcpos)
4329 stream->match_back = 0; 4329 stream->match_back = 0;
4330 stream->match_fwd = 0; 4330 stream->match_fwd = 0;
4331 4331
4332 /* This avoids a loop. TODO: if ever duplicates are added to the 4332 /* This avoids a non-blocking endless loop caused by scanning
4333 * source hash table, this logic won't suffice to avoid loops. 4333 * backwards across a block boundary, only to find not enough
4334 * TODO: how can you test this in a unittest? Need to search for 4334 * matching bytes to beat the current min_match due to a better lazy
4335 * hash collisions, I suppose. */ 4335 * target match: the re-entry to xd3_string_match() repeats the same
4336 * long match because the input position hasn't changed. TODO: if
4337 * ever duplicates are added to the source hash table, this logic
4338 * won't suffice to avoid loops. See testing/regtest.cc's
4339 * TestNonBlockingProgress test! */
4336 if (srcpos != 0 && srcpos == stream->match_last_srcpos) 4340 if (srcpos != 0 && srcpos == stream->match_last_srcpos)
4337 { 4341 {
4338 goto bad; 4342 goto bad;