diff --git a/Firestore/core/src/model/object_value.cc b/Firestore/core/src/model/object_value.cc index e18aecb8f15..4cb370a532a 100644 --- a/Firestore/core/src/model/object_value.cc +++ b/Firestore/core/src/model/object_value.cc @@ -24,6 +24,8 @@ #include "Firestore/core/src/nanopb/fields_array.h" #include "Firestore/core/src/nanopb/message.h" #include "Firestore/core/src/nanopb/nanopb_util.h" +#include "Firestore/core/src/util/hashing.h" +#include "absl/types/span.h" namespace firebase { namespace firestore { @@ -31,12 +33,15 @@ namespace model { namespace { -using nanopb::FieldsArray; +using nanopb::CheckedSize; using nanopb::FreeFieldsArray; +using nanopb::FreeNanopbMessage; using nanopb::MakeArray; using nanopb::MakeBytesArray; using nanopb::MakeString; using nanopb::MakeStringView; +using nanopb::ReleaseFieldOwnership; +using nanopb::SetRepeatedField; struct MapEntryKeyCompare { bool operator()(const google_firestore_v1_MapValue_FieldsEntry& entry, @@ -49,6 +54,28 @@ struct MapEntryKeyCompare { } }; +/** Traverses a Value proto and sorts all MapValues by key. */ +void SortFields(google_firestore_v1_Value& value) { + if (value.which_value_type == google_firestore_v1_Value_map_value_tag) { + google_firestore_v1_MapValue& map_value = value.map_value; + std::sort(map_value.fields, map_value.fields + map_value.fields_count, + [](const google_firestore_v1_MapValue_FieldsEntry& lhs, + const google_firestore_v1_MapValue_FieldsEntry& rhs) { + return nanopb::MakeStringView(lhs.key) < + nanopb::MakeStringView(rhs.key); + }); + + for (pb_size_t i = 0; i < map_value.fields_count; ++i) { + SortFields(map_value.fields[i].value); + } + } else if (value.which_value_type == + google_firestore_v1_Value_array_value_tag) { + for (pb_size_t i = 0; i < value.array_value.values_count; ++i) { + SortFields(value.array_value.values[i]); + } + } +} + /** * Finds an entry by key in the provided map value. Returns `nullptr` if the * entry does not exist. @@ -135,6 +162,7 @@ void ApplyChanges( target_entry.key = source_entry.key; target_entry.value = DeepClone(upsert_it->second); + SortFields(target_entry.value); ++upsert_it; ++source_index; @@ -162,22 +190,57 @@ void ApplyChanges( free(parent->fields); parent->fields = target_fields; - parent->fields_count = target_count; + parent->fields_count = CheckedSize(target_count); } } // namespace -MutableObjectValue::MutableObjectValue() { +ObjectValue::ObjectValue() { value_->which_value_type = google_firestore_v1_Value_map_value_tag; value_->map_value.fields_count = 0; value_->map_value.fields = nullptr; } -FieldMask MutableObjectValue::ToFieldMask() const { +ObjectValue::ObjectValue(const google_firestore_v1_Value& value) + : value_(value) { + HARD_ASSERT(value.which_value_type == google_firestore_v1_Value_map_value_tag, + "ObjectValues should be backed by a MapValue"); + SortFields(*value_); +} + +ObjectValue::ObjectValue(const ObjectValue& other) + : value_(DeepClone(*other.value_)) { +} + +ObjectValue ObjectValue::FromMapValue(google_firestore_v1_MapValue map_value) { + google_firestore_v1_Value value{}; + value.which_value_type = google_firestore_v1_Value_map_value_tag; + value.map_value = map_value; + return ObjectValue{value}; +} + +ObjectValue ObjectValue::FromFieldsEntry( + google_firestore_v1_Document_FieldsEntry* fields_entry, pb_size_t count) { + google_firestore_v1_Value value{}; + value.which_value_type = google_firestore_v1_Value_map_value_tag; + SetRepeatedField( + &value.map_value.fields, &value.map_value.fields_count, + absl::Span(fields_entry, count), + [](const google_firestore_v1_Document_FieldsEntry& entry) { + google_firestore_v1_MapValue_FieldsEntry result{}; + result.key = entry.key; + result.value = entry.value; + return result; + }); + ReleaseFieldOwnership(fields_entry, count); + return ObjectValue{value}; +} + +FieldMask ObjectValue::ToFieldMask() const { return ExtractFieldMask(value_->map_value); } -FieldMask MutableObjectValue::ExtractFieldMask( +FieldMask ObjectValue::ExtractFieldMask( const google_firestore_v1_MapValue& value) const { std::set fields; @@ -206,7 +269,7 @@ FieldMask MutableObjectValue::ExtractFieldMask( return FieldMask(std::move(fields)); } -absl::optional MutableObjectValue::Get( +absl::optional ObjectValue::Get( const FieldPath& path) const { if (path.empty()) { return *value_; @@ -222,8 +285,12 @@ absl::optional MutableObjectValue::Get( return nested_value; } -void MutableObjectValue::Set(const FieldPath& path, - const google_firestore_v1_Value& value) { +google_firestore_v1_Value ObjectValue::Get() const { + return *value_; +} + +void ObjectValue::Set(const FieldPath& path, + const google_firestore_v1_Value& value) { HARD_ASSERT(!path.empty(), "Cannot set field for empty path on ObjectValue"); google_firestore_v1_MapValue* parent_map = ParentMap(path.PopLast()); @@ -235,8 +302,7 @@ void MutableObjectValue::Set(const FieldPath& path, ApplyChanges(parent_map, upserts, /*deletes=*/{}); } -void MutableObjectValue::SetAll(const FieldMask& field_mask, - const MutableObjectValue& data) { +void ObjectValue::SetAll(const FieldMask& field_mask, const ObjectValue& data) { FieldPath parent; std::map upserts; @@ -264,7 +330,7 @@ void MutableObjectValue::SetAll(const FieldMask& field_mask, ApplyChanges(parent_map, upserts, deletes); } -void MutableObjectValue::Delete(const FieldPath& path) { +void ObjectValue::Delete(const FieldPath& path) { HARD_ASSERT(!path.empty(), "Cannot delete field with empty path"); google_firestore_v1_Value* nested_value = value_.get(); @@ -286,12 +352,19 @@ void MutableObjectValue::Delete(const FieldPath& path) { } } +std::string ObjectValue::ToString() const { + return CanonicalId(*value_); +} + +size_t ObjectValue::Hash() const { + return util::Hash(CanonicalId(*value_)); +} + /** * Returns the map that contains the leaf element of `path`. If the parent * entry does not yet exist, or if it is not a map, a new map will be created. */ -google_firestore_v1_MapValue* MutableObjectValue::ParentMap( - const FieldPath& path) { +google_firestore_v1_MapValue* ObjectValue::ParentMap(const FieldPath& path) { google_firestore_v1_Value* parent = value_.get(); // Find a or create a parent map entry for `path`. diff --git a/Firestore/core/src/model/object_value.h b/Firestore/core/src/model/object_value.h index 14b4f213578..2e3ea991ef2 100644 --- a/Firestore/core/src/model/object_value.h +++ b/Firestore/core/src/model/object_value.h @@ -37,26 +37,33 @@ namespace firestore { namespace model { /** A structured object value stored in Firestore. */ -// TODO(mutabledocuments): Rename to ObjectValue once other ObjectValue class -// is removed -class MutableObjectValue { +class ObjectValue { public: - MutableObjectValue(); + ObjectValue(); /** Creates a new MutableObjectValue and takes ownership of `value`. */ - explicit MutableObjectValue(const google_firestore_v1_Value& value) - : value_(value) { - HARD_ASSERT( - value.which_value_type == google_firestore_v1_Value_map_value_tag, - "ObjectValues should be backed by a MapValue"); - } + explicit ObjectValue(const google_firestore_v1_Value& value); - MutableObjectValue(MutableObjectValue&& other) noexcept = default; - MutableObjectValue& operator=(MutableObjectValue&& other) = default; + ObjectValue(ObjectValue&& other) noexcept = default; + ObjectValue& operator=(ObjectValue&& other) noexcept = default; + ObjectValue(const ObjectValue& other); - /** `MutableObjectValue` models unique ownership. */ - MutableObjectValue(const MutableObjectValue&) = delete; - MutableObjectValue& operator=(const MutableObjectValue&) = delete; + ObjectValue& operator=(const ObjectValue&) = delete; + + /** + * Creates a new ObjectValue that is backed by the given `map_value`. + * ObjectValue takes on ownership of the data. + */ + static ObjectValue FromMapValue(google_firestore_v1_MapValue map_value); + + /** + * Creates a new ObjectValue that is backed by the provided document fields. + * ObjectValue takes on ownership of the data and zeroes out the pointers in + * `fields_entry`. This allows the callsite to destruct the Document proto + * without affecting the fields data. + */ + static ObjectValue FromFieldsEntry( + google_firestore_v1_Document_FieldsEntry* fields_entry, pb_size_t count); /** Recursively extracts the FieldPaths that are set in this ObjectValue. */ FieldMask ToFieldMask() const; @@ -69,6 +76,11 @@ class MutableObjectValue { */ absl::optional Get(const FieldPath& path) const; + /** + * Returns the ObjectValue in its Protobuf representation. + */ + google_firestore_v1_Value Get() const; + /** * Sets the field to the provided value. * @@ -85,7 +97,7 @@ class MutableObjectValue { * @param field_mask The field mask that controls which fields to modify. * @param data A MutableObjectValue that contains the field values. */ - void SetAll(const FieldMask& field_mask, const MutableObjectValue& data); + void SetAll(const FieldMask& field_mask, const ObjectValue& data); /** * Removes the field at the specified path. If there is no field at the @@ -95,10 +107,13 @@ class MutableObjectValue { */ void Delete(const FieldPath& path); - friend bool operator==(const MutableObjectValue& lhs, - const MutableObjectValue& rhs); + std::string ToString() const; + + size_t Hash() const; + + friend bool operator==(const ObjectValue& lhs, const ObjectValue& rhs); friend std::ostream& operator<<(std::ostream& out, - const MutableObjectValue& object_value); + const ObjectValue& object_value); private: /** Returns the field mask for the provided map value. */ @@ -113,13 +128,12 @@ class MutableObjectValue { nanopb::Message value_; }; -inline bool operator==(const MutableObjectValue& lhs, - const MutableObjectValue& rhs) { +inline bool operator==(const ObjectValue& lhs, const ObjectValue& rhs) { return *lhs.value_ == *rhs.value_; } inline std::ostream& operator<<(std::ostream& out, - const MutableObjectValue& object_value) { + const ObjectValue& object_value) { return out << "ObjectValue(" << *object_value.value_ << ")"; } diff --git a/Firestore/core/test/unit/model/object_value_test.cc b/Firestore/core/test/unit/model/object_value_test.cc index 406f9196dcd..1c705b81123 100644 --- a/Firestore/core/test/unit/model/object_value_test.cc +++ b/Firestore/core/test/unit/model/object_value_test.cc @@ -35,43 +35,30 @@ using testutil::DbId; using testutil::Field; using testutil::Map; using testutil::Value; +using testutil::WrapObject; class ObjectValueTest : public ::testing::Test { - public: - template - google_firestore_v1_Value Wrap(T input) { - model::FieldValue fv = Value(input); - return serializer.EncodeFieldValue(fv); - } - - template - MutableObjectValue WrapObject(Args... key_value_pairs) { - FieldValue fv = testutil::WrapObject((key_value_pairs)...); - return MutableObjectValue{serializer.EncodeFieldValue(fv)}; - } - private: remote::Serializer serializer{DbId()}; }; TEST_F(ObjectValueTest, ExtractsFields) { - MutableObjectValue value = - WrapObject("foo", Map("a", 1, "b", true, "c", "string")); + ObjectValue value = WrapObject("foo", Map("a", 1, "b", true, "c", "string")); ASSERT_EQ(google_firestore_v1_Value_map_value_tag, value.Get(Field("foo"))->which_value_type); - EXPECT_TRUE(Wrap(1) == *value.Get(Field("foo.a"))); - EXPECT_TRUE(Wrap(true) == *value.Get(Field("foo.b"))); - EXPECT_TRUE(Wrap("string") == *value.Get(Field("foo.c"))); + EXPECT_EQ(Value(1), *value.Get(Field("foo.a"))); + EXPECT_EQ(Value(true), *value.Get(Field("foo.b"))); + EXPECT_EQ(Value("string"), *value.Get(Field("foo.c"))); - EXPECT_TRUE(nullopt == value.Get(Field("foo.a.b"))); - EXPECT_TRUE(nullopt == value.Get(Field("bar"))); - EXPECT_TRUE(nullopt == value.Get(Field("bar.a"))); + EXPECT_EQ(nullopt, value.Get(Field("foo.a.b"))); + EXPECT_EQ(nullopt, value.Get(Field("bar"))); + EXPECT_EQ(nullopt, value.Get(Field("bar.a"))); } TEST_F(ObjectValueTest, ExtractsFieldMask) { - MutableObjectValue value = + ObjectValue value = WrapObject("a", "b", "Map", Map("a", 1, "b", true, "c", "string", "nested", Map("d", "e")), "emptymap", Map()); @@ -85,60 +72,60 @@ TEST_F(ObjectValueTest, ExtractsFieldMask) { } TEST_F(ObjectValueTest, OverwritesExistingFields) { - MutableObjectValue object_value = WrapObject("a", "object_value"); + ObjectValue object_value = WrapObject("a", "object_value"); EXPECT_EQ(WrapObject("a", "object_value"), object_value); - object_value.Set(Field("a"), Wrap("object_value")); + object_value.Set(Field("a"), Value("object_value")); EXPECT_EQ(WrapObject("a", "object_value"), object_value); } TEST_F(ObjectValueTest, OverwritesNestedFields) { - MutableObjectValue object_value = + ObjectValue object_value = WrapObject("a", Map("b", kFooString, "c", Map("d", kFooString))); - object_value.Set(Field("a.b"), Wrap(kBarString)); - object_value.Set(Field("a.c.d"), Wrap(kBarString)); + object_value.Set(Field("a.b"), Value(kBarString)); + object_value.Set(Field("a.c.d"), Value(kBarString)); EXPECT_EQ(WrapObject("a", Map("b", kBarString, "c", Map("d", kBarString))), object_value); } TEST_F(ObjectValueTest, OverwritesDeeplyNestedField) { - MutableObjectValue object_value = WrapObject("a", Map("b", kFooString)); - object_value.Set(Field("a.b.c"), Wrap(kBarString)); + ObjectValue object_value = WrapObject("a", Map("b", kFooString)); + object_value.Set(Field("a.b.c"), Value(kBarString)); EXPECT_EQ(WrapObject("a", Map("b", Map("c", kBarString))), object_value); } TEST_F(ObjectValueTest, OverwritesNestedObject) { - MutableObjectValue object_value = + ObjectValue object_value = WrapObject("a", Map("b", Map("c", kFooString, "d", kFooString))); - object_value.Set(Field("a.b"), Wrap(kBarString)); + object_value.Set(Field("a.b"), Value(kBarString)); EXPECT_EQ(WrapObject("a", Map("b", "bar")), object_value); } TEST_F(ObjectValueTest, ReplacesNestedObject) { - MutableObjectValue object_value = WrapObject("a", Map("b", kFooString)); - object_value.Set(Field("a"), Wrap(Map("c", kBarString))); + ObjectValue object_value = WrapObject("a", Map("b", kFooString)); + object_value.Set(Field("a"), Value(Map("c", kBarString))); EXPECT_EQ(WrapObject("a", Map("c", kBarString)), object_value); } TEST_F(ObjectValueTest, ReplacesFieldWithNestedObject) { - MutableObjectValue object_value = WrapObject("a", 1); - object_value.Set(Field("a"), Wrap(Map("b", 2))); + ObjectValue object_value = WrapObject("a", 1); + object_value.Set(Field("a"), Value(Map("b", 2))); EXPECT_EQ(WrapObject("a", Map("b", 2)), object_value); } TEST_F(ObjectValueTest, AddsNewFields) { - MutableObjectValue object_value{}; - EXPECT_EQ(MutableObjectValue{}, object_value); + ObjectValue object_value{}; + EXPECT_EQ(ObjectValue{}, object_value); - object_value.Set(Field("a"), Wrap(1)); + object_value.Set(Field("a"), Value(1)); EXPECT_EQ(WrapObject("a", 1), object_value); - object_value.Set(Field("b"), Wrap(2)); + object_value.Set(Field("b"), Value(2)); EXPECT_EQ(WrapObject("a", 1, "b", 2), object_value); } TEST_F(ObjectValueTest, AddsMultipleFields) { - MutableObjectValue object_value{}; - EXPECT_EQ(MutableObjectValue{}, object_value); + ObjectValue object_value{}; + EXPECT_EQ(ObjectValue{}, object_value); object_value.SetAll( FieldMask( @@ -151,45 +138,45 @@ TEST_F(ObjectValueTest, AddsMultipleFields) { } TEST_F(ObjectValueTest, AddsNestedField) { - MutableObjectValue object_value{}; - object_value.Set(Field("a.b"), Wrap(kFooString)); - object_value.Set(Field("c.d.e"), Wrap(kFooString)); + ObjectValue object_value{}; + object_value.Set(Field("a.b"), Value(kFooString)); + object_value.Set(Field("c.d.e"), Value(kFooString)); EXPECT_EQ(WrapObject("a", Map("b", kFooString), "c", Map("d", Map("e", kFooString))), object_value); } TEST_F(ObjectValueTest, AddsFieldInNestedObject) { - MutableObjectValue object_value{}; - object_value.Set(Field("a"), Wrap(Map("b", kFooString))); - object_value.Set(Field("a.c"), Wrap(kFooString)); + ObjectValue object_value{}; + object_value.Set(Field("a"), Value(Map("b", kFooString))); + object_value.Set(Field("a.c"), Value(kFooString)); EXPECT_EQ(WrapObject("a", Map("b", kFooString, "c", kFooString)), object_value); } TEST_F(ObjectValueTest, AddsTwoFieldsInNestedObject) { - MutableObjectValue object_value{}; - object_value.Set(Field("a.b"), Wrap(kFooString)); - object_value.Set(Field("a.c"), Wrap(kFooString)); + ObjectValue object_value{}; + object_value.Set(Field("a.b"), Value(kFooString)); + object_value.Set(Field("a.c"), Value(kFooString)); EXPECT_EQ(WrapObject("a", Map("b", kFooString, "c", kFooString)), object_value); } TEST_F(ObjectValueTest, AddDeeplyNestedFieldInNestedObject) { - MutableObjectValue object_value{}; - object_value.Set(Field("a.b.c.d.e.f"), Wrap(kFooString)); + ObjectValue object_value{}; + object_value.Set(Field("a.b.c.d.e.f"), Value(kFooString)); EXPECT_EQ( WrapObject("a", Map("b", Map("c", Map("d", Map("e", Map("f", kFooString)))))), object_value); - object_value.Set(Field("a.a.b"), Wrap(kFooString)); + object_value.Set(Field("a.a.b"), Value(kFooString)); EXPECT_EQ( WrapObject("a", Map("a", Map("b", kFooString), "b", Map("c", Map("d", Map("e", Map("f", kFooString)))))), object_value); - object_value.Set(Field("a.c.d"), Wrap(kFooString)); + object_value.Set(Field("a.c.d"), Value(kFooString)); EXPECT_EQ( WrapObject("a", Map("a", Map("b", kFooString), "b", Map("c", Map("d", Map("e", Map("f", kFooString)))), @@ -198,48 +185,48 @@ TEST_F(ObjectValueTest, AddDeeplyNestedFieldInNestedObject) { } TEST_F(ObjectValueTest, AddsSingleFieldInExistingObject) { - MutableObjectValue object_value = WrapObject("a", kFooString); - object_value.Set(Field("b"), Wrap(kFooString)); + ObjectValue object_value = WrapObject("a", kFooString); + object_value.Set(Field("b"), Value(kFooString)); EXPECT_EQ(WrapObject("a", kFooString, "b", kFooString), object_value); } TEST_F(ObjectValueTest, SetsNestedFieldMultipleTimes) { - MutableObjectValue object_value{}; - object_value.Set(Field("a.c"), Wrap(kFooString)); - object_value.Set(Field("a"), Wrap(Map("b", kFooString))); + ObjectValue object_value{}; + object_value.Set(Field("a.c"), Value(kFooString)); + object_value.Set(Field("a"), Value(Map("b", kFooString))); EXPECT_EQ(WrapObject("a", Map("b", kFooString)), object_value); } TEST_F(ObjectValueTest, ImplicitlyCreatesObjects) { - MutableObjectValue object_value = WrapObject("a", "object_value"); + ObjectValue object_value = WrapObject("a", "object_value"); EXPECT_EQ(WrapObject("a", "object_value"), object_value); - object_value.Set(Field("b.c.d"), Wrap("object_value")); + object_value.Set(Field("b.c.d"), Value("object_value")); EXPECT_EQ( WrapObject("a", "object_value", "b", Map("c", Map("d", "object_value"))), object_value); } TEST_F(ObjectValueTest, CanOverwritePrimitivesWithObjects) { - MutableObjectValue object_value = WrapObject("a", Map("b", "object_value")); + ObjectValue object_value = WrapObject("a", Map("b", "object_value")); EXPECT_EQ(WrapObject("a", Map("b", "object_value")), object_value); - object_value.Set(Field("a"), Wrap(Map("b", "object_value"))); + object_value.Set(Field("a"), Value(Map("b", "object_value"))); EXPECT_EQ(WrapObject("a", Map("b", "object_value")), object_value); } TEST_F(ObjectValueTest, AddsToNestedObjects) { - MutableObjectValue object_value = WrapObject("a", Map("b", "object_value")); + ObjectValue object_value = WrapObject("a", Map("b", "object_value")); EXPECT_EQ(WrapObject("a", Map("b", "object_value")), object_value); - object_value.Set(Field("a.c"), Wrap("object_value")); + object_value.Set(Field("a.c"), Value("object_value")); EXPECT_EQ(WrapObject("a", Map("b", "object_value", "c", "object_value")), object_value); } TEST_F(ObjectValueTest, DeletesKey) { - MutableObjectValue object_value = WrapObject("a", 1, "b", 2); + ObjectValue object_value = WrapObject("a", 1, "b", 2); EXPECT_EQ(WrapObject("a", 1, "b", 2), object_value); object_value.Delete(Field("a")); @@ -247,11 +234,11 @@ TEST_F(ObjectValueTest, DeletesKey) { EXPECT_EQ(WrapObject("b", 2), object_value); object_value.Delete(Field("b")); - EXPECT_EQ(MutableObjectValue(), object_value); + EXPECT_EQ(ObjectValue(), object_value); } TEST_F(ObjectValueTest, DeletesMultipleKeys) { - MutableObjectValue object_value = + ObjectValue object_value = WrapObject("a", 1, "b", 2, "c", Map("d", 3, "e", 4)); object_value.SetAll(FieldMask({Field("a"), Field("b"), Field("c.d")}), @@ -261,7 +248,7 @@ TEST_F(ObjectValueTest, DeletesMultipleKeys) { } TEST_F(ObjectValueTest, DeletesHandleMissingKeys) { - MutableObjectValue object_value = WrapObject("a", Map("b", 1, "c", 2)); + ObjectValue object_value = WrapObject("a", Map("b", 1, "c", 2)); EXPECT_EQ(WrapObject("a", Map("b", 1, "c", 2)), object_value); object_value.Delete(Field("b")); @@ -273,25 +260,25 @@ TEST_F(ObjectValueTest, DeletesHandleMissingKeys) { } TEST_F(ObjectValueTest, DeletesNestedKeys) { - FieldValue::Map orig = Map("a", Map("b", 1, "c", Map("d", 2, "e", 3))); - MutableObjectValue object_value = WrapObject(orig); + auto orig = Map("a", Map("b", 1, "c", Map("d", 2, "e", 3))); + ObjectValue object_value = WrapObject(orig); object_value.Delete(Field("a.c.d")); - FieldValue::Map second = Map("a", Map("b", 1, "c", Map("e", 3))); + google_firestore_v1_Value second = Map("a", Map("b", 1, "c", Map("e", 3))); EXPECT_EQ(WrapObject(second), object_value); object_value.Delete(Field("a.c")); - FieldValue::Map third = Map("a", Map("b", 1)); + google_firestore_v1_Value third = Map("a", Map("b", 1)); EXPECT_EQ(WrapObject(third), object_value); object_value.Delete(Field("a")); - EXPECT_EQ(MutableObjectValue(), object_value); + EXPECT_EQ(ObjectValue(), object_value); } TEST_F(ObjectValueTest, DeletesNestedObject) { - MutableObjectValue object_value = WrapObject( + ObjectValue object_value = WrapObject( "a", Map("b", Map("c", kFooString, "d", kFooString), "f", kFooString)); object_value.Delete(Field("a.b")); EXPECT_EQ(WrapObject("a", Map("f", kFooString)), object_value); @@ -300,24 +287,24 @@ TEST_F(ObjectValueTest, DeletesNestedObject) { } TEST_F(ObjectValueTest, AddsAndDeletesField) { - MutableObjectValue object_value{}; - object_value.Set(Field(kFooString), Wrap(kFooString)); + ObjectValue object_value{}; + object_value.Set(Field(kFooString), Value(kFooString)); object_value.Delete(Field(kFooString)); EXPECT_EQ(WrapObject(), object_value); } TEST_F(ObjectValueTest, AddsAndDeletesMultipleFields) { - MutableObjectValue object_value = WrapObject("b", 2, "c", 3); + ObjectValue object_value = WrapObject("b", 2, "c", 3); object_value.SetAll(FieldMask({Field("a"), Field("b")}), WrapObject("a", 1)); EXPECT_EQ(WrapObject("a", 1, "c", 3), object_value); } TEST_F(ObjectValueTest, AddsAndDeletesNestedField) { - MutableObjectValue object_value{}; - object_value.Set(Field("a.b.c"), Wrap(kFooString)); - object_value.Set(Field("a.b.d"), Wrap(kFooString)); - object_value.Set(Field("f.g"), Wrap(kFooString)); - object_value.Set(Field("h"), Wrap(kFooString)); + ObjectValue object_value{}; + object_value.Set(Field("a.b.c"), Value(kFooString)); + object_value.Set(Field("a.b.d"), Value(kFooString)); + object_value.Set(Field("f.g"), Value(kFooString)); + object_value.Set(Field("h"), Value(kFooString)); object_value.Delete(Field("a.b.c")); object_value.Delete(Field("h")); EXPECT_EQ(WrapObject("a", Map("b", Map("d", kFooString)), "f", @@ -326,12 +313,25 @@ TEST_F(ObjectValueTest, AddsAndDeletesNestedField) { } TEST_F(ObjectValueTest, MergesExistingObject) { - MutableObjectValue object_value = WrapObject("a", Map("b", kFooString)); - object_value.Set(Field("a.c"), Wrap(kFooString)); + ObjectValue object_value = WrapObject("a", Map("b", kFooString)); + object_value.Set(Field("a.c"), Value(kFooString)); EXPECT_EQ(WrapObject("a", Map("b", kFooString, "c", kFooString)), object_value); } +TEST_F(ObjectValueTest, DoesNotRequireSortedValues) { + ObjectValue object_value = WrapObject("c", 2, "a", 1); + EXPECT_EQ(Value(2), *object_value.Get(Field("c"))); +} + +TEST_F(ObjectValueTest, DoesNotRequireSortedInserts) { + ObjectValue object_value{}; + object_value.Set(Field("nested"), + Map("c", 2, "nested", Map("c", 2, "a", 1), "a", 1)); + EXPECT_EQ(Value(2), *object_value.Get(Field("nested.c"))); + EXPECT_EQ(Value(2), *object_value.Get(Field("nested.nested.c"))); +} + } // namespace } // namespace model