Implement IsPropertyEqual for property store

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2768
This commit is contained in:
Matej Ferencevic 2020-05-06 13:37:59 +02:00
parent 0c42bedf2f
commit f047f55020
4 changed files with 443 additions and 4 deletions

View File

@ -347,6 +347,17 @@ class Reader {
return ReadBytes(reinterpret_cast<uint8_t *>(data), size);
}
bool VerifyBytes(const uint8_t *data, uint64_t size) {
if (pos_ + size > size_) return false;
if (memcmp(data, data_ + pos_, size) != 0) return false;
pos_ += size;
return true;
}
bool VerifyBytes(const char *data, uint64_t size) {
return VerifyBytes(reinterpret_cast<const uint8_t *>(data), size);
}
bool SkipBytes(uint64_t size) {
if (pos_ + size > size_) return false;
pos_ += size;
@ -436,6 +447,8 @@ std::optional<std::pair<Type, Size>> EncodePropertyValue(
// Function used to decode a PropertyValue from a byte stream. It can either
// decode or skip the encoded PropertyValue, depending on the supplied value
// pointer.
//
// @sa ComparePropertyValue
[[nodiscard]] bool DecodePropertyValue(Reader *reader, Type type,
Size payload_size,
PropertyValue *value) {
@ -550,6 +563,102 @@ std::optional<std::pair<Type, Size>> EncodePropertyValue(
}
}
// Function used to compare a PropertyValue to the one stored in the byte
// stream.
//
// NOTE: The logic in this function *MUST* be equal to the logic in
// `PropertyValue::operator==`. If you change this function make sure to change
// the operator so that they have identical functionality.
//
// @sa DecodePropertyValue
[[nodiscard]] bool ComparePropertyValue(Reader *reader, Type type,
Size payload_size,
const PropertyValue &value) {
switch (type) {
case Type::EMPTY: {
return false;
}
case Type::NONE: {
return value.IsNull();
}
case Type::BOOL: {
if (!value.IsBool()) return false;
bool bool_v = payload_size == Size::INT64;
return value.ValueBool() == bool_v;
}
case Type::INT: {
// Integer and double values are treated as the same in
// `PropertyValue::operator==`. That is why we accept both integer and
// double values here and use the `operator==` between them to verify that
// they are the same.
if (!value.IsInt() && !value.IsDouble()) return false;
auto int_v = reader->ReadInt(payload_size);
if (!int_v) return false;
if (value.IsInt()) {
return value.ValueInt() == int_v;
} else {
return value.ValueDouble() == int_v;
}
}
case Type::DOUBLE: {
// Integer and double values are treated as the same in
// `PropertyValue::operator==`. That is why we accept both integer and
// double values here and use the `operator==` between them to verify that
// they are the same.
if (!value.IsInt() && !value.IsDouble()) return false;
auto double_v = reader->ReadDouble(payload_size);
if (!double_v) return false;
if (value.IsDouble()) {
return value.ValueDouble() == double_v;
} else {
return value.ValueInt() == double_v;
}
}
case Type::STRING: {
if (!value.IsString()) return false;
const auto &str = value.ValueString();
auto size = reader->ReadUint(payload_size);
if (!size) return false;
if (*size != str.size()) return false;
return reader->VerifyBytes(str.data(), *size);
}
case Type::LIST: {
if (!value.IsList()) return false;
const auto &list = value.ValueList();
auto size = reader->ReadUint(payload_size);
if (!size) return false;
if (*size != list.size()) return false;
for (uint64_t i = 0; i < *size; ++i) {
auto metadata = reader->ReadMetadata();
if (!metadata) return false;
if (!ComparePropertyValue(reader, metadata->type,
metadata->payload_size, list[i]))
return false;
}
return true;
}
case Type::MAP: {
if (!value.IsMap()) return false;
const auto &map = value.ValueMap();
auto size = reader->ReadUint(payload_size);
if (!size) return false;
if (*size != map.size()) return false;
for (const auto &item : map) {
auto metadata = reader->ReadMetadata();
if (!metadata) return false;
auto key_size = reader->ReadUint(metadata->id_size);
if (!key_size) return false;
if (*key_size != item.first.size()) return false;
if (!reader->VerifyBytes(item.first.data(), *key_size)) return false;
if (!ComparePropertyValue(reader, metadata->type,
metadata->payload_size, item.second))
return false;
}
return true;
}
}
}
// Function used to encode a property (PropertyId, PropertyValue) into a byte
// stream.
bool EncodeProperty(Writer *writer, PropertyId property,
@ -593,6 +702,7 @@ enum class DecodeExpectedPropertyStatus {
// loaded in this case
//
// @sa DecodeAnyProperty
// @sa CompareExpectedProperty
[[nodiscard]] DecodeExpectedPropertyStatus DecodeExpectedProperty(
Reader *reader, PropertyId expected_property, PropertyValue *value) {
auto metadata = reader->ReadMetadata();
@ -602,7 +712,7 @@ enum class DecodeExpectedPropertyStatus {
if (!property_id) return DecodeExpectedPropertyStatus::MISSING_DATA;
// Don't load the value if this isn't the expected property.
if (property_id != expected_property.AsUint()) {
if (*property_id != expected_property.AsUint()) {
value = nullptr;
}
@ -610,9 +720,9 @@ enum class DecodeExpectedPropertyStatus {
value))
return DecodeExpectedPropertyStatus::MISSING_DATA;
if (property_id < expected_property.AsUint()) {
if (*property_id < expected_property.AsUint()) {
return DecodeExpectedPropertyStatus::SMALLER;
} else if (property_id == expected_property.AsUint()) {
} else if (*property_id == expected_property.AsUint()) {
return DecodeExpectedPropertyStatus::EQUAL;
} else {
return DecodeExpectedPropertyStatus::GREATER;
@ -624,6 +734,7 @@ enum class DecodeExpectedPropertyStatus {
// the provided PropertyValue.
//
// @sa DecodeExpectedProperty
// @sa CompareExpectedProperty
[[nodiscard]] std::optional<PropertyId> DecodeAnyProperty(
Reader *reader, PropertyValue *value) {
auto metadata = reader->ReadMetadata();
@ -639,6 +750,25 @@ enum class DecodeExpectedPropertyStatus {
return PropertyId::FromUint(*property_id);
}
// Function used to compare a property (PropertyId, PropertyValue) to current
// property in the byte stream.
//
// @sa DecodeExpectedProperty
// @sa DecodeAnyProperty
[[nodiscard]] bool CompareExpectedProperty(Reader *reader,
PropertyId expected_property,
const PropertyValue &value) {
auto metadata = reader->ReadMetadata();
if (!metadata) return false;
auto property_id = reader->ReadUint(metadata->id_size);
if (!property_id) return false;
if (*property_id != expected_property.AsUint()) return false;
return ComparePropertyValue(reader, metadata->type, metadata->payload_size,
value);
}
// Function used to find and (selectively) get the property value of the
// property whose ID is `property`. It relies on the fact that the properties
// are sorted (by ID) in the buffer. If the function doesn't find the property,
@ -824,6 +954,24 @@ bool PropertyStore::HasProperty(PropertyId property) const {
DecodeExpectedPropertyStatus::EQUAL;
}
bool PropertyStore::IsPropertyEqual(PropertyId property,
const PropertyValue &value) const {
uint64_t size;
const uint8_t *data;
std::tie(size, data) = GetSizeData(buffer_);
if (size % 8 != 0) {
// We are storing the data in the local buffer.
size = sizeof(buffer_) - 1;
data = &buffer_[1];
}
Reader reader(data, size);
auto info = FindSpecificPropertyAndBufferInfo(&reader, property);
if (info.property_size == 0) return value.IsNull();
Reader prop_reader(data + info.property_begin, info.property_size);
if (!CompareExpectedProperty(&prop_reader, property, value)) return false;
return prop_reader.GetPosition() == info.property_size;
}
std::map<PropertyId, PropertyValue> PropertyStore::Properties() const {
uint64_t size;
const uint8_t *data;

View File

@ -28,6 +28,12 @@ class PropertyStore {
/// complexity of this function is O(n).
bool HasProperty(PropertyId property) const;
/// Checks whether the property `property` is equal to the specified value
/// `value`. This function doesn't perform any memory allocations while
/// performing the equality check. The time complexity of this function is
/// O(n).
bool IsPropertyEqual(PropertyId property, const PropertyValue &value) const;
/// Returns all properties currently stored in the store. The time complexity
/// of this function is O(n).
/// @throw std::bad_alloc

View File

@ -243,7 +243,9 @@ inline std::ostream &operator<<(std::ostream &os, const PropertyValue &value) {
}
}
// comparison
// NOTE: The logic in this function *MUST* be equal to the logic in
// `PropertyStore::ComparePropertyValue`. If you change this operator make sure
// to change the function so that they have identical functionality.
inline bool operator==(const PropertyValue &first,
const PropertyValue &second) noexcept {
if (!PropertyValue::AreComparableTypes(first.type(), second.type()))

View File

@ -7,6 +7,54 @@
using testing::UnorderedElementsAre;
const storage::PropertyValue kSampleValues[] = {
storage::PropertyValue(),
storage::PropertyValue(false),
storage::PropertyValue(true),
storage::PropertyValue(0),
storage::PropertyValue(33),
storage::PropertyValue(-33),
storage::PropertyValue(-3137),
storage::PropertyValue(3137),
storage::PropertyValue(310000007),
storage::PropertyValue(-310000007),
storage::PropertyValue(3100000000007L),
storage::PropertyValue(-3100000000007L),
storage::PropertyValue(0.0),
storage::PropertyValue(33.33),
storage::PropertyValue(-33.33),
storage::PropertyValue(3137.3137),
storage::PropertyValue(-3137.3137),
storage::PropertyValue("sample"),
storage::PropertyValue(std::string(404, 'n')),
storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(33),
storage::PropertyValue(std::string("sample")),
storage::PropertyValue(-33.33)}),
storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(), storage::PropertyValue(false)}),
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"sample", storage::PropertyValue()},
{"key", storage::PropertyValue(false)}}),
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"test", storage::PropertyValue(33)},
{"map", storage::PropertyValue(std::string("sample"))},
{"item", storage::PropertyValue(-33.33)}}),
};
void TestIsPropertyEqual(const storage::PropertyStore &store,
storage::PropertyId property,
const storage::PropertyValue &value) {
ASSERT_TRUE(store.IsPropertyEqual(property, value));
for (const auto &sample : kSampleValues) {
if (sample == value) {
ASSERT_TRUE(store.IsPropertyEqual(property, sample));
} else {
ASSERT_FALSE(store.IsPropertyEqual(property, sample));
}
}
}
TEST(PropertyStore, Simple) {
storage::PropertyStore props;
auto prop = storage::PropertyId::FromInt(42);
@ -14,11 +62,13 @@ TEST(PropertyStore, Simple) {
ASSERT_TRUE(props.SetProperty(prop, value));
ASSERT_EQ(props.GetProperty(prop), value);
ASSERT_TRUE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, value);
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
ASSERT_FALSE(props.SetProperty(prop, storage::PropertyValue()));
ASSERT_TRUE(props.GetProperty(prop).IsNull());
ASSERT_FALSE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, storage::PropertyValue());
ASSERT_EQ(props.Properties().size(), 0);
}
@ -29,11 +79,13 @@ TEST(PropertyStore, SimpleLarge) {
ASSERT_TRUE(props.SetProperty(prop, value));
ASSERT_EQ(props.GetProperty(prop), value);
ASSERT_TRUE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, value);
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
ASSERT_FALSE(props.SetProperty(prop, storage::PropertyValue()));
ASSERT_TRUE(props.GetProperty(prop).IsNull());
ASSERT_FALSE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, storage::PropertyValue());
ASSERT_EQ(props.Properties().size(), 0);
}
@ -43,6 +95,7 @@ TEST(PropertyStore, EmptySetToNull) {
ASSERT_TRUE(props.SetProperty(prop, storage::PropertyValue()));
ASSERT_TRUE(props.GetProperty(prop).IsNull());
ASSERT_FALSE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, storage::PropertyValue());
ASSERT_EQ(props.Properties().size(), 0);
}
@ -53,10 +106,12 @@ TEST(PropertyStore, Clear) {
ASSERT_TRUE(props.SetProperty(prop, value));
ASSERT_EQ(props.GetProperty(prop), value);
ASSERT_TRUE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, value);
ASSERT_THAT(props.Properties(), UnorderedElementsAre(std::pair(prop, value)));
ASSERT_TRUE(props.ClearProperties());
ASSERT_TRUE(props.GetProperty(prop).IsNull());
ASSERT_FALSE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, storage::PropertyValue());
ASSERT_EQ(props.Properties().size(), 0);
}
@ -73,18 +128,21 @@ TEST(PropertyStore, MoveConstruct) {
ASSERT_TRUE(props1.SetProperty(prop, value));
ASSERT_EQ(props1.GetProperty(prop), value);
ASSERT_TRUE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, value);
ASSERT_THAT(props1.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
{
storage::PropertyStore props2(std::move(props1));
ASSERT_EQ(props2.GetProperty(prop), value);
ASSERT_TRUE(props2.HasProperty(prop));
TestIsPropertyEqual(props2, prop, value);
ASSERT_THAT(props2.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
}
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
ASSERT_FALSE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, storage::PropertyValue());
ASSERT_EQ(props1.Properties().size(), 0);
}
@ -95,18 +153,21 @@ TEST(PropertyStore, MoveConstructLarge) {
ASSERT_TRUE(props1.SetProperty(prop, value));
ASSERT_EQ(props1.GetProperty(prop), value);
ASSERT_TRUE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, value);
ASSERT_THAT(props1.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
{
storage::PropertyStore props2(std::move(props1));
ASSERT_EQ(props2.GetProperty(prop), value);
ASSERT_TRUE(props2.HasProperty(prop));
TestIsPropertyEqual(props2, prop, value);
ASSERT_THAT(props2.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
}
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
ASSERT_FALSE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, storage::PropertyValue());
ASSERT_EQ(props1.Properties().size(), 0);
}
@ -117,6 +178,7 @@ TEST(PropertyStore, MoveAssign) {
ASSERT_TRUE(props1.SetProperty(prop, value));
ASSERT_EQ(props1.GetProperty(prop), value);
ASSERT_TRUE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, value);
ASSERT_THAT(props1.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
{
@ -125,17 +187,20 @@ TEST(PropertyStore, MoveAssign) {
ASSERT_TRUE(props2.SetProperty(prop, value2));
ASSERT_EQ(props2.GetProperty(prop), value2);
ASSERT_TRUE(props2.HasProperty(prop));
TestIsPropertyEqual(props2, prop, value2);
ASSERT_THAT(props2.Properties(),
UnorderedElementsAre(std::pair(prop, value2)));
props2 = std::move(props1);
ASSERT_EQ(props2.GetProperty(prop), value);
ASSERT_TRUE(props2.HasProperty(prop));
TestIsPropertyEqual(props2, prop, value);
ASSERT_THAT(props2.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
}
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
ASSERT_FALSE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, storage::PropertyValue());
ASSERT_EQ(props1.Properties().size(), 0);
}
@ -146,6 +211,7 @@ TEST(PropertyStore, MoveAssignLarge) {
ASSERT_TRUE(props1.SetProperty(prop, value));
ASSERT_EQ(props1.GetProperty(prop), value);
ASSERT_TRUE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, value);
ASSERT_THAT(props1.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
{
@ -154,17 +220,20 @@ TEST(PropertyStore, MoveAssignLarge) {
ASSERT_TRUE(props2.SetProperty(prop, value2));
ASSERT_EQ(props2.GetProperty(prop), value2);
ASSERT_TRUE(props2.HasProperty(prop));
TestIsPropertyEqual(props2, prop, value2);
ASSERT_THAT(props2.Properties(),
UnorderedElementsAre(std::pair(prop, value2)));
props2 = std::move(props1);
ASSERT_EQ(props2.GetProperty(prop), value);
ASSERT_TRUE(props2.HasProperty(prop));
TestIsPropertyEqual(props2, prop, value);
ASSERT_THAT(props2.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
}
// NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move,hicpp-invalid-access-moved)
ASSERT_TRUE(props1.GetProperty(prop).IsNull());
ASSERT_FALSE(props1.HasProperty(prop));
TestIsPropertyEqual(props1, prop, storage::PropertyValue());
ASSERT_EQ(props1.Properties().size(), 0);
}
@ -186,20 +255,24 @@ TEST(PropertyStore, EmptySet) {
ASSERT_TRUE(props.SetProperty(prop, value));
ASSERT_EQ(props.GetProperty(prop), value);
ASSERT_TRUE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, value);
ASSERT_THAT(props.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
ASSERT_FALSE(props.SetProperty(prop, value));
ASSERT_EQ(props.GetProperty(prop), value);
ASSERT_TRUE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, value);
ASSERT_THAT(props.Properties(),
UnorderedElementsAre(std::pair(prop, value)));
ASSERT_FALSE(props.SetProperty(prop, storage::PropertyValue()));
ASSERT_TRUE(props.GetProperty(prop).IsNull());
ASSERT_FALSE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, storage::PropertyValue());
ASSERT_EQ(props.Properties().size(), 0);
ASSERT_TRUE(props.SetProperty(prop, storage::PropertyValue()));
ASSERT_TRUE(props.GetProperty(prop).IsNull());
ASSERT_FALSE(props.HasProperty(prop));
TestIsPropertyEqual(props, prop, storage::PropertyValue());
ASSERT_EQ(props.Properties().size(), 0);
}
}
@ -247,9 +320,11 @@ TEST(PropertyStore, FullSet) {
} else {
ASSERT_TRUE(props.HasProperty(item.first));
}
TestIsPropertyEqual(props, item.first, alt[i]);
} else {
ASSERT_EQ(props.GetProperty(item.first), item.second);
ASSERT_TRUE(props.HasProperty(item.first));
TestIsPropertyEqual(props, item.first, item.second);
}
}
auto current = data;
@ -271,9 +346,11 @@ TEST(PropertyStore, FullSet) {
} else {
ASSERT_TRUE(props.HasProperty(item.first));
}
TestIsPropertyEqual(props, item.first, alt[i]);
} else {
ASSERT_EQ(props.GetProperty(item.first), item.second);
ASSERT_TRUE(props.HasProperty(item.first));
TestIsPropertyEqual(props, item.first, item.second);
}
}
auto current = data;
@ -288,12 +365,14 @@ TEST(PropertyStore, FullSet) {
ASSERT_TRUE(props.SetProperty(target.first, target.second));
ASSERT_EQ(props.GetProperty(target.first), target.second);
ASSERT_TRUE(props.HasProperty(target.first));
TestIsPropertyEqual(props, target.first, target.second);
props.ClearProperties();
ASSERT_EQ(props.Properties().size(), 0);
for (const auto &item : data) {
ASSERT_TRUE(props.GetProperty(item.first).IsNull());
ASSERT_FALSE(props.HasProperty(item.first));
TestIsPropertyEqual(props, item.first, storage::PropertyValue());
}
}
}
@ -343,12 +422,14 @@ TEST(PropertyStore, IntEncoding) {
ASSERT_TRUE(props.SetProperty(item.first, item.second));
ASSERT_EQ(props.GetProperty(item.first), item.second);
ASSERT_TRUE(props.HasProperty(item.first));
TestIsPropertyEqual(props, item.first, item.second);
}
for (auto it = data.rbegin(); it != data.rend(); ++it) {
const auto &item = *it;
ASSERT_FALSE(props.SetProperty(item.first, item.second));
ASSERT_EQ(props.GetProperty(item.first), item.second);
ASSERT_TRUE(props.HasProperty(item.first));
TestIsPropertyEqual(props, item.first, item.second);
}
ASSERT_EQ(props.Properties(), data);
@ -358,5 +439,207 @@ TEST(PropertyStore, IntEncoding) {
for (const auto &item : data) {
ASSERT_TRUE(props.GetProperty(item.first).IsNull());
ASSERT_FALSE(props.HasProperty(item.first));
TestIsPropertyEqual(props, item.first, storage::PropertyValue());
}
}
TEST(PropertyStore, IsPropertyEqualIntAndDouble) {
storage::PropertyStore props;
auto prop = storage::PropertyId::FromInt(42);
ASSERT_TRUE(props.SetProperty(prop, storage::PropertyValue(42)));
std::vector<std::pair<storage::PropertyValue, storage::PropertyValue>> tests{
{storage::PropertyValue(0), storage::PropertyValue(0.0)},
{storage::PropertyValue(123), storage::PropertyValue(123.0)},
{storage::PropertyValue(12345), storage::PropertyValue(12345.0)},
{storage::PropertyValue(12345678), storage::PropertyValue(12345678.0)},
{storage::PropertyValue(1234567890123L),
storage::PropertyValue(1234567890123.0)},
};
// Test equality with raw values.
for (auto test : tests) {
ASSERT_EQ(test.first, test.second);
// Test first, second
ASSERT_FALSE(props.SetProperty(prop, test.first));
ASSERT_EQ(props.GetProperty(prop), test.first);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
// Test second, first
ASSERT_FALSE(props.SetProperty(prop, test.second));
ASSERT_EQ(props.GetProperty(prop), test.second);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
// Make both negative
test.first = storage::PropertyValue(test.first.ValueInt() * -1);
test.second = storage::PropertyValue(test.second.ValueDouble() * -1.0);
ASSERT_EQ(test.first, test.second);
// Test -first, -second
ASSERT_FALSE(props.SetProperty(prop, test.first));
ASSERT_EQ(props.GetProperty(prop), test.first);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
// Test -second, -first
ASSERT_FALSE(props.SetProperty(prop, test.second));
ASSERT_EQ(props.GetProperty(prop), test.second);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
}
// Test equality with values wrapped in lists.
for (auto test : tests) {
test.first = storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(test.first.ValueInt())});
test.second = storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(test.second.ValueDouble())});
ASSERT_EQ(test.first, test.second);
// Test first, second
ASSERT_FALSE(props.SetProperty(prop, test.first));
ASSERT_EQ(props.GetProperty(prop), test.first);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
// Test second, first
ASSERT_FALSE(props.SetProperty(prop, test.second));
ASSERT_EQ(props.GetProperty(prop), test.second);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
// Make both negative
test.first = storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(test.first.ValueList()[0].ValueInt() * -1)});
test.second = storage::PropertyValue(
std::vector<storage::PropertyValue>{storage::PropertyValue(
test.second.ValueList()[0].ValueDouble() * -1.0)});
ASSERT_EQ(test.first, test.second);
// Test -first, -second
ASSERT_FALSE(props.SetProperty(prop, test.first));
ASSERT_EQ(props.GetProperty(prop), test.first);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
// Test -second, -first
ASSERT_FALSE(props.SetProperty(prop, test.second));
ASSERT_EQ(props.GetProperty(prop), test.second);
ASSERT_TRUE(props.HasProperty(prop));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.second));
ASSERT_TRUE(props.IsPropertyEqual(prop, test.first));
}
}
TEST(PropertyStore, IsPropertyEqualString) {
storage::PropertyStore props;
auto prop = storage::PropertyId::FromInt(42);
ASSERT_TRUE(props.SetProperty(prop, storage::PropertyValue("test")));
ASSERT_TRUE(props.IsPropertyEqual(prop, storage::PropertyValue("test")));
// Different length.
ASSERT_FALSE(
props.IsPropertyEqual(prop, storage::PropertyValue("helloworld")));
// Same length, different value.
ASSERT_FALSE(props.IsPropertyEqual(prop, storage::PropertyValue("asdf")));
// Shortened and extended.
ASSERT_FALSE(props.IsPropertyEqual(prop, storage::PropertyValue("tes")));
ASSERT_FALSE(props.IsPropertyEqual(prop, storage::PropertyValue("testt")));
}
TEST(PropertyStore, IsPropertyEqualList) {
storage::PropertyStore props;
auto prop = storage::PropertyId::FromInt(42);
ASSERT_TRUE(props.SetProperty(
prop, storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(42), storage::PropertyValue("test")})));
ASSERT_TRUE(props.IsPropertyEqual(
prop, storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(42), storage::PropertyValue("test")})));
// Different length.
ASSERT_FALSE(props.IsPropertyEqual(
prop, storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(24)})));
// Same length, different value.
ASSERT_FALSE(props.IsPropertyEqual(
prop, storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(42), storage::PropertyValue("asdf")})));
// Shortened and extended.
ASSERT_FALSE(props.IsPropertyEqual(
prop, storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(42)})));
ASSERT_FALSE(props.IsPropertyEqual(
prop, storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(42), storage::PropertyValue("test"),
storage::PropertyValue(true)})));
}
TEST(PropertyStore, IsPropertyEqualMap) {
storage::PropertyStore props;
auto prop = storage::PropertyId::FromInt(42);
ASSERT_TRUE(props.SetProperty(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"abc", storage::PropertyValue(42)},
{"zyx", storage::PropertyValue("test")}})));
ASSERT_TRUE(props.IsPropertyEqual(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"abc", storage::PropertyValue(42)},
{"zyx", storage::PropertyValue("test")}})));
// Different length.
ASSERT_FALSE(props.IsPropertyEqual(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"fgh", storage::PropertyValue(24)}})));
// Same length, different value.
ASSERT_FALSE(props.IsPropertyEqual(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"abc", storage::PropertyValue(42)},
{"zyx", storage::PropertyValue("testt")}})));
// Same length, different key (different length).
ASSERT_FALSE(props.IsPropertyEqual(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"abc", storage::PropertyValue(42)},
{"zyxw", storage::PropertyValue("test")}})));
// Same length, different key (same length).
ASSERT_FALSE(props.IsPropertyEqual(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"abc", storage::PropertyValue(42)},
{"zyw", storage::PropertyValue("test")}})));
// Shortened and extended.
ASSERT_FALSE(props.IsPropertyEqual(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"abc", storage::PropertyValue(42)}})));
ASSERT_FALSE(props.IsPropertyEqual(
prop,
storage::PropertyValue(std::map<std::string, storage::PropertyValue>{
{"abc", storage::PropertyValue(42)},
{"sdf", storage::PropertyValue(true)},
{"zyx", storage::PropertyValue("test")}})));
}