From 06f1ae9aaca969ee95ef840f22b6b461c304542d Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Tue, 13 Mar 2018 18:19:34 +0000 Subject: [Zucchini] Move Zucchini from /chrome/installer/ to /components/. (Use "git log --follow" to see older revisions of files). /components/ is the most logical place to put Zucchini, which only depends on /base and /testing/gtest. This move also enables Zucchini to be used by the Component Updater. Details: - Move all files; run the following to change deps and guards: sed 's/chrome\/installer/components/' *.cc *.h -i sed 's/CHROME_INSTALLER/COMPONENTS/' *.cc *.h -i - Sorting works out pretty well! - Change all 'chrome/installer/zucchini' to 'components/zucchini' throughout other parts of the repo; sort if necessary. - Fix 6 'git cl lint' errors. - Change 1 Bind() usage to BindRepeated(). - Update OWNER. Bug: 729154 Change-Id: I50c5a7d411ea85f707b5994ab319dfb2a1acccf7 Reviewed-on: https://chromium-review.googlesource.com/954923 Reviewed-by: Greg Thompson Reviewed-by: Jochen Eisinger Reviewed-by: Samuel Huang Commit-Queue: Samuel Huang Cr-Commit-Position: refs/heads/master@{#542857} NOKEYCHECK=True GitOrigin-RevId: 577ef6c435e8d43be6e3e60ccbcbd1881780f4ec --- address_translator_unittest.cc | 556 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 address_translator_unittest.cc (limited to 'address_translator_unittest.cc') diff --git a/address_translator_unittest.cc b/address_translator_unittest.cc new file mode 100644 index 0000000..0aeff77 --- /dev/null +++ b/address_translator_unittest.cc @@ -0,0 +1,556 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/zucchini/address_translator.h" + +#include +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace zucchini { + +namespace { + +// Test case structs. The convention of EXPECT() specifies "expectd" value +// before ""actual". However, AddressTranslator interfaces explicitly state "X +// to Y". So it is clearer in test cases to specify "input" before "expect". +struct OffsetToRvaTestCase { + offset_t input; + rva_t expect; +}; + +struct RvaToOffsetTestCase { + rva_t input; + offset_t expect; +}; + +class TestAddressTranslator : public AddressTranslator { + public: + using AddressTranslator::AddressTranslator; + + // Initialize() alternative that parses a visual representation of offset and + // RVA ranges. Illustrative example ("special" means '.' or '!'): + // "..AAA...|....aaaa" => "..AAA..." for offsets, and "....aaaa" for RVAs: + // - "..AAA...": First non-period character is at 2, so |offset_begin| = 2. + // - "..AAA...": There are 3 non-special characters, so |offset_size| = +3. + // - "....aaaa": First non-period character is at 4, so |rva_begin| = 4. + // - "....aaaa": There are 4 non-special characters, so |rva_size| = +4. + // For the special case of length-0 range, '!' can be used. For example, + // "...!...." specifies |begin| = 3 and |size| = +0. + AddressTranslator::Status InitializeWithStrings( + const std::vector& specs) { + std::vector units; + units.reserve(specs.size()); + for (const std::string& s : specs) { + size_t sep = s.find('|'); + CHECK_NE(sep, std::string::npos); + std::string s1 = s.substr(0, sep); + std::string s2 = s.substr(sep + 1); + + auto first_non_blank = [](const std::string& t) { + auto is_blank = [](char ch) { return ch == '.'; }; + return std::find_if_not(t.begin(), t.end(), is_blank) - t.begin(); + }; + auto count_non_special = [](const std::string& t) { + auto is_special = [](char ch) { return ch == '.' || ch == '!'; }; + return t.size() - std::count_if(t.begin(), t.end(), is_special); + }; + units.push_back({static_cast(first_non_blank(s1)), + static_cast(count_non_special(s1)), + static_cast(first_non_blank(s2)), + static_cast(count_non_special(s2))}); + } + return Initialize(std::move(units)); + } +}; + +// Simple test: Initialize TestAddressTranslator using |specs|, and match +// |expected| results re. success or failure. +void SimpleTest(const std::vector& specs, + AddressTranslator::Status expected, + const std::string& case_name) { + TestAddressTranslator translator; + auto result = translator.InitializeWithStrings(specs); + EXPECT_EQ(expected, result) << case_name; +} + +// Test AddressTranslator::Initialize's Unit overlap and error checks over +// multiple test cases, each case consists of a fixed unit (specified as +// string), and a variable string taken from an list. +class TwoUnitOverlapTester { + public: + struct TestCase { + std::string unit_str; + AddressTranslator::Status expected; + }; + + static void RunTest(const std::string& unit_str1, + const std::vector& test_cases) { + for (size_t i = 0; i < test_cases.size(); ++i) { + const auto& test_case = test_cases[i]; + const std::string& unit_str2 = test_case.unit_str; + const std::string str = + base::StringPrintf("Case #%" PRIuS ": %s", i, unit_str2.c_str()); + SimpleTest({unit_str1, unit_str2}, test_case.expected, str); + // Switch order. Expect same results. + SimpleTest({unit_str2, unit_str1}, test_case.expected, str); + } + } +}; + +} // namespace + +TEST(AddressTranslatorTest, Empty) { + using AT = AddressTranslator; + TestAddressTranslator translator; + EXPECT_EQ(AT::kSuccess, + translator.Initialize(std::vector())); + offset_t fake_offset_begin = translator.fake_offset_begin(); + + // Optimized versions. + AddressTranslator::OffsetToRvaCache offset_to_rva(translator); + AddressTranslator::RvaToOffsetCache rva_to_offset(translator); + + EXPECT_EQ(kInvalidRva, translator.OffsetToRva(0U)); + EXPECT_EQ(kInvalidRva, translator.OffsetToRva(100U)); + EXPECT_EQ(kInvalidRva, offset_to_rva.Convert(0U)); + EXPECT_EQ(kInvalidRva, offset_to_rva.Convert(100U)); + + EXPECT_EQ(kInvalidOffset, translator.RvaToOffset(0U)); + EXPECT_EQ(kInvalidOffset, translator.RvaToOffset(100U)); + EXPECT_EQ(kInvalidOffset, rva_to_offset.Convert(0U)); + EXPECT_EQ(kInvalidOffset, rva_to_offset.Convert(100U)); + + EXPECT_EQ(kInvalidRva, translator.OffsetToRva(fake_offset_begin)); + EXPECT_EQ(kInvalidRva, offset_to_rva.Convert(fake_offset_begin)); +} + +TEST(AddressTranslatorTest, Single) { + using AT = AddressTranslator; + TestAddressTranslator translator; + // Offsets to RVA: [10, 30) -> [100, 120). + EXPECT_EQ(AT::kSuccess, translator.Initialize({{10U, +20U, 100U, +20U}})); + offset_t fake_offset_begin = translator.fake_offset_begin(); + + // Optimized versions. + AddressTranslator::OffsetToRvaCache offset_to_rva(translator); + AddressTranslator::RvaToOffsetCache rva_to_offset(translator); + EXPECT_EQ(30U, fake_offset_begin); // Test implementation detail. + + // Offsets to RVAs. + OffsetToRvaTestCase test_cases1[] = { + {0U, kInvalidRva}, {9U, kInvalidRva}, {10U, 100U}, + {20U, 110U}, {29U, 119U}, {30U, kInvalidRva}, + }; + for (auto& test_case : test_cases1) { + EXPECT_EQ(test_case.expect, translator.OffsetToRva(test_case.input)); + EXPECT_EQ(test_case.expect, offset_to_rva.Convert(test_case.input)); + } + + // RVAs to offsets. + RvaToOffsetTestCase test_cases2[] = { + {0U, kInvalidOffset}, {99U, kInvalidOffset}, {100U, 10U}, + {110U, 20U}, {119U, 29U}, {120U, kInvalidOffset}, + }; + for (auto& test_case : test_cases2) { + EXPECT_EQ(test_case.expect, translator.RvaToOffset(test_case.input)); + EXPECT_EQ(test_case.expect, rva_to_offset.Convert(test_case.input)); + } +} + +TEST(AddressTranslatorTest, SingleDanglingRva) { + using AT = AddressTranslator; + TestAddressTranslator translator; + // Offsets to RVA: [10, 30) -> [100, 120 + 7), so has dangling RVAs. + EXPECT_EQ(AT::kSuccess, + translator.Initialize({{10U, +20U, 100U, +20U + 7U}})); + offset_t fake_offset_begin = translator.fake_offset_begin(); + + EXPECT_EQ(30U, fake_offset_begin); // Test implementation detail. + + // Optimized versions. + AddressTranslator::OffsetToRvaCache offset_to_rva(translator); + AddressTranslator::RvaToOffsetCache rva_to_offset(translator); + + // Offsets to RVAs. + OffsetToRvaTestCase test_cases1[] = { + {0U, kInvalidRva}, + {9U, kInvalidRva}, + {10U, 100U}, + {20U, 110U}, + {29U, 119U}, + {30U, kInvalidRva}, + // Fake offsets to dangling RVAs. + {fake_offset_begin + 100U, kInvalidRva}, + {fake_offset_begin + 119U, kInvalidRva}, + {fake_offset_begin + 120U, 120U}, + {fake_offset_begin + 126U, 126U}, + {fake_offset_begin + 127U, kInvalidRva}, + }; + for (auto& test_case : test_cases1) { + EXPECT_EQ(test_case.expect, translator.OffsetToRva(test_case.input)); + EXPECT_EQ(test_case.expect, offset_to_rva.Convert(test_case.input)); + } + + // RVAs to offsets. + RvaToOffsetTestCase test_cases2[] = { + {0U, kInvalidOffset}, + {99U, kInvalidOffset}, + {100U, 10U}, + {110U, 20U}, + {119U, 29U}, + // Dangling RVAs to fake offsets. + {120U, fake_offset_begin + 120U}, + {126U, fake_offset_begin + 126U}, + {127U, kInvalidOffset}, + }; + for (auto& test_case : test_cases2) { + EXPECT_EQ(test_case.expect, translator.RvaToOffset(test_case.input)); + EXPECT_EQ(test_case.expect, rva_to_offset.Convert(test_case.input)); + } +} + +TEST(AddressTranslatorTest, BasicUsage) { + using AT = AddressTranslator; + TestAddressTranslator translator; + // Offsets covered: [10, 30), [40, 70), [70, 110). + // Map to RVAs: [200, 220 + 5), [300, 330), [100, 140), so has dangling RVAs. + auto result = translator.Initialize({ + {10U, +20U, 200U, +20U + 5U}, // Has dangling RVAs. + {40U, +30U, 300U, +20U}, // Extra offset truncated and ignored. + {50U, +20U, 310U, +20U}, // Overlap with previous: Merged. + {70U, +40U, 100U, +20U}, // Tangent with previous but inconsistent; extra + // offset truncated and ignored. + {90U, +20U, 120U, +20U}, // Tangent with previous and consistent: Merged. + }); + EXPECT_EQ(AT::kSuccess, result); + offset_t fake_offset_begin = translator.fake_offset_begin(); + EXPECT_EQ(110U, fake_offset_begin); // Test implementation detail. + + // Optimized versions. + AddressTranslator::OffsetToRvaCache offset_to_rva(translator); + AddressTranslator::RvaToOffsetCache rva_to_offset(translator); + + // Offsets to RVAs. + OffsetToRvaTestCase test_cases1[] = { + {0U, kInvalidRva}, + {9U, kInvalidRva}, + {10U, 200U}, + {20U, 210U}, + {29U, 219U}, + {30U, kInvalidRva}, + {39U, kInvalidRva}, + {40U, 300U}, + {55U, 315U}, + {69U, 329U}, + {70U, 100U}, + {90U, 120U}, + {109U, 139U}, + {110U, kInvalidRva}, + // Fake offsets to dangling RVAs. + {fake_offset_begin + 220U, 220U}, + {fake_offset_begin + 224U, 224U}, + {fake_offset_begin + 225U, kInvalidRva}, + }; + for (auto& test_case : test_cases1) { + EXPECT_EQ(test_case.expect, translator.OffsetToRva(test_case.input)); + EXPECT_EQ(test_case.expect, offset_to_rva.Convert(test_case.input)); + } + + // RVAs to offsets. + RvaToOffsetTestCase test_cases2[] = { + {0U, kInvalidOffset}, + {99U, kInvalidOffset}, + {100U, 70U}, + {120U, 90U}, + {139U, 109U}, + {140U, kInvalidOffset}, + {199U, kInvalidOffset}, + {200U, 10U}, + {210U, 20U}, + {219U, 29U}, + {225U, kInvalidOffset}, + {299U, kInvalidOffset}, + {300U, 40U}, + {315U, 55U}, + {329U, 69U}, + {330U, kInvalidOffset}, + // Dangling RVAs to fake offsets. + {220U, fake_offset_begin + 220U}, + {224U, fake_offset_begin + 224U}, + {225U, kInvalidOffset}, + }; + for (auto& test_case : test_cases2) { + EXPECT_EQ(test_case.expect, translator.RvaToOffset(test_case.input)); + EXPECT_EQ(test_case.expect, rva_to_offset.Convert(test_case.input)); + } +} + +TEST(AddressTranslatorTest, Overflow) { + using AT = AddressTranslator; + // Test assumes that offset_t and rva_t to be 32-bit. + static_assert(sizeof(offset_t) == 4 && sizeof(rva_t) == 4, + "Needs to update test."); + { + AddressTranslator translator1; + EXPECT_EQ(AT::kErrorOverflow, + translator1.Initialize({{0, +0xC0000000U, 0, +0xC0000000U}})); + } + { + AddressTranslator translator2; + EXPECT_EQ(AT::kErrorOverflow, + translator2.Initialize({{0, +0, 0, +0xC0000000U}})); + } + { + // Units are okay, owing to but limitations of the heuristic to convert + // dangling RVA to fake offset, AddressTranslator::Initialize() fails. + AddressTranslator translator3; + EXPECT_EQ(AT::kErrorFakeOffsetBeginTooLarge, + translator3.Initialize( + {{32, +0, 32, +0x50000000U}, {0x50000000U, +16, 0, +16}})); + } +} + +// Sanity test for TestAddressTranslator::InitializeWithStrings(); +TEST(AddressTranslatorTest, AddUnitAsString) { + using AT = AddressTranslator; + { + TestAddressTranslator translator1; + EXPECT_EQ(AT::kSuccess, translator1.InitializeWithStrings({"..A..|.aaa."})); + AddressTranslator::Unit unit1 = translator1.units_sorted_by_offset()[0]; + EXPECT_EQ(2U, unit1.offset_begin); + EXPECT_EQ(+1U, unit1.offset_size); + EXPECT_EQ(1U, unit1.rva_begin); + EXPECT_EQ(+3U, unit1.rva_size); + } + { + TestAddressTranslator translator2; + EXPECT_EQ(AT::kSuccess, + translator2.InitializeWithStrings({".....!...|.bbbbbb..."})); + AddressTranslator::Unit unit2 = translator2.units_sorted_by_offset()[0]; + EXPECT_EQ(5U, unit2.offset_begin); + EXPECT_EQ(+0U, unit2.offset_size); + EXPECT_EQ(1U, unit2.rva_begin); + EXPECT_EQ(+6U, unit2.rva_size); + } +} + +// AddressTranslator::Initialize() lists Unit merging examples in comments. The +// format is different from that used by InitializeWithStrings(), but adapting +// them is easy, so we may as well do so. +TEST(AddressTranslatorTest, OverlapFromComment) { + using AT = AddressTranslator; + constexpr auto OK = AT::kSuccess; + struct { + const char* rva_str; // RVA comes first in this case. + const char* offset_str; + AT::Status expected; + } test_cases[] = { + {"..ssssffff..", "..SSSSFFFF..", OK}, + {"..ssssffff..", "..SSSS..FFFF..", OK}, + {"..ssssffff..", "..FFFF..SSSS..", OK}, + {"..ssssffff..", "..SSOOFF..", AT::kErrorBadOverlap}, + {"..sssooofff..", "..SSSOOOFFF..", OK}, + {"..sssooofff..", "..SSSSSOFFFFF..", AT::kErrorBadOverlap}, + {"..sssooofff..", "..FFOOOOSS..", AT::kErrorBadOverlap}, + {"..sssooofff..", "..SSSOOOF..", OK}, + {"..sssooofff..", "..SSSOOOF..", OK}, + {"..sssooosss..", "..SSSOOOS..", OK}, + {"..sssooofff..", "..SSSOO..", OK}, + {"..sssooofff..", "..SSSOFFF..", AT::kErrorBadOverlapDanglingRva}, + {"..sssooosss..", "..SSSOOSSSS..", AT::kErrorBadOverlapDanglingRva}, + {"..oooooo..", "..OOO..", OK}, + }; + + auto to_period = [](std::string s, char ch) { // |s| passed by value. + std::replace(s.begin(), s.end(), ch, '.'); + return s; + }; + + size_t idx = 0; + for (const auto& test_case : test_cases) { + std::string base_str = + std::string(test_case.offset_str) + "|" + test_case.rva_str; + std::string unit_str1 = to_period(to_period(base_str, 'S'), 's'); + std::string unit_str2 = to_period(to_period(base_str, 'F'), 'f'); + SimpleTest({unit_str1, unit_str2}, test_case.expected, + base::StringPrintf("Case #%" PRIuS, idx)); + ++idx; + } +} + +TEST(AddressTranslatorTest, Overlap) { + using AT = AddressTranslator; + constexpr auto OK = AT::kSuccess; + constexpr const char* unit_str1 = "....AAA.......|.....aaa......"; + + std::vector test_cases = { + //....AAA.......|.....aaa...... The first Unit. NOLINT + {"....BBB.......|.....bbb......", OK}, + {"..BBB.........|...bbb........", OK}, + {"......BBB.....|.......bbb....", OK}, + {"..BBBBBBBBB...|...bbb........", OK}, // Extra offset get truncated. + {"......BBBBBBBB|.......bbb....", OK}, + {"....BBB.......|.......bbb....", AT::kErrorBadOverlap}, + {"..BBB.........|.......bbb....", AT::kErrorBadOverlap}, + {".......BBB....|.......bbb....", AT::kErrorBadOverlap}, + //....AAA.......|.....aaa...... The first Unit. NOLINT + {"....BBB.......|..........bbb.", AT::kErrorBadOverlap}, + {"..........BBB.|.......bbb....", AT::kErrorBadOverlap}, + {"......BBB.....|.....bbb......", AT::kErrorBadOverlap}, + {"......BBB.....|..bbb.........", AT::kErrorBadOverlap}, + {"......BBB.....|bbb...........", AT::kErrorBadOverlap}, + {"BBB...........|bbb...........", OK}, // Disjoint. + {"........BBB...|.........bbb..", OK}, // Disjoint. + {"BBB...........|..........bbb.", OK}, // Disjoint, offset elsewhere. + //....AAA.......|.....aaa...... The first Unit. NOLINT + {".BBB..........|..bbb.........", OK}, // Tangent. + {".......BBB....|........bbb...", OK}, // Tangent. + {".BBB..........|........bbb...", OK}, // Tangent, offset elsewhere. + {"BBBBBB........|bbb...........", OK}, // Repeat, with extra offsets. + {"........BBBB..|.........bbb..", OK}, + {"BBBBBB........|..........bbb.", OK}, + {".BBBBBB.......|..bbb.........", OK}, + {".......BBBBB..|........bbb...", OK}, + //....AAA.......|.....aaa...... The first Unit. NOLINT + {".BBB..........|........bbb...", OK}, // Tangent, offset elsewhere. + {"..BBB.........|........bbb...", AT::kErrorBadOverlap}, + {"...BB.........|....bb........", OK}, + {"....BB........|.....bb.......", OK}, + {".......BB.....|........bb....", OK}, + {"...BBBBBB.....|....bbbbbb....", OK}, + {"..BBBBBB......|...bbbbbb.....", OK}, + {"......BBBBBB..|.......bbbbbb.", OK}, + //....AAA.......|.....aaa...... The first Unit. NOLINT + {"BBBBBBBBBBBBBB|bbbbbbbbbbbbbb", AT::kErrorBadOverlap}, + {"B.............|b.............", OK}, + {"B.............|.............b", OK}, + {"....B.........|.....b........", OK}, + {"....B.........|......b.......", AT::kErrorBadOverlap}, + {"....B.........|......b.......", AT::kErrorBadOverlap}, + {"....BBB.......|.....bb.......", OK}, + {"....BBBB......|.....bbb......", OK}, + //....AAA.......|.....aaa...... The first Unit. NOLINT + {".........BBBBB|.b............", OK}, + {"....AAA.......|.....!........", OK}, + {"....!.........|.....!........", OK}, // Empty units gets deleted early. + {"....!.........|..........!...", OK}, // Forgiving! + }; + + TwoUnitOverlapTester::RunTest(unit_str1, test_cases); +} + +TEST(AddressTranslatorTest, OverlapOffsetMultiple) { + using AT = AddressTranslator; + // Simple case. Note that RVA ranges don't get merged. + SimpleTest({"A..|a....", // + ".A.|..a..", // + "..A|....a"}, + AT::kSuccess, "Case #0"); + + // Offset range 1 overlaps 2 and 3, but truncation takes place to trim down + // offset ranges, so still successful. + SimpleTest({"..A|a....", // + ".AA|..a..", // + "AAA|....a"}, + AT::kSuccess, "Case #1"); + + // Offset range 2 and 3 overlap, so fail. + SimpleTest({"A..|a....", // + ".A.|..a..", // + ".A.|....a"}, + AT::kErrorBadOverlap, "Case #2"); +} + +TEST(AddressTranslatorTest, OverlapDangling) { + using AT = AddressTranslator; + constexpr auto OK = AT::kSuccess; + // First Unit has dangling offsets at + constexpr const char* unit_str1 = "....AAA.......|.....aaaaaa..."; + + std::vector test_cases = { + //....AAA.......|.....aaaaaa... The first Unit. NOLINT + {"....BBB.......|.....bbbbbb...", OK}, + {"....BBB.......|.....bbbbb....", OK}, + {"....BBB.......|.....bbbb.....", OK}, + {"....BBB.......|.....bbb......", OK}, + {".....BBB......|......bbb.....", AT::kErrorBadOverlapDanglingRva}, + {".....BB.......|......bbb.....", OK}, + {"....BBB.......|.....bbbbbbbb.", OK}, + {"..BBBBB.......|...bbbbbbbb...", OK}, + //....AAA.......|.....aaaaaa... The first Unit. NOLINT + {"......!.......|.bbb..........", AT::kErrorBadOverlap}, + {"..BBBBB.......|...bbbbb......", OK}, + {".......BBB....|.bbb..........", OK}, // Just tangent: Can go elsewhere. + {".......BBB....|.bbbb.........", OK}, // Can be another dangling RVA. + {".......!......|.bbbb.........", OK}, // Same with empty. + {"......!.......|.......!......", OK}, // Okay, but gets deleted. + {"......!.......|.......b......", AT::kErrorBadOverlapDanglingRva}, + {"......B.......|.......b......", OK}, + //....AAA.......|.....aaaaaa... The first Unit. NOLINT + {"......BBBB....|.......bbbb...", AT::kErrorBadOverlapDanglingRva}, + {"......BB......|.......bb.....", AT::kErrorBadOverlapDanglingRva}, + {"......BB......|bb............", AT::kErrorBadOverlap}, + }; + + TwoUnitOverlapTester::RunTest(unit_str1, test_cases); +} + +// Tests implementation since algorithm is tricky. +TEST(AddressTranslatorTest, Merge) { + using AT = AddressTranslator; + // Merge a bunch of overlapping Units into one big Unit. + std::vector test_case1 = { + "AAA.......|.aaa......", // Comment to prevent wrap by formatter. + "AA........|.aa.......", // + "..AAA.....|...aaa....", // + "....A.....|.....a....", // + ".....AAA..|......aaa.", // + "........A.|.........a", // + }; + // Try all 6! permutations. + std::sort(test_case1.begin(), test_case1.end()); + do { + TestAddressTranslator translator1; + EXPECT_EQ(AT::kSuccess, translator1.InitializeWithStrings(test_case1)); + EXPECT_EQ(9U, translator1.fake_offset_begin()); + + AT::Unit expected{0U, +9U, 1U, +9U}; + EXPECT_EQ(1U, translator1.units_sorted_by_offset().size()); + EXPECT_EQ(expected, translator1.units_sorted_by_offset()[0]); + EXPECT_EQ(1U, translator1.units_sorted_by_rva().size()); + EXPECT_EQ(expected, translator1.units_sorted_by_rva()[0]); + } while (std::next_permutation(test_case1.begin(), test_case1.end())); + + // Merge RVA-adjacent Units into two Units. + std::vector test_case2 = { + ".....A..|.a......", // First Unit. + "......A.|..a.....", // + "A.......|...a....", // Second Unit: RVA-adjacent to first Unit, but + ".A......|....a...", // offset would become inconsistent, so a new + "..A.....|.....a..", // Unit gets created. + }; + // Try all 5! permutations. + std::sort(test_case2.begin(), test_case2.end()); + do { + TestAddressTranslator translator2; + EXPECT_EQ(AT::kSuccess, translator2.InitializeWithStrings(test_case2)); + EXPECT_EQ(7U, translator2.fake_offset_begin()); + + AT::Unit expected1{0U, +3U, 3U, +3U}; + AT::Unit expected2{5U, +2U, 1U, +2U}; + EXPECT_EQ(2U, translator2.units_sorted_by_offset().size()); + EXPECT_EQ(expected1, translator2.units_sorted_by_offset()[0]); + EXPECT_EQ(expected2, translator2.units_sorted_by_offset()[1]); + EXPECT_EQ(2U, translator2.units_sorted_by_rva().size()); + EXPECT_EQ(expected2, translator2.units_sorted_by_rva()[0]); + EXPECT_EQ(expected1, translator2.units_sorted_by_rva()[1]); + } while (std::next_permutation(test_case2.begin(), test_case2.end())); +} + +} // namespace zucchini -- cgit v1.2.3