// Copyright 2007 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 "addrcache.h" #include <limits.h> // INT_MAX, INT_MIN #include <stdint.h> // uint32_t #include <stdlib.h> // rand, srand #include <iostream> #include <string> #include <vector> #include "testing.h" #include "varint_bigendian.h" #include "vcdiff_defs.h" // RESULT_ERROR namespace open_vcdiff { namespace { // Provides an address_stream_ buffer and functions to manually encode // values into the buffer, and to manually decode and verify test results // from the buffer. // class VCDiffAddressCacheTest : public testing::Test { public: typedef std::string string; VCDiffAddressCacheTest() : decode_position_(NULL), decode_position_end_(NULL), verify_encode_position_(NULL), last_encode_size_(0), last_decode_position_(NULL) { } virtual ~VCDiffAddressCacheTest() { } virtual void SetUp() { EXPECT_TRUE(cache_.Init()); } // Benchmarks for timing encode/decode operations void BM_Setup(int test_size); void BM_CacheEncode(int iterations, int test_size); void BM_CacheDecode(int iterations, int test_size); protected: virtual void TestBody() { } // to allow instantiation of this class void BeginDecode() { decode_position_ = address_stream_.data(); EXPECT_TRUE(decode_position_ != NULL); last_decode_position_ = decode_position_; decode_position_end_ = decode_position_ + address_stream_.size(); } void ExpectEncodedSizeInBytes(int n) { EXPECT_EQ(last_encode_size_ + n, address_stream_.size()); last_encode_size_ = address_stream_.size(); } void ExpectDecodedSizeInBytes(int n) { EXPECT_EQ(last_decode_position_ + n, decode_position_); last_decode_position_ = decode_position_; } void ManualEncodeVarint(VCDAddress value) { VarintBE<VCDAddress>::AppendToString(value, &address_stream_); } void ManualEncodeByte(unsigned char byte) { address_stream_.push_back(byte); } void ExpectEncodedVarint(VCDAddress expected_value, int expected_size) { if (!verify_encode_position_) { verify_encode_position_ = address_stream_.data(); } EXPECT_EQ(expected_size, VarintBE<VCDAddress>::Length(expected_value)); VCDAddress output_val = VarintBE<VCDAddress>::Parse( address_stream_.data() + address_stream_.size(), &verify_encode_position_); EXPECT_EQ(expected_value, output_val); } void ExpectEncodedByte(unsigned char expected_value) { if (!verify_encode_position_) { verify_encode_position_ = address_stream_.data(); } EXPECT_EQ(expected_value, *verify_encode_position_); ++verify_encode_position_; } void TestEncode(VCDAddress address, VCDAddress here_address, unsigned char mode, int size) { VCDAddress encoded_addr = 0; EXPECT_EQ(mode, cache_.EncodeAddress(address, here_address, &encoded_addr)); if (cache_.WriteAddressAsVarintForMode(mode)) { ManualEncodeVarint(encoded_addr); } else { EXPECT_GT(256, encoded_addr); ManualEncodeByte(static_cast<unsigned char>(encoded_addr)); } ExpectEncodedSizeInBytes(size); } VCDiffAddressCache cache_; string address_stream_; const char* decode_position_; const char* decode_position_end_; string large_address_stream_; std::vector<unsigned char> mode_stream_; std::vector<VCDAddress> verify_stream_; private: const char* verify_encode_position_; string::size_type last_encode_size_; const char* last_decode_position_; }; #ifdef GTEST_HAS_DEATH_TEST // This synonym is needed for the tests that use ASSERT_DEATH typedef VCDiffAddressCacheTest VCDiffAddressCacheDeathTest; #endif // GTEST_HAS_DEATH_TEST // Having either or both cache size == 0 is acceptable TEST_F(VCDiffAddressCacheTest, ZeroCacheSizes) { VCDiffAddressCache zero_cache(0, 0); EXPECT_TRUE(zero_cache.Init()); } TEST_F(VCDiffAddressCacheTest, NegativeCacheSizes) { VCDiffAddressCache negative_cache(-1, -1); // The constructor must not fail EXPECT_FALSE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, OnlySameCacheSizeIsNegative) { VCDiffAddressCache negative_cache(0, -1); // The constructor must not fail EXPECT_FALSE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, ExtremePositiveCacheSizes) { // The constructor must not fail VCDiffAddressCache int_max_cache(INT_MAX, INT_MAX); EXPECT_FALSE(int_max_cache.Init()); } TEST_F(VCDiffAddressCacheTest, ExtremeNegativeCacheSizes) { // The constructor must not fail VCDiffAddressCache int_min_cache(INT_MIN, INT_MIN); EXPECT_FALSE(int_min_cache.Init()); } // VCD_MAX_MODES is the maximum number of modes, including SAME and HERE modes. // So neither the SAME cache nor the HERE cache can be larger than // (VCD_MAX_MODES - 2). TEST_F(VCDiffAddressCacheTest, NearCacheSizeIsTooBig) { VCDiffAddressCache negative_cache(VCD_MAX_MODES - 1, 0); EXPECT_FALSE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, SameCacheSizeIsTooBig) { VCDiffAddressCache negative_cache(0, VCD_MAX_MODES - 1); EXPECT_FALSE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, CombinedSizesAreTooBig) { VCDiffAddressCache negative_cache((VCD_MAX_MODES / 2), (VCD_MAX_MODES / 2) - 1); EXPECT_FALSE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, MaxLegalNearCacheSize) { VCDiffAddressCache negative_cache(VCD_MAX_MODES - 2, 0); EXPECT_TRUE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, MaxLegalSameCacheSize) { VCDiffAddressCache negative_cache(0, VCD_MAX_MODES - 2); EXPECT_TRUE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, MaxLegalCombinedSizes) { VCDiffAddressCache negative_cache((VCD_MAX_MODES / 2) - 1, (VCD_MAX_MODES / 2) - 1); EXPECT_TRUE(negative_cache.Init()); } TEST_F(VCDiffAddressCacheTest, DestroyWithoutInitialization) { VCDiffAddressCache no_init_cache(4, 3); // Should be destroyed without crashing } TEST_F(VCDiffAddressCacheTest, DestroyDefaultWithoutInitialization) { VCDiffAddressCache no_init_cache; // Should be destroyed without crashing } TEST_F(VCDiffAddressCacheTest, CacheContentsInitiallyZero) { VCDAddress test_address = 0; // Check that caches are initially set to zero for (test_address = 0; test_address < 4; ++test_address) { EXPECT_EQ(0, cache_.NearAddress(test_address)); } for (test_address = 0; test_address < 256 * 3; ++test_address) { EXPECT_EQ(0, cache_.SameAddress(test_address)); } } // Inserts values 1, 2, ... , 10 into the cache and tests its entire // contents for consistency. // TEST_F(VCDiffAddressCacheTest, InsertFirstTen) { VCDAddress test_address = 0; for (test_address = 1; test_address <= 10; ++test_address) { cache_.UpdateCache(test_address); } EXPECT_EQ(9, cache_.NearAddress(0)); // slot 0: 1 => 5 => 9 EXPECT_EQ(10, cache_.NearAddress(1)); // slot 1: 2 => 6 => 10 EXPECT_EQ(7, cache_.NearAddress(2)); // slot 2: 3 => 7 EXPECT_EQ(8, cache_.NearAddress(3)); // slot 3: 4 => 8 EXPECT_EQ(0, cache_.SameAddress(0)); for (test_address = 1; test_address <= 10; ++test_address) { EXPECT_EQ(test_address, cache_.SameAddress(test_address)); } for (test_address = 11; test_address < 256 * 3; ++test_address) { EXPECT_EQ(0, cache_.SameAddress(test_address)); } } TEST_F(VCDiffAddressCacheTest, InsertIntMax) { cache_.UpdateCache(INT_MAX); EXPECT_EQ(INT_MAX, cache_.NearAddress(0)); EXPECT_EQ(INT_MAX, cache_.SameAddress(INT_MAX % (256 * 3))); EXPECT_EQ(0, cache_.SameAddress((INT_MAX - 256) % (256 * 3))); EXPECT_EQ(0, cache_.SameAddress((INT_MAX - 512) % (256 * 3))); } // Exercises all four addressing mode types by encoding five values // with EncodeAddress. // Checks to see that the proper mode was selected in each case, // and that the encoding is correct. // TEST_F(VCDiffAddressCacheTest, EncodeAddressModes) { TestEncode(0x0000FFFF, 0x10000000, VCD_SELF_MODE, 3); TestEncode(0x10000000, 0x10000010, VCD_HERE_MODE, 1); TestEncode(0x10000004, 0x10000020, cache_.FirstNearMode() + 0x01, 1); TestEncode(0x0FFFFFFE, 0x10000030, VCD_HERE_MODE, 1); TestEncode(0x10000004, 0x10000040, cache_.FirstSameMode() + 0x01, 1); ExpectEncodedVarint(0xFFFF, 3); // SELF mode: addr 0x0000FFFF ExpectEncodedVarint(0x10, 1); // HERE mode: here - 0x10 = 0x10000000 ExpectEncodedVarint(0x04, 1); // NEAR cache #1: // last addr + 0x4 = 0x10000004 ExpectEncodedVarint(0x32, 1); // HERE mode: here - 0x32 = 0x0FFFFFFE ExpectEncodedByte(0x04); // SAME cache #1: 0x10000004 hits } // Exercises all four addressing mode types by manually encoding six values // and calling DecodeAddress on each one. // TEST_F(VCDiffAddressCacheTest, DecodeAddressModes) { ManualEncodeVarint(0xCAFE); ManualEncodeVarint(0xCAFE); ManualEncodeVarint(0x1000); ManualEncodeByte(0xFE); // SAME mode uses a byte, not a Varint ManualEncodeVarint(0xFE); ManualEncodeVarint(0x1000); BeginDecode(); EXPECT_EQ(0xCAFE, cache_.DecodeAddress(0x10000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xCAFE)); EXPECT_EQ(0x20000 - 0xCAFE, cache_.DecodeAddress(0x20000, VCD_HERE_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xCAFE)); EXPECT_EQ(0xDAFE, cache_.DecodeAddress(0x30000, cache_.FirstNearMode(), &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0x1000)); EXPECT_EQ(0xCAFE, cache_.DecodeAddress(0x40000, cache_.FirstSameMode() + (0xCA % 3), &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(sizeof(unsigned char)); // a byte, not a Varint EXPECT_EQ(0xFE, cache_.DecodeAddress(0x50000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xFE)); // NEAR mode #0 has been overwritten by fifth computed addr (wrap around) EXPECT_EQ(0x10FE, cache_.DecodeAddress(0x60000, cache_.FirstNearMode(), &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0x1000)); } // Test with both cache sizes == 0. The encoder should not choose // a SAME or NEAR mode under these conditions. TEST_F(VCDiffAddressCacheTest, EncodeAddressZeroCacheSizes) { VCDAddress encoded_addr = 0; VCDiffAddressCache zero_cache(0, 0); EXPECT_TRUE(zero_cache.Init()); EXPECT_EQ(VCD_SELF_MODE, zero_cache.EncodeAddress(0x0000FFFF, 0x10000000, &encoded_addr)); EXPECT_EQ(0xFFFF, encoded_addr); EXPECT_EQ(VCD_HERE_MODE, zero_cache.EncodeAddress(0x10000000, 0x10000010, &encoded_addr)); EXPECT_EQ(0x10, encoded_addr); EXPECT_EQ(VCD_HERE_MODE, zero_cache.EncodeAddress(0x10000004, 0x10000020, &encoded_addr)); EXPECT_EQ(0x1C, encoded_addr); EXPECT_EQ(VCD_HERE_MODE, zero_cache.EncodeAddress(0x0FFFFFFE, 0x10000030, &encoded_addr)); EXPECT_EQ(0x32, encoded_addr); EXPECT_EQ(VCD_HERE_MODE, zero_cache.EncodeAddress(0x10000004, 0x10000040, &encoded_addr)); EXPECT_EQ(0x3C, encoded_addr); } TEST_F(VCDiffAddressCacheTest, DecodeAddressZeroCacheSizes) { VCDiffAddressCache zero_cache(0, 0); EXPECT_TRUE(zero_cache.Init()); ManualEncodeVarint(0xCAFE); ManualEncodeVarint(0xCAFE); ManualEncodeVarint(0xDAFE); BeginDecode(); EXPECT_EQ(0xCAFE, zero_cache.DecodeAddress(0x10000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xCAFE)); EXPECT_EQ(0x20000 - 0xCAFE, zero_cache.DecodeAddress(0x20000, VCD_HERE_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xCAFE)); EXPECT_EQ(0xDAFE, zero_cache.DecodeAddress(0x30000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xDAFE)); } #ifdef GTEST_HAS_DEATH_TEST TEST_F(VCDiffAddressCacheDeathTest, EncodeNegativeAddress) { VCDAddress dummy_encoded_address = 0; EXPECT_DEBUG_DEATH(cache_.EncodeAddress(-1, -1, &dummy_encoded_address), "negative"); } TEST_F(VCDiffAddressCacheDeathTest, EncodeAddressPastHereAddress) { VCDAddress dummy_encoded_address = 0; EXPECT_DEBUG_DEATH(cache_.EncodeAddress(0x100, 0x100, &dummy_encoded_address), "address.*<.*here_address"); EXPECT_DEBUG_DEATH(cache_.EncodeAddress(0x200, 0x100, &dummy_encoded_address), "address.*<.*here_address"); } TEST_F(VCDiffAddressCacheDeathTest, DecodeInvalidMode) { ManualEncodeVarint(0xCAFE); BeginDecode(); EXPECT_DEBUG_DEATH(EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000000, cache_.LastMode() + 1, &decode_position_, decode_position_end_)), "mode"); EXPECT_DEBUG_DEATH(EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000000, 0xFF, &decode_position_, decode_position_end_)), "mode"); ExpectDecodedSizeInBytes(0); // Should not modify decode_position_ } TEST_F(VCDiffAddressCacheDeathTest, DecodeZeroOrNegativeHereAddress) { ManualEncodeVarint(0xCAFE); ManualEncodeVarint(0xCAFE); BeginDecode(); // Using a Debug build, the check will fail; using a Release build, // the check will not occur, and the SELF mode does not depend on // the value of here_address, so DecodeAddress() will succeed. EXPECT_DEBUG_DEATH(cache_.DecodeAddress(-1, VCD_SELF_MODE, &decode_position_, decode_position_end_), "negative"); // A zero value for here_address should not kill the decoder, // but instead should return an error value. A delta file may contain // a window that has no source segment and that (erroneously) // uses a COPY instruction as its first instruction. This should // cause an error to be reported, not a debug check failure. EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0, VCD_SELF_MODE, &decode_position_, decode_position_end_)); } #endif // GTEST_HAS_DEATH_TEST TEST_F(VCDiffAddressCacheTest, DecodeAddressPastHereAddress) { ManualEncodeVarint(0xCAFE); BeginDecode(); EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x1000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(0); // Should not modify decode_position_ } TEST_F(VCDiffAddressCacheTest, HereModeAddressTooLarge) { ManualEncodeVarint(0x10001); // here_address + 1 BeginDecode(); EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000, VCD_HERE_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(0); // Should not modify decode_position_ } TEST_F(VCDiffAddressCacheTest, NearModeAddressOverflow) { ManualEncodeVarint(0xCAFE); ManualEncodeVarint(0x7FFFFFFF); BeginDecode(); EXPECT_EQ(0xCAFE, cache_.DecodeAddress(0x10000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xCAFE)); // Now decode a NEAR mode address of base address 0xCAFE // (the first decoded address) + offset 0x7FFFFFFF. This will cause // an integer overflow and should signal an error. EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000000, cache_.FirstNearMode(), &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(0); // Should not modify decode_position_ } // A Varint should contain at most 9 bytes that have their continuation bit // (the uppermost, or 7 bit) set. A longer string of bytes that all have // bit 7 set is not a valid Varint. Try to parse such a string as a Varint // and confirm that it does not run off the end of the input buffer and // it returns an error value (RESULT_ERROR). // TEST_F(VCDiffAddressCacheTest, DecodeInvalidVarint) { address_stream_.clear(); // Write 512 0xFE bytes address_stream_.append(512, static_cast<char>(0xFE)); BeginDecode(); EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(0); // Should not modify decode_position_ } // If only part of a Varint appears in the data to be decoded, // then DecodeAddress should return RESULT_END_OF_DATA, // which means that the Varint *may* be valid if there is more // data expected to be returned. // TEST_F(VCDiffAddressCacheTest, DecodePartialVarint) { address_stream_.clear(); ManualEncodeByte(0xFE); ManualEncodeByte(0xFE); ManualEncodeByte(0xFE); BeginDecode(); EXPECT_EQ(RESULT_END_OF_DATA, cache_.DecodeAddress(0x10000000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(0); // Should not modify decode_position_ // Now add the missing last byte (supposedly read from a stream of data) // and verify that the Varint is now valid. ManualEncodeByte(0x01); // End the Varint with an additional byte BeginDecode(); // Reset read position to start of data EXPECT_EQ(0xFDFBF01, cache_.DecodeAddress(0x10000000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(4); // ManualEncodeByte was called for 4 byte values } #ifdef GTEST_HAS_DEATH_TEST TEST_F(VCDiffAddressCacheDeathTest, DecodeBadMode) { ManualEncodeVarint(0xCAFE); BeginDecode(); EXPECT_DEBUG_DEATH(EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000, cache_.LastMode() + 1, &decode_position_, decode_position_end_)), "maximum"); ExpectDecodedSizeInBytes(0); } #endif // GTEST_HAS_DEATH_TEST TEST_F(VCDiffAddressCacheTest, DecodeInvalidHereAddress) { ManualEncodeVarint(0x10001); // offset larger than here_address BeginDecode(); EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000, VCD_HERE_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(0); } TEST_F(VCDiffAddressCacheTest, DecodeInvalidNearAddress) { ManualEncodeVarint(0xCAFE); ManualEncodeVarint(INT_MAX); // offset will cause integer overflow BeginDecode(); EXPECT_EQ(0xCAFE, cache_.DecodeAddress(0x10000, VCD_SELF_MODE, &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(VarintBE<VCDAddress>::Length(0xCAFE)); EXPECT_EQ(RESULT_ERROR, cache_.DecodeAddress(0x10000, cache_.FirstNearMode(), &decode_position_, decode_position_end_)); ExpectDecodedSizeInBytes(0); } void VCDiffAddressCacheTest::BM_Setup(int test_size) { mode_stream_.resize(test_size); verify_stream_.resize(test_size); VCDAddress here_address = 1; srand(1); for (int i = 0; i < test_size; ++i) { verify_stream_[i] = PortableRandomInRange(here_address - 1); here_address += 4; } BM_CacheEncode(1, test_size); // populate large_address_stream_, mode_stream_ } void VCDiffAddressCacheTest::BM_CacheEncode(int iterations, int test_size) { VCDAddress here_address = 1; VCDAddress encoded_addr = 0; for (int test_iteration = 0; test_iteration < iterations; ++test_iteration) { cache_.Init(); large_address_stream_.clear(); here_address = 1; for (int i = 0; i < test_size; ++i) { const unsigned char mode = cache_.EncodeAddress(verify_stream_[i], here_address, &encoded_addr); if (cache_.WriteAddressAsVarintForMode(mode)) { VarintBE<VCDAddress>::AppendToString(encoded_addr, &large_address_stream_); } else { EXPECT_GT(256, encoded_addr); large_address_stream_.push_back( static_cast<unsigned char>(encoded_addr)); } mode_stream_[i] = mode; here_address += 4; } } } void VCDiffAddressCacheTest::BM_CacheDecode(int iterations, int test_size) { VCDAddress here_address = 1; for (int test_iteration = 0; test_iteration < iterations; ++test_iteration) { cache_.Init(); const char* large_decode_pointer = large_address_stream_.data(); const char* const end_of_encoded_data = large_decode_pointer + large_address_stream_.size(); here_address = 1; for (int i = 0; i < test_size; ++i) { EXPECT_EQ(verify_stream_[i], cache_.DecodeAddress(here_address, mode_stream_[i], &large_decode_pointer, end_of_encoded_data)); here_address += 4; } EXPECT_EQ(end_of_encoded_data, large_decode_pointer); } } TEST_F(VCDiffAddressCacheTest, PerformanceTest) { const int test_size = 20 * 1024; // 20K random encode/decode operations const int num_iterations = 40; // run test 40 times and take average BM_Setup(test_size); { CycleTimer encode_timer; encode_timer.Start(); BM_CacheEncode(num_iterations, test_size); encode_timer.Stop(); double encode_time_in_ms = static_cast<double>(encode_timer.GetInUsec()) / 1000; std::cout << "Time to encode: " << (encode_time_in_ms / num_iterations) << " ms" << std::endl; } { CycleTimer decode_timer; decode_timer.Start(); BM_CacheDecode(num_iterations, test_size); decode_timer.Stop(); double decode_time_in_ms = static_cast<double>(decode_timer.GetInUsec()) / 1000; std::cout << "Time to decode: " << (decode_time_in_ms / num_iterations) << " ms" << std::endl; } } } // unnamed namespace } // namespace open_vcdiff