1#include <Dataflow/RaDataflow.hpp>
2#include <catch2/catch_test_macros.hpp>
4#include <Dataflow/Core/DataflowGraph.hpp>
5#include <Dataflow/Core/Functionals/Types.hpp>
6#include <Dataflow/Core/Node.hpp>
7#include <Dataflow/Core/Sinks/SinkNode.hpp>
8#include <Dataflow/Core/Sinks/Types.hpp>
9#include <Dataflow/Core/Sources/Types.hpp>
16using namespace Ra::Dataflow::Core;
18class DummyNode :
public Node
24 DummyNode(
const DummyNode& ) =
delete;
25 DummyNode( DummyNode&& ) =
delete;
26 DummyNode& operator=(
const DummyNode& ) =
delete;
27 DummyNode& operator=( DummyNode&& ) =
delete;
28 explicit DummyNode(
const std::string& name ) : DummyNode( name, DummyNode::node_typename() ) {}
33 bool execute()
override {
return is_initialized(); }
37 void add_in_0() {
add_input( m_port_in_port0 ); }
38 void add_out_0() {
add_output( m_port_out_port0 ); }
41 RA_NODE_PORT_IN( Scalar, port0 );
42 RA_NODE_PORT_IN_WITH_DEFAULT( Scalar, port1, 2_ra );
43 RA_NODE_PORT_OUT( Scalar, port0 );
44 RA_NODE_PORT_OUT_WITH_DATA( Scalar, port1 );
45 RA_NODE_PARAMETER(
int, param0, 21 );
48TEST_CASE(
"Dataflow/Core/DummyNode",
"[unittests][Dataflow][Core]" ) {
50 DummyNode dummy {
"dummy" };
51 SECTION(
"Before initialization, node is not init (and exec fail if it checks init" ) {
52 REQUIRE( !dummy.is_initialized() );
53 REQUIRE( !dummy.execute() );
56 REQUIRE( dummy.is_initialized() );
57 REQUIRE( dummy.execute() );
60 SECTION(
"Two nodes are equals if same instance and type name" ) {
61 DummyNode dummy_bis {
"dummy" };
62 REQUIRE( dummy == dummy_bis );
65 SECTION(
"input ports are accessible by index or name or direct method" ) {
66 for (
int index = 0; index <= 1; ++index ) {
67 auto p_by_index = dummy.input_by_index( index );
68 REQUIRE( p_by_index );
70 REQUIRE( p_by_index->name() == port_name );
71 auto p_by_name = dummy.input_by_name( port_name );
72 REQUIRE( p_by_name.first == index );
73 REQUIRE( p_by_name.second );
74 REQUIRE( p_by_name.second->name() == port_name );
75 REQUIRE( p_by_index == p_by_name.second );
76 REQUIRE( p_by_index == dummy.port_by_index(
"in", index ) );
78 REQUIRE( dummy.port_in_port0().get() == dummy.input_by_index( 0 ) );
79 REQUIRE( dummy.port_in_port1().get() == dummy.input_by_index( 1 ) );
81 SECTION(
"output ports are accesiible by index or name" ) {
82 for (
int index = 0; index <= 1; ++index ) {
83 auto p_by_index = dummy.output_by_index( index );
84 REQUIRE( p_by_index );
86 REQUIRE( p_by_index->name() == port_name );
87 auto p_by_name = dummy.output_by_name( port_name );
88 REQUIRE( p_by_name.first == index );
89 REQUIRE( p_by_name.second );
90 REQUIRE( p_by_name.second->name() == port_name );
91 REQUIRE( p_by_index == p_by_name.second );
92 REQUIRE( p_by_index == dummy.port_by_index(
"out", index ) );
94 REQUIRE( dummy.port_out_port0().get() == dummy.output_by_index( 0 ) );
95 REQUIRE( dummy.port_out_port1().get() == dummy.output_by_index( 1 ) );
97 SECTION(
"port collection allows access to full port list" ) {
98 REQUIRE( dummy.inputs().size() == 2 );
99 REQUIRE( dummy.outputs().size() == 2 );
100 for (
int index = 0; index <= 1; ++index ) {
101 REQUIRE( dummy.inputs()[index].get() == dummy.input_by_index( index ) );
102 REQUIRE( dummy.inputs()[index].get() == dummy.input_by_index<Scalar>( index ) );
103 REQUIRE( dummy.outputs()[index].get() == dummy.output_by_index( index ) );
104 REQUIRE( dummy.outputs()[index].get() == dummy.output_by_index<Scalar>( index ) );
107 SECTION(
"node name can be tweaked" ) {
108 REQUIRE( dummy.model_name() ==
"DummyNode" );
109 REQUIRE( dummy.node_typename() ==
"DummyNode" );
110 REQUIRE( dummy.instance_name() ==
"dummy" );
111 REQUIRE( dummy.display_name() ==
"dummy" );
112 dummy.set_display_name(
"dummy new name" );
113 REQUIRE( dummy.display_name() ==
"dummy new name" );
114 REQUIRE( dummy.model_name() ==
"DummyNode" );
115 REQUIRE( dummy.node_typename() ==
"DummyNode" );
116 REQUIRE( dummy.instance_name() ==
"dummy" );
117 dummy.set_instance_name(
"dummy new name" );
118 REQUIRE( dummy.display_name() ==
"dummy new name" );
119 REQUIRE( dummy.model_name() ==
"DummyNode" );
120 REQUIRE( dummy.node_typename() ==
"DummyNode" );
121 REQUIRE( dummy.instance_name() ==
"dummy new name" );
123 SECTION(
"node metadata is empty at first, and can be edited" ) {
124 REQUIRE( dummy.metadata().empty() );
125 dummy.add_metadata( { {
"comment",
"test" }, {
"foo", 2 } } );
126 REQUIRE( !dummy.metadata().empty() );
127 REQUIRE( dummy.metadata()[
"comment"] ==
"test" );
128 REQUIRE( dummy.metadata()[
"foo"] == 2 );
129 dummy.add_metadata( { {
"comment",
"new" }, {
"bar", 3 } } );
130 REQUIRE( dummy.metadata()[
"comment"] ==
"new" );
131 REQUIRE( dummy.metadata()[
"foo"] == 2 );
132 REQUIRE( dummy.metadata()[
"bar"] == 3 );
134 dummy.add_metadata( { {
"comment",
"null"_json } } );
135 REQUIRE( !dummy.metadata().contains(
"comment" ) );
136 REQUIRE( dummy.metadata()[
"foo"] == 2 );
137 REQUIRE( dummy.metadata()[
"bar"] == 3 );
139 dummy.add_metadata( { {
"bar",
nullptr } } );
140 REQUIRE( !dummy.metadata().contains(
"comment" ) );
141 REQUIRE( dummy.metadata()[
"foo"] == 2 );
142 REQUIRE( !dummy.metadata().contains(
"bar" ) );
144 SECTION(
"Node's default value is set and editable" ) {
145 auto& def = dummy.port_in_port1()->default_value();
146 REQUIRE( def == 2_ra );
147 dummy.port_in_port1()->set_default_value( 3_ra );
148 REQUIRE( def == 3_ra );
150 REQUIRE( dummy.port_in_port1()->default_value() == 4_ra );
152 SECTION(
"Node's input variables are input ports default values" ) {
153 auto variables = dummy.input_variables();
154 REQUIRE( variables.size() == 1 );
156 REQUIRE( port1.get() == 2_ra );
158 REQUIRE( dummy.port_in_port1()->default_value() == 3_ra );
159 dummy.port_in_port1()->set_default_value( 4_ra );
160 REQUIRE( port1.get() == 4_ra );
162 SECTION(
"Node's parameters is accessible and editable with handle" ) {
163 auto& parameters = dummy.parameters();
164 REQUIRE( parameters.size() == 1 );
166 auto handle0 = parameters.getVariableHandle<
int>(
"param0" );
167 REQUIRE( handle0->second == 21 );
168 REQUIRE( dummy.param_param0() == handle0->second );
169 handle0->second = 42;
170 REQUIRE( parameters.getVariableHandle<
int>(
"param0" )->second == handle0->second );
171 REQUIRE( dummy.param_param0() == handle0->second );
173 SECTION(
"Node's parameters is accessible and editable with ref" ) {
174 auto& param0 = dummy.parameters().getVariable<
int>(
"param0" );
175 REQUIRE( param0 == 21 );
176 REQUIRE( dummy.param_param0() == param0 );
178 REQUIRE( dummy.parameters().getVariableHandle<
int>(
"param0" )->second == param0 );
179 REQUIRE( dummy.param_param0() == param0 );
182 SECTION(
"Missing instance fail from json" ) {
183 nlohmann::json json = { {} };
184 REQUIRE( !dummy.fromJson( json ) );
186 SECTION(
"Missing model fail from json" ) {
187 nlohmann::json json = { {
"instance",
"DummyNode" } };
188 REQUIRE( !dummy.fromJson( json ) );
190 SECTION(
"Valid json contains instance and model from json, display name set to model" ) {
191 nlohmann::json json = { {
"instance",
"DummyNode" }, {
"model",
"DummyNode" } };
192 REQUIRE( dummy.fromJson( json ) );
193 REQUIRE( dummy.display_name() ==
"DummyNode" );
195 SECTION(
"Valid json sets display name" ) {
196 nlohmann::json json = {
197 {
"instance",
"DummyNode" },
198 {
"model", { {
"name",
"DummyNode" }, {
"display_name",
"foo" } } } };
199 REQUIRE( dummy.fromJson( json ) );
200 REQUIRE( dummy.display_name() ==
"foo" );
202 SECTION(
"Valid json sets metadata" ) {
203 nlohmann::json json = { {
"instance",
"DummyNode" },
204 {
"model", { {
"name",
"DummyNode" } } },
206 REQUIRE( dummy.fromJson( json ) );
207 REQUIRE( dummy.metadata()[
"foo"] ==
"bar" );
209 SECTION(
"Remove port maintains ports index" ) {
210 REQUIRE( dummy.port_in_port1().get() == dummy.input_by_index( 1 ) );
212 REQUIRE( !dummy.input_by_index( 0 ) );
213 auto in_by_name = dummy.input_by_name(
"port0" );
214 REQUIRE( in_by_name.first.isInvalid() );
215 REQUIRE( !in_by_name.second );
216 REQUIRE( dummy.port_in_port1().get() == dummy.input_by_index( 1 ) );
218 REQUIRE( dummy.port_out_port1().get() == dummy.output_by_index( 1 ) );
219 dummy.remove_out_0();
220 REQUIRE( !dummy.input_by_index( 0 ) );
221 auto out_by_name = dummy.input_by_name(
"port0" );
222 REQUIRE( out_by_name.first.isInvalid() );
223 REQUIRE( !out_by_name.second );
224 REQUIRE( dummy.port_out_port1().get() == dummy.output_by_index( 1 ) );
227 REQUIRE( dummy.port_in_port0().get() == dummy.input_by_index( 0 ) );
228 REQUIRE( dummy.port_in_port1().get() == dummy.input_by_index( 1 ) );
230 REQUIRE( dummy.port_out_port0().get() == dummy.output_by_index( 0 ) );
231 REQUIRE( dummy.port_out_port1().get() == dummy.output_by_index( 1 ) );
235TEST_CASE(
"Dataflow/Core/LinkMandatory",
"[unittests][Dataflow][Core]" ) {
236 SECTION(
"Port without default value needs to be linked" ) {
237 using TestNode = Functionals::FunctionNode<int>;
239 REQUIRE( n->port_in_data()->is_link_mandatory() );
240 REQUIRE( !n->port_in_op()->is_link_mandatory() );
241 n->port_in_data()->set_default_value( 5 );
242 REQUIRE( !n->port_in_data()->is_link_mandatory() );
246TEST_CASE(
"Dataflow/Core/Sink",
"[unittests][Dataflow][Core]" ) {
248 REQUIRE( node.is_output() );
249 REQUIRE( !node.execute() );
250 REQUIRE( !node.is_initialized() );
252 SECTION(
"Without data, sink could not execute nor has data" ) {
257 SECTION(
"Set data needs also initilized to be executed" ) {
258 node.port_in_from()->set_default_value( 1 );
259 REQUIRE( !node.execute() );
260 REQUIRE( !node.is_initialized() );
262 REQUIRE( node.is_initialized() );
263 REQUIRE( node.execute() );
Base abstract class for all the nodes added and used by the node system.
PortIndex add_output(PortBaseOutPtr out)
Convenience alias to add_port(outputs(), out)
void remove_input(PortIndex index)
Remove the given port from the managed (input/output) ports.
PortIndex add_input(PortBaseInPtr in)
Convenience alias to add_port(inputs(), in)
void remove_output(PortIndex index)
Remove the given port from the managed (input/output) ports.
Base class for nodes that will store the result of a computation graph.