diff options
author | Martin Braun <martin.braun@ettus.com> | 2019-04-24 18:23:31 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:14 -0800 |
commit | c97bdc6c94c98753215a90cf499af4bdf06db8e2 (patch) | |
tree | 67f623ae84acb045d145bd22036df60a1724b789 /host/tests/rfnoc_detailgraph_test.cpp | |
parent | f0371292a43c3e4e3c68d8631c57d64ab10faf4c (diff) | |
download | uhd-c97bdc6c94c98753215a90cf499af4bdf06db8e2.tar.gz uhd-c97bdc6c94c98753215a90cf499af4bdf06db8e2.tar.bz2 uhd-c97bdc6c94c98753215a90cf499af4bdf06db8e2.zip |
rfnoc: Add property propagation, Boost.Graph storage
- Adds a detail::graph_t class, which handles the propagation
- Adds methods to node_t to aid with propagation
- Adds unit tests
- Adds dynamic property forwarding:
Nodes are now able to forward properties they don't know about by
providing a forwarding policy. A good example is the FIFO block which
simply forwards most properties verbatim.
- node: Temporarily disabling consistency check at init
Diffstat (limited to 'host/tests/rfnoc_detailgraph_test.cpp')
-rw-r--r-- | host/tests/rfnoc_detailgraph_test.cpp | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/host/tests/rfnoc_detailgraph_test.cpp b/host/tests/rfnoc_detailgraph_test.cpp new file mode 100644 index 000000000..6273430e6 --- /dev/null +++ b/host/tests/rfnoc_detailgraph_test.cpp @@ -0,0 +1,215 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/node.hpp> +#include <uhd/utils/log.hpp> +#include <uhdlib/rfnoc/node_accessor.hpp> +#include <uhdlib/rfnoc/prop_accessor.hpp> +#include <uhdlib/rfnoc/graph.hpp> +#include <boost/test/unit_test.hpp> +#include <iostream> + +#include "rfnoc_graph_mock_nodes.hpp" + +using uhd::rfnoc::detail::graph_t; +using namespace uhd::rfnoc; + +namespace uhd { namespace rfnoc { namespace detail { + +/*! Helper class to access internals of detail::graph + * + * This is basically a cheat code to get around the 'private' part of graph_t. + */ +class graph_accessor_t +{ +public: + using vertex_descriptor = graph_t::rfnoc_graph_t::vertex_descriptor; + + graph_accessor_t(graph_t* graph_ptr) : _graph_ptr(graph_ptr) + { /* nop */ + } + + graph_t::rfnoc_graph_t& get_graph() + { + return _graph_ptr->_graph; + } + + template <typename VertexIterator> + graph_t::node_ref_t get_node_ref_from_iterator(VertexIterator it) + { + return boost::get(graph_t::vertex_property_t(), get_graph(), *it); + } + + auto find_neighbour(vertex_descriptor origin, res_source_info port_info) + { + return _graph_ptr->_find_neighbour(origin, port_info); + } + + auto find_dirty_nodes() + { + return _graph_ptr->_find_dirty_nodes(); + } + + auto get_topo_sorted_nodes() + { + return _graph_ptr->_vertices_to_nodes(_graph_ptr->_get_topo_sorted_nodes()); + } +private: + graph_t* _graph_ptr; +}; + +}}}; + +BOOST_AUTO_TEST_CASE(test_graph) +{ + graph_t graph{}; + uhd::rfnoc::detail::graph_accessor_t graph_accessor(&graph); + node_accessor_t node_accessor{}; + + auto& bgl_graph = graph_accessor.get_graph(); + + // Define some mock nodes: + // Source radio + mock_radio_node_t mock_rx_radio(0); + // Sink radio + mock_radio_node_t mock_tx_radio(1); + + // These init calls would normally be done by the framework + node_accessor.init_props(&mock_rx_radio); + node_accessor.init_props(&mock_tx_radio); + + // In this simple graph, all connections are identical from an edge info + // perspective, so we're lazy and share an edge_info object: + uhd::rfnoc::detail::graph_t::graph_edge_t edge_info; + edge_info.src_port = 0; + edge_info.dst_port = 0; + edge_info.property_propagation_active = true; + edge_info.edge = uhd::rfnoc::detail::graph_t::graph_edge_t::DYNAMIC; + + // Now create the graph: + graph.connect(&mock_rx_radio, &mock_tx_radio, edge_info); + + // A whole bunch of low-level checks first: + BOOST_CHECK_EQUAL(boost::num_vertices(bgl_graph), 2); + auto vertex_iterators = boost::vertices(bgl_graph); + auto vertex_iterator = vertex_iterators.first; + auto rx_descriptor = *vertex_iterator; + graph_t::node_ref_t node_ref = + graph_accessor.get_node_ref_from_iterator(vertex_iterator++); + BOOST_CHECK_EQUAL(node_ref->get_unique_id(), mock_rx_radio.get_unique_id()); + auto tx_descriptor = *vertex_iterator; + node_ref = graph_accessor.get_node_ref_from_iterator(vertex_iterator++); + BOOST_CHECK_EQUAL(node_ref->get_unique_id(), mock_tx_radio.get_unique_id()); + BOOST_CHECK(vertex_iterator == vertex_iterators.second); + + auto rx_neighbour_info = + graph_accessor.find_neighbour(rx_descriptor, {res_source_info::OUTPUT_EDGE, 0}); + BOOST_REQUIRE(rx_neighbour_info.first); + BOOST_CHECK_EQUAL( + rx_neighbour_info.first->get_unique_id(), mock_tx_radio.get_unique_id()); + BOOST_CHECK(std::tie(rx_neighbour_info.second.src_port, + rx_neighbour_info.second.dst_port, + rx_neighbour_info.second.property_propagation_active) + == std::tie(edge_info.src_port, + edge_info.dst_port, + edge_info.property_propagation_active)); + + auto tx_neighbour_info = + graph_accessor.find_neighbour(tx_descriptor, {res_source_info::INPUT_EDGE, 0}); + BOOST_REQUIRE(tx_neighbour_info.first); + BOOST_CHECK_EQUAL( + tx_neighbour_info.first->get_unique_id(), mock_rx_radio.get_unique_id()); + BOOST_CHECK(std::tie(tx_neighbour_info.second.src_port, + tx_neighbour_info.second.dst_port, + tx_neighbour_info.second.property_propagation_active) + == std::tie(edge_info.src_port, + edge_info.dst_port, + edge_info.property_propagation_active)); + + auto rx_upstream_neighbour_info = + graph_accessor.find_neighbour(rx_descriptor, {res_source_info::INPUT_EDGE, 0}); + BOOST_CHECK(rx_upstream_neighbour_info.first == nullptr); + auto tx_downstream_neighbour_info = + graph_accessor.find_neighbour(tx_descriptor, {res_source_info::OUTPUT_EDGE, 0}); + BOOST_CHECK(tx_downstream_neighbour_info.first == nullptr); + auto rx_wrongport_neighbour_info = + graph_accessor.find_neighbour(rx_descriptor, {res_source_info::OUTPUT_EDGE, 1}); + BOOST_CHECK(rx_wrongport_neighbour_info.first == nullptr); + auto tx_wrongport_neighbour_info = + graph_accessor.find_neighbour(tx_descriptor, {res_source_info::INPUT_EDGE, 1}); + BOOST_CHECK(tx_wrongport_neighbour_info.first == nullptr); + + // Check there are no dirty nodes (init_props() will clean them all) + BOOST_CHECK_EQUAL(graph_accessor.find_dirty_nodes().empty(), true); + + auto topo_sorted_nodes = graph_accessor.get_topo_sorted_nodes(); + BOOST_CHECK_EQUAL(topo_sorted_nodes.size(), 2); + BOOST_CHECK_EQUAL( + topo_sorted_nodes.at(0)->get_unique_id(), mock_rx_radio.get_unique_id()); + + // Now initialize the graph (will force a call to resolve_all_properties()) + graph.initialize(); + + // This will be ignored + graph.connect(&mock_rx_radio, &mock_tx_radio, edge_info); + BOOST_CHECK_EQUAL(boost::num_vertices(bgl_graph), 2); + + // Now attempt illegal connections (they must all fail) + edge_info.src_port = 1; + edge_info.dst_port = 0; + BOOST_REQUIRE_THROW( + graph.connect(&mock_rx_radio, &mock_tx_radio, edge_info), uhd::rfnoc_error); + edge_info.src_port = 0; + edge_info.dst_port = 1; + BOOST_REQUIRE_THROW( + graph.connect(&mock_rx_radio, &mock_tx_radio, edge_info), uhd::rfnoc_error); + edge_info.src_port = 0; + edge_info.dst_port = 0; + edge_info.property_propagation_active = false; + BOOST_REQUIRE_THROW( + graph.connect(&mock_rx_radio, &mock_tx_radio, edge_info), uhd::rfnoc_error); +} + +BOOST_AUTO_TEST_CASE(test_graph_unresolvable) +{ + graph_t graph{}; + node_accessor_t node_accessor{}; + + // Define some mock nodes: + // Source radio + mock_radio_node_t mock_rx_radio(0); + // Sink radio + mock_radio_node_t mock_tx_radio(1); + + // These init calls would normally be done by the framework + node_accessor.init_props(&mock_rx_radio); + node_accessor.init_props(&mock_tx_radio); + + // In this simple graph, all connections are identical from an edge info + // perspective, so we're lazy and share an edge_info object: + uhd::rfnoc::detail::graph_t::graph_edge_t edge_info( + 0, 0, graph_t::graph_edge_t::DYNAMIC, true); + + // Now create the graph and commit: + graph.connect(&mock_rx_radio, &mock_tx_radio, edge_info); + graph.initialize(); + + // Now set a property that will cause the graph to fail to resolve: + BOOST_REQUIRE_THROW(mock_tx_radio.set_property<double>("master_clock_rate", 100e6, 0), + uhd::resolve_error); + + // Now we add a back-edge + edge_info.src_port = 0; + edge_info.dst_port = 0; + edge_info.property_propagation_active = false; + graph.connect(&mock_tx_radio, &mock_rx_radio, edge_info); + UHD_LOG_INFO("TEST", "Testing back edge error path"); + mock_tx_radio.disable_samp_out_resolver = true; + // The set_property would be valid if we hadn't futzed with the back-edge + BOOST_REQUIRE_THROW(mock_tx_radio.set_property<double>("master_clock_rate", 200e6, 0), + uhd::resolve_error); + UHD_LOG_INFO("TEST", "^^^ Expected ERROR here."); +} |