Blender  V2.93
FN_multi_function_test.cc
Go to the documentation of this file.
1 /* Apache License, Version 2.0 */
2 
3 #include "testing/testing.h"
4 
5 #include "FN_multi_function.hh"
7 
8 namespace blender::fn::tests {
9 namespace {
10 
11 class AddFunction : public MultiFunction {
12  public:
13  AddFunction()
14  {
15  static MFSignature signature = create_signature();
16  this->set_signature(&signature);
17  }
18 
19  static MFSignature create_signature()
20  {
21  MFSignatureBuilder signature("Add");
22  signature.single_input<int>("A");
23  signature.single_input<int>("B");
24  signature.single_output<int>("Result");
25  return signature.build();
26  }
27 
28  void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
29  {
30  const VArray<int> &a = params.readonly_single_input<int>(0, "A");
31  const VArray<int> &b = params.readonly_single_input<int>(1, "B");
32  MutableSpan<int> result = params.uninitialized_single_output<int>(2, "Result");
33 
34  for (int64_t i : mask) {
35  result[i] = a[i] + b[i];
36  }
37  }
38 };
39 
40 TEST(multi_function, AddFunction)
41 {
42  AddFunction fn;
43 
44  Array<int> input1 = {4, 5, 6};
45  Array<int> input2 = {10, 20, 30};
46  Array<int> output(3, -1);
47 
48  MFParamsBuilder params(fn, 3);
49  params.add_readonly_single_input(input1.as_span());
50  params.add_readonly_single_input(input2.as_span());
51  params.add_uninitialized_single_output(output.as_mutable_span());
52 
53  MFContextBuilder context;
54 
55  fn.call({0, 2}, params, context);
56 
57  EXPECT_EQ(output[0], 14);
58  EXPECT_EQ(output[1], -1);
59  EXPECT_EQ(output[2], 36);
60 }
61 
62 class AddPrefixFunction : public MultiFunction {
63  public:
64  AddPrefixFunction()
65  {
66  static MFSignature signature = create_signature();
67  this->set_signature(&signature);
68  }
69 
70  static MFSignature create_signature()
71  {
72  MFSignatureBuilder signature{"Add Prefix"};
73  signature.single_input<std::string>("Prefix");
74  signature.single_mutable<std::string>("Strings");
75  return signature.build();
76  }
77 
78  void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
79  {
80  const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix");
81  MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
82 
83  for (int64_t i : mask) {
84  strings[i] = prefixes[i] + strings[i];
85  }
86  }
87 };
88 
89 TEST(multi_function, AddPrefixFunction)
90 {
91  AddPrefixFunction fn;
92 
93  Array<std::string> strings = {
94  "Hello",
95  "World",
96  "This is a test",
97  "Another much longer string to trigger an allocation",
98  };
99 
100  std::string prefix = "AB";
101 
102  MFParamsBuilder params(fn, strings.size());
103  params.add_readonly_single_input(&prefix);
104  params.add_single_mutable(strings.as_mutable_span());
105 
106  MFContextBuilder context;
107 
108  fn.call({0, 2, 3}, params, context);
109 
110  EXPECT_EQ(strings[0], "ABHello");
111  EXPECT_EQ(strings[1], "World");
112  EXPECT_EQ(strings[2], "ABThis is a test");
113  EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation");
114 }
115 
116 class CreateRangeFunction : public MultiFunction {
117  public:
118  CreateRangeFunction()
119  {
120  static MFSignature signature = create_signature();
121  this->set_signature(&signature);
122  }
123 
124  static MFSignature create_signature()
125  {
126  MFSignatureBuilder signature{"Create Range"};
127  signature.single_input<uint>("Size");
128  signature.vector_output<uint>("Range");
129  return signature.build();
130  }
131 
132  void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
133  {
134  const VArray<uint> &sizes = params.readonly_single_input<uint>(0, "Size");
135  GVectorArray &ranges = params.vector_output(1, "Range");
136 
137  for (int64_t i : mask) {
138  uint size = sizes[i];
139  for (uint j : IndexRange(size)) {
140  ranges.append(i, &j);
141  }
142  }
143  }
144 };
145 
146 TEST(multi_function, CreateRangeFunction)
147 {
148  CreateRangeFunction fn;
149 
150  GVectorArray ranges(CPPType::get<uint>(), 5);
151  GVectorArray_TypedMutableRef<uint> ranges_ref{ranges};
152  Array<uint> sizes = {3, 0, 6, 1, 4};
153 
154  MFParamsBuilder params(fn, ranges.size());
155  params.add_readonly_single_input(sizes.as_span());
156  params.add_vector_output(ranges);
157 
158  MFContextBuilder context;
159 
160  fn.call({0, 1, 2, 3}, params, context);
161 
162  EXPECT_EQ(ranges[0].size(), 3);
163  EXPECT_EQ(ranges[1].size(), 0);
164  EXPECT_EQ(ranges[2].size(), 6);
165  EXPECT_EQ(ranges[3].size(), 1);
166  EXPECT_EQ(ranges[4].size(), 0);
167 
168  EXPECT_EQ(ranges_ref[0][0], 0);
169  EXPECT_EQ(ranges_ref[0][1], 1);
170  EXPECT_EQ(ranges_ref[0][2], 2);
171  EXPECT_EQ(ranges_ref[2][0], 0);
172  EXPECT_EQ(ranges_ref[2][1], 1);
173 }
174 
175 class GenericAppendFunction : public MultiFunction {
176  private:
177  MFSignature signature_;
178 
179  public:
180  GenericAppendFunction(const CPPType &type)
181  {
182  MFSignatureBuilder signature{"Append"};
183  signature.vector_mutable("Vector", type);
184  signature.single_input("Value", type);
185  signature_ = signature.build();
186  this->set_signature(&signature_);
187  }
188 
189  void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
190  {
191  GVectorArray &vectors = params.vector_mutable(0, "Vector");
192  const GVArray &values = params.readonly_single_input(1, "Value");
193 
194  for (int64_t i : mask) {
195  BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer);
196  values.get(i, buffer);
197  vectors.append(i, buffer);
198  values.type().destruct(buffer);
199  }
200  }
201 };
202 
203 TEST(multi_function, GenericAppendFunction)
204 {
205  GenericAppendFunction fn(CPPType::get<int32_t>());
206 
207  GVectorArray vectors(CPPType::get<int32_t>(), 4);
208  GVectorArray_TypedMutableRef<int> vectors_ref{vectors};
209  vectors_ref.append(0, 1);
210  vectors_ref.append(0, 2);
211  vectors_ref.append(2, 6);
212  Array<int> values = {5, 7, 3, 1};
213 
214  MFParamsBuilder params(fn, vectors.size());
215  params.add_vector_mutable(vectors);
216  params.add_readonly_single_input(values.as_span());
217 
218  MFContextBuilder context;
219 
220  fn.call(IndexRange(vectors.size()), params, context);
221 
222  EXPECT_EQ(vectors[0].size(), 3);
223  EXPECT_EQ(vectors[1].size(), 1);
224  EXPECT_EQ(vectors[2].size(), 2);
225  EXPECT_EQ(vectors[3].size(), 1);
226 
227  EXPECT_EQ(vectors_ref[0][0], 1);
228  EXPECT_EQ(vectors_ref[0][1], 2);
229  EXPECT_EQ(vectors_ref[0][2], 5);
230  EXPECT_EQ(vectors_ref[1][0], 7);
231  EXPECT_EQ(vectors_ref[2][0], 6);
232  EXPECT_EQ(vectors_ref[2][1], 3);
233  EXPECT_EQ(vectors_ref[3][0], 1);
234 }
235 
236 TEST(multi_function, CustomMF_SI_SO)
237 {
238  CustomMF_SI_SO<std::string, uint> fn("strlen",
239  [](const std::string &str) { return str.size(); });
240 
241  Array<std::string> strings = {"hello", "world", "test", "another test"};
242  Array<uint> sizes(strings.size(), 0);
243 
244  MFParamsBuilder params(fn, strings.size());
245  params.add_readonly_single_input(strings.as_span());
246  params.add_uninitialized_single_output(sizes.as_mutable_span());
247 
248  MFContextBuilder context;
249 
250  fn.call(IndexRange(strings.size()), params, context);
251 
252  EXPECT_EQ(sizes[0], 5);
253  EXPECT_EQ(sizes[1], 5);
254  EXPECT_EQ(sizes[2], 4);
255  EXPECT_EQ(sizes[3], 12);
256 }
257 
258 TEST(multi_function, CustomMF_SI_SI_SO)
259 {
260  CustomMF_SI_SI_SO<int, int, int> fn("mul", [](int a, int b) { return a * b; });
261 
262  Array<int> values_a = {4, 6, 8, 9};
263  int value_b = 10;
264  Array<int> outputs(values_a.size(), -1);
265 
266  MFParamsBuilder params(fn, values_a.size());
267  params.add_readonly_single_input(values_a.as_span());
268  params.add_readonly_single_input(&value_b);
269  params.add_uninitialized_single_output(outputs.as_mutable_span());
270 
271  MFContextBuilder context;
272 
273  fn.call({0, 1, 3}, params, context);
274 
275  EXPECT_EQ(outputs[0], 40);
276  EXPECT_EQ(outputs[1], 60);
277  EXPECT_EQ(outputs[2], -1);
278  EXPECT_EQ(outputs[3], 90);
279 }
280 
281 TEST(multi_function, CustomMF_SI_SI_SI_SO)
282 {
283  CustomMF_SI_SI_SI_SO<int, std::string, bool, uint> fn{
284  "custom",
285  [](int a, const std::string &b, bool c) { return (uint)((uint)a + b.size() + (uint)c); }};
286 
287  Array<int> values_a = {5, 7, 3, 8};
288  Array<std::string> values_b = {"hello", "world", "another", "test"};
289  Array<bool> values_c = {true, false, false, true};
290  Array<uint> outputs(values_a.size(), 0);
291 
292  MFParamsBuilder params(fn, values_a.size());
293  params.add_readonly_single_input(values_a.as_span());
294  params.add_readonly_single_input(values_b.as_span());
295  params.add_readonly_single_input(values_c.as_span());
296  params.add_uninitialized_single_output(outputs.as_mutable_span());
297 
298  MFContextBuilder context;
299 
300  fn.call({1, 2, 3}, params, context);
301 
302  EXPECT_EQ(outputs[0], 0);
303  EXPECT_EQ(outputs[1], 12);
304  EXPECT_EQ(outputs[2], 10);
305  EXPECT_EQ(outputs[3], 13);
306 }
307 
308 TEST(multi_function, CustomMF_SM)
309 {
310  CustomMF_SM<std::string> fn("AddSuffix", [](std::string &value) { value += " test"; });
311 
312  Array<std::string> values = {"a", "b", "c", "d", "e"};
313 
314  MFParamsBuilder params(fn, values.size());
315  params.add_single_mutable(values.as_mutable_span());
316 
317  MFContextBuilder context;
318 
319  fn.call({1, 2, 3}, params, context);
320 
321  EXPECT_EQ(values[0], "a");
322  EXPECT_EQ(values[1], "b test");
323  EXPECT_EQ(values[2], "c test");
324  EXPECT_EQ(values[3], "d test");
325  EXPECT_EQ(values[4], "e");
326 }
327 
328 TEST(multi_function, CustomMF_Constant)
329 {
330  CustomMF_Constant<int> fn{42};
331 
332  Array<int> outputs(4, 0);
333 
334  MFParamsBuilder params(fn, outputs.size());
335  params.add_uninitialized_single_output(outputs.as_mutable_span());
336 
337  MFContextBuilder context;
338 
339  fn.call({0, 2, 3}, params, context);
340 
341  EXPECT_EQ(outputs[0], 42);
342  EXPECT_EQ(outputs[1], 0);
343  EXPECT_EQ(outputs[2], 42);
344  EXPECT_EQ(outputs[3], 42);
345 }
346 
347 TEST(multi_function, CustomMF_GenericConstant)
348 {
349  int value = 42;
350  CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value};
351  EXPECT_EQ(fn.param_name(0), "42");
352 
353  Array<int> outputs(4, 0);
354 
355  MFParamsBuilder params(fn, outputs.size());
356  params.add_uninitialized_single_output(outputs.as_mutable_span());
357 
358  MFContextBuilder context;
359 
360  fn.call({0, 1, 2}, params, context);
361 
362  EXPECT_EQ(outputs[0], 42);
363  EXPECT_EQ(outputs[1], 42);
364  EXPECT_EQ(outputs[2], 42);
365  EXPECT_EQ(outputs[3], 0);
366 }
367 
368 TEST(multi_function, CustomMF_GenericConstantArray)
369 {
370  std::array<int, 4> values = {3, 4, 5, 6};
371  CustomMF_GenericConstantArray fn{GSpan(Span(values))};
372  EXPECT_EQ(fn.param_name(0), "[3, 4, 5, 6, ]");
373 
374  GVectorArray vector_array{CPPType::get<int32_t>(), 4};
375  GVectorArray_TypedMutableRef<int> vector_array_ref{vector_array};
376 
377  MFParamsBuilder params(fn, vector_array.size());
378  params.add_vector_output(vector_array);
379 
380  MFContextBuilder context;
381 
382  fn.call({1, 2, 3}, params, context);
383 
384  EXPECT_EQ(vector_array[0].size(), 0);
385  EXPECT_EQ(vector_array[1].size(), 4);
386  EXPECT_EQ(vector_array[2].size(), 4);
387  EXPECT_EQ(vector_array[3].size(), 4);
388  for (int i = 1; i < 4; i++) {
389  EXPECT_EQ(vector_array_ref[i][0], 3);
390  EXPECT_EQ(vector_array_ref[i][1], 4);
391  EXPECT_EQ(vector_array_ref[i][2], 5);
392  EXPECT_EQ(vector_array_ref[i][3], 6);
393  }
394 }
395 
396 TEST(multi_function, CustomMF_Convert)
397 {
398  CustomMF_Convert<float, int> fn;
399 
400  Array<float> inputs = {5.4f, 7.1f, 9.0f};
401  Array<int> outputs(inputs.size(), 0);
402 
403  MFParamsBuilder params(fn, inputs.size());
404  params.add_readonly_single_input(inputs.as_span());
405  params.add_uninitialized_single_output(outputs.as_mutable_span());
406 
407  MFContextBuilder context;
408  fn.call({0, 2}, params, context);
409 
410  EXPECT_EQ(outputs[0], 5);
411  EXPECT_EQ(outputs[1], 0);
412  EXPECT_EQ(outputs[2], 9);
413 }
414 
415 } // namespace
416 } // namespace blender::fn::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
unsigned int uint
Definition: BLI_sys_types.h:83
#define UNUSED(x)
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
Definition: FN_cpp_type.hh:676
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
#define output
#define str(s)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
__kernel void ccl_constant KernelData ccl_global void ccl_global char ccl_global int ccl_global char ccl_global unsigned int ccl_global float * buffer
static unsigned c
Definition: RandGen.cpp:97
static unsigned a[3]
Definition: RandGen.cpp:92
TEST(cpp_type, Size)
static bNodeSocketTemplate outputs[]
static bNodeSocketTemplate inputs[]
struct SELECTID_Context context
Definition: select_engine.c:47
__int64 int64_t
Definition: stdint.h:92
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)