aboutsummaryrefslogtreecommitdiff
path: root/address_translator_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'address_translator_unittest.cc')
-rw-r--r--address_translator_unittest.cc556
1 files changed, 556 insertions, 0 deletions
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 <algorithm>
+#include <string>
+#include <utility>
+
+#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<std::string>& specs) {
+ std::vector<Unit> 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<offset_t>(first_non_blank(s1)),
+ static_cast<offset_t>(count_non_special(s1)),
+ static_cast<rva_t>(first_non_blank(s2)),
+ static_cast<rva_t>(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<std::string>& 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<TestCase>& 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<AddressTranslator::Unit>()));
+ 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<TwoUnitOverlapTester::TestCase> 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<TwoUnitOverlapTester::TestCase> 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<std::string> 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<std::string> 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