aboutsummaryrefslogtreecommitdiffstats
path: root/host/tests
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2019-03-24 16:54:11 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:13 -0800
commitefb1d5a4729ea892ea03b8d0265aae9e8fadfff1 (patch)
tree23cd4dbf85c635d381aaf7b0f6e9bd15eeca08fc /host/tests
parent44f9bca2c5b561fa3f5f08d0d616c91d2142cbf9 (diff)
downloaduhd-efb1d5a4729ea892ea03b8d0265aae9e8fadfff1.tar.gz
uhd-efb1d5a4729ea892ea03b8d0265aae9e8fadfff1.tar.bz2
uhd-efb1d5a4729ea892ea03b8d0265aae9e8fadfff1.zip
rfnoc: Add properties, nodes, and accessors
Adds the following classes: - uhd::rfnoc::node_t, the base class for RFNoC nodes - uhd::rfnoc::node_accessor_t, a class to access private properties - uhd::rfnoc::res_source_info, a struct that identifies where properties come from - uhd::rfnoc::property_t, and property_base_t (its parent) - uhd::rfnoc::prop_accessor_t, a class to access properties Add always dirty property (dirtifier). Also adds unit tests for properties.
Diffstat (limited to 'host/tests')
-rw-r--r--host/tests/CMakeLists.txt2
-rw-r--r--host/tests/rfnoc_node_test.cpp108
-rw-r--r--host/tests/rfnoc_property_test.cpp152
3 files changed, 262 insertions, 0 deletions
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index e4255196b..eab27833b 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -53,6 +53,7 @@ set(test_sources
expert_test.cpp
fe_conn_test.cpp
rfnoc_chdr_test.cpp
+ rfnoc_node_test.cpp
)
set(benchmark_sources
@@ -72,6 +73,7 @@ if(ENABLE_RFNOC)
rate_node_test.cpp
stream_sig_test.cpp
tick_node_test.cpp
+ rfnoc_property_test.cpp
)
endif(ENABLE_RFNOC)
diff --git a/host/tests/rfnoc_node_test.cpp b/host/tests/rfnoc_node_test.cpp
new file mode 100644
index 000000000..046f8ab33
--- /dev/null
+++ b/host/tests/rfnoc_node_test.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/rfnoc/node.hpp>
+#include <boost/test/unit_test.hpp>
+#include <iostream>
+
+using namespace uhd::rfnoc;
+
+class test_node_t : public node_t
+{
+public:
+ test_node_t(size_t num_inputs, size_t num_outputs)
+ : _num_input_ports(num_inputs), _num_output_ports(num_outputs)
+ {
+ register_property(&_double_prop_user);
+ register_property(&_double_prop_in);
+ register_property(&_double_prop_out);
+
+ // A property with a simple 1:1 dependency
+ add_property_resolver(
+ {&_double_prop_user}, {&_double_prop_out}, []() { std::cout << "foo" << std::endl; });
+ }
+
+ //! Register a property for the second time, with the goal of triggering an
+ // exception
+ void double_register()
+ {
+ register_property(&_double_prop_user);
+ }
+
+ //! Register an identical property for the first time, with the goal of
+ //triggering an exception
+ void double_register_input()
+ {
+ property_t<double> double_prop_in{"double_prop", 0.0, {res_source_info::INPUT_EDGE, 0}};
+ register_property(&double_prop_in);
+ }
+
+ //! This should throw an error because the property in the output isn't
+ // registered
+ void add_unregistered_resolver_in() {
+ property_t<double> temp{"temp", 0.0, {res_source_info::INPUT_EDGE, 5}};
+ add_property_resolver(
+ {&temp}, {}, []() { std::cout << "foo" << std::endl; });
+ }
+
+ //! This should throw an error because the property in the output isn't
+ // registered
+ void add_unregistered_resolver_out() {
+ property_t<double> temp{"temp", 0.0, {res_source_info::INPUT_EDGE, 5}};
+ add_property_resolver(
+ {&_double_prop_user}, {&temp}, []() { std::cout << "foo" << std::endl; });
+ }
+
+ size_t get_num_input_ports() const { return _num_input_ports; }
+ size_t get_num_output_ports() const { return _num_output_ports; }
+
+private:
+ property_t<double> _double_prop_user{"double_prop", 0.0, {res_source_info::USER}};
+ property_t<double> _double_prop_in{"double_prop", 0.0, {res_source_info::INPUT_EDGE, 0}};
+ property_t<double> _double_prop_out{
+ "double_prop", 0.0, {res_source_info::OUTPUT_EDGE, 1}};
+
+ const size_t _num_input_ports;
+ const size_t _num_output_ports;
+};
+
+BOOST_AUTO_TEST_CASE(test_node_prop_access)
+{
+ test_node_t TN1(2, 3);
+ test_node_t TN2(1, 1);
+
+ BOOST_REQUIRE_THROW(TN2.double_register(), uhd::runtime_error);
+ BOOST_REQUIRE_THROW(TN2.double_register_input(), uhd::runtime_error);
+ BOOST_REQUIRE_THROW(TN2.add_unregistered_resolver_in(), uhd::runtime_error);
+ BOOST_REQUIRE_THROW(TN2.add_unregistered_resolver_out(), uhd::runtime_error);
+
+ BOOST_CHECK_EQUAL(TN1.get_num_input_ports(), 2);
+ BOOST_CHECK_EQUAL(TN1.get_num_output_ports(), 3);
+
+ std::cout << TN1.get_unique_id() << std::endl;
+ BOOST_CHECK(TN1.get_unique_id() != TN2.get_unique_id());
+
+ auto user_prop_ids = TN1.get_property_ids();
+ BOOST_REQUIRE_EQUAL(user_prop_ids.size(), 1);
+ BOOST_CHECK_EQUAL(user_prop_ids[0], "double_prop");
+
+ BOOST_REQUIRE_THROW(TN1.get_property<int>("nonexistant_prop"), uhd::lookup_error);
+ // If this next test fails, RTTI is not available. There might be cases when
+ // that's expected, and when we encounter those we'll reconsider the test.
+ BOOST_REQUIRE_THROW(TN1.get_property<int>("double_prop"), uhd::type_error);
+ BOOST_REQUIRE_THROW(TN1.get_property<double>("double_prop", 5), uhd::lookup_error);
+
+ BOOST_CHECK_EQUAL(TN1.get_property<double>("double_prop"), 0.0);
+
+ BOOST_REQUIRE_THROW(TN1.set_property<int>("nonexistant_prop", 5), uhd::lookup_error);
+ // If this next test fails, RTTI is not available. There might be cases when
+ // that's expected, and when we encounter those we'll reconsider the test.
+ BOOST_REQUIRE_THROW(TN1.set_property<int>("double_prop", 5), uhd::type_error);
+
+ TN1.set_property<double>("double_prop", 4.2);
+ BOOST_CHECK_EQUAL(TN1.get_property<double>("double_prop"), 4.2);
+}
+
diff --git a/host/tests/rfnoc_property_test.cpp b/host/tests/rfnoc_property_test.cpp
new file mode 100644
index 000000000..13ba1d4eb
--- /dev/null
+++ b/host/tests/rfnoc_property_test.cpp
@@ -0,0 +1,152 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/rfnoc/property.hpp>
+#include <uhd/rfnoc/dirtifier.hpp>
+#include <uhdlib/rfnoc/prop_accessor.hpp>
+#include <boost/test/unit_test.hpp>
+#include <iostream>
+
+using namespace uhd::rfnoc;
+
+BOOST_AUTO_TEST_CASE(test_res_source_info)
+{
+ res_source_info S1(res_source_info::USER);
+
+ BOOST_CHECK_EQUAL(S1.type, res_source_info::USER);
+ BOOST_CHECK_EQUAL(S1.instance, 0);
+ BOOST_CHECK_EQUAL(S1.to_string(), "USER:0");
+
+ res_source_info S2{res_source_info::USER, 5};
+ BOOST_CHECK_EQUAL(S2.type, res_source_info::USER);
+ BOOST_CHECK_EQUAL(S2.instance, 5);
+
+ // Check initializer
+ auto src_info_printer = [](res_source_info rsi){
+ std::cout << static_cast<int>(rsi.type) << "::" << rsi.instance << std::endl;
+ };
+ src_info_printer({res_source_info::OUTPUT_EDGE, 5});
+ src_info_printer({res_source_info::OUTPUT_EDGE});
+
+ res_source_info S3{res_source_info::USER, 5};
+ BOOST_CHECK(S2 == S3);
+}
+
+BOOST_AUTO_TEST_CASE(test_get_set)
+{
+ constexpr int prop_i_val = 5;
+ // This is a legitimate way to initialize a property:
+ property_t<int> prop_i{"int_prop", prop_i_val, {res_source_info::USER, 2}};
+ BOOST_CHECK_EQUAL(prop_i.get_src_info().instance, 2);
+ BOOST_CHECK_EQUAL(prop_i.get_src_info().type, res_source_info::USER);
+ prop_accessor_t prop_accessor;
+
+ prop_accessor.mark_clean(prop_i);
+ auto access_lock = prop_accessor.get_scoped_prop_access(prop_i, property_base_t::RW);
+
+ BOOST_CHECK(!prop_i.is_dirty());
+ BOOST_CHECK_EQUAL(prop_i.get_id(), "int_prop");
+ BOOST_CHECK_EQUAL(prop_i.get_src_info().type, res_source_info::USER);
+
+ BOOST_CHECK_EQUAL(prop_i.get(), prop_i_val);
+ BOOST_CHECK_EQUAL(int(prop_i), prop_i_val);
+ BOOST_CHECK(prop_i == 5);
+
+ prop_i.set(42);
+ BOOST_CHECK_EQUAL(prop_i.get(), 42);
+ BOOST_CHECK(prop_i.is_dirty());
+ prop_accessor.mark_clean(prop_i);
+ BOOST_CHECK(!prop_i.is_dirty());
+ prop_i = 23;
+ BOOST_CHECK_EQUAL(prop_i.get(), 23);
+ BOOST_CHECK(prop_i.is_dirty());
+}
+
+BOOST_AUTO_TEST_CASE(test_lock)
+{
+ prop_accessor_t prop_accessor;
+ constexpr int prop_i_val = 5;
+ property_t<int> prop_i{"int_prop", prop_i_val, {res_source_info::USER}};
+ prop_accessor.mark_clean(prop_i);
+ prop_accessor.set_access(prop_i, property_base_t::RWLOCKED);
+ prop_i.set(5);
+ BOOST_REQUIRE_THROW(prop_i.set(42), uhd::resolve_error);
+}
+
+BOOST_AUTO_TEST_CASE(test_access)
+{
+ constexpr int prop_i_val = 5;
+ property_t<int> prop_i{"int_prop", prop_i_val, {res_source_info::USER}};
+ prop_accessor_t prop_accessor;
+
+ prop_accessor.mark_clean(prop_i);
+ BOOST_REQUIRE_THROW(prop_i.set(23), uhd::access_error);
+ prop_accessor.set_access(prop_i, property_base_t::RO);
+ BOOST_CHECK_EQUAL(prop_i.get(), prop_i_val);
+ BOOST_REQUIRE_THROW(prop_i.set(23), uhd::access_error);
+ prop_accessor.set_access(prop_i, property_base_t::RW);
+ prop_i.set(23);
+ BOOST_CHECK_EQUAL(prop_i.get(), 23);
+ prop_accessor.set_access(prop_i, property_base_t::NONE);
+
+ // Now test the scoped access mode
+ {
+ auto access_lock = prop_accessor.get_scoped_prop_access(prop_i, property_base_t::RW, property_base_t::NONE);
+ prop_i.set(42);
+ BOOST_CHECK_EQUAL(prop_i.get(), 42);
+ }
+ BOOST_REQUIRE_THROW(prop_i.get(), uhd::access_error);
+ BOOST_REQUIRE_THROW(prop_i.set(23), uhd::access_error);
+
+ // Now test a different default access mode
+ {
+ auto access_lock = prop_accessor.get_scoped_prop_access(
+ prop_i, property_base_t::RW, property_base_t::RO);
+ prop_i.set(42);
+ }
+ BOOST_CHECK_EQUAL(prop_i.get(), 42);
+ // The prop is still in RO mode now!
+ BOOST_CHECK_EQUAL(prop_i.get_access_mode(), property_base_t::RO);
+
+ res_source_info new_src_info{res_source_info::INPUT_EDGE, 1};
+ auto cloned_prop = prop_i.clone(new_src_info);
+ BOOST_CHECK(cloned_prop->get_src_info() == new_src_info);
+ BOOST_CHECK(prop_i.equal(cloned_prop.get()));
+}
+
+BOOST_AUTO_TEST_CASE(test_forward)
+{
+ prop_accessor_t prop_accessor;
+ property_t<int> prop_i1{"int_prop", 5, {res_source_info::USER}};
+ property_t<int> prop_i2{"int_prop", 0, {res_source_info::USER}};
+ property_t<double> prop_d{"double_prop", 0.0, {res_source_info::USER}};
+ prop_accessor.mark_clean(prop_i1);
+ prop_accessor.mark_clean(prop_i2);
+
+ BOOST_CHECK(prop_accessor.are_compatible(&prop_i1, &prop_i2));
+ prop_accessor.forward<false>(&prop_i1, &prop_i2);
+ prop_accessor.set_access(prop_i1, property_base_t::RO);
+ prop_accessor.set_access(prop_i2, property_base_t::RW);
+ BOOST_CHECK_EQUAL(prop_i2.get(), prop_i1.get());
+ BOOST_CHECK(!prop_i1.is_dirty());
+ BOOST_CHECK(prop_i2.is_dirty());
+ BOOST_CHECK(!prop_accessor.are_compatible(&prop_i1, &prop_d));
+ BOOST_REQUIRE_THROW(prop_accessor.forward<false>(&prop_i1, &prop_d), uhd::type_error);
+}
+
+BOOST_AUTO_TEST_CASE(test_dirtifier)
+{
+ prop_accessor_t prop_accessor{};
+ dirtifier_t dirtifier;
+ BOOST_CHECK(dirtifier.is_dirty());
+ prop_accessor.mark_clean(dirtifier);
+ BOOST_CHECK(dirtifier.is_dirty());
+ property_t<int> prop_i{"int_prop", 5, {res_source_info::USER}};
+ BOOST_REQUIRE_THROW(prop_accessor.forward<false>(&dirtifier, &prop_i), uhd::type_error);
+ BOOST_REQUIRE_THROW(prop_accessor.forward<false>(&prop_i, &dirtifier), uhd::type_error);
+ BOOST_CHECK(!prop_accessor.are_compatible(&prop_i, &dirtifier));
+ BOOST_CHECK(!prop_accessor.are_compatible(&dirtifier, &prop_i));
+}