From cac2c3ff7aa7b94e3776bb1282765ca78be977ae Mon Sep 17 00:00:00 2001 From: zxu Date: Wed, 17 Jan 2018 13:18:54 -0500 Subject: implement FieldValue for null, boolean, and array in C++. (#637) * implement FieldValue for null and boolean. * refactoring to use union type instead of poly * refactor using union design intead of poly * refactoring to use anonymous union and fix styles * small fix * add field_value_test to the project * fix warning of cmake and fix style --- .../Example/Firestore.xcodeproj/project.pbxproj | 13 ++ Firestore/core/CMakeLists.txt | 6 +- .../src/firebase/firestore/model/CMakeLists.txt | 22 +++ .../src/firebase/firestore/model/field_value.cc | 176 +++++++++++++++++++++ .../src/firebase/firestore/model/field_value.h | 130 +++++++++++++++ .../src/firebase/firestore/util/firebase_assert.h | 4 +- .../test/firebase/firestore/model/CMakeLists.txt | 22 +++ .../firebase/firestore/model/field_value_test.cc | 164 +++++++++++++++++++ 8 files changed, 533 insertions(+), 4 deletions(-) create mode 100644 Firestore/core/src/firebase/firestore/model/CMakeLists.txt create mode 100644 Firestore/core/src/firebase/firestore/model/field_value.cc create mode 100644 Firestore/core/src/firebase/firestore/model/field_value.h create mode 100644 Firestore/core/test/firebase/firestore/model/CMakeLists.txt create mode 100644 Firestore/core/test/firebase/firestore/model/field_value_test.cc diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 6f2bf5a..538758d 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ 6ED54761B845349D43DB6B78 /* Pods_Firestore_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; + AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = AB356EF6200EA5EB0089B766 /* field_value_test.cc */; }; AB382F7C1FE02A1F007CA955 /* FIRDocumentReferenceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB382F7B1FE02A1F007CA955 /* FIRDocumentReferenceTests.m */; }; AB382F7E1FE03059007CA955 /* FIRFieldPathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB382F7D1FE03059007CA955 /* FIRFieldPathTests.m */; }; AB9945261FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9945251FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m */; }; @@ -245,6 +246,7 @@ 8E002F4AD5D9B6197C940847 /* Firestore.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Firestore.podspec; path = ../Firestore.podspec; sourceTree = ""; }; 9D52E67EE96AA7E5D6F69748 /* Pods-Firestore_IntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_IntegrationTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_IntegrationTests/Pods-Firestore_IntegrationTests.debug.xcconfig"; sourceTree = ""; }; 9EF477AD4B2B643FD320867A /* Pods-Firestore_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example/Pods-Firestore_Example.debug.xcconfig"; sourceTree = ""; }; + AB356EF6200EA5EB0089B766 /* field_value_test.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = field_value_test.cc; sourceTree = ""; }; AB382F7B1FE02A1F007CA955 /* FIRDocumentReferenceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRDocumentReferenceTests.m; sourceTree = ""; }; AB382F7D1FE03059007CA955 /* FIRFieldPathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRFieldPathTests.m; sourceTree = ""; }; AB9945251FE2D71100DFC1E6 /* FIRCollectionReferenceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRCollectionReferenceTests.m; sourceTree = ""; }; @@ -408,6 +410,7 @@ 54764FAC1FAA0C390085E60A /* GoogleTests */ = { isa = PBXGroup; children = ( + AB356EF5200E9D1A0089B766 /* model */, 54764FAD1FAA0C650085E60A /* Port */, 54740A561FC913EB00713A1A /* util */, 54764FAE1FAA21B90085E60A /* FSTGoogleTestTests.mm */, @@ -540,6 +543,15 @@ name = Pods; sourceTree = ""; }; + AB356EF5200E9D1A0089B766 /* model */ = { + isa = PBXGroup; + children = ( + AB356EF6200EA5EB0089B766 /* field_value_test.cc */, + ); + name = model; + path = ../../core/test/firebase/firestore/model; + sourceTree = ""; + }; DE0761E51F2FE611003233AF /* SwiftBuildTest */ = { isa = PBXGroup; children = ( @@ -1236,6 +1248,7 @@ DE51B1F11F0D49140013853F /* FSTMutationTests.m in Sources */, DE51B1FB1F0D492C0013853F /* FSTMemorySpecTests.m in Sources */, DE51B1DB1F0D490D0013853F /* FSTLevelDBQueryCacheTests.m in Sources */, + AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */, 54764FAB1FAA0C320085E60A /* string_util_test.cc in Sources */, 54E9282C1F339CAD00C1953E /* XCTestCase+Await.m in Sources */, AB99452E1FE30AC800DFC1E6 /* FIRFieldValueTests.m in Sources */, diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index 9291fbd..da08dfc 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -12,8 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -add_subdirectory(src/firebase/firestore/util) +add_subdirectory(src/firebase/firestore/model) add_subdirectory(src/firebase/firestore/remote) +add_subdirectory(src/firebase/firestore/util) -add_subdirectory(test/firebase/firestore/util) +add_subdirectory(test/firebase/firestore/model) add_subdirectory(test/firebase/firestore/remote) +add_subdirectory(test/firebase/firestore/util) diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt new file mode 100644 index 0000000..1beb3bb --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2018 Google +# +# 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. + +add_library( + firebase_firestore_model + field_value.cc +) +target_link_libraries( + firebase_firestore_model + firebase_firestore_util +) diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc new file mode 100644 index 0000000..5a19549 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -0,0 +1,176 @@ +/* + * Copyright 2018 Google + * + * 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 "Firestore/core/src/firebase/firestore/model/field_value.h" + +#include +#include +#include + +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +using Type = FieldValue::Type; + +namespace { +/** + * This deviates from the other platforms that define TypeOrder. Since + * we already define Type for union types, we use it together with this + * function to achive the equivalent order of types i.e. + * i) if two types are comparable, then they are of equal order; + * ii) otherwise, their order is the same as the order of their Type. + */ +bool Comparable(Type lhs, Type rhs) { + switch (lhs) { + case Type::Long: + case Type::Double: + return rhs == Type::Long || rhs == Type::Double; + case Type::Timestamp: + case Type::ServerTimestamp: + return rhs == Type::Timestamp || rhs == Type::ServerTimestamp; + default: + return lhs == rhs; + } +} + +} // namespace + +FieldValue::FieldValue(const FieldValue& value) { + *this = value; +} + +FieldValue::FieldValue(FieldValue&& value) { + *this = std::move(value); +} + +FieldValue::~FieldValue() { + SwitchTo(Type::Null); +} + +FieldValue& FieldValue::operator=(const FieldValue& value) { + SwitchTo(value.tag_); + switch (tag_) { + case Type::Null: + break; + case Type::Boolean: + boolean_value_ = value.boolean_value_; + break; + case Type::Array: { + // copy-and-swap + std::vector tmp = value.array_value_; + std::swap(array_value_, tmp); + break; + } + default: + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + false, lhs.type(), "Unsupported type %d", value.type()); + } + return *this; +} + +FieldValue& FieldValue::operator=(FieldValue&& value) { + switch (value.tag_) { + case Type::Array: + SwitchTo(Type::Array); + std::swap(array_value_, value.array_value_); + return *this; + default: + // We just copy over POD union types. + return *this = value; + } +} + +const FieldValue& FieldValue::NullValue() { + static const FieldValue kNullInstance; + return kNullInstance; +} + +const FieldValue& FieldValue::TrueValue() { + static const FieldValue kTrueInstance(true); + return kTrueInstance; +} + +const FieldValue& FieldValue::FalseValue() { + static const FieldValue kFalseInstance(false); + return kFalseInstance; +} + +const FieldValue& FieldValue::BooleanValue(bool value) { + return value ? TrueValue() : FalseValue(); +} + +FieldValue FieldValue::ArrayValue(const std::vector& value) { + std::vector copy(value); + return ArrayValue(std::move(copy)); +} + +FieldValue FieldValue::ArrayValue(std::vector&& value) { + FieldValue result; + result.SwitchTo(Type::Array); + std::swap(result.array_value_, value); + return result; +} + +bool operator<(const FieldValue& lhs, const FieldValue& rhs) { + if (!Comparable(lhs.type(), rhs.type())) { + return lhs.type() < rhs.type(); + } + + switch (lhs.type()) { + case Type::Null: + return false; + case Type::Boolean: + // lhs < rhs iff lhs == false and rhs == true. + return !lhs.boolean_value_ && rhs.boolean_value_; + case Type::Array: + return lhs.array_value_ < rhs.array_value_; + default: + FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION( + false, lhs.type(), "Unsupported type %d", lhs.type()); + // return false if assertion does not abort the program. We will say + // each unsupported type takes only one value thus everything is equal. + return false; + } +} + +void FieldValue::SwitchTo(const Type type) { + if (tag_ == type) { + return; + } + // Not same type. Destruct old type first and then initialize new type. + // Must call destructor explicitly for any non-POD type. + switch (tag_) { + case Type::Array: + array_value_.~vector(); + break; + default:; // The other types where there is nothing to worry about. + } + tag_ = type; + // Must call constructor explicitly for any non-POD type to initialize. + switch (tag_) { + case Type::Array: + new (&array_value_) std::vector(); + break; + default:; // The other types where there is nothing to worry about. + } +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h new file mode 100644 index 0000000..781e34f --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -0,0 +1,130 @@ +/* + * Copyright 2018 Google + * + * 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ + +#include +#include + +namespace firebase { +namespace firestore { +namespace model { + +/** + * tagged-union class representing an immutable data value as stored in + * Firestore. FieldValue represents all the different kinds of values + * that can be stored in fields in a document. + */ +class FieldValue { + public: + /** + * All the different kinds of values that can be stored in fields in + * a document. The types of the same comparison order should be defined + * together as a group. The order of each group is defined by the Firestore + * backend and is available at: + * https://firebase.google.com/docs/firestore/manage-data/data-types + */ + enum class Type { + Null, // Null + Boolean, // Boolean + Long, // Number type starts here + Double, + Timestamp, // Timestamp type starts here + ServerTimestamp, + String, // String + Blob, // Blob + Reference, // Reference + GeoPoint, // GeoPoint + Array, // Array + Object, // Object + // New enum should not always been added at the tail. Add it to the correct + // position instead, see the doc comment above. + }; + + FieldValue() : tag_(Type::Null) { + } + + // Do not inline these ctor/dtor below, which contain call to non-trivial + // operator=. + FieldValue(const FieldValue& value); + FieldValue(FieldValue&& value); + + ~FieldValue(); + + FieldValue& operator=(const FieldValue& value); + FieldValue& operator=(FieldValue&& value); + + /** Returns the true type for this value. */ + Type type() const { + return tag_; + } + + /** factory methods. */ + static const FieldValue& NullValue(); + static const FieldValue& TrueValue(); + static const FieldValue& FalseValue(); + static const FieldValue& BooleanValue(bool value); + static FieldValue ArrayValue(const std::vector& value); + static FieldValue ArrayValue(std::vector&& value); + + friend bool operator<(const FieldValue& lhs, const FieldValue& rhs); + + private: + explicit FieldValue(bool value) : tag_(Type::Boolean), boolean_value_(value) { + } + + /** + * Switch to the specified type, if different from the current type. + */ + void SwitchTo(const Type type); + + Type tag_; + union { + // There is no null type as tag_ alone is enough for Null FieldValue. + bool boolean_value_; + std::vector array_value_; + }; +}; + +/** Compares against another FieldValue. */ +bool operator<(const FieldValue& lhs, const FieldValue& rhs); + +inline bool operator>(const FieldValue& lhs, const FieldValue& rhs) { + return rhs < lhs; +} + +inline bool operator>=(const FieldValue& lhs, const FieldValue& rhs) { + return !(lhs < rhs); +} + +inline bool operator<=(const FieldValue& lhs, const FieldValue& rhs) { + return !(lhs > rhs); +} + +inline bool operator!=(const FieldValue& lhs, const FieldValue& rhs) { + return lhs < rhs || lhs > rhs; +} + +inline bool operator==(const FieldValue& lhs, const FieldValue& rhs) { + return !(lhs != rhs); +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ diff --git a/Firestore/core/src/firebase/firestore/util/firebase_assert.h b/Firestore/core/src/firebase/firestore/util/firebase_assert.h index da01864..cff550a 100644 --- a/Firestore/core/src/firebase/firestore/util/firebase_assert.h +++ b/Firestore/core/src/firebase/firestore/util/firebase_assert.h @@ -52,7 +52,7 @@ #else #define FIREBASE_DEV_ASSERT_WITH_EXPRESSION(condition, expression) \ FIREBASE_ASSERT_WITH_EXPRESSION(condition, expression) -#endif // !defined(NDEBUG) +#endif // defined(NDEBUG) // Custom assert() implementation that is not compiled out in release builds. #define FIREBASE_ASSERT(expression) \ @@ -85,7 +85,7 @@ #define FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, \ ...) \ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(condition, expression, __VA_ARGS__) -#endif // !defined(NDEBUG) +#endif // defined(NDEBUG) namespace firebase { namespace firestore { diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt new file mode 100644 index 0000000..f5d9c2c --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2018 Google +# +# 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. + +cc_test( + firebase_firestore_model_test + field_value_test.cc +) +target_link_libraries( + firebase_firestore_model_test + firebase_firestore_model +) diff --git a/Firestore/core/test/firebase/firestore/model/field_value_test.cc b/Firestore/core/test/firebase/firestore/model/field_value_test.cc new file mode 100644 index 0000000..1194e63 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc @@ -0,0 +1,164 @@ +/* + * Copyright 2018 Google + * + * 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 "Firestore/core/src/firebase/firestore/model/field_value.h" + +#include + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +using Type = FieldValue::Type; + +TEST(FieldValue, NullType) { + const FieldValue value = FieldValue::NullValue(); + EXPECT_EQ(Type::Null, value.type()); + EXPECT_FALSE(value < value); +} + +TEST(FieldValue, BooleanType) { + const FieldValue true_value = FieldValue::BooleanValue(true); + const FieldValue false_value = FieldValue::BooleanValue(false); + EXPECT_EQ(Type::Boolean, true_value.type()); + EXPECT_FALSE(true_value < true_value); + EXPECT_FALSE(true_value < false_value); + EXPECT_FALSE(false_value < false_value); + EXPECT_TRUE(false_value < true_value); +} + +TEST(FieldValue, ArrayType) { + const FieldValue empty = + FieldValue::ArrayValue(std::vector{}); + std::vector array{FieldValue::NullValue(), + FieldValue::BooleanValue(true), + FieldValue::BooleanValue(false)}; + // copy the array + const FieldValue small = FieldValue::ArrayValue(array); + std::vector another_array{FieldValue::BooleanValue(true), + FieldValue::BooleanValue(false)}; + // move the array + const FieldValue large = FieldValue::ArrayValue(std::move(another_array)); + EXPECT_EQ(Type::Array, empty.type()); + EXPECT_EQ(Type::Array, small.type()); + EXPECT_EQ(Type::Array, large.type()); + EXPECT_TRUE(empty < small); + EXPECT_FALSE(small < empty); + EXPECT_FALSE(small < small); + EXPECT_TRUE(small < large); + EXPECT_FALSE(large < small); +} + +TEST(FieldValue, Copy) { + FieldValue clone = FieldValue::TrueValue(); + const FieldValue null_value = FieldValue::NullValue(); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + EXPECT_EQ(FieldValue::NullValue(), null_value); + clone = clone; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue true_value = FieldValue::TrueValue(); + clone = true_value; + EXPECT_EQ(FieldValue::TrueValue(), clone); + EXPECT_EQ(FieldValue::TrueValue(), true_value); + clone = clone; + EXPECT_EQ(FieldValue::TrueValue(), clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); + + const FieldValue array_value = + FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}); + clone = array_value; + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + clone); + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + array_value); + clone = clone; + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + clone); + clone = null_value; + EXPECT_EQ(FieldValue::NullValue(), clone); +} + +TEST(FieldValue, Move) { + FieldValue clone = FieldValue::TrueValue(); + + FieldValue null_value = FieldValue::NullValue(); + clone = std::move(null_value); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue true_value = FieldValue::TrueValue(); + clone = std::move(true_value); + EXPECT_EQ(FieldValue::TrueValue(), clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); + + FieldValue array_value = FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}); + clone = std::move(array_value); + EXPECT_EQ(FieldValue::ArrayValue(std::vector{ + FieldValue::TrueValue(), FieldValue::FalseValue()}), + clone); + clone = FieldValue::NullValue(); + EXPECT_EQ(FieldValue::NullValue(), clone); +} + +TEST(FieldValue, CompareMixedType) { + const FieldValue null_value = FieldValue::NullValue(); + const FieldValue true_value = FieldValue::TrueValue(); + const FieldValue array_value = + FieldValue::ArrayValue(std::vector()); + EXPECT_TRUE(null_value < true_value); + EXPECT_TRUE(true_value < array_value); +} + +TEST(FieldValue, CompareWithOperator) { + const FieldValue small = FieldValue::NullValue(); + const FieldValue large = FieldValue::TrueValue(); + + EXPECT_TRUE(small < large); + EXPECT_FALSE(small < small); + EXPECT_FALSE(large < small); + + EXPECT_TRUE(large > small); + EXPECT_FALSE(small > small); + EXPECT_FALSE(small > large); + + EXPECT_TRUE(large >= small); + EXPECT_TRUE(small >= small); + EXPECT_FALSE(small >= large); + + EXPECT_TRUE(small <= large); + EXPECT_TRUE(small <= small); + EXPECT_FALSE(large <= small); + + EXPECT_TRUE(small != large); + EXPECT_FALSE(small != small); + + EXPECT_TRUE(small == small); + EXPECT_FALSE(small == large); +} + +} // namespace model +} // namespace firestore +} // namespace firebase -- cgit v1.2.3