diff options
author | Martin Braun <martin.braun@ettus.com> | 2019-03-24 16:54:11 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:13 -0800 |
commit | efb1d5a4729ea892ea03b8d0265aae9e8fadfff1 (patch) | |
tree | 23cd4dbf85c635d381aaf7b0f6e9bd15eeca08fc /host/tests | |
parent | 44f9bca2c5b561fa3f5f08d0d616c91d2142cbf9 (diff) | |
download | uhd-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.txt | 2 | ||||
-rw-r--r-- | host/tests/rfnoc_node_test.cpp | 108 | ||||
-rw-r--r-- | host/tests/rfnoc_property_test.cpp | 152 |
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)); +} |