/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "subtype_check_info.h" #include "gtest/gtest.h" #include "android-base/logging.h" namespace art { constexpr size_t BitString::kBitSizeAtPosition[BitString::kCapacity]; constexpr size_t BitString::kCapacity; }; // namespace art using namespace art; // NOLINT // These helper functions are only used by the test, // so they are not in the main BitString class. std::string Stringify(BitString bit_string) { std::stringstream ss; ss << bit_string; return ss.str(); } BitStringChar MakeBitStringChar(size_t idx, size_t val) { return BitStringChar(val, BitString::MaybeGetBitLengthAtPosition(idx)); } BitStringChar MakeBitStringChar(size_t val) { return BitStringChar(val, MinimumBitsToStore(val)); } BitString MakeBitString(std::initializer_list values = {}) { CHECK_GE(BitString::kCapacity, values.size()); BitString bs{}; size_t i = 0; for (size_t val : values) { bs.SetAt(i, MakeBitStringChar(i, val)); ++i; } return bs; } template size_t AsUint(const T& value) { size_t uint_value = 0; memcpy(&uint_value, &value, sizeof(value)); return uint_value; } // Make max bistring, e.g. BitString[4095,15,2047] for {12,4,11} template BitString MakeBitStringMax() { BitString bs{}; for (size_t i = 0; i < kCount; ++i) { bs.SetAt(i, MakeBitStringChar(i, MaxInt(BitString::kBitSizeAtPosition[i]))); } return bs; } BitString SetBitStringCharAt(BitString bit_string, size_t i, size_t val) { BitString bs = bit_string; bs.SetAt(i, MakeBitStringChar(i, val)); return bs; } struct SubtypeCheckInfoTest : public ::testing::Test { protected: void SetUp() override { android::base::InitLogging(/*argv=*/nullptr); } void TearDown() override { } static SubtypeCheckInfo MakeSubtypeCheckInfo(BitString path_to_root = {}, BitStringChar next = {}, bool overflow = false, size_t depth = 1u) { // Depth=1 is good default because it will go through all state transitions, // and its children will also go through all state transitions. return SubtypeCheckInfo(path_to_root, next, overflow, depth); } static SubtypeCheckInfo MakeSubtypeCheckInfoInfused(BitString bs = {}, bool overflow = false, size_t depth = 1u) { // Depth=1 is good default because it will go through all state transitions, // and its children will also go through all state transitions. SubtypeCheckBits iod; iod.bitstring_ = bs; iod.overflow_ = overflow; return SubtypeCheckInfo::Create(iod, depth); } static SubtypeCheckInfo MakeSubtypeCheckInfoUnchecked(BitString bs = {}, bool overflow = false, size_t depth = 1u) { // Depth=1 is good default because it will go through all state transitions, // and its children will also go through all state transitions. return SubtypeCheckInfo::MakeUnchecked(bs, overflow, depth); } static bool HasNext(const SubtypeCheckInfo& io) { return io.HasNext(); } static BitString GetPathToRoot(const SubtypeCheckInfo& io) { return io.GetPathToRoot(); } // Create an SubtypeCheckInfo with the same depth, but with everything else reset. // Returns: SubtypeCheckInfo in the Uninitialized state. static SubtypeCheckInfo CopyCleared(const SubtypeCheckInfo& sc) { SubtypeCheckInfo cleared_copy{}; cleared_copy.depth_ = sc.depth_; DCHECK_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState()); return cleared_copy; } }; const char* GetExpectedMessageForDeathTest(const char* msg) { #ifdef ART_TARGET_ANDROID // On Android, dcheck failure messages go to logcat, // which gtest death tests does not check, and thus the tests would fail with // "unexpected message ''" UNUSED(msg); return ""; // Still ensures there was a bad return code, but match anything. #else return msg; #endif } TEST_F(SubtypeCheckInfoTest, IllegalValues) { // This test relies on BitString being at least 3 large. // It will need to be updated otherwise. ASSERT_LE(3u, BitString::kCapacity); // Illegal values during construction would cause a Dcheck failure and crash. ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({1u}), /*next=*/MakeBitStringChar(0), /*overflow=*/false, /*depth=*/0u), GetExpectedMessageForDeathTest("Path was too long for the depth")); ASSERT_DEATH(MakeSubtypeCheckInfoInfused(MakeBitString({1u, 1u}), /*overflow=*/false, /*depth=*/0u), GetExpectedMessageForDeathTest("Bitstring too long for depth")); ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({1u}), /*next=*/MakeBitStringChar(0), /*overflow=*/false, /*depth=*/1u), GetExpectedMessageForDeathTest("Expected \\(Assigned\\|Initialized\\) " "state to have >0 Next value")); ASSERT_DEATH(MakeSubtypeCheckInfoInfused(MakeBitString({0u, 2u, 1u}), /*overflow=*/false, /*depth=*/2u), GetExpectedMessageForDeathTest("Path to root had non-0s following 0s")); ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({0u, 2u}), /*next=*/MakeBitStringChar(1u), /*overflow=*/false, /*depth=*/2u), GetExpectedMessageForDeathTest("Path to root had non-0s following 0s")); ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({0u, 1u, 1u}), /*next=*/MakeBitStringChar(0), /*overflow=*/false, /*depth=*/3u), GetExpectedMessageForDeathTest("Path to root had non-0s following 0s")); // These are really slow (~1sec per death test on host), // keep them down to a minimum. } TEST_F(SubtypeCheckInfoTest, States) { EXPECT_EQ(SubtypeCheckInfo::kUninitialized, MakeSubtypeCheckInfo().GetState()); EXPECT_EQ(SubtypeCheckInfo::kInitialized, MakeSubtypeCheckInfo(/*path_to_root=*/{}, /*next=*/MakeBitStringChar(1)).GetState()); EXPECT_EQ(SubtypeCheckInfo::kOverflowed, MakeSubtypeCheckInfo(/*path_to_root=*/{}, /*next=*/MakeBitStringChar(1), /*overflow=*/true, /*depth=*/1u).GetState()); EXPECT_EQ(SubtypeCheckInfo::kAssigned, MakeSubtypeCheckInfo(/*path_to_root=*/MakeBitString({1u}), /*next=*/MakeBitStringChar(1), /*overflow=*/false, /*depth=*/1u).GetState()); // Test edge conditions: depth == BitString::kCapacity (No Next value). EXPECT_EQ(SubtypeCheckInfo::kAssigned, MakeSubtypeCheckInfo(/*path_to_root=*/MakeBitStringMax(), /*next=*/MakeBitStringChar(0), /*overflow=*/false, /*depth=*/BitString::kCapacity).GetState()); EXPECT_EQ(SubtypeCheckInfo::kInitialized, MakeSubtypeCheckInfo(/*path_to_root=*/MakeBitStringMax(), /*next=*/MakeBitStringChar(0), /*overflow=*/false, /*depth=*/BitString::kCapacity).GetState()); // Test edge conditions: depth > BitString::kCapacity (Must overflow). EXPECT_EQ(SubtypeCheckInfo::kOverflowed, MakeSubtypeCheckInfo(/*path_to_root=*/MakeBitStringMax(), /*next=*/MakeBitStringChar(0), /*overflow=*/true, /*depth=*/BitString::kCapacity + 1u).GetState()); } TEST_F(SubtypeCheckInfoTest, NextValue) { // Validate "Next" is correctly aliased as the Bitstring[Depth] character. EXPECT_EQ(MakeBitStringChar(1u), MakeSubtypeCheckInfoUnchecked(MakeBitString({1u, 2u, 3u}), /*overflow=*/false, /*depth=*/0u).GetNext()); EXPECT_EQ(MakeBitStringChar(2u), MakeSubtypeCheckInfoUnchecked(MakeBitString({1u, 2u, 3u}), /*overflow=*/false, /*depth=*/1u).GetNext()); EXPECT_EQ(MakeBitStringChar(3u), MakeSubtypeCheckInfoUnchecked(MakeBitString({1u, 2u, 3u}), /*overflow=*/false, /*depth=*/2u).GetNext()); EXPECT_EQ(MakeBitStringChar(1u), MakeSubtypeCheckInfoUnchecked(MakeBitString({0u, 2u, 1u}), /*overflow=*/false, /*depth=*/2u).GetNext()); // Test edge conditions: depth == BitString::kCapacity (No Next value). EXPECT_FALSE(HasNext(MakeSubtypeCheckInfoUnchecked(MakeBitStringMax(), /*overflow=*/false, /*depth=*/BitString::kCapacity))); // Anything with depth >= BitString::kCapacity has no next value. EXPECT_FALSE(HasNext(MakeSubtypeCheckInfoUnchecked(MakeBitStringMax(), /*overflow=*/false, /*depth=*/BitString::kCapacity + 1u))); EXPECT_FALSE(HasNext(MakeSubtypeCheckInfoUnchecked(MakeBitStringMax(), /*overflow=*/false, /*depth=*/std::numeric_limits::max()))); } template size_t LenForPos() { return BitString::GetBitLengthTotalAtPosition(kPos); } TEST_F(SubtypeCheckInfoTest, EncodedPathToRoot) { using StorageType = BitString::StorageType; SubtypeCheckInfo sci = MakeSubtypeCheckInfo(/*path_to_root=*/MakeBitStringMax(), /*next=*/BitStringChar{}, /*overflow=*/false, /*depth=*/BitString::kCapacity); // 0b000...111 where LSB == 1, and trailing 1s = the maximum bitstring representation. EXPECT_EQ(MaxInt(LenForPos()), sci.GetEncodedPathToRoot()); // The rest of this test is written assuming kCapacity == 3 for convenience. // Please update the test if this changes. ASSERT_EQ(3u, BitString::kCapacity); ASSERT_EQ(12u, BitString::kBitSizeAtPosition[0]); ASSERT_EQ(4u, BitString::kBitSizeAtPosition[1]); ASSERT_EQ(11u, BitString::kBitSizeAtPosition[2]); SubtypeCheckInfo sci2 = MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<2u>(), /*overflow=*/false, /*depth=*/BitString::kCapacity); #define MAKE_ENCODED_PATH(pos0, pos1, pos2) \ (((pos0) << 0) | \ ((pos1) << BitString::kBitSizeAtPosition[0]) | \ ((pos2) << (BitString::kBitSizeAtPosition[0] + BitString::kBitSizeAtPosition[1]))) EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt(12), 0b1111, 0b0), sci2.GetEncodedPathToRoot()); EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt(12), 0b1111, 0b11111111111), sci2.GetEncodedPathToRootMask()); SubtypeCheckInfo sci3 = MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<2u>(), /*overflow=*/false, /*depth=*/BitString::kCapacity - 1u); EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt(12), 0b1111, 0b0), sci3.GetEncodedPathToRoot()); EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt(12), 0b1111, 0b0), sci3.GetEncodedPathToRootMask()); SubtypeCheckInfo sci4 = MakeSubtypeCheckInfoUnchecked(MakeBitString({0b1010101u}), /*overflow=*/false, /*depth=*/BitString::kCapacity - 2u); EXPECT_EQ(MAKE_ENCODED_PATH(0b1010101u, 0b0000, 0b0), sci4.GetEncodedPathToRoot()); EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt(12), 0b0000, 0b0), sci4.GetEncodedPathToRootMask()); } TEST_F(SubtypeCheckInfoTest, NewForRoot) { SubtypeCheckInfo sci = SubtypeCheckInfo::CreateRoot(); EXPECT_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); // Root is always assigned. EXPECT_EQ(0u, GetPathToRoot(sci).Length()); // Root's path length is 0. EXPECT_TRUE(HasNext(sci)); // Root always has a "Next". EXPECT_EQ(MakeBitStringChar(1u), sci.GetNext()); // Next>=1 to disambiguate from Uninitialized. } TEST_F(SubtypeCheckInfoTest, CopyCleared) { SubtypeCheckInfo root = SubtypeCheckInfo::CreateRoot(); EXPECT_EQ(MakeBitStringChar(1u), root.GetNext()); SubtypeCheckInfo childC = root.CreateChild(/*assign_next=*/true); EXPECT_EQ(SubtypeCheckInfo::kAssigned, childC.GetState()); EXPECT_EQ(MakeBitStringChar(2u), root.GetNext()); // Next incremented for Assign. EXPECT_EQ(MakeBitString({1u}), GetPathToRoot(childC)); SubtypeCheckInfo cleared_copy = CopyCleared(childC); EXPECT_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState()); EXPECT_EQ(MakeBitString({}), GetPathToRoot(cleared_copy)); // CopyCleared is just a thin wrapper around value-init and providing the depth. SubtypeCheckInfo cleared_copy_value = SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth=*/1u); EXPECT_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy_value.GetState()); EXPECT_EQ(MakeBitString({}), GetPathToRoot(cleared_copy_value)); } TEST_F(SubtypeCheckInfoTest, NewForChild2) { SubtypeCheckInfo root = SubtypeCheckInfo::CreateRoot(); EXPECT_EQ(MakeBitStringChar(1u), root.GetNext()); SubtypeCheckInfo childC = root.CreateChild(/*assign_next=*/true); EXPECT_EQ(SubtypeCheckInfo::kAssigned, childC.GetState()); EXPECT_EQ(MakeBitStringChar(2u), root.GetNext()); // Next incremented for Assign. EXPECT_EQ(MakeBitString({1u}), GetPathToRoot(childC)); } TEST_F(SubtypeCheckInfoTest, NewForChild) { SubtypeCheckInfo root = SubtypeCheckInfo::CreateRoot(); EXPECT_EQ(MakeBitStringChar(1u), root.GetNext()); SubtypeCheckInfo childA = root.CreateChild(/*assign_next=*/false); EXPECT_EQ(SubtypeCheckInfo::kInitialized, childA.GetState()); EXPECT_EQ(MakeBitStringChar(1u), root.GetNext()); // Next unchanged for Initialize. EXPECT_EQ(MakeBitString({}), GetPathToRoot(childA)); SubtypeCheckInfo childB = root.CreateChild(/*assign_next=*/false); EXPECT_EQ(SubtypeCheckInfo::kInitialized, childB.GetState()); EXPECT_EQ(MakeBitStringChar(1u), root.GetNext()); // Next unchanged for Initialize. EXPECT_EQ(MakeBitString({}), GetPathToRoot(childB)); SubtypeCheckInfo childC = root.CreateChild(/*assign_next=*/true); EXPECT_EQ(SubtypeCheckInfo::kAssigned, childC.GetState()); EXPECT_EQ(MakeBitStringChar(2u), root.GetNext()); // Next incremented for Assign. EXPECT_EQ(MakeBitString({1u}), GetPathToRoot(childC)); { size_t cur_depth = 1u; SubtypeCheckInfo latest_child = childC; while (cur_depth != BitString::kCapacity) { latest_child = latest_child.CreateChild(/*assign_next=*/true); ASSERT_EQ(SubtypeCheckInfo::kAssigned, latest_child.GetState()); ASSERT_EQ(cur_depth + 1u, GetPathToRoot(latest_child).Length()); cur_depth++; } // Future assignments will result in a too-deep overflow. SubtypeCheckInfo child_of_deep = latest_child.CreateChild(/*assign_next=*/true); EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of_deep.GetState()); EXPECT_EQ(GetPathToRoot(latest_child), GetPathToRoot(child_of_deep)); // Assignment of too-deep overflow also causes overflow. SubtypeCheckInfo child_of_deep_2 = child_of_deep.CreateChild(/*assign_next=*/true); EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of_deep_2.GetState()); EXPECT_EQ(GetPathToRoot(child_of_deep), GetPathToRoot(child_of_deep_2)); } { size_t cur_next = 2u; while (true) { if (cur_next == MaxInt(BitString::kBitSizeAtPosition[0u])) { break; } SubtypeCheckInfo child = root.CreateChild(/*assign_next=*/true); ASSERT_EQ(SubtypeCheckInfo::kAssigned, child.GetState()); ASSERT_EQ(MakeBitStringChar(cur_next+1u), root.GetNext()); ASSERT_EQ(MakeBitString({cur_next}), GetPathToRoot(child)); cur_next++; } // Now the root will be in a state that further assigns will be too-wide overflow. // Initialization still succeeds. SubtypeCheckInfo child = root.CreateChild(/*assign_next=*/false); EXPECT_EQ(SubtypeCheckInfo::kInitialized, child.GetState()); EXPECT_EQ(MakeBitStringChar(cur_next), root.GetNext()); EXPECT_EQ(MakeBitString({}), GetPathToRoot(child)); // Assignment goes to too-wide Overflow. SubtypeCheckInfo child_of = root.CreateChild(/*assign_next=*/true); EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of.GetState()); EXPECT_EQ(MakeBitStringChar(cur_next), root.GetNext()); EXPECT_EQ(MakeBitString({}), GetPathToRoot(child_of)); // Assignment of overflowed child still succeeds. // The path to root is the same. SubtypeCheckInfo child_of2 = child_of.CreateChild(/*assign_next=*/true); EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of2.GetState()); EXPECT_EQ(GetPathToRoot(child_of), GetPathToRoot(child_of2)); } }