-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add ObjectValue #7712
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ObjectValue #7712
Conversation
Generated by 🚫 Danger |
Coverage ReportAffected SDKs
Test Logs |
using testutil::Map; | ||
using testutil::Value; | ||
|
||
class ObjectValueTest : public ::testing::Test { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note - can be compared to end result of firebase/firebase-android-sdk#2494
I think this leaks memory - we need to free the underlying Protobuf structures when MutableObjectValue is destructed. I will work on a fix. |
Done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a partial review.
// it to verify that all values own their own memory. | ||
nanopb::Message<google_firestore_v1_Value> clone1{DeepClone(value)}; | ||
nanopb::Message<google_firestore_v1_Value> clone2{DeepClone(*clone1)}; | ||
EXPECT_TRUE(value == *clone1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: when these tests fail, what do the failure messages look like? Is it clear which invocation of VerifyDeepClone
failed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it requires debugging. I don't know how to solve this though (other than by passing in the index of the callsite). I wish there was a proper stracktrace.
I added a better error message though, which should help a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check again after replacing EXPECT_TRUE
with EXPECT_EQ
? One possible approach is to add SCOPED_TRACE
before every invocation of this helper, but please check with EXPECT_EQ
first to see if it's necessary. Another approach, should EXPECT_EQ
not work out, is to log the stringified form of both values being compared upon failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a log for both message (similar to what I do above). I am not sure why EXPECT_EQ does not compile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next round. Thanks for the feedback.
// it to verify that all values own their own memory. | ||
nanopb::Message<google_firestore_v1_Value> clone1{DeepClone(value)}; | ||
nanopb::Message<google_firestore_v1_Value> clone2{DeepClone(*clone1)}; | ||
EXPECT_TRUE(value == *clone1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it requires debugging. I don't know how to solve this though (other than by passing in the index of the callsite). I wish there was a proper stracktrace.
I added a better error message though, which should help a bit.
for (pb_size_t source_index = 0, target_index = 0; | ||
target_index < target_count;) { | ||
if (source_index < source_count) { | ||
std::string key = MakeString(source_fields[source_index].key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: Using a string_view here causes an ASAN failure as the key may get freed using FreeNanopbMessage in the loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, but key
doesn't seem to be used after FreeNanopbMessage
? AFAICS, the memory can only be freed after a comparison and it causes the loop to continue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Close to LGTM. Please see a couple of comments I bumped from the last time.
|
||
[&] { | ||
nanopb::Message<google_firestore_v1_Value> clone2{DeepClone(value)}; | ||
EXPECT_TRUE(value == *clone2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use EXPECT_EQ
. Also, a failure message should contain the line where it happened, so the before/after message is probably not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't able to get this to work:
/Users/mrschmidt/GitHub/firebase/firebase-ios-sdk/cmake-build-debug/external/src/googletest/googletest/include/gtest/gtest.h:1527:11: error: invalid operands to binary expression ('const firebase::firestore::_google_firestore_v1_Value' and 'const firebase::firestore::_google_firestore_v1_Value')
if (lhs == rhs) {
~~~ ^ ~~~
/Users/mrschmidt/GitHub/firebase/firebase-ios-sdk/cmake-build-debug/external/src/googletest/googletest/include/gtest/gtest.h:1554:12: note: in instantiation of function template specialization 'testing::internal::CmpHelperEQ<firebase::firestore::_google_firestore_v1_Value, firebase::firestore::_google_firestore_v1_Value>' requested here
return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
^
/Users/mrschmidt/GitHub/firebase/firebase-ios-sdk/Firestore/core/test/unit/model/value_util_test.cc:141:7: note: in instantiation of function template specialization 'testing::internal::EqHelper::Compare<firebase::firestore::_google_firestore_v1_Value, firebase::firestore::_google_firestore_v1_Value, nullptr>' requested here
EXPECT_EQ(value, *clone2)
^
/Users/mrschmidt/GitHub/firebase/firebase-ios-sdk/cmake-build-debug/external/src/googletest/googletest/include/gtest/gtest.h:2028:54: note: expanded from macro 'EXPECT_EQ'
EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
^
/Users/mrschmidt/GitHub/firebase/firebase-ios-sdk/Firestore/core/include/firebase/firestore/geo_point.h:113:13: note: candidate function not viable: no known conversion from 'const firebase::firestore::_google_firestore_v1_Value' to 'const firebase::firestore::GeoPoint' for 1st argument
inline bool operator==(const GeoPoint& lhs, const GeoPoint& rhs) {
^
/Users/mrschmidt/GitHub/firebase/firebase-ios-sdk/cmake-build-debug/external/src/googletest/googletest/include/gtest/gtest.h:1518:13: note: candidate function not viable: no known conversion from 'const firebase::firestore::_google_firestore_v1_Value' to 'testing::internal::faketype' for 1st argument
inline bool operator==(faketype, faketype) { return true; }
I tried to use EXPECT_EQ in my earlier attempts at writing these tests and for some reason the ==
operator is not recognized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, what happens is that due to the way EXPECT_EQ
is implemented, the operator==
has to be in the same namespace as at least one of the classes being compared. In this case, google_firestore_v1_Value
is in the firebase::firestore
namespace, but the comparison operator is in firebase::firestore::model
. It's probably a good idea to move operator==
one namespace level up.
// it to verify that all values own their own memory. | ||
nanopb::Message<google_firestore_v1_Value> clone1{DeepClone(value)}; | ||
nanopb::Message<google_firestore_v1_Value> clone2{DeepClone(*clone1)}; | ||
EXPECT_TRUE(value == *clone1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check again after replacing EXPECT_TRUE
with EXPECT_EQ
? One possible approach is to add SCOPED_TRACE
before every invocation of this helper, but please check with EXPECT_EQ
first to see if it's necessary. Another approach, should EXPECT_EQ
not work out, is to log the stringified form of both values being compared upon failure.
for (pb_size_t source_index = 0, target_index = 0; | ||
target_index < target_count;) { | ||
if (source_index < source_count) { | ||
std::string key = MakeString(source_fields[source_index].key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, but key
doesn't seem to be used after FreeNanopbMessage
? AFAICS, the memory can only be freed after a comparison and it causes the loop to continue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing all the feedback! LGTM (with one small comment about includes).
@@ -23,6 +23,7 @@ | |||
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h" | |||
#include "Firestore/Protos/nanopb/google/firestore/v1/firestore.nanopb.h" | |||
#include "Firestore/Protos/nanopb/google/type/latlng.nanopb.h" | |||
#include "Firestore/core/src/nanopb/message.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This include should no longer be necessary.
This PR adds a mutable, Protobuf-backed ObjectValue similar to https://github.com/firebase/firebase-android-sdk/blob/master/firebase-firestore/src/main/java/com/google/firebase/firestore/model/ObjectValue.java
Since nanopb Protos can be directly manipulated, I did not port the
Overlay
approach from Android. Rather, the protos are modified directly. To ensure O(log n) Map lookups, Maps are kept sorted as well (all maps start out sorted when we receive them from the backend, but on Web and Android they are not necessarily sorted after we apply local modifications). The guaranteed sorted order also allows me to simplify some of the code in value_utils as well.