xref: /llvm-project/clang/unittests/Lex/HeaderMapTest.cpp (revision 37b530a2ea8bdc28a22a3f8ca701455fb7febdea)
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("a");
97   auto b = Maker.addString("b");
98   auto c = Maker.addString("c");
99   Maker.addBucket("a", a, b, 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<FileTy>::value,
119                 "Expected standard layout");
120   static_assert(sizeof(FileTy) == 64, "check the math");
121   PaddedFile<FileTy, uint64_t> P;
122   auto &File = P.File;
123   auto &Padding = P.Padding;
124   File.init();
125 
126   HMapFileMockMaker<FileTy> Maker(File);
127   auto a = Maker.addString("a");
128   auto b = Maker.addString("b");
129   auto c = Maker.addString("c");
130   Maker.addBucket("a", a, b, c);
131 
132   // Add 'x' characters to cause an overflow into Padding.
133   ASSERT_EQ('c', File.Bytes[5]);
134   for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
135     ASSERT_EQ(0, File.Bytes[I]);
136     File.Bytes[I] = 'x';
137   }
138   Padding = 0xffffffff; // Padding won't stop it either.
139 
140   bool NeedsSwap;
141   ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
142   ASSERT_FALSE(NeedsSwap);
143   HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
144 
145   // The string for "c" runs to the end of File.  Check that the suffix
146   // ("cxxxx...") is detected as truncated, and an empty string is returned.
147   SmallString<24> DestPath;
148   ASSERT_EQ("", Map.lookupFilename("a", DestPath));
149 }
150 
151 TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) {
152   typedef HMapFileMock<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)>
153       FileTy;
154   static_assert(std::is_standard_layout<FileTy>::value,
155                 "Expected standard layout");
156   static_assert(sizeof(FileTy) == 64, "check the math");
157   PaddedFile<FileTy, uint64_t> P;
158   auto &File = P.File;
159   auto &Padding = P.Padding;
160   File.init();
161 
162   HMapFileMockMaker<FileTy> Maker(File);
163   auto a = Maker.addString("a");
164   auto c = Maker.addString("c");
165   auto b = Maker.addString("b"); // Store the prefix last.
166   Maker.addBucket("a", a, b, c);
167 
168   // Add 'x' characters to cause an overflow into Padding.
169   ASSERT_EQ('b', File.Bytes[5]);
170   for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
171     ASSERT_EQ(0, File.Bytes[I]);
172     File.Bytes[I] = 'x';
173   }
174   Padding = 0xffffffff; // Padding won't stop it either.
175 
176   bool NeedsSwap;
177   ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
178   ASSERT_FALSE(NeedsSwap);
179   HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
180 
181   // The string for "b" runs to the end of File.  Check that the prefix
182   // ("bxxxx...") is detected as truncated, and an empty string is returned.
183   SmallString<24> DestPath;
184   ASSERT_EQ("", Map.lookupFilename("a", DestPath));
185 }
186 
187 } // end namespace
188