Blender V4.3
idprop_serialize_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "testing/testing.h"
6
7#include "BLI_listbase.h"
8#include "BLI_serialize.hh"
9
10#include "DNA_ID.h"
11
12#include "BKE_idprop.hh"
13
15
16using namespace blender::io::serialize;
17
19{
20 ASSERT_NE(value, nullptr);
21 ASSERT_EQ(value->type(), eValueType::Array);
22 const Span<std::shared_ptr<Value>> elements = value->elements();
23 EXPECT_FALSE(elements.is_empty());
24 EXPECT_EQ(elements.size(), 1);
25
26 const std::shared_ptr<Value> &item = value->elements()[0];
27 ASSERT_EQ(item->type(), eValueType::Dictionary);
28}
29
31 const std::string expected_key,
32 const std::string expected_value)
33{
34 EXPECT_TRUE(lookup.contains(expected_key));
35 const std::shared_ptr<Value> &element = *lookup.lookup_ptr(expected_key);
36 ASSERT_EQ(element->type(), eValueType::String);
37 EXPECT_EQ(element->as_string_value()->value(), expected_value);
38}
39
41 const std::string expected_key,
42 const int32_t expected_value)
43{
44 EXPECT_TRUE(lookup.contains(expected_key));
45 const std::shared_ptr<Value> &element = *lookup.lookup_ptr(expected_key);
46 ASSERT_EQ(element->type(), eValueType::Int);
47 EXPECT_EQ(element->as_int_value()->value(), expected_value);
48}
49
51 const std::string expected_key,
52 const float expected_value)
53{
54 EXPECT_TRUE(lookup.contains(expected_key));
55 const std::shared_ptr<Value> &element = *lookup.lookup_ptr(expected_key);
56 ASSERT_EQ(element->type(), eValueType::Double);
57 EXPECT_EQ(element->as_double_value()->value(), expected_value);
58}
59
61 const std::string expected_key,
62 const double expected_value)
63{
64 EXPECT_TRUE(lookup.contains(expected_key));
65 const std::shared_ptr<Value> &element = *lookup.lookup_ptr(expected_key);
66 ASSERT_EQ(element->type(), eValueType::Double);
67 EXPECT_EQ(element->as_double_value()->value(), expected_value);
68}
69
70static void test_string_to_value(const StringRefNull prop_name, const StringRefNull prop_content)
71{
72 std::unique_ptr<IDProperty, IDPropertyDeleter> property = create(prop_name, prop_content);
73
74 std::unique_ptr<ArrayValue> value = convert_to_serialize_values(property.get());
75 check_container_value(value.get());
76 const std::shared_ptr<Value> &item = value->elements()[0];
77 const DictionaryValue *object = item->as_dictionary_value();
78 const DictionaryValue::Lookup lookup = object->create_lookup();
79
80 EXPECT_EQ(lookup.size(), 3);
81 check_object_attribute(lookup, "name", prop_name);
82 check_object_attribute(lookup, "type", "IDP_STRING");
83 check_object_attribute(lookup, "value", prop_content);
84}
85
86TEST(idprop, convert_idp_string_to_value)
87{
88 test_string_to_value("mykey", "mycontent");
89}
90
91static void test_int_to_value(const StringRefNull prop_name, int32_t prop_content)
92{
93 std::unique_ptr<IDProperty, IDPropertyDeleter> property = create(prop_name, prop_content);
94
95 std::unique_ptr<ArrayValue> value = convert_to_serialize_values(property.get());
96 check_container_value(value.get());
97 const std::shared_ptr<Value> &item = value->elements()[0];
98 const DictionaryValue *object = item->as_dictionary_value();
99 const DictionaryValue::Lookup lookup = object->create_lookup();
100
101 EXPECT_EQ(lookup.size(), 3);
102 check_object_attribute(lookup, "name", prop_name);
103 check_object_attribute(lookup, "type", "IDP_INT");
104 check_object_attribute(lookup, "value", prop_content);
105}
106
107TEST(idprop, convert_idp_int_to_value)
108{
109 test_int_to_value("mykey", 0);
110}
111
112static void test_float_to_value(const StringRefNull prop_name, float prop_content)
113{
114 std::unique_ptr<IDProperty, IDPropertyDeleter> property = create(prop_name, prop_content);
115
116 std::unique_ptr<ArrayValue> value = convert_to_serialize_values(property.get());
117 check_container_value(value.get());
118 const std::shared_ptr<Value> &item = value->elements()[0];
119 const DictionaryValue *object = item->as_dictionary_value();
120 const DictionaryValue::Lookup lookup = object->create_lookup();
121
122 EXPECT_EQ(lookup.size(), 3);
123 check_object_attribute(lookup, "name", prop_name);
124 check_object_attribute(lookup, "type", "IDP_FLOAT");
125 check_object_attribute(lookup, "value", prop_content);
126}
127
128TEST(idprop, convert_idp_float_to_value)
129{
130 test_float_to_value("mykey", 0.2f);
131}
132
133static void test_double_to_value(const StringRefNull prop_name, double prop_content)
134{
135 std::unique_ptr<IDProperty, IDPropertyDeleter> property = create(prop_name, prop_content);
136
137 std::unique_ptr<ArrayValue> value = convert_to_serialize_values(property.get());
138 check_container_value(value.get());
139 const std::shared_ptr<Value> &item = value->elements()[0];
140 const DictionaryValue *object = item->as_dictionary_value();
141 const DictionaryValue::Lookup lookup = object->create_lookup();
142
143 EXPECT_EQ(lookup.size(), 3);
144 check_object_attribute(lookup, "name", prop_name);
145 check_object_attribute(lookup, "type", "IDP_DOUBLE");
146 check_object_attribute(lookup, "value", prop_content);
147}
148
149TEST(idprop, convert_idp_double_to_value)
150{
151 test_double_to_value("mykey", 0.2);
152}
153
154template<typename PrimitiveType, typename ValueType>
155static void test_array_to_value(const StringRefNull prop_name, Vector<PrimitiveType> prop_content)
156{
157 std::unique_ptr<IDProperty, IDPropertyDeleter> property = create(prop_name, prop_content);
158 std::unique_ptr<ArrayValue> value = convert_to_serialize_values(property.get());
159
160 check_container_value(value.get());
161 const std::shared_ptr<Value> &item = value->elements()[0];
162 const DictionaryValue *object = item->as_dictionary_value();
163 const DictionaryValue::Lookup lookup = object->create_lookup();
164
165 EXPECT_EQ(lookup.size(), 4);
166 check_object_attribute(lookup, "name", prop_name);
167 check_object_attribute(lookup, "type", "IDP_ARRAY");
168
169 const std::shared_ptr<Value> &element = *lookup.lookup_ptr("value");
170 const ArrayValue *subvalues = element->as_array_value();
171 ASSERT_NE(subvalues, nullptr);
172 const Span<std::shared_ptr<Value>> subitems = subvalues->elements();
173 ASSERT_EQ(subitems.size(), prop_content.size());
174
175 for (size_t i = 0; i < prop_content.size(); i++) {
176 EXPECT_EQ(static_cast<ValueType *>(subitems[i].get())->value(), prop_content[i]);
177 }
178}
179
180TEST(idprop, convert_idp_int_array_to_value)
181{
183 {-16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16});
184}
185
186TEST(idprop, convert_idp_float_array_to_value)
187{
189 "my_float_array", {-16.8f, -8.4f, -4.2f, -2.1f, -1.0f, 0.0f, 1.0f, 2.1f, 4.2f, 8.4f, 16.8f});
190}
191
192TEST(idprop, convert_idp_double_array_to_value)
193{
195 "my_double_array", {-16.8, -8.4, -4.2, -2.1, -1.0, 0.0, 1.0, 2.1, 4.2, 8.4, 16.8});
196}
197
198static std::unique_ptr<Value> parse_json(StringRef input)
199{
200 std::stringstream is(input);
201 JsonFormatter json;
202 std::unique_ptr<Value> value = json.deserialize(is);
203 return value;
204}
205
206static std::string to_json(const Value &value)
207{
208 std::stringstream out;
209 JsonFormatter json;
210 json.serialize(out, value);
211 return out.str();
212}
213
214static void test_idprop(const IDProperty *id_property,
215 StringRef expected_name,
216 StringRef expected_value)
217{
218 ASSERT_NE(id_property, nullptr);
219 EXPECT_EQ(id_property->type, IDP_STRING);
220 EXPECT_EQ(id_property->name, expected_name);
221 EXPECT_EQ(IDP_String(id_property), expected_value);
222}
223
224static void test_idprop(const IDProperty *id_property,
225 StringRef expected_name,
226 int32_t expected_value)
227{
228 ASSERT_NE(id_property, nullptr);
229 EXPECT_EQ(id_property->type, IDP_INT);
230 EXPECT_EQ(id_property->name, expected_name);
231 EXPECT_EQ(IDP_Int(id_property), expected_value);
232}
233
234static void test_idprop(const IDProperty *id_property,
235 StringRef expected_name,
236 float expected_value)
237{
238 ASSERT_NE(id_property, nullptr);
239 EXPECT_EQ(id_property->type, IDP_FLOAT);
240 EXPECT_EQ(id_property->name, expected_name);
241 EXPECT_EQ(IDP_Float(id_property), expected_value);
242}
243
244static void test_idprop(const IDProperty *id_property,
245 StringRef expected_name,
246 double expected_value)
247{
248 ASSERT_NE(id_property, nullptr);
249 EXPECT_EQ(id_property->type, IDP_DOUBLE);
250 EXPECT_EQ(id_property->name, expected_name);
251 EXPECT_EQ(IDP_Double(id_property), expected_value);
252}
253
254static void test_idprop(const IDProperty *id_property,
255 StringRef expected_name,
256 const Span<int32_t> values)
257{
258 ASSERT_NE(id_property, nullptr);
259 EXPECT_EQ(id_property->type, IDP_ARRAY);
260 EXPECT_EQ(id_property->subtype, IDP_INT);
261 EXPECT_EQ(id_property->len, values.size());
262 EXPECT_EQ(id_property->name, expected_name);
263 int32_t *idprop_values = static_cast<int32_t *>(IDP_Array(id_property));
264 for (int i = 0; i < values.size(); i++) {
265 EXPECT_EQ(idprop_values[i], values[i]);
266 }
267}
268
269static void test_idprop(const IDProperty *id_property,
270 StringRef expected_name,
271 const Span<float> values)
272{
273 ASSERT_NE(id_property, nullptr);
274 EXPECT_EQ(id_property->type, IDP_ARRAY);
275 EXPECT_EQ(id_property->subtype, IDP_FLOAT);
276 EXPECT_EQ(id_property->len, values.size());
277 EXPECT_EQ(id_property->name, expected_name);
278 float *idprop_values = static_cast<float *>(IDP_Array(id_property));
279 for (int i = 0; i < values.size(); i++) {
280 EXPECT_EQ(idprop_values[i], values[i]);
281 }
282}
283
284static void test_idprop(const IDProperty *id_property,
285 StringRef expected_name,
286 const Span<double> values)
287{
288 ASSERT_NE(id_property, nullptr);
289 EXPECT_EQ(id_property->type, IDP_ARRAY);
290 EXPECT_EQ(id_property->subtype, IDP_DOUBLE);
291 EXPECT_EQ(id_property->len, values.size());
292 EXPECT_EQ(id_property->name, expected_name);
293 double *idprop_values = static_cast<double *>(IDP_Array(id_property));
294 for (int i = 0; i < values.size(); i++) {
295 EXPECT_EQ(idprop_values[i], values[i]);
296 }
297}
298
299template<typename Type>
301 StringRef expected_name,
302 Type expected_value)
303{
304 std::unique_ptr<Value> value = parse_json(input);
305 IDProperty *id_property = convert_from_serialize_value(*value);
306 test_idprop(id_property, expected_name, expected_value);
307 IDP_FreeProperty(id_property);
308}
309
310TEST(idprop, convert_idp_string_from_value)
311{
313 R"([{"name":"MyStringName","type":"IDP_STRING","value":"MyString"}])",
314 "MyStringName",
315 "MyString");
316}
317
318TEST(idprop, convert_idp_int_from_value)
319{
321 R"([{"name":"MyIntegerName","type":"IDP_INT","value":42}])", "MyIntegerName", 42);
322}
323
324TEST(idprop, convert_idp_float_from_value)
325{
327 R"([{"name":"MyFloatName","type":"IDP_FLOAT","value":42.24}])", "MyFloatName", 42.24f);
328}
329
330TEST(idprop, convert_idp_double_from_value)
331{
333 R"([{"name":"MyDoubleName","type":"IDP_DOUBLE","value":42.24}])", "MyDoubleName", 42.24);
334}
335
336TEST(idprop, convert_idp_array_int_from_value)
337{
339 R"([{"name":"MyArrayName","type":"IDP_ARRAY","subtype":"IDP_INT","value":[42, 24, 35]}])",
340 "MyArrayName",
341 Vector<int32_t>{42, 24, 35});
342}
343
344TEST(idprop, convert_idp_array_float_from_value)
345{
347 R"([{"name":"MyArrayName","type":"IDP_ARRAY","subtype":"IDP_FLOAT","value":[42.0, 24.4, 35.2]}])",
348 "MyArrayName",
349 Vector<float>{42.0f, 24.4f, 35.2f});
350}
351
352TEST(idprop, convert_idp_array_double_from_value)
353{
355 R"([{"name":"MyArrayName","type":"IDP_ARRAY","subtype":"IDP_DOUBLE","value":[42.43,24.5,35.8]}])",
356 "MyArrayName",
357 Vector<double>{42.43, 24.5, 35.8});
358}
359
360TEST(idprop, convert_idp_multiple_from_value)
361{
362 static const std::string input_json =
363 R"([{"name":"MyIntegerName","type":"IDP_INT","value":42},{"name":"MyStringName","type":"IDP_STRING","value":"MyString"},{"name":"MyFloatName","type":"IDP_FLOAT","value":42.24},{"name":"MyDoubleName","type":"IDP_DOUBLE","value":42.24}])";
364 std::unique_ptr<Value> value = parse_json(input_json);
365
366 IDProperty *id_property = convert_from_serialize_value(*value);
367 IDProperty *id_property_1 = id_property;
368 ASSERT_NE(id_property_1, nullptr);
369 IDProperty *id_property_2 = id_property_1->next;
370 ASSERT_NE(id_property_2, nullptr);
371 IDProperty *id_property_3 = id_property_2->next;
372 ASSERT_NE(id_property_3, nullptr);
373 IDProperty *id_property_4 = id_property_3->next;
374 ASSERT_NE(id_property_4, nullptr);
375
376 EXPECT_EQ(id_property_1->prev, nullptr);
377 EXPECT_EQ(id_property_2->prev, id_property_1);
378 EXPECT_EQ(id_property_3->prev, id_property_2);
379 EXPECT_EQ(id_property_4->prev, id_property_3);
380 EXPECT_EQ(id_property_4->next, nullptr);
381
382 test_idprop(id_property_1, "MyIntegerName", 42);
383 test_idprop(id_property_2, "MyStringName", "MyString");
384 test_idprop(id_property_3, "MyFloatName", 42.24f);
385 test_idprop(id_property_4, "MyDoubleName", 42.24);
386
387 IDP_FreeProperty(id_property_1);
388 IDP_FreeProperty(id_property_2);
389 IDP_FreeProperty(id_property_3);
390 IDP_FreeProperty(id_property_4);
391}
392
393TEST(idprop, convert_idp_multiple_roundtrip)
394{
395 static const std::string input_json =
396 R"([{"name":"MyIntegerName","type":"IDP_INT","value":42},{"name":"MyStringName","type":"IDP_STRING","value":"MyString"},{"name":"MyFloatName","type":"IDP_FLOAT","value":42.2400016784668},{"name":"MyDoubleName","type":"IDP_DOUBLE","value":42.24}])";
397 std::unique_ptr<Value> value = parse_json(input_json);
398
399 IDProperty *id_property = convert_from_serialize_value(*value);
400 IDProperty *id_property_1 = id_property;
401 ASSERT_NE(id_property_1, nullptr);
402 IDProperty *id_property_2 = id_property_1->next;
403 ASSERT_NE(id_property_2, nullptr);
404 IDProperty *id_property_3 = id_property_2->next;
405 ASSERT_NE(id_property_3, nullptr);
406 IDProperty *id_property_4 = id_property_3->next;
407 ASSERT_NE(id_property_4, nullptr);
408
409 std::unique_ptr<Value> value_from_id_properties = convert_to_serialize_values(id_property);
410 std::string output_json = to_json(*value_from_id_properties);
411 EXPECT_EQ(input_json, output_json);
412
413 IDP_FreeProperty(id_property_1);
414 IDP_FreeProperty(id_property_2);
415 IDP_FreeProperty(id_property_3);
416 IDP_FreeProperty(id_property_4);
417}
418
419TEST(idprop, convert_idp_group_from_value)
420{
421 static const std::string input_json =
422 R"([{"name":"AssetMetaData.properties","type":"IDP_GROUP","value":[{"name":"dimensions","type":"IDP_ARRAY","subtype":"IDP_FLOAT","value":[2.0,2.0,2.0]}]}])";
423 std::unique_ptr<Value> value = parse_json(input_json);
424
425 IDProperty *id_property = convert_from_serialize_value(*value);
426 ASSERT_NE(id_property, nullptr);
427 EXPECT_EQ(id_property->type, IDP_GROUP);
428 EXPECT_EQ(BLI_listbase_count(&id_property->data.group), 1);
429
430 test_idprop(static_cast<IDProperty *>(id_property->data.group.first),
431 "dimensions",
432 Vector<float>{2.0f, 2.0f, 2.0f});
433
434 IDP_FreeProperty(id_property);
435}
436
437} // namespace blender::bke::idprop::tests
#define IDP_Float(prop)
#define IDP_Int(prop)
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1227
#define IDP_String(prop)
#define IDP_Double(prop)
#define IDP_Array(prop)
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
ID and Library types, which are fundamental for SDNA.
@ IDP_DOUBLE
@ IDP_FLOAT
@ IDP_STRING
@ IDP_INT
@ IDP_GROUP
@ IDP_ARRAY
constexpr int64_t size() const
Definition BLI_span.hh:253
int64_t size() const
Span< std::shared_ptr< Value > > elements() const
std::unique_ptr< Value > deserialize(std::istream &is) override
Definition serialize.cc:363
void serialize(std::ostream &os, const Value &value) override
Definition serialize.cc:351
const ArrayValue * as_array_value() const
Definition serialize.cc:52
const DictionaryValue * as_dictionary_value() const
Definition serialize.cc:60
static void test_float_to_value(const StringRefNull prop_name, float prop_content)
static void test_string_to_value(const StringRefNull prop_name, const StringRefNull prop_content)
static void test_convert_idprop_from_value(StringRef input, StringRef expected_name, Type expected_value)
static void test_idprop(const IDProperty *id_property, StringRef expected_name, StringRef expected_value)
static void test_array_to_value(const StringRefNull prop_name, Vector< PrimitiveType > prop_content)
static void check_container_value(ArrayValue *value)
static void test_int_to_value(const StringRefNull prop_name, int32_t prop_content)
TEST(idprop, convert_idp_string_to_value)
static void check_object_attribute(const DictionaryValue::Lookup &lookup, const std::string expected_key, const std::string expected_value)
static std::unique_ptr< Value > parse_json(StringRef input)
static void test_double_to_value(const StringRefNull prop_name, double prop_content)
static std::string to_json(const Value &value)
std::unique_ptr< blender::io::serialize::ArrayValue > convert_to_serialize_values(const IDProperty *properties)
Convert the given properties to Value objects for serialization.
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_INT, set its name and value.
IDProperty * convert_from_serialize_value(const blender::io::serialize::Value &value)
Convert the given value to an IDProperty.
signed int int32_t
Definition stdint.h:77
ListBase group
Definition DNA_ID.h:146
int len
Definition DNA_ID.h:174
struct IDProperty * next
Definition DNA_ID.h:152
char name[64]
Definition DNA_ID.h:163
IDPropertyData data
Definition DNA_ID.h:168
struct IDProperty * prev
Definition DNA_ID.h:152
char subtype
Definition DNA_ID.h:159
char type
Definition DNA_ID.h:154
void * first