uhd/host/tests/rfnoc_property_test.cpp
Aaron Rossetto a5fe0b071d rfnoc: Support instance overrides in set_properties()
This commit adds an enhancement to node_t::set_properties() in which
the instance argument provided to the function (which normally applies
to all properties in the key/value list) can be overridden on a
per-property basis using a special syntax.

If the key consists of the property name followed by a colon (':') and
then a number, the number following the colon is used to determine which
instance of the property this set pertains to, and the value passed via
the instance parameter is ignored for that property. For example, in the
following call:

    node->set_properties("dog=10,cat:2=5,bird:0=0.5", 1)

instance 1 of node's 'dog' property is set to 10, the 1 coming from the
instance parameter, instance 2 of the node's 'cat' property is set to 5
due to the override syntax provided in the string, and instance 0 of the
node's 'bird' property is set to 0.5 due to its override.

If the name/instance pair is malformed, e.g. 'value:=10' or
'value:foobar=10', a runtime error is thrown.
2020-07-24 13:15:30 -05:00

195 lines
6.9 KiB
C++

//
// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include <uhd/rfnoc/dirtifier.hpp>
#include <uhd/rfnoc/property.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_valid_names)
{
bool value_error_caught = false;
try {
property_t<int> prop_i{"int_prop:0", 10, {res_source_info::USER, 0}};
} catch(const uhd::value_error& e) {
value_error_caught = true;
} catch(...) {
}
BOOST_CHECK(value_error_caught);
}
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));
}
BOOST_AUTO_TEST_CASE(test_from_str)
{
prop_accessor_t prop_accessor{};
property_t<double> prop_d{"double_prop", 0.0, {res_source_info::USER}};
property_t<int> prop_i{"int_prop", 0, {res_source_info::USER}};
property_t<std::string> prop_s{"str_prop", "0", {res_source_info::USER}};
prop_accessor.set_access(prop_d, property_base_t::RW);
prop_accessor.set_access(prop_i, property_base_t::RW);
prop_accessor.set_access(prop_s, property_base_t::RW);
property_base_t* prop_base_ptr_d = static_cast<property_base_t*>(&prop_d);
property_base_t* prop_base_ptr_i = static_cast<property_base_t*>(&prop_i);
property_base_t* prop_base_ptr_s = static_cast<property_base_t*>(&prop_s);
prop_base_ptr_d->set_from_str(".25");
BOOST_CHECK_EQUAL(prop_d.get(), 0.25);
prop_base_ptr_i->set_from_str("23");
BOOST_CHECK_EQUAL(prop_i.get(), 23);
prop_base_ptr_s->set_from_str("foo");
BOOST_CHECK_EQUAL(prop_s.get(), "foo");
BOOST_REQUIRE_THROW(prop_base_ptr_d->set_from_str("banana"), uhd::runtime_error);
BOOST_REQUIRE_THROW(prop_base_ptr_i->set_from_str("potato"), uhd::runtime_error);
}