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