// 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 VCDiffCodeTableWriter, found in encodetable.h. #include <config.h> #include "encodetable.h" #include <string.h> // strlen #include <algorithm> #include <string> #include <vector> #include "addrcache.h" // VCDiffAddressCache::kDefaultNearCacheSize #include "checksum.h" #include "codetable.h" #include "google/output_string.h" #include "testing.h" #include "vcdiff_defs.h" namespace open_vcdiff { namespace { class CodeTableWriterTest : public testing::Test { protected: typedef std::string string; CodeTableWriterTest() : standard_writer(false), interleaved_writer(true), exercise_writer(true, VCDiffAddressCache::kDefaultNearCacheSize, VCDiffAddressCache::kDefaultSameCacheSize, *g_exercise_code_table_, kLastExerciseMode), output_string(&out), out_index(0) { } virtual ~CodeTableWriterTest() { } 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++); } } // This is a CHECK rather than an EXPECT because it validates only // the logic of the test, not of the code being tested. CHECK_EQ(VCDiffCodeTableData::kCodeTableSize, opcode); EXPECT_TRUE(g_exercise_code_table_->Validate(kLastExerciseMode)); } static void TearDownTestCase() { delete g_exercise_code_table_; } void ExpectByte(unsigned char b) { EXPECT_EQ(b, static_cast<unsigned char>(out[out_index])); ++out_index; } void ExpectString(const char* s) { const size_t size = strlen(s); // don't include terminating NULL char EXPECT_EQ(string(s, size), string(out.data() + out_index, size)); out_index += size; } void ExpectNoMoreBytes() { EXPECT_EQ(out_index, out.size()); } static bool AnyMatch(int match_count) { return match_count != 0; } static void ExpectNoMatchesForWriter(const VCDiffCodeTableWriter& writer) { const std::vector<int>& match_counts = writer.match_counts(); EXPECT_TRUE(find_if(match_counts.begin(), match_counts.end(), AnyMatch) == match_counts.end()); } void ExpectNoMatches() const { ExpectNoMatchesForWriter(standard_writer); ExpectNoMatchesForWriter(interleaved_writer); ExpectNoMatchesForWriter(exercise_writer); } // 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; // 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_; // The code table writer for standard encoding, default code table. VCDiffCodeTableWriter standard_writer; // The code table writer for interleaved encoding, default code table. VCDiffCodeTableWriter interleaved_writer; // The code table writer corresponding to g_exercise_code_table_ // (interleaved encoding). VCDiffCodeTableWriter exercise_writer; // Destination for VCDiffCodeTableWriter::Output() string out; OutputString<string> output_string; size_t out_index; }; VCDiffCodeTableData* CodeTableWriterTest::g_exercise_code_table_; #ifdef GTEST_HAS_DEATH_TEST typedef CodeTableWriterTest CodeTableWriterDeathTest; #endif // GTEST_HAS_DEATH_TEST #ifdef GTEST_HAS_DEATH_TEST TEST_F(CodeTableWriterDeathTest, WriterAddWithoutInit) { #ifndef NDEBUG // This condition is only checked in the debug build. EXPECT_DEBUG_DEATH(standard_writer.Add("Hello", 5), "Init"); #endif // !NDEBUG } TEST_F(CodeTableWriterDeathTest, WriterRunWithoutInit) { #ifndef NDEBUG // This condition is only checked in the debug build. EXPECT_DEBUG_DEATH(standard_writer.Run(3, 'a'), "Init"); #endif // !NDEBUG } TEST_F(CodeTableWriterDeathTest, WriterCopyWithoutInit) { #ifndef NDEBUG // This condition is only checked in the debug build. EXPECT_DEBUG_DEATH(standard_writer.Copy(6, 5), "Init"); #endif // !NDEBUG } #endif // GTEST_HAS_DEATH_TEST // Output() without Init() is harmless, but will produce no output. TEST_F(CodeTableWriterTest, WriterOutputWithoutInit) { standard_writer.Output(&output_string); EXPECT_TRUE(out.empty()); } TEST_F(CodeTableWriterTest, WriterEncodeNothing) { EXPECT_TRUE(standard_writer.Init(0)); standard_writer.Output(&output_string); // The writer should know not to append a delta file window // if nothing was encoded. EXPECT_TRUE(out.empty()); out.clear(); EXPECT_TRUE(interleaved_writer.Init(0x10)); interleaved_writer.Output(&output_string); EXPECT_TRUE(out.empty()); out.clear(); EXPECT_TRUE(exercise_writer.Init(0x20)); exercise_writer.Output(&output_string); EXPECT_TRUE(out.empty()); ExpectNoMatches(); } TEST_F(CodeTableWriterTest, StandardWriterEncodeAdd) { EXPECT_TRUE(standard_writer.Init(0x11)); standard_writer.Add("foo", 3); standard_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x09); // Length of the delta encoding ExpectByte(0x03); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x03); // length of data for ADDs and RUNs ExpectByte(0x01); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectString("foo"); ExpectByte(0x04); // ADD(3) opcode ExpectNoMoreBytes(); ExpectNoMatches(); } TEST_F(CodeTableWriterTest, ExerciseWriterEncodeAdd) { EXPECT_TRUE(exercise_writer.Init(0x11)); exercise_writer.Add("foo", 3); exercise_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x0A); // Length of the delta encoding ExpectByte(0x03); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x00); // length of data for ADDs and RUNs ExpectByte(0x05); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectByte(0x04); // Opcode: NOOP + ADD(0) ExpectByte(0x03); // Size of ADD (3) ExpectString("foo"); ExpectNoMatches(); } TEST_F(CodeTableWriterTest, StandardWriterEncodeRun) { EXPECT_TRUE(standard_writer.Init(0x11)); standard_writer.Run(3, 'a'); standard_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x08); // Length of the delta encoding ExpectByte(0x03); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x01); // length of data for ADDs and RUNs ExpectByte(0x02); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectByte('a'); ExpectByte(0x00); // RUN(0) opcode ExpectByte(0x03); // Size of RUN (3) ExpectNoMoreBytes(); ExpectNoMatches(); } TEST_F(CodeTableWriterTest, ExerciseWriterEncodeRun) { EXPECT_TRUE(exercise_writer.Init(0x11)); exercise_writer.Run(3, 'a'); exercise_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x08); // Length of the delta encoding ExpectByte(0x03); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x00); // length of data for ADDs and RUNs ExpectByte(0x03); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectByte(0x08); // Opcode: NOOP + RUN(0) ExpectByte(0x03); // Size of RUN (3) ExpectByte('a'); ExpectNoMoreBytes(); ExpectNoMatches(); } TEST_F(CodeTableWriterTest, StandardWriterEncodeCopy) { EXPECT_TRUE(standard_writer.Init(0x11)); standard_writer.Copy(2, 8); standard_writer.Copy(2, 8); standard_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x09); // Length of the delta encoding ExpectByte(0x10); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x00); // length of data for ADDs and RUNs ExpectByte(0x02); // length of instructions section ExpectByte(0x02); // length of addresses for COPYs ExpectByte(0x18); // COPY mode SELF, size 8 ExpectByte(0x78); // COPY mode SAME(0), size 8 ExpectByte(0x02); // COPY address (2) ExpectByte(0x02); // COPY address (2) ExpectNoMoreBytes(); EXPECT_LE(9U, standard_writer.match_counts().size()); EXPECT_EQ(0, standard_writer.match_counts()[0]); EXPECT_EQ(0, standard_writer.match_counts()[1]); EXPECT_EQ(0, standard_writer.match_counts()[2]); EXPECT_EQ(0, standard_writer.match_counts()[3]); EXPECT_EQ(0, standard_writer.match_counts()[4]); EXPECT_EQ(0, standard_writer.match_counts()[5]); EXPECT_EQ(0, standard_writer.match_counts()[6]); EXPECT_EQ(0, standard_writer.match_counts()[7]); EXPECT_EQ(2, standard_writer.match_counts()[8]); } // The exercise code table can't be used to test how the code table // writer encodes COPY instructions because the code table writer // always uses the default cache sizes, which exceed the maximum mode // used in the exercise table. TEST_F(CodeTableWriterTest, InterleavedWriterEncodeCopy) { EXPECT_TRUE(interleaved_writer.Init(0x11)); interleaved_writer.Copy(2, 8); interleaved_writer.Copy(2, 8); interleaved_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x09); // Length of the delta encoding ExpectByte(0x10); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x00); // length of data for ADDs and RUNs ExpectByte(0x04); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectByte(0x18); // COPY mode SELF, size 8 ExpectByte(0x02); // COPY address (2) ExpectByte(0x78); // COPY mode SAME(0), size 8 ExpectByte(0x02); // COPY address (2) ExpectNoMoreBytes(); EXPECT_LE(9U, interleaved_writer.match_counts().size()); EXPECT_EQ(0, interleaved_writer.match_counts()[0]); EXPECT_EQ(0, interleaved_writer.match_counts()[1]); EXPECT_EQ(0, interleaved_writer.match_counts()[2]); EXPECT_EQ(0, interleaved_writer.match_counts()[3]); EXPECT_EQ(0, interleaved_writer.match_counts()[4]); EXPECT_EQ(0, interleaved_writer.match_counts()[5]); EXPECT_EQ(0, interleaved_writer.match_counts()[6]); EXPECT_EQ(0, interleaved_writer.match_counts()[7]); EXPECT_EQ(2, interleaved_writer.match_counts()[8]); } TEST_F(CodeTableWriterTest, StandardWriterEncodeCombo) { EXPECT_TRUE(standard_writer.Init(0x11)); standard_writer.Add("rayo", 4); standard_writer.Copy(2, 5); standard_writer.Copy(0, 4); standard_writer.Add("X", 1); standard_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x0E); // Length of the delta encoding ExpectByte(0x0E); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x05); // length of data for ADDs and RUNs ExpectByte(0x02); // length of instructions section ExpectByte(0x02); // length of addresses for COPYs ExpectString("rayoX"); ExpectByte(0xAD); // Combo: Add size 4 + COPY mode SELF, size 5 ExpectByte(0xFD); // Combo: COPY mode SAME(0), size 4 + Add size 1 ExpectByte(0x02); // COPY address (2) ExpectByte(0x00); // COPY address (0) ExpectNoMoreBytes(); EXPECT_LE(6U, standard_writer.match_counts().size()); EXPECT_EQ(0, standard_writer.match_counts()[0]); EXPECT_EQ(0, standard_writer.match_counts()[1]); EXPECT_EQ(0, standard_writer.match_counts()[2]); EXPECT_EQ(0, standard_writer.match_counts()[3]); EXPECT_EQ(1, standard_writer.match_counts()[4]); EXPECT_EQ(1, standard_writer.match_counts()[5]); } TEST_F(CodeTableWriterTest, InterleavedWriterEncodeCombo) { EXPECT_TRUE(interleaved_writer.Init(0x11)); interleaved_writer.Add("rayo", 4); interleaved_writer.Copy(2, 5); interleaved_writer.Copy(0, 4); interleaved_writer.Add("X", 1); interleaved_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x0E); // Length of the delta encoding ExpectByte(0x0E); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x00); // length of data for ADDs and RUNs ExpectByte(0x09); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectByte(0xAD); // Combo: Add size 4 + COPY mode SELF, size 5 ExpectString("rayo"); ExpectByte(0x02); // COPY address (2) ExpectByte(0xFD); // Combo: COPY mode SAME(0), size 4 + Add size 1 ExpectByte(0x00); // COPY address (0) ExpectByte('X'); ExpectNoMoreBytes(); EXPECT_LE(6U, interleaved_writer.match_counts().size()); EXPECT_EQ(0, interleaved_writer.match_counts()[0]); EXPECT_EQ(0, interleaved_writer.match_counts()[1]); EXPECT_EQ(0, interleaved_writer.match_counts()[2]); EXPECT_EQ(0, interleaved_writer.match_counts()[3]); EXPECT_EQ(1, interleaved_writer.match_counts()[4]); EXPECT_EQ(1, interleaved_writer.match_counts()[5]); } TEST_F(CodeTableWriterTest, InterleavedWriterEncodeComboWithChecksum) { EXPECT_TRUE(interleaved_writer.Init(0x11)); const VCDChecksum checksum = 0xFFFFFFFF; // would be negative if signed interleaved_writer.AddChecksum(checksum); interleaved_writer.Add("rayo", 4); interleaved_writer.Copy(2, 5); interleaved_writer.Copy(0, 4); interleaved_writer.Add("X", 1); interleaved_writer.Output(&output_string); ExpectByte(VCD_SOURCE | VCD_CHECKSUM); // Win_Indicator ExpectByte(0x11); // Source segment size: dictionary length ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x13); // Length of the delta encoding ExpectByte(0x0E); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x00); // length of data for ADDs and RUNs ExpectByte(0x09); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectByte(0x8F); // checksum byte 1 ExpectByte(0xFF); // checksum byte 2 ExpectByte(0xFF); // checksum byte 3 ExpectByte(0xFF); // checksum byte 4 ExpectByte(0x7F); // checksum byte 5 ExpectByte(0xAD); // Combo: Add size 4 + COPY mode SELF, size 5 ExpectString("rayo"); ExpectByte(0x02); // COPY address (2) ExpectByte(0xFD); // Combo: COPY mode SAME(0), size 4 + Add size 1 ExpectByte(0x00); // COPY address (0) ExpectByte('X'); ExpectNoMoreBytes(); } TEST_F(CodeTableWriterTest, ReallyBigDictionary) { EXPECT_TRUE(interleaved_writer.Init(0x3FFFFFFF)); interleaved_writer.Copy(2, 8); interleaved_writer.Copy(0x3FFFFFFE, 8); interleaved_writer.Output(&output_string); ExpectByte(VCD_SOURCE); // Win_Indicator: VCD_SOURCE (dictionary) ExpectByte(0x83); // Source segment size: dictionary length (1) ExpectByte(0xFF); // Source segment size: dictionary length (2) ExpectByte(0xFF); // Source segment size: dictionary length (3) ExpectByte(0xFF); // Source segment size: dictionary length (4) ExpectByte(0x7F); // Source segment size: dictionary length (5) ExpectByte(0x00); // Source segment position: start of dictionary ExpectByte(0x09); // Length of the delta encoding ExpectByte(0x10); // Size of the target window ExpectByte(0x00); // Delta_indicator (no compression) ExpectByte(0x00); // length of data for ADDs and RUNs ExpectByte(0x04); // length of instructions section ExpectByte(0x00); // length of addresses for COPYs ExpectByte(0x18); // COPY mode SELF, size 8 ExpectByte(0x02); // COPY address (2) ExpectByte(0x28); // COPY mode HERE, size 8 ExpectByte(0x09); // COPY address (9) ExpectNoMoreBytes(); EXPECT_LE(9U, interleaved_writer.match_counts().size()); EXPECT_EQ(0, interleaved_writer.match_counts()[0]); EXPECT_EQ(0, interleaved_writer.match_counts()[1]); EXPECT_EQ(0, interleaved_writer.match_counts()[2]); EXPECT_EQ(0, interleaved_writer.match_counts()[3]); EXPECT_EQ(0, interleaved_writer.match_counts()[4]); EXPECT_EQ(0, interleaved_writer.match_counts()[5]); EXPECT_EQ(0, interleaved_writer.match_counts()[6]); EXPECT_EQ(0, interleaved_writer.match_counts()[7]); EXPECT_EQ(2, interleaved_writer.match_counts()[8]); } #ifdef GTEST_HAS_DEATH_TEST TEST_F(CodeTableWriterDeathTest, DictionaryTooBig) { EXPECT_TRUE(interleaved_writer.Init(0x7FFFFFFF)); interleaved_writer.Copy(2, 8); EXPECT_DEBUG_DEATH(interleaved_writer.Copy(0x7FFFFFFE, 8), "address.*<.*here_address"); } #endif // GTEST_HAS_DEATH_TEST } // unnamed namespace } // namespace open_vcdiff