/* * 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. */ // gtest matchers for thrift ops. #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace apache::thrift::test { template > struct IdenticalToMatcher { T expected; template bool MatchAndExplain(const U& actual, ::testing::MatchResultListener*) const { return op::identical(actual, expected); } void DescribeTo(std::ostream* os) const { *os << "is identical to " << ::testing::PrintToString(expected); } void DescribeNegationTo(std::ostream* os) const { *os << "is not identical to " << ::testing::PrintToString(expected); } }; template > auto IsIdenticalTo(T expected) { return ::testing::MakePolymorphicMatcher( IdenticalToMatcher{std::move(expected)}); } template struct EqualToMatcher { T expected; template bool MatchAndExplain(const U& actual, ::testing::MatchResultListener*) const { return op::equal(actual, expected); } void DescribeTo(std::ostream* os) const { *os << "is equal to " << ::testing::PrintToString(expected); } void DescribeNegationTo(std::ostream* os) const { *os << "is not equal to " << ::testing::PrintToString(expected); } }; template > auto IsEqualTo(T expected) { return ::testing::MakePolymorphicMatcher( EqualToMatcher{std::move(expected)}); } template struct EmptyMatcher { template bool MatchAndExplain(const U& actual, ::testing::MatchResultListener*) const { return op::isEmpty(actual); } void DescribeTo(std::ostream* os) const { *os << "is empty"; } void DescribeNegationTo(std::ostream* os) const { *os << "is not empty"; } }; template auto IsEmpty() { return ::testing::MakePolymorphicMatcher(EmptyMatcher{}); } // Applies a patch twice and checks the results. template < typename P, typename T1 = typename P::value_type, typename T2 = T1, typename T3 = T2> void expectPatch( P patch, const T1& actual, const T2& expected1, const T3& expected2) { { // Applying twice produces the expected results. auto actual1 = actual; patch.apply(actual1); EXPECT_EQ(actual1, expected1); if (actual1 != expected1) { EXPECT_FALSE(patch.empty()); } patch.apply(actual1); EXPECT_EQ(actual1, expected2); } { // Merging with self, is the same as applying twice. patch.merge(patch); auto actual2 = actual; patch.apply(actual2); EXPECT_EQ(actual2, expected2); } { // Reset should be a noop patch. patch.reset(); EXPECT_TRUE(patch.empty()); auto actual3 = actual; patch.apply(actual3); EXPECT_EQ(actual3, actual); } } template void expectPatch( P patch, const folly::IOBuf& actual, const folly::IOBuf& expected1, const folly::IOBuf& expected2) { { // Applying twice produces the expected results. auto actual1 = actual; patch.apply(actual1); EXPECT_TRUE(folly::IOBufEqualTo{}(actual1, expected1)) << "actual " << actual1.to() << " expected " << expected1.to(); if (!folly::IOBufEqualTo{}(actual1, expected1)) { EXPECT_FALSE(patch.empty()); } patch.apply(actual1); EXPECT_TRUE(folly::IOBufEqualTo{}(actual1, expected2)) << "actual " << actual1.to() << " expected " << expected2.to(); ; } { // Merging with self, is the same as applying twice. patch.merge(patch); auto actual2 = actual; patch.apply(actual2); EXPECT_TRUE(folly::IOBufEqualTo{}(actual2, expected2)) << "actual " << actual2.to() << " expected " << expected2.to(); ; } { // Reset should be a noop patch. patch.reset(); EXPECT_TRUE(patch.empty()); auto actual3 = actual; patch.apply(actual3); EXPECT_TRUE(folly::IOBufEqualTo{}(actual3, actual)) << "actual " << actual3.to() << " expected " << actual.to(); ; } } template void expectPatch(P patch, const T1& actual, const T2& expected) { expectPatch(std::move(patch), actual, expected, expected); } // Checks round trips using the given serializer. template > void expectRoundTrip(const S& seralizer, const T& expected) { folly::IOBufQueue queue; seralizer.encode(expected, folly::io::QueueAppender{&queue, 2 << 4}); folly::io::Cursor cursor(queue.front()); T actual = seralizer.template decode(cursor); EXPECT_EQ(actual, expected); } // Always serializes integers to the number 1. class Number1Serializer : public op::TagSerializer { using Base = op::TagSerializer; public: const type::Protocol& getProtocol() const override; using Base::encode; void encode(const int&, folly::io::QueueAppender&& appender) const { std::string data = "number 1!!"; appender.push(reinterpret_cast(data.data()), data.size()); } using Base::decode; void decode(folly::io::Cursor& cursor, int& value) const { cursor.readFixedString(10); value = 1; } }; extern const type::Protocol kFollyToStringProtocol; // A serializer based on `folly::to`. template class FollyToStringSerializer : public op::TagSerializer> { using Base = op::TagSerializer; using T = type::native_type; public: const type::Protocol& getProtocol() const override { return kFollyToStringProtocol; } using Base::encode; void encode(const T& value, folly::io::QueueAppender&& appender) const { std::string data = folly::to(value); appender.push(reinterpret_cast(data.data()), data.size()); } using Base::decode; void decode(folly::io::Cursor& cursor, T& value) const { value = folly::to(cursor.readFixedString(cursor.totalLength())); } }; class MultiSerializer : public op::Serializer { using Base = op::Serializer; public: mutable size_t intEncCount = 0; mutable size_t dblEncCount = 0; mutable size_t intDecCount = 0; mutable size_t dblDecCount = 0; mutable size_t anyDecCount = 0; using Base::decode; using Base::encode; const type::Protocol& getProtocol() const override { return kFollyToStringProtocol; } void encode( type::ConstRef value, folly::io::QueueAppender&& appender) const override; void encode(type::AnyConstRef value, folly::io::QueueAppender&& appender) const override; void decode(folly::io::Cursor& cursor, type::Ref value) const override; void decode(folly::io::Cursor& cursor, type::AnyRef value) const override; void decode( const type::Type& type, folly::io::Cursor& cursor, type::AnyValue& value) const override; // Helper functions to check the statis void checkAndResetInt(size_t enc, size_t dec) const; void checkAndResetDbl(size_t enc, size_t dec) const; void checkAndResetAny(size_t dec) const; void checkAndResetAll() const; void checkAnyDec() const; void checkIntEnc() const; void checkIntDec() const; void checkDblEnc() const; void checkDblDec() const; void checkAnyIntDec() const; void checkAnyDblDec() const; }; } // namespace apache::thrift::test