/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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 #include #include #include #include #include #include #include using namespace apache::thrift::type; namespace apache::thrift::test { namespace { template bool isRegistered(std::string_view uri) { return conformance::AnyRegistry::generated().getSerializerByUri( uri, conformance::getStandardProtocol

()) != nullptr; } TEST(AnyTest, Registered) { using conformance::StandardProtocol; { constexpr auto kType = "facebook.com/thrift/test/AnyTestStruct"; EXPECT_TRUE(isRegistered(kType)); EXPECT_TRUE(isRegistered(kType)); EXPECT_TRUE(isRegistered(kType)); EXPECT_FALSE(isRegistered(kType)); } { constexpr auto kType = "facebook.com/thrift/test/AnyTestException"; EXPECT_TRUE(isRegistered(kType)); EXPECT_TRUE(isRegistered(kType)); EXPECT_TRUE(isRegistered(kType)); EXPECT_FALSE(isRegistered(kType)); } { // Has Json enabled. constexpr auto kType = "facebook.com/thrift/test/AnyTestUnion"; EXPECT_TRUE(isRegistered(kType)); EXPECT_TRUE(isRegistered(kType)); EXPECT_TRUE(isRegistered(kType)); EXPECT_FALSE(isRegistered(kType)); } { // Does not have `any` in buck target options. constexpr auto kType = "facebook.com/thrift/test/AnyTestMissingAnyOption"; EXPECT_FALSE(isRegistered(kType)); EXPECT_FALSE(isRegistered(kType)); EXPECT_FALSE(isRegistered(kType)); EXPECT_FALSE(isRegistered(kType)); } } TEST(AnyTest, BaseApi) { SemiAny builder; EXPECT_THROW(AnyData{builder}, std::runtime_error); builder.type() = i16_t{}; EXPECT_THROW(AnyData{builder}, std::runtime_error); builder.protocol() = StandardProtocol::Compact; builder.data() = folly::IOBuf::wrapBufferAsValue("hi", 2); AnyData any(builder); EXPECT_EQ(any.type(), Type::get()); EXPECT_EQ(any.protocol(), Protocol::get()); EXPECT_EQ(any.data().data(), builder.data()->data()); EXPECT_EQ(any.data().length(), 2); builder.type() = {}; EXPECT_THROW(AnyData{builder}, std::runtime_error); } template class AnyTestFixture : public ::testing::Test {}; TYPED_TEST_SUITE(AnyTestFixture, Tags); TYPED_TEST(AnyTestFixture, ToAny) { const auto& value = tagToValue; AnyData any; native_type v1, v2; if constexpr ( !std::is_same_v && !std::is_same_v) { // Rely on infer_tag if TypeParam is not string_t or binary_t any = AnyData::toAny(value); std::as_const(any).get(v1); } else { any = toAnyData(); std::as_const(any).get(v1); } EXPECT_EQ(v1, value); EXPECT_EQ(any.type(), Type{TypeParam{}}); EXPECT_EQ(any.protocol(), Protocol::get()); CompactProtocolReader reader; reader.setInput(&any.data()); op::decode(reader, v2); EXPECT_EQ(v2, value); } TYPED_TEST(AnyTestFixture, MoveToSemiAny) { const auto& value = tagToValue; AnyData any; if constexpr ( !std::is_same_v && !std::is_same_v) { // Rely on infer_tag if TypeParam is not string_t or binary_t any = AnyData::toAny(value); } else { any = toAnyData(); } auto anyCopy = any; auto semiAny = std::move(any).moveToSemiAny(); AnyData anyFromSemiAny(semiAny); EXPECT_EQ(anyCopy, anyFromSemiAny); } bool contains(std::string_view s, std::string_view pattern) { return s.find(pattern) != std::string_view::npos; } TEST(AnyTest, GetTypeMismatch) { auto any = toAnyData(); int16_t i = 0; // We don't use EXPECT_THROW since we want to check the content try { any.get(i); EXPECT_TRUE(false); } catch (std::runtime_error& e) { EXPECT_TRUE(contains(e.what(), "Type mismatch")); EXPECT_TRUE(contains(e.what(), "i16Type")); EXPECT_TRUE(contains(e.what(), "i32Type")); } } TEST(AnyTest, UnsupportedProtocol) { SemiAny builder; builder.type() = struct_t{}; builder.protocol() = StandardProtocol::SimpleJson; builder.data() = folly::IOBuf::wrapBufferAsValue("{}", 2); AnyData any(builder); test::AnyTestStruct strct; // We don't use EXPECT_THROW since we want to check the content try { any.get(strct); EXPECT_TRUE(false); } catch (std::runtime_error& e) { EXPECT_TRUE(contains(e.what(), "Unsupported protocol")); } } TYPED_TEST(AnyTestFixture, BinaryProtocol) { const auto& value = tagToValue; AnyData any; if constexpr ( !std::is_same_v && !std::is_same_v) { // Rely on infer_tag if TypeParam is not string_t or binary_t any = AnyData::toAny(value); } else { any = toAnyData(); } EXPECT_EQ(any.type(), Type{TypeParam{}}); EXPECT_EQ(any.protocol(), Protocol::get()); native_type v; any.get(v); EXPECT_EQ(v, value); } TEST(AnyTest, isValid) { AnyStruct builder; // Any is empty, so it is valid EXPECT_TRUE(AnyData::isValid(builder)); builder.data() = folly::IOBuf::wrapBufferAsValue("{}", 2); // type and protocol are missing EXPECT_FALSE(AnyData::isValid(builder)); builder.type() = struct_t{}; // protocol is missing EXPECT_FALSE(AnyData::isValid(builder)); builder.protocol() = StandardProtocol::SimpleJson; // All fields have valid values EXPECT_TRUE(AnyData::isValid(builder)); } } // namespace } // namespace apache::thrift::test