| //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===--------------------------------------------------------------===// |
| |
| #include "HeaderMapTestUtils.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "gtest/gtest.h" |
| #include <type_traits> |
| |
| using namespace clang; |
| using namespace llvm; |
| using namespace clang::test; |
| |
| namespace { |
| |
| TEST(HeaderMapTest, checkHeaderEmpty) { |
| bool NeedsSwap; |
| ASSERT_FALSE(HeaderMapImpl::checkHeader( |
| *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap)); |
| ASSERT_FALSE(HeaderMapImpl::checkHeader( |
| *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap)); |
| } |
| |
| TEST(HeaderMapTest, checkHeaderMagic) { |
| HMapFileMock<1, 1> File; |
| File.init(); |
| File.Header.Magic = 0; |
| bool NeedsSwap; |
| ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| } |
| |
| TEST(HeaderMapTest, checkHeaderReserved) { |
| HMapFileMock<1, 1> File; |
| File.init(); |
| File.Header.Reserved = 1; |
| bool NeedsSwap; |
| ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| } |
| |
| TEST(HeaderMapTest, checkHeaderVersion) { |
| HMapFileMock<1, 1> File; |
| File.init(); |
| ++File.Header.Version; |
| bool NeedsSwap; |
| ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| } |
| |
| TEST(HeaderMapTest, checkHeaderValidButEmpty) { |
| HMapFileMock<1, 1> File; |
| File.init(); |
| bool NeedsSwap; |
| ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| ASSERT_FALSE(NeedsSwap); |
| |
| File.swapBytes(); |
| ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| ASSERT_TRUE(NeedsSwap); |
| } |
| |
| TEST(HeaderMapTest, checkHeader3Buckets) { |
| HMapFileMock<3, 1> File; |
| ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets)); |
| |
| File.init(); |
| bool NeedsSwap; |
| ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| } |
| |
| TEST(HeaderMapTest, checkHeader0Buckets) { |
| // Create with 1 bucket to avoid 0-sized arrays. |
| HMapFileMock<1, 1> File; |
| File.init(); |
| File.Header.NumBuckets = 0; |
| bool NeedsSwap; |
| ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| } |
| |
| TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { |
| HMapFileMock<1, 1> File; |
| File.init(); |
| File.Header.NumBuckets = 8; |
| bool NeedsSwap; |
| ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| } |
| |
| TEST(HeaderMapTest, lookupFilename) { |
| typedef HMapFileMock<2, 7> FileTy; |
| FileTy File; |
| File.init(); |
| |
| HMapFileMockMaker<FileTy> Maker(File); |
| auto a = Maker.addString("a"); |
| auto b = Maker.addString("b"); |
| auto c = Maker.addString("c"); |
| Maker.addBucket("a", a, b, c); |
| |
| bool NeedsSwap; |
| ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| ASSERT_FALSE(NeedsSwap); |
| HeaderMapImpl Map(File.getBuffer(), NeedsSwap); |
| |
| SmallString<8> DestPath; |
| ASSERT_EQ("bc", Map.lookupFilename("a", DestPath)); |
| } |
| |
| template <class FileTy, class PaddingTy> struct PaddedFile { |
| FileTy File; |
| PaddingTy Padding; |
| }; |
| |
| TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) { |
| typedef HMapFileMock<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> |
| FileTy; |
| static_assert(std::is_standard_layout<FileTy>::value, |
| "Expected standard layout"); |
| static_assert(sizeof(FileTy) == 64, "check the math"); |
| PaddedFile<FileTy, uint64_t> P; |
| auto &File = P.File; |
| auto &Padding = P.Padding; |
| File.init(); |
| |
| HMapFileMockMaker<FileTy> Maker(File); |
| auto a = Maker.addString("a"); |
| auto b = Maker.addString("b"); |
| auto c = Maker.addString("c"); |
| Maker.addBucket("a", a, b, c); |
| |
| // Add 'x' characters to cause an overflow into Padding. |
| ASSERT_EQ('c', File.Bytes[5]); |
| for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { |
| ASSERT_EQ(0, File.Bytes[I]); |
| File.Bytes[I] = 'x'; |
| } |
| Padding = 0xffffffff; // Padding won't stop it either. |
| |
| bool NeedsSwap; |
| ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| ASSERT_FALSE(NeedsSwap); |
| HeaderMapImpl Map(File.getBuffer(), NeedsSwap); |
| |
| // The string for "c" runs to the end of File. Check that the suffix |
| // ("cxxxx...") is detected as truncated, and an empty string is returned. |
| SmallString<24> DestPath; |
| ASSERT_EQ("", Map.lookupFilename("a", DestPath)); |
| } |
| |
| TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) { |
| typedef HMapFileMock<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> |
| FileTy; |
| static_assert(std::is_standard_layout<FileTy>::value, |
| "Expected standard layout"); |
| static_assert(sizeof(FileTy) == 64, "check the math"); |
| PaddedFile<FileTy, uint64_t> P; |
| auto &File = P.File; |
| auto &Padding = P.Padding; |
| File.init(); |
| |
| HMapFileMockMaker<FileTy> Maker(File); |
| auto a = Maker.addString("a"); |
| auto c = Maker.addString("c"); |
| auto b = Maker.addString("b"); // Store the prefix last. |
| Maker.addBucket("a", a, b, c); |
| |
| // Add 'x' characters to cause an overflow into Padding. |
| ASSERT_EQ('b', File.Bytes[5]); |
| for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { |
| ASSERT_EQ(0, File.Bytes[I]); |
| File.Bytes[I] = 'x'; |
| } |
| Padding = 0xffffffff; // Padding won't stop it either. |
| |
| bool NeedsSwap; |
| ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); |
| ASSERT_FALSE(NeedsSwap); |
| HeaderMapImpl Map(File.getBuffer(), NeedsSwap); |
| |
| // The string for "b" runs to the end of File. Check that the prefix |
| // ("bxxxx...") is detected as truncated, and an empty string is returned. |
| SmallString<24> DestPath; |
| ASSERT_EQ("", Map.lookupFilename("a", DestPath)); |
| } |
| |
| } // end namespace |