1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/zucchini/element_detection.h"
6
7 #include <map>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "components/zucchini/buffer_view.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace zucchini {
15 namespace {
16 // This test uses a mock archive format where regions are determined by their
17 // consecutive byte values rather than parsing real executables.
18 //
19 // 0 - Padding or raw data (not mapped to an executable).
20 // 1 - A Win32x86 executable.
21 // 2 - A Win32x64 executable.
22 //
23 // So an example archive file of;
24 // 0 1 1 1 0 1 1 0 0 2 2 2 2
25 // contains (in order left to right):
26 // - One padding byte
27 // - Three byte Win32x86 executable
28 // - One padding byte
29 // - Two byte Win32x86 executable
30 // - Two padding bytes
31 // - Four byte Win32x64 executable
32
33 class ElementDetectionTest : public ::testing::Test {
34 protected:
35 using ElementVector = std::vector<Element>;
36 using ExeTypeMap = std::map<uint8_t, ExecutableType>;
37
ElementDetectionTest()38 ElementDetectionTest()
39 : exe_map_({{1, kExeTypeWin32X86}, {2, kExeTypeWin32X64}}) {}
40
TestElementFinder(std::vector<uint8_t> buffer)41 ElementVector TestElementFinder(std::vector<uint8_t> buffer) {
42 ConstBufferView image(buffer.data(), buffer.size());
43
44 ElementFinder finder(
45 image,
46 base::BindRepeating(
47 [](ExeTypeMap exe_map, ConstBufferView image,
48 ConstBufferView region) -> std::optional<Element> {
49 EXPECT_GE(region.begin(), image.begin());
50 EXPECT_LE(region.end(), image.end());
51 EXPECT_GE(region.size(), 0U);
52
53 if (region[0] != 0) {
54 offset_t length = 1;
55 while (length < region.size() && region[length] == region[0])
56 ++length;
57 return Element{{0, length}, exe_map[region[0]]};
58 }
59 return std::nullopt;
60 },
61 exe_map_, image));
62 std::vector<Element> elements;
63 for (auto element = finder.GetNext(); element; element = finder.GetNext()) {
64 elements.push_back(*element);
65 }
66 return elements;
67 }
68
69 // Translation map from mock archive bytes to actual types used in Zucchini.
70 ExeTypeMap exe_map_;
71 };
72
TEST_F(ElementDetectionTest,ElementFinderEmpty)73 TEST_F(ElementDetectionTest, ElementFinderEmpty) {
74 std::vector<uint8_t> buffer(10, 0);
75 ElementFinder finder(
76 ConstBufferView(buffer.data(), buffer.size()),
77 base::BindRepeating([](ConstBufferView image) -> std::optional<Element> {
78 return std::nullopt;
79 }));
80 EXPECT_EQ(std::nullopt, finder.GetNext());
81 }
82
TEST_F(ElementDetectionTest,ElementFinder)83 TEST_F(ElementDetectionTest, ElementFinder) {
84 EXPECT_EQ(ElementVector(), TestElementFinder({}));
85 EXPECT_EQ(ElementVector(), TestElementFinder({0, 0}));
86 EXPECT_EQ(ElementVector({{{0, 2}, kExeTypeWin32X86}}),
87 TestElementFinder({1, 1}));
88 EXPECT_EQ(
89 ElementVector({{{0, 2}, kExeTypeWin32X86}, {{2, 2}, kExeTypeWin32X64}}),
90 TestElementFinder({1, 1, 2, 2}));
91 EXPECT_EQ(ElementVector({{{1, 2}, kExeTypeWin32X86}}),
92 TestElementFinder({0, 1, 1, 0}));
93 EXPECT_EQ(
94 ElementVector({{{1, 2}, kExeTypeWin32X86}, {{3, 3}, kExeTypeWin32X64}}),
95 TestElementFinder({0, 1, 1, 2, 2, 2}));
96 EXPECT_EQ(
97 ElementVector({{{1, 2}, kExeTypeWin32X86}, {{4, 3}, kExeTypeWin32X64}}),
98 TestElementFinder({0, 1, 1, 0, 2, 2, 2}));
99 }
100
101 } // namespace
102 } // namespace zucchini
103