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