// 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.
//
// Unit tests for the class VCDiffCodeTableReader, found in decodetable.h.
#include <config.h>
#include "decodetable.h"
#include <stdint.h> // int32_t
#include <vector>
#include "addrcache.h"
#include "codetable.h"
#include "testing.h"
#include "varint_bigendian.h"
namespace open_vcdiff {
namespace {
class DecodeTableTest : public testing::Test {
protected:
DecodeTableTest()
: instructions_and_sizes_(instruction_buffer_size),
found_size_(0),
found_mode_(0) {
instructions_and_sizes_ptr_ = &instructions_and_sizes_[0];
reader_.Init(&instructions_and_sizes_ptr_,
instructions_and_sizes_ptr_ + instruction_buffer_size);
}
static void AddExerciseOpcode(unsigned char inst1,
unsigned char mode1,
unsigned char size1,
unsigned char inst2,
unsigned char mode2,
unsigned char size2,
int opcode) {
g_exercise_code_table_->inst1[opcode] = inst1;
g_exercise_code_table_->mode1[opcode] = mode1;
g_exercise_code_table_->size1[opcode] = (inst1 == VCD_NOOP) ? 0 : size1;
g_exercise_code_table_->inst2[opcode] = inst2;
g_exercise_code_table_->mode2[opcode] = mode2;
g_exercise_code_table_->size2[opcode] = (inst2 == VCD_NOOP) ? 0 : size2;
}
static void SetUpTestCase() {
g_exercise_code_table_ = new VCDiffCodeTableData;
int opcode = 0;
for (unsigned char inst_mode1 = 0;
inst_mode1 <= VCD_LAST_INSTRUCTION_TYPE + kLastExerciseMode;
++inst_mode1) {
unsigned char inst1 = inst_mode1;
unsigned char mode1 = 0;
if (inst_mode1 > VCD_COPY) {
inst1 = VCD_COPY;
mode1 = inst_mode1 - VCD_COPY;
}
for (unsigned char inst_mode2 = 0;
inst_mode2 <= VCD_LAST_INSTRUCTION_TYPE + kLastExerciseMode;
++inst_mode2) {
unsigned char inst2 = inst_mode2;
unsigned char mode2 = 0;
if (inst_mode2 > VCD_COPY) {
inst2 = VCD_COPY;
mode2 = inst_mode2 - VCD_COPY;
}
AddExerciseOpcode(inst1, mode1, 0, inst2, mode2, 0, opcode++);
AddExerciseOpcode(inst1, mode1, 0, inst2, mode2, 255, opcode++);
AddExerciseOpcode(inst1, mode1, 255, inst2, mode2, 0, opcode++);
AddExerciseOpcode(inst1, mode1, 255, inst2, mode2, 255, opcode++);
}
}
CHECK_EQ(VCDiffCodeTableData::kCodeTableSize, opcode);
EXPECT_TRUE(VCDiffCodeTableData::kDefaultCodeTableData.Validate());
EXPECT_TRUE(g_exercise_code_table_->Validate(kLastExerciseMode));
}
static void TearDownTestCase() {
delete g_exercise_code_table_;
}
void VerifyInstModeSize(unsigned char inst,
unsigned char mode,
unsigned char size,
unsigned char opcode) {
if (inst == VCD_NOOP) return; // GetNextInstruction skips NOOPs
int32_t found_size = 0;
unsigned char found_mode = 0;
unsigned char found_inst = reader_.GetNextInstruction(&found_size,
&found_mode);
EXPECT_EQ(inst, found_inst);
EXPECT_EQ(mode, found_mode);
if (size == 0) {
EXPECT_EQ(1000 + opcode, found_size);
} else {
EXPECT_EQ(size, found_size);
}
}
void VerifyInstModeSize1(unsigned char inst,
unsigned char mode,
unsigned char size,
unsigned char opcode) {
if (inst == VCD_NOOP) size = 0;
EXPECT_EQ(g_exercise_code_table_->inst1[opcode], inst);
EXPECT_EQ(g_exercise_code_table_->mode1[opcode], mode);
EXPECT_EQ(g_exercise_code_table_->size1[opcode], size);
VerifyInstModeSize(inst, mode, size, opcode);
}
void VerifyInstModeSize2(unsigned char inst,
unsigned char mode,
unsigned char size,
unsigned char opcode) {
if (inst == VCD_NOOP) size = 0;
EXPECT_EQ(g_exercise_code_table_->inst2[opcode], inst);
EXPECT_EQ(g_exercise_code_table_->mode2[opcode], mode);
EXPECT_EQ(g_exercise_code_table_->size2[opcode], size);
VerifyInstModeSize(inst, mode, size, opcode);
}
// This value is designed so that the total number of inst values and modes
// will equal 8 (VCD_NOOP, VCD_ADD, VCD_RUN, VCD_COPY modes 0 - 4).
// Eight combinations of inst and mode, times two possible size values,
// squared (because there are two instructions per opcode), makes
// exactly 256 possible instruction combinations, which fits kCodeTableSize
// (the number of opcodes in the table.)
static const int kLastExerciseMode = 4;
// The buffer size (in bytes) needed to store kCodeTableSize opcodes plus
// up to kCodeTableSize VarintBE-encoded size values.
static const int instruction_buffer_size;
// A code table that exercises as many combinations as possible:
// 2 instructions, each is a NOOP, ADD, RUN, or one of 5 copy modes
// (== 8 total combinations of inst and mode), and each has
// size == 0 or 255 (2 possibilities.)
static VCDiffCodeTableData* g_exercise_code_table_;
VCDiffCodeTableReader reader_;
// A buffer to which instructions and sizes will be added manually
// in order to exercise VCDiffCodeTableReader.
std::vector<char> instructions_and_sizes_;
// The buffer pointer used by the VCDiffCodeTableReader.
const char* instructions_and_sizes_ptr_;
// The size and mode returned by GetNextInstruction().
int32_t found_size_;
unsigned char found_mode_;
};
VCDiffCodeTableData* DecodeTableTest::g_exercise_code_table_ = NULL;
const int DecodeTableTest::instruction_buffer_size =
VCDiffCodeTableData::kCodeTableSize *
(1 + (VarintBE<VCDAddress>::kMaxBytes));
TEST_F(DecodeTableTest, ReadAdd) {
instructions_and_sizes_[0] = 1;
VarintBE<VCDAddress>::Encode(257, &instructions_and_sizes_[1]);
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(257, found_size_);
EXPECT_EQ(0, found_mode_);
}
TEST_F(DecodeTableTest, ReadRun) {
instructions_and_sizes_[0] = 0;
VarintBE<VCDAddress>::Encode(111, &instructions_and_sizes_[1]);
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_RUN, found_inst);
EXPECT_EQ(111, found_size_);
EXPECT_EQ(0, found_mode_);
}
TEST_F(DecodeTableTest, ReadCopy) {
instructions_and_sizes_[0] = 58;
instructions_and_sizes_[1] = 0;
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(10, found_size_);
EXPECT_EQ(2, found_mode_);
}
TEST_F(DecodeTableTest, ReadAddCopy) {
instructions_and_sizes_[0] = 175;
instructions_and_sizes_[1] = 0;
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(1, found_mode_);
}
TEST_F(DecodeTableTest, ReadCopyAdd) {
instructions_and_sizes_[0] = 255;
instructions_and_sizes_[1] = 0;
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(8, found_mode_);
found_mode_ = 0;
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
}
TEST_F(DecodeTableTest, UnGetAdd) {
instructions_and_sizes_[0] = 1;
instructions_and_sizes_[1] = 255;
VarintBE<VCDAddress>::Encode(257, &instructions_and_sizes_[1]);
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(257, found_size_);
EXPECT_EQ(0, found_mode_);
reader_.UnGetInstruction();
found_size_ = 0;
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(257, found_size_);
EXPECT_EQ(0, found_mode_);
}
TEST_F(DecodeTableTest, UnGetCopy) {
instructions_and_sizes_[0] = 58;
instructions_and_sizes_[1] = 0;
instructions_and_sizes_[2] = 255;
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(10, found_size_);
EXPECT_EQ(2, found_mode_);
reader_.UnGetInstruction();
found_size_ = 0;
found_mode_ = 0;
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(10, found_size_);
EXPECT_EQ(2, found_mode_);
}
TEST_F(DecodeTableTest, UnGetCopyAdd) {
instructions_and_sizes_[0] = 255;
instructions_and_sizes_[1] = 0;
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(8, found_mode_);
reader_.UnGetInstruction();
found_mode_ = 0;
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(8, found_mode_);
found_mode_ = 0;
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
}
TEST_F(DecodeTableTest, UnGetTwice) {
instructions_and_sizes_[0] = 255;
instructions_and_sizes_[1] = 0;
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(8, found_mode_);
reader_.UnGetInstruction();
reader_.UnGetInstruction();
found_mode_ = 0;
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(8, found_mode_);
found_mode_ = 0;
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
}
TEST_F(DecodeTableTest, UnGetBeforeGet) {
instructions_and_sizes_[0] = 255;
instructions_and_sizes_[1] = 0;
reader_.UnGetInstruction();
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(8, found_mode_);
}
TEST_F(DecodeTableTest, UnGetAddCopy) {
instructions_and_sizes_[0] = 175;
instructions_and_sizes_[1] = 0;
unsigned char found_inst = reader_.GetNextInstruction(&found_size_,
&found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
reader_.UnGetInstruction();
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_ADD, found_inst);
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
found_inst = reader_.GetNextInstruction(&found_size_, &found_mode_);
EXPECT_EQ(VCD_COPY, found_inst);
EXPECT_EQ(4, found_size_);
EXPECT_EQ(1, found_mode_);
}
TEST_F(DecodeTableTest, ReReadIncomplete) {
instructions_and_sizes_[0] = 175; // Add(1) + Copy1(4)
instructions_and_sizes_[1] = 1; // Add(0)
instructions_and_sizes_[2] = 111; // with size 111
instructions_and_sizes_[3] = 255; // Copy8(4) + Add(1)
reader_.Init(&instructions_and_sizes_ptr_,
instructions_and_sizes_ptr_ + 0); // 0 bytes available
EXPECT_EQ(VCD_INSTRUCTION_END_OF_DATA,
reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(&instructions_and_sizes_[0], instructions_and_sizes_ptr_);
reader_.Init(&instructions_and_sizes_ptr_,
instructions_and_sizes_ptr_ + 1); // 1 more byte available
EXPECT_EQ(VCD_ADD, reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
EXPECT_EQ(VCD_COPY, reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(4, found_size_);
EXPECT_EQ(1, found_mode_);
EXPECT_EQ(VCD_INSTRUCTION_END_OF_DATA,
reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(&instructions_and_sizes_[1], instructions_and_sizes_ptr_);
reader_.Init(&instructions_and_sizes_ptr_,
instructions_and_sizes_ptr_ + 1); // 1 more byte available
// The opcode is available, but the separately encoded size is not
EXPECT_EQ(VCD_INSTRUCTION_END_OF_DATA,
reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(&instructions_and_sizes_[1], instructions_and_sizes_ptr_);
reader_.Init(&instructions_and_sizes_ptr_,
instructions_and_sizes_ptr_ + 2); // 2 more bytes available
EXPECT_EQ(VCD_ADD, reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(111, found_size_);
EXPECT_EQ(0, found_mode_);
EXPECT_EQ(VCD_INSTRUCTION_END_OF_DATA,
reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(&instructions_and_sizes_[3], instructions_and_sizes_ptr_);
reader_.Init(&instructions_and_sizes_ptr_,
instructions_and_sizes_ptr_ + 1); // 1 more byte available
EXPECT_EQ(VCD_COPY, reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(4, found_size_);
EXPECT_EQ(8, found_mode_);
EXPECT_EQ(VCD_ADD, reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(1, found_size_);
EXPECT_EQ(0, found_mode_);
EXPECT_EQ(VCD_INSTRUCTION_END_OF_DATA,
reader_.GetNextInstruction(&found_size_, &found_mode_));
EXPECT_EQ(&instructions_and_sizes_[4], instructions_and_sizes_ptr_);
}
TEST_F(DecodeTableTest, ExerciseCodeTableReader) {
char* instruction_ptr = &instructions_and_sizes_[0];
for (int opcode = 0; opcode < VCDiffCodeTableData::kCodeTableSize; ++opcode) {
*instruction_ptr = opcode;
++instruction_ptr;
if ((g_exercise_code_table_->inst1[opcode] != VCD_NOOP) &&
(g_exercise_code_table_->size1[opcode] == 0)) {
// A separately-encoded size value
int encoded_size = VarintBE<VCDAddress>::Encode(1000 + opcode,
instruction_ptr);
EXPECT_LT(0, encoded_size);
instruction_ptr += encoded_size;
}
if ((g_exercise_code_table_->inst2[opcode] != VCD_NOOP) &&
(g_exercise_code_table_->size2[opcode] == 0)) {
int encoded_size = VarintBE<VCDAddress>::Encode(1000 + opcode,
instruction_ptr);
EXPECT_LT(0, encoded_size);
instruction_ptr += encoded_size;
}
}
EXPECT_TRUE(reader_.UseCodeTable(*g_exercise_code_table_, kLastExerciseMode));
int opcode = 0;
// This loop has the same bounds as the one in SetUpTestCase.
// Iterate over the instruction types and make sure that the opcodes,
// interpreted in order, return exactly those instruction types.
for (unsigned char inst_mode1 = 0;
inst_mode1 <= VCD_LAST_INSTRUCTION_TYPE + kLastExerciseMode;
++inst_mode1) {
unsigned char inst1 = inst_mode1;
unsigned char mode1 = 0;
if (inst_mode1 > VCD_COPY) {
inst1 = VCD_COPY;
mode1 = inst_mode1 - VCD_COPY;
}
for (unsigned char inst_mode2 = 0;
inst_mode2 <= VCD_LAST_INSTRUCTION_TYPE + kLastExerciseMode;
++inst_mode2) {
unsigned char inst2 = inst_mode2;
unsigned char mode2 = 0;
if (inst_mode2 > VCD_COPY) {
inst2 = VCD_COPY;
mode2 = inst_mode2 - VCD_COPY;
}
VerifyInstModeSize1(inst1, mode1, 0, opcode);
VerifyInstModeSize2(inst2, mode2, 0, opcode);
++opcode;
VerifyInstModeSize1(inst1, mode1, 0, opcode);
VerifyInstModeSize2(inst2, mode2, 255, opcode);
++opcode;
VerifyInstModeSize1(inst1, mode1, 255, opcode);
VerifyInstModeSize2(inst2, mode2, 0, opcode);
++opcode;
VerifyInstModeSize1(inst1, mode1, 255, opcode);
VerifyInstModeSize2(inst2, mode2, 255, opcode);
++opcode;
}
}
CHECK_EQ(VCDiffCodeTableData::kCodeTableSize, opcode);
}
} // unnamed namespace
} // namespace open_vcdiff