1 | //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===--------------------------------------------------------------===// |
8 | |
9 | #include "HeaderMapTestUtils.h" |
10 | #include "llvm/ADT/SmallString.h" |
11 | #include "gtest/gtest.h" |
12 | #include <type_traits> |
13 | |
14 | using namespace clang; |
15 | using namespace llvm; |
16 | using namespace clang::test; |
17 | |
18 | namespace { |
19 | |
20 | TEST(HeaderMapTest, checkHeaderEmpty) { |
21 | bool NeedsSwap; |
22 | ASSERT_FALSE(HeaderMapImpl::checkHeader( |
23 | *MemoryBuffer::getMemBufferCopy("" , "empty" ), NeedsSwap)); |
24 | ASSERT_FALSE(HeaderMapImpl::checkHeader( |
25 | *MemoryBuffer::getMemBufferCopy("" , "empty" ), NeedsSwap)); |
26 | } |
27 | |
28 | TEST(HeaderMapTest, checkHeaderMagic) { |
29 | HMapFileMock<1, 1> File; |
30 | File.init(); |
31 | File.Header.Magic = 0; |
32 | bool NeedsSwap; |
33 | ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
34 | } |
35 | |
36 | TEST(HeaderMapTest, checkHeaderReserved) { |
37 | HMapFileMock<1, 1> File; |
38 | File.init(); |
39 | File.Header.Reserved = 1; |
40 | bool NeedsSwap; |
41 | ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
42 | } |
43 | |
44 | TEST(HeaderMapTest, checkHeaderVersion) { |
45 | HMapFileMock<1, 1> File; |
46 | File.init(); |
47 | ++File.Header.Version; |
48 | bool NeedsSwap; |
49 | ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
50 | } |
51 | |
52 | TEST(HeaderMapTest, checkHeaderValidButEmpty) { |
53 | HMapFileMock<1, 1> File; |
54 | File.init(); |
55 | bool NeedsSwap; |
56 | ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
57 | ASSERT_FALSE(NeedsSwap); |
58 | |
59 | File.swapBytes(); |
60 | ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
61 | ASSERT_TRUE(NeedsSwap); |
62 | } |
63 | |
64 | TEST(HeaderMapTest, checkHeader3Buckets) { |
65 | HMapFileMock<3, 1> File; |
66 | ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets)); |
67 | |
68 | File.init(); |
69 | bool NeedsSwap; |
70 | ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
71 | } |
72 | |
73 | TEST(HeaderMapTest, checkHeader0Buckets) { |
74 | // Create with 1 bucket to avoid 0-sized arrays. |
75 | HMapFileMock<1, 1> File; |
76 | File.init(); |
77 | File.Header.NumBuckets = 0; |
78 | bool NeedsSwap; |
79 | ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
80 | } |
81 | |
82 | TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { |
83 | HMapFileMock<1, 1> File; |
84 | File.init(); |
85 | File.Header.NumBuckets = 8; |
86 | bool NeedsSwap; |
87 | ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
88 | } |
89 | |
90 | TEST(HeaderMapTest, lookupFilename) { |
91 | typedef HMapFileMock<2, 7> FileTy; |
92 | FileTy File; |
93 | File.init(); |
94 | |
95 | HMapFileMockMaker<FileTy> Maker(File); |
96 | auto a = Maker.addString(S: "a" ); |
97 | auto b = Maker.addString(S: "b" ); |
98 | auto c = Maker.addString(S: "c" ); |
99 | Maker.addBucket(Str: "a" , Key: a, Prefix: b, Suffix: c); |
100 | |
101 | bool NeedsSwap; |
102 | ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
103 | ASSERT_FALSE(NeedsSwap); |
104 | HeaderMapImpl Map(File.getBuffer(), NeedsSwap); |
105 | |
106 | SmallString<8> DestPath; |
107 | ASSERT_EQ("bc" , Map.lookupFilename("a" , DestPath)); |
108 | } |
109 | |
110 | template <class FileTy, class PaddingTy> struct PaddedFile { |
111 | FileTy File; |
112 | PaddingTy Padding; |
113 | }; |
114 | |
115 | TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) { |
116 | typedef HMapFileMock<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> |
117 | FileTy; |
118 | static_assert(std::is_standard_layout_v<FileTy>, "Expected standard layout" ); |
119 | static_assert(sizeof(FileTy) == 64, "check the math" ); |
120 | PaddedFile<FileTy, uint64_t> P; |
121 | auto &File = P.File; |
122 | auto &Padding = P.Padding; |
123 | File.init(); |
124 | |
125 | HMapFileMockMaker<FileTy> Maker(File); |
126 | auto a = Maker.addString(S: "a" ); |
127 | auto b = Maker.addString(S: "b" ); |
128 | auto c = Maker.addString(S: "c" ); |
129 | Maker.addBucket(Str: "a" , Key: a, Prefix: b, Suffix: c); |
130 | |
131 | // Add 'x' characters to cause an overflow into Padding. |
132 | ASSERT_EQ('c', File.Bytes[5]); |
133 | for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { |
134 | ASSERT_EQ(0, File.Bytes[I]); |
135 | File.Bytes[I] = 'x'; |
136 | } |
137 | Padding = 0xffffffff; // Padding won't stop it either. |
138 | |
139 | bool NeedsSwap; |
140 | ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
141 | ASSERT_FALSE(NeedsSwap); |
142 | HeaderMapImpl Map(File.getBuffer(), NeedsSwap); |
143 | |
144 | // The string for "c" runs to the end of File. Check that the suffix |
145 | // ("cxxxx...") is detected as truncated, and an empty string is returned. |
146 | SmallString<24> DestPath; |
147 | ASSERT_EQ("" , Map.lookupFilename("a" , DestPath)); |
148 | } |
149 | |
150 | TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) { |
151 | typedef HMapFileMock<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> |
152 | FileTy; |
153 | static_assert(std::is_standard_layout_v<FileTy>, "Expected standard layout" ); |
154 | static_assert(sizeof(FileTy) == 64, "check the math" ); |
155 | PaddedFile<FileTy, uint64_t> P; |
156 | auto &File = P.File; |
157 | auto &Padding = P.Padding; |
158 | File.init(); |
159 | |
160 | HMapFileMockMaker<FileTy> Maker(File); |
161 | auto a = Maker.addString(S: "a" ); |
162 | auto c = Maker.addString(S: "c" ); |
163 | auto b = Maker.addString(S: "b" ); // Store the prefix last. |
164 | Maker.addBucket(Str: "a" , Key: a, Prefix: b, Suffix: c); |
165 | |
166 | // Add 'x' characters to cause an overflow into Padding. |
167 | ASSERT_EQ('b', File.Bytes[5]); |
168 | for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { |
169 | ASSERT_EQ(0, File.Bytes[I]); |
170 | File.Bytes[I] = 'x'; |
171 | } |
172 | Padding = 0xffffffff; // Padding won't stop it either. |
173 | |
174 | bool NeedsSwap; |
175 | ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
176 | ASSERT_FALSE(NeedsSwap); |
177 | HeaderMapImpl Map(File.getBuffer(), NeedsSwap); |
178 | |
179 | // The string for "b" runs to the end of File. Check that the prefix |
180 | // ("bxxxx...") is detected as truncated, and an empty string is returned. |
181 | SmallString<24> DestPath; |
182 | ASSERT_EQ("" , Map.lookupFilename("a" , DestPath)); |
183 | } |
184 | |
185 | } // end namespace |
186 | |