Changeset View
Standalone View
unittests/Support/DataExtractorTest.cpp
//===- llvm/unittest/Support/DataExtractorTest.cpp - DataExtractor tests --===// | //===- llvm/unittest/Support/DataExtractorTest.cpp - DataExtractor tests --===// | ||||
// | // | ||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||
// See https://llvm.org/LICENSE.txt for license information. | // See https://llvm.org/LICENSE.txt for license information. | ||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||
// | // | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
#include "llvm/Support/DataExtractor.h" | #include "llvm/Support/DataExtractor.h" | ||||
#include "llvm/Testing/Support/Error.h" | |||||
probinson: The Error is part of the DataExtractor interface, you should not need to #include this here. | |||||
This is the testing support header which defines stuff that makes things like EXPECT_THAT_ERROR(..., Succeeded()) work. labath: This is the testing support header which defines stuff that makes things like… | |||||
Doh! I'm due for an eye test. probinson: Doh! I'm due for an eye test. | |||||
#include "gtest/gtest.h" | #include "gtest/gtest.h" | ||||
using namespace llvm; | using namespace llvm; | ||||
namespace { | namespace { | ||||
const char numberData[] = "\x80\x90\xFF\xFF\x80\x00\x00\x00"; | const char numberData[] = "\x80\x90\xFF\xFF\x80\x00\x00\x00"; | ||||
const char stringData[] = "hellohello\0hello"; | const char stringData[] = "hellohello\0hello"; | ||||
const char leb128data[] = "\xA6\x49"; | const char leb128data[] = "\xA6\x49"; | ||||
▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | TEST(DataExtractorTest, LEB128_error) { | ||||
uint64_t Offset = 0; | uint64_t Offset = 0; | ||||
EXPECT_EQ(0U, DE.getULEB128(&Offset)); | EXPECT_EQ(0U, DE.getULEB128(&Offset)); | ||||
EXPECT_EQ(0U, Offset); | EXPECT_EQ(0U, Offset); | ||||
Offset = 0; | Offset = 0; | ||||
EXPECT_EQ(0U, DE.getSLEB128(&Offset)); | EXPECT_EQ(0U, DE.getSLEB128(&Offset)); | ||||
EXPECT_EQ(0U, Offset); | EXPECT_EQ(0U, Offset); | ||||
} | } | ||||
TEST(DataExtractorTest, Cursor_tell) { | |||||
DataExtractor DE(StringRef("AB"), false, 8); | |||||
DataExtractor::Cursor C(0); | |||||
// A successful read operation advances the cursor | |||||
EXPECT_EQ('A', DE.getU8(C)); | |||||
EXPECT_EQ(1u, C.tell()); | |||||
// An unsuccessful one doesn't. | |||||
EXPECT_EQ(0u, DE.getU16(C)); | |||||
EXPECT_EQ(1u, C.tell()); | |||||
// And neither do any subsequent operations. | |||||
EXPECT_EQ(0, DE.getU8(C)); | |||||
EXPECT_EQ(1u, C.tell()); | |||||
consumeError(C.takeError()); | |||||
} | |||||
TEST(DataExtractorTest, Cursor_takeError) { | |||||
DataExtractor DE(StringRef("AB"), false, 8); | |||||
DataExtractor::Cursor C(0); | |||||
// Initially, the cursor is in the "success" state. | |||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded()); | |||||
// It remains "success" after a successful read. | |||||
EXPECT_EQ('A', DE.getU8(C)); | |||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded()); | |||||
// An unsuccessful read sets the error state. | |||||
EXPECT_EQ(0u, DE.getU32(C)); | |||||
EXPECT_THAT_ERROR(C.takeError(), Failed()); | |||||
// Once set the error sticks until explicitly cleared. | |||||
EXPECT_EQ(0u, DE.getU32(C)); | |||||
EXPECT_EQ(0, DE.getU8(C)); | |||||
EXPECT_THAT_ERROR(C.takeError(), Failed()); | |||||
// At which point reads can be succeed again. | |||||
EXPECT_EQ('B', DE.getU8(C)); | |||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded()); | |||||
} | |||||
TEST(DataExtractorTest, Cursor_chaining) { | |||||
DataExtractor DE(StringRef("ABCD"), false, 8); | |||||
DataExtractor::Cursor C(0); | |||||
// Multiple reads can be chained without trigerring any assertions. | |||||
EXPECT_EQ('A', DE.getU8(C)); | |||||
EXPECT_EQ('B', DE.getU8(C)); | |||||
EXPECT_EQ('C', DE.getU8(C)); | |||||
EXPECT_EQ('D', DE.getU8(C)); | |||||
// And the error checked at the end. | |||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded()); | |||||
} | |||||
#if defined(GTEST_HAS_DEATH_TEST) && defined(_DEBUG) | |||||
TEST(DataExtractorDeathTest, Cursor) { | |||||
DataExtractor DE(StringRef("AB"), false, 8); | |||||
// Even an unused cursor must be checked for errors: | |||||
EXPECT_DEATH(DataExtractor::Cursor(0), | |||||
"Success values must still be checked prior to being destroyed"); | |||||
{ | |||||
auto C = std::make_unique<DataExtractor::Cursor>(0); | |||||
EXPECT_EQ(0u, DE.getU32(*C)); | |||||
// It must also be checked after an unsuccessful operation. | |||||
// destruction. | |||||
EXPECT_DEATH(C.reset(), "unexpected end of data"); | |||||
EXPECT_THAT_ERROR(C->takeError(), Failed()); | |||||
} | |||||
{ | |||||
auto C = std::make_unique<DataExtractor::Cursor>(0); | |||||
EXPECT_EQ('A', DE.getU8(*C)); | |||||
// Same goes for a successful one. | |||||
EXPECT_DEATH( | |||||
C.reset(), | |||||
"Success values must still be checked prior to being destroyed"); | |||||
EXPECT_THAT_ERROR(C->takeError(), Succeeded()); | |||||
} | |||||
{ | |||||
auto C = std::make_unique<DataExtractor::Cursor>(0); | |||||
EXPECT_EQ('A', DE.getU8(*C)); | |||||
EXPECT_EQ(0u, DE.getU32(*C)); | |||||
// Even if a successful operation is followed by an unsuccessful one. | |||||
EXPECT_DEATH(C.reset(), "unexpected end of data"); | |||||
EXPECT_THAT_ERROR(C->takeError(), Failed()); | |||||
} | |||||
{ | |||||
auto C = std::make_unique<DataExtractor::Cursor>(0); | |||||
EXPECT_EQ(0u, DE.getU32(*C)); | |||||
EXPECT_EQ(0, DE.getU8(*C)); | |||||
// Even if an unsuccessful operation is followed by one that would normally | |||||
// succeed. | |||||
EXPECT_DEATH(C.reset(), "unexpected end of data"); | |||||
This produces undefined behavior in the non-death case (the dtor will run twice - importantly it'll run on something that isn't an object of the right type (because that object's already been destroyed) You could use an Optional<Cursor> to have control over the point of destruction (then "EXPECT_DEATH(opt.reset()..." to observe the unchecked Error). dblaikie: This produces undefined behavior in the non-death case (the dtor will run twice - importantly… | |||||
Done. I've gone with a std::unique_ptr because it makes the construction of the cursor slightly nicer (missing an in_place_t for llvm::Optional). labath: Done. I've gone with a std::unique_ptr because it makes the construction of the cursor slightly… | |||||
EXPECT_THAT_ERROR(C->takeError(), Failed()); | |||||
} | |||||
} | |||||
#endif | |||||
TEST(DataExtractorTest, getU8_vector) { | |||||
DataExtractor DE(StringRef("AB"), false, 8); | |||||
DataExtractor::Cursor C(0); | |||||
SmallString<2> S; | |||||
DE.getU8(C, S, 4); | |||||
EXPECT_THAT_ERROR(C.takeError(), Failed()); | |||||
EXPECT_EQ("", S); | |||||
DE.getU8(C, S, 2); | |||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded()); | |||||
EXPECT_EQ("AB", S); | |||||
} | |||||
TEST(DataExtractorTest, skip) { | |||||
DataExtractor DE(StringRef("AB"), false, 8); | |||||
DataExtractor::Cursor C(0); | |||||
DE.skip(C, 4); | |||||
EXPECT_THAT_ERROR(C.takeError(), Failed()); | |||||
EXPECT_EQ(0u, C.tell()); | |||||
DE.skip(C, 2); | |||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded()); | |||||
EXPECT_EQ(2u, C.tell()); | |||||
} | |||||
TEST(DataExtractorTest, eof) { | |||||
DataExtractor DE(StringRef("A"), false, 8); | |||||
DataExtractor::Cursor C(0); | |||||
EXPECT_FALSE(DE.eof(C)); | |||||
EXPECT_EQ(0, DE.getU16(C)); | |||||
EXPECT_FALSE(DE.eof(C)); | |||||
EXPECT_THAT_ERROR(C.takeError(), Failed()); | |||||
Test for eof() in the error case? (attempt to read too far, but eof is still false, if that's the correct contract.) probinson: Test for eof() in the error case? (attempt to read too far, but eof is still false, if that's… | |||||
EXPECT_EQ('A', DE.getU8(C)); | |||||
EXPECT_TRUE(DE.eof(C)); | |||||
EXPECT_THAT_ERROR(C.takeError(), Succeeded()); | |||||
} | |||||
} | } |
The Error is part of the DataExtractor interface, you should not need to #include this here.