// 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