1
1
This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/functions/tests/FN_multi_function_procedure_test.cc
2021-08-23 15:57:50 +02:00

345 lines
10 KiB
C++

/* Apache License, Version 2.0 */
#include "testing/testing.h"
#include "FN_multi_function_builder.hh"
#include "FN_multi_function_procedure_builder.hh"
#include "FN_multi_function_procedure_executor.hh"
#include "FN_multi_function_test_common.hh"
namespace blender::fn::tests {
TEST(multi_function_procedure, SimpleTest)
{
/**
* procedure(int var1, int var2, int *var4) {
* int var3 = var1 + var2;
* var4 = var2 + var3;
* var4 += 10;
* }
*/
CustomMF_SI_SI_SO<int, int, int> add_fn{"add", [](int a, int b) { return a + b; }};
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var1 = &builder.add_single_input_parameter<int>();
MFVariable *var2 = &builder.add_single_input_parameter<int>();
auto [var3] = builder.add_call<1>(add_fn, {var1, var2});
auto [var4] = builder.add_call<1>(add_fn, {var2, var3});
builder.add_call(add_10_fn, {var4});
builder.add_destruct({var1, var2, var3});
builder.add_return();
builder.add_output_parameter(*var4);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor executor{"My Procedure", procedure};
MFParamsBuilder params{executor, 3};
MFContextBuilder context;
Array<int> input_array = {1, 2, 3};
params.add_readonly_single_input(input_array.as_span());
params.add_readonly_single_input_value(3);
Array<int> output_array(3);
params.add_uninitialized_single_output(output_array.as_mutable_span());
executor.call(IndexRange(3), params, context);
EXPECT_EQ(output_array[0], 17);
EXPECT_EQ(output_array[1], 18);
EXPECT_EQ(output_array[2], 19);
}
TEST(multi_function_procedure, BranchTest)
{
/**
* procedure(int &var1, bool var2) {
* if (var2) {
* var1 += 100;
* }
* else {
* var1 += 10;
* }
* var1 += 10;
* }
*/
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
CustomMF_SM<int> add_100_fn{"add_100", [](int &a) { a += 100; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var1 = &builder.add_single_mutable_parameter<int>();
MFVariable *var2 = &builder.add_single_input_parameter<bool>();
MFProcedureBuilder::Branch branch = builder.add_branch(*var2);
branch.branch_false.add_call(add_10_fn, {var1});
branch.branch_true.add_call(add_100_fn, {var1});
builder.set_cursor_after_branch(branch);
builder.add_call(add_10_fn, {var1});
builder.add_destruct({var2});
builder.add_return();
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Condition Test", procedure};
MFParamsBuilder params(procedure_fn, 5);
Array<int> values_a = {1, 5, 3, 6, 2};
Array<bool> values_cond = {true, false, true, true, false};
params.add_single_mutable(values_a.as_mutable_span());
params.add_readonly_single_input(values_cond.as_span());
MFContextBuilder context;
procedure_fn.call({1, 2, 3, 4}, params, context);
EXPECT_EQ(values_a[0], 1);
EXPECT_EQ(values_a[1], 25);
EXPECT_EQ(values_a[2], 113);
EXPECT_EQ(values_a[3], 116);
EXPECT_EQ(values_a[4], 22);
}
TEST(multi_function_procedure, EvaluateOne)
{
/**
* procedure(int var1, int *var2) {
* var2 = var1 + 10;
* }
*/
int tot_evaluations = 0;
CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) {
tot_evaluations++;
return a + 10;
}};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var1 = &builder.add_single_input_parameter<int>();
auto [var2] = builder.add_call<1>(add_10_fn, {var1});
builder.add_destruct(*var1);
builder.add_return();
builder.add_output_parameter(*var2);
MFProcedureExecutor procedure_fn{"Evaluate One", procedure};
MFParamsBuilder params{procedure_fn, 5};
Array<int> values_out = {1, 2, 3, 4, 5};
params.add_readonly_single_input_value(1);
params.add_uninitialized_single_output(values_out.as_mutable_span());
MFContextBuilder context;
procedure_fn.call({0, 1, 3, 4}, params, context);
EXPECT_EQ(values_out[0], 11);
EXPECT_EQ(values_out[1], 11);
EXPECT_EQ(values_out[2], 3);
EXPECT_EQ(values_out[3], 11);
EXPECT_EQ(values_out[4], 11);
/* We expect only one evaluation, because the input is constant. */
EXPECT_EQ(tot_evaluations, 1);
}
TEST(multi_function_procedure, SimpleLoop)
{
/**
* procedure(int count, int *out) {
* out = 1;
* int index = 0'
* loop {
* if (index >= count) {
* break;
* }
* out *= 2;
* index += 1;
* }
* out += 1000;
* }
*/
CustomMF_Constant<int> const_1_fn{1};
CustomMF_Constant<int> const_0_fn{0};
CustomMF_SI_SI_SO<int, int, bool> greater_or_equal_fn{"greater or equal",
[](int a, int b) { return a >= b; }};
CustomMF_SM<int> double_fn{"double", [](int &a) { a *= 2; }};
CustomMF_SM<int> add_1000_fn{"add 1000", [](int &a) { a += 1000; }};
CustomMF_SM<int> add_1_fn{"add 1", [](int &a) { a += 1; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var_count = &builder.add_single_input_parameter<int>("count");
auto [var_out] = builder.add_call<1>(const_1_fn);
var_out->set_name("out");
auto [var_index] = builder.add_call<1>(const_0_fn);
var_index->set_name("index");
MFProcedureBuilder::Loop loop = builder.add_loop();
auto [var_condition] = builder.add_call<1>(greater_or_equal_fn, {var_index, var_count});
var_condition->set_name("condition");
MFProcedureBuilder::Branch branch = builder.add_branch(*var_condition);
branch.branch_true.add_destruct(*var_condition);
branch.branch_true.add_loop_break(loop);
branch.branch_false.add_destruct(*var_condition);
builder.set_cursor_after_branch(branch);
builder.add_call(double_fn, {var_out});
builder.add_call(add_1_fn, {var_index});
builder.add_loop_continue(loop);
builder.set_cursor_after_loop(loop);
builder.add_call(add_1000_fn, {var_out});
builder.add_destruct({var_count, var_index});
builder.add_return();
builder.add_output_parameter(*var_out);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Simple Loop", procedure};
MFParamsBuilder params{procedure_fn, 5};
Array<int> counts = {4, 3, 7, 6, 4};
Array<int> results(5, -1);
params.add_readonly_single_input(counts.as_span());
params.add_uninitialized_single_output(results.as_mutable_span());
MFContextBuilder context;
procedure_fn.call({0, 1, 3, 4}, params, context);
EXPECT_EQ(results[0], 1016);
EXPECT_EQ(results[1], 1008);
EXPECT_EQ(results[2], -1);
EXPECT_EQ(results[3], 1064);
EXPECT_EQ(results[4], 1016);
}
TEST(multi_function_procedure, Vectors)
{
/**
* procedure(vector<int> v1, vector<int> &v2, vector<int> *v3) {
* v1.extend(v2);
* int constant = 5;
* v2.append(constant);
* v2.extend(v1);
* int len = sum(v2);
* v3 = range(len);
* }
*/
CreateRangeFunction create_range_fn;
ConcatVectorsFunction extend_fn;
GenericAppendFunction append_fn{CPPType::get<int>()};
SumVectorFunction sum_elements_fn;
CustomMF_Constant<int> constant_5_fn{5};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var_v1 = &builder.add_input_parameter(MFDataType::ForVector<int>());
MFVariable *var_v2 = &builder.add_parameter(MFParamType::ForMutableVector(CPPType::get<int>()));
builder.add_call(extend_fn, {var_v1, var_v2});
auto [var_constant] = builder.add_call<1>(constant_5_fn);
builder.add_call(append_fn, {var_v2, var_constant});
builder.add_destruct(*var_constant);
builder.add_call(extend_fn, {var_v2, var_v1});
auto [var_len] = builder.add_call<1>(sum_elements_fn, {var_v2});
auto [var_v3] = builder.add_call<1>(create_range_fn, {var_len});
builder.add_destruct({var_v1, var_len});
builder.add_return();
builder.add_output_parameter(*var_v3);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Vectors", procedure};
MFParamsBuilder params{procedure_fn, 5};
Array<int> v1 = {5, 2, 3};
GVectorArray v2{CPPType::get<int>(), 5};
GVectorArray v3{CPPType::get<int>(), 5};
int value_10 = 10;
v2.append(0, &value_10);
v2.append(4, &value_10);
params.add_readonly_vector_input(v1.as_span());
params.add_vector_mutable(v2);
params.add_vector_output(v3);
MFContextBuilder context;
procedure_fn.call({0, 1, 3, 4}, params, context);
EXPECT_EQ(v2[0].size(), 6);
EXPECT_EQ(v2[1].size(), 4);
EXPECT_EQ(v2[2].size(), 0);
EXPECT_EQ(v2[3].size(), 4);
EXPECT_EQ(v2[4].size(), 6);
EXPECT_EQ(v3[0].size(), 35);
EXPECT_EQ(v3[1].size(), 15);
EXPECT_EQ(v3[2].size(), 0);
EXPECT_EQ(v3[3].size(), 15);
EXPECT_EQ(v3[4].size(), 35);
}
TEST(multi_function_procedure, BufferReuse)
{
/**
* procedure(int a, int *out) {
* int b = a + 10;
* int c = c + 10;
* int d = d + 10;
* int e = d + 10;
* out = e + 10;
* }
*/
CustomMF_SI_SO<int, int> add_10_fn{"add 10", [](int a) { return a + 10; }};
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
MFVariable *var_a = &builder.add_single_input_parameter<int>();
auto [var_b] = builder.add_call<1>(add_10_fn, {var_a});
builder.add_destruct(*var_a);
auto [var_c] = builder.add_call<1>(add_10_fn, {var_b});
builder.add_destruct(*var_b);
auto [var_d] = builder.add_call<1>(add_10_fn, {var_c});
builder.add_destruct(*var_c);
auto [var_e] = builder.add_call<1>(add_10_fn, {var_d});
builder.add_destruct(*var_d);
auto [var_out] = builder.add_call<1>(add_10_fn, {var_e});
builder.add_destruct(*var_e);
builder.add_return();
builder.add_output_parameter(*var_out);
EXPECT_TRUE(procedure.validate());
MFProcedureExecutor procedure_fn{"Buffer Reuse", procedure};
Array<int> inputs = {4, 1, 6, 2, 3};
Array<int> results(5, -1);
MFParamsBuilder params{procedure_fn, 5};
params.add_readonly_single_input(inputs.as_span());
params.add_uninitialized_single_output(results.as_mutable_span());
MFContextBuilder context;
procedure_fn.call({0, 2, 3, 4}, params, context);
EXPECT_EQ(results[0], 54);
EXPECT_EQ(results[1], -1);
EXPECT_EQ(results[2], 56);
EXPECT_EQ(results[3], 52);
EXPECT_EQ(results[4], 53);
}
} // namespace blender::fn::tests