// Copyright 2020 The Tint Authors. // // 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 "src/tint/castable.h" #include #include #include "gtest/gtest.h" namespace tint { struct Animal : public tint::Castable {}; struct Amphibian : public tint::Castable {}; struct Mammal : public tint::Castable {}; struct Reptile : public tint::Castable {}; struct Frog : public tint::Castable {}; struct Bear : public tint::Castable {}; struct Lizard : public tint::Castable {}; struct Gecko : public tint::Castable {}; struct Iguana : public tint::Castable {}; namespace { TEST(CastableBase, Is) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); ASSERT_TRUE(frog->Is()); ASSERT_TRUE(bear->Is()); ASSERT_TRUE(gecko->Is()); ASSERT_TRUE(frog->Is()); ASSERT_FALSE(bear->Is()); ASSERT_FALSE(gecko->Is()); ASSERT_FALSE(frog->Is()); ASSERT_TRUE(bear->Is()); ASSERT_FALSE(gecko->Is()); ASSERT_FALSE(frog->Is()); ASSERT_FALSE(bear->Is()); ASSERT_TRUE(gecko->Is()); } TEST(CastableBase, Is_kDontErrorOnImpossibleCast) { // Unlike TEST(CastableBase, Is), we're dynamically querying [A -> B] without // going via CastableBase. auto frog = std::make_unique(); auto bear = std::make_unique(); auto gecko = std::make_unique(); ASSERT_TRUE((frog->Is())); ASSERT_TRUE((bear->Is())); ASSERT_TRUE((gecko->Is())); ASSERT_TRUE((frog->Is())); ASSERT_FALSE((bear->Is())); ASSERT_FALSE((gecko->Is())); ASSERT_FALSE((frog->Is())); ASSERT_TRUE((bear->Is())); ASSERT_FALSE((gecko->Is())); ASSERT_FALSE((frog->Is())); ASSERT_FALSE((bear->Is())); ASSERT_TRUE((gecko->Is())); } TEST(CastableBase, IsWithPredicate) { std::unique_ptr frog = std::make_unique(); frog->Is([&frog](const Animal* a) { EXPECT_EQ(a, frog.get()); return true; }); ASSERT_TRUE((frog->Is([](const Animal*) { return true; }))); ASSERT_FALSE((frog->Is([](const Animal*) { return false; }))); // Predicate not called if cast is invalid auto expect_not_called = [] { FAIL() << "Should not be called"; }; ASSERT_FALSE((frog->Is([&](const Animal*) { expect_not_called(); return true; }))); } TEST(CastableBase, IsAnyOf) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); ASSERT_TRUE((frog->IsAnyOf())); ASSERT_TRUE((frog->IsAnyOf())); ASSERT_TRUE((frog->IsAnyOf())); ASSERT_FALSE((frog->IsAnyOf())); ASSERT_TRUE((bear->IsAnyOf())); ASSERT_TRUE((bear->IsAnyOf())); ASSERT_TRUE((bear->IsAnyOf())); ASSERT_FALSE((bear->IsAnyOf())); ASSERT_TRUE((gecko->IsAnyOf())); ASSERT_TRUE((gecko->IsAnyOf())); ASSERT_TRUE((gecko->IsAnyOf())); ASSERT_FALSE((gecko->IsAnyOf())); } TEST(CastableBase, As) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); ASSERT_EQ(frog->As(), static_cast(frog.get())); ASSERT_EQ(bear->As(), static_cast(bear.get())); ASSERT_EQ(gecko->As(), static_cast(gecko.get())); ASSERT_EQ(frog->As(), static_cast(frog.get())); ASSERT_EQ(bear->As(), nullptr); ASSERT_EQ(gecko->As(), nullptr); ASSERT_EQ(frog->As(), nullptr); ASSERT_EQ(bear->As(), static_cast(bear.get())); ASSERT_EQ(gecko->As(), nullptr); ASSERT_EQ(frog->As(), nullptr); ASSERT_EQ(bear->As(), nullptr); ASSERT_EQ(gecko->As(), static_cast(gecko.get())); } TEST(CastableBase, As_kDontErrorOnImpossibleCast) { // Unlike TEST(CastableBase, As), we're dynamically casting [A -> B] without // going via CastableBase. auto frog = std::make_unique(); auto bear = std::make_unique(); auto gecko = std::make_unique(); ASSERT_EQ((frog->As()), static_cast(frog.get())); ASSERT_EQ((bear->As()), static_cast(bear.get())); ASSERT_EQ((gecko->As()), static_cast(gecko.get())); ASSERT_EQ((frog->As()), static_cast(frog.get())); ASSERT_EQ((bear->As()), nullptr); ASSERT_EQ((gecko->As()), nullptr); ASSERT_EQ((frog->As()), nullptr); ASSERT_EQ((bear->As()), static_cast(bear.get())); ASSERT_EQ((gecko->As()), nullptr); ASSERT_EQ((frog->As()), nullptr); ASSERT_EQ((bear->As()), nullptr); ASSERT_EQ((gecko->As()), static_cast(gecko.get())); } TEST(Castable, Is) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); ASSERT_TRUE(frog->Is()); ASSERT_TRUE(bear->Is()); ASSERT_TRUE(gecko->Is()); ASSERT_TRUE(frog->Is()); ASSERT_FALSE(bear->Is()); ASSERT_FALSE(gecko->Is()); ASSERT_FALSE(frog->Is()); ASSERT_TRUE(bear->Is()); ASSERT_FALSE(gecko->Is()); ASSERT_FALSE(frog->Is()); ASSERT_FALSE(bear->Is()); ASSERT_TRUE(gecko->Is()); } TEST(Castable, IsWithPredicate) { std::unique_ptr frog = std::make_unique(); frog->Is([&frog](const Animal* a) { EXPECT_EQ(a, frog.get()); return true; }); ASSERT_TRUE((frog->Is([](const Animal*) { return true; }))); ASSERT_FALSE((frog->Is([](const Animal*) { return false; }))); // Predicate not called if cast is invalid auto expect_not_called = [] { FAIL() << "Should not be called"; }; ASSERT_FALSE((frog->Is([&](const Bear*) { expect_not_called(); return true; }))); } TEST(Castable, As) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); ASSERT_EQ(frog->As(), static_cast(frog.get())); ASSERT_EQ(bear->As(), static_cast(bear.get())); ASSERT_EQ(gecko->As(), static_cast(gecko.get())); ASSERT_EQ(frog->As(), static_cast(frog.get())); ASSERT_EQ(bear->As(), nullptr); ASSERT_EQ(gecko->As(), nullptr); ASSERT_EQ(frog->As(), nullptr); ASSERT_EQ(bear->As(), static_cast(bear.get())); ASSERT_EQ(gecko->As(), nullptr); ASSERT_EQ(frog->As(), nullptr); ASSERT_EQ(bear->As(), nullptr); ASSERT_EQ(gecko->As(), static_cast(gecko.get())); } TEST(Castable, SwitchNoDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { bool frog_matched_amphibian = false; Switch( frog.get(), // [&](Reptile*) { FAIL() << "frog is not reptile"; }, [&](Mammal*) { FAIL() << "frog is not mammal"; }, [&](Amphibian* amphibian) { EXPECT_EQ(amphibian, frog.get()); frog_matched_amphibian = true; }); EXPECT_TRUE(frog_matched_amphibian); } { bool bear_matched_mammal = false; Switch( bear.get(), // [&](Reptile*) { FAIL() << "bear is not reptile"; }, [&](Amphibian*) { FAIL() << "bear is not amphibian"; }, [&](Mammal* mammal) { EXPECT_EQ(mammal, bear.get()); bear_matched_mammal = true; }); EXPECT_TRUE(bear_matched_mammal); } { bool gecko_matched_reptile = false; Switch( gecko.get(), // [&](Mammal*) { FAIL() << "gecko is not mammal"; }, [&](Amphibian*) { FAIL() << "gecko is not amphibian"; }, [&](Reptile* reptile) { EXPECT_EQ(reptile, gecko.get()); gecko_matched_reptile = true; }); EXPECT_TRUE(gecko_matched_reptile); } } TEST(Castable, SwitchWithUnusedDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { bool frog_matched_amphibian = false; Switch( frog.get(), // [&](Reptile*) { FAIL() << "frog is not reptile"; }, [&](Mammal*) { FAIL() << "frog is not mammal"; }, [&](Amphibian* amphibian) { EXPECT_EQ(amphibian, frog.get()); frog_matched_amphibian = true; }, [&](Default) { FAIL() << "default should not have been selected"; }); EXPECT_TRUE(frog_matched_amphibian); } { bool bear_matched_mammal = false; Switch( bear.get(), // [&](Reptile*) { FAIL() << "bear is not reptile"; }, [&](Amphibian*) { FAIL() << "bear is not amphibian"; }, [&](Mammal* mammal) { EXPECT_EQ(mammal, bear.get()); bear_matched_mammal = true; }, [&](Default) { FAIL() << "default should not have been selected"; }); EXPECT_TRUE(bear_matched_mammal); } { bool gecko_matched_reptile = false; Switch( gecko.get(), // [&](Mammal*) { FAIL() << "gecko is not mammal"; }, [&](Amphibian*) { FAIL() << "gecko is not amphibian"; }, [&](Reptile* reptile) { EXPECT_EQ(reptile, gecko.get()); gecko_matched_reptile = true; }, [&](Default) { FAIL() << "default should not have been selected"; }); EXPECT_TRUE(gecko_matched_reptile); } } TEST(Castable, SwitchDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { bool frog_matched_default = false; Switch( frog.get(), // [&](Reptile*) { FAIL() << "frog is not reptile"; }, [&](Mammal*) { FAIL() << "frog is not mammal"; }, [&](Default) { frog_matched_default = true; }); EXPECT_TRUE(frog_matched_default); } { bool bear_matched_default = false; Switch( bear.get(), // [&](Reptile*) { FAIL() << "bear is not reptile"; }, [&](Amphibian*) { FAIL() << "bear is not amphibian"; }, [&](Default) { bear_matched_default = true; }); EXPECT_TRUE(bear_matched_default); } { bool gecko_matched_default = false; Switch( gecko.get(), // [&](Mammal*) { FAIL() << "gecko is not mammal"; }, [&](Amphibian*) { FAIL() << "gecko is not amphibian"; }, [&](Default) { gecko_matched_default = true; }); EXPECT_TRUE(gecko_matched_default); } } TEST(Castable, SwitchMatchFirst) { std::unique_ptr frog = std::make_unique(); { bool frog_matched_animal = false; Switch( frog.get(), [&](Animal* animal) { EXPECT_EQ(animal, frog.get()); frog_matched_animal = true; }, [&](Amphibian*) { FAIL() << "animal should have been matched first"; }); EXPECT_TRUE(frog_matched_animal); } { bool frog_matched_amphibian = false; Switch( frog.get(), [&](Amphibian* amphibain) { EXPECT_EQ(amphibain, frog.get()); frog_matched_amphibian = true; }, [&](Animal*) { FAIL() << "amphibian should have been matched first"; }); EXPECT_TRUE(frog_matched_amphibian); } } TEST(Castable, SwitchReturnValueWithDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { const char* result = Switch( frog.get(), // [](Mammal*) { return "mammal"; }, // [](Amphibian*) { return "amphibian"; }, // [](Default) { return "unknown"; }); static_assert(std::is_same_v); EXPECT_EQ(std::string(result), "amphibian"); } { const char* result = Switch( bear.get(), // [](Mammal*) { return "mammal"; }, // [](Amphibian*) { return "amphibian"; }, // [](Default) { return "unknown"; }); static_assert(std::is_same_v); EXPECT_EQ(std::string(result), "mammal"); } { const char* result = Switch( gecko.get(), // [](Mammal*) { return "mammal"; }, // [](Amphibian*) { return "amphibian"; }, // [](Default) { return "unknown"; }); static_assert(std::is_same_v); EXPECT_EQ(std::string(result), "unknown"); } } TEST(Castable, SwitchReturnValueWithoutDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { const char* result = Switch( frog.get(), // [](Mammal*) { return "mammal"; }, // [](Amphibian*) { return "amphibian"; }); static_assert(std::is_same_v); EXPECT_EQ(std::string(result), "amphibian"); } { const char* result = Switch( bear.get(), // [](Mammal*) { return "mammal"; }, // [](Amphibian*) { return "amphibian"; }); static_assert(std::is_same_v); EXPECT_EQ(std::string(result), "mammal"); } { auto* result = Switch( gecko.get(), // [](Mammal*) { return "mammal"; }, // [](Amphibian*) { return "amphibian"; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } } TEST(Castable, SwitchInferPODReturnTypeWithDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto result = Switch( frog.get(), // [](Mammal*) { return 1; }, // [](Amphibian*) { return 2.0f; }, // [](Default) { return 3.0; }); static_assert(std::is_same_v); EXPECT_EQ(result, 2.0); } { auto result = Switch( bear.get(), // [](Mammal*) { return 1.0; }, // [](Amphibian*) { return 2.0f; }, // [](Default) { return 3; }); static_assert(std::is_same_v); EXPECT_EQ(result, 1.0); } { auto result = Switch( gecko.get(), // [](Mammal*) { return 1.0f; }, // [](Amphibian*) { return 2; }, // [](Default) { return 3.0; }); static_assert(std::is_same_v); EXPECT_EQ(result, 3.0); } } TEST(Castable, SwitchInferPODReturnTypeWithoutDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto result = Switch( frog.get(), // [](Mammal*) { return 1; }, // [](Amphibian*) { return 2.0f; }); static_assert(std::is_same_v); EXPECT_EQ(result, 2.0f); } { auto result = Switch( bear.get(), // [](Mammal*) { return 1.0f; }, // [](Amphibian*) { return 2; }); static_assert(std::is_same_v); EXPECT_EQ(result, 1.0f); } { auto result = Switch( gecko.get(), // [](Mammal*) { return 1.0; }, // [](Amphibian*) { return 2.0f; }); static_assert(std::is_same_v); EXPECT_EQ(result, 0.0); } } TEST(Castable, SwitchInferCastableReturnTypeWithDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto* result = Switch( frog.get(), // [](Mammal* p) { return p; }, // [](Amphibian*) { return nullptr; }, // [](Default) { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } { auto* result = Switch( bear.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return const_cast(p); }, [](Default) { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, bear.get()); } { auto* result = Switch( gecko.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return p; }, // [](Default) -> CastableBase* { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } } TEST(Castable, SwitchInferCastableReturnTypeWithoutDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto* result = Switch( frog.get(), // [](Mammal* p) { return p; }, // [](Amphibian*) { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } { auto* result = Switch( bear.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return const_cast(p); }); // static_assert(std::is_same_v); EXPECT_EQ(result, bear.get()); } { auto* result = Switch( gecko.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return p; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } } TEST(Castable, SwitchExplicitPODReturnTypeWithDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto result = Switch( frog.get(), // [](Mammal*) { return 1; }, // [](Amphibian*) { return 2.0f; }, // [](Default) { return 3.0; }); static_assert(std::is_same_v); EXPECT_EQ(result, 2.0f); } { auto result = Switch( bear.get(), // [](Mammal*) { return 1; }, // [](Amphibian*) { return 2; }, // [](Default) { return 3; }); static_assert(std::is_same_v); EXPECT_EQ(result, 1.0f); } { auto result = Switch( gecko.get(), // [](Mammal*) { return 1.0f; }, // [](Amphibian*) { return 2.0f; }, // [](Default) { return 3.0f; }); static_assert(std::is_same_v); EXPECT_EQ(result, 3.0f); } } TEST(Castable, SwitchExplicitPODReturnTypeWithoutDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto result = Switch( frog.get(), // [](Mammal*) { return 1; }, // [](Amphibian*) { return 2.0f; }); static_assert(std::is_same_v); EXPECT_EQ(result, 2.0f); } { auto result = Switch( bear.get(), // [](Mammal*) { return 1.0f; }, // [](Amphibian*) { return 2; }); static_assert(std::is_same_v); EXPECT_EQ(result, 1.0f); } { auto result = Switch( gecko.get(), // [](Mammal*) { return 1.0; }, // [](Amphibian*) { return 2.0f; }); static_assert(std::is_same_v); EXPECT_EQ(result, 0.0); } } TEST(Castable, SwitchExplicitCastableReturnTypeWithDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto* result = Switch( frog.get(), // [](Mammal* p) { return p; }, // [](Amphibian*) { return nullptr; }, // [](Default) { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } { auto* result = Switch( bear.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return const_cast(p); }, [](Default) { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, bear.get()); } { auto* result = Switch( gecko.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return p; }, // [](Default) { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } } TEST(Castable, SwitchExplicitCastableReturnTypeWithoutDefault) { std::unique_ptr frog = std::make_unique(); std::unique_ptr bear = std::make_unique(); std::unique_ptr gecko = std::make_unique(); { auto* result = Switch( frog.get(), // [](Mammal* p) { return p; }, // [](Amphibian*) { return nullptr; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } { auto* result = Switch( bear.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return const_cast(p); }); // static_assert(std::is_same_v); EXPECT_EQ(result, bear.get()); } { auto* result = Switch( gecko.get(), // [](Mammal* p) { return p; }, // [](Amphibian* p) { return p; }); static_assert(std::is_same_v); EXPECT_EQ(result, nullptr); } } TEST(Castable, SwitchNull) { Animal* null = nullptr; Switch( null, // [&](Amphibian*) { FAIL() << "should not be called"; }, [&](Animal*) { FAIL() << "should not be called"; }); } TEST(Castable, SwitchNullNoDefault) { Animal* null = nullptr; bool default_called = false; Switch( null, // [&](Amphibian*) { FAIL() << "should not be called"; }, [&](Animal*) { FAIL() << "should not be called"; }, [&](Default) { default_called = true; }); EXPECT_TRUE(default_called); } // IsCastable static tests static_assert(IsCastable); static_assert(IsCastable); static_assert(IsCastable); static_assert(IsCastable); static_assert(!IsCastable); static_assert(!IsCastable); static_assert(!IsCastable); static_assert(!IsCastable); // CastableCommonBase static tests static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert( std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert( std::is_same_v>); static_assert( std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert( std::is_same_v>); static_assert( std::is_same_v>); static_assert( std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert(std::is_same_v>); static_assert( std::is_same_v>); } // namespace TINT_INSTANTIATE_TYPEINFO(Animal); TINT_INSTANTIATE_TYPEINFO(Amphibian); TINT_INSTANTIATE_TYPEINFO(Mammal); TINT_INSTANTIATE_TYPEINFO(Reptile); TINT_INSTANTIATE_TYPEINFO(Frog); TINT_INSTANTIATE_TYPEINFO(Bear); TINT_INSTANTIATE_TYPEINFO(Lizard); TINT_INSTANTIATE_TYPEINFO(Gecko); } // namespace tint