Implement IsPropertyEqual
for property store
Reviewers: buda Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2768
This commit is contained in:
parent
0c42bedf2f
commit
f047f55020
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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()))
|
||||
|
@ -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")}})));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user