/* * 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 namespace apache::thrift::op { namespace { TEST(CompareTest, IOBuf) { using STag = type::cpp_type; using BTag = type::cpp_type; auto one = IOBuf::wrapBufferAsValue("a", 1); auto two = IOBuf::wrapBufferAsValue("ab", 2); EXPECT_TRUE(op::equal(one, one)); EXPECT_FALSE(op::equal(one, two)); EXPECT_FALSE((op::equal(two, one))); EXPECT_TRUE((op::equal(two, two))); EXPECT_FALSE(op::less(one, one)); EXPECT_TRUE(op::less(one, two)); EXPECT_FALSE((op::less(two, one))); EXPECT_FALSE((op::less(two, two))); EXPECT_EQ(op::compare(one, one), folly::ordering::eq); EXPECT_EQ(op::compare(one, two), folly::ordering::lt); EXPECT_EQ((op::compare(two, one)), folly::ordering::gt); EXPECT_EQ((op::compare(two, two)), folly::ordering::eq); } TEST(CompareTest, Double) { auto hash = op::hash; EXPECT_EQ(hash(0.0), hash(-0.0)); // 1 is identical and equal to itself. EXPECT_TRUE(identical(1.0, 1.0)); EXPECT_TRUE(equal(1.0, 1.0)); EXPECT_FALSE(less(1.0, 1.0)); EXPECT_EQ(compare(1.0, 1.0), folly::ordering::eq); // 1 is neither identical or equal to 2. EXPECT_FALSE(identical(1.0, 2.0)); EXPECT_FALSE(equal(1.0, 2.0)); EXPECT_TRUE(less(1.0, 2.0)); EXPECT_EQ(compare(1.0, 2.0), folly::ordering::lt); // -0 is equal to, but not identical to 0. EXPECT_FALSE(identical<>(-0.0, +0.0)); EXPECT_TRUE(equal<>(-0.0, +0.0)); EXPECT_FALSE(less<>(-0.0, +0.0)); EXPECT_EQ(compare<>(-0.0, +0.0), folly::ordering::eq); // NaN is identical to, but not equal to itself. EXPECT_TRUE(identical( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); EXPECT_FALSE(equal( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); EXPECT_FALSE(less( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); EXPECT_EQ( compare( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()), folly::ordering::gt); } TEST(CompareTest, Float) { auto hash = op::hash; EXPECT_EQ(hash(0.0f), hash(-0.0f)); // 1 is equal and identical to itself. EXPECT_TRUE(equal(1.0f, 1.0f)); EXPECT_TRUE(identical(1.0f, 1.0f)); // 1 is neither equal or identical to 2. EXPECT_FALSE(equal(1.0f, 2.0f)); EXPECT_FALSE(identical(1.0f, 2.0f)); // -0 is equal to, but not identical to 0. EXPECT_TRUE(equal(-0.0f, +0.0f)); EXPECT_FALSE(identical(-0.0f, +0.0f)); // NaN is identical to, but not equal to itself. EXPECT_FALSE(equal( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); EXPECT_TRUE(identical( std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); } TEST(CompareTest, StructWithFloat) { protocol::Value lhs; protocol::Value rhs; EqualTo> equal; IdenticalTo> identical; lhs.floatValue_ref().ensure() = std::numeric_limits::quiet_NaN(); rhs.floatValue_ref().ensure() = std::numeric_limits::quiet_NaN(); EXPECT_FALSE(op::equal(lhs, rhs)); EXPECT_FALSE(op::identical(lhs, rhs)); // Should be true! lhs.floatValue_ref().ensure() = -0.0f; rhs.floatValue_ref().ensure() = +0.0f; EXPECT_TRUE(equal(lhs, rhs)); EXPECT_TRUE(identical(lhs, rhs)); // Should be false! } TEST(CompareTest, ListWithDouble) { EqualTo> equal; IdenticalTo> identical; EXPECT_FALSE(equal( {1, std::numeric_limits::quiet_NaN()}, {1, std::numeric_limits::quiet_NaN()})); EXPECT_TRUE(identical( {1, std::numeric_limits::quiet_NaN()}, {1, std::numeric_limits::quiet_NaN()})); EXPECT_TRUE(equal({-0.0, 2.0}, {+0.0, 2.0})); EXPECT_FALSE(identical({-0.0, 2.0}, {+0.0, 2.0})); } TEST(CompareTest, SetWithDouble) { EqualTo> equal; IdenticalTo> identical; // Note: NaN in a set is undefined behavior. EXPECT_TRUE(equal({-0.0, 2.0}, {+0.0, 2.0})); EXPECT_FALSE(identical({-0.0, 2.0}, {+0.0, 2.0})); } TEST(CompareTest, MapWithDouble) { EqualTo> equal; IdenticalTo> identical; // Note: NaN in a map keys is undefined behavior. EXPECT_FALSE(equal( {{1, std::numeric_limits::quiet_NaN()}}, {{1, std::numeric_limits::quiet_NaN()}})); EXPECT_TRUE(identical( {{1, std::numeric_limits::quiet_NaN()}}, {{1, std::numeric_limits::quiet_NaN()}})); EXPECT_TRUE(equal({{-0.0, 2.0}}, {{+0.0, 2.0}})); EXPECT_FALSE(identical({{-0.0, 2.0}}, {{+0.0, 2.0}})); EXPECT_TRUE(equal({{2.0, +0.0}}, {{2.0, -0.0}})); EXPECT_FALSE(identical({{2.0, +0.0}}, {{2.0, -0.0}})); EXPECT_TRUE(equal({{-0.0, +0.0}}, {{+0.0, -0.0}})); EXPECT_FALSE(identical({{-0.0, +0.0}}, {{+0.0, -0.0}})); } // Sets and maps that use representational uniqueness. template > using InternSet = std::unordered_set, IdenticalTo>; template using InternSetTag = type::cpp_type, type::set>; TEST(CompareTest, InternSet_Dbl) { using Tag = InternSetTag; using SetT = type::native_type; static_assert(detail::less_than_comparable_v, ""); static_assert(detail::comparable_v, ""); EXPECT_FALSE(identical({0.0}, {-0.0})); EXPECT_TRUE(equal({0.0}, {-0.0})); EXPECT_TRUE(less({0}, {1})); EXPECT_FALSE(less({0, 1}, {1, 0})); SetT set{0.0, -0.0}; EXPECT_EQ(set.size(), 2); EXPECT_TRUE(identical(set, SetT(set))); EXPECT_TRUE(equal(set, SetT(set))); EXPECT_FALSE(less(set, SetT(set))); } template < typename KTag, typename VTag, typename K = type::native_type, typename V = type::native_type> using InternMap = std::unordered_map, IdenticalTo>; template using InternMapTag = type::cpp_type, type::map>; TEST(CompareTest, InternMap_Flt) { using Tag = InternMapTag; using MapT = type::native_type; static_assert(detail::less_than_comparable_v, ""); static_assert(detail::comparable_v, ""); MapT map{{0.0f, 0.0f}, {-0.0f, 0.0f}}; EXPECT_EQ(map.size(), 2); // identical and equal to a copy of itself. EXPECT_TRUE(identical(map, MapT(map))); EXPECT_TRUE(equal(map, MapT(map))); EXPECT_FALSE(less(map, MapT(map))); // Equal, but not identical to a map with equal but not identical values. MapT otherMap{{0.0f, 0.0f}, {-0.0f, -0.0f}}; EXPECT_FALSE(identical(map, otherMap)); EXPECT_TRUE(equal(map, otherMap)); EXPECT_FALSE(less(map, otherMap)); MapT largerMap{{1.0f, 0.0f}}; EXPECT_FALSE(identical(map, largerMap)); EXPECT_FALSE(equal(map, largerMap)); EXPECT_TRUE(less(map, largerMap)); } TEST(CompareTest, Struct) { detail::StructEquality equalTo; detail::StructLessThan lessThan; test::OneOfEach lhs; test::OneOfEach rhs; EXPECT_TRUE(equalTo(lhs, rhs)); EXPECT_FALSE(lessThan(lhs, rhs)); --*lhs.myStruct()->mySubI64(); EXPECT_FALSE(equalTo(lhs, rhs)); EXPECT_TRUE(lessThan(lhs, rhs)); --*rhs.myStruct()->mySubI64(); EXPECT_TRUE(equalTo(lhs, rhs)); EXPECT_FALSE(lessThan(lhs, rhs)); // compare lhs with lhs EXPECT_TRUE(equalTo(lhs, lhs)); EXPECT_FALSE(lessThan(lhs, lhs)); } TEST(CompareTest, UnorderedFields) { detail::StructEquality equalTo; detail::StructLessThan lessThan; test::CppTemplateListField lhs; test::CppTemplateListField rhs; EXPECT_TRUE(equalTo(lhs, rhs)); EXPECT_FALSE(lessThan(lhs, rhs)); lhs.f1()->push_front("0"); EXPECT_FALSE(equalTo(lhs, rhs)); EXPECT_TRUE(lessThan(lhs, rhs)); rhs.f1()->push_front("0"); EXPECT_TRUE(equalTo(lhs, rhs)); EXPECT_FALSE(lessThan(lhs, rhs)); // compare lhs with lhs EXPECT_TRUE(equalTo(lhs, lhs)); EXPECT_FALSE(lessThan(lhs, lhs)); } TEST(CompareTest, Union) { detail::UnionEquality equalTo; detail::UnionLessThan lessThan; test::UnionIntegers empty, u1, u2; u1.myI16_ref() = 10; u2.myI32_ref() = 1; EXPECT_TRUE(lessThan(empty, u1)); EXPECT_FALSE(equalTo(empty, u1)); EXPECT_TRUE(lessThan(u1, u2)); EXPECT_FALSE(equalTo(u1, u2)); EXPECT_FALSE(lessThan(u1, u1)); EXPECT_TRUE(equalTo(u1, u1)); EXPECT_FALSE(lessThan(u2, u1)); EXPECT_FALSE(equalTo(u2, u1)); EXPECT_FALSE(lessThan(empty, empty)); EXPECT_TRUE(equalTo(empty, empty)); u2.myI16_ref() = 10; EXPECT_FALSE(lessThan(u1, u2)); EXPECT_TRUE(equalTo(u1, u2)); } TEST(CompareTest, ListIOBufCompare) { detail::StructEquality equalTo; detail::StructLessThan lessThan; test::ListIOBuf lhs, rhs; EXPECT_FALSE(lessThan(lhs, rhs)); EXPECT_TRUE(equalTo(lhs, rhs)); lhs.field()->push_back(*folly::IOBuf::copyBuffer("a", 1)); rhs.field()->push_back(*folly::IOBuf::copyBuffer("b", 1)); EXPECT_TRUE(lessThan(lhs, rhs)); EXPECT_FALSE(equalTo(lhs, rhs)); lhs.field()->insert(lhs.field()->begin(), *folly::IOBuf::copyBuffer("b", 1)); rhs.field()->push_back(*folly::IOBuf::copyBuffer("a", 1)); EXPECT_FALSE(lessThan(lhs, rhs)); EXPECT_TRUE(equalTo(lhs, rhs)); } TEST(CompareTest, MapIOBufCompare) { detail::StructEquality equalTo; detail::StructLessThan lessThan; test::ListIOBuf lhs, rhs; EXPECT_FALSE(lessThan(lhs, rhs)); EXPECT_TRUE(equalTo(lhs, rhs)); lhs.field_2()["10"]; rhs.field_2()["10"] = *folly::IOBuf::copyBuffer("a", 1); EXPECT_TRUE(lessThan(lhs, rhs)); EXPECT_FALSE(equalTo(lhs, rhs)); lhs.field_2()["10"] = *folly::IOBuf::copyBuffer("a", 1); EXPECT_FALSE(lessThan(lhs, rhs)); EXPECT_TRUE(equalTo(lhs, rhs)); rhs.field_2()["11"]; EXPECT_TRUE(lessThan(lhs, rhs)); EXPECT_FALSE(equalTo(lhs, rhs)); } TEST(CompareTest, Nan) { test::OneOfEach value; value.myDouble() = NAN; EXPECT_FALSE(detail::StructEquality{}(value, value)); EXPECT_FALSE(detail::StructLessThan{}(value, value)); } } // namespace } // namespace apache::thrift::op