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
14using namespace clang;
15using namespace llvm;
16using namespace clang::test;
17
18namespace {
19
20TEST(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
28TEST(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
36TEST(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
44TEST(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
52TEST(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
64TEST(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
73TEST(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
82TEST(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
90TEST(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
110template <class FileTy, class PaddingTy> struct PaddedFile {
111 FileTy File;
112 PaddingTy Padding;
113};
114
115TEST(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
150TEST(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

source code of clang/unittests/Lex/HeaderMapTest.cpp