// Copyright 2008 Google Inc.
// Author: Lincoln Smith
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <config.h>
#include "vcdecoder_test.h"
#include <string.h> // strlen
#include "checksum.h"
#include "codetable.h"
#include "testing.h"
#include "varint_bigendian.h"
#include "vcdiff_defs.h"
namespace open_vcdiff {
const char VCDiffDecoderTest::kStandardFileHeader[] = {
0xD6, // 'V' | 0x80
0xC3, // 'C' | 0x80
0xC4, // 'D' | 0x80
0x00, // Draft standard version number
0x00 // Hdr_Indicator: no custom code table, no compression
};
const char VCDiffDecoderTest::kInterleavedFileHeader[] = {
0xD6, // 'V' | 0x80
0xC3, // 'C' | 0x80
0xC4, // 'D' | 0x80
'S', // SDCH version code
0x00 // Hdr_Indicator: no custom code table, no compression
};
const char VCDiffDecoderTest::kDictionary[] =
"\"Just the place for a Snark!\" the Bellman cried,\n"
"As he landed his crew with care;\n"
"Supporting each man on the top of the tide\n"
"By a finger entwined in his hair.\n";
const char VCDiffDecoderTest::kExpectedTarget[] =
"\"Just the place for a Snark! I have said it twice:\n"
"That alone should encourage the crew.\n"
"Just the place for a Snark! I have said it thrice:\n"
"What I tell you three times is true.\"\n";
VCDiffDecoderTest::VCDiffDecoderTest() : fuzzer_(0), fuzzed_byte_position_(0) {
dictionary_ = kDictionary;
expected_target_ = kExpectedTarget;
}
void VCDiffDecoderTest::SetUp() {
InitializeDeltaFile();
}
void VCDiffDecoderTest::UseStandardFileHeader() {
delta_file_header_.assign(kStandardFileHeader,
sizeof(kStandardFileHeader));
}
void VCDiffDecoderTest::UseInterleavedFileHeader() {
delta_file_header_.assign(kInterleavedFileHeader,
sizeof(kInterleavedFileHeader));
}
void VCDiffDecoderTest::InitializeDeltaFile() {
delta_file_ = delta_file_header_ + delta_window_header_ + delta_window_body_;
}
char VCDiffDecoderTest::GetByteFromStringLength(const char* s,
int which_byte) {
char varint_buf[VarintBE<int32_t>::kMaxBytes];
VarintBE<int32_t>::Encode(static_cast<int32_t>(strlen(s)), varint_buf);
return varint_buf[which_byte];
}
void VCDiffDecoderTest::AddChecksum(VCDChecksum checksum) {
int32_t checksum_as_int32 = static_cast<int32_t>(checksum);
delta_window_header_[0] |= VCD_CHECKSUM;
VarintBE<int32_t>::AppendToString(checksum_as_int32, &delta_window_header_);
// Adjust delta window size to include checksum.
// This method wouldn't work if adding to the length caused the VarintBE
// value to spill over into another byte. Luckily, this test data happens
// not to cause such an overflow.
delta_window_header_[4] += VarintBE<int32_t>::Length(checksum_as_int32);
}
void VCDiffDecoderTest::ComputeAndAddChecksum() {
AddChecksum(ComputeAdler32(expected_target_.data(),
expected_target_.size()));
}
// Write the maximum expressible positive 32-bit VarintBE
// (0x7FFFFFFF) at the given offset in the delta window.
void VCDiffDecoderTest::WriteMaxVarintAtOffset(int offset,
int bytes_to_replace) {
static const char kMaxVarint[] = { 0x87, 0xFF, 0xFF, 0xFF, 0x7F };
delta_file_.replace(delta_file_header_.size() + offset,
bytes_to_replace,
kMaxVarint,
sizeof(kMaxVarint));
}
// Write a negative 32-bit VarintBE (0x80000000) at the given offset
// in the delta window.
void VCDiffDecoderTest::WriteNegativeVarintAtOffset(int offset,
int bytes_to_replace) {
static const char kNegativeVarint[] = { 0x88, 0x80, 0x80, 0x80, 0x00 };
delta_file_.replace(delta_file_header_.size() + offset,
bytes_to_replace,
kNegativeVarint,
sizeof(kNegativeVarint));
}
// Write a VarintBE that has too many continuation bytes
// at the given offset in the delta window.
void VCDiffDecoderTest::WriteInvalidVarintAtOffset(int offset,
int bytes_to_replace) {
static const char kInvalidVarint[] = { 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F };
delta_file_.replace(delta_file_header_.size() + offset,
bytes_to_replace,
kInvalidVarint,
sizeof(kInvalidVarint));
}
bool VCDiffDecoderTest::FuzzOneByteInDeltaFile() {
static const struct Fuzzer {
char _and;
char _or;
char _xor;
} fuzzers[] = {
{ 0xff, 0x80, 0x00 },
{ 0xff, 0xff, 0x00 },
{ 0xff, 0x00, 0x80 },
{ 0xff, 0x00, 0xff },
{ 0xff, 0x01, 0x00 },
{ 0x7f, 0x00, 0x00 },
};
for (; fuzzer_ < (sizeof(fuzzers) / sizeof(fuzzers[0])); ++fuzzer_) {
for (; fuzzed_byte_position_ < delta_file_.size();
++fuzzed_byte_position_) {
char fuzzed_byte = (((delta_file_[fuzzed_byte_position_]
& fuzzers[fuzzer_]._and)
| fuzzers[fuzzer_]._or)
^ fuzzers[fuzzer_]._xor);
if (fuzzed_byte != delta_file_[fuzzed_byte_position_]) {
delta_file_[fuzzed_byte_position_] = fuzzed_byte;
++fuzzed_byte_position_;
return true;
}
}
fuzzed_byte_position_ = 0;
}
return false;
}
const char VCDiffStandardDecoderTest::kWindowHeader[] = {
VCD_SOURCE, // Win_Indicator: take source from dictionary
FirstByteOfStringLength(kDictionary), // Source segment size
SecondByteOfStringLength(kDictionary),
0x00, // Source segment position: start of dictionary
0x79, // Length of the delta encoding
FirstByteOfStringLength(kExpectedTarget), // Size of the target window
SecondByteOfStringLength(kExpectedTarget),
0x00, // Delta_indicator (no compression)
0x64, // length of data for ADDs and RUNs
0x0C, // length of instructions section
0x03 // length of addresses for COPYs
};
const char VCDiffStandardDecoderTest::kWindowBody[] = {
// Data for ADDs: 1st section (length 61)
' ', 'I', ' ', 'h', 'a', 'v', 'e', ' ', 's', 'a', 'i', 'd', ' ',
'i', 't', ' ', 't', 'w', 'i', 'c', 'e', ':', '\n',
'T', 'h', 'a', 't', ' ',
'a', 'l', 'o', 'n', 'e', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ',
'e', 'n', 'c', 'o', 'u', 'r', 'a', 'g', 'e', ' ',
't', 'h', 'e', ' ', 'c', 'r', 'e', 'w', '.', '\n',
// Data for ADDs: 2nd section (length 2)
'h', 'r',
// Data for ADDs: 3rd section (length 9)
'W', 'h', 'a', 't', ' ',
'I', ' ', 't', 'e',
// Data for RUN: 4th section (length 1)
'l',
// Data for ADD: 4th section (length 27)
' ', 'y', 'o', 'u', ' ',
't', 'h', 'r', 'e', 'e', ' ', 't', 'i', 'm', 'e', 's', ' ', 'i', 's', ' ',
't', 'r', 'u', 'e', '.', '\"', '\n',
// Instructions and sizes (length 13)
0x13, // VCD_COPY mode VCD_SELF, size 0
0x1C, // Size of COPY (28)
0x01, // VCD_ADD size 0
0x3D, // Size of ADD (61)
0x23, // VCD_COPY mode VCD_HERE, size 0
0x2C, // Size of COPY (44)
0xCB, // VCD_ADD size 2 + VCD_COPY mode NEAR(1), size 5
0x0A, // VCD_ADD size 9
0x00, // VCD_RUN size 0
0x02, // Size of RUN (2)
0x01, // VCD_ADD size 0
0x1B, // Size of ADD (27)
// Addresses for COPYs (length 3)
0x00, // Start of dictionary
0x58, // HERE mode address for 2nd copy (27+61 back from here_address)
0x2D // NEAR(1) mode address for 2nd copy (45 after prior address)
};
VCDiffStandardDecoderTest::VCDiffStandardDecoderTest() {
UseStandardFileHeader();
delta_window_header_.assign(kWindowHeader, sizeof(kWindowHeader));
delta_window_body_.assign(kWindowBody, sizeof(kWindowBody));
}
const char VCDiffInterleavedDecoderTest::kWindowHeader[] = {
VCD_SOURCE, // Win_Indicator: take source from dictionary
FirstByteOfStringLength(kDictionary), // Source segment size
SecondByteOfStringLength(kDictionary),
0x00, // Source segment position: start of dictionary
0x79, // Length of the delta encoding
FirstByteOfStringLength(kExpectedTarget), // Size of the target window
SecondByteOfStringLength(kExpectedTarget),
0x00, // Delta_indicator (no compression)
0x00, // length of data for ADDs and RUNs (unused)
0x73, // length of interleaved section
0x00 // length of addresses for COPYs (unused)
};
const char VCDiffInterleavedDecoderTest::kWindowBody[] = {
0x13, // VCD_COPY mode VCD_SELF, size 0
0x1C, // Size of COPY (28)
0x00, // Address of COPY: Start of dictionary
0x01, // VCD_ADD size 0
0x3D, // Size of ADD (61)
// Data for ADD (length 61)
' ', 'I', ' ', 'h', 'a', 'v', 'e', ' ', 's', 'a', 'i', 'd', ' ',
'i', 't', ' ', 't', 'w', 'i', 'c', 'e', ':', '\n',
'T', 'h', 'a', 't', ' ',
'a', 'l', 'o', 'n', 'e', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ',
'e', 'n', 'c', 'o', 'u', 'r', 'a', 'g', 'e', ' ',
't', 'h', 'e', ' ', 'c', 'r', 'e', 'w', '.', '\n',
0x23, // VCD_COPY mode VCD_HERE, size 0
0x2C, // Size of COPY (44)
0x58, // HERE mode address (27+61 back from here_address)
0xCB, // VCD_ADD size 2 + VCD_COPY mode NEAR(1), size 5
// Data for ADDs: 2nd section (length 2)
'h', 'r',
0x2D, // NEAR(1) mode address (45 after prior address)
0x0A, // VCD_ADD size 9
// Data for ADDs: 3rd section (length 9)
'W', 'h', 'a', 't', ' ',
'I', ' ', 't', 'e',
0x00, // VCD_RUN size 0
0x02, // Size of RUN (2)
// Data for RUN: 4th section (length 1)
'l',
0x01, // VCD_ADD size 0
0x1B, // Size of ADD (27)
// Data for ADD: 4th section (length 27)
' ', 'y', 'o', 'u', ' ',
't', 'h', 'r', 'e', 'e', ' ', 't', 'i', 'm', 'e', 's', ' ', 'i', 's', ' ',
't', 'r', 'u', 'e', '.', '\"', '\n'
};
VCDiffInterleavedDecoderTest::VCDiffInterleavedDecoderTest() {
UseInterleavedFileHeader();
delta_window_header_.assign(kWindowHeader, sizeof(kWindowHeader));
delta_window_body_.assign(kWindowBody, sizeof(kWindowBody));
}
} // namespace open_vcdiff